From 3ec362e4b299128b91d961250b444d930b4c60fa Mon Sep 17 00:00:00 2001 From: Alain SAVARD Date: Sun, 14 Jul 2019 13:13:06 -0400 Subject: [PATCH 0001/1766] 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 9a67a8e4290..10dddc7de00 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 0002/1766] 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 2c2321ee9ca..1fc0bf1e6c6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1144,6 +1144,9 @@ namespace { 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 0003/1766] 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 10dddc7de00..23af60f36ae 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 0004/1766] 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 23af60f36ae..7359eb92b35 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 0005/1766] 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 1fc0bf1e6c6..f50fdf8803f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1114,6 +1114,13 @@ namespace { + (*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 0006/1766] 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 0d3a57bfa6e..9755c2ec2e0 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,22 +111,21 @@ 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))) + // 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))); + + // Passed pawns will be properly scored later in evaluation when we have + // full attack info. + if (passed) e->passedPawns[Us] |= s; - 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; - } - // 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 f50fdf8803f..222be393be5 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1152,7 +1152,7 @@ namespace { : -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 0007/1766] 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 222be393be5..09df1ac2697 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1041,7 +1041,7 @@ namespace { 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 0008/1766] 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 281579c4a62..2afd3766bab 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 3a05f3faf6b..11d4c687dfe 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 9f06e174e58..fbde810b503 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 7864486cb5f..90c86388e78 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 b9c01fe700c..b0c333b8eb8 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 0009/1766] 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 09df1ac2697..eda7f2f771a 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 0010/1766] 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 9755c2ec2e0..86c4b8ef252 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 0011/1766] 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 eda7f2f771a..6d1a66e5f86 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 @@ namespace { // 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 @@ namespace { 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 @@ namespace { 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 @@ namespace { 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 @@ namespace { + (*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 @@ namespace { 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 @@ namespace { 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 0012/1766] 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 6d1a66e5f86..98419b20fc7 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 @@ namespace { 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 46ddb495293..c11d17873df 100644 --- a/src/thread.h +++ b/src/thread.h @@ -59,7 +59,7 @@ class Thread { 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 0013/1766] 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 7359eb92b35..edebb269565 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 0014/1766] 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 431bc8385f5..207c5a85c4a 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 7a16597d200..477b1655d5d 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 edebb269565..05fa45bc9e0 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 86c4b8ef252..5df175b233c 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 fbde810b503..4358f9685ed 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 98419b20fc7..b154c6d9f64 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 @@ namespace { 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 90c86388e78..10864744c2e 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 0015/1766] 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 d0a5a97e08a..e29f877782c 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 0016/1766] 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 b154c6d9f64..69488ad583f 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 0017/1766] 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 69488ad583f..7f421b9cd38 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1074,7 +1074,8 @@ namespace { && 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 0018/1766] 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 7f421b9cd38..7a16ef8cfc0 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 0019/1766] 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 4c609352358..fc99ec26611 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 5df175b233c..1cacc1e3c53 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 4358f9685ed..db66f416559 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 2106414ba2d..a0a9a306b7a 100644 --- a/src/position.h +++ b/src/position.h @@ -100,9 +100,9 @@ class Position { // 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 b0c333b8eb8..3559d72b5ee 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 0020/1766] 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 1cacc1e3c53..33e859e5a3d 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 1f930988a9f..4c041716db8 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 0021/1766] 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 7a16ef8cfc0..f0fa6f05132 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 0022/1766] 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 f0fa6f05132..28cbffd6d4c 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 @@ namespace { // 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 24c58d3085e..c77ca3ad899 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 e5043b6ea35..5165fd90ae9 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 c11d17873df..ed427b10c1e 100644 --- a/src/thread.h +++ b/src/thread.h @@ -56,6 +56,7 @@ class Thread { 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 0023/1766] 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 28cbffd6d4c..f8535a5f2e2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1113,7 +1113,7 @@ namespace { // 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 @@ namespace { 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 3559d72b5ee..934f884e8fb 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 0024/1766] 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 33e859e5a3d..1ae65bf181b 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 934f884e8fb..c77d804037e 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 0025/1766] 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 05fa45bc9e0..cb1ad1f4200 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 0026/1766] 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 cb1ad1f4200..73e2144cc2d 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 0027/1766] 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 285d314ec30..6deb0e27a6a 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 0028/1766] 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 6deb0e27a6a..70246f56293 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 0029/1766] 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 73e2144cc2d..ad76fc8d727 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 0030/1766] 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 207c5a85c4a..e55f94fb5a9 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 db66f416559..6336a5ed193 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 f8535a5f2e2..79942bcdf1c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1050,7 +1050,7 @@ namespace { 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 @@ namespace { 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 0031/1766] 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 ad76fc8d727..d92239c152c 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 0032/1766] 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 d92239c152c..a7a091abd15 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 0033/1766] 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 88900540204..5583a06e33f 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 0034/1766] 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 e55f94fb5a9..455cf6ca62a 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 0035/1766] 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 a7a091abd15..9521cd10028 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 1ae65bf181b..f60411993fc 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 0036/1766] 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 79942bcdf1c..2d5fb6305b8 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 0037/1766] 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 9521cd10028..f37820afcef 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 f60411993fc..d822ef4ef85 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 36d99feb7e0..13e69499b18 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 c77d804037e..6af03687203 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 0038/1766] 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 f37820afcef..9521cd10028 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 d822ef4ef85..f60411993fc 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 13e69499b18..36d99feb7e0 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 6af03687203..c77d804037e 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 0039/1766] 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 36d99feb7e0..2b06931c6f9 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 0040/1766] 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 455cf6ca62a..aff6a6c93c8 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 9521cd10028..f37820afcef 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 f60411993fc..ca4156dac52 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 2b06931c6f9..83d11cf1c1a 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 c77d804037e..6d2c09a6010 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 0041/1766] 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 ca4156dac52..50eb3aa32c3 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 0042/1766] 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 b1539ce20ea..a5717e7a185 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 ddd05e4e13d..0efdd562e1d 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 a4235f2b23f..6e560572dd9 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 0043/1766] 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 a5717e7a185..b1539ce20ea 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 0efdd562e1d..ddd05e4e13d 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 6e560572dd9..a4235f2b23f 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 0044/1766] 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 b1539ce20ea..17644eed895 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 10864744c2e..c7d2078841d 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 5165fd90ae9..f7445f98ca4 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 ed427b10c1e..79c6e43fbfe 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 5583a06e33f..f8cb466b71e 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 0045/1766] 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 f37820afcef..880aa1375ba 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 83d11cf1c1a..655eb993663 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 0046/1766] 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 2d5fb6305b8..01de12a4cea 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -997,10 +997,6 @@ namespace { && (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 @@ namespace { && 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 0047/1766] 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 01de12a4cea..2ad535ccb56 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 0048/1766] 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 880aa1375ba..fadc0fea43e 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 2ad535ccb56..d34e1823928 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1008,7 +1008,7 @@ namespace { && 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 0049/1766] 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 f94a322c4af..40081e8d0c2 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 d34e1823928..55df6172c64 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 f7445f98ca4..90ec274d820 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 0050/1766] 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 fadc0fea43e..ee98da90e60 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 50eb3aa32c3..8022ae51c20 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 0051/1766] 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 8022ae51c20..bc1cf38af9c 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 0052/1766] 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 bc1cf38af9c..1e5b4f43b9a 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 0053/1766] 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 1d56a23e904..a59ea425141 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 64380da9c33..fab8cea8646 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 @@ Move MovePicker::next_move(bool skipQuiets) { 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 55df6172c64..c05e72e0bcd 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 @@ namespace { 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 @@ namespace { // 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 @@ namespace { // 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 @@ namespace { && 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 @@ namespace { 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 @@ namespace { 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 @@ namespace { // 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 @@ namespace { // 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 @@ namespace { // 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 @@ namespace { // 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 @@ namespace { 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 @@ namespace { // 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 @@ namespace { // 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 90ec274d820..19687aad7c4 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 6121b3ad771..d3cd094eed9 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 3a5ba5da8ee..d087cc38f77 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 6d2c09a6010..cc4008b3e72 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 0054/1766] 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 fab8cea8646..e39f2afa261 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 @@ Move MovePicker::next_move(bool skipQuiets) { 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 e916514dfd1..105c95d7d13 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 c05e72e0bcd..156f6d1c626 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 @@ namespace { // 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 @@ namespace { // 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 @@ namespace { 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 @@ namespace { (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 @@ namespace { } 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 19687aad7c4..3c9473c283f 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 79c6e43fbfe..0a77d5b8282 100644 --- a/src/thread.h +++ b/src/thread.h @@ -71,7 +71,7 @@ class Thread { 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 0055/1766] 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 156f6d1c626..6cf999380b6 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 @@ namespace { // 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 @@ namespace { 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 @@ namespace { } 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 0056/1766] 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 | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index 1e5b4f43b9a..1825b6e286b 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))); - - // 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); - } + // the adjacent files and cannot safely advance. + backward = !(neighbours & forward_ranks_bb(Them, s + Up)) + && (stoppers & (leverPush | blocked)); + + // 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 0057/1766] 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 ee98da90e60..0963ddd606d 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 0058/1766] 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 aff6a6c93c8..8317f545a2d 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 6cf999380b6..1742c6766aa 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 0059/1766] 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 0963ddd606d..a0ad09f084b 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 0060/1766] 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 | 16 +++++++++------- src/thread.h | 2 +- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 1742c6766aa..7e2df215ee8 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 @@ namespace { // 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 @@ namespace { } 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 3c9473c283f..476f1d64d84 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 (StatsType c : { NoCaptures, Captures }) - continuationHistory[c][NO_PIECE][0]->fill(Search::CounterMovePruneThreshold - 1); + for (bool inCheck : { false, true }) + for (StatsType c : { NoCaptures, Captures }) + for (auto& to : continuationHistory[inCheck][c]) + for (auto& h : to) + h->fill(0); + + 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 0a77d5b8282..0517afc5d14 100644 --- a/src/thread.h +++ b/src/thread.h @@ -71,7 +71,7 @@ class Thread { 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 0061/1766] 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 b23c5d17ef7..8f30bee4f8e 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 0062/1766] 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 7e2df215ee8..6e59bb54178 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 @@ namespace { 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 @@ namespace { // 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 @@ namespace { 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 @@ namespace { (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 @@ namespace { } 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 @@ namespace { assert(is_ok(move)); givesCheck = pos.gives_check(move); + captureOrPromotion = pos.capture_or_promotion(move); moveCount++; @@ -1475,7 +1483,10 @@ namespace { } 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 0063/1766] 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 a4235f2b23f..99bf1a13fa7 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; From 215cd19108d97376284192c29790b42a0b0e618a Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 21 Oct 2019 08:05:14 +0200 Subject: [PATCH 0064/1766] Avoid crashing on Log File opening Stockfish crashes immediately if users enter a wrong file name (or even an existing folder name) for debug log file. It may be hard for users to find out since it prints nothing. If they enter the string via a chess GUI, the chess GUI may remember and auto-send to Stockfish next time, makes Stockfish crashes all the time. Bug report by Nguyen Hong Pham in this issue: https://github.com/official-stockfish/Stockfish/issues/2365 This patch avoids the crash and instead prefers to exit gracefully with a error message on std:cerr, like we do with the fenFile for instance. Closes https://github.com/official-stockfish/Stockfish/pull/2366 No functional change. --- src/misc.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/misc.cpp b/src/misc.cpp index 17644eed895..6f908fd2730 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -102,6 +102,13 @@ class Logger { if (!fname.empty() && !l.file.is_open()) { l.file.open(fname, ifstream::out); + + if (!l.file.is_open()) + { + cerr << "Unable to open debug log file " << fname << endl; + exit(EXIT_FAILURE); + } + cin.rdbuf(&l.in); cout.rdbuf(&l.out); } From 7e89a71624e07c735c2230dbbf2c7dbae864916e Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 21 Oct 2019 22:21:50 +0200 Subject: [PATCH 0065/1766] Simplify reductions on singular extension Current master employs a scheme to adjust reductions on singular nodes that is somewhat controversial, see https://github.com/official-stockfish/Stockfish/pull/2167 This patch removes this use of a search result outside of [a,b], by observing that the main effect of this code is to adjust the reduction by an average of ~2 (1.7) rather than 1. Claims the first blue at STC and LTC: STC: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 30142 W: 6547 L: 6442 D: 17153 http://tests.stockfishchess.org/tests/view/5daf16c40ebc5902c06da566 LTC: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 45715 W: 7380 L: 7298 D: 31037 http://tests.stockfishchess.org/tests/view/5daf2f3c0ebc5902c06da6c7 Closes https://github.com/official-stockfish/Stockfish/pull/2367 Bench: 5115841 --- src/search.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 6e59bb54178..c70fbf60cd6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -595,16 +595,16 @@ namespace { Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue; bool ttHit, ttPv, inCheck, givesCheck, improving, doLMR, priorCapture; - bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture; + bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture, singularLMR; Piece movedPiece; - int moveCount, captureCount, quietCount, singularLMR; + int moveCount, captureCount, quietCount; // 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; + moveCount = captureCount = quietCount = ss->moveCount = 0; bestValue = -VALUE_INFINITE; maxValue = VALUE_INFINITE; @@ -917,7 +917,7 @@ namespace { ss->killers); value = bestValue; - moveCountPruning = false; + singularLMR = moveCountPruning = false; ttCapture = ttMove && pos.capture_or_promotion(ttMove); // Mark this node as being searched @@ -980,10 +980,7 @@ namespace { if (value < singularBeta) { extension = 1; - singularLMR++; - - if (value < singularBeta - std::min(4 * depth, 36)) - singularLMR++; + singularLMR = true; } // Multi-cut pruning @@ -1106,7 +1103,8 @@ namespace { r--; // Decrease reduction if ttMove has been singularly extended - r -= singularLMR; + if (singularLMR) + r -= 2; if (!captureOrPromotion) { From 90c0385724a0d9b6c0737bc2711ad77e315d17ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sat, 19 Oct 2019 02:20:38 +0200 Subject: [PATCH 0066/1766] Assorted trivial cleanups - Cleanups by Alain - Group king attacks and king defenses - Signature of futility_move_count() - Use is_discovery_check_on_king() - Simplify backward definition - Use static asserts in move generator - Factor a statement in move generator No functional change --- src/Makefile | 2 +- src/evaluate.cpp | 15 +++++++-------- src/movegen.cpp | 9 +++------ src/pawns.cpp | 2 +- src/position.h | 3 ++- src/search.cpp | 12 ++++++------ src/syzygy/tbprobe.cpp | 8 +++----- src/thread.cpp | 10 +++++----- src/types.h | 2 +- 9 files changed, 29 insertions(+), 34 deletions(-) diff --git a/src/Makefile b/src/Makefile index 70246f56293..679eb8d90fc 100644 --- a/src/Makefile +++ b/src/Makefile @@ -290,7 +290,7 @@ ifeq ($(optimize),yes) CXXFLAGS += -fno-gcse -mthumb -march=armv7-a -mfloat-abi=softfp endif endif - + ifeq ($(comp),$(filter $(comp),gcc clang icc)) ifeq ($(KERNEL),Darwin) CXXFLAGS += -mdynamic-no-pic diff --git a/src/evaluate.cpp b/src/evaluate.cpp index a0ad09f084b..6bbd72a7cae 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -448,16 +448,16 @@ namespace { int kingFlankAttacks = popcount(b1) + popcount(b2); kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them] - + 69 * kingAttacksCount[Them] + 185 * popcount(kingRing[Us] & weak) - - 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING]) - - 35 * bool(attackedBy[Us][BISHOP] & attackedBy[Us][KING]) + 148 * popcount(unsafeChecks) + 98 * popcount(pos.blockers_for_king(Us)) + + 69 * kingAttacksCount[Them] + + 3 * kingFlankAttacks * kingFlankAttacks / 8 + + mg_value(mobility[Them] - mobility[Us]) - 873 * !pos.count(Them) + - 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING]) + - 35 * bool(attackedBy[Us][BISHOP] & attackedBy[Us][KING]) - 6 * mg_value(score) / 8 - + mg_value(mobility[Them] - mobility[Us]) - + 3 * kingFlankAttacks * kingFlankAttacks / 8 - 7; // Transform the kingDanger units into a Score, and subtract it from the evaluation @@ -596,7 +596,6 @@ 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]; @@ -646,7 +645,7 @@ namespace { || (pos.pieces(PAWN) & (s + Up))) bonus = bonus / 2; - score += bonus - PassedFile * map_to_queenside(f); + score += bonus - PassedFile * map_to_queenside(file_of(s)); } if (T) @@ -755,7 +754,7 @@ namespace { else sf = std::min(sf, 36 + (pos.opposite_bishops() ? 2 : 7) * pos.count(strongSide)); - sf = std::max(0, sf - (pos.rule50_count() - 12) / 4 ); + sf = std::max(0, sf - (pos.rule50_count() - 12) / 4); } return ScaleFactor(sf); diff --git a/src/movegen.cpp b/src/movegen.cpp index fc99ec26611..ef7821e0019 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -60,6 +60,7 @@ namespace { constexpr Direction UpRight = (Us == WHITE ? NORTH_EAST : SOUTH_WEST); constexpr Direction UpLeft = (Us == WHITE ? NORTH_WEST : SOUTH_EAST); + const Square ksq = pos.square(Them); Bitboard emptySquares; Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB; @@ -84,8 +85,6 @@ namespace { if (Type == QUIET_CHECKS) { - Square ksq = pos.square(Them); - b1 &= pos.attacks_from(ksq, Them); b2 &= pos.attacks_from(ksq, Them); @@ -130,8 +129,6 @@ namespace { Bitboard b2 = shift(pawnsOn7) & enemies; Bitboard b3 = shift(pawnsOn7) & emptySquares; - Square ksq = pos.square(Them); - while (b1) moveList = make_promotions(moveList, pop_lsb(&b1), ksq); @@ -187,7 +184,7 @@ namespace { ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Color us, Bitboard target) { - assert(Pt != KING && Pt != PAWN); + static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()"); const Square* pl = pos.squares(us); @@ -261,7 +258,7 @@ namespace { template ExtMove* generate(const Position& pos, ExtMove* moveList) { - assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS); + static_assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS, "Unsupported type in generate()"); assert(!pos.checkers()); Color us = pos.side_to_move(); diff --git a/src/pawns.cpp b/src/pawns.cpp index 1825b6e286b..84f3d97748d 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -108,7 +108,7 @@ namespace { // A pawn is backward when it is behind all pawns of the same color on // the adjacent files and cannot safely advance. backward = !(neighbours & forward_ranks_bb(Them, s + Up)) - && (stoppers & (leverPush | blocked)); + && (leverPush | blocked); // Compute additional span if pawn is not backward nor blocked if (!backward && !blocked) diff --git a/src/position.h b/src/position.h index a0a9a306b7a..e6c901ea2c9 100644 --- a/src/position.h +++ b/src/position.h @@ -286,7 +286,8 @@ inline Square Position::castling_rook_square(CastlingRights cr) const { template inline Bitboard Position::attacks_from(Square s) const { - assert(Pt != PAWN); + static_assert(Pt != PAWN, "Pawn attacks need color"); + return Pt == BISHOP || Pt == ROOK ? attacks_bb(s, byTypeBB[ALL_PIECES]) : Pt == QUEEN ? attacks_from(s) | attacks_from(s) : PseudoAttacks[Pt][s]; diff --git a/src/search.cpp b/src/search.cpp index c70fbf60cd6..e0dcc4d1346 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -75,7 +75,7 @@ namespace { return (r + 520) / 1024 + (!i && r > 999); } - constexpr int futility_move_count(bool improving, int depth) { + constexpr int futility_move_count(bool improving, Depth depth) { return (5 + depth * depth) * (1 + improving) / 2; } @@ -594,7 +594,7 @@ namespace { Move ttMove, move, excludedMove, bestMove; Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue; - bool ttHit, ttPv, inCheck, givesCheck, improving, doLMR, priorCapture; + bool ttHit, ttPv, inCheck, givesCheck, improving, didLMR, priorCapture; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture, singularLMR; Piece movedPiece; int moveCount, captureCount, quietCount; @@ -1151,17 +1151,17 @@ namespace { value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); - doFullDepthSearch = (value > alpha && d != newDepth), doLMR = true; + doFullDepthSearch = (value > alpha && d != newDepth), didLMR = true; } else - doFullDepthSearch = !PvNode || moveCount > 1, doLMR = false; + doFullDepthSearch = !PvNode || moveCount > 1, didLMR = 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) + if (didLMR && !captureOrPromotion) { int bonus = value > alpha ? stat_bonus(newDepth) : -stat_bonus(newDepth); @@ -1466,7 +1466,7 @@ namespace { // Don't search moves with negative SEE values if ( (!inCheck || evasionPrunable) - && (!givesCheck || !(pos.blockers_for_king(~pos.side_to_move()) & from_sq(move))) + && !(givesCheck && pos.is_discovery_check_on_king(~pos.side_to_move(), move)) && !pos.see_ge(move)) continue; diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index c7d2078841d..a9378b4ba09 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -706,9 +706,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu std::swap(squares[0], *std::max_element(squares, squares + leadPawnsCnt, pawns_comp)); - tbFile = file_of(squares[0]); - if (tbFile > FILE_D) - tbFile = file_of(squares[0] ^ 7); // Horizontal flip: SQ_H1 -> SQ_A1 + tbFile = map_to_queenside(file_of(squares[0])); } // DTZ tables are one-sided, i.e. they store positions only for white to @@ -1062,8 +1060,8 @@ void set(T& e, uint8_t* data) { enum { Split = 1, HasPawns = 2 }; - assert(e.hasPawns == !!(*data & HasPawns)); - assert((e.key != e.key2) == !!(*data & Split)); + assert(e.hasPawns == bool(*data & HasPawns)); + assert((e.key != e.key2) == bool(*data & Split)); data++; // First byte stores flags diff --git a/src/thread.cpp b/src/thread.cpp index 476f1d64d84..680cd3adb8c 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -71,13 +71,13 @@ void Thread::clear() { captureHistory.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 }) + for (auto& to : continuationHistory[inCheck][c]) + for (auto& h : to) + h->fill(0); for (bool inCheck : { false, true }) - for (StatsType c : { NoCaptures, Captures }) + for (StatsType c : { NoCaptures, Captures }) continuationHistory[inCheck][c][NO_PIECE][0]->fill(Search::CounterMovePruneThreshold - 1); } diff --git a/src/types.h b/src/types.h index cc4008b3e72..5197e9fbbe3 100644 --- a/src/types.h +++ b/src/types.h @@ -341,7 +341,7 @@ inline Score operator*(Score s, int i) { return result; } -/// Multiplication of a Score by an boolean +/// Multiplication of a Score by a boolean inline Score operator*(Score s, bool b) { return Score(int(s) * int(b)); } From 648c7ec25db2040c0af34dd846dfa3f57af5ad0a Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 23 Oct 2019 08:26:47 +0200 Subject: [PATCH 0067/1766] Refactor final stats updates. This PR refactors update_quiet_stats, update_capture_stats and search to more clearly reflect what is actually done. Effectively, all stat updates that need to be done after search is finished and a bestmove is found, are collected in a new function ```final_stats_update()```. This shortens our main search routine, and simplifies ```update_quiet_stats```. The latter function is now more easily reusable with fewer arguments, as the handling of ```quietsSearched``` is only needed in ```final_stats_update```. ```update_capture_stats```, which was only called once is now integrated in ```final_stats_update```, which allows for removing a branch and reusing some ```stat_bonus``` calls. The need for refactoring was also suggested by the fact that the comments of ```update_quiet_stats``` and ```update_capture_stats``` were incorrect (e.g. ```update_capture_stats``` was called, correctly, also when the bestmove was a quiet and not a capture). passed non-regression STC: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 75196 W: 16364 L: 16347 D: 42485 http://tests.stockfishchess.org/tests/view/5db004ec0ebc5902c06db9e1 The diff is most easily readable as ```git diff master --patience``` No functional change --- src/search.cpp | 102 ++++++++++++++++++++++++++----------------------- 1 file changed, 54 insertions(+), 48 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index e0dcc4d1346..086761a3561 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -153,8 +153,9 @@ namespace { Value value_from_tt(Value v, int ply); void update_pv(Move* pv, Move move, Move* childPv); void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus); - void update_quiet_stats(const Position& pos, Stack* ss, Move move, Move* quiets, int quietCount, int bonus); - void update_capture_stats(const Position& pos, Move move, Move* captures, int captureCount, int bonus); + void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus); + void update_all_stats(const Position& pos, Stack* ss, Move bestMove, Value bestValue, Value beta, Square prevSq, + Move* quietsSearched, int quietCount, Move* capturesSearched, int captureCount, Depth depth); // perft() is our utility to verify move generation. All the leaf nodes up // to the given depth are generated and counted, and the sum is returned. @@ -679,7 +680,7 @@ namespace { if (ttValue >= beta) { if (!pos.capture_or_promotion(ttMove)) - update_quiet_stats(pos, ss, ttMove, nullptr, 0, stat_bonus(depth)); + update_quiet_stats(pos, ss, ttMove, stat_bonus(depth)); // Extra penalty for early quiet moves of the previous ply if ((ss-1)->moveCount <= 2 && !priorCapture) @@ -1276,21 +1277,11 @@ namespace { if (!moveCount) bestValue = excludedMove ? alpha : inCheck ? mated_in(ss->ply) : VALUE_DRAW; - else if (bestMove) - { - // 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))); - - 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])) - && !priorCapture) - update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + 1)); + else if (bestMove) + update_all_stats(pos, ss, bestMove, bestValue, beta, prevSq, + quietsSearched, quietCount, capturesSearched, captureCount, depth); - } // Bonus for prior countermove that caused the fail low else if ( (depth >= 3 || PvNode) && !priorCapture) @@ -1564,43 +1555,65 @@ namespace { } - // update_continuation_histories() updates histories of the move pairs formed - // by moves at ply -1, -2, and -4 with current move. + // update_all_stats() updates stats at the end of search() when a bestMove is found - void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { + void update_all_stats(const Position& pos, Stack* ss, Move bestMove, Value bestValue, Value beta, Square prevSq, + Move* quietsSearched, int quietCount, Move* capturesSearched, int captureCount, Depth depth) { - for (int i : {1, 2, 4, 6}) - if (is_ok((ss-i)->currentMove)) - (*(ss-i)->continuationHistory)[pc][to] << bonus; - } + int bonus1, bonus2; + Color us = pos.side_to_move(); + Thread* thisThread = pos.this_thread(); + CapturePieceToHistory& captureHistory = thisThread->captureHistory; + Piece moved_piece = pos.moved_piece(bestMove); + PieceType captured = type_of(pos.piece_on(to_sq(bestMove))); + + bonus1 = stat_bonus(depth + 1); + bonus2 = bestValue > beta + PawnValueMg ? bonus1 // larger bonus + : stat_bonus(depth); // smaller bonus + + if (!pos.capture_or_promotion(bestMove)) + { + update_quiet_stats(pos, ss, bestMove, bonus2); + + // Decrease all the non-best quiet moves + for (int i = 0; i < quietCount; ++i) + { + thisThread->mainHistory[us][from_to(quietsSearched[i])] << -bonus2; + update_continuation_histories(ss, pos.moved_piece(quietsSearched[i]), to_sq(quietsSearched[i]), -bonus2); + } + } + else + captureHistory[moved_piece][to_sq(bestMove)][captured] << bonus1; + // 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, -bonus1); - // update_capture_stats() updates move sorting heuristics when a new capture best move is found + // Decrease all the non-best capture moves + for (int i = 0; i < captureCount; ++i) + { + moved_piece = pos.moved_piece(capturesSearched[i]); + captured = type_of(pos.piece_on(to_sq(capturesSearched[i]))); + captureHistory[moved_piece][to_sq(capturesSearched[i])][captured] << -bonus1; + } + } - void update_capture_stats(const Position& pos, Move move, - Move* captures, int captureCount, int bonus) { - CapturePieceToHistory& captureHistory = pos.this_thread()->captureHistory; - Piece moved_piece = pos.moved_piece(move); - PieceType captured = type_of(pos.piece_on(to_sq(move))); + // update_continuation_histories() updates histories of the move pairs formed + // by moves at ply -1, -2, and -4 with current move. - if (pos.capture_or_promotion(move)) - captureHistory[moved_piece][to_sq(move)][captured] << bonus; + void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { - // Decrease all the other played capture moves - for (int i = 0; i < captureCount; ++i) - { - moved_piece = pos.moved_piece(captures[i]); - captured = type_of(pos.piece_on(to_sq(captures[i]))); - captureHistory[moved_piece][to_sq(captures[i])][captured] << -bonus; - } + for (int i : {1, 2, 4, 6}) + if (is_ok((ss-i)->currentMove)) + (*(ss-i)->continuationHistory)[pc][to] << bonus; } - // update_quiet_stats() updates move sorting heuristics when a new quiet best move is found + // update_quiet_stats() updates move sorting heuristics - void update_quiet_stats(const Position& pos, Stack* ss, Move move, - Move* quiets, int quietCount, int bonus) { + void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus) { if (ss->killers[0] != move) { @@ -1621,13 +1634,6 @@ namespace { Square prevSq = to_sq((ss-1)->currentMove); thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] = move; } - - // Decrease all the other played quiet moves - for (int i = 0; i < quietCount; ++i) - { - thisThread->mainHistory[us][from_to(quiets[i])] << -bonus; - update_continuation_histories(ss, pos.moved_piece(quiets[i]), to_sq(quiets[i]), -bonus); - } } // When playing with strength handicap, choose best move among a set of RootMoves From 1725ed39adcc4aa6d96117a7b83e1552b37a6baa Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Tue, 29 Oct 2019 10:35:56 +0100 Subject: [PATCH 0068/1766] Tweak dynamic contempt (the birthday patch) Make dynamic contempt weight factor dependent on static contempt so that higher static contempt implies less dynamic contempt and vice versa. For default contempt 24 this is a non-functional change. But tests with contempt 0 shows an elo gain. Also today is my birthday so i have already give to myself a gift with this patch :-)! Further proceedings: in the past we checked for default contempt that it doesn't regress against contempt 0. Now that the later is stronger and the former is the same strength this should be rechecked. Perhaps the default contempt have to be lowered. It would be interesting to get some idea of the impact of this patch outside of the 0-24 contempt range. STC: (both with contempt=0) LLR: 2.95 (-2.94,2.94) [-1.50,4.50] Total: 21912 W: 3898 L: 3740 D: 14274 http://tests.stockfishchess.org/tests/view/5db74b6f0ebc5902d1f37405 LTC: (both with contempt=0) LLR: 2.96 (-2.94,2.94) [0.00,3.50] Total: 27172 W: 3350 L: 3126 D: 20696 http://tests.stockfishchess.org/tests/view/5db760020ebc5902d1f375d0 Closes https://github.com/official-stockfish/Stockfish/pull/2382 No functional change (for current default contempt 24). --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 086761a3561..b571cdf1787 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -418,7 +418,7 @@ void Thread::search() { beta = std::min(previousScore + delta, VALUE_INFINITE); // Adjust contempt based on root move's previousScore (dynamic contempt) - int dct = ct + 86 * previousScore / (abs(previousScore) + 176); + int dct = ct + (111 - ct / 2) * previousScore / (abs(previousScore) + 176); contempt = (us == WHITE ? make_score(dct, dct / 2) : -make_score(dct, dct / 2)); From 6f3796adaf44c48cf1353181d386a61a57859b67 Mon Sep 17 00:00:00 2001 From: protonspring Date: Thu, 31 Oct 2019 17:17:46 +0100 Subject: [PATCH 0069/1766] Consolidate pawn_push and up This is a non-functional simplification. Pawn_push and Up are redundant. If we make up pawn_push, we can use it for all of the Up's and Down's. In this version, I've also left the Up and Down constants so that there is no worse readability. STC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 23878 W: 5202 L: 5085 D: 13591 http://tests.stockfishchess.org/tests/view/5db5569a0ebc5902d6b14de4 Closes https://github.com/official-stockfish/Stockfish/pull/2378 No functional change --- src/evaluate.cpp | 12 ++++++------ src/movegen.cpp | 2 +- src/pawns.cpp | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 6bbd72a7cae..c1640d945d0 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -213,8 +213,8 @@ namespace { void Evaluation::initialize() { constexpr Color Them = (Us == WHITE ? BLACK : WHITE); - constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH); - constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH); + constexpr Direction Up = pawn_push(Us); + constexpr Direction Down = -Up; constexpr Bitboard LowRanks = (Us == WHITE ? Rank2BB | Rank3BB : Rank7BB | Rank6BB); const Square ksq = pos.square(Us); @@ -258,7 +258,7 @@ namespace { Score Evaluation::pieces() { constexpr Color Them = (Us == WHITE ? BLACK : WHITE); - constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH); + constexpr Direction Down = -pawn_push(Us); constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB : Rank5BB | Rank4BB | Rank3BB); const Square* pl = pos.squares(Us); @@ -484,7 +484,7 @@ namespace { Score Evaluation::threats() const { constexpr Color Them = (Us == WHITE ? BLACK : WHITE); - constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH); + constexpr Direction Up = pawn_push(Us); constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); Bitboard b, weak, defended, nonPawnEnemies, stronglyProtected, safe; @@ -578,7 +578,7 @@ namespace { Score Evaluation::passed() const { constexpr Color Them = (Us == WHITE ? BLACK : WHITE); - constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH); + constexpr Direction Up = pawn_push(Us); auto king_proximity = [&](Color c, Square s) { return std::min(distance(pos.square(c), s), 5); @@ -669,7 +669,7 @@ namespace { return SCORE_ZERO; constexpr Color Them = (Us == WHITE ? BLACK : WHITE); - constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH); + constexpr Direction Down = -pawn_push(Us); constexpr Bitboard SpaceMask = Us == WHITE ? CenterFiles & (Rank2BB | Rank3BB | Rank4BB) : CenterFiles & (Rank7BB | Rank6BB | Rank5BB); diff --git a/src/movegen.cpp b/src/movegen.cpp index ef7821e0019..0b91582ec99 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -56,7 +56,7 @@ namespace { constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); - constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH); + constexpr Direction Up = pawn_push(Us); constexpr Direction UpRight = (Us == WHITE ? NORTH_EAST : SOUTH_WEST); constexpr Direction UpLeft = (Us == WHITE ? NORTH_WEST : SOUTH_EAST); diff --git a/src/pawns.cpp b/src/pawns.cpp index 84f3d97748d..3ddf70308c5 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -69,7 +69,7 @@ namespace { Score evaluate(const Position& pos, Pawns::Entry* e) { constexpr Color Them = (Us == WHITE ? BLACK : WHITE); - constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH); + constexpr Direction Up = pawn_push(Us); Bitboard neighbours, stoppers, support, phalanx, opposed; Bitboard lever, leverPush, blocked; From e8fca713424e814756e2db4a7195f69fdb669c2a Mon Sep 17 00:00:00 2001 From: protonspring Date: Thu, 31 Oct 2019 09:01:33 -0600 Subject: [PATCH 0070/1766] Simplify kingRing Simplify the king ring initialization and make it more regular, by just moving the king square off the edges and using PseudoAttacks by king from this new square. There is a small functional difference from the previous master, as the old master excludes the original ksq square while this patch always includes the nine squares block (after moving the king from the edges). Additionally, master does not adjust the kingRing down if we are on relative rank 8, while this patch treats all of the edges the same. STC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 13263 W: 2968 L: 2830 D: 7465 http://tests.stockfishchess.org/tests/view/5db872830ebc5902d1f388aa LTC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 72996 W: 11819 L: 11780 D: 49397 http://tests.stockfishchess.org/tests/view/5db899c20ebc5902d1f38b5e Closes https://github.com/official-stockfish/Stockfish/pull/2384 Bench: 4959244 --- src/evaluate.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index c1640d945d0..959ccd8431c 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -235,15 +235,9 @@ namespace { attackedBy2[Us] = dblAttackByPawn | (attackedBy[Us][KING] & attackedBy[Us][PAWN]); // Init our king safety tables - kingRing[Us] = attackedBy[Us][KING]; - if (relative_rank(Us, ksq) == RANK_1) - kingRing[Us] |= shift(kingRing[Us]); - - if (file_of(ksq) == FILE_H) - kingRing[Us] |= shift(kingRing[Us]); - - else if (file_of(ksq) == FILE_A) - kingRing[Us] |= shift(kingRing[Us]); + Square s = make_square(clamp(file_of(ksq), FILE_B, FILE_G), + clamp(rank_of(ksq), RANK_2, RANK_7)); + kingRing[Us] = PseudoAttacks[KING][s] | s; kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them)); kingAttacksCount[Them] = kingAttackersWeight[Them] = 0; From 474d133565564146ec28878afca54739cc4e22d2 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Fri, 1 Nov 2019 13:58:11 +0800 Subject: [PATCH 0071/1766] Combo of Parameter Tweaks This patch is a combo of the following tweaks: Complexity parameters Knight PSQT Bishop PSQT King PSQT Piece Values Passed STC: LLR: 2.95 (-2.94,2.94) [-1.50,4.50] Total: 56527 W: 12326 L: 12052 D: 32149 http://tests.stockfishchess.org/tests/view/5dbbca3f0ebc5925b64ee6d6 Passed LTC: LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 64010 W: 10549 L: 10199 D: 43262 http://tests.stockfishchess.org/tests/view/5dbc30dc0ebc5925b64eee0c Closes https://github.com/official-stockfish/Stockfish/pull/2390 Bench: 4312945 --- AUTHORS | 2 +- src/evaluate.cpp | 8 ++++---- src/psqt.cpp | 48 ++++++++++++++++++++++++------------------------ src/types.h | 8 ++++---- 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/AUTHORS b/AUTHORS index 8317f545a2d..979410ae4bd 100644 --- a/AUTHORS +++ b/AUTHORS @@ -64,7 +64,7 @@ Jean Gauthier (QuaisBla) Jean-Francois Romang (jromang) Jerry Donald Watson (jerrydonaldwatson) Jonathan Calovski (Mysseno) -Jonathan D. (SFisGOD) +Jonathan Dumale (SFisGOD) Joost VandeVondele (vondele) Jörg Oster (joergoster) Joseph Ellis (jhellis3) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 959ccd8431c..ea54e271390 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -713,10 +713,10 @@ namespace { int complexity = 9 * pe->passed_count() + 11 * pos.count() + 9 * outflanking - + 18 * pawnsOnBothFlanks - + 49 * !pos.non_pawn_material() - - 36 * almostUnwinnable - -103 ; + + 21 * pawnsOnBothFlanks + + 51 * !pos.non_pawn_material() + - 43 * almostUnwinnable + - 95 ; // 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 diff --git a/src/psqt.cpp b/src/psqt.cpp index 655eb993663..60d17ad29e5 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -39,24 +39,24 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { { }, { }, { // Knight - { S(-169,-105), S(-96,-74), S(-80,-46), S(-79,-18) }, - { S( -79, -70), S(-39,-56), S(-24,-15), S( -9, 6) }, - { S( -64, -38), S(-20,-33), S( 4, -5), S( 19, 27) }, - { S( -28, -36), S( 5, 0), S( 41, 13), S( 47, 34) }, - { S( -29, -41), S( 13,-20), S( 42, 4), S( 52, 35) }, - { S( -11, -51), S( 28,-38), S( 63,-17), S( 55, 19) }, - { S( -67, -64), S(-21,-45), S( 6,-37), S( 37, 16) }, - { S(-200, -98), S(-80,-89), S(-53,-53), S(-32,-16) } + { S(-175, -96), S(-92,-65), S(-74,-49), S(-73,-21) }, + { S( -77, -67), S(-41,-54), S(-27,-18), S(-15, 8) }, + { S( -61, -40), S(-17,-27), S( 6, -8), S( 12, 29) }, + { S( -35, -35), S( 8, -2), S( 40, 13), S( 49, 28) }, + { S( -34, -45), S( 13,-16), S( 44, 9), S( 51, 39) }, + { S( -9, -51), S( 22,-44), S( 58,-16), S( 53, 17) }, + { S( -67, -69), S(-27,-50), S( 4,-51), S( 37, 12) }, + { S(-201,-100), S(-83,-88), S(-56,-56), S(-26,-17) } }, { // Bishop - { S(-44,-63), S( -4,-30), S(-11,-35), S(-28, -8) }, - { S(-18,-38), S( 7,-13), S( 14,-14), S( 3, 0) }, - { S( -8,-18), S( 24, 0), S( -3, -7), S( 15, 13) }, - { S( 1,-26), S( 8, -3), S( 26, 1), S( 37, 16) }, - { S( -7,-24), S( 30, -6), S( 23,-10), S( 28, 17) }, - { S(-17,-26), S( 4, 2), S( -1, 1), S( 8, 16) }, - { S(-21,-34), S(-19,-18), S( 10, -7), S( -6, 9) }, - { S(-48,-51), S( -3,-40), S(-12,-39), S(-25,-20) } + { S(-53,-57), S( -5,-30), S( -8,-37), S(-23,-12) }, + { S(-15,-37), S( 8,-13), S( 19,-17), S( 4, 1) }, + { S( -7,-16), S( 21, -1), S( -5, -2), S( 17, 10) }, + { S( -5,-20), S( 11, -6), S( 25, 0), S( 39, 17) }, + { S(-12,-17), S( 29, -1), S( 22,-14), S( 31, 15) }, + { S(-16,-30), S( 6, 6), S( 1, 4), S( 11, 6) }, + { S(-17,-31), S(-14,-20), S( 5, -1), S( 0, 1) }, + { S(-48,-46), S( 1,-42), S(-14,-37), S(-23,-24) } }, { // Rook { S(-31, -9), S(-20,-13), S(-14,-10), S(-5, -9) }, @@ -79,14 +79,14 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { { S(-2,-75), S(-2,-52), S( 1,-43), S(-2,-36) } }, { // King - { S(272, 0), S(325, 41), S(273, 80), S(190, 93) }, - { S(277, 57), S(305, 98), S(241,138), S(183,131) }, - { S(198, 86), S(253,138), S(168,165), S(120,173) }, - { S(169,103), S(191,152), S(136,168), S(108,169) }, - { S(145, 98), S(176,166), S(112,197), S( 69,194) }, - { S(122, 87), S(159,164), S( 85,174), S( 36,189) }, - { S( 87, 40), S(120, 99), S( 64,128), S( 25,141) }, - { S( 64, 5), S( 87, 60), S( 49, 75), S( 0, 75) } + { S(271, 1), S(327, 45), S(270, 85), S(192, 76) }, + { S(278, 53), S(303,100), S(230,133), S(174,135) }, + { S(195, 88), S(258,130), S(169,169), S(120,175) }, + { S(164,103), S(190,156), S(138,172), S( 98,172) }, + { S(154, 96), S(179,166), S(105,199), S( 70,199) }, + { S(123, 92), S(145,172), S( 81,184), S( 31,191) }, + { S( 88, 47), S(120,121), S( 65,116), S( 33,131) }, + { S( 59, 11), S( 89, 59), S( 45, 73), S( -1, 78) } } }; diff --git a/src/types.h b/src/types.h index 5197e9fbbe3..13c3bbf236c 100644 --- a/src/types.h +++ b/src/types.h @@ -180,10 +180,10 @@ enum Value : int { VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY, PawnValueMg = 128, PawnValueEg = 213, - KnightValueMg = 782, KnightValueEg = 865, - BishopValueMg = 830, BishopValueEg = 918, - RookValueMg = 1289, RookValueEg = 1378, - QueenValueMg = 2529, QueenValueEg = 2687, + KnightValueMg = 781, KnightValueEg = 854, + BishopValueMg = 825, BishopValueEg = 915, + RookValueMg = 1276, RookValueEg = 1380, + QueenValueMg = 2538, QueenValueEg = 2682, MidgameLimit = 15258, EndgameLimit = 3915 }; From cff9a8672c1da7d36bc54d168d10ea2b1ce5c728 Mon Sep 17 00:00:00 2001 From: 31m059 <37052095+31m059@users.noreply.github.com> Date: Fri, 1 Nov 2019 00:27:19 -0400 Subject: [PATCH 0072/1766] Make Square and Bitboard operators commutative As Stockfish developers, we aim to make our code as legible and as close to simple English as possible. However, one of the more notable exceptions to this rule concerns operations between Squares and Bitboards. Prior to this pull request, AND, OR, and XOR were only defined when the Bitboard was the first operand, and the Square the second. For example, for a Bitboard b and Square s, "b & s" would be valid but "s & b" would not. This conflicts with natural reasoning about logical operators, both mathematically and intuitively, which says that logical operators should commute. More dangerously, however, both Square and Bitboard are defined as integers "under the hood." As a result, code like "s & b" would still compile and give reasonable bench values. This trap occasionally ensnares even experienced Stockfish developers, but it is especially dangerous for new developers not aware of this peculiarity. Because there is no compilation or runtime error, and a reasonable bench, only a close review by approvers can spot this error when a test has been submitted--and many times, these bugs have slipped past review. This is by far the most common logical error on Fishtest, and has wasted uncountable STC games over the years. However, it can be fixed by adding three non-functional lines of code. In this patch, we define the operators when the operands are provided in the opposite order, i.e., we make AND, OR, and XOR commutative for Bitboards and Squares. Because these are inline methods and implemented identically, the executable does not change at all. This patch has the small side-effect of requiring Squares to be explicitly cast to integers before AND, OR, or XOR with integers. This is only performed twice in Stockfish's source code, and again does not change the executable at all (since Square is an enum defined as an integer anyway). For demonstration purposes, this pull request also inverts the order of one AND and one OR, to show that neither the bench nor the executable change. (This change can be removed before merging, if preferred.) I hope that this pull request significantly lowers the barrier-of-entry for new developer to join the Stockfish project. I also hope that this change will improve our efficiency in using our generous CPU donors' machines, since it will remove one of the most common causes of buggy tests. Following helpful review and comments by Michael Stembera (@mstembera), we add a further clean-up by implementing OR for two Squares, to anticipate additional traps developers may encounter and handle them cleanly. Closes https://github.com/official-stockfish/Stockfish/pull/2387 No functional change. --- src/bitbase.cpp | 2 +- src/bitboard.h | 6 ++++++ src/endgame.cpp | 2 +- src/evaluate.cpp | 4 ++-- src/position.h | 2 +- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/bitbase.cpp b/src/bitbase.cpp index 2b1a5517fb8..9301dcfad13 100644 --- a/src/bitbase.cpp +++ b/src/bitbase.cpp @@ -44,7 +44,7 @@ namespace { // bit 13-14: white pawn file (from FILE_A to FILE_D) // bit 15-17: white pawn RANK_7 - rank (from RANK_7 - RANK_7 to RANK_7 - RANK_2) unsigned index(Color us, Square bksq, Square wksq, Square psq) { - return wksq | (bksq << 6) | (us << 12) | (file_of(psq) << 13) | ((RANK_7 - rank_of(psq)) << 15); + return int(wksq) | (bksq << 6) | (us << 12) | (file_of(psq) << 13) | ((RANK_7 - rank_of(psq)) << 15); } enum Result { diff --git a/src/bitboard.h b/src/bitboard.h index 477b1655d5d..8d748eeed86 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -119,6 +119,12 @@ inline Bitboard operator^( Bitboard b, Square s) { return b ^ square_bb(s); } inline Bitboard& operator|=(Bitboard& b, Square s) { return b |= square_bb(s); } inline Bitboard& operator^=(Bitboard& b, Square s) { return b ^= square_bb(s); } +inline Bitboard operator&(Square s, Bitboard b) { return b & s; } +inline Bitboard operator|(Square s, Bitboard b) { return b | s; } +inline Bitboard operator^(Square s, Bitboard b) { return b ^ s; } + +inline Bitboard operator|(Square s, Square s2) { return square_bb(s) | square_bb(s2); } + constexpr bool more_than_one(Bitboard b) { return b & (b - 1); } diff --git a/src/endgame.cpp b/src/endgame.cpp index e10f8d5da97..ca38a662a01 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -74,7 +74,7 @@ namespace { assert(pos.count(strongSide) == 1); if (file_of(pos.square(strongSide)) >= FILE_E) - sq = Square(sq ^ 7); // Mirror SQ_H1 -> SQ_A1 + sq = Square(int(sq) ^ 7); // Mirror SQ_H1 -> SQ_A1 return strongSide == WHITE ? sq : ~sq; } diff --git a/src/evaluate.cpp b/src/evaluate.cpp index ea54e271390..cefd9db427f 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -237,7 +237,7 @@ namespace { // Init our king safety tables Square s = make_square(clamp(file_of(ksq), FILE_B, FILE_G), clamp(rank_of(ksq), RANK_2, RANK_7)); - kingRing[Us] = PseudoAttacks[KING][s] | s; + kingRing[Us] = s | PseudoAttacks[KING][s]; kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them)); kingAttacksCount[Them] = kingAttackersWeight[Them] = 0; @@ -291,7 +291,7 @@ 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) + if (s & bb) score += Outpost * (Pt == KNIGHT ? 2 : 1); else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us)) diff --git a/src/position.h b/src/position.h index e6c901ea2c9..2ec2729cf3a 100644 --- a/src/position.h +++ b/src/position.h @@ -430,7 +430,7 @@ inline void Position::move_piece(Piece pc, Square from, Square to) { // index[from] is not updated and becomes stale. This works as long as index[] // is accessed just by known occupied squares. - Bitboard fromTo = square_bb(from) | square_bb(to); + Bitboard fromTo = from | to; byTypeBB[ALL_PIECES] ^= fromTo; byTypeBB[type_of(pc)] ^= fromTo; byColorBB[color_of(pc)] ^= fromTo; From ef38046e734490499e4b6254dbf4fc434bf37fc9 Mon Sep 17 00:00:00 2001 From: MichaelB7 Date: Sat, 2 Nov 2019 20:04:05 -0400 Subject: [PATCH 0073/1766] Remove shuffle extension It was noted in an earlier patch that all of the positions below needed the Shuffle Detection idea to be solved: 3r4/p3r1pk/PpBb1pRp/1KpPpP1P/2P1P1R1/8/8/8 b - - 32 86 8/8/8/1k6/2p5/p1K5/N2B2r1/8 b - - 59 109 1r4k1/1r1bq3/4p1p1/3pPpPp/pNpN1P1P/P1PnQ3/1PK5/1R3R2 b - - 13 82 5k2/3b4/5p2/p1p1pPp1/PpPpP1Pp/1P1P3P/8/3R1K2 w - - 20 1 But Stockfish has envolved a bit since the Shuffle Detection patch introduction, and this patch proves Stockfish is able to solves these drawn positions without it, even on single core without EGTB. Passed STC LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 14231 W: 3114 L: 2978 D: 8139 http://tests.stockfishchess.org/tests/view/5dbe1a610ebc5925b64f09d9 Passed LTC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 42781 W: 6917 L: 6831 D: 29033 http://tests.stockfishchess.org/tests/view/5dbe24c20ebc5925b64f0a7a Passed VLTC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 32556 W: 4573 L: 4469 D: 23514 http://tests.stockfishchess.org/tests/view/5dbec3830ebc5925b64f11aa Closes https://github.com/official-stockfish/Stockfish/pull/2394 Bench: 4362323 ---------------------------- Example of search by Michael Byrne for the FEN position: q1B5/1P1q4/8/8/8/6R1/8/1K1k4 w - - 0 1 This position is win for white and the only moves that wins is Rg1 - all other moves either draw or lose. With single core and 1024M hash, it is solved without shuffle detection in 38 seconds on my machine (with no EGTB). This was the position that was locked in a loop in the initial shuffle detection patch! ``` dep score nodes time (not shown: tbhits knps seldep) 50 +1.71 298.9M 2:43.63 Rg1+ Kd2 Rg2+ Kc3 Rc2+ Kb3 Rb2+ Kc3 Bxd7 Qf8 Ba4 Qb8 Bd1 Kd4 Rb5 Kc4 Be2+ Kc3 Rb6 Kd4 Bf3 Ke5 Kb2 Kf4 Bd1 Qe5+ Kb1 Qe4+ Ka2 Qd5+ Rb3 Qd2+ Ka3 Qc1+ Kb4 Qc7 Ka4 Qb8 Rb6 Ke5 Kb3 Qg8+ Kb4 Qf8+ Ka5 Qb8 Bb3 Kd4 Kb4 Qf8+ Ka4 Qb8 Ka5 K 49 +1.68 288.5M 2:38.35 Rg1+ Kd2 Rg2+ Kc3 Rc2+ Kb3 Rb2+ Kc3 Bxd7 Qf8 Ba4 Qb8 Bd1 Kd4 Rb5 Kc4 Be2+ Kc3 Rb6 Kd4 Bf3 Ke5 Kb2 Kf4 Bd1 Qe5+ Kb1 Qe4+ Ka2 Qd5+ Rb3 Qd2+ Ka3 Qc1+ Kb4 Qc7 Ka4 Qb8 Rb6 Ke5 Kb3 Qg8+ Kb4 Qf8+ Ka5 Qb8 Bb3 Kd4 Kb4 Ke3 Be6 Ke4 Bc4 Ke 48 +1.78 228.5M 2:01.93 Rg1+ Kd2 Rg2+ Kc3 Rc2+ Kb3 Rb2+ Kc3 Bxd7 Qf8 Ba4 Qb8 Bd1 Kd4 Rb5 Kc4 Be2+ Kc3 Rb6 Kd4 Bf3 Ke5 Kb2 Kf4 Bd1 Qe5+ Kb1 Qe4+ Ka2 Qd5+ Rb3 Qd2+ Ka3 Qa5+ Kb2 Qe5+ Ka2 Qb8 Rb5 Ke3 Kb1 Ke4 Bb3 Kf4 Be6 Ke3 Rb4 Kd3 Kb2 Ke3 Bd5 Qe5+ Kc2 Qh 46 +1.49 198.4M 1:44.89 Rg1+ Kd2 Rg2+ Kc3 Rc2+ Kb3 Rb2+ Kc3 Bxd7 Qf8 Ba4 Qb8 Bd1 Kd4 Rb5 Kc4 Be2+ Kc3 Rb6 Kd4 Bf3 Ke5 Kb2 Kf4 Bd1 Qe5+ Kb1 Qe4+ Ka2 Qd5+ Rb3 Qd2+ Ka3 Qc1+ Kb4 Qc7 Ka4 Qb8 Rb6 Qe8+ Rb5 Qb8 Bc2 Qa7+ Kb3 Qe3+ Kc4 Qe6+ Kb4 Qd6+ Kb3 Qb8 Rb4 45 +1.45 154.5M 1:20.75 Rg1+ Kd2 Rg2+ Kc3 Rc2+ Kb3 Rb2+ Kc3 Bxd7 Qf8 Ba4 Qb8 Bd1 Kd4 Rb5 Kc4 Be2+ Kc3 Rb6 Kd4 Bf3 Ke3 Bg2 Kd4 Rb5 Kc4 Bf1+ Kd4 Kb2 Qh2+ Kb3 Qg3+ Ka4 Qb8 Be2 Ke3 Bc4 Kf4 Kb4 Qd6+ Kc3 Qb8 Kc2 Ke4 Be6 Qh2+ Kb3 Qg3+ Ka4 Qb8 Bb3 Kd4 Bd5 Ke3 44 +1.36 141.9M 1:14.40 Rg1+ Kd2 Rg2+ Kc3 Rc2+ Kb3 Rb2+ Kc3 Bxd7 Qf8 Ba4 Qb8 Bd1 Qd6 Rc2+ Kd3 Be2+ Ke3 Rb2 Qb8 Bd1 Ke4 Rb5 Kd4 Bf3 Kc4 Be2+ Kc3 Rb6 Kd2 Bc4 Kc3 Bd5 Kd4 Bg2 Ke5 Kb2 Kd4 Rb5 Kc4 Bf1+ Kd4 Be2 Ke4 Bc4 Qh2+ Kb3 Qg3+ Ka4 Qb8 Bd5+ Kd4 Be6 Ke4 43 +1.36 134.1M 1:10.46 Rg1+ Kd2 Rg2+ Kc3 Rc2+ Kb3 Rb2+ Kc3 Bxd7 Qf8 Ba4 Qb8 Bd1 Qd6 Rc2+ Kd3 Be2+ Ke3 Rb2 Qb8 Bd1 Ke4 Rb5 Kd4 Bf3 Kc4 Be2+ Kc3 Rb6 Kd2 Bc4 Kc3 Be6 Kd4 Rb5 Kc3 Bf7 Kd4 Kb2 Ke4 Kb3 Kf4 Kc3 Ke4 Kb2 Qh2+ Kb3 Qg3+ Ka4 Qb8 Rb4+ Ke5 Rb6 Kf4 42 +1.36 118.7M 1:01.60 Rg1+ Kd2 Rg2+ Kc3 Rc2+ Kb3 Rb2+ Kc3 Bxd7 Qf8 Ba4 Qb8 Bd1 Qd6 Rc2+ Kd3 Be2+ Ke3 Rb2 Qb8 Bd1 Ke4 Rb5 Kd4 Bf3 Kc4 Be2+ Kc3 Rb6 Kd2 Bc4 Kc3 Be6 Kd4 Rb5 Kc3 Bf7 Kd4 Kb2 Ke4 Bc4 Qh2+ Kb3 Qg3+ Ka4 Qb8 Bd5+ Kd4 Bb3 Qa7+ Kb4 Qb8 Bc4 Ke4 41 +1.38 110.3M 0:56.80 Rg1+ Kd2 Rg2+ Kc3 Rc2+ Kb3 Rb2+ Kc3 Bxd7 Qf8 Ba4 Qb8 Bd1 Qd6 Rc2+ Kd3 Be2+ Ke3 Rb2 Qb8 Bd1 Ke4 Rb5 Kd4 Bf3 Kc4 Be2+ Kc3 Rb6 Kd2 Bc4 Kc3 Be6 Kd4 Rb5 Kc3 Bd5 Kd4 Ba2 Ke4 Be6 Kd4 Kb2 Qh2+ Kb3 Qb8 Bc4 Ke3 Kc3 Qh8+ Kb4 Qb2+ Ka4 Qa1+ 39 +1.25 87.3M 0:44.48 Rg1+ Kd2 Rg2+ Kc3 Rc2+ Kb3 Rb2+ Kc3 Bxd7 Qf8 Ba4 Qb8 Bd1 Kd4 Rb5 Kc4 Be2+ Kc3 Rb6 Kd4 Bf3 Ke5 Kb2 Kf4 Bd1 Kg5 Kb1 Kf5 Bb3 Ke5 Kb2 Kd4 Rb5 Qh2+ Bc2 Qb8 Bd1 Kc4 Be2+ Kd4 Kc2 Ke3 Bd1 Kd4 Kb3 Qg3+ Ka4 Qb8 Bb3 Kc3 Rb6 Kd4 Kb5 Ke5 K 38 +1.25 82.0M 0:41.90 Rg1+ Kd2 Rg2+ Kc3 Rc2+ Kb3 Rb2+ Kc3 Bxd7 Qf8 Ba4 Qb8 Bd1 Kd4 Rb5 Kc4 Be2+ Kc3 Rb6 Kd4 Bf3 Ke5 Kb2 Kf4 Bd1 Kg5 Kb1 Kf5 Bb3 Ke5 Kb2 Kd4 Rb5 Qh2+ Bc2 Qb8 Kb3 Qg3+ Ka4 Qb8 Bb3 Kc3 Rb6 Kd4 Kb5 Ke5 Kb4 Kd4 Be6 Kd3 Bd5 Kd4 Bf3 Ke5 Be 37 +0.13 79.3M 0:40.44 Rg1+ Kd2 Rg2+ Kc3 Rc2+ Kb3 Rb2+ Kc3 Bxd7 Qf8 Ba4 Qb8 Bd1 Kc4 Bf3 Kd4 Rb5 Kc4 Rb6 Kd4 Rb2 Ke5 Rb3 Kd6 Rb5 Ke6 Rb4 Kd6 Kc2 Kc5 Kb3 Kd6 Be4 Ke7 Kc3 Qc7+ Kd3 Qg3+ Kc2 Qf2+ Kb3 Qe3+ Ka2 Qa7+ Kb2 Qb8 Kb3 Kd6 Bf3 Qg8+ Ka3 Kc7 b8=R Qx 37 +0.67! 78.3M 0:39.90 Rg1+! 37 +0.47! 77.0M 0:39.18 Rg1+! 37 +0.32! 76.8M 0:39.11 Rg1+! 37 +0.23! 76.8M 0:39.07 Rg1+! 36 +0.57! 76.1M 0:38.72 Rg1+! 36 +0.37! 75.8M 0:38.59 Rg1+! 36 +0.23! 75.7M 0:38.51 Rg1+! 36 +0.13! 75.6M 0:38.49 Rg1+! 35 +0.03? 58.0M 0:29.84 bxa8=Q Qb5+? ``` --- src/search.cpp | 7 ------- src/thread.cpp | 2 +- src/thread.h | 2 +- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index b571cdf1787..4b90ab1c973 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -999,13 +999,6 @@ namespace { && (pos.is_discovery_check_on_king(~us, move) || pos.see_ge(move))) extension = 1; - // Shuffle extension - else if ( PvNode - && pos.rule50_count() > 18 - && depth < 3 - && ++thisThread->shuffleExts < thisThread->nodes.load(std::memory_order_relaxed) / 4) // To avoid too many extensions - extension = 1; - // Passed pawn extension else if ( move == ss->killers[0] && pos.advanced_pawn_push(move) diff --git a/src/thread.cpp b/src/thread.cpp index 680cd3adb8c..6eb00d63a64 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -207,7 +207,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states, for (Thread* th : *this) { - th->shuffleExts = th->nodes = th->tbHits = th->nmpMinPly = 0; + th->nodes = th->tbHits = th->nmpMinPly = 0; th->rootDepth = th->completedDepth = 0; th->rootMoves = rootMoves; th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th); diff --git a/src/thread.h b/src/thread.h index 0517afc5d14..087ed3820fd 100644 --- a/src/thread.h +++ b/src/thread.h @@ -60,7 +60,7 @@ class Thread { Pawns::Table pawnsTable; Material::Table materialTable; - size_t pvIdx, pvLast, shuffleExts; + size_t pvIdx, pvLast; int selDepth, nmpMinPly; Color nmpColor; std::atomic nodes, tbHits, bestMoveChanges; From 3804effb341b3008326a1613923177eb83d02826 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Tue, 5 Nov 2019 03:06:41 +0800 Subject: [PATCH 0074/1766] Rook PSQT Tuned This patch uses about half the changes of the SPSA tuning run: http://tests.stockfishchess.org/tests/view/5dba93d30ebc5925b64ed3bf About a month ago, xoto10's patch raised the mg value of the third rank center files from -1 to 7 to encourage rook lifts to the third rank. About three days later, Rocky's patch lowered this value from 7 to 3. This patch raises that again from 3 to 12 and ends up greater than the original rook lift patch. Passed STC: LLR: 2.95 (-2.94,2.94) [-1.50,4.50] Total: 104094 W: 22573 L: 22161 D: 59360 http://tests.stockfishchess.org/tests/view/5dbc77f20ebc5925b64ef1d0 Passed LTC: LLR: 2.96 (-2.94,2.94) [0.00,3.50] Total: 168291 W: 27410 L: 26777 D: 114104 http://tests.stockfishchess.org/tests/view/5dbd9f1e0ebc5925b64f0647 Bench: 4707799 --- src/psqt.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/psqt.cpp b/src/psqt.cpp index 60d17ad29e5..6469c43a1db 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -59,14 +59,14 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { { S(-48,-46), S( 1,-42), S(-14,-37), S(-23,-24) } }, { // Rook - { 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) } + { S(-26, -7), S(-19,-15), S( -8,-10), S(-1,-10) }, + { S(-21,-19), S(-24, -7), S( -6, 0), S( 5, -1) }, + { S(-29, -1), S(-20, -4), S( 6, 8), S(12,-19) }, + { S(-14, -1), S( -1, -3), S(-12, -8), S(-7, 10) }, + { S(-37, 1), S( -6, 13), S( 0, 14), S(10, 4) }, + { S(-16, 9), S( -1, 15), S( 4, -8), S( 1, 16) }, + { S( -1, 2), S( 10, 4), S( 13, 23), S(23, -4) }, + { S( -2, 21), S(-21, -3), S( 4, 26), S(-1, 20) } }, { // Queen { S( 3,-69), S(-5,-57), S(-5,-47), S( 4,-26) }, From 9f312c80d918c6f669dcf83df3d4332e02bfa1d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Wed, 6 Nov 2019 11:06:53 +0100 Subject: [PATCH 0075/1766] Revert "Rook PSQT Tuned" This reverts the previous commit. The PSQT changes in this previous commit originated from tests against quite an old version of master which did not include the other PSQT changes of 474d133 for the other pieces, and there might be some unknown interactions between the PSQT tables. So we made a non-regression test of the last commit against the last-but-one commit. This test failed, leading to the revert decision. Failed non-regression test: LLR: -2.96 (-2.94,2.94) [-3.00,1.00] Total: 95536 W: 15047 L: 15347 D: 65142 http://tests.stockfishchess.org/tests/view/5dc0ba1d0ebc5904493b0112 Closes https://github.com/official-stockfish/Stockfish/pull/2395 Bench: 4362323 --- src/psqt.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/psqt.cpp b/src/psqt.cpp index 6469c43a1db..60d17ad29e5 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -59,14 +59,14 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { { S(-48,-46), S( 1,-42), S(-14,-37), S(-23,-24) } }, { // Rook - { S(-26, -7), S(-19,-15), S( -8,-10), S(-1,-10) }, - { S(-21,-19), S(-24, -7), S( -6, 0), S( 5, -1) }, - { S(-29, -1), S(-20, -4), S( 6, 8), S(12,-19) }, - { S(-14, -1), S( -1, -3), S(-12, -8), S(-7, 10) }, - { S(-37, 1), S( -6, 13), S( 0, 14), S(10, 4) }, - { S(-16, 9), S( -1, 15), S( 4, -8), S( 1, 16) }, - { S( -1, 2), S( 10, 4), S( 13, 23), S(23, -4) }, - { S( -2, 21), S(-21, -3), S( 4, 26), S(-1, 20) } + { 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 5ae195ee7e3ccac01b8145f9b7022e352a288f07 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 26 Oct 2019 16:34:19 +0200 Subject: [PATCH 0076/1766] Fix incorrect mate score. Current master 648c7ec25db2040c0af34dd846dfa3f57af5ad0a will generate an incorrect mate score for: ``` setoption name Hash value 8 setoption name Threads value 1 position fen 8/1p2KP2/1p4q1/1Pp5/2P5/N1Pp1k2/3P4/1N6 b - - 76 40 go depth 49 ``` even though the position is a draw. Generally, SF tries to display only proven mate scores, so this is a bug. This was posted http://www.talkchess.com/forum3/viewtopic.php?f=2&t=72166 by Uri Blass, with the correct analysis that this must be related to the 50 moves draw rule being ignored somewhere. Indeed, this is possible as positions and there eval are stored in the TT, without reference to the 50mr counter. Depending on the search path followed a position can thus be mate or draw in the TT (GHI or Graph history interaction). Therefore, to prove mate lines, the TT content has to be used with care. Rather than ignoring TT content in general or for mate scores (which impact search or mate finding), it is possible to be more selective. In particular, @WOnder93 suggested to only ignore the TT if the 50mr draw ply is closer than the mate ply. This patch implements this idea, by clamping the eval in the TT to +-VALUE_MATED_IN_MAX_PLY. This retains the TTmove, but causes a research of these lines (with the current 50mr counter) as needed. This patch hardly ever affects search (as indicated by the unchanged bench), but fixes the testcase. As the conditions are very specific, also mate finding will almost never be less efficient (testing welcome). It was also shown to pass STC and LTC non-regression testing, in a form using if/then/else instead of ternary operators: STC: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 93605 W: 15346 L: 15340 D: 62919 http://tests.stockfishchess.org/tests/view/5db45bb00ebc5908127538d4 LTC: LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 33873 W: 7359 L: 7261 D: 19253 http://tests.stockfishchess.org/tests/view/5db4c8940ebc5902d6b146fc closes https://github.com/official-stockfish/Stockfish/issues/2370 Bench: 4362323 --- src/search.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 4b90ab1c973..be296443f3d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -150,7 +150,7 @@ namespace { 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); + Value value_from_tt(Value v, int ply, int r50c); void update_pv(Move* pv, Move move, Move* childPv); void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus); void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus); @@ -661,7 +661,7 @@ namespace { excludedMove = ss->excludedMove; posKey = pos.key() ^ Key(excludedMove << 16); // Isn't a very good hash tte = TT.probe(posKey, ttHit); - ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; + ttValue = ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] : ttHit ? tte->move() : MOVE_NONE; ttPv = PvNode || (ttHit && tte->is_pv()); @@ -899,7 +899,7 @@ namespace { search(pos, ss, alpha, beta, depth - 7, cutNode); tte = TT.probe(posKey, ttHit); - ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; + ttValue = ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttMove = ttHit ? tte->move() : MOVE_NONE; } @@ -1344,7 +1344,7 @@ namespace { // Transposition table lookup posKey = pos.key(); tte = TT.probe(posKey, ttHit); - ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE; + ttValue = ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttMove = ttHit ? tte->move() : MOVE_NONE; pvHit = ttHit && tte->is_pv(); @@ -1530,11 +1530,11 @@ namespace { // from the transposition table (which refers to the plies to mate/be mated // from current position) to "plies to mate/be mated from the root". - Value value_from_tt(Value v, int ply) { + Value value_from_tt(Value v, int ply, int r50c) { return v == VALUE_NONE ? VALUE_NONE - : v >= VALUE_MATE_IN_MAX_PLY ? v - ply - : v <= VALUE_MATED_IN_MAX_PLY ? v + ply : v; + : v >= VALUE_MATE_IN_MAX_PLY ? VALUE_MATE - v > 99 - r50c ? VALUE_MATE_IN_MAX_PLY : v - ply + : v <= VALUE_MATED_IN_MAX_PLY ? VALUE_MATE + v > 99 - r50c ? VALUE_MATED_IN_MAX_PLY : v + ply : v; } From 9b8b259388f15d9f669cfc526a2bb7c5a5b1ee71 Mon Sep 17 00:00:00 2001 From: Alain SAVARD Date: Sun, 27 Oct 2019 16:16:26 -0400 Subject: [PATCH 0077/1766] Sequencing tweak in tbprobe() Followup of "issue" #2372, which was in fact a small speed-up proposal by user @d3vv for the probing code of tablebases. See comments on this issue where it was proven by Alin Savard that the proposed change is more efficient on average than master on all type of sequences it will usually be called. Note that on gcc 4.3, this will produce a bogus warning which was solved with ulterior gcc versions: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=43949 Closes https://github.com/official-stockfish/Stockfish/issues/2372 Closes https://github.com/official-stockfish/Stockfish/pull/2379 Non functional change --- src/syzygy/tbprobe.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index a9378b4ba09..c04534921ea 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -730,8 +730,8 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu // Then we reorder the pieces to have the same sequence as the one stored // in pieces[i]: the sequence that ensures the best compression. - for (int i = leadPawnsCnt; i < size; ++i) - for (int j = i; j < size; ++j) + for (int i = leadPawnsCnt; i < size - 1; ++i) + for (int j = i + 1; j < size; ++j) if (d->pieces[i] == pieces[j]) { std::swap(pieces[i], pieces[j]); From 44b6697f19ed45a6fb3f542acc83fc5d7111f375 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 9 Nov 2019 06:56:18 +0100 Subject: [PATCH 0078/1766] Remove explicit moveCount pruning The removed lines approximately duplicate equivalent logic in the movePicker. Adjust the futility_move_count to componsate for some difference (the movePicker prunes one iteration of the move loop later). Passed STC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 8114 W: 1810 L: 1663 D: 4641 http://tests.stockfishchess.org/tests/view/5dc6afe60ebc5902562bd318 Passed LTC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 89956 W: 14473 L: 14460 D: 61023 http://tests.stockfishchess.org/tests/view/5dc6bdcf0ebc5902562bd3c0 Closes https://github.com/official-stockfish/Stockfish/pull/2407 Bench: 4256440 --------------------- How to continue from there? It would be interesting to see if we can extract some Elo gain from the new futility_move_count formula, for instance by somehow incorporating the final -1 in the 5 constant, or adding a linear term to the quadratics... ``` futility_move_count = (5 + depth * depth) * (1 + improving) / 2 - 1 ``` --- src/search.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index be296443f3d..f9579ae0ab4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -76,7 +76,7 @@ namespace { } constexpr int futility_move_count(bool improving, Depth depth) { - return (5 + depth * depth) * (1 + improving) / 2; + return (5 + depth * depth) * (1 + improving) / 2 - 1; } // History and stats update bonus, based on depth @@ -1024,10 +1024,6 @@ namespace { && !givesCheck && (!pos.advanced_pawn_push(move) || pos.non_pawn_material(~us) > BishopValueMg)) { - // Move count based pruning - if (moveCountPruning) - continue; - // Reduced depth of the next LMR search int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), 0); From 9ab2590963b029092a378c7a69d1c80c43999db8 Mon Sep 17 00:00:00 2001 From: Miguel Lahoz Date: Sun, 10 Nov 2019 17:49:06 +0800 Subject: [PATCH 0079/1766] Shallow depth pruning on NonPV advanced pawn push Usually advanced pawn pushes are not considered in shallow depth pruning because it is risky to do so with possible promotions near the horizon. However, this heuristic is not also beneficial on NonPV nodes since we can afford to take slightly more risk on less important nodes. STC: LLR: 2.95 (-2.94,2.94) [-1.50,4.50] Total: 54530 W: 11955 L: 11686 D: 30889 http://tests.stockfishchess.org/tests/view/5dc7dda30ebc5902ea57efd0 LTC: LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 77336 W: 12786 L: 12399 D: 52151 http://tests.stockfishchess.org/tests/view/5dc8050d0ebc5902ea57f491 Closes https://github.com/official-stockfish/Stockfish/pull/2408 Bench: 4422068 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index f9579ae0ab4..b484dcbe062 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1022,7 +1022,7 @@ namespace { if ( !captureOrPromotion && !givesCheck - && (!pos.advanced_pawn_push(move) || pos.non_pawn_material(~us) > BishopValueMg)) + && (!PvNode || !pos.advanced_pawn_push(move) || pos.non_pawn_material(~us) > BishopValueMg)) { // Reduced depth of the next LMR search int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), 0); From a1319751700272055e0cf5649292ea4bbaabd6ca Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Tue, 12 Nov 2019 09:22:09 +0800 Subject: [PATCH 0080/1766] Rank-based outposts Introduce OutpostRank[RANK_NB] which contains a bonus according to the rank of the outpost. We use it for the primary Outpost bonus. The values are based on the trends of the SPSA tuning run with some manual tweaks. Passed STC: LLR: 2.96 (-2.94,2.94) [-1.50,4.50] Total: 27454 W: 6059 L: 5869 D: 15526 http://tests.stockfishchess.org/tests/view/5dcadba20ebc590256922f09 Passed LTC: LLR: 2.94 (-2.94,2.94) [0.00,3.50] Total: 57950 W: 9443 L: 9112 D: 39395 http://tests.stockfishchess.org/tests/view/5dcaea880ebc5902569230bc Bench: 4778405 ---------------------------- The inspiration for this patch came from Stefan Geschwentner's attempt of modifying BishopPawns into a rank-based penalty. Michael Stembera suggested that maybe the S(0, 0) ranks (3rd, 7th and also maybe 8th) can still be tuned. This would expand our definition of Outpost and OutpostRanks would be removed altogether. Special thanks to Mark Tenzer for all the help and excellent suggestions. --- src/evaluate.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index cefd9db427f..07bacf8dcc0 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -125,6 +125,11 @@ namespace { constexpr Score PassedRank[RANK_NB] = { S(0, 0), S(10, 28), S(17, 33), S(15, 41), S(62, 72), S(168, 177), S(276, 260) }; + + // OutpostRank[Rank] contains a bonus according to the rank of the outpost + constexpr Score OutpostRank[RANK_NB] = { + S(0, 0), S(0, 0), S(0, 0), S(28, 18), S(30, 24), S(32, 19) + }; // Assorted bonuses and penalties constexpr Score BishopPawns = S( 3, 7); @@ -292,7 +297,7 @@ namespace { // Bonus if piece is on an outpost square or can reach one bb = OutpostRanks & attackedBy[Us][PAWN] & ~pe->pawn_attacks_span(Them); if (s & bb) - score += Outpost * (Pt == KNIGHT ? 2 : 1); + score += OutpostRank[relative_rank(Us, s)] * (Pt == KNIGHT ? 2 : 1); else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us)) score += Outpost; From a00a336946fa9e6dcfa39f8b656413d2de032a89 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Tue, 12 Nov 2019 18:36:12 +0100 Subject: [PATCH 0081/1766] Prune before extension Switch execution order in search: do move pruning before extension detection. STC: LLR: 2.96 (-2.94,2.94) [-1.50,4.50] Total: 5762 W: 1307 L: 1181 D: 3274 http://tests.stockfishchess.org/tests/view/5dcc56e90ebc59025bcbb833 LTC: LLR: 2.96 (-2.94,2.94) [0.00,3.50] Total: 72956 W: 11959 L: 11585 D: 49412 http://tests.stockfishchess.org/tests/view/5dcc62840ebc59025bcbb96f Closes https://github.com/official-stockfish/Stockfish/pull/2413 Bench: 4532366 --- src/evaluate.cpp | 2 +- src/search.cpp | 80 +++++++++++++++++++++++++----------------------- 2 files changed, 42 insertions(+), 40 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 07bacf8dcc0..dbd2d6d840d 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -125,7 +125,7 @@ namespace { constexpr Score PassedRank[RANK_NB] = { S(0, 0), S(10, 28), S(17, 33), S(15, 41), S(62, 72), S(168, 177), S(276, 260) }; - + // OutpostRank[Rank] contains a bonus according to the rank of the outpost constexpr Score OutpostRank[RANK_NB] = { S(0, 0), S(0, 0), S(0, 0), S(28, 18), S(30, 24), S(32, 19) diff --git a/src/search.cpp b/src/search.cpp index b484dcbe062..e75db243cfc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -955,7 +955,45 @@ namespace { movedPiece = pos.moved_piece(move); givesCheck = pos.gives_check(move); - // Step 13. Extensions (~70 Elo) + // Calculate new depth for this move + newDepth = depth - 1; + + // Step 13. Pruning at shallow depth (~170 Elo) + if ( !rootNode + && pos.non_pawn_material(us) + && bestValue > VALUE_MATED_IN_MAX_PLY) + { + // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold + moveCountPruning = moveCount >= futility_move_count(improving, depth); + + if ( !captureOrPromotion + && !givesCheck + && (!PvNode || !pos.advanced_pawn_push(move) || pos.non_pawn_material(~us) > BishopValueMg)) + { + // Reduced depth of the next LMR search + 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) + && (*contHist[0])[movedPiece][to_sq(move)] < CounterMovePruneThreshold + && (*contHist[1])[movedPiece][to_sq(move)] < CounterMovePruneThreshold) + continue; + + // Futility pruning: parent node (~2 Elo) + if ( lmrDepth < 6 + && !inCheck + && ss->staticEval + 250 + 211 * lmrDepth <= alpha) + continue; + + // Prune moves with negative SEE (~10 Elo) + if (!pos.see_ge(move, Value(-(31 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth))) + continue; + } + else if (!pos.see_ge(move, Value(-199) * depth)) // (~20 Elo) + continue; + } + + // Step 14. Extensions (~70 Elo) // Singular extension search (~60 Elo). If all moves but one fail low on a // search of (alpha-s, beta-s), and just one fails high on (alpha, beta), @@ -1009,44 +1047,8 @@ namespace { if (type_of(move) == CASTLING) extension = 1; - // Calculate new depth for this move - newDepth = depth - 1 + extension; - - // Step 14. Pruning at shallow depth (~170 Elo) - if ( !rootNode - && pos.non_pawn_material(us) - && bestValue > VALUE_MATED_IN_MAX_PLY) - { - // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold - moveCountPruning = moveCount >= futility_move_count(improving, depth); - - if ( !captureOrPromotion - && !givesCheck - && (!PvNode || !pos.advanced_pawn_push(move) || pos.non_pawn_material(~us) > BishopValueMg)) - { - // Reduced depth of the next LMR search - 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) - && (*contHist[0])[movedPiece][to_sq(move)] < CounterMovePruneThreshold - && (*contHist[1])[movedPiece][to_sq(move)] < CounterMovePruneThreshold) - continue; - - // Futility pruning: parent node (~2 Elo) - if ( lmrDepth < 6 - && !inCheck - && ss->staticEval + 250 + 211 * lmrDepth <= alpha) - continue; - - // Prune moves with negative SEE (~10 Elo) - if (!pos.see_ge(move, Value(-(31 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth))) - continue; - } - else if ( !(givesCheck && extension) - && !pos.see_ge(move, Value(-199) * depth)) // (~20 Elo) - continue; - } + // Add extension to new depth + newDepth += extension; // Speculative prefetch as early as possible prefetch(TT.first_entry(pos.key_after(move))); From 3468138210cacdf46051e9d5bde6e28effee2cdc Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sat, 16 Nov 2019 14:53:11 +0300 Subject: [PATCH 0082/1766] Introduce king flank defenders This patch implements what we have been trying for quite some time - dependance of kingdanger on balance of attackers and defenders of king flank, to avoid overestimate attacking power if the opponent has enough defenders of king position. We already have some form of it in bishop and knight defenders - this is further work in this direction. What to do based on this? 1) constant 4 is arbitrary, maybe it is not optimal 2) maybe we can use quadratic formula as in kingflankattack 3) simplification into alrealy existing terms is always a possibility :) 4) overall kingdanger tuning always can be done. passed STC: http://tests.stockfishchess.org/tests/view/5dcf40560ebc590256325f30 LLR: 2.96 (-2.94,2.94) [-1.50,4.50] Total: 26298 W: 5819 L: 5632 D: 14847 passed LTC: http://tests.stockfishchess.org/tests/view/5dcfa5760ebc590256326464 LLR: 2.96 (-2.94,2.94) [0.00,3.50] Total: 30600 W: 5042 L: 4784 D: 20774 Closes https://github.com/official-stockfish/Stockfish/pull/2415 Bench: 4496847 --- src/evaluate.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index dbd2d6d840d..2b7ab39678e 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -380,7 +380,7 @@ namespace { constexpr Bitboard Camp = (Us == WHITE ? AllSquares ^ Rank6BB ^ Rank7BB ^ Rank8BB : AllSquares ^ Rank1BB ^ Rank2BB ^ Rank3BB); - Bitboard weak, b1, b2, safe, unsafeChecks = 0; + Bitboard weak, b1, b2, b3, safe, unsafeChecks = 0; Bitboard rookChecks, queenChecks, bishopChecks, knightChecks; int kingDanger = 0; const Square ksq = pos.square(Us); @@ -439,19 +439,22 @@ namespace { else unsafeChecks |= knightChecks; - // Find the squares that opponent attacks in our king flank, and the squares - // which are attacked twice in that flank. + // Find the squares that opponent attacks in our king flank, the squares + // which they attack twice in that flank, and the squares that we defend. b1 = attackedBy[Them][ALL_PIECES] & KingFlank[file_of(ksq)] & Camp; b2 = b1 & attackedBy2[Them]; + b3 = attackedBy[Us][ALL_PIECES] & KingFlank[file_of(ksq)] & Camp; - int kingFlankAttacks = popcount(b1) + popcount(b2); + int kingFlankAttack = popcount(b1) + popcount(b2); + int kingFlankDefense = popcount(b3); kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them] + 185 * popcount(kingRing[Us] & weak) + 148 * popcount(unsafeChecks) + 98 * popcount(pos.blockers_for_king(Us)) + 69 * kingAttacksCount[Them] - + 3 * kingFlankAttacks * kingFlankAttacks / 8 + + 4 * (kingFlankAttack - kingFlankDefense) + + 3 * kingFlankAttack * kingFlankAttack / 8 + mg_value(mobility[Them] - mobility[Us]) - 873 * !pos.count(Them) - 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING]) @@ -468,7 +471,7 @@ namespace { score -= PawnlessFlank; // Penalty if king flank is under attack, potentially moving toward the king - score -= FlankAttacks * kingFlankAttacks; + score -= FlankAttacks * kingFlankAttack; if (T) Trace::add(KING, Us, score); From fe124896b241b4791454fd151da10101ad48f6d7 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 15 Nov 2019 18:16:27 +0100 Subject: [PATCH 0083/1766] Use exploration rate for reductions This patch measures how frequently search is exploring new configurations. This is done be computing a running average of ttHit. The ttHitAverage rate is somewhat low (e.g. 30% for startpos) in the normal case, while it can be very high if no progress is made (e.g. 90% for the fortress I used for testing). This information can be used to influence search. In this patch, by adjusting reductions if the rate > 50%. A first version (using a low ttHitAverageResolution and this 50% threshold) passed testing: STC LLR: 2.96 (-2.94,2.94) [-1.50,4.50] Total: 26425 W: 5837 L: 5650 D: 14938 http://tests.stockfishchess.org/tests/view/5dcede8b0ebc5902563258fa LTC LLR: 2.96 (-2.94,2.94) [0.00,3.50] Total: 32313 W: 5392 L: 5128 D: 21793 http://tests.stockfishchess.org/tests/view/5dcefb1f0ebc590256325c0e However, as discussed in pull request 2414, using a larger ttHitAverageResolution gives a better approximation of the underlying distributions. This needs a slight adjustment for the threshold as the new distributions are shifted a bit compared to the older ones, and this threshold seemingly is sensitive (we used 0.53125 here). https://github.com/official-stockfish/Stockfish/pull/2414 This final version also passed testing, and is used for the patch: STC LLR: 2.95 (-2.94,2.94) [-1.50,4.50] Total: 16025 W: 3555 L: 3399 D: 9071 http://tests.stockfishchess.org/tests/view/5dd070b90ebc5902579e20c2 LTC LLR: 2.96 (-2.94,2.94) [0.00,3.50] Total: 37576 W: 6277 L: 5998 D: 25301 http://tests.stockfishchess.org/tests/view/5dd0f58e6f544e798086f224 Closes https://github.com/official-stockfish/Stockfish/pull/2414 Bench: 4989584 --- src/search.cpp | 11 +++++++++++ src/thread.h | 1 + 2 files changed, 12 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index e75db243cfc..24bcb9ad2af 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -61,6 +61,9 @@ namespace { // Different node types, used as a template parameter enum NodeType { NonPV, PV }; + constexpr uint64_t ttHitAverageWindow = 4096; + constexpr uint64_t ttHitAverageResolution = 1024; + // Razor and futility margins constexpr int RazorMargin = 661; Value futility_margin(Depth d, bool improving) { @@ -363,6 +366,7 @@ void Thread::search() { multiPV = std::max(multiPV, (size_t)4); multiPV = std::min(multiPV, rootMoves.size()); + ttHitAverage = ttHitAverageWindow * ttHitAverageResolution / 2; int ct = int(Options["Contempt"]) * PawnValueEg / 100; // From centipawns @@ -665,6 +669,9 @@ namespace { ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] : ttHit ? tte->move() : MOVE_NONE; ttPv = PvNode || (ttHit && tte->is_pv()); + // thisThread->ttHitAverage can be used to approximate the running average of ttHit + thisThread->ttHitAverage = (ttHitAverageWindow - 1) * thisThread->ttHitAverage / ttHitAverageWindow + + ttHitAverageResolution * ttHit; // At non-PV nodes we check for an early TT cutoff if ( !PvNode @@ -1082,6 +1089,10 @@ namespace { { Depth r = reduction(improving, depth, moveCount); + // Decrease reduction if the ttHit running average is large + if (thisThread->ttHitAverage > 544 * ttHitAverageResolution * ttHitAverageWindow / 1024) + r--; + // Reduction if other threads are searching this position. if (th.marked()) r++; diff --git a/src/thread.h b/src/thread.h index 087ed3820fd..2b1f92b2092 100644 --- a/src/thread.h +++ b/src/thread.h @@ -61,6 +61,7 @@ class Thread { Pawns::Table pawnsTable; Material::Table materialTable; size_t pvIdx, pvLast; + uint64_t ttHitAverage; int selDepth, nmpMinPly; Color nmpColor; std::atomic nodes, tbHits, bestMoveChanges; From e0f42aa956e731b5faae0585f5cc47da23fbe53c Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sun, 17 Nov 2019 21:47:17 +0300 Subject: [PATCH 0084/1766] Simplify advanced pawn push pruning This patch simplifies away all conditions related to advanced pawn pushes in shallow depth pruning. Idea is based on fact that in master we have advanced pawn pushes not being pruned what we are only in PV node and when non-pawn material of opponent is > Bishop, so pretty rarely. With this patch we will have all pruning heuristics working for this moves as for every other move. STC LLR: 2.94 (-2.94,2.94) [-3.00,1.00] Total: 159143 W: 34271 L: 34418 D: 90454 http://tests.stockfishchess.org/tests/view/5dcdb3110ebc5902563249d7 LTC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 63900 W: 10375 L: 10322 D: 43203 http://tests.stockfishchess.org/tests/view/5dd05e820ebc5902579e1fb8 Closes https://github.com/official-stockfish/Stockfish/pull/2416 bench 4897149 --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 24bcb9ad2af..b54ff19648d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -974,8 +974,7 @@ namespace { moveCountPruning = moveCount >= futility_move_count(improving, depth); if ( !captureOrPromotion - && !givesCheck - && (!PvNode || !pos.advanced_pawn_push(move) || pos.non_pawn_material(~us) > BishopValueMg)) + && !givesCheck) { // Reduced depth of the next LMR search int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), 0); From 37698b0396e26a0f1364912dd1feae5dae5892ef Mon Sep 17 00:00:00 2001 From: Alain SAVARD Date: Sat, 16 Nov 2019 14:42:47 -0500 Subject: [PATCH 0085/1766] Outpost Endgame values Remove the recent rank based Outpost array by using a weighted average value computed using a frequency analysis by rank from a large set of middle game positions. The higher eg values introduced by the new Outpost array (which were about twice the previous masters) are thus preserved. STC http://tests.stockfishchess.org/tests/view/5dd05c870ebc5902579e1f7f LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 42466 W: 9232 L: 9151 D: 24083 LTC http://tests.stockfishchess.org/tests/view/5dd146e342928ff08153dab1 LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 66968 W: 10921 L: 10873 D: 45174 Closes https://github.com/official-stockfish/Stockfish/pull/2418 Bench: 5103360 --- src/evaluate.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 2b7ab39678e..7760f70538d 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -126,11 +126,6 @@ namespace { S(0, 0), S(10, 28), S(17, 33), S(15, 41), S(62, 72), S(168, 177), S(276, 260) }; - // OutpostRank[Rank] contains a bonus according to the rank of the outpost - constexpr Score OutpostRank[RANK_NB] = { - S(0, 0), S(0, 0), S(0, 0), S(28, 18), S(30, 24), S(32, 19) - }; - // Assorted bonuses and penalties constexpr Score BishopPawns = S( 3, 7); constexpr Score CorneredBishop = S( 50, 50); @@ -140,10 +135,11 @@ namespace { constexpr Score KnightOnQueen = S( 16, 12); constexpr Score LongDiagonalBishop = S( 45, 0); constexpr Score MinorBehindPawn = S( 18, 3); - constexpr Score Outpost = S( 32, 10); + constexpr Score Outpost = S( 30, 21); constexpr Score PassedFile = S( 11, 8); constexpr Score PawnlessFlank = S( 17, 95); constexpr Score RestrictedPiece = S( 7, 7); + constexpr Score ReachableOutpost = S( 32, 10); constexpr Score RookOnQueenFile = S( 7, 6); constexpr Score SliderOnQueen = S( 59, 18); constexpr Score ThreatByKing = S( 24, 89); @@ -242,7 +238,7 @@ namespace { // Init our king safety tables Square s = make_square(clamp(file_of(ksq), FILE_B, FILE_G), clamp(rank_of(ksq), RANK_2, RANK_7)); - kingRing[Us] = s | PseudoAttacks[KING][s]; + kingRing[Us] = PseudoAttacks[KING][s] | s; kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them)); kingAttacksCount[Them] = kingAttackersWeight[Them] = 0; @@ -296,11 +292,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 (s & bb) - score += OutpostRank[relative_rank(Us, s)] * (Pt == KNIGHT ? 2 : 1); + if (bb & s) + score += Outpost * (Pt == KNIGHT ? 2 : 1); else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us)) - score += Outpost; + score += ReachableOutpost; // Knight and Bishop bonus for being right behind a pawn if (shift(pos.pieces(PAWN)) & s) From 3f4191392c18f08011294aab880c31b15fc6f61c Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Wed, 20 Nov 2019 23:20:20 +0300 Subject: [PATCH 0086/1766] Do lmr for more captures Based on machinery introduced by vondele. Logic behind patch if relatively simple - if we reduce less with high hit rate of transposition table somewhat logical is to reduce more with low hit rate. For example enable all captures for LMR. Threshold 0.375 is arbitrary and can be tweaked :) STC http://tests.stockfishchess.org/tests/view/5dd4d51df531e81cf278eaac LLR: 2.97 (-2.94,2.94) [-1.50,4.50] Total: 16495 W: 3591 L: 3434 D: 9470 LTC http://tests.stockfishchess.org/tests/view/5dd52265f531e81cf278eace LLR: 2.96 (-2.94,2.94) [0.00,3.50] Total: 23598 W: 3956 L: 3716 D: 15926 Closes https://github.com/official-stockfish/Stockfish/pull/2420 Bench: 5067870 --- src/search.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index b54ff19648d..d3f38aae109 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1084,7 +1084,8 @@ namespace { && ( !captureOrPromotion || moveCountPruning || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha - || cutNode)) + || cutNode + || thisThread->ttHitAverage < 384 * ttHitAverageResolution * ttHitAverageWindow / 1024)) { Depth r = reduction(improving, depth, moveCount); From 1fdf1f1ff5800bd8b1b1c33fc281b9731d40583d Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Thu, 21 Nov 2019 03:31:23 +0800 Subject: [PATCH 0087/1766] Simplify endgame factor for opposite colored bishops Stockfish is continually improving. Patches that gain elo in the past may no longer be needed as stockfish improved elsewhere. This patch removes passed pawns count dependence in opposite colored bishops scale factor. We used the mean of passed count pawns (~1.4) to compensate, and changed the base value from 16 to 22. Passed STC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 57879 W: 12657 L: 12607 D: 32615 http://tests.stockfishchess.org/tests/view/5dd1644f42928ff08153dc1e Passed LTC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 121648 W: 19622 L: 19659 D: 82367 http://tests.stockfishchess.org/tests/view/5dd24572ccb823d41d4b47bb Closes https://github.com/official-stockfish/Stockfish/pull/2419 Bench: 5067864 --- src/evaluate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 7760f70538d..eb5719bdad0 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -748,7 +748,7 @@ namespace { { if ( pos.opposite_bishops() && pos.non_pawn_material() == 2 * BishopValueMg) - sf = 16 + 4 * pe->passed_count(); + sf = 22 ; else sf = std::min(sf, 36 + (pos.opposite_bishops() ? 2 : 7) * pos.count(strongSide)); From 87ed9facf16bc9d0af9daf8680801759ce1d8662 Mon Sep 17 00:00:00 2001 From: 31m059 <37052095+31m059@users.noreply.github.com> Date: Sat, 23 Nov 2019 04:03:51 -0500 Subject: [PATCH 0088/1766] King danger: retire attacked-by-bishop defense In a recent commit, "Introduce king flank defenders," a term was introduced by Michael Chaly (@Vizvezdenec) to reduce king danger based on king defenders, i.e., friendly attacks on our King Flank and Camp. This is a powerful idea and broadly applicable to all of our pieces. An earlier, but narrower, version of a similar idea was already coded into king danger, with a term reducing king danger simply if we had a bishop and king attacking the same square -- there is also a similar term for knights, but roughly three times larger. I had attempted to tweak this term's coefficient fairly recently, in a series of tests in early September which increased this coefficient. All failed STC with significantly negative scores. Now that the king flank defenders term has been introduced, it appears that the bishop-defense term can be simplified away without compensation or significant Elo loss. Where do we go from here? This PR is a natural follow-up to "Introduce king flank defenders," which proposed simplification with existing and overlapping terms, such as this one. That PR also mentioned that the coefficient it introduced appeared arbitrary, so perhaps this PR can facilitate a tweak to increase king flank defenders' coefficient. Additionally, this pull request is extremely similar to https://github.com/official-stockfish/Stockfish/pull/1821, which was (coincidentally) merged a year ago, to the day (November 23, 2018). That patch also simplified away a linear king danger tropism term, which was soon after replaced with a quadratic term by @Vizvezdenec (which would not have passed without the simplification). @Vizvezdenec, again by coincidence, has recently been trying to implement a quadratic term, this time for defenders rather than attackers. This history of this evaluation code suggests that this simplification might be enough to help a patch for quadratic king-flank defenders pass. Bench: 4959670 STC: LLR: 2.94 (-2.94,2.94) [-3.00,1.00] Total: 22209 W: 4920 L: 4800 D: 12489 https://tests.stockfishchess.org/tests/view/5dd444d914339111b9b6bed7 LTC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 152107 W: 24658 L: 24743 D: 102706 https://tests.stockfishchess.org/tests/view/5dd4be31f531e81cf278ea9d Interesting discussion on Github about this pull request: https://github.com/official-stockfish/Stockfish/pull/2424 --- This pull request was opened less than one week before the holiday of Thanksgiving here in the United States. In keeping with the holiday tradition of expressing gratitude, I would like to thank our generous CPU donors, talented forum contributors, innovative developers, speedy fishtest approvers, and especially our hardworking server maintainers (@ppigazzini and @tomtor). Thank you all for a year of great Stockfish progress! --- src/evaluate.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index eb5719bdad0..4c876820d06 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -454,7 +454,6 @@ namespace { + mg_value(mobility[Them] - mobility[Us]) - 873 * !pos.count(Them) - 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING]) - - 35 * bool(attackedBy[Us][BISHOP] & attackedBy[Us][KING]) - 6 * mg_value(score) / 8 - 7; From 53125902e461ae129b408a81ada07aaf44810c6c Mon Sep 17 00:00:00 2001 From: Moez Jellouli <37274752+MJZ1977@users.noreply.github.com> Date: Sun, 24 Nov 2019 21:57:09 +0100 Subject: [PATCH 0089/1766] Extend last non-pawn captures Extend last non-pawn captures at principal variation nodes because they are in general decisive moves with clear endgame result. STC http://tests.stockfishchess.org/tests/view/5ddafc86e75c0005326d2140 LLR: 2.96 (-2.94,2.94) [-1.50,4.50] Total: 9892 W: 2238 L: 2099 D: 5555 LTC http://tests.stockfishchess.org/tests/view/5ddb0401e75c0005326d2150 LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 30369 W: 5013 L: 4756 D: 20600 Closes https://github.com/official-stockfish/Stockfish/pull/2425 Bench: 5059526 --- src/search.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index d3f38aae109..e976274f28f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1049,6 +1049,12 @@ namespace { && pos.pawn_passed(us, to_sq(move))) extension = 1; + // Last captures extension + else if ( PvNode + && PieceValue[EG][pos.captured_piece()] > PawnValueEg + && pos.non_pawn_material() <= 2 * RookValueMg) + extension = 1; + // Castling extension if (type_of(move) == CASTLING) extension = 1; From df340a839c4d223c3053dc95dca02547ed83acee Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Tue, 26 Nov 2019 02:56:53 +0300 Subject: [PATCH 0090/1766] Simplify king danger This patch is a cleanup/simplification of king flank defenders patch, removing king flanks attacks linear dependance in kingdanger. Result of experiments with quadratic kingflank defenders scaling. Rebased on the latest master. passed STC http://tests.stockfishchess.org/tests/view/5ddc2b99e0b4af579302bacf LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 19660 W: 4309 L: 4184 D: 11167 passed LTC http://tests.stockfishchess.org/tests/view/5ddc3168e0b4af579302bade LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 24362 W: 3974 L: 3859 D: 16529 Closes https://github.com/official-stockfish/Stockfish/pull/2428 bench 5742013 --- src/evaluate.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 4c876820d06..116d5d66168 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -449,13 +449,13 @@ namespace { + 148 * popcount(unsafeChecks) + 98 * popcount(pos.blockers_for_king(Us)) + 69 * kingAttacksCount[Them] - + 4 * (kingFlankAttack - kingFlankDefense) + 3 * kingFlankAttack * kingFlankAttack / 8 + mg_value(mobility[Them] - mobility[Us]) - 873 * !pos.count(Them) - 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING]) - 6 * mg_value(score) / 8 - - 7; + - 4 * kingFlankDefense + + 37; // Transform the kingDanger units into a Score, and subtract it from the evaluation if (kingDanger > 100) From 54253bcce69a0ebc3e6bc4c35dfa76f8ff46521e Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 27 Nov 2019 19:03:23 +0100 Subject: [PATCH 0091/1766] Extend bench to static evaluations this patch extends bench to print static evaluations. ./stockfish bench 16 1 1 filename eval will now print the evaluations for all fens in the file. This complements the various 'go' flavors for bench and might be useful for debugging and/or tuning. No functional change. --- src/benchmark.cpp | 2 +- src/evaluate.cpp | 3 +++ src/uci.cpp | 15 ++++++++++----- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 8f30bee4f8e..58f05e668a5 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -117,7 +117,7 @@ vector setup_bench(const Position& current, istream& is) { string fenFile = (is >> token) ? token : "default"; string limitType = (is >> token) ? token : "depth"; - go = "go " + limitType + " " + limit; + go = limitType == "eval" ? "eval" : "go " + limitType + " " + limit; if (fenFile == "default") fens = Defaults; diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 116d5d66168..e42b4f387cc 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -847,6 +847,9 @@ Value Eval::evaluate(const Position& pos) { std::string Eval::trace(const Position& pos) { + if (pos.checkers()) + return "Total evaluation: none (in check)"; + std::memset(scores, 0, sizeof(scores)); pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt diff --git a/src/uci.cpp b/src/uci.cpp index 99bf1a13fa7..6f0bdd769d1 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -146,7 +146,7 @@ namespace { uint64_t num, nodes = 0, cnt = 1; vector list = setup_bench(pos, args); - num = count_if(list.begin(), list.end(), [](string s) { return s.find("go ") == 0; }); + num = count_if(list.begin(), list.end(), [](string s) { return s.find("go ") == 0 || s.find("eval") == 0; }); TimePoint elapsed = now(); @@ -155,12 +155,17 @@ namespace { istringstream is(cmd); is >> skipws >> token; - if (token == "go") + if (token == "go" || token == "eval") { cerr << "\nPosition: " << cnt++ << '/' << num << endl; - go(pos, is, states); - Threads.main()->wait_for_search_finished(); - nodes += Threads.nodes_searched(); + if (token == "go") + { + go(pos, is, states); + Threads.main()->wait_for_search_finished(); + nodes += Threads.nodes_searched(); + } + else + sync_cout << "\n" << Eval::trace(pos) << sync_endl; } else if (token == "setoption") setoption(is); else if (token == "position") position(pos, is, states); From f0047ce08e655e61ca07508cb9793405f75286f9 Mon Sep 17 00:00:00 2001 From: Alain SAVARD Date: Sat, 30 Nov 2019 09:47:43 -0500 Subject: [PATCH 0092/1766] King proximity tweak for passed pawns Decrease slightly the penalty for opponent king distance to passed pawn. Instead of 5:2 ratio (or 20:8) we now have 19:8 STC http://tests.stockfishchess.org/tests/view/5de281b2727dc1d26718a673 LLR: 2.95 (-2.94,2.94) [-1.50,4.50] Total: 28638 W: 6297 L: 6104 D: 16237 LTC http://tests.stockfishchess.org/tests/view/5de2a2ff727dc1d26718a67b LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 59586 W: 9766 L: 9429 D: 40391 Where to go from here: Further tests will try a similar tweak on the friendly king proximity penalty, because recent experiments indicate that this penalty is quite sensitive, but I wanted to try first on the larger term. Closes https://github.com/official-stockfish/Stockfish/pull/2435 bench: 5258928 --------------- Increasing the penalty ratio to 21:8 was neutral. http://tests.stockfishchess.org/tests/view/5de2814d727dc1d26718a671 Decreasing the penalty ratio a bit more to 9:4 seems less promising http://tests.stockfishchess.org/tests/view/5de2f4c2727dc1d26718a691 http://tests.stockfishchess.org/tests/view/5de32ecc727dc1d26718a6b0 --- src/evaluate.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index e42b4f387cc..1988e20ec50 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -602,8 +602,8 @@ namespace { Square blockSq = s + Up; // Adjust bonus based on the king's proximity - bonus += make_score(0, ( king_proximity(Them, blockSq) * 5 - - king_proximity(Us, blockSq) * 2) * w); + bonus += make_score(0, ( (king_proximity(Them, blockSq) * 19) / 4 + - king_proximity(Us, blockSq) * 2) * w); // If blockSq is not the queening square then consider also a second push if (r != RANK_7) From 97a0e4e8170df33b927c48d734e0132e9ef8a22f Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Fri, 29 Nov 2019 19:15:21 +0200 Subject: [PATCH 0093/1766] UnblockedStorm tuned STC http://tests.stockfishchess.org/tests/view/5de155980294ec4750cba9bd LLR: 2.96 (-2.94,2.94) [0.00,4.00] Total: 60206 W: 13295 L: 12895 D: 34016 LTC http://tests.stockfishchess.org/tests/view/5de22f6f0294ec4750cba9e7 LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 182005 W: 29571 L: 28902 D: 123532 VLTC http://tests.stockfishchess.org/tests/view/5de4adca5e868d334be516c1 LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 42101 W: 6068 L: 5978 D: 30055 Bench: 5122362 --- src/pawns.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index 3ddf70308c5..04222a2aa4e 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -56,10 +56,10 @@ namespace { // 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) }, - { V( 4), V( 52), V( 162), V(37), V( 7), V(-14), V( -2) }, - { V(-10), V( -14), V( 90), V(15), V( 2), V( -7), V(-16) } + { V( 85), V(-289), V(-166), V(97), V(50), V( 45), V( 50) }, + { V( 46), V( -25), V( 122), V(45), V(37), V(-10), V( 20) }, + { V( -6), V( 51), V( 168), V(34), V(-2), V(-22), V(-14) }, + { V(-15), V( -11), V( 101), V( 4), V(11), V(-15), V(-29) } }; #undef S From 6a6fc28551b84719868df99950584b878199d0c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Mon, 9 Dec 2019 00:00:34 +0100 Subject: [PATCH 0094/1766] The sudo tag is deprecated in Travis CI Reported by Christian Clauss. Thanks! No functional change --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a59ea425141..e2b42e6d5ed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,4 @@ language: cpp -sudo: required dist: xenial matrix: From 0256416bb7b3ba7e96a487062104a9379c0c3a82 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Tue, 12 Nov 2019 16:12:09 +0100 Subject: [PATCH 0095/1766] Remove unneeded & incorrect check. the removed line is not needed, since with the conditions on SE, eval equals ttValue (except inCheck), which must be larger than beta if the second condition is true. The removed line is also incorrect as eval might be VALUE_NONE at this location if inCheck. This removal addresses part of https://github.com/official-stockfish/Stockfish/pull/2406#issuecomment-552642608 No functional change. --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index e976274f28f..26010108dd4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1033,8 +1033,7 @@ namespace { // search without the ttMove. So we assume this expected Cut-node is not singular, // that multiple moves fail high, and we can prune the whole subtree by returning // a soft bound. - else if ( eval >= beta - && singularBeta >= beta) + else if (singularBeta >= beta) return singularBeta; } From 20484ccdd5876deee4138d8badea4ef44b73341f Mon Sep 17 00:00:00 2001 From: xoto10 Date: Sun, 8 Dec 2019 11:06:19 +0000 Subject: [PATCH 0096/1766] Tweak time management (failing eval) Adjust fallingEval with score change in last 5 iterations. FallingEval adjusts the time used on a move depending on whether the position score is better or worse than on the previous move. This change adds a dependency on the score change in the last 5 iterations of the current search. Tests with original code: STC : LLR: 2.97 (-2.94,2.94) [-1.50,4.50] Total: 18728 W: 4170 L: 4005 D: 10553 https://tests.stockfishchess.org/tests/view/5de68a5bb407ee7bfda68a94 LTC : LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 180217 W: 29214 L: 28551 D: 122452 https://tests.stockfishchess.org/tests/view/5de690a4b407ee7bfda68a9a Revised code using a simple array instead of a deque and different values gave a slightly quicker pass at LTC. The merged patch now uses this: STC : LLR: 2.96 (-2.94,2.94) [-1.50,4.50] Total: 18616 W: 4114 L: 3950 D: 10552 https://tests.stockfishchess.org/tests/view/5debb790b7bdefd50db28d14 LTC : LLR: 2.96 (-2.94,2.94) [0.00,3.50] Total: 134151 W: 21729 L: 21191 D: 91231 https://tests.stockfishchess.org/tests/view/5debc13fb7bdefd50db28d19 No functional change --- src/search.cpp | 17 ++++++++++++++++- src/thread.h | 1 + 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 26010108dd4..e8ca5c5896d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -335,6 +335,7 @@ void Thread::search() { MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr); double timeReduction = 1, totBestMoveChanges = 0; Color us = rootPos.side_to_move(); + int iterIdx = 0; std::memset(ss-7, 0, 10 * sizeof(Stack)); for (int i = 7; i > 0; i--) @@ -345,6 +346,16 @@ void Thread::search() { bestValue = delta = alpha = -VALUE_INFINITE; beta = VALUE_INFINITE; + if (mainThread) + { + if (mainThread->previousScore == VALUE_INFINITE) + for (int i=0; i<4; ++i) + mainThread->iterValue[i] = VALUE_ZERO; + else + for (int i=0; i<4; ++i) + mainThread->iterValue[i] = mainThread->previousScore; + } + size_t multiPV = Options["MultiPV"]; // Pick integer skill levels, but non-deterministically round up or down @@ -520,7 +531,8 @@ void Thread::search() { && !Threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (354 + 10 * (mainThread->previousScore - bestValue)) / 692.0; + double fallingEval = (354 + 6 * (mainThread->previousScore - bestValue) + + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 692.0; fallingEval = clamp(fallingEval, 0.5, 1.5); // If the bestMove is stable over several iterations, reduce time accordingly @@ -547,6 +559,9 @@ void Thread::search() { Threads.stop = true; } } + + mainThread->iterValue[iterIdx] = bestValue; + iterIdx = (iterIdx + 1) & 3; } if (!mainThread) diff --git a/src/thread.h b/src/thread.h index 2b1f92b2092..a1545072843 100644 --- a/src/thread.h +++ b/src/thread.h @@ -88,6 +88,7 @@ struct MainThread : public Thread { double previousTimeReduction; Value previousScore; + Value iterValue[4]; int callsCnt; bool stopOnPonderhit; std::atomic_bool ponder; From a6b5ba1b6404ce8aec8a2be8b7354dcb89cfda3f Mon Sep 17 00:00:00 2001 From: joergoster Date: Fri, 6 Dec 2019 10:11:45 +0100 Subject: [PATCH 0097/1766] Fix output of PV lines with invalid scores #2439 As reported on the forum it is possible, on very rare occasions, that we are trying to print a PV line with an invalid previousScore, although this line has a valid actual score. This patch fixes output of PV lines with invalid scores in a MultiPV search. This is a follow-up patch to 8b15961 and makes the fix finally complete. The reason is the i <= pvIdx condition which probably is a leftover from the times there was a special root search function. This check is no longer needed today and prevents PV lines past the current one (current pvIdx) to be flagged as updated even though they do have a valid score. https://github.com/official-stockfish/Stockfish/commit/8b15961349e18a9ba113973c53f53913d0cd0fad https://groups.google.com/forum/?fromgroups=#!topic/fishcooking/PrnoDLvMvro No functional change. --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index e8ca5c5896d..a6c93b43550 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1741,7 +1741,7 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) { for (size_t i = 0; i < multiPV; ++i) { - bool updated = (i <= pvIdx && rootMoves[i].score != -VALUE_INFINITE); + bool updated = rootMoves[i].score != -VALUE_INFINITE; if (depth == 1 && !updated) continue; From 78eeba29a20e007a5bd940e65569a594ee6d6324 Mon Sep 17 00:00:00 2001 From: protonspring Date: Fri, 6 Dec 2019 08:56:17 -0700 Subject: [PATCH 0098/1766] Simplify pruning moves with negative SEE This patch simplifies pruning moves with negative SEE values. STC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 18847 W: 4211 L: 4084 D: 10552 http://tests.stockfishchess.org/tests/view/5de983f2caa7c610e4d1866e LTC LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 25556 W: 4200 L: 4087 D: 17269 http://tests.stockfishchess.org/tests/view/5de99e21caa7c610e4d18676 Bench 5390930 --- src/search.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index a6c93b43550..f39f1de1c63 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1478,9 +1478,7 @@ namespace { && !pos.capture(move); // Don't search moves with negative SEE values - if ( (!inCheck || evasionPrunable) - && !(givesCheck && pos.is_discovery_check_on_king(~pos.side_to_move(), move)) - && !pos.see_ge(move)) + if ( (!inCheck || evasionPrunable) && !pos.see_ge(move)) continue; // Speculative prefetch as early as possible From d00b2ec6bdd63bd88f5553b81b5da88bb298cb4f Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sat, 7 Dec 2019 17:56:33 +0300 Subject: [PATCH 0099/1766] Do last capture extensions for every single node This patch simplifies latest @MJZ1977 elo gainer. Seems like PvNode check in condition of last capture extension is not needed. Note - even if this is a simplification it actually causes this extension to be applied more often, thus strengthening effect of @MJZ1977's patch. passed STC http://tests.stockfishchess.org/tests/view/5deb9a3eb7bdefd50db28d0e LLR: 2.96 (-2.94,2.94) [-3.00,1.00] Total: 80244 W: 17421 L: 17414 D: 45409 passed LTC http://tests.stockfishchess.org/tests/view/5deba860b7bdefd50db28d11 LLR: 2.94 (-2.94,2.94) [-3.00,1.00] Total: 21506 W: 3565 L: 3446 D: 14495 Bench: 5097036 --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index f39f1de1c63..21c36f5090b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1064,8 +1064,7 @@ namespace { extension = 1; // Last captures extension - else if ( PvNode - && PieceValue[EG][pos.captured_piece()] > PawnValueEg + else if ( PieceValue[EG][pos.captured_piece()] > PawnValueEg && pos.non_pawn_material() <= 2 * RookValueMg) extension = 1; From 764b9adda6cf59719b5c9c8a75d2a2e696395709 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sun, 8 Dec 2019 17:10:14 +0300 Subject: [PATCH 0100/1766] Exclude blockers for king from mobility area This patch excludes blockers for king from mobility area. It was tried a couple of times by now but now it passed. Performance is not enormously good but this patch makes a lot of sence - blockers for king can't really move until king moves (in most cases) so logic behind it is the same as behind excluding king square from mobility area. STC http://tests.stockfishchess.org/tests/view/5dec388651219d7befdc76be LLR: 2.95 (-2.94,2.94) [-1.50,4.50] Total: 6155 W: 1428 L: 1300 D: 3427 LTC http://tests.stockfishchess.org/tests/view/5dec4a3151219d7befdc76d3 LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 120800 W: 19636 L: 19134 D: 82030 Bench: 5173081 --- src/evaluate.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 1988e20ec50..20d1059e568 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -225,9 +225,9 @@ namespace { // Find our pawns that are blocked or on the first two ranks Bitboard b = pos.pieces(Us, PAWN) & (shift(pos.pieces()) | LowRanks); - // Squares occupied by those pawns, by our king or queen or controlled by - // enemy pawns are excluded from the mobility area. - mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pe->pawn_attacks(Them)); + // Squares occupied by those pawns, by our king or queen, by blockers to attacks on our king + // or controlled by enemy pawns are excluded from the mobility area. + mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pos.blockers_for_king(Us) | pe->pawn_attacks(Them)); // Initialize attackedBy[] for king and pawns attackedBy[Us][KING] = pos.attacks_from(ksq); From 3ef0c3c34a00e6b13d6c96d8c2f0d8d7a6cc25a6 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Mon, 9 Dec 2019 21:38:57 +0000 Subject: [PATCH 0101/1766] TrappedRook value and King positional tables Small tweak to increase the TrappedRook penalty. Nice idea by Alain Savard! STC LLR: 2.96 (-2.94,2.94) [-1.50,4.50] Total: 36977 W: 8212 L: 7993 D: 20772 https://tests.stockfishchess.org/tests/view/5dee1c1e3cff9a249bb9e46d LTC LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 36395 W: 6070 L: 5795 D: 24530 https://tests.stockfishchess.org/tests/view/5dee90153cff9a249bb9e479 Closes https://github.com/official-stockfish/Stockfish/pull/2447 Bench: 5176990 ------------------------- Comments by Alain Savard: For the record, the idea was to run an experimental tuning with disabled castling in the hope to get more hits on the TrappedRook and the king in the c1- f1-f2-c2 area http://tests.stockfishchess.org/tests/view/5dec57be51219d7befdc76e1 A first interpretation of that tuning was green STC (0, 4) and yellow LTC (0, 4): http://tests.stockfishchess.org/tests/view/5ded04bc51219d7befdc773a http://tests.stockfishchess.org/tests/view/5ded1e7a51219d7befdc7760 Thank you @xoto for trying this. Indeed, because the tuned Kc2 and Kf2 values were quite different, it was a good idea to try something more neutral. --- src/evaluate.cpp | 2 +- src/psqt.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 20d1059e568..e7d30825e29 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -145,7 +145,7 @@ namespace { constexpr Score ThreatByKing = S( 24, 89); constexpr Score ThreatByPawnPush = S( 48, 39); constexpr Score ThreatBySafePawn = S(173, 94); - constexpr Score TrappedRook = S( 47, 4); + constexpr Score TrappedRook = S( 52, 10); constexpr Score WeakQueen = S( 49, 15); #undef S diff --git a/src/psqt.cpp b/src/psqt.cpp index 60d17ad29e5..c11dc5ba68c 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -79,8 +79,8 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { { S(-2,-75), S(-2,-52), S( 1,-43), S(-2,-36) } }, { // King - { S(271, 1), S(327, 45), S(270, 85), S(192, 76) }, - { S(278, 53), S(303,100), S(230,133), S(174,135) }, + { S(271, 1), S(327, 45), S(271, 85), S(198, 76) }, + { S(278, 53), S(303,100), S(234,133), S(179,135) }, { S(195, 88), S(258,130), S(169,169), S(120,175) }, { S(164,103), S(190,156), S(138,172), S( 98,172) }, { S(154, 96), S(179,166), S(105,199), S( 70,199) }, From 443787b0d1dceb6186e217ea2b2224e76806ea15 Mon Sep 17 00:00:00 2001 From: lantonov Date: Mon, 9 Dec 2019 20:50:47 +0200 Subject: [PATCH 0102/1766] Tuned razor and futility margins Tuning was done with Bayesian optimisation with the following parameters: Acquisition function: Expected Improvement alpha: 0.05 xi: 1e-4 TC: 60+0.6 Number of iterations: 100 Initial points: 5 Batch size: 20 games STC http://tests.stockfishchess.org/tests/view/5dee291e3cff9a249bb9e470 LLR: 2.97 (-2.94,2.94) [-1.50,4.50] Total: 19586 W: 4382 L: 4214 D: 10990 LTC http://tests.stockfishchess.org/tests/view/5dee4e273cff9a249bb9e473 LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 38840 W: 6315 L: 6036 D: 26489 Bench: 5033242 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 21c36f5090b..c856980b424 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -65,9 +65,9 @@ namespace { constexpr uint64_t ttHitAverageResolution = 1024; // Razor and futility margins - constexpr int RazorMargin = 661; + constexpr int RazorMargin = 594; Value futility_margin(Depth d, bool improving) { - return Value(198 * (d - improving)); + return Value(232 * (d - improving)); } // Reductions lookup table, initialized at startup From b6482472a03833287dc21bdaa783f156978ac63e Mon Sep 17 00:00:00 2001 From: Guenther Demetz Date: Tue, 10 Dec 2019 08:07:34 +0100 Subject: [PATCH 0103/1766] Refine improving-logic Don't rely on the assumption that we are improving after surviving a check. Instead, compare with the static eval of 2 moves before. STC https://tests.stockfishchess.org/tests/view/5dedfd7f3cff9a249bb9e44d LLR: 2.95 (-2.94,2.94) [-1.50,4.50] Total: 38859 W: 8621 L: 8397 D: 21841 LTC https://tests.stockfishchess.org/tests/view/5dee1b5a3cff9a249bb9e465 LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 51130 W: 8308 L: 7996 D: 34826 Bench: 5371271 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c856980b424..55e04ec363e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -812,8 +812,8 @@ namespace { && eval <= alpha - RazorMargin) return qsearch(pos, ss, alpha, beta); - improving = ss->staticEval >= (ss-2)->staticEval - || (ss-2)->staticEval == VALUE_NONE; + improving = (ss-2)->staticEval == VALUE_NONE ? (ss->staticEval >= (ss-4)->staticEval + || (ss-4)->staticEval == VALUE_NONE) : ss->staticEval >= (ss-2)->staticEval; // Step 8. Futility pruning: child node (~30 Elo) if ( !PvNode From 13f70d0392ca3ce7f1c8e34dd0a10c3537d2fbab Mon Sep 17 00:00:00 2001 From: xoto10 Date: Fri, 13 Dec 2019 04:59:06 +0000 Subject: [PATCH 0104/1766] Tune search constants STC failed red : LLR: -2.95 (-2.94,2.94) [0.00,3.50] Total: 41667 W: 9094 L: 9138 D: 23435 https://tests.stockfishchess.org/tests/view/5df7bb566932658fe9b45253 LTC failed yellow : LLR: -2.96 (-2.94,2.94) [0.00,3.50] Total: 113667 W: 18330 L: 18196 D: 77141 https://tests.stockfishchess.org/tests/view/5df562386932658fe9b451c7 VLTC turned green : LLR: 2.95 (-2.94,2.94) [0.00,3.50] Total: 128630 W: 17747 L: 17273 D: 93610 https://tests.stockfishchess.org/tests/view/5df9054dcde01bf360ab78db Bench 5180012 --- src/search.cpp | 52 +++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 55e04ec363e..b0bcc57a44e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -65,9 +65,9 @@ namespace { constexpr uint64_t ttHitAverageResolution = 1024; // Razor and futility margins - constexpr int RazorMargin = 594; + constexpr int RazorMargin = 531; Value futility_margin(Depth d, bool improving) { - return Value(232 * (d - improving)); + return Value(217 * (d - improving)); } // Reductions lookup table, initialized at startup @@ -75,7 +75,7 @@ namespace { Depth reduction(bool i, Depth d, int mn) { int r = Reductions[d] * Reductions[mn]; - return (r + 520) / 1024 + (!i && r > 999); + return (r + 511) / 1024 + (!i && r > 1007); } constexpr int futility_move_count(bool improving, Depth depth) { @@ -84,7 +84,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return d > 17 ? -8 : 22 * d * d + 151 * d - 140; + return d > 15 ? -8 : 19 * d * d + 155 * d - 132; } // Add a small random component to draw evaluations to avoid 3fold-blindness @@ -194,7 +194,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int((23.4 + std::log(Threads.size()) / 2) * std::log(i)); + Reductions[i] = int((24.8 + std::log(Threads.size()) / 2) * std::log(i)); } @@ -428,12 +428,12 @@ void Thread::search() { if (rootDepth >= 4) { Value previousScore = rootMoves[pvIdx].previousScore; - delta = Value(21 + abs(previousScore) / 128); + delta = Value(21 + abs(previousScore) / 256); 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 + (111 - ct / 2) * previousScore / (abs(previousScore) + 176); + int dct = ct + (102 - ct / 2) * previousScore / (abs(previousScore) + 157); contempt = (us == WHITE ? make_score(dct, dct / 2) : -make_score(dct, dct / 2)); @@ -531,13 +531,13 @@ void Thread::search() { && !Threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (354 + 6 * (mainThread->previousScore - bestValue) - + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 692.0; + double fallingEval = (332 + 6 * (mainThread->previousScore - bestValue) + + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 704.0; fallingEval = clamp(fallingEval, 0.5, 1.5); // If the bestMove is stable over several iterations, reduce time accordingly - timeReduction = lastBestMoveDepth + 9 < completedDepth ? 1.97 : 0.98; - double reduction = (1.36 + mainThread->previousTimeReduction) / (2.29 * timeReduction); + timeReduction = lastBestMoveDepth + 9 < completedDepth ? 1.94 : 0.91; + double reduction = (1.41 + mainThread->previousTimeReduction) / (2.27 * timeReduction); // Use part of the gained time from a previous stable move for the current move for (Thread* th : Threads) @@ -817,7 +817,7 @@ namespace { // Step 8. Futility pruning: child node (~30 Elo) if ( !PvNode - && depth < 7 + && depth < 6 && eval - futility_margin(depth, improving) >= beta && eval < VALUE_KNOWN_WIN) // Do not return unproven wins return eval; @@ -825,10 +825,10 @@ namespace { // Step 9. Null move search with verification search (~40 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 22661 + && (ss-1)->statScore < 23405 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 33 * depth + 299 - improving * 30 + && ss->staticEval >= beta - 32 * depth + 317 - improving * 30 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -836,7 +836,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and value - Depth R = (835 + 70 * depth) / 256 + std::min(int(eval - beta) / 185, 3); + Depth R = (854 + 68 * depth) / 258 + std::min(int(eval - beta) / 192, 3); ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -879,7 +879,7 @@ namespace { && depth >= 5 && abs(beta) < VALUE_MATE_IN_MAX_PLY) { - Value raisedBeta = std::min(beta + 191 - 46 * improving, VALUE_INFINITE); + Value raisedBeta = std::min(beta + 189 - 45 * improving, VALUE_INFINITE); MovePicker mp(pos, ttMove, raisedBeta - ss->staticEval, &thisThread->captureHistory); int probCutCount = 0; @@ -1003,14 +1003,14 @@ namespace { // Futility pruning: parent node (~2 Elo) if ( lmrDepth < 6 && !inCheck - && ss->staticEval + 250 + 211 * lmrDepth <= alpha) + && ss->staticEval + 255 + 182 * lmrDepth <= alpha) continue; // Prune moves with negative SEE (~10 Elo) - if (!pos.see_ge(move, Value(-(31 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth))) + if (!pos.see_ge(move, Value(-(32 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth))) continue; } - else if (!pos.see_ge(move, Value(-199) * depth)) // (~20 Elo) + else if (!pos.see_ge(move, Value(-194) * depth)) // (~20 Elo) continue; } @@ -1104,12 +1104,12 @@ namespace { || moveCountPruning || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha || cutNode - || thisThread->ttHitAverage < 384 * ttHitAverageResolution * ttHitAverageWindow / 1024)) + || thisThread->ttHitAverage < 375 * ttHitAverageResolution * ttHitAverageWindow / 1024)) { Depth r = reduction(improving, depth, moveCount); // Decrease reduction if the ttHit running average is large - if (thisThread->ttHitAverage > 544 * ttHitAverageResolution * ttHitAverageWindow / 1024) + if (thisThread->ttHitAverage > 500 * ttHitAverageResolution * ttHitAverageWindow / 1024) r--; // Reduction if other threads are searching this position. @@ -1121,7 +1121,7 @@ namespace { r -= 2; // Decrease reduction if opponent's move count is high (~10 Elo) - if ((ss-1)->moveCount > 15) + if ((ss-1)->moveCount > 14) r--; // Decrease reduction if ttMove has been singularly extended @@ -1149,7 +1149,7 @@ namespace { + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 4729; + - 4926; // Reset statScore to zero if negative and most stats shows >= 0 if ( ss->statScore < 0 @@ -1159,10 +1159,10 @@ namespace { ss->statScore = 0; // Decrease/increase reduction by comparing opponent's stat score (~10 Elo) - if (ss->statScore >= -99 && (ss-1)->statScore < -116) + if (ss->statScore >= -102 && (ss-1)->statScore < -114) r--; - else if ((ss-1)->statScore >= -117 && ss->statScore < -144) + else if ((ss-1)->statScore >= -116 && ss->statScore < -154) r++; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) @@ -1421,7 +1421,7 @@ namespace { if (PvNode && bestValue > alpha) alpha = bestValue; - futilityBase = bestValue + 153; + futilityBase = bestValue + 154; } const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, From 44f56e04e22bb449d44475eb054b67c1c22d27ef Mon Sep 17 00:00:00 2001 From: ppigazzini Date: Sat, 4 Jan 2020 02:48:32 +0100 Subject: [PATCH 0105/1766] Update Readme.md Update fishtest server URL, fix a broken wiki link, fix a typo. --- Readme.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Readme.md b/Readme.md index 10ffdeae4ff..5b807ee6355 100644 --- a/Readme.md +++ b/Readme.md @@ -162,12 +162,12 @@ community effort. There are a few ways to help contribute to its growth. ### Donating hardware Improving Stockfish requires a massive amount of testing. You can donate -your hardware resources by installing the [Fishtest Worker](https://github.com/glinscott/fishtest/wiki/Running-the-worker) -and view the current tests on [Fishtest](http://tests.stockfishchess.org/tests). +your hardware resources by installing the [Fishtest Worker](https://github.com/glinscott/fishtest/wiki/Running-the-worker:-overview) +and view the current tests on [Fishtest](https://tests.stockfishchess.org/tests). ### Improving the code -If you want to help improve the code, there are several valuable ressources: +If you want to help improve the code, there are several valuable resources: * [In this wiki,](https://www.chessprogramming.org) many techniques used in Stockfish are explained with a lot of background information. @@ -179,7 +179,7 @@ Nevertheless, a helpful resource. * The latest source can always be found on [GitHub](https://github.com/official-stockfish/Stockfish). Discussions about Stockfish take place in the [FishCooking](https://groups.google.com/forum/#!forum/fishcooking) -group and engine testing is done on [Fishtest](http://tests.stockfishchess.org/tests). +group and engine testing is done on [Fishtest](https://tests.stockfishchess.org/tests). If you want to help improve Stockfish, please read this [guideline](https://github.com/glinscott/fishtest/wiki/Creating-my-first-test) first, where the basics of Stockfish development are explained. From 83ecfa7c33ab3e89fcbc506f0f4d5312baa26aeb Mon Sep 17 00:00:00 2001 From: Alain SAVARD Date: Sat, 4 Jan 2020 13:54:35 -0500 Subject: [PATCH 0106/1766] Use a faster implementation of Static Exchange Evaluation SEE (Static Exchange Evaluation) is a critical component, so we might indulge some tricks to make it faster. Another pull request #2469 showed some speedup by removing templates, this version uses Ronald de Man (@syzygy1) SEE implementation which also unrolls the for loop by suppressing the min_attacker() helper function and exits as soon as the last swap is conclusive. See Ronald de Man version there: https://github.com/syzygy1/Cfish/blob/master/src/position.c Patch testes against pull request #2469: LLR: 2.95 (-2.94,2.94) {-1.00,3.00} Total: 19365 W: 3771 L: 3634 D: 11960 Ptnml(0-2): 241, 1984, 5099, 2092, 255 http://tests.stockfishchess.org/tests/view/5e10eb135e5436dd91b27ba3 And since we are using new SPRT statistics, and that both pull requests finished with less than 20000 games I also tested against master as a speed-up: LLR: 2.99 (-2.94,2.94) {-1.00,3.00} Total: 18878 W: 3674 L: 3539 D: 11665 Ptnml(0-2): 193, 1999, 4966, 2019, 250 http://tests.stockfishchess.org/tests/view/5e10febf12ef906c8b388745 Non functional change --- src/position.cpp | 146 +++++++++++++++++++++-------------------------- 1 file changed, 65 insertions(+), 81 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index 6336a5ed193..9644e02c1b5 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -50,41 +50,6 @@ const string PieceToChar(" PNBRQK pnbrqk"); constexpr Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING }; - -// min_attacker() is a helper function used by see_ge() to locate the least -// valuable attacker for the side to move, remove the attacker we just found -// from the bitboards and scan for new X-ray attacks behind it. - -template -PieceType min_attacker(const Bitboard* byTypeBB, Square to, Bitboard stmAttackers, - Bitboard& occupied, Bitboard& attackers) { - - Bitboard b = stmAttackers & byTypeBB[Pt]; - if (!b) - return min_attacker(byTypeBB, to, stmAttackers, occupied, attackers); - - occupied ^= lsb(b); // Remove the attacker from occupied - - // Add any X-ray attack behind the just removed piece. For instance with - // rooks in a8 and a7 attacking a1, after removing a7 we add rook in a8. - // Note that new added attackers can be of any color. - if (Pt == PAWN || Pt == BISHOP || Pt == QUEEN) - attackers |= attacks_bb(to, occupied) & (byTypeBB[BISHOP] | byTypeBB[QUEEN]); - - if (Pt == ROOK || Pt == QUEEN) - attackers |= attacks_bb(to, occupied) & (byTypeBB[ROOK] | byTypeBB[QUEEN]); - - // X-ray may add already processed pieces because byTypeBB[] is constant: in - // the rook example, now attackers contains _again_ rook in a7, so remove it. - attackers &= occupied; - return Pt; -} - -template<> -PieceType min_attacker(const Bitboard*, Square, Bitboard, Bitboard&, Bitboard&) { - return KING; // No need to update bitboards: it is the last cycle -} - } // namespace @@ -1052,77 +1017,96 @@ bool Position::see_ge(Move m, Value threshold) const { if (type_of(m) != NORMAL) return VALUE_ZERO >= threshold; - Bitboard stmAttackers; Square from = from_sq(m), to = to_sq(m); - PieceType nextVictim = type_of(piece_on(from)); - Color us = color_of(piece_on(from)); - Color stm = ~us; // First consider opponent's move - Value balance; // Values of the pieces taken by us minus opponent's ones - - // The opponent may be able to recapture so this is the best result - // we can hope for. - balance = PieceValue[MG][piece_on(to)] - threshold; - if (balance < VALUE_ZERO) + int swap = PieceValue[MG][piece_on(to)] - threshold; + if (swap < 0) return false; - // Now assume the worst possible result: that the opponent can - // capture our piece for free. - balance -= PieceValue[MG][nextVictim]; - - // If it is enough (like in PxQ) then return immediately. Note that - // in case nextVictim == KING we always return here, this is ok - // if the given move is legal. - if (balance >= VALUE_ZERO) + swap = PieceValue[MG][piece_on(from)] - swap; + if (swap <= 0) return true; - // Find all attackers to the destination square, with the moving piece - // removed, but possibly an X-ray attacker added behind it. - Bitboard occupied = pieces() ^ from ^ to; - Bitboard attackers = attackers_to(to, occupied) & occupied; + Bitboard occ = pieces() ^ from ^ to; + Color stm = color_of(piece_on(from)); + Bitboard attackers = attackers_to(to, occ); + Bitboard stmAttackers, bb; + int res = 1; while (true) { - stmAttackers = attackers & pieces(stm); + stm = ~stm; + attackers &= occ; + + // If stm has no more attackers then give up: stm loses + if (!(stmAttackers = attackers & pieces(stm))) + break; // Don't allow pinned pieces to attack (except the king) as long as - // any pinners are on their original square. - if (st->pinners[~stm] & occupied) + // there are pinners on their original square. + if (st->pinners[~stm] & occ) stmAttackers &= ~st->blockersForKing[stm]; - // If stm has no more attackers then give up: stm loses if (!stmAttackers) break; + res ^= 1; + // Locate and remove the next least valuable attacker, and add to - // the bitboard 'attackers' the possibly X-ray attackers behind it. - nextVictim = min_attacker(byTypeBB, to, stmAttackers, occupied, attackers); + // the bitboard 'attackers' any X-ray attackers behind it. + if ((bb = stmAttackers & pieces(PAWN))) + { + if ((swap = PawnValueMg - swap) < res) + break; - stm = ~stm; // Switch side to move + occ ^= lsb(bb); + attackers |= attacks_bb(to, occ) & pieces(BISHOP, QUEEN); + } - // Negamax the balance with alpha = balance, beta = balance+1 and - // add nextVictim's value. - // - // (balance, balance+1) -> (-balance-1, -balance) - // - assert(balance < VALUE_ZERO); + else if ((bb = stmAttackers & pieces(KNIGHT))) + { + if ((swap = KnightValueMg - swap) < res) + break; - balance = -balance - 1 - PieceValue[MG][nextVictim]; + occ ^= lsb(bb); + } - // If balance is still non-negative after giving away nextVictim then we - // win. The only thing to be careful about it is that we should revert - // stm if we captured with the king when the opponent still has attackers. - if (balance >= VALUE_ZERO) + else if ((bb = stmAttackers & pieces(BISHOP))) { - if (nextVictim == KING && (attackers & pieces(stm))) - stm = ~stm; - break; + if ((swap = BishopValueMg - swap) < res) + break; + + occ ^= lsb(bb); + attackers |= attacks_bb(to, occ) & pieces(BISHOP, QUEEN); + } + + else if ((bb = stmAttackers & pieces(ROOK))) + { + if ((swap = RookValueMg - swap) < res) + break; + + occ ^= lsb(bb); + attackers |= attacks_bb(to, occ) & pieces(ROOK, QUEEN); + } + + else if ((bb = stmAttackers & pieces(QUEEN))) + { + if ((swap = QueenValueMg - swap) < res) + break; + + occ ^= lsb(bb); + attackers |= (attacks_bb(to, occ) & pieces(BISHOP, QUEEN)) + | (attacks_bb(to, occ) & pieces(ROOK , QUEEN)); } - assert(nextVictim != KING); + + else // KING + // If we "capture" with the king but opponent still has attackers, + // reverse the result. + return (attackers & ~pieces(stm)) ? res ^ 1 : res; } - return us != stm; // We break the above loop when stm loses -} + return res; +} /// Position::is_draw() tests whether the position is drawn by 50-move rule /// or by repetition. It does not detect stalemates. From 56d5504f6548b69ce6faaa271a8b55f3773db70c Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Fri, 3 Jan 2020 11:49:25 +0100 Subject: [PATCH 0107/1766] Tweak futility pruning Exclude moves with a good history total from futility pruning. This adds a condition for quiet futility pruning: history total has to be low. STC: LLR: 2.94 (-2.94,2.94) {-1.00,3.00} Total: 20095 W: 4503 L: 4342 D: 11250 Ptnml(0-2): 362, 2380, 4422, 2486, 388 http://tests.stockfishchess.org/tests/view/5e0d7c5387585b1706b68370 LTC: LLR: 2.94 (-2.94,2.94) {0.00,2.00} Total: 53016 W: 8587 L: 8302 D: 36127 Ptnml(0-2): 353, 5397, 14751, 5545, 423 http://tests.stockfishchess.org/tests/view/5e0e30d062fb773bb7047e95 Closes https://github.com/official-stockfish/Stockfish/pull/2472 Bench: 5215200 --- src/search.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index b0bcc57a44e..57316d85613 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1003,7 +1003,11 @@ namespace { // Futility pruning: parent node (~2 Elo) if ( lmrDepth < 6 && !inCheck - && ss->staticEval + 255 + 182 * lmrDepth <= alpha) + && ss->staticEval + 255 + 182 * lmrDepth <= alpha + && thisThread->mainHistory[us][from_to(move)] + + (*contHist[0])[movedPiece][to_sq(move)] + + (*contHist[1])[movedPiece][to_sq(move)] + + (*contHist[3])[movedPiece][to_sq(move)] < 30000) continue; // Prune moves with negative SEE (~10 Elo) From de4e1cb88d9d5a6b4bf6e47bdc2a71e025bf8f46 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Fri, 3 Jan 2020 05:53:59 +0300 Subject: [PATCH 0108/1766] Introduce king infiltration bonus Add king infiltration bonus to initiative calculation. Idea is somewhat similar to outflanking - endgames are hard to win if each king is on it side of the board. So this adds extra bonus for one of kings crossing the middle line. STC LLR: 2.94 (-2.94,2.94) {-1.00,3.00} Total: 10533 W: 2372 L: 2242 D: 5919 Ptnml(0-2): 196, 1198, 2352, 1316, 202 http://tests.stockfishchess.org/tests/view/5e0e6fd1e97ea42ea89da9b3 LTC LLR: 2.96 (-2.94,2.94) {0.00,2.00} Total: 15074 W: 2563 L: 2381 D: 10130 Ptnml(0-2): 118, 1500, 4111, 1663, 129 http://tests.stockfishchess.org/tests/view/5e0e857ae97ea42ea89da9cc Closes https://github.com/official-stockfish/Stockfish/pull/2471 Bench: 5146339 --- src/evaluate.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index e7d30825e29..27fcd477630 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -705,6 +705,9 @@ namespace { int outflanking = distance(pos.square(WHITE), pos.square(BLACK)) - distance(pos.square(WHITE), pos.square(BLACK)); + bool infiltration = rank_of(pos.square(WHITE)) > RANK_4 + || rank_of(pos.square(BLACK)) < RANK_5; + bool pawnsOnBothFlanks = (pos.pieces(PAWN) & QueenSide) && (pos.pieces(PAWN) & KingSide); @@ -716,10 +719,11 @@ namespace { int complexity = 9 * pe->passed_count() + 11 * pos.count() + 9 * outflanking + + 12 * infiltration + 21 * pawnsOnBothFlanks + 51 * !pos.non_pawn_material() - 43 * almostUnwinnable - - 95 ; + - 100 ; // 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 From 44f79bdf5a092c3acec0a8bf8f2c1440e5a9da90 Mon Sep 17 00:00:00 2001 From: lantonov Date: Wed, 1 Jan 2020 10:10:39 +0200 Subject: [PATCH 0109/1766] Tuned nullmove search Tuning was done with Bayesian optimisation and sequential use of gaussian process regressor and gaussian process classifier. The latter is used in lieu of ordinal categorical modelling. Details will be given in Fishcooking forum topic: https://groups.google.com/forum/?fromgroups=#!topic/fishcooking/b3uhBBJcJG4 STC: LLR: 2.96 (-2.94,2.94) {-1.00,3.00} Total: 10248 W: 2361 L: 2233 D: 5654 Ptnml(0-2): 191, 1153, 2303, 1276, 194 http://tests.stockfishchess.org/tests/view/5e0ba4159d3fbe26f672d4e6 LTC: LLR: 2.94 (-2.94,2.94) {0.00,2.00} Total: 16003 W: 2648 L: 2458 D: 10897 Ptnml(0-2): 121, 1595, 4394, 1718, 153 http://tests.stockfishchess.org/tests/view/5e0bb8519d3fbe26f672d4fd Closes https://github.com/official-stockfish/Stockfish/pull/2468 Bench 4747984 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 57316d85613..25a7cf44044 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -825,10 +825,10 @@ namespace { // Step 9. Null move search with verification search (~40 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 23405 + && (ss-1)->statScore < 23397 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 32 * depth + 317 - improving * 30 + && ss->staticEval >= beta - 32 * depth + 292 - improving * 30 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) From 09bef14c76e119103cc1a9404cbde7e249205deb Mon Sep 17 00:00:00 2001 From: Alain SAVARD Date: Tue, 7 Jan 2020 15:35:47 -0500 Subject: [PATCH 0110/1766] Update lists of authors and contributors Preparing for version 11 of Stockfish: update lists of authors, contributors giving CPU time to the fishtest framework, etc. No functional change --- AUTHORS | 50 ++++--- Top CPU Contributors.txt | 296 ++++++++++++++++++++------------------- src/benchmark.cpp | 2 +- src/bitbase.cpp | 2 +- src/bitboard.cpp | 2 +- src/bitboard.h | 2 +- src/endgame.cpp | 2 +- src/endgame.h | 2 +- src/evaluate.cpp | 2 +- src/evaluate.h | 2 +- src/main.cpp | 2 +- src/material.cpp | 2 +- src/material.h | 2 +- src/misc.cpp | 2 +- src/misc.h | 2 +- src/movegen.cpp | 2 +- src/movegen.h | 2 +- src/movepick.cpp | 2 +- src/movepick.h | 2 +- src/pawns.cpp | 2 +- src/pawns.h | 2 +- src/position.cpp | 2 +- src/position.h | 2 +- src/psqt.cpp | 2 +- src/search.cpp | 2 +- src/search.h | 2 +- src/syzygy/tbprobe.cpp | 2 +- src/syzygy/tbprobe.h | 2 +- src/thread.cpp | 2 +- src/thread.h | 2 +- src/thread_win32_osx.h | 2 +- src/timeman.cpp | 2 +- src/timeman.h | 2 +- src/tt.cpp | 2 +- src/tt.h | 2 +- src/types.h | 2 +- src/uci.cpp | 2 +- src/uci.h | 2 +- src/ucioption.cpp | 2 +- 39 files changed, 222 insertions(+), 198 deletions(-) diff --git a/AUTHORS b/AUTHORS index 979410ae4bd..4638b41a391 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,4 +1,4 @@ -# List of authors for Stockfish, updated for version 10 +# List of authors for Stockfish, as of January 7, 2020 Tord Romstad (romstad) Marco Costalba (mcostalba) @@ -22,18 +22,23 @@ Auguste Pop Balint Pfliegel Ben Koshy (BKSpurgeon) Bill Henry (VoyagerOne) +Bojun Guo (noobpwnftw, Nooby) braich -Bojun Guo (noobpwnftw) -Brian Sheppard (SapphireBrand) +Brian Sheppard (SapphireBrand, briansheppard-toast) Bryan Cross (crossbr) +candirufish +Chess13234 Chris Cain (ceebo) -Dan Schmidt +Dan Schmidt (dfannius) +Daniel Axtens (daxtens) Daniel Dugovic (ddugovic) Dariusz Orzechowski David Zar Daylen Yang (daylen) DiscanX -Eelco de Groot +double-beep +Eduardo Cáceres (eduherminio) +Eelco de Groot (KingDefender) Elvin Liu (solarlight2) erbsenzaehler Ernesto Gatti @@ -60,8 +65,9 @@ Jacques B. (Timshel) Jan Ondruš (hxim) Jared Kish (Kurtbusch) Jarrod Torriero (DU-jdto) -Jean Gauthier (QuaisBla) +Jean Gauthier (OuaisBla) Jean-Francois Romang (jromang) +Jekaa Jerry Donald Watson (jerrydonaldwatson) Jonathan Calovski (Mysseno) Jonathan Dumale (SFisGOD) @@ -70,7 +76,7 @@ Jörg Oster (joergoster) Joseph Ellis (jhellis3) Joseph R. Prostko jundery -Justin Blanchard +Justin Blanchard (UncombedCoconut) Kelly Wilson Ken Takusagawa kinderchocolate @@ -78,23 +84,26 @@ Kiran Panditrao (Krgp) Kojirion Leonardo Ljubičić (ICCF World Champion) Leonid Pechenik (lp--) -Linus Arver +Linus Arver (listx) loco-loco 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) +marotear +Matthew Lai (matthewlai) +Matthew Sullivan (Matt14916) +Michael An (man) Michael Byrne (MichaelB7) -Michael Stembera (mstembera) Michael Chaly (Vizvezdenec) +Michael Stembera (mstembera) +Michael Whiteley (protonspring) Michel Van den Bergh (vdbergh) Miguel Lahoz (miguel-l) Mikael Bäckman (mbootsector) -Michael Whiteley (protonspring) +Mira Miroslav Fontán (Hexik) Moez Jellouli (MJZ1977) Mohammed Li (tthsqe12) @@ -102,9 +111,11 @@ Nathan Rugg (nmrugg) Nick Pelling (nickpelling) Nicklas Persson (NicklasPersson) Niklas Fiekas (niklasf) +Nikolay Kostov (NikolayIT) Ondrej Mosnáček (WOnder93) Oskar Werkelin Ahlin Pablo Vazquez +Panthee Pascal Romaret Pasquale Pigazzini (ppigazzini) Patrick Jansen (mibere) @@ -117,23 +128,28 @@ Reuven Peleg Richard Lloyd Rodrigo Exterckötter Tjäder Ron Britvich (Britvich) -Ronald de Man (syzygy1) +Ronald de Man (syzygy1, syzygy) Ryan Schmitt Ryan Takker +Sami Kiminki (skiminki) Sebastian Buchwald (UniQP) Sergei Antonov (saproj) +Sergei Ivanov (svivanov72) sf-x -shane31 -Steinar Gunderson (sesse) +Shane Booth (shane31) Stefan Geschwentner (locutus2) Stefano Cardanobile (Stefano80) +Steinar Gunderson (sesse) Stéphane Nicolet (snicolet) Thanar2 thaspel +theo77186 +Tom Truscott Tom Vijlbrief (tomtor) -Torsten Franz (torfranz) +Torsten Franz (torfranz, tfranzer) +Tracey Emery (basepr1me) Uri Blass (uriblass) -Vince Negri +Vince Negri (cuddlestmonkey) # Additionally, we acknowledge the authors of fishtest, # an essential framework for the development of Stockfish: diff --git a/Top CPU Contributors.txt b/Top CPU Contributors.txt index e882aa4317a..0ea5ac72e20 100644 --- a/Top CPU Contributors.txt +++ b/Top CPU Contributors.txt @@ -1,146 +1,154 @@ -Contributors with >10,000 CPU hours as of November 4, 2018 +Contributors with >10,000 CPU hours as of January 7, 2020 Thank you! -Username CPU Hours Games played -noobpwnftw 3730975 292309380 -mibere 535242 43333774 -crunchy 375564 29121434 -cw 371664 28748719 -fastgm 318178 22283584 -JojoM 295354 20958931 -dew 215476 17079219 -ctoks 214031 17312035 -glinscott 204517 13932027 -bking_US 187568 12233168 -velislav 168404 13336219 -CSU_Dynasty 168069 14417712 -Thanar 162373 13842179 -spams 149531 10940322 -Fisherman 141137 12099359 -drabel 134441 11180178 -leszek 133658 9812120 -marrco 133566 10115202 -sqrt2 128420 10022279 -vdbergh 123230 9200516 -tvijlbrief 123007 9498831 -vdv 120381 8555423 -malala 117291 8126488 -dsmith 114010 7622414 -BrunoBanani 104938 7448565 -CoffeeOne 100042 4593596 -Data 94621 8433010 -mgrabiak 92248 7787406 -bcross 89440 8506568 -brabos 81868 6647613 -BRAVONE 80811 5341681 -psk 77195 6156031 -nordlandia 74833 6231930 -robal 72818 5969856 -TueRens 72523 6383294 -sterni1971 71049 5647590 -sunu 65855 5360884 -mhoram 65034 5192880 -davar 64794 5457564 -nssy 64607 5371952 -Pking_cda 64499 5704075 -biffhero 63557 5480444 -teddybaer 62147 5585620 -solarlight 61278 5402642 -ElbertoOne 60156 5504304 -jromang 58854 4704502 -dv8silencer 57421 3961325 -tinker 56039 4204914 -Freja 50331 3808121 -renouve 50318 3544864 -robnjr 47504 4131742 -grandphish2 47377 4110003 -eva42 46857 4075716 -ttruscott 46802 3811534 -finfish 46244 3481661 -rap 46201 3219490 -ronaldjerum 45641 3964331 -xoto 44998 4170431 -gvreuls 44359 3902234 -bigpen0r 41780 3448224 -Bobo1239 40767 3657490 -Antihistamine 39218 2792761 -mhunt 38991 2697512 -racerschmacer 38929 3756111 -VoyagerOne 35896 3378887 -homyur 35561 3012398 -rkl 33217 2978536 -pb00067 33034 2803485 -speedycpu 32043 2531964 -SC 31954 2848432 -EthanOConnor 31638 2143255 -oryx 30962 2899534 -gri 30108 2429137 -csnodgrass 29396 2808611 -Garf 28887 2873564 -Pyafue 28885 1986098 -jkiiski 28014 1923255 -slakovv 27017 2031279 -Prcuvu 26300 2307154 -hyperbolic.tom 26248 2200777 -jbwiebe 25663 2129063 -anst 25525 2279159 -Patrick_G 24222 1835674 -nabildanial 23524 1586321 -achambord 23495 1942546 -Sharaf_DG 22975 1790697 -chriswk 22876 1947731 -ncfish1 22689 1830009 -cuistot 22201 1383031 -Zirie 21171 1493227 -Isidor 20634 1736219 -JanErik 20596 1791991 -xor12 20535 1819280 -team-oh 20364 1653708 -nesoneg 20264 1493435 -dex 20110 1682756 -rstoesser 19802 1335177 -Vizvezdenec 19750 1695579 -eastorwest 19531 1841839 -sg4032 18913 1720157 -horst.prack 18425 1708197 -cisco2015 18408 1793774 -ianh2105 18133 1668562 -MazeOfGalious 18022 1644593 -ville 17900 1539130 -j3corre 17607 975954 -eudhan 17502 1424648 -jmdana 17351 1287546 -iisiraider 17175 1118788 -jundery 17172 1115855 -wei 16852 1822582 -SFTUser 16635 1363975 -purplefishies 16621 1106850 -DragonLord 16599 1252348 -chris 15274 1575333 -IgorLeMasson 15201 1364148 -dju 15074 914278 -Flopzee 14700 1331632 -OssumOpossum 14149 1029265 -enedene 13762 935618 -ako027ako 13442 1250249 -AdrianSA 13324 924980 -bpfliegel 13318 886523 -Nikolay.IT 13260 1155612 -jpulman 12776 854815 -joster 12438 988413 -fatmurphy 12015 901134 -Nesa92 11711 1132245 -Adrian.Schmidt123 11542 898699 -modolief 11228 926456 -Dark_wizzie 11214 1017910 -mschmidt 10973 818594 -Andrew Grant 10780 947859 -infinity 10762 746397 -SapphireBrand 10692 1024604 -Thomas A. Anderson 10553 736094 -basepi 10434 935168 -lantonov 10325 972610 -pgontarz 10294 878746 -Spprtr 10189 823246 -crocogoat 10115 1017325 -stocky 10083 718114 \ No newline at end of file +Username CPU Hours Games played +-------------------------------------------------- +noobpwnftw 9305707 695548021 +mlang 780050 61648867 +dew 621626 43921547 +mibere 524702 42238645 +crunchy 354587 27344275 +cw 354495 27274181 +fastgm 332801 22804359 +JojoM 295750 20437451 +CSU_Dynasty 262015 21828122 +Fisherman 232181 18939229 +ctoks 218866 17622052 +glinscott 201989 13780820 +tvijlbrief 201204 15337115 +velislav 188630 14348485 +gvreuls 187164 15149976 +bking_US 180289 11876016 +nordlandia 172076 13467830 +leszek 157152 11443978 +Thanar 148021 12365359 +spams 141975 10319326 +drabel 138073 11121749 +vdv 137850 9394330 +mgrabiak 133578 10454324 +TueRens 132485 10878471 +bcross 129683 11557084 +marrco 126078 9356740 +sqrt2 125830 9724586 +robal 122873 9593418 +vdbergh 120766 8926915 +malala 115926 8002293 +CoffeeOne 114241 5004100 +dsmith 113189 7570238 +BrunoBanani 104644 7436849 +Data 92328 8220352 +mhoram 89333 6695109 +davar 87924 7009424 +xoto 81094 6869316 +ElbertoOne 80899 7023771 +grandphish2 78067 6160199 +brabos 77212 6186135 +psk 75733 5984901 +BRAVONE 73875 5054681 +sunu 70771 5597972 +sterni1971 70605 5590573 +MaZePallas 66886 5188978 +Vizvezdenec 63708 4967313 +nssy 63462 5259388 +jromang 61634 4940891 +teddybaer 61231 5407666 +Pking_cda 60099 5293873 +solarlight 57469 5028306 +dv8silencer 56913 3883992 +tinker 54936 4086118 +renouve 49732 3501516 +Freja 49543 3733019 +robnjr 46972 4053117 +rap 46563 3219146 +Bobo1239 46036 3817196 +ttruscott 45304 3649765 +racerschmacer 44881 3975413 +finfish 44764 3370515 +eva42 41783 3599691 +biffhero 40263 3111352 +bigpen0r 39817 3291647 +mhunt 38871 2691355 +ronaldjerum 38820 3240695 +Antihistamine 38785 2761312 +pb00067 38038 3086320 +speedycpu 37591 3003273 +rkl 37207 3289580 +VoyagerOne 37050 3441673 +jbwiebe 35320 2805433 +cuistot 34191 2146279 +homyur 33927 2850481 +manap 32873 2327384 +gri 32538 2515779 +oryx 31267 2899051 +EthanOConnor 30959 2090311 +SC 30832 2730764 +csnodgrass 29505 2688994 +jmdana 29458 2205261 +strelock 28219 2067805 +jkiiski 27832 1904470 +Pyafue 27533 1902349 +Garf 27515 2747562 +eastorwest 27421 2317535 +slakovv 26903 2021889 +Prcuvu 24835 2170122 +anst 24714 2190091 +hyperbolic.tom 24319 2017394 +Patrick_G 23687 1801617 +Sharaf_DG 22896 1786697 +nabildanial 22195 1519409 +chriswk 21931 1868317 +achambord 21665 1767323 +Zirie 20887 1472937 +team-oh 20217 1636708 +Isidor 20096 1680691 +ncfish1 19931 1520927 +nesoneg 19875 1463031 +Spprtr 19853 1548165 +JanErik 19849 1703875 +agg177 19478 1395014 +SFTUser 19231 1567999 +xor12 19017 1680165 +sg4032 18431 1641865 +rstoesser 18118 1293588 +MazeOfGalious 17917 1629593 +j3corre 17743 941444 +cisco2015 17725 1690126 +ianh2105 17706 1632562 +dex 17678 1467203 +jundery 17194 1115855 +iisiraider 17019 1101015 +horst.prack 17012 1465656 +Adrian.Schmidt123 16563 1281436 +purplefishies 16342 1092533 +wei 16274 1745989 +ville 16144 1384026 +eudhan 15712 1283717 +OuaisBla 15581 972000 +DragonLord 15559 1162790 +dju 14716 875569 +chris 14479 1487385 +0xB00B1ES 14079 1001120 +OssumOpossum 13776 1007129 +enedene 13460 905279 +bpfliegel 13346 884523 +Ente 13198 1156722 +IgorLeMasson 13087 1147232 +jpulman 13000 870599 +ako027ako 12775 1173203 +Nikolay.IT 12352 1068349 +Andrew Grant 12327 895539 +joster 12008 950160 +AdrianSA 11996 804972 +Nesa92 11455 1111993 +fatmurphy 11345 853210 +Dark_wizzie 11108 1007152 +modolief 10869 896470 +mschmidt 10757 803401 +infinity 10594 727027 +mabichito 10524 749391 +Thomas A. Anderson 10474 732094 +thijsk 10431 719357 +Flopzee 10339 894821 +crocogoat 10104 1013854 +SapphireBrand 10104 969604 +stocky 10017 699440 diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 58f05e668a5..f906e731b40 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/bitbase.cpp b/src/bitbase.cpp index 9301dcfad13..78614fa2acb 100644 --- a/src/bitbase.cpp +++ b/src/bitbase.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 2afd3766bab..45d51504087 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/bitboard.h b/src/bitboard.h index 8d748eeed86..440de1ea1ee 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/endgame.cpp b/src/endgame.cpp index ca38a662a01..276b942eac5 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/endgame.h b/src/endgame.h index e29f877782c..4642e44857b 100644 --- a/src/endgame.h +++ b/src/endgame.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 27fcd477630..3a4adef3006 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/evaluate.h b/src/evaluate.h index cccdd25d2f3..077de70c974 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/main.cpp b/src/main.cpp index 40081e8d0c2..148bf248f53 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/material.cpp b/src/material.cpp index 11d4c687dfe..0e1308780dd 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/material.h b/src/material.h index b472c3fd266..9ab1d81c5e7 100644 --- a/src/material.h +++ b/src/material.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/misc.cpp b/src/misc.cpp index 6f908fd2730..2a5bc603b8a 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/misc.h b/src/misc.h index ddd05e4e13d..b1385c2f80a 100644 --- a/src/misc.h +++ b/src/misc.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/movegen.cpp b/src/movegen.cpp index 0b91582ec99..8f6edffbfc3 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/movegen.h b/src/movegen.h index aeba93add02..c2e7c3f1101 100644 --- a/src/movegen.h +++ b/src/movegen.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/movepick.cpp b/src/movepick.cpp index e39f2afa261..025f5b82cde 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/movepick.h b/src/movepick.h index 105c95d7d13..cdedc9b616f 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/pawns.cpp b/src/pawns.cpp index 04222a2aa4e..c3f7872f9e7 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/pawns.h b/src/pawns.h index 4c041716db8..bd17618f5d8 100644 --- a/src/pawns.h +++ b/src/pawns.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/position.cpp b/src/position.cpp index 9644e02c1b5..7c226deec40 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/position.h b/src/position.h index 2ec2729cf3a..783bb4a3c33 100644 --- a/src/position.h +++ b/src/position.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/psqt.cpp b/src/psqt.cpp index c11dc5ba68c..647bd8642df 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/search.cpp b/src/search.cpp index 25a7cf44044..f357db5e43c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/search.h b/src/search.h index c77ca3ad899..a900d094b0c 100644 --- a/src/search.h +++ b/src/search.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index c04534921ea..99f1834b498 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1,7 +1,7 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (c) 2013 Ronald de Man - Copyright (C) 2016-2019 Marco Costalba, Lucas Braesch + Copyright (C) 2016-2020 Marco Costalba, Lucas Braesch Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/syzygy/tbprobe.h b/src/syzygy/tbprobe.h index 264f6e84a45..df3ca4feccc 100644 --- a/src/syzygy/tbprobe.h +++ b/src/syzygy/tbprobe.h @@ -1,7 +1,7 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (c) 2013 Ronald de Man - Copyright (C) 2016-2019 Marco Costalba, Lucas Braesch + Copyright (C) 2016-2020 Marco Costalba, Lucas Braesch Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/thread.cpp b/src/thread.cpp index 6eb00d63a64..f55bcb222a6 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/thread.h b/src/thread.h index a1545072843..4de30edb4ce 100644 --- a/src/thread.h +++ b/src/thread.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/thread_win32_osx.h b/src/thread_win32_osx.h index f8cb466b71e..0ef5c98132a 100644 --- a/src/thread_win32_osx.h +++ b/src/thread_win32_osx.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/timeman.cpp b/src/timeman.cpp index 484aaa65998..0848be420c0 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/timeman.h b/src/timeman.h index 41befff0af5..9301dc946f7 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/tt.cpp b/src/tt.cpp index d3cd094eed9..0b4a59de559 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/tt.h b/src/tt.h index d087cc38f77..98b054d3972 100644 --- a/src/tt.h +++ b/src/tt.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/types.h b/src/types.h index 13c3bbf236c..902c2cfce87 100644 --- a/src/types.h +++ b/src/types.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/uci.cpp b/src/uci.cpp index 6f0bdd769d1..9c84ade3128 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/uci.h b/src/uci.h index 31b63e2f6fd..b845889bcc1 100644 --- a/src/uci.h +++ b/src/uci.h @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 23c0c4805b8..26fcf30227a 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -2,7 +2,7 @@ Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by From bae019b53e5c2bfcf0d69b4ebfc52b4f4de762eb Mon Sep 17 00:00:00 2001 From: joergoster Date: Thu, 12 Dec 2019 12:53:47 +0100 Subject: [PATCH 0111/1766] 50-moves rule improvement for transposition table User "adentong" reported recently of a game where Stockfish blundered a game in a tournament because during a search there was an hash-table issue for positions inside the tree very close to the 50-moves draw rule. This is part of a problem which is commonly referred to as the Graph History Interaction (GHI), and is difficult to solve in computer chess because storing the 50-moves counter in the hash-table loses Elo in general. Links: Issue 2451 : https://github.com/official-stockfish/Stockfish/issues/2451 About the GHI : https://www.chessprogramming.org/Graph_History_Interaction This patch tries to address the issue in this particular game and similar reported games: it prevents that values from the transposition table are getting used when the 50-move counter is close to reaching 100 (). The idea is that in such cases values from previous searches, with a much lower 50-move count, become less and less reliable. More precisely, the heuristic we use in this patch is that we don't take the transposition table cutoff when we have reached a 45-moves limit, but let the search continue doing its job. There is a possible slowdown involved, but it will also help to find either a draw when it thought to be losing, or a way to avoid the draw by 50-move rule. This heuristics probably will not fix all possible cases, but seems to be working reasonably well in practice while not losing too much Elo. Passed non-regression tests: STC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 274452 W: 59700 L: 60075 D: 154677 http://tests.stockfishchess.org/tests/view/5df546116932658fe9b451bf LTC: LLR: 2.95 (-2.94,2.94) [-3.00,1.00] Total: 95235 W: 15297 L: 15292 D: 64646 http://tests.stockfishchess.org/tests/view/5df69c926932658fe9b4520e Closes https://github.com/official-stockfish/Stockfish/pull/2453 Bench: 4586187 --- src/search.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index f357db5e43c..dfc0e5bf79a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -716,7 +716,9 @@ namespace { update_continuation_histories(ss, pos.moved_piece(ttMove), to_sq(ttMove), penalty); } } - return ttValue; + + if (pos.rule50_count() < 90) + return ttValue; } // Step 5. Tablebases probe From 384bff4264f199ded8fa28d241ce0e7dc021a97c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Thu, 9 Jan 2020 20:49:13 +0100 Subject: [PATCH 0112/1766] Assorted trivial cleanups January 2020 Assorted trivial cleanups. No functional change --- src/endgame.cpp | 4 ++-- src/evaluate.cpp | 25 ++++++++++++------------- src/position.cpp | 34 +++++++++++++++++----------------- src/position.h | 6 +++++- src/syzygy/tbprobe.cpp | 4 ++-- 5 files changed, 38 insertions(+), 35 deletions(-) diff --git a/src/endgame.cpp b/src/endgame.cpp index 276b942eac5..2ed6ebc27b7 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -155,7 +155,7 @@ Value Endgame::operator()(const Position& pos) const { Square loserKSq = pos.square(weakSide); Square bishopSq = pos.square(strongSide); - // If our Bishop does not attack A1/H8, we flip the enemy king square + // If our bishop does not attack A1/H8, we flip the enemy king square // to drive to opposite corners (A8/H1). Value result = VALUE_KNOWN_WIN @@ -167,7 +167,7 @@ Value Endgame::operator()(const Position& pos) const { } -/// KP vs K. This endgame is evaluated with the help of a bitbase. +/// KP vs K. This endgame is evaluated with the help of a bitbase template<> Value Endgame::operator()(const Position& pos) const { diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 3a4adef3006..7c7ce95cebc 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -317,20 +317,19 @@ namespace { // Bonus for bishop on a long diagonal which can "see" both center squares if (more_than_one(attacks_bb(s, pos.pieces(PAWN)) & Center)) score += LongDiagonalBishop; - } - // An important Chess960 pattern: A cornered bishop blocked by a friendly - // pawn diagonally in front of it is a very serious problem, especially - // when that pawn is also blocked. - if ( Pt == BISHOP - && pos.is_chess960() - && (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1))) - { - Direction d = pawn_push(Us) + (file_of(s) == FILE_A ? EAST : WEST); - if (pos.piece_on(s + d) == make_piece(Us, PAWN)) - score -= !pos.empty(s + d + pawn_push(Us)) ? CorneredBishop * 4 - : pos.piece_on(s + d + d) == make_piece(Us, PAWN) ? CorneredBishop * 2 - : CorneredBishop; + // An important Chess960 pattern: a cornered bishop blocked by a friendly + // pawn diagonally in front of it is a very serious problem, especially + // when that pawn is also blocked. + if ( pos.is_chess960() + && (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1))) + { + Direction d = pawn_push(Us) + (file_of(s) == FILE_A ? EAST : WEST); + if (pos.piece_on(s + d) == make_piece(Us, PAWN)) + score -= !pos.empty(s + d + pawn_push(Us)) ? CorneredBishop * 4 + : pos.piece_on(s + d + d) == make_piece(Us, PAWN) ? CorneredBishop * 2 + : CorneredBishop; + } } } diff --git a/src/position.cpp b/src/position.cpp index 7c226deec40..53d9b64e9ff 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -817,7 +817,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { st->nonPawnMaterial[us] += PieceValue[MG][promotion]; } - // Update pawn hash key and prefetch access to pawnsTable + // Update pawn hash key st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; // Reset rule 50 draw counter @@ -944,7 +944,7 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ } -/// Position::do(undo)_null_move() is used to do(undo) a "null move": It flips +/// Position::do(undo)_null_move() is used to do(undo) a "null move": it flips /// the side to move without executing any move on the board. void Position::do_null_move(StateInfo& newSt) { @@ -1027,16 +1027,16 @@ bool Position::see_ge(Move m, Value threshold) const { if (swap <= 0) return true; - Bitboard occ = pieces() ^ from ^ to; + Bitboard occupied = pieces() ^ from ^ to; Color stm = color_of(piece_on(from)); - Bitboard attackers = attackers_to(to, occ); + Bitboard attackers = attackers_to(to, occupied); Bitboard stmAttackers, bb; int res = 1; while (true) { stm = ~stm; - attackers &= occ; + attackers &= occupied; // If stm has no more attackers then give up: stm loses if (!(stmAttackers = attackers & pieces(stm))) @@ -1044,7 +1044,7 @@ bool Position::see_ge(Move m, Value threshold) const { // Don't allow pinned pieces to attack (except the king) as long as // there are pinners on their original square. - if (st->pinners[~stm] & occ) + if (st->pinners[~stm] & occupied) stmAttackers &= ~st->blockersForKing[stm]; if (!stmAttackers) @@ -1059,8 +1059,8 @@ bool Position::see_ge(Move m, Value threshold) const { if ((swap = PawnValueMg - swap) < res) break; - occ ^= lsb(bb); - attackers |= attacks_bb(to, occ) & pieces(BISHOP, QUEEN); + occupied ^= lsb(bb); + attackers |= attacks_bb(to, occupied) & pieces(BISHOP, QUEEN); } else if ((bb = stmAttackers & pieces(KNIGHT))) @@ -1068,7 +1068,7 @@ bool Position::see_ge(Move m, Value threshold) const { if ((swap = KnightValueMg - swap) < res) break; - occ ^= lsb(bb); + occupied ^= lsb(bb); } else if ((bb = stmAttackers & pieces(BISHOP))) @@ -1076,8 +1076,8 @@ bool Position::see_ge(Move m, Value threshold) const { if ((swap = BishopValueMg - swap) < res) break; - occ ^= lsb(bb); - attackers |= attacks_bb(to, occ) & pieces(BISHOP, QUEEN); + occupied ^= lsb(bb); + attackers |= attacks_bb(to, occupied) & pieces(BISHOP, QUEEN); } else if ((bb = stmAttackers & pieces(ROOK))) @@ -1085,8 +1085,8 @@ bool Position::see_ge(Move m, Value threshold) const { if ((swap = RookValueMg - swap) < res) break; - occ ^= lsb(bb); - attackers |= attacks_bb(to, occ) & pieces(ROOK, QUEEN); + occupied ^= lsb(bb); + attackers |= attacks_bb(to, occupied) & pieces(ROOK, QUEEN); } else if ((bb = stmAttackers & pieces(QUEEN))) @@ -1094,9 +1094,9 @@ bool Position::see_ge(Move m, Value threshold) const { if ((swap = QueenValueMg - swap) < res) break; - occ ^= lsb(bb); - attackers |= (attacks_bb(to, occ) & pieces(BISHOP, QUEEN)) - | (attacks_bb(to, occ) & pieces(ROOK , QUEEN)); + occupied ^= lsb(bb); + attackers |= (attacks_bb(to, occupied) & pieces(BISHOP, QUEEN)) + | (attacks_bb(to, occupied) & pieces(ROOK , QUEEN)); } else // KING @@ -1105,7 +1105,7 @@ bool Position::see_ge(Move m, Value threshold) const { return (attackers & ~pieces(stm)) ? res ^ 1 : res; } - return res; + return bool(res); } /// Position::is_draw() tests whether the position is drawn by 50-move rule diff --git a/src/position.h b/src/position.h index 783bb4a3c33..6791455fd00 100644 --- a/src/position.h +++ b/src/position.h @@ -46,7 +46,6 @@ struct StateInfo { Square epSquare; // Not copied when making a move (will be recomputed anyhow) - int repetition; Key key; Bitboard checkersBB; Piece capturedPiece; @@ -54,6 +53,7 @@ struct StateInfo { Bitboard blockersForKing[COLOR_NB]; Bitboard pinners[COLOR_NB]; Bitboard checkSquares[PIECE_TYPE_NB]; + int repetition; }; /// A list to keep track of the position states along the setup moves (from the @@ -277,10 +277,14 @@ inline int Position::castling_rights(Color c) const { } inline bool Position::castling_impeded(CastlingRights cr) const { + assert(cr == WHITE_OO || cr == WHITE_OOO || cr == BLACK_OO || cr == BLACK_OOO); + return byTypeBB[ALL_PIECES] & castlingPath[cr]; } inline Square Position::castling_rook_square(CastlingRights cr) const { + assert(cr == WHITE_OO || cr == WHITE_OOO || cr == BLACK_OO || cr == BLACK_OOO); + return castlingRookSquare[cr]; } diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 99f1834b498..721a0ef5ba6 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -683,7 +683,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu bool blackStronger = (pos.material_key() != entry->key); int flipColor = (symmetricBlackToMove || blackStronger) * 8; - int flipSquares = (symmetricBlackToMove || blackStronger) * 070; + int flipSquares = (symmetricBlackToMove || blackStronger) * 56; int stm = (symmetricBlackToMove || blackStronger) ^ pos.side_to_move(); // For pawns, TB files store 4 separate tables according if leading pawn is on @@ -762,7 +762,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu // piece is below RANK_5. if (rank_of(squares[0]) > RANK_4) for (int i = 0; i < size; ++i) - squares[i] ^= 070; // Vertical flip: SQ_A8 -> SQ_A1 + squares[i] ^= SQ_A8; // Vertical flip: SQ_A8 -> SQ_A1 // Look for the first piece of the leading group not on the A1-D4 diagonal // and ensure it is mapped below the diagonal. From 7f623206f413b96170d432b401fe3c647325d01a Mon Sep 17 00:00:00 2001 From: protonspring Date: Fri, 3 Jan 2020 11:33:18 -0700 Subject: [PATCH 0113/1766] Rewrite initialization of PseudoMoves This is a non-functional code style change. I believe master is a bit convoluted here and propose this version for clarity. No functional change --- src/bitboard.cpp | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 45d51504087..70114f20fad 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -76,25 +76,29 @@ void Bitboards::init() { for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) - SquareDistance[s1][s2] = std::max(distance(s1, s2), distance(s1, s2)); - - int steps[][5] = { {}, { 7, 9 }, { 6, 10, 15, 17 }, {}, {}, {}, { 1, 7, 8, 9 } }; - - 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) - { - Square to = s + Direction(c == WHITE ? steps[pt][i] : -steps[pt][i]); - - if (is_ok(to) && distance(s, to) < 3) - { - if (pt == PAWN) - PawnAttacks[c][s] |= to; - else - PseudoAttacks[pt][s] |= to; - } - } + SquareDistance[s1][s2] = std::max(distance(s1, s2), distance(s1, s2)); + + for (Square s = SQ_A1; s <= SQ_H8; ++s) + { + PawnAttacks[WHITE][s] = pawn_attacks_bb(square_bb(s)); + PawnAttacks[BLACK][s] = pawn_attacks_bb(square_bb(s)); + } + + // Helper returning the target bitboard of a step from a square + auto landing_square_bb = [&](Square s, int step) + { + Square to = Square(s + step); + return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0); + }; + + for (Square s = SQ_A1; s <= SQ_H8; ++s) + { + for (int step : {-9, -8, -7, -1, 1, 7, 8, 9} ) + PseudoAttacks[KING][s] |= landing_square_bb(s, step); + + for (int step : {-17, -15, -10, -6, 6, 10, 15, 17} ) + PseudoAttacks[KNIGHT][s] |= landing_square_bb(s, step); + } Direction RookDirections[] = { NORTH, EAST, SOUTH, WEST }; Direction BishopDirections[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }; From 114ddb789bed2d74d6a786f5da6c9ce63d44de27 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 10 Jan 2020 03:02:09 +0100 Subject: [PATCH 0114/1766] Update Elo estimates for terms in search This updates estimates from 1.5 year ago, and adds missing terms. All estimates from tests run on fishtest at 10+0.1 (STC), 20000 games, error bars +- 3 Elo, see the original message in the pull request for the full list of tests. Noteworthy changes are step 7 (futility pruning) going from ~30 to ~50 Elo and step 13 (pruning at shallow depth) going from ~170 to ~200 Elo. Full list of tests: https://github.com/official-stockfish/Stockfish/pull/2401 @Rocky640 made the suggestion to look at time control dependence of these terms. I picked two large terms (early futility pruning and singular extension), so with small relative error. It turns out it is actually quite interesting (see figure 1). Contrary to my expectation, the Elo gain for early futility pruning is pretty time control sensitive, while singular extension gain is not. Figure 1: TC dependence of two search terms ![elo_search_tc]( http://cassio.free.fr/divers/elo_search_tc.png ) Going back to the old measurement of futility pruning (30 Elo vs today 50 Elo), the code is actually identical but the margins have changed. It seems like a nice example of how connected terms in search really are, i.e. the value of early futility pruning increased significantly due to changes elsewhere in search. No functional change. --- src/search.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index dfc0e5bf79a..6146bdf6b5a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -808,7 +808,7 @@ namespace { tte->save(posKey, VALUE_NONE, ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); } - // Step 7. Razoring (~2 Elo) + // Step 7. Razoring (~1 Elo) if ( !rootNode // The required rootNode PV handling is not available in qsearch && depth < 2 && eval <= alpha - RazorMargin) @@ -817,7 +817,7 @@ namespace { improving = (ss-2)->staticEval == VALUE_NONE ? (ss->staticEval >= (ss-4)->staticEval || (ss-4)->staticEval == VALUE_NONE) : ss->staticEval >= (ss-2)->staticEval; - // Step 8. Futility pruning: child node (~30 Elo) + // Step 8. Futility pruning: child node (~50 Elo) if ( !PvNode && depth < 6 && eval - futility_margin(depth, improving) >= beta @@ -917,7 +917,7 @@ namespace { } } - // Step 11. Internal iterative deepening (~2 Elo) + // Step 11. Internal iterative deepening (~1 Elo) if (depth >= 7 && !ttMove) { search(pos, ss, alpha, beta, depth - 7, cutNode); @@ -982,7 +982,7 @@ namespace { // Calculate new depth for this move newDepth = depth - 1; - // Step 13. Pruning at shallow depth (~170 Elo) + // Step 13. Pruning at shallow depth (~200 Elo) if ( !rootNode && pos.non_pawn_material(us) && bestValue > VALUE_MATED_IN_MAX_PLY) @@ -1002,7 +1002,7 @@ namespace { && (*contHist[1])[movedPiece][to_sq(move)] < CounterMovePruneThreshold) continue; - // Futility pruning: parent node (~2 Elo) + // Futility pruning: parent node (~5 Elo) if ( lmrDepth < 6 && !inCheck && ss->staticEval + 255 + 182 * lmrDepth <= alpha @@ -1012,17 +1012,17 @@ namespace { + (*contHist[3])[movedPiece][to_sq(move)] < 30000) continue; - // Prune moves with negative SEE (~10 Elo) + // Prune moves with negative SEE (~20 Elo) if (!pos.see_ge(move, Value(-(32 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth))) continue; } - else if (!pos.see_ge(move, Value(-194) * depth)) // (~20 Elo) + else if (!pos.see_ge(move, Value(-194) * depth)) // (~25 Elo) continue; } - // Step 14. Extensions (~70 Elo) + // Step 14. Extensions (~75 Elo) - // Singular extension search (~60 Elo). If all moves but one fail low on a + // Singular extension search (~70 Elo). If all moves but one fail low on a // search of (alpha-s, beta-s), and just one fails high on (alpha, beta), // 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 @@ -1101,7 +1101,7 @@ namespace { // Step 15. Make the move pos.do_move(move, st, givesCheck); - // Step 16. Reduced depth search (LMR). If the move fails high it will be + // Step 16. Reduced depth search (LMR, ~200 Elo). If the move fails high it will be // re-searched at full depth. if ( depth >= 3 && moveCount > 1 + 2 * rootNode @@ -1122,31 +1122,31 @@ namespace { if (th.marked()) r++; - // Decrease reduction if position is or has been on the PV + // Decrease reduction if position is or has been on the PV (~10 Elo) if (ttPv) r -= 2; - // Decrease reduction if opponent's move count is high (~10 Elo) + // Decrease reduction if opponent's move count is high (~5 Elo) if ((ss-1)->moveCount > 14) r--; - // Decrease reduction if ttMove has been singularly extended + // Decrease reduction if ttMove has been singularly extended (~3 Elo) if (singularLMR) r -= 2; if (!captureOrPromotion) { - // Increase reduction if ttMove is a capture (~0 Elo) + // Increase reduction if ttMove is a capture (~5 Elo) if (ttCapture) r++; - // Increase reduction for cut nodes (~5 Elo) + // Increase reduction for cut nodes (~10 Elo) if (cutNode) 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) + // hence break make_move(). (~2 Elo) else if ( type_of(move) == NORMAL && !pos.see_ge(reverse_move(move))) r -= 2; From 9f800a25775ddb5335a20eac92d8d288ca74f4c2 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 0115/1766] Show compiler info at startup This patch shows a description of the compiler used to compile Stockfish, when starting from the console. Usage: ``` ./stockfish compiler ``` Example of output: ``` Stockfish 120120 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 2a5bc603b8a..053ee67ecf6 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -151,6 +151,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 b1385c2f80a..b11c5aa843c 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 9c84ade3128..8b35e6fdd56 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -235,10 +235,11 @@ void UCI::loop(int argc, char* argv[]) { // 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; - 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 69204f0720bba198952fb7a848ed4377430ef433 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Sat, 11 Jan 2020 22:10:22 +0000 Subject: [PATCH 0116/1766] Smarter time management near stop limit This patch makes Stockfish search same depth again if > 60% of optimum time is already used, instead of trying the next iteration. The idea is that the next iteration will generally take about the same amount of time as has already been used in total. When we are likely to begin the last iteration, as judged by total time taken so far > 0.6 * optimum time, searching the last depth again instead of increasing the depth still helps the other threads in lazy SMP and prepares better move ordering for the next moves. STC : LLR: 2.95 (-2.94,2.94) {-1.00,3.00} Total: 13436 W: 2695 L: 2558 D: 8183 Ptnml(0-2): 222, 1538, 3087, 1611, 253 https://tests.stockfishchess.org/tests/view/5e1618a761fe5f83a67dd964 LTC : LLR: 2.94 (-2.94,2.94) {0.00,2.00} Total: 32160 W: 4261 L: 4047 D: 23852 Ptnml(0-2): 211, 2988, 9448, 3135, 247 https://tests.stockfishchess.org/tests/view/5e162ca061fe5f83a67dd96d The code was revised as suggested by @vondele for multithreading: STC (8 threads): LLR: 2.95 (-2.94,2.94) {0.00,2.00} Total: 16640 W: 2049 L: 1885 D: 12706 Ptnml(0-2): 119, 1369, 5158, 1557, 108 https://tests.stockfishchess.org/tests/view/5e19826a2cc590e03c3c2f52 LTC (8 threads): LLR: 2.95 (-2.94,2.94) {-1.00,3.00} Total: 16536 W: 2758 L: 2629 D: 11149 Ptnml(0-2): 182, 1758, 4296, 1802, 224 https://tests.stockfishchess.org/tests/view/5e18b91a27dab692fcf9a140 Thanks to those discussing Stockfish lazy SMP on fishcooking which made me try this, and to @vondele for suggestions and doing related tests. See full discussion in the pull request thread: https://github.com/official-stockfish/Stockfish/pull/2482 Bench: 4586187 --- AUTHORS | 11 +++++++++-- src/search.cpp | 12 +++++++++++- src/thread.cpp | 1 + src/thread.h | 2 +- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index 4638b41a391..33a7a3d5597 100644 --- a/AUTHORS +++ b/AUTHORS @@ -91,6 +91,7 @@ Luca Brivio (lucabrivio) Lucas Braesch (lucasart) Lyudmil Antonov (lantonov) Maciej Żenczykowski (zenczykowski) +Malcolm Campbell (xoto10) Mark Tenzer (31m059) marotear Matthew Lai (matthewlai) @@ -151,6 +152,12 @@ Tracey Emery (basepr1me) Uri Blass (uriblass) Vince Negri (cuddlestmonkey) -# Additionally, we acknowledge the authors of fishtest, -# an essential framework for the development of Stockfish: + +# Additionally, we acknowledge the authors and maintainer of fishtest, +# an amazing and essential framework for the development of Stockfish! +# # https://github.com/glinscott/fishtest/blob/master/AUTHORS + + + + diff --git a/src/search.cpp b/src/search.cpp index 6146bdf6b5a..ec9c6b1d050 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -393,6 +393,8 @@ void Thread::search() { contempt = (us == WHITE ? make_score(ct, ct / 2) : -make_score(ct, ct / 2)); + int searchAgainCounter = 0; + // Iterative deepening loop until requested to stop or the target depth is reached while ( ++rootDepth < MAX_PLY && !Threads.stop @@ -410,6 +412,9 @@ void Thread::search() { size_t pvFirst = 0; pvLast = 0; + if (!Threads.increaseDepth) + searchAgainCounter++; + // MultiPV loop. We perform a full root search for each PV line for (pvIdx = 0; pvIdx < multiPV && !Threads.stop; ++pvIdx) { @@ -445,7 +450,7 @@ void Thread::search() { int failedHighCnt = 0; while (true) { - Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt); + Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt - searchAgainCounter); bestValue = ::search(rootPos, ss, alpha, beta, adjustedDepth, false); // Bring the best move to the front. It is critical that sorting @@ -558,6 +563,11 @@ void Thread::search() { else Threads.stop = true; } + else if ( Threads.increaseDepth + && Time.elapsed() > Time.optimum() * fallingEval * reduction * bestMoveInstability * 0.6) + Threads.increaseDepth = false; + else + Threads.increaseDepth = true; } mainThread->iterValue[iterIdx] = bestValue; diff --git a/src/thread.cpp b/src/thread.cpp index f55bcb222a6..615d482cafa 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -179,6 +179,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states, main()->wait_for_search_finished(); main()->stopOnPonderhit = stop = false; + increaseDepth = true; main()->ponder = ponderMode; Search::Limits = limits; Search::RootMoves rootMoves; diff --git a/src/thread.h b/src/thread.h index 4de30edb4ce..aea86fd5c69 100644 --- a/src/thread.h +++ b/src/thread.h @@ -109,7 +109,7 @@ struct ThreadPool : public std::vector { uint64_t nodes_searched() const { return accumulate(&Thread::nodes); } uint64_t tb_hits() const { return accumulate(&Thread::tbHits); } - std::atomic_bool stop; + std::atomic_bool stop, increaseDepth; private: StateListPtr setupStates; From 01dfdb95dcc92fec97e49c857c96841f16553af2 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Mon, 13 Jan 2020 09:05:49 +0000 Subject: [PATCH 0117/1766] Fix previous patch in case of ponder No functional change --- src/search.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/search.cpp b/src/search.cpp index ec9c6b1d050..7804119febe 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -564,6 +564,7 @@ void Thread::search() { Threads.stop = true; } else if ( Threads.increaseDepth + && !mainThread->ponder && Time.elapsed() > Time.optimum() * fallingEval * reduction * bestMoveInstability * 0.6) Threads.increaseDepth = false; else From 4901218d4cc31658942486ccd1dbadf2d2df783a Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Mon, 13 Jan 2020 02:59:06 +0300 Subject: [PATCH 0118/1766] Tweak futility pruning constants Based on recent improvement of futility pruning by @locutus2 : we lower the futility margin to apply it for more nodes but as a compensation we also lower the history threshold to apply it to less nodes. Further work in tweaking constants can always be done - numbers are guessed "by hand" and are not results of some tuning, maybe there is some more Elo to squeeze from this part of code. Passed STC LLR: 2.98 (-2.94,2.94) {-1.00,3.00} Total: 15300 W: 3081 L: 2936 D: 9283 Ptnml(0-2): 260, 1816, 3382, 1900, 290 http://tests.stockfishchess.org/tests/view/5e18da3b27dab692fcf9a158 Passed LTC LLR: 2.94 (-2.94,2.94) {0.00,2.00} Total: 108670 W: 14509 L: 14070 D: 80091 Ptnml(0-2): 813, 10259, 31736, 10665, 831 http://tests.stockfishchess.org/tests/view/5e18fc9627dab692fcf9a180 Bench: 4643972 --- AUTHORS | 2 +- src/search.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index 33a7a3d5597..a9f141f961e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -153,7 +153,7 @@ Uri Blass (uriblass) Vince Negri (cuddlestmonkey) -# Additionally, we acknowledge the authors and maintainer of fishtest, +# Additionally, we acknowledge the authors and maintainers of fishtest, # an amazing and essential framework for the development of Stockfish! # # https://github.com/glinscott/fishtest/blob/master/AUTHORS diff --git a/src/search.cpp b/src/search.cpp index 7804119febe..035dd40a5c7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1016,11 +1016,11 @@ namespace { // Futility pruning: parent node (~5 Elo) if ( lmrDepth < 6 && !inCheck - && ss->staticEval + 255 + 182 * lmrDepth <= alpha + && ss->staticEval + 235 + 172 * lmrDepth <= alpha && thisThread->mainHistory[us][from_to(move)] + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] - + (*contHist[3])[movedPiece][to_sq(move)] < 30000) + + (*contHist[3])[movedPiece][to_sq(move)] < 25000) continue; // Prune moves with negative SEE (~20 Elo) From 7150183d07a40653aff6ba64a73f27fe049f525d Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Mon, 13 Jan 2020 11:19:03 +0100 Subject: [PATCH 0119/1766] Tweak reductions for captures/promotions From the third move reduce captures and promotions more if remaining depth is low. STC: LLR: 2.94 (-2.94,2.94) {-1.00,3.00} Total: 25218 W: 5008 L: 4837 D: 15373 Ptnml(0-2): 439, 2950, 5717, 3001, 499 http://tests.stockfishchess.org/tests/view/5e1b33abd12216a2857e6359 LTC: LLR: 2.95 (-2.94,2.94) {0.00,2.00} Total: 35491 W: 4760 L: 4524 D: 26207 Ptnml(0-2): 264, 3288, 10413, 3460, 294 http://tests.stockfishchess.org/tests/view/5e1b88d5d12216a2857e6385 Closes https://github.com/official-stockfish/Stockfish/pull/2488 Bench: 4979757 --- src/search.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 035dd40a5c7..c1ed1f9540a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1186,6 +1186,10 @@ namespace { r -= ss->statScore / 16384; } + // Increase reduction for captures/promotions if late move and at low depth + else if (depth < 8 && moveCount > 2) + r++; + Depth d = clamp(newDepth - r, 1, newDepth); value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); From baf184e8d9e22c86407538ab4b4004f58e0e996d Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 13 Jan 2020 15:46:40 +0100 Subject: [PATCH 0120/1766] Tweak late move reductions at root More LMR at root, unless a fail low might happen. passed STC: LLR: 2.94 (-2.94,2.94) {-1.00,3.00} Total: 25428 W: 4960 L: 4789 D: 15679 Ptnml(0-2): 424, 2948, 5832, 3045, 460 http://tests.stockfishchess.org/tests/view/5e1c9afed12216a2857e6401 passed LTC: LLR: 2.94 (-2.94,2.94) {0.00,2.00} Total: 187423 W: 24253 L: 23599 D: 139571 Ptnml(0-2): 1284, 17437, 55536, 18085, 1292 http://tests.stockfishchess.org/tests/view/5e1ceb9975be933c8fe635a3 Closes https://github.com/official-stockfish/Stockfish/pull/2493 Bench: 5156767 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index c1ed1f9540a..0eea4127f80 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1115,7 +1115,7 @@ namespace { // Step 16. Reduced depth search (LMR, ~200 Elo). If the move fails high it will be // re-searched at full depth. if ( depth >= 3 - && moveCount > 1 + 2 * rootNode + && moveCount > 1 + rootNode + (rootNode && bestValue < alpha) && (!rootNode || thisThread->best_move_count(move) == 0) && ( !captureOrPromotion || moveCountPruning From 446a3c2522af66401181f2716e12eb35ca75d8b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Wed, 15 Jan 2020 22:21:15 +0100 Subject: [PATCH 0121/1766] Update Readme.md for the compiler command No functional change --- Readme.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Readme.md b/Readme.md index 5b807ee6355..a759eff665b 100644 --- a/Readme.md +++ b/Readme.md @@ -153,6 +153,14 @@ compile (for instance with Microsoft MSVC) you need to manually set/unset some switches in the compiler command line; see file *types.h* for a quick reference. +When reporting an issue or a bug, please tell us which version and +compiler you used to create your executable. These informations can +be found by typing the following commands in a console: + +``` + ./stockfish + compiler +``` ## Understanding the code base and participating in the project From c3483fa9a7d7c0ffa9fcc32b467ca844cfb63790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Wed, 15 Jan 2020 22:39:27 +0100 Subject: [PATCH 0122/1766] Stockfish 11 Official release version of Stockfish 11. Bench: 5156767 ----------------------- It is our pleasure to release Stockfish 11 to our fans and supporters. Downloads are freely available at http://stockfishchess.org/download/ This version 11 of Stockfish is 50 Elo stronger than the last version, and 150 Elo stronger than the version which famously lost a match to AlphaZero two years ago. This makes Stockfish the strongest chess engine running on your smartphone or normal desktop PC, and we estimate that on a modern four cores CPU, Stockfish 11 could give 1:1000 time odds to the human chess champion having classical time control, and be on par with him. More specific data, including nice cumulative curves for the progression of Stockfish strength over the last seven years, can be found on [our progression page][1], at [Stefan Pohl site][2] or at [NextChessMove][3]. In October 2019 Stockfish has regained its crown in the TCEC competition, beating in the superfinal of season 16 an evolution of the neural-network engine Leela that had won the previous season. This clash of style between an alpha-beta and an neural-network engine produced spectacular chess as always, with Stockfish [emerging victorious this time][0]. Compared to Stockfish 10, we have made hundreds of improvements to the [codebase][4], from the evaluation function (improvements in king attacks, middlegame/endgame transitions, and many more) to the search algorithm (some innovative coordination methods for the searching threads, better pruning of unsound tactical lines, etc), and fixed a couple of bugs en passant. Our testing framework [Fishtest][5] has also seen its share of improvements to continue propelling Stockfish forward. Along with a lot of small enhancements, Fishtest has switched to new SPRT bounds to increase the chance of catching Elo gainers, along with a new testing book and the use of pentanomial statistics to be more resource-efficient. Overall the Stockfish project is an example of open-source at its best, as its buzzing community of programmers sharing ideas and daily reviewing their colleagues' patches proves to be an ideal form to develop innovative ideas for chess programming, while the mathematical accuracy of the testing framework allows us an unparalleled level of quality control for each patch we put in the engine. If you wish, you too can help our ongoing efforts to keep improving it, just [get involved][6] :-) Stockfish is also special in that every chess fan, even if not a programmer, [can easily help][7] the team to improve the engine by connecting their PC to Fishtest and let it play some games in the background to test new patches. Individual contributions vary from 1 to 32 cores, but this year Bojun Guo made it a little bit special by plugging a whole data center during the whole year: it was a vertiginous experience to see Fishtest spikes with 17466 cores connected playing [25600 games/minute][8]. Thanks Guo! The Stockfish team [0]: [1]: [2]: [3]: [4]: [5]: [6]: [7]: [8]: --- src/misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index 053ee67ecf6..95862ebbd36 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -56,7 +56,7 @@ namespace { /// Version number. If Version is left empty, then compile date in the format /// DD-MM-YY and show in engine_info. -const string Version = ""; +const string Version = "11"; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We From bcf9282844f17dbb451231d8ae0a957d1b7be43d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Thu, 23 Jan 2020 17:17:26 +0100 Subject: [PATCH 0123/1766] Restore development version No functional change --- src/misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index 95862ebbd36..053ee67ecf6 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -56,7 +56,7 @@ namespace { /// Version number. If Version is left empty, then compile date in the format /// DD-MM-YY and show in engine_info. -const string Version = "11"; +const string Version = ""; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We From 7a7bcd6359b38651e1bfecf78aa07e633abed5b2 Mon Sep 17 00:00:00 2001 From: protonspring Date: Sat, 21 Dec 2019 15:36:29 -0700 Subject: [PATCH 0124/1766] Simplify signature of remove_piece() This is a non-functional simplification. Instead of passing the piece type for remove_piece, we can rely on the board. The only exception is en-passant which must be explicitly set because the destination square for the capture is not the same as the piece to remove. Verified also in the Chess960 castling case by running a couple of perft, see the pull request discussion: https://github.com/official-stockfish/Stockfish/pull/2460 STC LLR: 2.94 (-2.94,2.94) [-3.00,1.00] Total: 18624 W: 4147 L: 4070 D: 10407 Ptnml(0-2): 223, 1933, 4945, 1938, 260 http://tests.stockfishchess.org/tests/view/5dfeaa93e70446e17e451163 No functional change --- src/position.cpp | 21 +++++++++++---------- src/position.h | 10 ++++++---- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index 53d9b64e9ff..de9722fff2b 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -743,8 +743,6 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { assert(relative_rank(us, to) == RANK_6); assert(piece_on(to) == NO_PIECE); assert(piece_on(capsq) == make_piece(them, PAWN)); - - board[capsq] = NO_PIECE; // Not done by remove_piece() } st->pawnKey ^= Zobrist::psq[captured][capsq]; @@ -753,7 +751,10 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { st->nonPawnMaterial[them] -= PieceValue[MG][captured]; // Update board and piece lists - remove_piece(captured, capsq); + remove_piece(capsq); + + if (type_of(m) == ENPASSANT) + board[capsq] = NO_PIECE; // Update material hash key and prefetch access to materialTable k ^= Zobrist::psq[captured][capsq]; @@ -784,7 +785,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // Move the piece. The tricky Chess960 castling is handled earlier if (type_of(m) != CASTLING) - move_piece(pc, from, to); + move_piece(from, to); // If the moving piece is a pawn do some special extra work if (type_of(pc) == PAWN) @@ -804,7 +805,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { assert(relative_rank(us, to) == RANK_8); assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN); - remove_piece(pc, to); + remove_piece(to); put_piece(promotion, to); // Update hash keys @@ -884,7 +885,7 @@ void Position::undo_move(Move m) { assert(type_of(pc) == promotion_type(m)); assert(type_of(pc) >= KNIGHT && type_of(pc) <= QUEEN); - remove_piece(pc, to); + remove_piece(to); pc = make_piece(us, PAWN); put_piece(pc, to); } @@ -896,7 +897,7 @@ void Position::undo_move(Move m) { } else { - move_piece(pc, to, from); // Put the piece back at the source square + move_piece(to, from); // Put the piece back at the source square if (st->capturedPiece) { @@ -936,9 +937,9 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ to = relative_square(us, kingSide ? SQ_G1 : SQ_C1); // Remove both pieces first since squares could overlap in Chess960 - remove_piece(make_piece(us, KING), Do ? from : to); - remove_piece(make_piece(us, ROOK), Do ? rfrom : rto); - board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do it for us + remove_piece(Do ? from : to); + remove_piece(Do ? rfrom : rto); + board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do this for us put_piece(make_piece(us, KING), Do ? to : from); put_piece(make_piece(us, ROOK), Do ? rto : rfrom); } diff --git a/src/position.h b/src/position.h index 6791455fd00..e5071d537a7 100644 --- a/src/position.h +++ b/src/position.h @@ -174,8 +174,8 @@ class Position { // Other helpers void put_piece(Piece pc, Square s); - void remove_piece(Piece pc, Square s); - void move_piece(Piece pc, Square from, Square to); + void remove_piece(Square s); + void move_piece(Square from, Square to); template void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto); @@ -412,12 +412,13 @@ inline void Position::put_piece(Piece pc, Square s) { psq += PSQT::psq[pc][s]; } -inline void Position::remove_piece(Piece pc, Square s) { +inline void Position::remove_piece(Square s) { // WARNING: This is not a reversible operation. If we remove a piece in // do_move() and then replace it in undo_move() we will put it at the end of // the list and not in its original place, it means index[] and pieceList[] // are not invariant to a do_move() + undo_move() sequence. + Piece pc = board[s]; byTypeBB[ALL_PIECES] ^= s; byTypeBB[type_of(pc)] ^= s; byColorBB[color_of(pc)] ^= s; @@ -430,10 +431,11 @@ inline void Position::remove_piece(Piece pc, Square s) { psq -= PSQT::psq[pc][s]; } -inline void Position::move_piece(Piece pc, Square from, Square to) { +inline void Position::move_piece(Square from, Square to) { // index[from] is not updated and becomes stale. This works as long as index[] // is accessed just by known occupied squares. + Piece pc = board[from]; Bitboard fromTo = from | to; byTypeBB[ALL_PIECES] ^= fromTo; byTypeBB[type_of(pc)] ^= fromTo; From 75dfdeac119d0ce71c36ba5ab4f33318f589b158 Mon Sep 17 00:00:00 2001 From: protonspring Date: Fri, 10 Jan 2020 15:08:47 -0700 Subject: [PATCH 0125/1766] Simplify KPK classify This is a non-functional simplification. If we use the "side to move" of the entry instead of the template, one of the classify methods goes away. Furthermore, I've resolved the colors in some of the statements (we're already assuming direction using NORTH), and used stm (side to move) instead of "us," since this is much clearer to me. This is not tested because it is non-functional, only applies building the bitbase and there are no changes to the binary (on my machine). Closes https://github.com/official-stockfish/Stockfish/pull/2485 No functional change --- src/bitbase.cpp | 56 ++++++++++++++++++++++--------------------------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/src/bitbase.cpp b/src/bitbase.cpp index 78614fa2acb..7bc4a65c259 100644 --- a/src/bitbase.cpp +++ b/src/bitbase.cpp @@ -43,8 +43,8 @@ namespace { // bit 12: side to move (WHITE or BLACK) // bit 13-14: white pawn file (from FILE_A to FILE_D) // bit 15-17: white pawn RANK_7 - rank (from RANK_7 - RANK_7 to RANK_7 - RANK_2) - unsigned index(Color us, Square bksq, Square wksq, Square psq) { - return int(wksq) | (bksq << 6) | (us << 12) | (file_of(psq) << 13) | ((RANK_7 - rank_of(psq)) << 15); + unsigned index(Color stm, Square bksq, Square wksq, Square psq) { + return int(wksq) | (bksq << 6) | (stm << 12) | (file_of(psq) << 13) | ((RANK_7 - rank_of(psq)) << 15); } enum Result { @@ -60,12 +60,9 @@ namespace { KPKPosition() = default; explicit KPKPosition(unsigned idx); operator Result() const { return result; } - Result classify(const std::vector& db) - { return us == WHITE ? classify(db) : classify(db); } + Result classify(const std::vector& db); - template Result classify(const std::vector& db); - - Color us; + Color stm; Square ksq[COLOR_NB], psq; Result result; }; @@ -73,11 +70,11 @@ namespace { } // namespace -bool Bitbases::probe(Square wksq, Square wpsq, Square bksq, Color us) { +bool Bitbases::probe(Square wksq, Square wpsq, Square bksq, Color stm) { assert(file_of(wpsq) <= FILE_D); - unsigned idx = index(us, bksq, wksq, wpsq); + unsigned idx = index(stm, bksq, wksq, wpsq); return KPKBitbase[idx / 32] & (1 << (idx & 0x1F)); } @@ -110,28 +107,28 @@ namespace { ksq[WHITE] = Square((idx >> 0) & 0x3F); ksq[BLACK] = Square((idx >> 6) & 0x3F); - us = Color ((idx >> 12) & 0x01); + stm = Color ((idx >> 12) & 0x01); psq = make_square(File((idx >> 13) & 0x3), Rank(RANK_7 - ((idx >> 15) & 0x7))); // Check if two pieces are on the same square or if a king can be captured if ( distance(ksq[WHITE], ksq[BLACK]) <= 1 || ksq[WHITE] == psq || ksq[BLACK] == psq - || (us == WHITE && (PawnAttacks[WHITE][psq] & ksq[BLACK]))) + || (stm == WHITE && (PawnAttacks[WHITE][psq] & ksq[BLACK]))) result = INVALID; // Immediate win if a pawn can be promoted without getting captured - else if ( us == WHITE + else if ( stm == WHITE && rank_of(psq) == RANK_7 - && ksq[us] != psq + NORTH - && ( distance(ksq[~us], psq + NORTH) > 1 - || (PseudoAttacks[KING][ksq[us]] & (psq + NORTH)))) + && ksq[stm] != psq + NORTH + && ( distance(ksq[~stm], psq + NORTH) > 1 + || (PseudoAttacks[KING][ksq[stm]] & (psq + NORTH)))) result = WIN; // Immediate draw if it is a stalemate or a king captures undefended pawn - else if ( us == BLACK - && ( !(PseudoAttacks[KING][ksq[us]] & ~(PseudoAttacks[KING][ksq[~us]] | PawnAttacks[~us][psq])) - || (PseudoAttacks[KING][ksq[us]] & psq & ~PseudoAttacks[KING][ksq[~us]]))) + else if ( stm == BLACK + && ( !(PseudoAttacks[KING][ksq[stm]] & ~(PseudoAttacks[KING][ksq[~stm]] | PawnAttacks[~stm][psq])) + || (PseudoAttacks[KING][ksq[stm]] & psq & ~PseudoAttacks[KING][ksq[~stm]]))) result = DRAW; // Position will be classified later @@ -139,7 +136,6 @@ namespace { result = UNKNOWN; } - template Result KPKPosition::classify(const std::vector& db) { // White to move: If one move leads to a position classified as WIN, the result @@ -151,27 +147,25 @@ namespace { // of the current position is DRAW. If all moves lead to positions classified // as WIN, the position is classified as WIN, otherwise the current position is // classified as UNKNOWN. - - constexpr Color Them = (Us == WHITE ? BLACK : WHITE); - constexpr Result Good = (Us == WHITE ? WIN : DRAW); - constexpr Result Bad = (Us == WHITE ? DRAW : WIN); + const Result Good = (stm == WHITE ? WIN : DRAW); + const Result Bad = (stm == WHITE ? DRAW : WIN); Result r = INVALID; - Bitboard b = PseudoAttacks[KING][ksq[Us]]; + Bitboard b = PseudoAttacks[KING][ksq[stm]]; while (b) - r |= Us == WHITE ? db[index(Them, ksq[Them] , pop_lsb(&b), psq)] - : db[index(Them, pop_lsb(&b), ksq[Them] , psq)]; + r |= stm == WHITE ? db[index(BLACK, ksq[BLACK] , pop_lsb(&b), psq)] + : db[index(WHITE, pop_lsb(&b), ksq[WHITE], psq)]; - if (Us == WHITE) + if (stm == WHITE) { if (rank_of(psq) < RANK_7) // Single push - r |= db[index(Them, ksq[Them], ksq[Us], psq + NORTH)]; + r |= db[index(BLACK, ksq[BLACK], ksq[WHITE], psq + NORTH)]; if ( rank_of(psq) == RANK_2 // Double push - && psq + NORTH != ksq[Us] - && psq + NORTH != ksq[Them]) - r |= db[index(Them, ksq[Them], ksq[Us], psq + NORTH + NORTH)]; + && psq + NORTH != ksq[WHITE] + && psq + NORTH != ksq[BLACK]) + r |= db[index(BLACK, ksq[BLACK], ksq[WHITE], psq + NORTH + NORTH)]; } return result = r & Good ? Good : r & UNKNOWN ? UNKNOWN : Bad; From f3c83ed46cdc2062c30551f457ac53ad635794ea Mon Sep 17 00:00:00 2001 From: protonspring Date: Mon, 23 Dec 2019 10:58:30 -0700 Subject: [PATCH 0126/1766] Determine opposite colors mathematically This is a non-functional speed-up: master has to access SquareBB twice while this patch determines opposite_colors just using the values of the squares. It doesn't seem to change the overall speed of bench, but calling opposite_colors(...) 10 Million times: master: 39.4 seconds patch: 11.4 seconds. The only data point I have (other than my own tests), is a quite old failed STC test: LLR: -2.93 (-2.94,2.94) [-1.50,4.50] Total: 24308 W: 5331 L: 5330 D: 13647 Ptnml(0-2): 315, 2577, 6326, 2623, 289 http://tests.stockfishchess.org/tests/view/5e010256c13ac2425c4a9a67 Closes https://github.com/official-stockfish/Stockfish/pull/2498 No functional change --- src/bitboard.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bitboard.h b/src/bitboard.h index 440de1ea1ee..d11b7e732b6 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -129,8 +129,8 @@ constexpr bool more_than_one(Bitboard b) { return b & (b - 1); } -inline bool opposite_colors(Square s1, Square s2) { - return bool(DarkSquares & s1) != bool(DarkSquares & s2); +constexpr bool opposite_colors(Square s1, Square s2) { + return (s1 + rank_of(s1) + s2 + rank_of(s2)) & 1; } From 6f1013794cecc179e7432004b8555e64ddca75a5 Mon Sep 17 00:00:00 2001 From: protonspring Date: Thu, 23 Jan 2020 18:18:58 +0100 Subject: [PATCH 0127/1766] Use a std::bitset for KPKBitbase This is a non-functional simplification. Looks like std::bitset works good for the KPKBitbase. Thanks for Jorg Oster for helping get the speed up (the [] accessor is faster than test()). Speed testing: 10k calls to probe: master 9.8 sec patch 9.8 sec. STC LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 100154 W: 19025 L: 18992 D: 62137 Ptnml(0-2): 1397, 11376, 24572, 11254, 1473 http://tests.stockfishchess.org/tests/view/5e21e601346e35ac603b7d2b Closes https://github.com/official-stockfish/Stockfish/pull/2502 No functional change --- src/bitbase.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/bitbase.cpp b/src/bitbase.cpp index 7bc4a65c259..ed6ed2088e1 100644 --- a/src/bitbase.cpp +++ b/src/bitbase.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "bitboard.h" #include "types.h" @@ -31,8 +32,7 @@ namespace { // Positions with the pawn on files E to H will be mirrored before probing. constexpr unsigned MAX_INDEX = 2*24*64*64; // stm * psq * wksq * bksq = 196608 - // Each uint32_t stores results of 32 positions, one per bit - uint32_t KPKBitbase[MAX_INDEX / 32]; + std::bitset KPKBitbase; // A KPK bitbase index is an integer in [0, IndexMax] range // @@ -74,8 +74,7 @@ bool Bitbases::probe(Square wksq, Square wpsq, Square bksq, Color stm) { assert(file_of(wpsq) <= FILE_D); - unsigned idx = index(stm, bksq, wksq, wpsq); - return KPKBitbase[idx / 32] & (1 << (idx & 0x1F)); + return KPKBitbase[index(stm, bksq, wksq, wpsq)]; } @@ -94,10 +93,10 @@ void Bitbases::init() { for (repeat = idx = 0; idx < MAX_INDEX; ++idx) repeat |= (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN); - // Map 32 results into one KPKBitbase[] entry + // Fill the bitbase with the decisive results for (idx = 0; idx < MAX_INDEX; ++idx) if (db[idx] == WIN) - KPKBitbase[idx / 32] |= 1 << (idx & 0x1F); + KPKBitbase.set(idx); } From 7ed817d7e4847196a5ba0a3aded0519000074be7 Mon Sep 17 00:00:00 2001 From: Chess13234 <37609326+Chess13234@users.noreply.github.com> Date: Tue, 21 Jan 2020 18:20:00 +0200 Subject: [PATCH 0128/1766] Minor fixes for misc.cpp Fixes conflict with tune.h STRINGIFY macro. No functional change --- src/misc.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index 053ee67ecf6..484d0b210cb 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -155,9 +155,9 @@ const string engine_info(bool to_uci) { 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) + #define stringify2(x) #x + #define stringify(x) stringify2(x) + #define make_version_string(major, minor, patch) stringify(major) "." stringify(minor) "." stringify(patch) /// Predefined macros hell: /// @@ -171,20 +171,20 @@ const std::string compiler_info() { #ifdef __clang__ compiler += "clang++ "; - compiler += VER_STRING(__clang_major__, __clang_minor__, __clang_patchlevel__); + compiler += make_version_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 += 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 += stringify(_MSC_FULL_VER) "." stringify(_MSC_BUILD); compiler += ")"; #elif __GNUC__ compiler += "g++ (GNUC) "; - compiler += VER_STRING(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); + compiler += make_version_string(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); #else compiler += "Unknown compiler "; compiler += "(unknown version)"; From f63d112c710fcd38c9d4946f38603b9c2b4689a4 Mon Sep 17 00:00:00 2001 From: Guenther Demetz Date: Thu, 16 Jan 2020 08:39:20 +0100 Subject: [PATCH 0129/1766] Use (strict) greater-than-operator for 'improving' Currently on a normal bench run in ~0,7% of cases 'improving' is set to true although the static eval isn't improving at all, just keeping equal. It looks like the strict gt-operator is more appropriate here, since it returns to 'improving' its literal meaning without sideffects. STC {-1.00,3.00} failed yellow: https://tests.stockfishchess.org/tests/view/5e1ec38c8fd5f550e4ae1c28 LLR: -2.93 (-2.94,2.94) {-1.00,3.00} Total: 53155 W: 10170 L: 10109 D: 32876 Ptnml(0-2): 863, 6282, 12251, 6283, 892 non-regression LTC passed: https://tests.stockfishchess.org/tests/view/5e1f1c0d8fd5f550e4ae1c41 LLR: 2.98 (-2.94,2.94) {-1.50,0.50} Total: 23961 W: 3114 L: 3018 D: 17829 Ptnml(0-2): 163, 2220, 7114, 2298, 170 CLoses https://github.com/official-stockfish/Stockfish/pull/2496 bench: 4561386 --- src/search.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 0eea4127f80..21df156c7ce 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -625,7 +625,7 @@ namespace { Move ttMove, move, excludedMove, bestMove; Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue; - bool ttHit, ttPv, inCheck, givesCheck, improving, didLMR, priorCapture; + bool ttHit, ttPv, inCheck, givesCheck, improving , didLMR, priorCapture; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture, singularLMR; Piece movedPiece; int moveCount, captureCount, quietCount; @@ -825,8 +825,9 @@ namespace { && eval <= alpha - RazorMargin) return qsearch(pos, ss, alpha, beta); - improving = (ss-2)->staticEval == VALUE_NONE ? (ss->staticEval >= (ss-4)->staticEval - || (ss-4)->staticEval == VALUE_NONE) : ss->staticEval >= (ss-2)->staticEval; + // (~13 Elo) + improving = (ss-2)->staticEval == VALUE_NONE ? (ss->staticEval > (ss-4)->staticEval + || (ss-4)->staticEval == VALUE_NONE) : ss->staticEval > (ss-2)->staticEval; // Step 8. Futility pruning: child node (~50 Elo) if ( !PvNode From 56e698ef8382cc837507c8910bf2409ebb4aec77 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Tue, 21 Jan 2020 09:28:58 +0100 Subject: [PATCH 0130/1766] Less reduction for escape moves at ttPv nodes At expected PV nodes or nodes which marked as PV node in the hash table, reduce escape moves even one ply less. STC: LLR: 2.94 (-2.94,2.94) {-1.00,3.00} Total: 31795 W: 6140 L: 5953 D: 19702 Ptnml(0-2): 525, 3625, 7455, 3695, 583 http://tests.stockfishchess.org/tests/view/5e25d77fc3b97aa0d75bc013 LTC: LLR: 2.94 (-2.94,2.94) {0.00,2.00} Total: 43975 W: 5708 L: 5454 D: 32813 Ptnml(0-2): 314, 4012, 13070, 4242, 325 http://tests.stockfishchess.org/tests/view/5e2618c1c3b97aa0d75bc03c Closes https://github.com/official-stockfish/Stockfish/pull/2505 Bench: 4475583 --- src/search.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 21df156c7ce..3bd1a92d7fc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -625,7 +625,7 @@ namespace { Move ttMove, move, excludedMove, bestMove; Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue; - bool ttHit, ttPv, inCheck, givesCheck, improving , didLMR, priorCapture; + bool ttHit, ttPv, inCheck, givesCheck, improving, didLMR, priorCapture; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture, singularLMR; Piece movedPiece; int moveCount, captureCount, quietCount; @@ -825,7 +825,6 @@ namespace { && eval <= alpha - RazorMargin) return qsearch(pos, ss, alpha, beta); - // (~13 Elo) improving = (ss-2)->staticEval == VALUE_NONE ? (ss->staticEval > (ss-4)->staticEval || (ss-4)->staticEval == VALUE_NONE) : ss->staticEval > (ss-2)->staticEval; @@ -1161,7 +1160,7 @@ namespace { // hence break make_move(). (~2 Elo) else if ( type_of(move) == NORMAL && !pos.see_ge(reverse_move(move))) - r -= 2; + r -= 2 + ttPv; ss->statScore = thisThread->mainHistory[us][from_to(move)] + (*contHist[0])[movedPiece][to_sq(move)] From 18fc21eba0368fd5e3c4c4b8ee1000c9ac445425 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Wed, 22 Jan 2020 03:54:44 +0300 Subject: [PATCH 0131/1766] Tweak trapped rook penalty This patch greatly increases the endgame penalty for having a trapped rook. Idea was a result of witnessing Stockfish losing some games at CCCC exchanging pieces in the position with a trapped rook which directly lead to a lost endgame. This patch should partially fix such behavior making this penalty high even in deep endgames. Passed STC http://tests.stockfishchess.org/tests/view/5e279d7cc3b97aa0d75bc1c4 LLR: 2.94 (-2.94,2.94) {-1.00,3.00} Total: 8528 W: 1706 L: 1588 D: 5234 Ptnml(0-2): 133, 957, 1985, 1024, 159 Passed LTC http://tests.stockfishchess.org/tests/view/5e27aee4c3b97aa0d75bc1e1 LLR: 2.95 (-2.94,2.94) {0.00,2.00} Total: 88713 W: 11520 L: 11130 D: 66063 Ptnml(0-2): 646, 8170, 26342, 8492, 676 Closes https://github.com/official-stockfish/Stockfish/pull/2510 Bench: 4964462 ---------------------- Comment by Malcolm Campbell: Congrats! I think this might be a common pattern - scores that seem to mainly apply to the midgame are often better with a similar (or at least fairly big) endgame value as well. Maybe there are others eval parameters we can tweak like this... --- src/evaluate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 7c7ce95cebc..fad0771db4a 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -145,7 +145,7 @@ namespace { constexpr Score ThreatByKing = S( 24, 89); constexpr Score ThreatByPawnPush = S( 48, 39); constexpr Score ThreatBySafePawn = S(173, 94); - constexpr Score TrappedRook = S( 52, 10); + constexpr Score TrappedRook = S( 52, 30); constexpr Score WeakQueen = S( 49, 15); #undef S From 01b6088af39902001d2d6844561b6a2faa549282 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Thu, 23 Jan 2020 15:41:03 +0100 Subject: [PATCH 0132/1766] History update for pruned captures Use a SEE pruned capture move for history updates: this patch collects pruned capture moves also in the failed captures list, so that they get an update in capture history. STC: LLR: 2.95 (-2.94,2.94) {-1.00,3.00} Total: 11124 W: 2222 L: 2089 D: 6813 Ptnml(0-2): 186, 1280, 2506, 1381, 200 http://tests.stockfishchess.org/tests/view/5e28995fc3b97aa0d75bc294 LTC: LLR: 2.94 (-2.94,2.94) {0.00,2.00} Total: 25552 W: 3418 L: 3211 D: 18923 Ptnml(0-2): 168, 2354, 7538, 2490, 200 http://tests.stockfishchess.org/tests/view/5e2943734744cfa4d6af415b Closes https://github.com/official-stockfish/Stockfish/pull/2511 Bench: 4810202 --- src/search.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 3bd1a92d7fc..1490a26691f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1028,7 +1028,11 @@ namespace { continue; } else if (!pos.see_ge(move, Value(-194) * depth)) // (~25 Elo) - continue; + { + if (captureOrPromotion && captureCount < 32) + capturesSearched[captureCount++] = move; + continue; + } } // Step 14. Extensions (~75 Elo) From 0ae00454ba6928d181b46103e5c83e6d58fcebe5 Mon Sep 17 00:00:00 2001 From: Lolligerhans Date: Fri, 24 Jan 2020 23:04:35 +0100 Subject: [PATCH 0133/1766] Tweak RestrictedPiece bonus Double the "RestrictedPiece" bonus for restricted moves targeting occupied squares. STC LLR: 3.58 (-2.94,2.94) {-1.00,3.00} Total: 25504 W: 4887 L: 4697 D: 15920 Ptnml(0-2): 387, 2935, 5947, 3051, 422 https://tests.stockfishchess.org/tests/view/5e2aa15dab2d69d58394f94d LTC LLR: 2.94 (-2.94,2.94) {0.00,2.00} Total: 28572 W: 3826 L: 3621 D: 21125 Ptnml(0-2): 224, 2609, 8403, 2791, 239 https://tests.stockfishchess.org/tests/view/5e2ae7f4ab2d69d58394f9a6 Bench: 4719086 --- src/evaluate.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index fad0771db4a..a1a3b4ed267 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -520,11 +520,11 @@ namespace { } // Bonus for restricting their piece moves + // Greater bonus when landing square is occupied b = attackedBy[Them][ALL_PIECES] & ~stronglyProtected & attackedBy[Us][ALL_PIECES]; - - score += RestrictedPiece * popcount(b); + score += RestrictedPiece * (popcount(b) + popcount(b & pos.pieces())); // Protected or unattacked squares safe = ~attackedBy[Them][ALL_PIECES] | attackedBy[Us][ALL_PIECES]; From 6d0eabd5fe2961551477820ab7619e2c31e01ffd Mon Sep 17 00:00:00 2001 From: Alain SAVARD Date: Sat, 25 Jan 2020 07:59:42 -0500 Subject: [PATCH 0134/1766] Dynamic complexity Instead of computing the initiative bonus on the material score + dynamic score compute it on (material score/2) + dynamic score, Passed STC http://tests.stockfishchess.org/tests/view/5e2c4945ab2d69d58394fa8f LLR: 2.94 (-2.94,2.94) {-1.00,3.00} Total: 39387 W: 7594 L: 7386 D: 24407 Ptnml(0-2): 658, 4519, 9165, 4649, 697 Passed LTC http://tests.stockfishchess.org/tests/view/5e2c85ccab2d69d58394faa7 LLR: 2.95 (-2.94,2.94) {0.00,2.00} Total: 32588 W: 4206 L: 3986 D: 24396 Ptnml(0-2): 244, 2909, 9738, 3111, 253 closes https://github.com/official-stockfish/Stockfish/pull/2516 Bench: 4765486 --- src/evaluate.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index a1a3b4ed267..be39f8a7b31 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -168,7 +168,7 @@ namespace { template Score passed() const; template Score space() const; ScaleFactor scale_factor(Value eg) const; - Score initiative(Score score) const; + Score initiative(Score score, Score materialScore) const; const Position& pos; Material::Entry* me; @@ -696,10 +696,7 @@ namespace { // known attacking/defending status of the players. template - Score Evaluation::initiative(Score score) const { - - Value mg = mg_value(score); - Value eg = eg_value(score); + Score Evaluation::initiative(Score score, Score materialScore) const { int outflanking = distance(pos.square(WHITE), pos.square(BLACK)) - distance(pos.square(WHITE), pos.square(BLACK)); @@ -724,6 +721,11 @@ namespace { - 43 * almostUnwinnable - 100 ; + // Give more importance to non-material score + score = (score * 2 - materialScore) / 2; + Value mg = mg_value(score); + Value eg = eg_value(score); + // 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. @@ -792,6 +794,9 @@ namespace { if (abs(v) > LazyThreshold + pos.non_pawn_material() / 64) return pos.side_to_move() == WHITE ? v : -v; + // Remember this score + Score materialScore = score; + // Main evaluation begins here initialize(); @@ -810,7 +815,7 @@ namespace { + passed< WHITE>() - passed< BLACK>() + space< WHITE>() - space< BLACK>(); - score += initiative(score); + score += initiative(score, materialScore); // Interpolate between a middlegame and a (scaled by 'sf') endgame score ScaleFactor sf = scale_factor(eg_value(score)); From 39437f4e55aaa26ef9f0d5a1c762e560e9ffde32 Mon Sep 17 00:00:00 2001 From: Sami Kiminki Date: Sat, 21 Dec 2019 21:41:42 +0200 Subject: [PATCH 0135/1766] Advise the kernel to use huge pages (Linux) Align the TT allocation by 2M to make it huge page friendly and advise the kernel to use huge pages. Benchmarks on my i7-8700K (6C/12T) box: (3 runs per bench per config) vanilla (nps) hugepages (nps) avg ================================================================================== bench | 3012490 3024364 3036331 3071052 3067544 3071052 +1.5% bench 16 12 20 | 19237932 19050166 19085315 19266346 19207025 19548758 +1.1% bench 16384 12 20 | 18182313 18371581 18336838 19381275 19738012 19620225 +7.0% On my box, huge pages have a significant perf impact when using a big hash size. They also speed up TT initialization big time: vanilla (s) huge pages (s) speed-up ======================================================================= time stockfish bench 16384 1 1 | 5.37 1.48 3.6x In practice, huge pages with auto-defrag may always be enabled in the system, in which case this patch has no effect. This depends on the values in /sys/kernel/mm/transparent_hugepage/enabled and /sys/kernel/mm/transparent_hugepage/defrag. closes https://github.com/official-stockfish/Stockfish/pull/2463 No functional change --- src/misc.cpp | 36 +++++++++++++++++++++++++++++++++++- src/misc.h | 1 + src/tt.cpp | 6 ++---- src/tt.h | 16 +++++++--------- 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index 484d0b210cb..0bae9f1e0e9 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -47,6 +47,11 @@ typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY); #include #include +#ifdef __linux__ +#include +#include +#endif + #include "misc.h" #include "thread.h" @@ -190,7 +195,7 @@ const std::string compiler_info() { compiler += "(unknown version)"; #endif - #if defined(__APPLE__) + #if defined(__APPLE__) compiler += " on Apple"; #elif defined(__CYGWIN__) compiler += " on Cygwin"; @@ -288,6 +293,35 @@ void prefetch(void* addr) { #endif + +/// aligned_ttmem_alloc will return suitably aligned memory, and if possible use large pages. +/// The returned pointer is the aligned one, while the mem argument is the one that needs to be passed to free. +/// With c++17 some of this functionality can be simplified. +#ifdef __linux__ + +void* aligned_ttmem_alloc(size_t allocSize, void** mem) { + + constexpr size_t alignment = 2 * 1024 * 1024; // assumed 2MB page sizes + size_t size = ((allocSize + alignment - 1) / alignment) * alignment; // multiple of alignment + *mem = aligned_alloc(alignment, size); + madvise(*mem, allocSize, MADV_HUGEPAGE); + return *mem; +} + +#else + +void* aligned_ttmem_alloc(size_t allocSize, void** mem) { + + constexpr size_t alignment = 64; // assumed cache line size + size_t size = allocSize + alignment - 1; // allocate some extra space + *mem = malloc(size); + void* ret = reinterpret_cast((uintptr_t(*mem) + alignment - 1) & ~uintptr_t(alignment - 1)); + return ret; +} + +#endif + + namespace WinProcGroup { #ifndef _WIN32 diff --git a/src/misc.h b/src/misc.h index b11c5aa843c..45d9951a72a 100644 --- a/src/misc.h +++ b/src/misc.h @@ -33,6 +33,7 @@ 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); +void* aligned_ttmem_alloc(size_t size, void** mem); void dbg_hit_on(bool b); void dbg_hit_on(bool c, bool b); diff --git a/src/tt.cpp b/src/tt.cpp index 0b4a59de559..080d3a6bd48 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -63,11 +63,10 @@ void TranspositionTable::resize(size_t mbSize) { Threads.main()->wait_for_search_finished(); - clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster); - free(mem); - mem = malloc(clusterCount * sizeof(Cluster) + CacheLineSize - 1); + clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster); + table = static_cast(aligned_ttmem_alloc(clusterCount * sizeof(Cluster), &mem)); if (!mem) { std::cerr << "Failed to allocate " << mbSize @@ -75,7 +74,6 @@ void TranspositionTable::resize(size_t mbSize) { exit(EXIT_FAILURE); } - table = (Cluster*)((uintptr_t(mem) + CacheLineSize - 1) & ~(CacheLineSize - 1)); clear(); } diff --git a/src/tt.h b/src/tt.h index 98b054d3972..142afd90e56 100644 --- a/src/tt.h +++ b/src/tt.h @@ -57,24 +57,22 @@ struct TTEntry { }; -/// A TranspositionTable consists of a power of 2 number of clusters and each -/// cluster consists of ClusterSize number of TTEntry. Each non-empty entry -/// contains information of exactly one position. The size of a cluster should -/// divide the size of a cache line size, to ensure that clusters never cross -/// cache lines. This ensures best cache performance, as the cacheline is -/// prefetched, as soon as possible. +/// A TranspositionTable is an array of Cluster, of size clusterCount. Each +/// cluster consists of ClusterSize number of TTEntry. Each non-empty TTEntry +/// contains information on exactly one position. The size of a Cluster should +/// divide the size of a cache line for best performance, +/// as the cacheline is prefetched when possible. class TranspositionTable { - static constexpr int CacheLineSize = 64; static constexpr int ClusterSize = 3; struct Cluster { TTEntry entry[ClusterSize]; - char padding[2]; // Align to a divisor of the cache line size + char padding[2]; // Pad to 32 bytes }; - static_assert(CacheLineSize % sizeof(Cluster) == 0, "Cluster size incorrect"); + static_assert(sizeof(Cluster) == 32, "Unexpected Cluster size"); public: ~TranspositionTable() { free(mem); } From 1d3efff47274bd03d4deced8d7ba0360bd1dba8a Mon Sep 17 00:00:00 2001 From: Alain SAVARD Date: Mon, 27 Jan 2020 09:25:41 -0500 Subject: [PATCH 0136/1766] Dynamic Complexity based on psqt Adjust initiative score by psqt/2 instead of materialScore/2 which simplifies #2516 Passed STC http://tests.stockfishchess.org/tests/view/5e2e667dab2d69d58394fc73 LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 23198 W: 4506 L: 4353 D: 14339 Ptnml(0-2): 396, 2615, 5380, 2728, 418 Passed LTC http://tests.stockfishchess.org/tests/view/5e2ed75cab2d69d58394fcbf LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 8519 W: 1179 L: 1062 D: 6278 Ptnml(0-2): 50, 775, 2472, 843, 74 closes https://github.com/official-stockfish/Stockfish/pull/2522 Bench: 4684459 --- src/evaluate.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index be39f8a7b31..8d7976d5773 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -168,7 +168,7 @@ namespace { template Score passed() const; template Score space() const; ScaleFactor scale_factor(Value eg) const; - Score initiative(Score score, Score materialScore) const; + Score initiative(Score score) const; const Position& pos; Material::Entry* me; @@ -696,7 +696,7 @@ namespace { // known attacking/defending status of the players. template - Score Evaluation::initiative(Score score, Score materialScore) const { + Score Evaluation::initiative(Score score) const { int outflanking = distance(pos.square(WHITE), pos.square(BLACK)) - distance(pos.square(WHITE), pos.square(BLACK)); @@ -722,7 +722,7 @@ namespace { - 100 ; // Give more importance to non-material score - score = (score * 2 - materialScore) / 2; + score = score - pos.psq_score() / 2; Value mg = mg_value(score); Value eg = eg_value(score); @@ -794,9 +794,6 @@ namespace { if (abs(v) > LazyThreshold + pos.non_pawn_material() / 64) return pos.side_to_move() == WHITE ? v : -v; - // Remember this score - Score materialScore = score; - // Main evaluation begins here initialize(); @@ -815,7 +812,7 @@ namespace { + passed< WHITE>() - passed< BLACK>() + space< WHITE>() - space< BLACK>(); - score += initiative(score, materialScore); + score += initiative(score); // Interpolate between a middlegame and a (scaled by 'sf') endgame score ScaleFactor sf = scale_factor(eg_value(score)); From d878bc8cda47044f4db37a954f25e6122dc0d0ca Mon Sep 17 00:00:00 2001 From: 31m059 <37052095+31m059@users.noreply.github.com> Date: Mon, 27 Jan 2020 20:48:01 -0500 Subject: [PATCH 0137/1766] Less NMP if the position was previously in PV. The intention of the patch is to avoid aggressive null move pruning (NMP) in positions that have previously been found to be important (PV nodes). If we already do not apply NMP for current PV nodes, it makes sense to apply it less often for positions that have previously been PV nodes too. STC: LLR: 2.96 (-2.94,2.94) {-1.00,3.00} Total: 14959 W: 2921 L: 2782 D: 9256 Ptnml(0-2): 254, 1679, 3493, 1762, 282 http://tests.stockfishchess.org/tests/view/5e2f6637ab2d69d58394fcfd LTC: LLR: 2.95 (-2.94,2.94) {0.00,2.00} Total: 6442 W: 899 L: 753 D: 4790 Ptnml(0-2): 42, 549, 1885, 659, 61 http://tests.stockfishchess.org/tests/view/5e2f767bab2d69d58394fd04 closes https://github.com/official-stockfish/Stockfish/pull/2525 Bench: 4725546 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 1490a26691f..044187e5d90 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -841,7 +841,7 @@ namespace { && (ss-1)->statScore < 23397 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 32 * depth + 292 - improving * 30 + && ss->staticEval >= beta - 32 * depth - 30 * improving + 120 * ttPv + 292 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) From 71e0b5385e2717679a57c6b77d8c7ac5fff3b89f Mon Sep 17 00:00:00 2001 From: Guenther Demetz Date: Tue, 28 Jan 2020 13:38:03 +0100 Subject: [PATCH 0138/1766] More bonus for bestMoves on past PV nodes It looks like it is important to keep past PV (ttPv) nodes as close as possible to current PV nodes. Credits to Mark Tenzer (31m059) & Stefan Geschwentner who first tried ideas on ttPv nodes. STC: https://tests.stockfishchess.org/tests/view/5e2ff5efab2d69d58394fd52 LLR: 2.95 (-2.94,2.94) {-1.00,3.00} Total: 13302 W: 2647 L: 2507 D: 8148 Ptnml(0-2): 237, 1540, 2956, 1632, 260 LTC: https://tests.stockfishchess.org/tests/view/5e2fff38ab2d69d58394fd55 LLR: 2.95 (-2.94,2.94) {0.00,2.00} Total: 15797 W: 2137 L: 1960 D: 11700 Ptnml(0-2): 96, 1443, 4628, 1547, 130 closes https://github.com/official-stockfish/Stockfish/pull/2529 bench: 5545845 --- src/search.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 044187e5d90..84f9bb23f0e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -158,7 +158,7 @@ namespace { void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus); void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus); void update_all_stats(const Position& pos, Stack* ss, Move bestMove, Value bestValue, Value beta, Square prevSq, - Move* quietsSearched, int quietCount, Move* capturesSearched, int captureCount, Depth depth); + Move* quietsSearched, int quietCount, Move* capturesSearched, int captureCount, Depth depth, bool pastPV); // perft() is our utility to verify move generation. All the leaf nodes up // to the given depth are generated and counted, and the sum is returned. @@ -713,7 +713,7 @@ namespace { if (ttValue >= beta) { if (!pos.capture_or_promotion(ttMove)) - update_quiet_stats(pos, ss, ttMove, stat_bonus(depth)); + update_quiet_stats(pos, ss, ttMove, stat_bonus(depth + (!PvNode && ttPv))); // Extra penalty for early quiet moves of the previous ply if ((ss-1)->moveCount <= 2 && !priorCapture) @@ -722,7 +722,7 @@ namespace { // Penalty for a quiet ttMove that fails low else if (!pos.capture_or_promotion(ttMove)) { - int penalty = -stat_bonus(depth); + int penalty = -stat_bonus(depth + (!PvNode && ttPv)); thisThread->mainHistory[us][from_to(ttMove)] << penalty; update_continuation_histories(ss, pos.moved_piece(ttMove), to_sq(ttMove), penalty); } @@ -1326,7 +1326,7 @@ namespace { else if (bestMove) update_all_stats(pos, ss, bestMove, bestValue, beta, prevSq, - quietsSearched, quietCount, capturesSearched, captureCount, depth); + quietsSearched, quietCount, capturesSearched, captureCount, depth, (!PvNode && ttPv)); // Bonus for prior countermove that caused the fail low else if ( (depth >= 3 || PvNode) @@ -1602,7 +1602,7 @@ namespace { // update_all_stats() updates stats at the end of search() when a bestMove is found void update_all_stats(const Position& pos, Stack* ss, Move bestMove, Value bestValue, Value beta, Square prevSq, - Move* quietsSearched, int quietCount, Move* capturesSearched, int captureCount, Depth depth) { + Move* quietsSearched, int quietCount, Move* capturesSearched, int captureCount, Depth depth, bool pastPV) { int bonus1, bonus2; Color us = pos.side_to_move(); @@ -1612,8 +1612,8 @@ namespace { PieceType captured = type_of(pos.piece_on(to_sq(bestMove))); bonus1 = stat_bonus(depth + 1); - bonus2 = bestValue > beta + PawnValueMg ? bonus1 // larger bonus - : stat_bonus(depth); // smaller bonus + bonus2 = pastPV || bestValue > beta + PawnValueMg ? bonus1 // larger bonus + : stat_bonus(depth); // smaller bonus if (!pos.capture_or_promotion(bestMove)) { From a910ba71eedde4f67805f05b29215cbeff4fe5f1 Mon Sep 17 00:00:00 2001 From: joergoster Date: Mon, 27 Jan 2020 18:53:25 +0100 Subject: [PATCH 0139/1766] Simplify hashfull calculation. We can simplify the calculation of the hashfull info by looping over exact 1,000 entries, and then divide the result by ClusterSize. Somewhat memory accesses, somewhat more accurate. Passed non-regression LTC https://tests.stockfishchess.org/tests/view/5e30079dab2d69d58394fd5d LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 30125 W: 3987 L: 3926 D: 22212 Ptnml(0-2): 177, 2504, 9558, 2642, 141 closes https://github.com/official-stockfish/Stockfish/pull/2523 No functional change. --- src/tt.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tt.cpp b/src/tt.cpp index 080d3a6bd48..46860fe900d 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -148,9 +148,9 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const { int TranspositionTable::hashfull() const { int cnt = 0; - for (int i = 0; i < 1000 / ClusterSize; ++i) + for (int i = 0; i < 1000; ++i) for (int j = 0; j < ClusterSize; ++j) cnt += (table[i].entry[j].genBound8 & 0xF8) == generation8; - return cnt * 1000 / (ClusterSize * (1000 / ClusterSize)); + return cnt / ClusterSize; } From 3b70932b0dee0cf1817baf0daa43ac92e18003c4 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Tue, 28 Jan 2020 16:17:52 +0100 Subject: [PATCH 0140/1766] Fix compilation on android Fall back to the default implementation of aligned_ttmem_alloc, which was introduced as part of 39437f4e55aaa26ef9f0d5a1c762e560e9ffde32 Fixes #2524 No functional change. --- src/misc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index 0bae9f1e0e9..cf18d2e2fc5 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -47,7 +47,7 @@ typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY); #include #include -#ifdef __linux__ +#if defined(__linux__) && !defined(__ANDROID__) #include #include #endif @@ -297,7 +297,7 @@ void prefetch(void* addr) { /// aligned_ttmem_alloc will return suitably aligned memory, and if possible use large pages. /// The returned pointer is the aligned one, while the mem argument is the one that needs to be passed to free. /// With c++17 some of this functionality can be simplified. -#ifdef __linux__ +#if defined(__linux__) && !defined(__ANDROID__) void* aligned_ttmem_alloc(size_t allocSize, void** mem) { From 6ccb1cac5aaaf7337da8b1738448793be63fdfdb Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 30 Jan 2020 21:44:04 +0100 Subject: [PATCH 0141/1766] Revert 5 recent patches Revert 5 patches which were merged, but lead to a regression test that showed negative Elo gain: http://tests.stockfishchess.org/tests/view/5e307251ab2d69d58394fdb9 This was discussed in depth in: https://github.com/official-stockfish/Stockfish/issues/2531 Each patch was removed and tested as a simplification, full list below, and the whole combo as well. After the revert the regression test showed a neutral result: http://tests.stockfishchess.org/tests/view/5e334851708b13464ceea33c As a result of this experience, the SPRT testing bounds will be made more strict. Reverted patches: 1 Dynamic Complexity 6d0eabd5fe2961551477820ab7619e2c31e01ffd : STC 10+0.1 https://tests.stockfishchess.org/tests/view/5e31fcacec661e2e6a340d08 : LLR: 2.97 (-2.94,2.94) {-1.50,0.50} Total: 38130 W: 7326 L: 7189 D: 23615 Ptnml(0-2): 677, 4346, 8843, 4545, 646 LTC 60+0.6 https://tests.stockfishchess.org/tests/view/5e32c18fec661e2e6a340d73 : LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 38675 W: 4941 L: 4866 D: 28868 Ptnml(0-2): 270, 3556, 11429, 3584, 291 3 More bonus for bestMoves on past PV nodes 71e0b5385e2717679a57c6b77d8c7ac5fff3b89f : STC 10+0.1 https://tests.stockfishchess.org/tests/view/5e31fe93ec661e2e6a340d10 : LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 46100 W: 8853 L: 8727 D: 28520 Ptnml(0-2): 796, 5297, 10749, 5387, 813 LTC 60+0.6 https://tests.stockfishchess.org/tests/view/5e32c187ec661e2e6a340d71 : LLR: 2.96 (-2.94,2.94) {-1.50,0.50} Total: 16920 W: 2161 L: 2055 D: 12704 Ptnml(0-2): 115, 1498, 5006, 1569, 130 4 Tweak Restricted Piece Bonus 0ae00454ba6928d181b46103e5c83e6d58fcebe5 : STC 10+0.1 https://tests.stockfishchess.org/tests/view/5e31fefaec661e2e6a340d15 : LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 88328 W: 17060 L: 16997 D: 54271 Ptnml(0-2): 1536, 10446, 20169, 10422, 1581 LTC 60+0.6 https://tests.stockfishchess.org/tests/view/5e32c17aec661e2e6a340d6f : LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 34784 W: 4551 L: 4466 D: 25767 Ptnml(0-2): 255, 3279, 10061, 3345, 262 5 History update for pruned captures 01b6088af39902001d2d6844561b6a2faa549282 : STC 10+0.1 https://tests.stockfishchess.org/tests/view/5e31ff5eec661e2e6a340d1a : LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 29541 W: 5735 L: 5588 D: 18218 Ptnml(0-2): 483, 3445, 6820, 3469, 545 LTC 60+0.6 https://tests.stockfishchess.org/tests/view/5e32c196ec661e2e6a340d75 : LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 22177 W: 2854 L: 2757 D: 16566 Ptnml(0-2): 143, 2005, 6555, 2055, 164 6 Tweak trapped rook penalty 18fc21eba0368fd5e3c4c4b8ee1000c9ac445425 : STC 10+0.1 https://tests.stockfishchess.org/tests/view/5e31ffb1ec661e2e6a340d1c : LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 24476 W: 4727 L: 4569 D: 15180 Ptnml(0-2): 390, 2834, 5659, 2933, 417 LTC 60+0.6 https://tests.stockfishchess.org/tests/view/5e32c19eec661e2e6a340d77 : LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 97332 W: 12492 L: 12466 D: 72374 Ptnml(0-2): 690, 9107, 28738, 9034, 720 All 5 as one simplification : LTC 60+0.6 https://tests.stockfishchess.org/tests/view/5e334098708b13464ceea330 : LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 7829 W: 1079 L: 964 D: 5786 Ptnml(0-2): 52, 690, 2281, 781, 65 Bench: 5153165 --- src/evaluate.cpp | 5 ++--- src/search.cpp | 18 +++++++----------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 8d7976d5773..bf1eee9b9c6 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -145,7 +145,7 @@ namespace { constexpr Score ThreatByKing = S( 24, 89); constexpr Score ThreatByPawnPush = S( 48, 39); constexpr Score ThreatBySafePawn = S(173, 94); - constexpr Score TrappedRook = S( 52, 30); + constexpr Score TrappedRook = S( 52, 10); constexpr Score WeakQueen = S( 49, 15); #undef S @@ -524,7 +524,7 @@ namespace { b = attackedBy[Them][ALL_PIECES] & ~stronglyProtected & attackedBy[Us][ALL_PIECES]; - score += RestrictedPiece * (popcount(b) + popcount(b & pos.pieces())); + score += RestrictedPiece * popcount(b); // Protected or unattacked squares safe = ~attackedBy[Them][ALL_PIECES] | attackedBy[Us][ALL_PIECES]; @@ -722,7 +722,6 @@ namespace { - 100 ; // Give more importance to non-material score - score = score - pos.psq_score() / 2; Value mg = mg_value(score); Value eg = eg_value(score); diff --git a/src/search.cpp b/src/search.cpp index 84f9bb23f0e..985af7bc2dc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -158,7 +158,7 @@ namespace { void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus); void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus); void update_all_stats(const Position& pos, Stack* ss, Move bestMove, Value bestValue, Value beta, Square prevSq, - Move* quietsSearched, int quietCount, Move* capturesSearched, int captureCount, Depth depth, bool pastPV); + Move* quietsSearched, int quietCount, Move* capturesSearched, int captureCount, Depth depth); // perft() is our utility to verify move generation. All the leaf nodes up // to the given depth are generated and counted, and the sum is returned. @@ -713,7 +713,7 @@ namespace { if (ttValue >= beta) { if (!pos.capture_or_promotion(ttMove)) - update_quiet_stats(pos, ss, ttMove, stat_bonus(depth + (!PvNode && ttPv))); + update_quiet_stats(pos, ss, ttMove, stat_bonus(depth)); // Extra penalty for early quiet moves of the previous ply if ((ss-1)->moveCount <= 2 && !priorCapture) @@ -722,7 +722,7 @@ namespace { // Penalty for a quiet ttMove that fails low else if (!pos.capture_or_promotion(ttMove)) { - int penalty = -stat_bonus(depth + (!PvNode && ttPv)); + int penalty = -stat_bonus(depth); thisThread->mainHistory[us][from_to(ttMove)] << penalty; update_continuation_histories(ss, pos.moved_piece(ttMove), to_sq(ttMove), penalty); } @@ -1028,11 +1028,7 @@ namespace { continue; } else if (!pos.see_ge(move, Value(-194) * depth)) // (~25 Elo) - { - if (captureOrPromotion && captureCount < 32) - capturesSearched[captureCount++] = move; continue; - } } // Step 14. Extensions (~75 Elo) @@ -1326,7 +1322,7 @@ namespace { else if (bestMove) update_all_stats(pos, ss, bestMove, bestValue, beta, prevSq, - quietsSearched, quietCount, capturesSearched, captureCount, depth, (!PvNode && ttPv)); + quietsSearched, quietCount, capturesSearched, captureCount, depth); // Bonus for prior countermove that caused the fail low else if ( (depth >= 3 || PvNode) @@ -1602,7 +1598,7 @@ namespace { // update_all_stats() updates stats at the end of search() when a bestMove is found void update_all_stats(const Position& pos, Stack* ss, Move bestMove, Value bestValue, Value beta, Square prevSq, - Move* quietsSearched, int quietCount, Move* capturesSearched, int captureCount, Depth depth, bool pastPV) { + Move* quietsSearched, int quietCount, Move* capturesSearched, int captureCount, Depth depth) { int bonus1, bonus2; Color us = pos.side_to_move(); @@ -1612,8 +1608,8 @@ namespace { PieceType captured = type_of(pos.piece_on(to_sq(bestMove))); bonus1 = stat_bonus(depth + 1); - bonus2 = pastPV || bestValue > beta + PawnValueMg ? bonus1 // larger bonus - : stat_bonus(depth); // smaller bonus + bonus2 = bestValue > beta + PawnValueMg ? bonus1 // larger bonus + : stat_bonus(depth); // smaller bonus if (!pos.capture_or_promotion(bestMove)) { From c390b734c451b3d976b331244e281d261b26ac3a Mon Sep 17 00:00:00 2001 From: xoto10 Date: Thu, 30 Jan 2020 20:42:58 +0000 Subject: [PATCH 0142/1766] Simplify Tweak late move reductions at root. Revert change from Jan 15. STC 10+0.1 : LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 65135 W: 12543 L: 12436 D: 40156 Ptnml(0-2): 1090, 7618, 14947, 7623, 1136 https://tests.stockfishchess.org/tests/view/5e334016708b13464ceea32e LTC 60+0.6 : LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 17768 W: 2286 L: 2191 D: 13291 Ptnml(0-2): 128, 1602, 5273, 1679, 140 https://tests.stockfishchess.org/tests/view/5e34011e57e1ecae66ec2aab closes https://github.com/official-stockfish/Stockfish/pull/2537 Bench: 4914050 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 985af7bc2dc..9562bf8640d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1115,7 +1115,7 @@ namespace { // Step 16. Reduced depth search (LMR, ~200 Elo). If the move fails high it will be // re-searched at full depth. if ( depth >= 3 - && moveCount > 1 + rootNode + (rootNode && bestValue < alpha) + && moveCount > 1 + 2 * rootNode && (!rootNode || thisThread->best_move_count(move) == 0) && ( !captureOrPromotion || moveCountPruning From 0f37da0e34e02efaaca907878d1a3e0d916f447c Mon Sep 17 00:00:00 2001 From: xoto10 Date: Fri, 31 Jan 2020 15:55:29 +0000 Subject: [PATCH 0143/1766] Simplify away king infiltration. STC : LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 91438 W: 17496 L: 17438 D: 56504 Ptnml(0-2): 1573, 10711, 21067, 10790, 1563 https://tests.stockfishchess.org/tests/view/5e34812630ae32da08941d65 LTC : LLR: 2.96 (-2.94,2.94) {-1.50,0.50} Total: 40485 W: 5246 L: 5177 D: 30062 Ptnml(0-2): 289, 3818, 11976, 3812, 327 https://tests.stockfishchess.org/tests/view/5e354daee70d848499f6380c closes https://github.com/official-stockfish/Stockfish/pull/2542 Bench: 5047825 --- src/evaluate.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index bf1eee9b9c6..17597648bf3 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -701,9 +701,6 @@ namespace { int outflanking = distance(pos.square(WHITE), pos.square(BLACK)) - distance(pos.square(WHITE), pos.square(BLACK)); - bool infiltration = rank_of(pos.square(WHITE)) > RANK_4 - || rank_of(pos.square(BLACK)) < RANK_5; - bool pawnsOnBothFlanks = (pos.pieces(PAWN) & QueenSide) && (pos.pieces(PAWN) & KingSide); @@ -715,11 +712,10 @@ namespace { int complexity = 9 * pe->passed_count() + 11 * pos.count() + 9 * outflanking - + 12 * infiltration + 21 * pawnsOnBothFlanks + 51 * !pos.non_pawn_material() - 43 * almostUnwinnable - - 100 ; + - 95 ; // Give more importance to non-material score Value mg = mg_value(score); From ddd4224640e04463c62b5896660f6f6abe4cc1a5 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Tue, 4 Feb 2020 20:41:58 +0300 Subject: [PATCH 0144/1766] Reintroduce king infiltration This patch reintroduces the recently simplified king infiltration bonus in initiative calculation, doubling its effect, and compensating more. passed STC http://tests.stockfishchess.org/tests/view/5e3476f630ae32da08941d5c LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 75323 W: 14434 L: 14140 D: 46749 Ptnml(0-2): 1231, 8729, 17528, 8826, 1331 passed LTC http://tests.stockfishchess.org/tests/view/5e377353e70d848499f638c1 LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 171466 W: 22223 L: 21561 D: 127682 Ptnml(0-2): 1204, 15951, 50831, 16397, 1312 closes https://github.com/official-stockfish/Stockfish/pull/2545 Brench: 4869669 --- src/evaluate.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 17597648bf3..df7ff5ea337 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -708,14 +708,18 @@ namespace { && outflanking < 0 && !pawnsOnBothFlanks; + bool infiltration = rank_of(pos.square(WHITE)) > RANK_4 + || rank_of(pos.square(BLACK)) < RANK_5; + // Compute the initiative bonus for the attacking side int complexity = 9 * pe->passed_count() + 11 * pos.count() + 9 * outflanking + 21 * pawnsOnBothFlanks + + 24 * infiltration + 51 * !pos.non_pawn_material() - 43 * almostUnwinnable - - 95 ; + -110 ; // Give more importance to non-material score Value mg = mg_value(score); From 0c878adb36c1013944231083d6a7b3b53aa1ad7e Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 5 Feb 2020 15:18:24 +0100 Subject: [PATCH 0145/1766] Small cleanups. closes https://github.com/official-stockfish/Stockfish/pull/2532 Bench: 4869669 --- src/bitbase.cpp | 1 - src/endgame.h | 2 +- src/evaluate.cpp | 2 -- src/main.cpp | 2 +- src/misc.cpp | 14 +++++++------- src/misc.h | 2 +- src/movegen.cpp | 1 - src/position.cpp | 4 ++-- src/thread.cpp | 17 ++++++++--------- src/thread.h | 2 +- src/tt.cpp | 2 +- 11 files changed, 22 insertions(+), 27 deletions(-) diff --git a/src/bitbase.cpp b/src/bitbase.cpp index ed6ed2088e1..bef2dc49e2f 100644 --- a/src/bitbase.cpp +++ b/src/bitbase.cpp @@ -19,7 +19,6 @@ */ #include -#include #include #include diff --git a/src/endgame.h b/src/endgame.h index 4642e44857b..f61353542aa 100644 --- a/src/endgame.h +++ b/src/endgame.h @@ -21,10 +21,10 @@ #ifndef ENDGAME_H_INCLUDED #define ENDGAME_H_INCLUDED -#include #include #include #include +#include #include #include "position.h" diff --git a/src/evaluate.cpp b/src/evaluate.cpp index df7ff5ea337..ceba2588105 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -520,7 +520,6 @@ namespace { } // Bonus for restricting their piece moves - // Greater bonus when landing square is occupied b = attackedBy[Them][ALL_PIECES] & ~stronglyProtected & attackedBy[Us][ALL_PIECES]; @@ -721,7 +720,6 @@ namespace { - 43 * almostUnwinnable -110 ; - // Give more importance to non-material score Value mg = mg_value(score); Value eg = eg_value(score); diff --git a/src/main.cpp b/src/main.cpp index 148bf248f53..182cf105edc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,12 +21,12 @@ #include #include "bitboard.h" +#include "endgame.h" #include "position.h" #include "search.h" #include "thread.h" #include "tt.h" #include "uci.h" -#include "endgame.h" #include "syzygy/tbprobe.h" namespace PSQT { diff --git a/src/misc.cpp b/src/misc.cpp index cf18d2e2fc5..4d6483e73d2 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -299,23 +299,23 @@ void prefetch(void* addr) { /// With c++17 some of this functionality can be simplified. #if defined(__linux__) && !defined(__ANDROID__) -void* aligned_ttmem_alloc(size_t allocSize, void** mem) { +void* aligned_ttmem_alloc(size_t allocSize, void*& mem) { constexpr size_t alignment = 2 * 1024 * 1024; // assumed 2MB page sizes size_t size = ((allocSize + alignment - 1) / alignment) * alignment; // multiple of alignment - *mem = aligned_alloc(alignment, size); - madvise(*mem, allocSize, MADV_HUGEPAGE); - return *mem; + mem = aligned_alloc(alignment, size); + madvise(mem, allocSize, MADV_HUGEPAGE); + return mem; } #else -void* aligned_ttmem_alloc(size_t allocSize, void** mem) { +void* aligned_ttmem_alloc(size_t allocSize, void*& mem) { constexpr size_t alignment = 64; // assumed cache line size size_t size = allocSize + alignment - 1; // allocate some extra space - *mem = malloc(size); - void* ret = reinterpret_cast((uintptr_t(*mem) + alignment - 1) & ~uintptr_t(alignment - 1)); + mem = malloc(size); + void* ret = reinterpret_cast((uintptr_t(mem) + alignment - 1) & ~uintptr_t(alignment - 1)); return ret; } diff --git a/src/misc.h b/src/misc.h index 45d9951a72a..a3780ba599d 100644 --- a/src/misc.h +++ b/src/misc.h @@ -33,7 +33,7 @@ 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); -void* aligned_ttmem_alloc(size_t size, void** mem); +void* aligned_ttmem_alloc(size_t size, void*& mem); void dbg_hit_on(bool b); void dbg_hit_on(bool c, bool b); diff --git a/src/movegen.cpp b/src/movegen.cpp index 8f6edffbfc3..7e8961ae3b6 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -52,7 +52,6 @@ namespace { template ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) { - // Compute some compile time parameters relative to the white side constexpr Color Them = (Us == WHITE ? BLACK : WHITE); constexpr Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); diff --git a/src/position.cpp b/src/position.cpp index de9722fff2b..5efd9c42a5e 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -633,11 +633,11 @@ bool Position::gives_check(Move m) const { Square to = to_sq(m); // Is there a direct check? - if (st->checkSquares[type_of(piece_on(from))] & to) + if (check_squares(type_of(piece_on(from))) & to) return true; // Is there a discovered check? - if ( (st->blockersForKing[~sideToMove] & from) + if ( (blockers_for_king(~sideToMove) & from) && !aligned(from, to, square(~sideToMove))) return true; diff --git a/src/thread.cpp b/src/thread.cpp index 615d482cafa..10ec96dddba 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -54,7 +54,7 @@ Thread::~Thread() { /// Thread::bestMoveCount(Move move) return best move counter for the given root move -int Thread::best_move_count(Move move) { +int Thread::best_move_count(Move move) const { auto rm = std::find(rootMoves.begin() + pvIdx, rootMoves.begin() + pvLast, move); @@ -71,14 +71,13 @@ void Thread::clear() { captureHistory.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 (bool inCheck : { false, true }) - for (StatsType c : { NoCaptures, Captures }) - continuationHistory[inCheck][c][NO_PIECE][0]->fill(Search::CounterMovePruneThreshold - 1); + for (StatsType c : { NoCaptures, Captures }) + { + for (auto& to : continuationHistory[inCheck][c]) + for (auto& h : to) + h->fill(0); + 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 aea86fd5c69..63629e33812 100644 --- a/src/thread.h +++ b/src/thread.h @@ -56,7 +56,7 @@ class Thread { void idle_loop(); void start_searching(); void wait_for_search_finished(); - int best_move_count(Move move); + int best_move_count(Move move) const; Pawns::Table pawnsTable; Material::Table materialTable; diff --git a/src/tt.cpp b/src/tt.cpp index 46860fe900d..7e95a2a4e6d 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -66,7 +66,7 @@ void TranspositionTable::resize(size_t mbSize) { free(mem); clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster); - table = static_cast(aligned_ttmem_alloc(clusterCount * sizeof(Cluster), &mem)); + table = static_cast(aligned_ttmem_alloc(clusterCount * sizeof(Cluster), mem)); if (!mem) { std::cerr << "Failed to allocate " << mbSize From 0aee1ec4ab3f7891c6a59d9d9e0113655300ce64 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 7 Feb 2020 10:42:10 +0100 Subject: [PATCH 0146/1766] Fix wrong assert. can trigger an abort when compiling with debug=yes, and using 7men TB. The assert should check that less than 8 pieces are in the key for each side, matching the assumption that underlies the FEN string construction. Also take explicitly care of a 'v' character in material strings. Fixes an issue reported in the forum: https://groups.google.com/d/msg/fishcooking/yoVC7etIpz0/7mS7ntZMBAAJ closes https://github.com/official-stockfish/Stockfish/pull/2547 No functional change. --- src/position.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index 5efd9c42a5e..0ac450570b9 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -375,11 +375,13 @@ void Position::set_state(StateInfo* si) const { Position& Position::set(const string& code, Color c, StateInfo* si) { - assert(code.length() > 0 && code.length() < 8); assert(code[0] == 'K'); string sides[] = { code.substr(code.find('K', 1)), // Weak - code.substr(0, code.find('K', 1)) }; // Strong + code.substr(0, std::min(code.find('v'), code.find('K', 1))) }; // Strong + + assert(sides[0].length() > 0 && sides[0].length() < 8); + assert(sides[1].length() > 0 && sides[1].length() < 8); std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower); From 4e8986483a83296e37433da7b07b64de53613f6f Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Fri, 7 Feb 2020 20:04:43 +0300 Subject: [PATCH 0147/1766] Modify singular beta for ttPv positions. This patch lowers singular beta for positions that have been in pv and are not pv nodes. The idea of using ttpv && !PvNode improved scaling with TC and could be useful for other search heuristics. passed STC http://tests.stockfishchess.org/tests/view/5e3f6d7ce70d848499f63bbc LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 154953 W: 29688 L: 29272 D: 95993 Ptnml(0-2): 2616, 17912, 36037, 18210, 2673 passed LTC http://tests.stockfishchess.org/tests/view/5e405561e70d848499f63bfa LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 70974 W: 9305 L: 8932 D: 52737 Ptnml(0-2): 466, 6658, 20920, 6826, 569 closes https://github.com/official-stockfish/Stockfish/pull/2550 Bench: 4932981 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 9562bf8640d..c8a35a36d2f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1048,7 +1048,7 @@ namespace { && tte->depth() >= depth - 3 && pos.legal(move)) { - Value singularBeta = ttValue - 2 * depth; + Value singularBeta = ttValue - (((ttPv && !PvNode) + 4) * depth) / 2; Depth halfDepth = depth / 2; ss->excludedMove = move; value = search(pos, ss, singularBeta - 1, singularBeta, halfDepth, cutNode); From be5a2f015e45886e32867b4559ef51dd694a3cec Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Tue, 11 Feb 2020 20:42:32 +0100 Subject: [PATCH 0148/1766] Fix for incorrect VALUE_MATE_IN_MAX_PLY usage. Fixes #2533, fixes #2543, fixes #2423. the code that prevents false mate announcements depending on the TT state (GHI), incorrectly used VALUE_MATE_IN_MAX_PLY. The latter constant, however, also includes, counterintuitively, the TB win range. This patch fixes that, by restoring the behavior for TB win scores, while retaining the false mate correctness, and improving the mate finding ability. In particular no alse mates are announced with the poisened hash testcase ``` position fen 8/8/8/3k4/8/8/6K1/7R w - - 0 1 go depth 40 position fen 8/8/8/3k4/8/8/6K1/7R w - - 76 1 go depth 20 ucinewgame ``` mates are found with the testcases reported in #2543 ``` position fen 4k3/3pp3/8/8/8/8/2PPP3/4K3 w - - 0 1 setoption name Hash value 1024 go depth 55 ucinewgame ``` and ``` position fen 4k3/4p3/8/8/8/8/3PP3/4K3 w - - 0 1 setoption name Hash value 1024 go depth 45 ucinewgame ``` furthermore, on the mate finding benchmark (ChestUCI_23102018.epd), performance improves over master, roughly reaching performance with the false mate protection reverted ``` Analyzing 6566 mate positions for best and found mates: ----------------best ---------------found nodes master revert fixed master revert fixed 16000000 4233 4236 4235 5200 5201 5199 32000000 4583 4585 4585 5417 5424 5418 64000000 4852 4853 4855 5575 5584 5579 128000000 5071 5068 5066 5710 5720 5716 256000000 5280 5282 5279 5819 5827 5826 512000000 5471 5468 5468 5919 5935 5932 ``` On a testcase with TB enabled, progress is made consistently, contrary to master ``` setoption name SyzygyPath value ../../../syzygy/3-4-5/ setoption name Hash value 2048 position fen 1R6/3k4/8/K2p4/4n3/2P5/8/8 w - - 0 1 go depth 58 ucinewgame ``` The PR (prior to a rewrite for clarity) passed STC: LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 65405 W: 12454 L: 12384 D: 40567 Ptnml(0-2): 920, 7256, 16285, 7286, 944 http://tests.stockfishchess.org/tests/view/5e441a3be70d848499f63d15 passed LTC: LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 27096 W: 3477 L: 3413 D: 20206 Ptnml(0-2): 128, 2215, 8776, 2292, 122 http://tests.stockfishchess.org/tests/view/5e44e277e70d848499f63d63 The incorrectly named VALUE_MATE_IN_MAX_PLY and VALUE_MATED_IN_MAX_PLY were renamed into VALUE_TB_WIN_IN_MAX_PLY and VALUE_TB_LOSS_IN_MAX_PLY, and correclty defined VALUE_MATE_IN_MAX_PLY and VALUE_MATED_IN_MAX_PLY were introduced. One further (corner case) mistake using these constants was fixed (go mate X), which could lead to a premature return if X > MAX_PLY / 2, but TB were present. Thanks to @svivanov72 for one of the reports and help fixing the issue. closes https://github.com/official-stockfish/Stockfish/pull/2552 Bench: 4932981 --- src/endgame.cpp | 4 ++-- src/search.cpp | 58 +++++++++++++++++++++++++++++++++---------------- src/types.h | 6 +++-- 3 files changed, 45 insertions(+), 23 deletions(-) diff --git a/src/endgame.cpp b/src/endgame.cpp index 2ed6ebc27b7..6745ee2664c 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -137,7 +137,7 @@ Value Endgame::operator()(const Position& pos) const { ||(pos.count(strongSide) && pos.count(strongSide)) || ( (pos.pieces(strongSide, BISHOP) & ~DarkSquares) && (pos.pieces(strongSide, BISHOP) & DarkSquares))) - result = std::min(result + VALUE_KNOWN_WIN, VALUE_MATE_IN_MAX_PLY - 1); + result = std::min(result + VALUE_KNOWN_WIN, VALUE_TB_WIN_IN_MAX_PLY - 1); return strongSide == pos.side_to_move() ? result : -result; } @@ -162,7 +162,7 @@ Value Endgame::operator()(const Position& pos) const { + PushClose[distance(winnerKSq, loserKSq)] + PushToCorners[opposite_colors(bishopSq, SQ_A1) ? ~loserKSq : loserKSq]; - assert(abs(result) < VALUE_MATE_IN_MAX_PLY); + assert(abs(result) < VALUE_TB_WIN_IN_MAX_PLY); return strongSide == pos.side_to_move() ? result : -result; } diff --git a/src/search.cpp b/src/search.cpp index c8a35a36d2f..f1416a74091 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -290,13 +290,13 @@ void MainThread::search() { votes[th->rootMoves[0].pv[0]] += (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth); - if (bestThread->rootMoves[0].score >= VALUE_MATE_IN_MAX_PLY) + if (bestThread->rootMoves[0].score >= VALUE_TB_WIN_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 + else if ( th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY || votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]]) bestThread = th; } @@ -755,9 +755,10 @@ namespace { int drawScore = TB::UseRule50 ? 1 : 0; - value = wdl < -drawScore ? -VALUE_MATE + MAX_PLY + ss->ply + 1 - : wdl > drawScore ? VALUE_MATE - MAX_PLY - ss->ply - 1 - : VALUE_DRAW + 2 * wdl * drawScore; + // use the range VALUE_MATE_IN_MAX_PLY to VALUE_TB_WIN_IN_MAX_PLY to score + value = wdl < -drawScore ? VALUE_MATED_IN_MAX_PLY + ss->ply + 1 + : wdl > drawScore ? VALUE_MATE_IN_MAX_PLY - ss->ply - 1 + : VALUE_DRAW + 2 * wdl * drawScore; Bound b = wdl < -drawScore ? BOUND_UPPER : wdl > drawScore ? BOUND_LOWER : BOUND_EXACT; @@ -863,7 +864,7 @@ namespace { if (nullValue >= beta) { // Do not return unproven mate scores - if (nullValue >= VALUE_MATE_IN_MAX_PLY) + if (nullValue >= VALUE_TB_WIN_IN_MAX_PLY) nullValue = beta; if (thisThread->nmpMinPly || (abs(beta) < VALUE_KNOWN_WIN && depth < 13)) @@ -890,7 +891,7 @@ namespace { // much above beta, we can (almost) safely prune the previous move. if ( !PvNode && depth >= 5 - && abs(beta) < VALUE_MATE_IN_MAX_PLY) + && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) { Value raisedBeta = std::min(beta + 189 - 45 * improving, VALUE_INFINITE); MovePicker mp(pos, ttMove, raisedBeta - ss->staticEval, &thisThread->captureHistory); @@ -996,7 +997,7 @@ namespace { // Step 13. Pruning at shallow depth (~200 Elo) if ( !rootNode && pos.non_pawn_material(us) - && bestValue > VALUE_MATED_IN_MAX_PLY) + && bestValue > VALUE_TB_LOSS_IN_MAX_PLY) { // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold moveCountPruning = moveCount >= futility_move_count(improving, depth); @@ -1494,7 +1495,7 @@ namespace { // Detect non-capture evasions that are candidates to be pruned evasionPrunable = inCheck && (depth != 0 || moveCount > 2) - && bestValue > VALUE_MATED_IN_MAX_PLY + && bestValue > VALUE_TB_LOSS_IN_MAX_PLY && !pos.capture(move); // Don't search moves with negative SEE values @@ -1560,28 +1561,47 @@ namespace { } - // value_to_tt() adjusts a mate score from "plies to mate from the root" to - // "plies to mate from the current position". Non-mate scores are unchanged. + // value_to_tt() adjusts a mate or TB score from "plies to mate from the root" to + // "plies to mate from the current position". standard scores are unchanged. // The function is called before storing a value in the transposition table. Value value_to_tt(Value v, int ply) { assert(v != VALUE_NONE); - return v >= VALUE_MATE_IN_MAX_PLY ? v + ply - : v <= VALUE_MATED_IN_MAX_PLY ? v - ply : v; + return v >= VALUE_TB_WIN_IN_MAX_PLY ? v + ply + : v <= VALUE_TB_LOSS_IN_MAX_PLY ? v - ply : v; } - // value_from_tt() is the inverse of value_to_tt(): It adjusts a mate score + // value_from_tt() is the inverse of value_to_tt(): It adjusts a mate or TB score // from the transposition table (which refers to the plies to mate/be mated - // from current position) to "plies to mate/be mated from the root". + // from current position) to "plies to mate/be mated (TB win/loss) from the root". + // However, for mate scores, to avoid potentially false mate scores related to the 50 moves rule, + // and the graph history interaction, return an optimal TB score instead. Value value_from_tt(Value v, int ply, int r50c) { - return v == VALUE_NONE ? VALUE_NONE - : v >= VALUE_MATE_IN_MAX_PLY ? VALUE_MATE - v > 99 - r50c ? VALUE_MATE_IN_MAX_PLY : v - ply - : v <= VALUE_MATED_IN_MAX_PLY ? VALUE_MATE + v > 99 - r50c ? VALUE_MATED_IN_MAX_PLY : v + ply : v; + if (v == VALUE_NONE) + return VALUE_NONE; + + if (v >= VALUE_TB_WIN_IN_MAX_PLY) // TB win or better + { + if (v >= VALUE_MATE_IN_MAX_PLY && VALUE_MATE - v > 99 - r50c) + return VALUE_MATE_IN_MAX_PLY - 1; // do not return a potentially false mate score + + return v - ply; + } + + if (v <= VALUE_TB_LOSS_IN_MAX_PLY) // TB loss or worse + { + if (v <= VALUE_MATED_IN_MAX_PLY && VALUE_MATE + v > 99 - r50c) + return VALUE_MATED_IN_MAX_PLY + 1; // do not return a potentially false mate score + + return v + ply; + } + + return v; } @@ -1767,7 +1787,7 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) { 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; + bool tb = TB::RootInTB && abs(v) < VALUE_MATE_IN_MAX_PLY; v = tb ? rootMoves[i].tbScore : v; if (ss.rdbuf()->in_avail()) // Not at first line diff --git a/src/types.h b/src/types.h index 902c2cfce87..7ab7560a7ed 100644 --- a/src/types.h +++ b/src/types.h @@ -176,8 +176,10 @@ enum Value : int { VALUE_INFINITE = 32001, VALUE_NONE = 32002, - VALUE_MATE_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY, - VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY, + VALUE_TB_WIN_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY, + VALUE_TB_LOSS_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY, + VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY, + VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + MAX_PLY, PawnValueMg = 128, PawnValueEg = 213, KnightValueMg = 781, KnightValueEg = 854, From 10ead8a724671132df60ca2e23ddcad7eff7b3e5 Mon Sep 17 00:00:00 2001 From: protonspring Date: Mon, 10 Feb 2020 15:18:16 -0700 Subject: [PATCH 0149/1766] Simplify Futility Move Count remove two constants STC LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 62050 W: 11903 L: 11802 D: 38345 Ptnml(0-2): 1002, 7346, 14283, 7320, 1065 http://tests.stockfishchess.org/tests/view/5e41d73be70d848499f63c6d LTC LLR: 2.93 (-2.94,2.94) {-1.50,0.50} Total: 12850 W: 1679 L: 1572 D: 9599 Ptnml(0-2): 82, 1171, 3818, 1249, 96 http://tests.stockfishchess.org/tests/view/5e42bf07e70d848499f63cc0 Bench: 4762351 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index f1416a74091..c3ebf9abc30 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -79,7 +79,7 @@ namespace { } constexpr int futility_move_count(bool improving, Depth depth) { - return (5 + depth * depth) * (1 + improving) / 2 - 1; + return (4 + depth * depth) / (2 - improving); } // History and stats update bonus, based on depth From ab930f8d3f4a657f493305559756e85534c88911 Mon Sep 17 00:00:00 2001 From: protonspring Date: Tue, 18 Feb 2020 09:10:58 -0700 Subject: [PATCH 0150/1766] Updated KNNKP endgame. This is a patch that significantly improves playing KNNKP endgames: ``` Score of 2553 vs master: 132 - 38 - 830 [0.547] 1000 Elo difference: 32.8 +/- 8.7, LOS: 100.0 %, DrawRatio: 83.0 % ``` At the same time it reduces the evaluation of this mostly draw engame from ~7.5 to ~1.5 This patch does not regress against master in normal games: STC LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 96616 W: 18459 L: 18424 D: 59733 Ptnml(0-2): 1409, 10812, 23802, 10905, 1380 http://tests.stockfishchess.org/tests/view/5e49dfe6f8d1d52b40cd31bc LTC LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 49726 W: 6340 L: 6304 D: 37082 Ptnml(0-2): 239, 4227, 15906, 4241, 250 http://tests.stockfishchess.org/tests/view/5e4ab9ee16fb3df8c4cc01d0 Theory: KNNK is a dead draw, however the presence of the additional weakSide pawn opens up some mate opportunities. The idea is to block the pawn (preferably behind the Troitsky line) with one of the knights and press the weakSide king into a corner. If we can stalemate the king, we release the pawn with the knight (to avoid actual stalemate), and use the knight to complete the mate before the pawn promotes. This is also why there is an additional penalty for advancement of the pawn. closes https://github.com/official-stockfish/Stockfish/pull/2553 Bench: 4981770 --- src/endgame.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/endgame.cpp b/src/endgame.cpp index 6745ee2664c..74e16fa6941 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -310,16 +310,17 @@ Value Endgame::operator()(const Position& pos) const { } -/// KNN vs KP. Simply push the opposing king to the corner +/// KNN vs KP. Very drawish, but there are some mate opportunities if we can +// press the weakSide King to a corner before the pawn advances too much. template<> Value Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, 2 * KnightValueMg, 0)); assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); - Value result = 2 * KnightValueEg - - PawnValueEg - + PushToEdges[pos.square(weakSide)]; + Value result = PawnValueEg + + 2 * PushToEdges[pos.square(weakSide)] + - 10 * relative_rank(weakSide, pos.square(weakSide)); return strongSide == pos.side_to_move() ? result : -result; } From b8c00efa2767ebf74545d2ba4bd344ef7c963319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Demetz?= Date: Fri, 21 Feb 2020 14:01:59 +0100 Subject: [PATCH 0151/1766] Improve move order near the root Current move histories are known to work well near the leaves, whilst at higher depths they aren't very helpful. To address this problem this patch introduces a table dedicated for what's happening at plies 0-3. It's structured like mainHistory with ply index instead of color. It get cleared with each new search and is filled during iterative deepening at higher depths when recording successful quiet moves near the root or traversing nodes which were in the principal variation (ttPv). Medium TC (20+0.2): https://tests.stockfishchess.org/tests/view/5e4d358790a0a02810d096dc LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 100910 W: 16682 L: 16376 D: 67852 Ptnml(0-2): 1177, 10983, 25883, 11181, 1231 LTC: https://tests.stockfishchess.org/tests/view/5e4e2cb790a0a02810d09714 LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 80444 W: 10495 L: 10095 D: 59854 Ptnml(0-2): 551, 7479, 23803, 7797, 592 closes https://github.com/official-stockfish/Stockfish/pull/2557 Bench: 4705960 --- src/movepick.cpp | 11 ++++++----- src/movepick.h | 12 +++++++++++- src/search.cpp | 19 ++++++++++++++----- src/thread.cpp | 2 ++ src/thread.h | 1 + 5 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 025f5b82cde..575c902228b 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -56,10 +56,10 @@ namespace { /// ordering is at the current node. /// MovePicker constructor for the main search -MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, - const CapturePieceToHistory* cph, const PieceToHistory** ch, Move cm, Move* killers) - : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), - refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d) { +MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const LowPlyHistory* lp, + const CapturePieceToHistory* cph, const PieceToHistory** ch, Move cm, Move* killers, int pl) + : pos(p), mainHistory(mh), lowPlyHistory(lp), captureHistory(cph), continuationHistory(ch), + refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d) , ply(pl) { assert(d > 0); @@ -115,7 +115,8 @@ void MovePicker::score() { + 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)]; + + (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)] + + (ply < MAX_LPH ? 4 * (*lowPlyHistory)[ply][from_to(m)] : 0); else // Type == EVASIONS { diff --git a/src/movepick.h b/src/movepick.h index cdedc9b616f..33c4b08602b 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -88,6 +88,12 @@ enum StatsType { NoCaptures, Captures }; /// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards typedef Stats ButterflyHistory; +/// LowPlyHistory at higher depths records successful quiet moves on plies 0 to 3 +/// and quiet moves which are/were in the PV (ttPv) +/// It get cleared with each new search and get filled during iterative deepening +constexpr int MAX_LPH = 4; +typedef Stats LowPlyHistory; + /// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous /// move, see www.chessprogramming.org/Countermove_Heuristic typedef Stats CounterMoveHistory; @@ -123,10 +129,12 @@ class MovePicker { const PieceToHistory**, Square); MovePicker(const Position&, Move, Depth, const ButterflyHistory*, + const LowPlyHistory*, const CapturePieceToHistory*, const PieceToHistory**, Move, - Move*); + Move*, + int); Move next_move(bool skipQuiets = false); private: @@ -137,6 +145,7 @@ class MovePicker { const Position& pos; const ButterflyHistory* mainHistory; + const LowPlyHistory* lowPlyHistory; const CapturePieceToHistory* captureHistory; const PieceToHistory** continuationHistory; Move ttMove; @@ -145,6 +154,7 @@ class MovePicker { Square recaptureSquare; Value threshold; Depth depth; + int ply; ExtMove moves[MAX_MOVES]; }; diff --git a/src/search.cpp b/src/search.cpp index c3ebf9abc30..3f860ac5e5b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -156,7 +156,7 @@ namespace { Value value_from_tt(Value v, int ply, int r50c); void update_pv(Move* pv, Move move, Move* childPv); void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus); - void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus); + void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus, int depth); void update_all_stats(const Position& pos, Stack* ss, Move bestMove, Value bestValue, Value beta, Square prevSq, Move* quietsSearched, int quietCount, Move* capturesSearched, int captureCount, Depth depth); @@ -695,6 +695,10 @@ namespace { ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] : ttHit ? tte->move() : MOVE_NONE; ttPv = PvNode || (ttHit && tte->is_pv()); + + if (ttPv && depth > 12 && ss->ply - 1 < MAX_LPH && !pos.captured_piece() && is_ok((ss-1)->currentMove)) + thisThread->lowPlyHistory[ss->ply - 1][from_to((ss-1)->currentMove)] << stat_bonus(depth - 5); + // thisThread->ttHitAverage can be used to approximate the running average of ttHit thisThread->ttHitAverage = (ttHitAverageWindow - 1) * thisThread->ttHitAverage / ttHitAverageWindow + ttHitAverageResolution * ttHit; @@ -713,7 +717,7 @@ namespace { if (ttValue >= beta) { if (!pos.capture_or_promotion(ttMove)) - update_quiet_stats(pos, ss, ttMove, stat_bonus(depth)); + update_quiet_stats(pos, ss, ttMove, stat_bonus(depth), depth); // Extra penalty for early quiet moves of the previous ply if ((ss-1)->moveCount <= 2 && !priorCapture) @@ -948,10 +952,12 @@ namespace { Move countermove = thisThread->counterMoves[pos.piece_on(prevSq)][prevSq]; MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, + &thisThread->lowPlyHistory, &thisThread->captureHistory, contHist, countermove, - ss->killers); + ss->killers, + depth > 12 && ttPv ? ss->ply : MAX_PLY); value = bestValue; singularLMR = moveCountPruning = false; @@ -1633,7 +1639,7 @@ namespace { if (!pos.capture_or_promotion(bestMove)) { - update_quiet_stats(pos, ss, bestMove, bonus2); + update_quiet_stats(pos, ss, bestMove, bonus2, depth); // Decrease all the non-best quiet moves for (int i = 0; i < quietCount; ++i) @@ -1673,7 +1679,7 @@ namespace { // update_quiet_stats() updates move sorting heuristics - void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus) { + void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus, int depth) { if (ss->killers[0] != move) { @@ -1694,6 +1700,9 @@ namespace { Square prevSq = to_sq((ss-1)->currentMove); thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] = move; } + + if (depth > 12 && ss->ply < MAX_LPH) + thisThread->lowPlyHistory[ss->ply][from_to(move)] << stat_bonus(depth - 7); } // When playing with strength handicap, choose best move among a set of RootMoves diff --git a/src/thread.cpp b/src/thread.cpp index 10ec96dddba..b5cb87d9356 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -68,6 +68,7 @@ void Thread::clear() { counterMoves.fill(MOVE_NONE); mainHistory.fill(0); + lowPlyHistory.fill(0); captureHistory.fill(0); for (bool inCheck : { false, true }) @@ -211,6 +212,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states, th->rootDepth = th->completedDepth = 0; th->rootMoves = rootMoves; th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th); + th->lowPlyHistory.fill(0); } setupStates->back() = tmp; diff --git a/src/thread.h b/src/thread.h index 63629e33812..41d2b8f6d27 100644 --- a/src/thread.h +++ b/src/thread.h @@ -71,6 +71,7 @@ class Thread { Depth rootDepth, completedDepth; CounterMoveHistory counterMoves; ButterflyHistory mainHistory; + LowPlyHistory lowPlyHistory; CapturePieceToHistory captureHistory; ContinuationHistory continuationHistory[2][2]; Score contempt; From 8352977b91d9246618c7273d59400a4a05a32e2a Mon Sep 17 00:00:00 2001 From: 31m059 <37052095+31m059@users.noreply.github.com> Date: Sat, 22 Feb 2020 21:27:32 -0500 Subject: [PATCH 0152/1766] Use single param for Outpost and ReachableOutpost. In November 2019, as a result of the simplification of rank-based outposts by 37698b0, separate bonuses were introduced for outposts that are currently occupied and outposts that are reachable on the next move. However, the values of these two bonuses are quite similar, and they have remained that way for three months of development. It appears that we can safely retire the separate ReachableOutpost parameter and use the same Outpost bonus in both cases, restoring the basic principles of Stockfish outpost evaluation to their pre-November state, while also reducing the size of the parameter space. STC: LLR: 2.96 (-2.94,2.94) {-1.50,0.50} Total: 47680 W: 9213 L: 9092 D: 29375 Ptnml(0-2): 776, 5573, 11071, 5594, 826 https://tests.stockfishchess.org/tests/view/5e51e33190a0a02810d09802 LTC: LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 14690 W: 1960 L: 1854 D: 10876 Ptnml(0-2): 93, 1381, 4317, 1435, 119 https://tests.stockfishchess.org/tests/view/5e52197990a0a02810d0980f closes https://github.com/official-stockfish/Stockfish/pull/2559 Bench: 4697493 --- src/evaluate.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index ceba2588105..25aba6448aa 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -139,7 +139,6 @@ namespace { constexpr Score PassedFile = S( 11, 8); constexpr Score PawnlessFlank = S( 17, 95); constexpr Score RestrictedPiece = S( 7, 7); - constexpr Score ReachableOutpost = S( 32, 10); constexpr Score RookOnQueenFile = S( 7, 6); constexpr Score SliderOnQueen = S( 59, 18); constexpr Score ThreatByKing = S( 24, 89); @@ -296,7 +295,7 @@ namespace { score += Outpost * (Pt == KNIGHT ? 2 : 1); else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us)) - score += ReachableOutpost; + score += Outpost; // Knight and Bishop bonus for being right behind a pawn if (shift(pos.pieces(PAWN)) & s) From 2e1369d0302a87d570e509af7041a1be22b836a0 Mon Sep 17 00:00:00 2001 From: AndyGrant Date: Mon, 24 Feb 2020 23:32:17 +0100 Subject: [PATCH 0153/1766] Fix TT write in MultiPV case. fixes an error reported earlier as https://github.com/official-stockfish/Stockfish/issues/2404 by @AndyGrant. MultiPV at root shouldn't write to the TT for later lines, as that is neither the eval nor the bestmove for that position. Fixing this error doesn't matter for playing games (http://tests.stockfishchess.org/tests/view/5dcdbd810ebc590256324a11). However, it can lead to wrong mate announcements as reported by @uriblass. In particular the following testcase gives wrong results for the second search, prior to this patch: ``` setoption name MultiPV value 2 position fen 5R2/2kB2p1/p2bR3/8/3p1B2/8/PPP5/2K5 b - - 0 49 go depth 40 position fen 2B2R2/3r2p1/p1kbR3/8/3p1B2/8/PPP5/2K5 b - - 8 48 go depth 40 ``` fixes https://github.com/official-stockfish/Stockfish/issues/2561 closes https://github.com/official-stockfish/Stockfish/pull/2562 Only affects MultiPV search. Bench: 4697493 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 3f860ac5e5b..a32ff4b6b2b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1339,7 +1339,7 @@ namespace { if (PvNode) bestValue = std::min(bestValue, maxValue); - if (!excludedMove) + if (!excludedMove && !(rootNode && thisThread->pvIdx)) tte->save(posKey, value_to_tt(bestValue, ss->ply), ttPv, bestValue >= beta ? BOUND_LOWER : PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER, From 09f53dbfa5b55e761ca8070960345ab140baad04 Mon Sep 17 00:00:00 2001 From: Moez Jellouli <37274752+MJZ1977@users.noreply.github.com> Date: Sat, 22 Feb 2020 14:57:01 +0100 Subject: [PATCH 0154/1766] Weak queen protection Extra penalty if weak piece is only protected by a queen. STC: http://tests.stockfishchess.org/tests/view/5e53c6ab84a82b4acd4148fa LLR: 2.96 (-2.94,2.94) {-0.50,1.50} Total: 44630 W: 8615 L: 8359 D: 27656 Ptnml(0-2): 746, 5156, 10323, 5276, 814 LTC: http://tests.stockfishchess.org/tests/view/5e54e05d84a82b4acd414947 LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 175480 W: 23085 L: 22409 D: 129986 Ptnml(0-2): 1264, 16494, 51678, 16910, 1394 closes https://github.com/official-stockfish/Stockfish/pull/2564 Bench: 4923286 --- src/evaluate.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 25aba6448aa..06366e09501 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -114,11 +114,11 @@ 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, 32), S(59, 41), S(79, 56), S(90, 119), S(79, 161) + S(0, 0), S(5, 32), S(57, 41), S(77, 56), S(88, 119), S(79, 161) }; constexpr Score ThreatByRook[PIECE_TYPE_NB] = { - S(0, 0), S(3, 44), S(38, 71), S(38, 61), S(0, 38), S(51, 38) + S(0, 0), S(2, 44), S(36, 71), S(36, 61), S(0, 38), S(51, 38) }; // PassedRank[Rank] contains a bonus according to the rank of a passed pawn @@ -516,6 +516,9 @@ namespace { b = ~attackedBy[Them][ALL_PIECES] | (nonPawnEnemies & attackedBy2[Us]); score += Hanging * popcount(weak & b); + + // Additional bonus if weak piece is only protected by a queen + score += make_score(14, 0) * popcount(weak & attackedBy[Them][QUEEN]); } // Bonus for restricting their piece moves From f27339d35b6c8ccd1f83914b334c89111e62f320 Mon Sep 17 00:00:00 2001 From: Guenther Demetz Date: Thu, 27 Feb 2020 15:58:22 +0100 Subject: [PATCH 0155/1766] Simplify lowply-history logic Don't restrict usage to ttPv nodes exclusively STC: http://tests.stockfishchess.org/tests/view/5e5634f284a82b4acd41499a LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 152796 W: 29146 L: 29178 D: 94472 Ptnml(0-2): 2590, 17792, 35628, 17836, 2552 LTC: http://tests.stockfishchess.org/tests/view/5e575d4984a82b4acd4149e8 LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 20078 W: 2688 L: 2587 D: 14803 Ptnml(0-2): 139, 1914, 5853, 1973, 160 closes https://github.com/official-stockfish/Stockfish/pull/2565 bench: 4923286 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index a32ff4b6b2b..544c3ee51ec 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -957,7 +957,7 @@ namespace { contHist, countermove, ss->killers, - depth > 12 && ttPv ? ss->ply : MAX_PLY); + depth > 12 ? ss->ply : MAX_PLY); value = bestValue; singularLMR = moveCountPruning = false; From c6839a26155c18dbb7700175971fe01c5a67b01c Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 1 Mar 2020 09:31:17 +0100 Subject: [PATCH 0156/1766] Small cleanups closes https://github.com/official-stockfish/Stockfish/pull/2546 No functional change. --- src/Makefile | 2 +- src/bitboard.h | 2 +- src/endgame.cpp | 2 +- src/evaluate.cpp | 44 ++++++++++++++++++++++---------------------- src/evaluate.h | 2 -- src/psqt.cpp | 8 -------- src/search.cpp | 10 +++++----- src/types.h | 8 +++++++- 8 files changed, 37 insertions(+), 41 deletions(-) diff --git a/src/Makefile b/src/Makefile index 679eb8d90fc..15ad6353c80 100644 --- a/src/Makefile +++ b/src/Makefile @@ -409,7 +409,7 @@ help: @echo "" -.PHONY: help build profile-build strip install clean objclean profileclean help \ +.PHONY: help build profile-build strip install clean objclean profileclean \ config-sanity icc-profile-use icc-profile-make gcc-profile-use gcc-profile-make \ clang-profile-use clang-profile-make diff --git a/src/bitboard.h b/src/bitboard.h index d11b7e732b6..ca161481c45 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -154,7 +154,7 @@ inline Bitboard file_bb(Square s) { } -/// shift() moves a bitboard one step along direction D +/// shift() moves a bitboard one or two steps as specified by the direction D template constexpr Bitboard shift(Bitboard b) { diff --git a/src/endgame.cpp b/src/endgame.cpp index 74e16fa6941..5fdd307e442 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -281,7 +281,7 @@ Value Endgame::operator()(const Position& pos) const { if ( relative_rank(weakSide, pawnSq) != RANK_7 || distance(loserKSq, pawnSq) != 1 - || !((FileABB | FileCBB | FileFBB | FileHBB) & pawnSq)) + || ((FileBBB | FileDBB | FileEBB | FileGBB) & pawnSq)) result += QueenValueEg - PawnValueEg; return strongSide == pos.side_to_move() ? result : -result; diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 06366e09501..31272f2cffc 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -127,25 +127,26 @@ namespace { }; // Assorted bonuses and penalties - constexpr Score BishopPawns = S( 3, 7); - constexpr Score CorneredBishop = S( 50, 50); - constexpr Score FlankAttacks = S( 8, 0); - constexpr Score Hanging = S( 69, 36); - constexpr Score KingProtector = S( 7, 8); - constexpr Score KnightOnQueen = S( 16, 12); - constexpr Score LongDiagonalBishop = S( 45, 0); - constexpr Score MinorBehindPawn = S( 18, 3); - constexpr Score Outpost = S( 30, 21); - constexpr Score PassedFile = S( 11, 8); - constexpr Score PawnlessFlank = S( 17, 95); - constexpr Score RestrictedPiece = S( 7, 7); - constexpr Score RookOnQueenFile = S( 7, 6); - constexpr Score SliderOnQueen = S( 59, 18); - constexpr Score ThreatByKing = S( 24, 89); - constexpr Score ThreatByPawnPush = S( 48, 39); - constexpr Score ThreatBySafePawn = S(173, 94); - constexpr Score TrappedRook = S( 52, 10); - constexpr Score WeakQueen = S( 49, 15); + constexpr Score BishopPawns = S( 3, 7); + constexpr Score CorneredBishop = S( 50, 50); + constexpr Score FlankAttacks = S( 8, 0); + constexpr Score Hanging = S( 69, 36); + constexpr Score KingProtector = S( 7, 8); + constexpr Score KnightOnQueen = S( 16, 12); + constexpr Score LongDiagonalBishop = S( 45, 0); + constexpr Score MinorBehindPawn = S( 18, 3); + constexpr Score Outpost = S( 30, 21); + constexpr Score PassedFile = S( 11, 8); + constexpr Score PawnlessFlank = S( 17, 95); + constexpr Score RestrictedPiece = S( 7, 7); + constexpr Score RookOnQueenFile = S( 7, 6); + constexpr Score SliderOnQueen = S( 59, 18); + constexpr Score ThreatByKing = S( 24, 89); + constexpr Score ThreatByPawnPush = S( 48, 39); + constexpr Score ThreatBySafePawn = S(173, 94); + constexpr Score TrappedRook = S( 52, 10); + constexpr Score WeakQueen = S( 49, 15); + constexpr Score WeakQueenProtection = S( 14, 0); #undef S @@ -518,7 +519,7 @@ namespace { score += Hanging * popcount(weak & b); // Additional bonus if weak piece is only protected by a queen - score += make_score(14, 0) * popcount(weak & attackedBy[Them][QUEEN]); + score += WeakQueenProtection * popcount(weak & attackedBy[Them][QUEEN]); } // Bonus for restricting their piece moves @@ -830,8 +831,7 @@ namespace { Trace::add(TOTAL, score); } - return (pos.side_to_move() == WHITE ? v : -v) // Side to move point of view - + Eval::Tempo; + return (pos.side_to_move() == WHITE ? v : -v) + Tempo; // Side to move point of view } } // namespace diff --git a/src/evaluate.h b/src/evaluate.h index 077de70c974..7c8a2a6f77e 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -29,8 +29,6 @@ class Position; namespace Eval { -constexpr Value Tempo = Value(28); // Must be visible to search - std::string trace(const Position& pos); Value evaluate(const Position& pos); diff --git a/src/psqt.cpp b/src/psqt.cpp index 647bd8642df..8bad7ed427c 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -22,11 +22,6 @@ #include "types.h" -Value PieceValue[PHASE_NB][PIECE_NB] = { - { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg }, - { VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } -}; - namespace PSQT { #define S(mg, eg) make_score(mg, eg) @@ -112,9 +107,6 @@ void init() { for (Piece pc = W_PAWN; pc <= W_KING; ++pc) { - PieceValue[MG][~pc] = PieceValue[MG][pc]; - PieceValue[EG][~pc] = PieceValue[EG][pc]; - Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]); for (Square s = SQ_A1; s <= SQ_H8; ++s) diff --git a/src/search.cpp b/src/search.cpp index 544c3ee51ec..7f6abf15aef 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -819,14 +819,14 @@ namespace { ss->staticEval = eval = evaluate(pos) + bonus; } else - ss->staticEval = eval = -(ss-1)->staticEval + 2 * Eval::Tempo; + ss->staticEval = eval = -(ss-1)->staticEval + 2 * Tempo; tte->save(posKey, VALUE_NONE, ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); } // Step 7. Razoring (~1 Elo) if ( !rootNode // The required rootNode PV handling is not available in qsearch - && depth < 2 + && depth == 1 && eval <= alpha - RazorMargin) return qsearch(pos, ss, alpha, beta); @@ -1434,13 +1434,13 @@ namespace { else ss->staticEval = bestValue = (ss-1)->currentMove != MOVE_NULL ? evaluate(pos) - : -(ss-1)->staticEval + 2 * Eval::Tempo; + : -(ss-1)->staticEval + 2 * Tempo; // Stand pat. Return immediately if static value is at least beta if (bestValue >= beta) { if (!ttHit) - tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit, BOUND_LOWER, + tte->save(posKey, value_to_tt(bestValue, ss->ply), false, BOUND_LOWER, DEPTH_NONE, MOVE_NONE, ss->staticEval); return bestValue; @@ -1667,7 +1667,7 @@ namespace { // update_continuation_histories() updates histories of the move pairs formed - // by moves at ply -1, -2, and -4 with current move. + // by moves at ply -1, -2, -4, and -6 with current move. void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { diff --git a/src/types.h b/src/types.h index 7ab7560a7ed..58d05d2c6d8 100644 --- a/src/types.h +++ b/src/types.h @@ -186,6 +186,7 @@ enum Value : int { BishopValueMg = 825, BishopValueEg = 915, RookValueMg = 1276, RookValueEg = 1380, QueenValueMg = 2538, QueenValueEg = 2682, + Tempo = 28, MidgameLimit = 15258, EndgameLimit = 3915 }; @@ -203,7 +204,12 @@ enum Piece { PIECE_NB = 16 }; -extern Value PieceValue[PHASE_NB][PIECE_NB]; +constexpr Value PieceValue[PHASE_NB][PIECE_NB] = { + { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, VALUE_ZERO, VALUE_ZERO, + VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, VALUE_ZERO, VALUE_ZERO }, + { VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg, VALUE_ZERO, VALUE_ZERO, + VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg, VALUE_ZERO, VALUE_ZERO } +}; typedef int Depth; From 960d59d54143d84aab26deae65279a611fc989f4 Mon Sep 17 00:00:00 2001 From: protonspring Date: Sun, 1 Mar 2020 02:03:36 -0700 Subject: [PATCH 0157/1766] Consolidate Square Flipping Add a flip_rank() and flip_file() so that all of the square flipping can be consolidated. STC LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 57234 W: 11064 L: 10969 D: 35201 Ptnml(0-2): 822, 6562, 13801, 6563, 869 http://tests.stockfishchess.org/tests/view/5e5d2f2aafe6254521f2ffaa closes https://github.com/official-stockfish/Stockfish/pull/2568 No functional change. --- src/endgame.cpp | 4 ++-- src/psqt.cpp | 2 +- src/syzygy/tbprobe.cpp | 7 +++---- src/types.h | 8 ++++++-- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/endgame.cpp b/src/endgame.cpp index 5fdd307e442..16c072beb2c 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -74,9 +74,9 @@ namespace { assert(pos.count(strongSide) == 1); if (file_of(pos.square(strongSide)) >= FILE_E) - sq = Square(int(sq) ^ 7); // Mirror SQ_H1 -> SQ_A1 + sq = flip_file(sq); - return strongSide == WHITE ? sq : ~sq; + return strongSide == WHITE ? sq : flip_rank(sq); } } // namespace diff --git a/src/psqt.cpp b/src/psqt.cpp index 8bad7ed427c..f6f5933cb25 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -114,7 +114,7 @@ void init() { 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]; + psq[~pc][flip_rank(s)] = -psq[pc][s]; } } } diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 721a0ef5ba6..6f36945587f 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -66,7 +66,6 @@ enum TBType { KEY, WDL, DTZ }; // Used as template parameter enum TBFlag { STM = 1, Mapped = 2, WinPlies = 4, LossPlies = 8, Wide = 16, SingleValue = 128 }; inline WDLScore operator-(WDLScore d) { return WDLScore(-int(d)); } -inline Square operator^=(Square& s, int i) { return s = Square(int(s) ^ i); } inline Square operator^(Square s, int i) { return Square(int(s) ^ i); } const std::string PieceToChar = " PNBRQK pnbrqk"; @@ -743,7 +742,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu // the triangle A1-D1-D4. if (file_of(squares[0]) > FILE_D) for (int i = 0; i < size; ++i) - squares[i] ^= 7; // Horizontal flip: SQ_H1 -> SQ_A1 + squares[i] = flip_file(squares[i]); // Encode leading pawns starting with the one with minimum MapPawns[] and // proceeding in ascending order. @@ -762,7 +761,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu // piece is below RANK_5. if (rank_of(squares[0]) > RANK_4) for (int i = 0; i < size; ++i) - squares[i] ^= SQ_A8; // Vertical flip: SQ_A8 -> SQ_A1 + squares[i] = flip_rank(squares[i]); // Look for the first piece of the leading group not on the A1-D4 diagonal // and ensure it is mapped below the diagonal. @@ -1344,7 +1343,7 @@ void Tablebases::init(const std::string& paths) { if (leadPawnsCnt == 1) { MapPawns[sq] = availableSquares--; - MapPawns[sq ^ 7] = availableSquares--; // Horizontal flip + MapPawns[flip_file(sq)] = availableSquares--; } LeadPawnIdx[leadPawnsCnt][sq] = idx; idx += Binomial[leadPawnsCnt - 1][MapPawns[sq]]; diff --git a/src/types.h b/src/types.h index 58d05d2c6d8..d4937fd65cd 100644 --- a/src/types.h +++ b/src/types.h @@ -358,8 +358,12 @@ constexpr Color operator~(Color c) { return Color(c ^ BLACK); // Toggle color } -constexpr Square operator~(Square s) { - return Square(s ^ SQ_A8); // Vertical flip SQ_A1 -> SQ_A8 +constexpr Square flip_rank(Square s) { + return Square(s ^ SQ_A8); +} + +constexpr Square flip_file(Square s) { + return Square(s ^ SQ_H1); } constexpr Piece operator~(Piece pc) { From 5a7b45eac9dedbf7ebc61d9deb4dd934058d1ca1 Mon Sep 17 00:00:00 2001 From: protonspring Date: Mon, 2 Mar 2020 17:32:02 -0700 Subject: [PATCH 0158/1766] Use equations for PushAway and PushClose A functional simplification replacing the corresponding arrays. Tested in two variants, also the simpler one performs well, even though differences to master should be minimal. STC LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 57864 W: 11092 L: 11001 D: 35771 Ptnml(0-2): 826, 6458, 14320, 6455, 873 http://tests.stockfishchess.org/tests/view/5e5da5b6e42a5c3b3ca2e05c LTC LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 7198 W: 982 L: 883 D: 5333 Ptnml(0-2): 33, 575, 2296, 650, 45 http://tests.stockfishchess.org/tests/view/5e5df13ae42a5c3b3ca2e077 LTC (This exact version. . . more simplified) LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 5392 W: 729 L: 631 D: 4032 Ptnml(0-2): 23, 405, 1751, 485, 32 http://tests.stockfishchess.org/tests/view/5e5ead99e42a5c3b3ca2e0e4 closes https://github.com/official-stockfish/Stockfish/pull/2570 Bench 5123316 --- src/endgame.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/endgame.cpp b/src/endgame.cpp index 16c072beb2c..73a4463362a 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -54,9 +54,9 @@ namespace { 4160, 4480, 4800, 5120, 5440, 5760, 6080, 6400 }; - // Tables used to drive a piece towards or away from another piece - constexpr int PushClose[8] = { 0, 0, 100, 80, 60, 40, 20, 10 }; - constexpr int PushAway [8] = { 0, 5, 20, 40, 60, 80, 90, 100 }; + // Drive a piece close to or away from another piece + inline int push_close(Square s1, Square s2) { return 140 - 20 * distance(s1, s2); } + inline int push_away(Square s1, Square s2) { return 120 - push_close(s1, s2); } // Pawn Rank based scaling factors used in KRPPKRP endgame constexpr int KRPPKRPScaleFactors[RANK_NB] = { 0, 9, 10, 14, 21, 44, 0, 0 }; @@ -130,7 +130,7 @@ Value Endgame::operator()(const Position& pos) const { Value result = pos.non_pawn_material(strongSide) + pos.count(strongSide) * PawnValueEg + PushToEdges[loserKSq] - + PushClose[distance(winnerKSq, loserKSq)]; + + push_close(winnerKSq, loserKSq); if ( pos.count(strongSide) || pos.count(strongSide) @@ -159,7 +159,7 @@ Value Endgame::operator()(const Position& pos) const { // to drive to opposite corners (A8/H1). Value result = VALUE_KNOWN_WIN - + PushClose[distance(winnerKSq, loserKSq)] + + push_close(winnerKSq, loserKSq) + PushToCorners[opposite_colors(bishopSq, SQ_A1) ? ~loserKSq : loserKSq]; assert(abs(result) < VALUE_TB_WIN_IN_MAX_PLY); @@ -258,7 +258,7 @@ Value Endgame::operator()(const Position& pos) const { Square bksq = pos.square(weakSide); Square bnsq = pos.square(weakSide); - Value result = Value(PushToEdges[bksq] + PushAway[distance(bksq, bnsq)]); + Value result = Value(PushToEdges[bksq] + push_away(bksq, bnsq)); return strongSide == pos.side_to_move() ? result : -result; } @@ -277,7 +277,7 @@ Value Endgame::operator()(const Position& pos) const { Square loserKSq = pos.square(weakSide); Square pawnSq = pos.square(weakSide); - Value result = Value(PushClose[distance(winnerKSq, loserKSq)]); + Value result = Value(push_close(winnerKSq, loserKSq)); if ( relative_rank(weakSide, pawnSq) != RANK_7 || distance(loserKSq, pawnSq) != 1 @@ -304,7 +304,7 @@ Value Endgame::operator()(const Position& pos) const { Value result = QueenValueEg - RookValueEg + PushToEdges[loserKSq] - + PushClose[distance(winnerKSq, loserKSq)]; + + push_close(winnerKSq, loserKSq); return strongSide == pos.side_to_move() ? result : -result; } From 0424273d0b20ae7ad65143b530b2db8b94de0338 Mon Sep 17 00:00:00 2001 From: protonspring Date: Tue, 3 Mar 2020 16:35:45 -0700 Subject: [PATCH 0159/1766] Small speed-up in BetweenBB A speed-up removing some comparisons. closes https://github.com/official-stockfish/Stockfish/pull/2571 No functional change. --- src/bitboard.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bitboard.h b/src/bitboard.h index ca161481c45..b0e272338c0 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -199,8 +199,8 @@ inline Bitboard adjacent_files_bb(Square s) { /// If the given squares are not on a same file/rank/diagonal, return 0. inline Bitboard between_bb(Square s1, Square s2) { - return LineBB[s1][s2] & ( (AllSquares << (s1 + (s1 < s2))) - ^(AllSquares << (s2 + !(s1 < s2)))); + Bitboard b = LineBB[s1][s2] & ((AllSquares << s1) ^ (AllSquares << s2)); + return b & (b - 1); //exclude lsb } From e7c1c8c1abd85a71fd8190e0c1af49214625904b Mon Sep 17 00:00:00 2001 From: protonspring Date: Thu, 5 Mar 2020 12:07:48 -0700 Subject: [PATCH 0160/1766] Cleanup KBPsK endgame * Clarify distinction between strong side pawns and all pawns. * Simplify and speed-up determination of pawns on the same file. * Clarify comments. * more_than_one() is probably faster than pos.count. Passed STC: LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 40696 W: 7856 L: 7740 D: 25100 Ptnml(0-2): 584, 4519, 10054, 4579, 612 http://tests.stockfishchess.org/tests/view/5e6153b1e42a5c3b3ca2e1a9 closes https://github.com/official-stockfish/Stockfish/pull/2574 No functional change. --- src/endgame.cpp | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/endgame.cpp b/src/endgame.cpp index 73a4463362a..748b05ffdd8 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -343,29 +343,27 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // No assertions about the material of weakSide, because we want draws to // be detected even when the weaker side has some pawns. - Bitboard pawns = pos.pieces(strongSide, PAWN); - File pawnsFile = file_of(lsb(pawns)); + Bitboard strongpawns = pos.pieces(strongSide, PAWN); + Bitboard allpawns = pos.pieces(PAWN); - // All pawns are on a single rook file? - if ( (pawnsFile == FILE_A || pawnsFile == FILE_H) - && !(pawns & ~file_bb(pawnsFile))) + // All strongSide pawns are on a single rook file? + if (!(strongpawns & ~FileABB) || !(strongpawns & ~FileHBB)) { Square bishopSq = pos.square(strongSide); - Square queeningSq = relative_square(strongSide, make_square(pawnsFile, RANK_8)); - Square kingSq = pos.square(weakSide); + Square queeningSq = relative_square(strongSide, make_square(file_of(lsb(strongpawns)), RANK_8)); + Square weakkingSq = pos.square(weakSide); if ( opposite_colors(queeningSq, bishopSq) - && distance(queeningSq, kingSq) <= 1) + && distance(queeningSq, weakkingSq) <= 1) return SCALE_FACTOR_DRAW; } // If all the pawns are on the same B or G file, then it's potentially a draw - if ( (pawnsFile == FILE_B || pawnsFile == FILE_G) - && !(pos.pieces(PAWN) & ~file_bb(pawnsFile)) + if ((!(allpawns & ~FileBBB) || !(allpawns & ~FileGBB)) && pos.non_pawn_material(weakSide) == 0 && pos.count(weakSide) >= 1) { - // Get weakSide pawn that is closest to the home rank + // Get the least advanced weakSide pawn Square weakPawnSq = frontmost_sq(strongSide, pos.pieces(weakSide, PAWN)); Square strongKingSq = pos.square(strongSide); @@ -375,8 +373,8 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // There's potential for a draw if our pawn is blocked on the 7th rank, // the bishop cannot attack it or they only have one pawn left if ( relative_rank(strongSide, weakPawnSq) == RANK_7 - && (pos.pieces(strongSide, PAWN) & (weakPawnSq + pawn_push(weakSide))) - && (opposite_colors(bishopSq, weakPawnSq) || pos.count(strongSide) == 1)) + && (strongpawns & (weakPawnSq + pawn_push(weakSide))) + && (opposite_colors(bishopSq, weakPawnSq) || !more_than_one(strongpawns))) { int strongKingDist = distance(weakPawnSq, strongKingSq); int weakKingDist = distance(weakPawnSq, weakKingSq); From 9690cd6295fbed93ee434e7b2e16181e475755ac Mon Sep 17 00:00:00 2001 From: protonspring Date: Wed, 4 Mar 2020 11:32:17 -0700 Subject: [PATCH 0161/1766] Remove KRPPKRPScaleFactors array Fucntional simplification that removes the KRPPKRPScaleFactors array. STC LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 47374 W: 9159 L: 9049 D: 29166 Ptnml(0-2): 707, 5325, 11560, 5341, 754 http://tests.stockfishchess.org/tests/view/5e5ff464e42a5c3b3ca2e156 LTC LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 31268 W: 4064 L: 3995 D: 23209 Ptnml(0-2): 173, 2734, 9764, 2777, 186 http://tests.stockfishchess.org/tests/view/5e61be6ce42a5c3b3ca2e1c1 closes https://github.com/official-stockfish/Stockfish/pull/2575 Bench 5123316 --- src/endgame.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/endgame.cpp b/src/endgame.cpp index 748b05ffdd8..53c9ec8364c 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -58,9 +58,6 @@ namespace { inline int push_close(Square s1, Square s2) { return 140 - 20 * distance(s1, s2); } inline int push_away(Square s1, Square s2) { return 120 - push_close(s1, s2); } - // Pawn Rank based scaling factors used in KRPPKRP endgame - constexpr int KRPPKRPScaleFactors[RANK_NB] = { 0, 9, 10, 14, 21, 44, 0, 0 }; - #ifndef NDEBUG bool verify_material(const Position& pos, Color c, Value npm, int pawnsCnt) { return pos.non_pawn_material(c) == npm && pos.count(c) == pawnsCnt; @@ -587,7 +584,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { && relative_rank(strongSide, bksq) > r) { assert(r > RANK_1 && r < RANK_7); - return ScaleFactor(KRPPKRPScaleFactors[r]); + return ScaleFactor(7 * r); } return SCALE_FACTOR_NONE; } From 37e38639279bf58558b92932739da57e7c2e3bdc Mon Sep 17 00:00:00 2001 From: Gary Heckman Date: Thu, 5 Mar 2020 12:37:08 -0500 Subject: [PATCH 0162/1766] Fix ambiguity between clamp implementations There is an ambiguity between global and std clamp implementations when compiling in c++17, and on certain toolchains that are not strictly conforming to c++11. This is solved by putting our clamp implementation in a namespace. closes https://github.com/official-stockfish/Stockfish/pull/2572 No functional change. --- AUTHORS | 1 + src/bitboard.h | 3 --- src/evaluate.cpp | 4 ++-- src/material.cpp | 2 +- src/misc.h | 8 ++++++++ src/pawns.cpp | 2 +- src/search.cpp | 6 +++--- 7 files changed, 16 insertions(+), 10 deletions(-) diff --git a/AUTHORS b/AUTHORS index a9f141f961e..7657acee6b7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -48,6 +48,7 @@ fanon Fauzi Akram Dabat (FauziAkram) Felix Wittmann gamander +Gary Heckman (gheckman) gguliash Gian-Carlo Pascutto (gcp) Gontran Lemaire (gonlem) diff --git a/src/bitboard.h b/src/bitboard.h index b0e272338c0..3ea18dd88d6 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -255,9 +255,6 @@ template<> inline int distance(Square x, Square y) { return std::abs(file_ template<> inline int distance(Square x, Square y) { return std::abs(rank_of(x) - rank_of(y)); } template<> inline int distance(Square x, Square y) { return SquareDistance[x][y]; } -template constexpr const T& clamp(const T& v, const T& lo, const T& hi) { - return v < lo ? lo : v > hi ? hi : v; -} /// attacks_bb() returns a bitboard representing all the squares attacked by a /// piece of type Pt (bishop or rook) placed on 's'. diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 31272f2cffc..5d073b15756 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -236,8 +236,8 @@ namespace { attackedBy2[Us] = dblAttackByPawn | (attackedBy[Us][KING] & attackedBy[Us][PAWN]); // Init our king safety tables - Square s = make_square(clamp(file_of(ksq), FILE_B, FILE_G), - clamp(rank_of(ksq), RANK_2, RANK_7)); + Square s = make_square(Utility::clamp(file_of(ksq), FILE_B, FILE_G), + Utility::clamp(rank_of(ksq), RANK_2, RANK_7)); kingRing[Us] = PseudoAttacks[KING][s] | s; kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them)); diff --git a/src/material.cpp b/src/material.cpp index 0e1308780dd..7e212461098 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -129,7 +129,7 @@ Entry* probe(const Position& pos) { Value npm_w = pos.non_pawn_material(WHITE); Value npm_b = pos.non_pawn_material(BLACK); - Value npm = clamp(npm_w + npm_b, EndgameLimit, MidgameLimit); + Value npm = Utility::clamp(npm_w + npm_b, EndgameLimit, MidgameLimit); // Map total non-pawn material into [PHASE_ENDGAME, PHASE_MIDGAME] e->gamePhase = Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit)); diff --git a/src/misc.h b/src/misc.h index a3780ba599d..e0e0e98be83 100644 --- a/src/misc.h +++ b/src/misc.h @@ -64,6 +64,14 @@ std::ostream& operator<<(std::ostream&, SyncCout); #define sync_cout std::cout << IO_LOCK #define sync_endl std::endl << IO_UNLOCK +namespace Utility { + +/// Clamp a value between lo and hi. Available in c++17. +template constexpr const T& clamp(const T& v, const T& lo, const T& hi) { + return v < lo ? lo : v > hi ? hi : v; +} + +} /// xorshift64star Pseudo-Random Number Generator /// This class is based on original code written and dedicated diff --git a/src/pawns.cpp b/src/pawns.cpp index c3f7872f9e7..9981ac0140d 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -193,7 +193,7 @@ Score Entry::evaluate_shelter(const Position& pos, Square ksq) { Score bonus = make_score(5, 5); - File center = clamp(file_of(ksq), FILE_B, FILE_G); + File center = Utility::clamp(file_of(ksq), FILE_B, FILE_G); for (File f = File(center - 1); f <= File(center + 1); ++f) { b = ourPawns & file_bb(f); diff --git a/src/search.cpp b/src/search.cpp index 7f6abf15aef..3d130efcdb2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -365,7 +365,7 @@ void Thread::search() { // for match (TC 60+0.6) results spanning a wide range of k values. PRNG rng(now()); double floatLevel = Options["UCI_LimitStrength"] ? - clamp(std::pow((Options["UCI_Elo"] - 1346.6) / 143.4, 1 / 0.806), 0.0, 20.0) : + Utility::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); @@ -538,7 +538,7 @@ void Thread::search() { { double fallingEval = (332 + 6 * (mainThread->previousScore - bestValue) + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 704.0; - fallingEval = clamp(fallingEval, 0.5, 1.5); + fallingEval = Utility::clamp(fallingEval, 0.5, 1.5); // If the bestMove is stable over several iterations, reduce time accordingly timeReduction = lastBestMoveDepth + 9 < completedDepth ? 1.94 : 0.91; @@ -1197,7 +1197,7 @@ namespace { else if (depth < 8 && moveCount > 2) r++; - Depth d = clamp(newDepth - r, 1, newDepth); + Depth d = Utility::clamp(newDepth - r, 1, newDepth); value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); From 47be966d3028ca9b5c4d095f266663eb205c0c07 Mon Sep 17 00:00:00 2001 From: protonspring Date: Mon, 9 Mar 2020 22:11:08 +0100 Subject: [PATCH 0163/1766] Equations for edges and corners. This is a functional simplification that removes the large arrays in endgames.cpp. It also fixes a recently introduced bug (960d59d54143d84aab26deae65279a611fc989f4) in KNBvK, now using flip_file() instead of ~. One fen added to bench to increase endgame coverage. STC LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 174724 W: 33325 L: 33404 D: 107995 Ptnml(0-2): 2503, 19607, 43181, 19608, 2463 http://tests.stockfishchess.org/tests/view/5e6448ffe42a5c3b3ca2e287 LTC LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 35640 W: 4679 L: 4621 D: 26340 Ptnml(0-2): 189, 2991, 11424, 3005, 211 http://tests.stockfishchess.org/tests/view/5e650b24e42a5c3b3ca2e2d8 closes https://github.com/official-stockfish/Stockfish/pull/2577 Bench: 5527957 --- src/benchmark.cpp | 1 + src/bitboard.h | 2 ++ src/endgame.cpp | 48 +++++++++++++++--------------------------- src/evaluate.cpp | 2 +- src/pawns.cpp | 2 +- src/psqt.cpp | 3 ++- src/syzygy/tbprobe.cpp | 2 +- src/types.h | 4 ---- 8 files changed, 25 insertions(+), 39 deletions(-) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index f906e731b40..f338cdda859 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -65,6 +65,7 @@ const vector Defaults = { "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", + "4k3/3q1r2/1N2r1b1/3ppN2/2nPP3/1B1R2n1/2R1Q3/3K4 w - - 5 1", // 5-man positions "8/8/8/8/5kp1/P7/8/1K1N4 w - - 0 1", // Kc2 - mate diff --git a/src/bitboard.h b/src/bitboard.h index 3ea18dd88d6..f1d14603687 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -255,6 +255,8 @@ template<> inline int distance(Square x, Square y) { return std::abs(file_ template<> inline int distance(Square x, Square y) { return std::abs(rank_of(x) - rank_of(y)); } template<> inline int distance(Square x, Square y) { return SquareDistance[x][y]; } +inline File edge_distance(File f) { return std::min(f, File(FILE_H - f)); } +inline Rank edge_distance(Rank r) { return std::min(r, Rank(RANK_8 - r)); } /// attacks_bb() returns a bitboard representing all the squares attacked by a /// piece of type Pt (bishop or rook) placed on 's'. diff --git a/src/endgame.cpp b/src/endgame.cpp index 53c9ec8364c..0a2b02ad6c9 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -28,31 +28,17 @@ using std::string; namespace { - // Table used to drive the king towards the edge of the board + // Used to drive the king towards the edge of the board // in KX vs K and KQ vs KR endgames. - constexpr int PushToEdges[SQUARE_NB] = { - 100, 90, 80, 70, 70, 80, 90, 100, - 90, 70, 60, 50, 50, 60, 70, 90, - 80, 60, 40, 30, 30, 40, 60, 80, - 70, 50, 30, 20, 20, 30, 50, 70, - 70, 50, 30, 20, 20, 30, 50, 70, - 80, 60, 40, 30, 30, 40, 60, 80, - 90, 70, 60, 50, 50, 60, 70, 90, - 100, 90, 80, 70, 70, 80, 90, 100 - }; - - // Table used to drive the king towards a corner square of the - // right color in KBN vs K endgames. - constexpr int PushToCorners[SQUARE_NB] = { - 6400, 6080, 5760, 5440, 5120, 4800, 4480, 4160, - 6080, 5760, 5440, 5120, 4800, 4480, 4160, 4480, - 5760, 5440, 4960, 4480, 4480, 4000, 4480, 4800, - 5440, 5120, 4480, 3840, 3520, 4480, 4800, 5120, - 5120, 4800, 4480, 3520, 3840, 4480, 5120, 5440, - 4800, 4480, 4000, 4480, 4480, 4960, 5440, 5760, - 4480, 4160, 4480, 4800, 5120, 5440, 5760, 6080, - 4160, 4480, 4800, 5120, 5440, 5760, 6080, 6400 - }; + inline int push_to_edge(Square s) { + int rd = edge_distance(rank_of(s)), fd = edge_distance(file_of(s)); + return 90 - (7 * fd * fd / 2 + 7 * rd * rd / 2); + } + + // Used to drive the king towards A1H8 corners in KBN vs K endgames. + inline int push_to_corner(Square s) { + return abs(7 - rank_of(s) - file_of(s)); + } // Drive a piece close to or away from another piece inline int push_close(Square s1, Square s2) { return 140 - 20 * distance(s1, s2); } @@ -126,7 +112,7 @@ Value Endgame::operator()(const Position& pos) const { Value result = pos.non_pawn_material(strongSide) + pos.count(strongSide) * PawnValueEg - + PushToEdges[loserKSq] + + push_to_edge(loserKSq) + push_close(winnerKSq, loserKSq); if ( pos.count(strongSide) @@ -155,9 +141,9 @@ Value Endgame::operator()(const Position& pos) const { // If our bishop does not attack A1/H8, we flip the enemy king square // to drive to opposite corners (A8/H1). - Value result = VALUE_KNOWN_WIN + Value result = (VALUE_KNOWN_WIN + 3520) + push_close(winnerKSq, loserKSq) - + PushToCorners[opposite_colors(bishopSq, SQ_A1) ? ~loserKSq : loserKSq]; + + 420 * push_to_corner(opposite_colors(bishopSq, SQ_A1) ? flip_file(loserKSq) : loserKSq); assert(abs(result) < VALUE_TB_WIN_IN_MAX_PLY); return strongSide == pos.side_to_move() ? result : -result; @@ -240,7 +226,7 @@ Value Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, RookValueMg, 0)); assert(verify_material(pos, weakSide, BishopValueMg, 0)); - Value result = Value(PushToEdges[pos.square(weakSide)]); + Value result = Value(push_to_edge(pos.square(weakSide))); return strongSide == pos.side_to_move() ? result : -result; } @@ -255,7 +241,7 @@ Value Endgame::operator()(const Position& pos) const { Square bksq = pos.square(weakSide); Square bnsq = pos.square(weakSide); - Value result = Value(PushToEdges[bksq] + push_away(bksq, bnsq)); + Value result = Value(push_to_edge(bksq) + push_away(bksq, bnsq)); return strongSide == pos.side_to_move() ? result : -result; } @@ -300,7 +286,7 @@ Value Endgame::operator()(const Position& pos) const { Value result = QueenValueEg - RookValueEg - + PushToEdges[loserKSq] + + push_to_edge(loserKSq) + push_close(winnerKSq, loserKSq); return strongSide == pos.side_to_move() ? result : -result; @@ -316,7 +302,7 @@ Value Endgame::operator()(const Position& pos) const { assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); Value result = PawnValueEg - + 2 * PushToEdges[pos.square(weakSide)] + + 2 * push_to_edge(pos.square(weakSide)) - 10 * relative_rank(weakSide, pos.square(weakSide)); return strongSide == pos.side_to_move() ? result : -result; diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 5d073b15756..40630d22e31 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -643,7 +643,7 @@ namespace { || (pos.pieces(PAWN) & (s + Up))) bonus = bonus / 2; - score += bonus - PassedFile * map_to_queenside(file_of(s)); + score += bonus - PassedFile * edge_distance(file_of(s)); } if (T) diff --git a/src/pawns.cpp b/src/pawns.cpp index 9981ac0140d..560fd76b171 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -202,7 +202,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; - File d = map_to_queenside(f); + File d = edge_distance(f); bonus += make_score(ShelterStrength[d][ourRank], 0); if (ourRank && (ourRank == theirRank - 1)) diff --git a/src/psqt.cpp b/src/psqt.cpp index f6f5933cb25..d86e98e4ee3 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -21,6 +21,7 @@ #include #include "types.h" +#include "bitboard.h" namespace PSQT { @@ -111,7 +112,7 @@ void init() { for (Square s = SQ_A1; s <= SQ_H8; ++s) { - File f = map_to_queenside(file_of(s)); + File f = edge_distance(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][flip_rank(s)] = -psq[pc][s]; diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 6f36945587f..2532bbd34fc 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -705,7 +705,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu std::swap(squares[0], *std::max_element(squares, squares + leadPawnsCnt, pawns_comp)); - tbFile = map_to_queenside(file_of(squares[0])); + tbFile = edge_distance(file_of(squares[0])); } // DTZ tables are one-sided, i.e. they store positions only for white to diff --git a/src/types.h b/src/types.h index d4937fd65cd..71893c0ffd6 100644 --- a/src/types.h +++ b/src/types.h @@ -370,10 +370,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 c077bfb413fbed8d6fa94459135ca81f9977c2f2 Mon Sep 17 00:00:00 2001 From: silversolver1 <61594747+silversolver1@users.noreply.github.com> Date: Sun, 8 Mar 2020 14:52:05 -0500 Subject: [PATCH 0164/1766] Remove set statScore to zero Simplification. Removes setting statScore to zero if negative. STC: LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 84820 W: 16100 L: 16033 D: 52687 Ptnml(0-2): 1442, 9865, 19723, 9944, 1436 https://tests.stockfishchess.org/tests/view/5e654fdae42a5c3b3ca2e2f8 LTC: LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 57658 W: 7435 L: 7391 D: 42832 Ptnml(0-2): 441, 5397, 17104, 5451, 436 https://tests.stockfishchess.org/tests/view/5e657ce9e42a5c3b3ca2e307 closes https://github.com/official-stockfish/Stockfish/pull/2578 Bench: 5168890 --- AUTHORS | 5 +---- src/search.cpp | 7 ------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/AUTHORS b/AUTHORS index 7657acee6b7..4826d1c4c3c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -123,6 +123,7 @@ Pasquale Pigazzini (ppigazzini) Patrick Jansen (mibere) pellanda Peter Zsifkovits (CoffeeOne) +Rahul Dsilva (silversolver1) Ralph Stößer (Ralph Stoesser) Raminder Singh renouve @@ -158,7 +159,3 @@ Vince Negri (cuddlestmonkey) # an amazing and essential framework for the development of Stockfish! # # https://github.com/glinscott/fishtest/blob/master/AUTHORS - - - - diff --git a/src/search.cpp b/src/search.cpp index 3d130efcdb2..f9910fb7208 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1175,13 +1175,6 @@ namespace { + (*contHist[3])[movedPiece][to_sq(move)] - 4926; - // 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 >= -102 && (ss-1)->statScore < -114) r--; From 442e1e0f9348226986e568bd52e1b909ec347218 Mon Sep 17 00:00:00 2001 From: protonspring Date: Wed, 11 Mar 2020 16:27:51 -0600 Subject: [PATCH 0165/1766] simplify castling part of generate_all. somewhat more compact, generates same code. close https://github.com/official-stockfish/Stockfish/pull/2580 No functional change. --- src/movegen.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index 7e8961ae3b6..9964ad343b8 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -214,9 +214,6 @@ namespace { template ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target) { - - 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); @@ -232,14 +229,10 @@ namespace { while (b) *moveList++ = make_move(ksq, pop_lsb(&b)); - 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)); - - if (!pos.castling_impeded(OOO) && pos.can_castle(OOO)) - *moveList++ = make(ksq, pos.castling_rook_square(OOO)); - } + if ((Type != CAPTURES) && pos.can_castle(Us & ANY_CASTLING)) + for(CastlingRights cr : { Us & KING_SIDE, Us & QUEEN_SIDE } ) + if (!pos.castling_impeded(cr) && pos.can_castle(cr)) + *moveList++ = make(ksq, pos.castling_rook_square(cr)); } return moveList; From ec2002c594cce22dfbbdc7b6b8df2828a00d18cf Mon Sep 17 00:00:00 2001 From: pb00067 Date: Fri, 13 Mar 2020 19:29:36 +0100 Subject: [PATCH 0166/1766] Simplify futility pruning parent node only continuation histories seem needed for this purpose. STC: http://tests.stockfishchess.org/tests/view/5e6b88dfe42a5c3b3ca2e4ab LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 113356 W: 21725 L: 21696 D: 69935 Ptnml(0-2): 1999, 13255, 26163, 13240, 2021 LTC: http://tests.stockfishchess.org/tests/view/5e6babbfe42a5c3b3ca2e4c2 LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 22164 W: 2917 L: 2821 D: 16426 Ptnml(0-2): 173, 2040, 6548, 2160, 161 closes https://github.com/official-stockfish/Stockfish/pull/2583 bench: 4839496 --- src/search.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index f9910fb7208..70520ac9ff7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1024,10 +1024,9 @@ namespace { if ( lmrDepth < 6 && !inCheck && ss->staticEval + 235 + 172 * lmrDepth <= alpha - && thisThread->mainHistory[us][from_to(move)] - + (*contHist[0])[movedPiece][to_sq(move)] + && (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] - + (*contHist[3])[movedPiece][to_sq(move)] < 25000) + + (*contHist[3])[movedPiece][to_sq(move)] < 27400) continue; // Prune moves with negative SEE (~20 Elo) From ddcbacd04d1c860e808202ce8c1206c8acdca627 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 14 Mar 2020 17:04:50 +0100 Subject: [PATCH 0167/1766] Small cleanups closes https://github.com/official-stockfish/Stockfish/pull/2567 No functional change. --- src/bitboard.h | 5 ++++- src/endgame.cpp | 31 +++++++++++++------------------ src/position.cpp | 5 +---- src/search.cpp | 10 ++++++++-- src/syzygy/tbprobe.cpp | 2 +- src/tt.h | 2 +- src/types.h | 2 +- src/uci.cpp | 2 +- 8 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/bitboard.h b/src/bitboard.h index f1d14603687..529e3dfe5f4 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -375,14 +375,17 @@ inline Square msb(Bitboard b) { /// pop_lsb() finds and clears the least significant bit in a non-zero bitboard inline Square pop_lsb(Bitboard* b) { + assert(*b); const Square s = lsb(*b); *b &= *b - 1; return s; } -/// frontmost_sq() returns the most advanced square for the given color +/// frontmost_sq() returns the most advanced square for the given color, +/// requires a non-zero bitboard. inline Square frontmost_sq(Color c, Bitboard b) { + assert(b); return c == WHITE ? msb(b) : lsb(b); } diff --git a/src/endgame.cpp b/src/endgame.cpp index 0a2b02ad6c9..56583c58890 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -24,8 +24,6 @@ #include "endgame.h" #include "movegen.h" -using std::string; - namespace { // Used to drive the king towards the edge of the board @@ -326,23 +324,23 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // No assertions about the material of weakSide, because we want draws to // be detected even when the weaker side has some pawns. - Bitboard strongpawns = pos.pieces(strongSide, PAWN); - Bitboard allpawns = pos.pieces(PAWN); + Bitboard strongPawns = pos.pieces(strongSide, PAWN); + Bitboard allPawns = pos.pieces(PAWN); // All strongSide pawns are on a single rook file? - if (!(strongpawns & ~FileABB) || !(strongpawns & ~FileHBB)) + if (!(strongPawns & ~FileABB) || !(strongPawns & ~FileHBB)) { Square bishopSq = pos.square(strongSide); - Square queeningSq = relative_square(strongSide, make_square(file_of(lsb(strongpawns)), RANK_8)); - Square weakkingSq = pos.square(weakSide); + Square queeningSq = relative_square(strongSide, make_square(file_of(lsb(strongPawns)), RANK_8)); + Square weakKingSq = pos.square(weakSide); if ( opposite_colors(queeningSq, bishopSq) - && distance(queeningSq, weakkingSq) <= 1) + && distance(queeningSq, weakKingSq) <= 1) return SCALE_FACTOR_DRAW; } // If all the pawns are on the same B or G file, then it's potentially a draw - if ((!(allpawns & ~FileBBB) || !(allpawns & ~FileGBB)) + if ((!(allPawns & ~FileBBB) || !(allPawns & ~FileGBB)) && pos.non_pawn_material(weakSide) == 0 && pos.count(weakSide) >= 1) { @@ -356,8 +354,8 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // There's potential for a draw if our pawn is blocked on the 7th rank, // the bishop cannot attack it or they only have one pawn left if ( relative_rank(strongSide, weakPawnSq) == RANK_7 - && (strongpawns & (weakPawnSq + pawn_push(weakSide))) - && (opposite_colors(bishopSq, weakPawnSq) || !more_than_one(strongpawns))) + && (strongPawns & (weakPawnSq + pawn_push(weakSide))) + && (opposite_colors(bishopSq, weakPawnSq) || !more_than_one(strongPawns))) { int strongKingDist = distance(weakPawnSq, strongKingSq); int weakKingDist = distance(weakPawnSq, weakKingSq); @@ -588,11 +586,9 @@ ScaleFactor Endgame::operator()(const Position& pos) const { Square ksq = pos.square(weakSide); Bitboard pawns = pos.pieces(strongSide, PAWN); - // If all pawns are ahead of the king, on a single rook file and - // the king is within one file of the pawns, it's a draw. - if ( !(pawns & ~forward_ranks_bb(weakSide, ksq)) - && !((pawns & ~FileABB) && (pawns & ~FileHBB)) - && distance(ksq, lsb(pawns)) <= 1) + // If all pawns are ahead of the king on a single rook file, it's a draw. + if (!((pawns & ~FileABB) || (pawns & ~FileHBB)) && + !(pawns & ~passed_pawn_span(weakSide, ksq))) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; @@ -615,8 +611,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { Square weakKingSq = pos.square(weakSide); // Case 1: Defending king blocks the pawn, and cannot be driven away - if ( file_of(weakKingSq) == file_of(pawnSq) - && relative_rank(strongSide, pawnSq) < relative_rank(strongSide, weakKingSq) + if ( (forward_file_bb(strongSide, pawnSq) & weakKingSq) && ( opposite_colors(weakKingSq, strongBishopSq) || relative_rank(strongSide, weakKingSq) <= RANK_6)) return SCALE_FACTOR_DRAW; diff --git a/src/position.cpp b/src/position.cpp index 0ac450570b9..fefce56e2aa 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1121,10 +1121,7 @@ bool Position::is_draw(int ply) const { // Return a draw score if a position repeats once earlier but strictly // after the root, or repeats twice before or at the root. - if (st->repetition && st->repetition < ply) - return true; - - return false; + return st->repetition && st->repetition < ply; } diff --git a/src/search.cpp b/src/search.cpp index 70520ac9ff7..a54e181a2a9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1193,10 +1193,16 @@ namespace { value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); - doFullDepthSearch = (value > alpha && d != newDepth), didLMR = true; + doFullDepthSearch = value > alpha && d != newDepth; + + didLMR = true; } else - doFullDepthSearch = !PvNode || moveCount > 1, didLMR = false; + { + doFullDepthSearch = !PvNode || moveCount > 1; + + didLMR = false; + } // Step 17. Full depth search when LMR is skipped or fails high if (doFullDepthSearch) diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 2532bbd34fc..34e4331da1c 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -769,7 +769,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu if (!off_A1H8(squares[i])) continue; - if (off_A1H8(squares[i]) > 0) // A1-H8 diagonal flip: SQ_A3 -> SQ_C3 + if (off_A1H8(squares[i]) > 0) // A1-H8 diagonal flip: SQ_A3 -> SQ_C1 for (int j = i; j < size; ++j) squares[j] = Square(((squares[j] >> 3) | (squares[j] << 3)) & 63); break; diff --git a/src/tt.h b/src/tt.h index 142afd90e56..8b70f797876 100644 --- a/src/tt.h +++ b/src/tt.h @@ -41,7 +41,7 @@ struct TTEntry { Value value() const { return (Value)value16; } Value eval() const { return (Value)eval16; } Depth depth() const { return (Depth)depth8 + DEPTH_OFFSET; } - bool is_pv() const { return (bool)(genBound8 & 0x4); } + 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 71893c0ffd6..bfcd3f23f11 100644 --- a/src/types.h +++ b/src/types.h @@ -220,7 +220,7 @@ enum : int { DEPTH_QS_RECAPTURES = -5, DEPTH_NONE = -6, - DEPTH_OFFSET = DEPTH_NONE, + DEPTH_OFFSET = DEPTH_NONE }; enum Square : int { diff --git a/src/uci.cpp b/src/uci.cpp index 8b35e6fdd56..33577a4eca3 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -260,7 +260,7 @@ string UCI::value(Value v) { stringstream ss; - if (abs(v) < VALUE_MATE - MAX_PLY) + if (abs(v) < VALUE_MATE_IN_MAX_PLY) ss << "cp " << v * 100 / PawnValueEg; else ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; From 07caca2587d3090921b99f37fa8c9bf6a29a89af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Tue, 17 Mar 2020 08:26:27 +0100 Subject: [PATCH 0168/1766] Anchored bishops Reduce the "bad bishop" penalty when the bishop is protected by one of our pawns, as it may indicate that the bishop has found a safe spot outside the pawn chain. STC: LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 176942 W: 34142 L: 33696 D: 109104 Ptnml(0-2): 3129, 20422, 40919, 20876, 3125 http://tests.stockfishchess.org/tests/view/5e6f61aae42a5c3b3ca2e62d LTC: LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 42252 W: 5615 L: 5322 D: 31315 Ptnml(0-2): 308, 3881, 12500, 4084, 353 http://tests.stockfishchess.org/tests/view/5e701382e42a5c3b3ca2e661 closes https://github.com/official-stockfish/Stockfish/pull/2587 Bench: 4963440 --- src/evaluate.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 40630d22e31..fad2a785acc 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -308,11 +308,12 @@ namespace { if (Pt == BISHOP) { // Penalty according to number of pawns on the same color square as the - // bishop, bigger when the center files are blocked with pawns. + // bishop, bigger when the center files are blocked with pawns and smaller + // when the bishop is outside the pawn chain. Bitboard blocked = pos.pieces(Us, PAWN) & shift(pos.pieces()); score -= BishopPawns * pos.pawns_on_same_color_squares(Us, s) - * (1 + popcount(blocked & CenterFiles)); + * (!bool(attackedBy[Us][PAWN] & s) + popcount(blocked & CenterFiles)); // Bonus for bishop on a long diagonal which can "see" both center squares if (more_than_one(attacks_bb(s, pos.pieces(PAWN)) & Center)) From ff271093139de92ebf4f4f8b8c67474e07d6a8bf Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Tue, 17 Mar 2020 19:38:21 +0300 Subject: [PATCH 0169/1766] Adjust singular LMR for positions seen in PV This patch continues work on altering search for ttPv nodes, using recent idea to alter it more in not PvNodes. Previous tweak based on this idea adjusted singularBeta - this one adjusts value of singularLMR, so they are both related to singular extension search. passed STC http://tests.stockfishchess.org/tests/view/5e700737e42a5c3b3ca2e659 LLR: 2.95 (-2.94,2.94) {-0.50,1.50} Total: 140608 W: 27053 L: 26659 D: 86896 Ptnml(0-2): 2425, 16337, 32439, 16625, 2478 passed LTC http://tests.stockfishchess.org/tests/view/5e7068eae42a5c3b3ca2e687 LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 79318 W: 10463 L: 10064 D: 58791 Ptnml(0-2): 567, 7416, 23359, 7685, 632 closes https://github.com/official-stockfish/Stockfish/pull/2588 Bench: 4952322 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index a54e181a2a9..fe57806c707 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1149,7 +1149,7 @@ namespace { // Decrease reduction if ttMove has been singularly extended (~3 Elo) if (singularLMR) - r -= 2; + r -= 1 + (ttPv && !PvNode); if (!captureOrPromotion) { From 6ecab03dee15fe30bc0237919180a2e51e0ce4b1 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Fri, 20 Mar 2020 12:12:56 +0300 Subject: [PATCH 0170/1766] Adjust singular extension search depth This patch applies a different singular extension search logic in case the position is ttPv && !PvNode. It changes the depth of this search, higher for this types of nodes, and lower for other nodes. passed STC http://tests.stockfishchess.org/tests/view/5e72bbaae42a5c3b3ca2e75e LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 12692 W: 2608 L: 2389 D: 7695 Ptnml(0-2): 238, 1414, 2839, 1601, 254 passed LTC http://tests.stockfishchess.org/tests/view/5e731c07e42a5c3b3ca2e770 LLR: 2.96 (-2.94,2.94) {0.25,1.75} Total: 145716 W: 19218 L: 18626 D: 107872 Ptnml(0-2): 1100, 13605, 42899, 14111, 1143 closes https://github.com/official-stockfish/Stockfish/pull/2590 Bench: 5398277 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index fe57806c707..abc6874e995 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1055,9 +1055,9 @@ namespace { && pos.legal(move)) { Value singularBeta = ttValue - (((ttPv && !PvNode) + 4) * depth) / 2; - Depth halfDepth = depth / 2; + Depth singularDepth = (depth - 1 + 3 * (ttPv && !PvNode)) / 2; ss->excludedMove = move; - value = search(pos, ss, singularBeta - 1, singularBeta, halfDepth, cutNode); + value = search(pos, ss, singularBeta - 1, singularBeta, singularDepth, cutNode); ss->excludedMove = MOVE_NONE; if (value < singularBeta) From 8b229381daa9c2537dc9a8cf5080a3a282265d0a Mon Sep 17 00:00:00 2001 From: protonspring Date: Sun, 22 Mar 2020 22:21:49 -0600 Subject: [PATCH 0171/1766] Remove KNPKB endgame. This is a functional simplification that removes the KNPKB endgame. Testing on only KNPKB positions suggests that this removal actually gains Elo: Score of patch vs master: 3380 - 3035 - 33585 [0.504] 40000 Elo difference: 3.0 +/- 1.4, LOS: 100.0 %, DrawRatio: 84.0 % Score of patch vs master: 290 - 36 - 39674 [0.503] 40000 Elo difference: 2.2 +/- 0.3, LOS: 100.0 %, DrawRatio: 99.2 % removal also doesn't cause a regression with the standard book: STC LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 71376 W: 13794 L: 13719 D: 43863 Ptnml(0-2): 1066, 8092, 17290, 8181, 1059 https://tests.stockfishchess.org/tests/view/5e76c3d5e42a5c3b3ca2e8be LTC LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 28394 W: 3731 L: 3662 D: 21001 Ptnml(0-2): 167, 2339, 9116, 2408, 167 https://tests.stockfishchess.org/tests/view/5e76e5eae42a5c3b3ca2e8d3 closes https://github.com/official-stockfish/Stockfish/pull/2594 Bench 5480811 --- src/endgame.cpp | 22 ---------------------- src/endgame.h | 1 - 2 files changed, 23 deletions(-) diff --git a/src/endgame.cpp b/src/endgame.cpp index 56583c58890..1a5959e5fea 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -80,7 +80,6 @@ namespace Endgames { add("KNNKP"); add("KNPK"); - add("KNPKB"); add("KRPKR"); add("KRPKB"); add("KBPKB"); @@ -733,27 +732,6 @@ ScaleFactor Endgame::operator()(const Position& pos) const { } -/// KNP vs KB. If knight can block bishop from taking pawn, it's a win. -/// Otherwise the position is drawn. -template<> -ScaleFactor Endgame::operator()(const Position& pos) const { - - assert(verify_material(pos, strongSide, KnightValueMg, 1)); - assert(verify_material(pos, weakSide, BishopValueMg, 0)); - - Square pawnSq = pos.square(strongSide); - Square bishopSq = pos.square(weakSide); - Square weakKingSq = pos.square(weakSide); - - // King needs to get close to promoting pawn to prevent knight from blocking. - // Rules for this are very tricky, so just approximate. - if (forward_file_bb(strongSide, pawnSq) & pos.attacks_from(bishopSq)) - return ScaleFactor(distance(weakKingSq, pawnSq)); - - return SCALE_FACTOR_NONE; -} - - /// KP vs KP. This is done by removing the weakest side's pawn and probing the /// KP vs K bitbase: If the weakest side has a draw without the pawn, it probably /// has at least a draw with the pawn as well. The exception is when the stronger diff --git a/src/endgame.h b/src/endgame.h index f61353542aa..49ebb603a1f 100644 --- a/src/endgame.h +++ b/src/endgame.h @@ -58,7 +58,6 @@ enum EndgameCode { KBPPKB, // KBPP vs KB KBPKN, // KBP vs KN KNPK, // KNP vs K - KNPKB, // KNP vs KB KPKP // KP vs KP }; From c8e8e48b144bcc50c5423b771eb668eafb13af99 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Wed, 25 Mar 2020 16:06:25 +0000 Subject: [PATCH 0172/1766] Remove passed_count from almostUnwinnable. This simplification allows the almostUnwinnable flag to match endgames where the pawns are all on the same flank but are not symmetrical. STC: LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 23356 W: 4543 L: 4395 D: 14418 Ptnml(0-2): 346, 2651, 5582, 2707, 392 https://tests.stockfishchess.org/tests/view/5e7b8f57e42a5c3b3ca2eb09 LTC: LLR: 2.96 (-2.94,2.94) {-1.50,0.50} Total: 31778 W: 4097 L: 4023 D: 23658 Ptnml(0-2): 199, 2853, 9729, 2891, 217 https://tests.stockfishchess.org/tests/view/5e7ba5ade42a5c3b3ca2eb16 closes https://github.com/official-stockfish/Stockfish/pull/2596 Bench 4777139 --- src/evaluate.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index fad2a785acc..d51325f0d99 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -707,8 +707,7 @@ namespace { bool pawnsOnBothFlanks = (pos.pieces(PAWN) & QueenSide) && (pos.pieces(PAWN) & KingSide); - bool almostUnwinnable = !pe->passed_count() - && outflanking < 0 + bool almostUnwinnable = outflanking < 0 && !pawnsOnBothFlanks; bool infiltration = rank_of(pos.square(WHITE)) > RANK_4 From 58746d9fb8bab4e9617cd7c809c6a0afd809c35e Mon Sep 17 00:00:00 2001 From: Lyudmil Antonov Date: Mon, 17 Feb 2020 11:13:03 +0200 Subject: [PATCH 0173/1766] Tuned history reduction Value after a long Bayesian tuning, using a home-made classification approach. STC https://tests.stockfishchess.org/tests/view/5e7c7b16e42a5c3b3ca2eb66 LLR: 2.96 (-2.94,2.94) {-0.50,1.50} Total: 45472 W: 8992 L: 8732 D: 27748 Ptnml(0-2): 795, 5276, 10352, 5500, 813 LTC https://tests.stockfishchess.org/tests/view/5e7c8be7e42a5c3b3ca2eb75 LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 22744 W: 3085 L: 2849 D: 16810 Ptnml(0-2): 156, 2090, 6658, 2298, 170 closes https://github.com/official-stockfish/Stockfish/pull/2597 Bench 5030855 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index abc6874e995..c5a7582ce4d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1182,7 +1182,7 @@ namespace { r++; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - r -= ss->statScore / 16384; + r -= ss->statScore / 16434; } // Increase reduction for captures/promotions if late move and at low depth From 8c73339a3639f1753b2270b569532daffa7d93f5 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Thu, 26 Mar 2020 19:47:48 +0000 Subject: [PATCH 0174/1766] Remove previousScore adjustment of delta. STC: LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 14580 W: 2904 L: 2731 D: 8945 Ptnml(0-2): 243, 1665, 3339, 1762, 281 https://tests.stockfishchess.org/tests/view/5e7d080ae42a5c3b3ca2ebc6 LTC: LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 60338 W: 7870 L: 7831 D: 44637 Ptnml(0-2): 451, 5596, 18018, 5671, 433 https://tests.stockfishchess.org/tests/view/5e7d11b3e42a5c3b3ca2ebd3 closes https://github.com/official-stockfish/Stockfish/pull/2598 Bench 5247262 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index c5a7582ce4d..f866afe5ce7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -433,7 +433,7 @@ void Thread::search() { if (rootDepth >= 4) { Value previousScore = rootMoves[pvIdx].previousScore; - delta = Value(21 + abs(previousScore) / 256); + delta = Value(21); alpha = std::max(previousScore - delta,-VALUE_INFINITE); beta = std::min(previousScore + delta, VALUE_INFINITE); From f2430bf034cc31258c870797501bce0605bce3d0 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sun, 29 Mar 2020 20:04:20 +0300 Subject: [PATCH 0175/1766] Count only the most advanced passed pawn for each file. This patch adjusts definition of passed pawns - if there is a pawn of our color in the same file in front of a current pawn it's no longer counts as passed. passed STC https://tests.stockfishchess.org/tests/view/5e802037e42a5c3b3ca2ed07 LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 215296 W: 41843 L: 41341 D: 132112 Ptnml(0-2): 3688, 25313, 49304, 25495, 3848 passed LTC https://tests.stockfishchess.org/tests/view/5e806441e42a5c3b3ca2ed2b LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 74050 W: 9761 L: 9379 D: 54910 Ptnml(0-2): 510, 6838, 22025, 7064, 588 closes https://github.com/official-stockfish/Stockfish/pull/2602 bench: 4902237 --- src/pawns.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pawns.cpp b/src/pawns.cpp index 560fd76b171..3023021d408 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -124,6 +124,8 @@ namespace { || ( stoppers == blocked && r >= RANK_5 && (shift(support) & ~(theirPawns | doubleAttackThem))); + passed &= !(forward_file_bb(Us, s) & ourPawns); + // Passed pawns will be properly scored later in evaluation when we have // full attack info. if (passed) From b7ecdaada7e2690dd286c41d3a73463f06fb088f Mon Sep 17 00:00:00 2001 From: Praveen tummala Date: Mon, 30 Mar 2020 10:22:42 +0530 Subject: [PATCH 0176/1766] Movecount pruning reduction logic This patch refines search reduction logic in case the position is not a former PV node and is pruned based on move count. passed STC https://tests.stockfishchess.org/tests/view/5e8092bde42a5c3b3ca2ed35 LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 78848 W: 15480 L: 15170 D: 48198 Ptnml(0-2): 1406, 9310, 17773, 9438, 1497 passed LTC https://tests.stockfishchess.org/tests/view/5e80bb13e42a5c3b3ca2ed4b LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 86596 W: 11451 L: 11033 D: 64112 Ptnml(0-2): 624, 7993, 25687, 8329, 665 closes https://github.com/official-stockfish/Stockfish/pull/2605 Bench: 5138771 --- AUTHORS | 3 ++- src/search.cpp | 10 +++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index 4826d1c4c3c..79eb98a0b53 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,4 +1,4 @@ -# List of authors for Stockfish, as of January 7, 2020 +# List of authors for Stockfish, as of March 30, 2020 Tord Romstad (romstad) Marco Costalba (mcostalba) @@ -123,6 +123,7 @@ Pasquale Pigazzini (ppigazzini) Patrick Jansen (mibere) pellanda Peter Zsifkovits (CoffeeOne) +Praveen Kumar Tummala (praveentml) Rahul Dsilva (silversolver1) Ralph Stößer (Ralph Stoesser) Raminder Singh diff --git a/src/search.cpp b/src/search.cpp index f866afe5ce7..993fa853f47 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -962,6 +962,7 @@ namespace { value = bestValue; singularLMR = moveCountPruning = false; ttCapture = ttMove && pos.capture_or_promotion(ttMove); + bool formerPv = ttPv && !PvNode; // Mark this node as being searched ThreadHolding th(thisThread, posKey, ss->ply); @@ -1054,8 +1055,8 @@ namespace { && tte->depth() >= depth - 3 && pos.legal(move)) { - Value singularBeta = ttValue - (((ttPv && !PvNode) + 4) * depth) / 2; - Depth singularDepth = (depth - 1 + 3 * (ttPv && !PvNode)) / 2; + Value singularBeta = ttValue - ((formerPv + 4) * depth) / 2; + Depth singularDepth = (depth - 1 + 3 * formerPv) / 2; ss->excludedMove = move; value = search(pos, ss, singularBeta - 1, singularBeta, singularDepth, cutNode); ss->excludedMove = MOVE_NONE; @@ -1143,13 +1144,16 @@ namespace { if (ttPv) r -= 2; + if (moveCountPruning && !formerPv) + r++; + // Decrease reduction if opponent's move count is high (~5 Elo) if ((ss-1)->moveCount > 14) r--; // Decrease reduction if ttMove has been singularly extended (~3 Elo) if (singularLMR) - r -= 1 + (ttPv && !PvNode); + r -= 1 + formerPv; if (!captureOrPromotion) { From 209e94203f8c4d0a48405192d1e71c80f28f3159 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 30 Mar 2020 22:45:35 +0200 Subject: [PATCH 0177/1766] Small cleanups https://github.com/official-stockfish/Stockfish/pull/2584 No functional change. --- src/bitboard.cpp | 28 +++++++++++----------------- src/evaluate.cpp | 26 +++++++++++++------------- src/material.cpp | 2 +- src/movegen.cpp | 4 ++-- src/movepick.cpp | 2 +- src/pawns.cpp | 4 ++-- src/position.cpp | 2 +- src/search.cpp | 4 ++-- src/uci.cpp | 2 +- 9 files changed, 34 insertions(+), 40 deletions(-) diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 70114f20fad..bb03dfebf28 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -78,11 +78,11 @@ void Bitboards::init() { for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) SquareDistance[s1][s2] = std::max(distance(s1, s2), distance(s1, s2)); - for (Square s = SQ_A1; s <= SQ_H8; ++s) - { - PawnAttacks[WHITE][s] = pawn_attacks_bb(square_bb(s)); - PawnAttacks[BLACK][s] = pawn_attacks_bb(square_bb(s)); - } + Direction RookDirections[] = { NORTH, EAST, SOUTH, WEST }; + Direction BishopDirections[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }; + + init_magics(RookTable, RookMagics, RookDirections); + init_magics(BishopTable, BishopMagics, BishopDirections); // Helper returning the target bitboard of a step from a square auto landing_square_bb = [&](Square s, int step) @@ -91,23 +91,17 @@ void Bitboards::init() { return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0); }; - for (Square s = SQ_A1; s <= SQ_H8; ++s) + for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) { + PawnAttacks[WHITE][s1] = pawn_attacks_bb(square_bb(s1)); + PawnAttacks[BLACK][s1] = pawn_attacks_bb(square_bb(s1)); + for (int step : {-9, -8, -7, -1, 1, 7, 8, 9} ) - PseudoAttacks[KING][s] |= landing_square_bb(s, step); + PseudoAttacks[KING][s1] |= landing_square_bb(s1, step); for (int step : {-17, -15, -10, -6, 6, 10, 15, 17} ) - PseudoAttacks[KNIGHT][s] |= landing_square_bb(s, step); - } - - Direction RookDirections[] = { NORTH, EAST, SOUTH, WEST }; - Direction BishopDirections[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }; + PseudoAttacks[KNIGHT][s1] |= landing_square_bb(s1, step); - init_magics(RookTable, RookMagics, RookDirections); - init_magics(BishopTable, BishopMagics, BishopDirections); - - for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) - { PseudoAttacks[QUEEN][s1] = PseudoAttacks[BISHOP][s1] = attacks_bb(s1, 0); PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ ROOK][s1] = attacks_bb< ROOK>(s1, 0); diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d51325f0d99..63541c2affb 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -91,15 +91,15 @@ namespace { // MobilityBonus[PieceType-2][attacked] contains bonuses for middle and end game, // indexed by piece type and number of attacked squares in the mobility area. constexpr Score MobilityBonus[][32] = { - { S(-62,-81), S(-53,-56), S(-12,-30), S( -4,-14), S( 3, 8), S( 13, 15), // Knights + { S(-62,-81), S(-53,-56), S(-12,-30), S( -4,-14), S( 3, 8), S( 13, 15), // Knight S( 22, 23), S( 28, 27), S( 33, 33) }, - { S(-48,-59), S(-20,-23), S( 16, -3), S( 26, 13), S( 38, 24), S( 51, 42), // Bishops + { S(-48,-59), S(-20,-23), S( 16, -3), S( 26, 13), S( 38, 24), S( 51, 42), // Bishop S( 55, 54), S( 63, 57), S( 63, 65), S( 68, 73), S( 81, 78), S( 81, 86), S( 91, 88), S( 98, 97) }, - { S(-58,-76), S(-27,-18), S(-15, 28), S(-10, 55), S( -5, 69), S( -2, 82), // Rooks + { S(-58,-76), S(-27,-18), S(-15, 28), S(-10, 55), S( -5, 69), S( -2, 82), // Rook S( 9,112), S( 16,118), S( 30,132), S( 29,142), S( 32,155), S( 38,165), S( 46,166), S( 48,169), S( 58,171) }, - { S(-39,-36), S(-21,-15), S( 3, 8), S( 3, 18), S( 14, 34), S( 22, 54), // Queens + { S(-39,-36), S(-21,-15), S( 3, 8), S( 3, 18), S( 14, 34), S( 22, 54), // Queen S( 28, 61), S( 41, 73), S( 43, 79), S( 48, 92), S( 56, 94), S( 60,104), S( 60,113), S( 66,120), S( 67,123), S( 70,126), S( 71,133), S( 73,136), S( 79,140), S( 88,143), S( 88,148), S( 99,166), S(102,170), S(102,175), @@ -213,7 +213,7 @@ namespace { template template void Evaluation::initialize() { - constexpr Color Them = (Us == WHITE ? BLACK : WHITE); + constexpr Color Them = ~Us; constexpr Direction Up = pawn_push(Us); constexpr Direction Down = -Up; constexpr Bitboard LowRanks = (Us == WHITE ? Rank2BB | Rank3BB : Rank7BB | Rank6BB); @@ -252,7 +252,7 @@ namespace { template template Score Evaluation::pieces() { - constexpr Color Them = (Us == WHITE ? BLACK : WHITE); + constexpr Color Them = ~Us; constexpr Direction Down = -pawn_push(Us); constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB : Rank5BB | Rank4BB | Rank3BB); @@ -298,12 +298,12 @@ namespace { else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us)) score += Outpost; - // Knight and Bishop bonus for being right behind a pawn + // Bonus for a knight or bishop shielded by pawn if (shift(pos.pieces(PAWN)) & s) score += MinorBehindPawn; // Penalty if the piece is far from the king - score -= KingProtector * distance(s, pos.square(Us)); + score -= KingProtector * distance(pos.square(Us), s); if (Pt == BISHOP) { @@ -313,7 +313,7 @@ namespace { Bitboard blocked = pos.pieces(Us, PAWN) & shift(pos.pieces()); score -= BishopPawns * pos.pawns_on_same_color_squares(Us, s) - * (!bool(attackedBy[Us][PAWN] & s) + popcount(blocked & CenterFiles)); + * (!(attackedBy[Us][PAWN] & s) + popcount(blocked & CenterFiles)); // Bonus for bishop on a long diagonal which can "see" both center squares if (more_than_one(attacks_bb(s, pos.pieces(PAWN)) & Center)) @@ -372,7 +372,7 @@ namespace { template template Score Evaluation::king() const { - constexpr Color Them = (Us == WHITE ? BLACK : WHITE); + constexpr Color Them = ~Us; constexpr Bitboard Camp = (Us == WHITE ? AllSquares ^ Rank6BB ^ Rank7BB ^ Rank8BB : AllSquares ^ Rank1BB ^ Rank2BB ^ Rank3BB); @@ -480,7 +480,7 @@ namespace { template template Score Evaluation::threats() const { - constexpr Color Them = (Us == WHITE ? BLACK : WHITE); + constexpr Color Them = ~Us; constexpr Direction Up = pawn_push(Us); constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); @@ -576,7 +576,7 @@ namespace { template template Score Evaluation::passed() const { - constexpr Color Them = (Us == WHITE ? BLACK : WHITE); + constexpr Color Them = ~Us; constexpr Direction Up = pawn_push(Us); auto king_proximity = [&](Color c, Square s) { @@ -667,7 +667,7 @@ namespace { if (pos.non_pawn_material() < SpaceThreshold) return SCORE_ZERO; - constexpr Color Them = (Us == WHITE ? BLACK : WHITE); + constexpr Color Them = ~Us; constexpr Direction Down = -pawn_push(Us); constexpr Bitboard SpaceMask = Us == WHITE ? CenterFiles & (Rank2BB | Rank3BB | Rank4BB) diff --git a/src/material.cpp b/src/material.cpp index 7e212461098..93699f5f60b 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -84,7 +84,7 @@ namespace { template int imbalance(const int pieceCount[][PIECE_TYPE_NB]) { - constexpr Color Them = (Us == WHITE ? BLACK : WHITE); + constexpr Color Them = ~Us; int bonus = 0; diff --git a/src/movegen.cpp b/src/movegen.cpp index 9964ad343b8..804ef87b85b 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -52,7 +52,7 @@ namespace { template ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) { - constexpr Color Them = (Us == WHITE ? BLACK : WHITE); + constexpr Color Them = ~Us; constexpr Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); constexpr Direction Up = pawn_push(Us); @@ -319,7 +319,7 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { while (sliders) { Square checksq = pop_lsb(&sliders); - sliderAttacks |= LineBB[checksq][ksq] ^ checksq; + sliderAttacks |= LineBB[ksq][checksq] ^ checksq; } // Generate evasions for king, capture and non capture moves diff --git a/src/movepick.cpp b/src/movepick.cpp index 575c902228b..580c6d75c75 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -59,7 +59,7 @@ namespace { MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const LowPlyHistory* lp, const CapturePieceToHistory* cph, const PieceToHistory** ch, Move cm, Move* killers, int pl) : pos(p), mainHistory(mh), lowPlyHistory(lp), captureHistory(cph), continuationHistory(ch), - refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d) , ply(pl) { + refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d), ply(pl) { assert(d > 0); diff --git a/src/pawns.cpp b/src/pawns.cpp index 3023021d408..8759631ac6c 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -68,7 +68,7 @@ namespace { template Score evaluate(const Position& pos, Pawns::Entry* e) { - constexpr Color Them = (Us == WHITE ? BLACK : WHITE); + constexpr Color Them = ~Us; constexpr Direction Up = pawn_push(Us); Bitboard neighbours, stoppers, support, phalanx, opposed; @@ -187,7 +187,7 @@ Entry* probe(const Position& pos) { template Score Entry::evaluate_shelter(const Position& pos, Square ksq) { - constexpr Color Them = (Us == WHITE ? BLACK : WHITE); + constexpr Color Them = ~Us; Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq); Bitboard ourPawns = b & pos.pieces(Us); diff --git a/src/position.cpp b/src/position.cpp index fefce56e2aa..6bbb7914ae8 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -666,7 +666,7 @@ bool Position::gives_check(Move m) const { case CASTLING: { Square kfrom = from; - Square rfrom = to; // Castling is encoded as 'King captures the rook' + Square rfrom = to; // Castling is encoded as 'king captures the rook' Square kto = relative_square(sideToMove, rfrom > kfrom ? SQ_G1 : SQ_C1); Square rto = relative_square(sideToMove, rfrom > kfrom ? SQ_F1 : SQ_D1); diff --git a/src/search.cpp b/src/search.cpp index 993fa853f47..38d3204c844 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -292,7 +292,7 @@ void MainThread::search() { if (bestThread->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY) { - // Make sure we pick the shortest mate + // Make sure we pick the shortest mate / TB conversion if (th->rootMoves[0].score > bestThread->rootMoves[0].score) bestThread = th; } @@ -867,7 +867,7 @@ namespace { if (nullValue >= beta) { - // Do not return unproven mate scores + // Do not return unproven mate or TB scores if (nullValue >= VALUE_TB_WIN_IN_MAX_PLY) nullValue = beta; diff --git a/src/uci.cpp b/src/uci.cpp index 33577a4eca3..11d5adc644e 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -115,7 +115,7 @@ namespace { limits.startTime = now(); // As early as possible! while (is >> token) - if (token == "searchmoves") + if (token == "searchmoves") // Needs to be the last command on the line while (is >> token) limits.searchmoves.push_back(UCI::to_move(pos, token)); From 0b8ce4b3037c0efcb2c8bf10598ec3f4fd919e1a Mon Sep 17 00:00:00 2001 From: protonspring Date: Wed, 25 Mar 2020 19:57:36 -0600 Subject: [PATCH 0178/1766] Limit array access in Position This is a non-functional code style change that routes all position array accesses to single methods, and adds an assert to check correctness. Passed STC LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 37312 W: 7378 L: 7246 D: 22688 Ptnml(0-2): 606, 4280, 8762, 4392, 616 https://tests.stockfishchess.org/tests/view/5e7c0c69e42a5c3b3ca2eb3d closes https://github.com/official-stockfish/Stockfish/pull/2595 No functional change. --- src/position.h | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/src/position.h b/src/position.h index e5071d537a7..f79c5463a7a 100644 --- a/src/position.h +++ b/src/position.h @@ -83,7 +83,6 @@ class Position { const std::string fen() const; // Position representation - Bitboard pieces() const; Bitboard pieces(PieceType pt) const; Bitboard pieces(PieceType pt1, PieceType pt2) const; Bitboard pieces(Color c) const; @@ -207,28 +206,25 @@ inline Color Position::side_to_move() const { return sideToMove; } -inline bool Position::empty(Square s) const { - return board[s] == NO_PIECE; -} - inline Piece Position::piece_on(Square s) const { + assert(is_ok(s)); return board[s]; } -inline Piece Position::moved_piece(Move m) const { - return board[from_sq(m)]; +inline bool Position::empty(Square s) const { + return piece_on(s) == NO_PIECE; } -inline Bitboard Position::pieces() const { - return byTypeBB[ALL_PIECES]; +inline Piece Position::moved_piece(Move m) const { + return piece_on(from_sq(m)); } -inline Bitboard Position::pieces(PieceType pt) const { +inline Bitboard Position::pieces(PieceType pt = ALL_PIECES) const { return byTypeBB[pt]; } inline Bitboard Position::pieces(PieceType pt1, PieceType pt2) const { - return byTypeBB[pt1] | byTypeBB[pt2]; + return pieces(pt1) | pieces(pt2); } inline Bitboard Position::pieces(Color c) const { @@ -236,11 +232,11 @@ inline Bitboard Position::pieces(Color c) const { } inline Bitboard Position::pieces(Color c, PieceType pt) const { - return byColorBB[c] & byTypeBB[pt]; + return pieces(c) & pieces(pt); } inline Bitboard Position::pieces(Color c, PieceType pt1, PieceType pt2) const { - return byColorBB[c] & (byTypeBB[pt1] | byTypeBB[pt2]); + return pieces(c) & (pieces(pt1) | pieces(pt2)); } template inline int Position::count(Color c) const { @@ -248,7 +244,7 @@ template inline int Position::count(Color c) const { } template inline int Position::count() const { - return pieceCount[make_piece(WHITE, Pt)] + pieceCount[make_piece(BLACK, Pt)]; + return count(WHITE) + count(BLACK); } template inline const Square* Position::squares(Color c) const { @@ -257,7 +253,7 @@ template inline const Square* Position::squares(Color c) const { template inline Square Position::square(Color c) const { assert(pieceCount[make_piece(c, Pt)] == 1); - return pieceList[make_piece(c, Pt)][0]; + return squares(c)[0]; } inline Square Position::ep_square() const { @@ -279,7 +275,7 @@ inline int Position::castling_rights(Color c) const { inline bool Position::castling_impeded(CastlingRights cr) const { assert(cr == WHITE_OO || cr == WHITE_OOO || cr == BLACK_OO || cr == BLACK_OOO); - return byTypeBB[ALL_PIECES] & castlingPath[cr]; + return pieces() & castlingPath[cr]; } inline Square Position::castling_rook_square(CastlingRights cr) const { @@ -292,7 +288,7 @@ template inline Bitboard Position::attacks_from(Square s) const { static_assert(Pt != PAWN, "Pawn attacks need color"); - return Pt == BISHOP || Pt == ROOK ? attacks_bb(s, byTypeBB[ALL_PIECES]) + return Pt == BISHOP || Pt == ROOK ? attacks_bb(s, pieces()) : Pt == QUEEN ? attacks_from(s) | attacks_from(s) : PseudoAttacks[Pt][s]; } @@ -303,11 +299,11 @@ inline Bitboard Position::attacks_from(Square s, Color c) const { } inline Bitboard Position::attacks_from(PieceType pt, Square s) const { - return attacks_bb(pt, s, byTypeBB[ALL_PIECES]); + return attacks_bb(pt, s, pieces()); } inline Bitboard Position::attackers_to(Square s) const { - return attackers_to(s, byTypeBB[ALL_PIECES]); + return attackers_to(s, pieces()); } inline Bitboard Position::checkers() const { @@ -360,7 +356,7 @@ inline Value Position::non_pawn_material(Color c) const { } inline Value Position::non_pawn_material() const { - return st->nonPawnMaterial[WHITE] + st->nonPawnMaterial[BLACK]; + return non_pawn_material(WHITE) + non_pawn_material(BLACK); } inline int Position::game_ply() const { @@ -372,8 +368,8 @@ inline int Position::rule50_count() const { } inline bool Position::opposite_bishops() const { - return pieceCount[W_BISHOP] == 1 - && pieceCount[B_BISHOP] == 1 + return count(WHITE) == 1 + && count(BLACK) == 1 && opposite_colors(square(WHITE), square(BLACK)); } From 84f3bf594d323ac4fcaac0b6681edcdf2f9da70f Mon Sep 17 00:00:00 2001 From: mstembera Date: Sun, 29 Mar 2020 14:09:19 -0700 Subject: [PATCH 0179/1766] No voting for TB loss / mate. Just as we pick the shortest mate also make sure we stave off mate as long as possible. https://github.com/official-stockfish/Stockfish/pull/2603 bench: 5138771 --- src/search.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 38d3204c844..ad5b364d41d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -280,7 +280,7 @@ void MainThread::search() { std::map votes; Value minScore = this->rootMoves[0].score; - // Find out minimum score + // Find minimum score for (Thread* th: Threads) minScore = std::min(minScore, th->rootMoves[0].score); @@ -290,14 +290,15 @@ void MainThread::search() { votes[th->rootMoves[0].pv[0]] += (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth); - if (bestThread->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY) + if (abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY) { - // Make sure we pick the shortest mate / TB conversion + // Make sure we pick the shortest mate / TB conversion or stave off mate the longest if (th->rootMoves[0].score > bestThread->rootMoves[0].score) bestThread = th; } else if ( th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY - || votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]]) + || ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY + && votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]])) bestThread = th; } } From 375e4eeaf5e739c176c38ff05ae954bb60a98987 Mon Sep 17 00:00:00 2001 From: 31m059 <37052095+31m059@users.noreply.github.com> Date: Mon, 30 Mar 2020 21:53:02 -0400 Subject: [PATCH 0180/1766] Simplify a candidate passer condition. STC: LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 31528 W: 6208 L: 6061 D: 19259 Ptnml(0-2): 541, 3673, 7205, 3788, 557 https://tests.stockfishchess.org/tests/view/5e825db0e42a5c3b3ca2ee21 LTC: LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 38546 W: 5083 L: 5009 D: 28454 Ptnml(0-2): 299, 3628, 11362, 3668, 316 https://tests.stockfishchess.org/tests/view/5e826ec7e42a5c3b3ca2ee2a closes https://github.com/official-stockfish/Stockfish/pull/2607 Bench: 5139561 --- src/evaluate.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 63541c2affb..57491f343f9 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -639,9 +639,8 @@ namespace { } // r > RANK_3 // Scale down bonus for candidate passers which need more than one - // pawn push to become passed, or have a pawn in front of them. - if ( !pos.pawn_passed(Us, s + Up) - || (pos.pieces(PAWN) & (s + Up))) + // pawn push to become passed. + if (!pos.pawn_passed(Us, s + Up)) bonus = bonus / 2; score += bonus - PassedFile * edge_distance(file_of(s)); From c14f4877cf8067e0913a6db4ab05fef9a853c1d0 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Thu, 2 Apr 2020 06:33:53 +0300 Subject: [PATCH 0181/1766] Increase reduction for captures. The idea behind this patch is that if static eval is really bad so capturing of current piece on spot will still produce a position with an eval much lower than alpha then our best chance is to create some kind of king attack. So captures without check are mostly worse than captures with check and can be reduced more. passed STC https://tests.stockfishchess.org/tests/view/5e8514b44411759d9d098543 LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 46196 W: 9039 L: 8781 D: 28376 Ptnml(0-2): 750, 5412, 10628, 5446, 862 passed LTC https://tests.stockfishchess.org/tests/view/5e8530134411759d9d09854c LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 23462 W: 3228 L: 2988 D: 17246 Ptnml(0-2): 186, 2125, 6849, 2405, 166 close https://github.com/official-stockfish/Stockfish/pull/2612 bench 4742598 --- src/search.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ad5b364d41d..eb30d9fac8c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1189,10 +1189,17 @@ namespace { // Decrease/increase reduction for moves with a good/bad history (~30 Elo) r -= ss->statScore / 16434; } - - // Increase reduction for captures/promotions if late move and at low depth - else if (depth < 8 && moveCount > 2) - r++; + else + { + // Increase reduction for captures/promotions if late move and at low depth + if (depth < 8 && moveCount > 2) + r++; + + // Unless giving check, this capture is likely bad + if ( !givesCheck + && ss->staticEval + PieceValue[EG][pos.captured_piece()] + 200 * depth <= alpha) + r++; + } Depth d = Utility::clamp(newDepth - r, 1, newDepth); From 3cb1c6c3c6206c3c2a0d78ce1cb9820256efc96e Mon Sep 17 00:00:00 2001 From: protonspring Date: Tue, 31 Mar 2020 15:08:55 -0600 Subject: [PATCH 0182/1766] remove KNPK endgame code In more than 100k local KNPK games, there is no discernible difference between master and master with this endgame removed: master:42971, patch:42973, draws: 3969. Removal does not seem to regress in normal games. STC LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 46390 W: 8998 L: 8884 D: 28508 Ptnml(0-2): 707, 5274, 11163, 5300, 751 https://tests.stockfishchess.org/tests/view/5e83b18ee42a5c3b3ca2ef02 LTC LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 44768 W: 5863 L: 5814 D: 33091 Ptnml(0-2): 251, 3918, 14028, 3905, 282 https://tests.stockfishchess.org/tests/view/5e84a82a4411759d9d0984f4 In tests with a book of endgames that can convert into KNPK, no significant difference can be seen either ``` TC 1.0+0.01 Score of patch vs master: 6131 - 6188 - 27681 [0.499] 40000 Elo difference: -0.5 +/- 1.9, LOS: 30.4 %, DrawRatio: 69.2 % TC 2.0+0.02 Score of patch vs master: 5740 - 5741 - 28519 [0.500] 40000 Elo difference: -0.0 +/- 1.8, LOS: 49.6 %, DrawRatio: 71.3 % `` closes https://github.com/official-stockfish/Stockfish/pull/2611 Bench 4512059 --- src/endgame.cpp | 20 -------------------- src/endgame.h | 1 - 2 files changed, 21 deletions(-) diff --git a/src/endgame.cpp b/src/endgame.cpp index 1a5959e5fea..e232da6243a 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -79,7 +79,6 @@ namespace Endgames { add("KQKR"); add("KNNKP"); - add("KNPK"); add("KRPKR"); add("KRPKB"); add("KBPKB"); @@ -713,25 +712,6 @@ ScaleFactor Endgame::operator()(const Position& pos) const { } -/// KNP vs K. There is a single rule: if the pawn is a rook pawn on the 7th rank -/// and the defending king prevents the pawn from advancing, the position is drawn. -template<> -ScaleFactor Endgame::operator()(const Position& pos) const { - - assert(verify_material(pos, strongSide, KnightValueMg, 1)); - assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); - - // Assume strongSide is white and the pawn is on files A-D - Square pawnSq = normalize(pos, strongSide, pos.square(strongSide)); - Square weakKingSq = normalize(pos, strongSide, pos.square(weakSide)); - - if (pawnSq == SQ_A7 && distance(SQ_A8, weakKingSq) <= 1) - return SCALE_FACTOR_DRAW; - - return SCALE_FACTOR_NONE; -} - - /// KP vs KP. This is done by removing the weakest side's pawn and probing the /// KP vs K bitbase: If the weakest side has a draw without the pawn, it probably /// has at least a draw with the pawn as well. The exception is when the stronger diff --git a/src/endgame.h b/src/endgame.h index 49ebb603a1f..fd1aba2dfbc 100644 --- a/src/endgame.h +++ b/src/endgame.h @@ -57,7 +57,6 @@ enum EndgameCode { KBPKB, // KBP vs KB KBPPKB, // KBPP vs KB KBPKN, // KBP vs KN - KNPK, // KNP vs K KPKP // KP vs KP }; From fbc7a328c67092799547f93e684323e2c1a6226e Mon Sep 17 00:00:00 2001 From: 31m059 <37052095+31m059@users.noreply.github.com> Date: Thu, 2 Apr 2020 23:57:15 -0400 Subject: [PATCH 0183/1766] Retire candidate passed pawns Before this commit, some pawns were considered "candidate" passed pawns and given half bonus. After this commit, all of these pawns are scored as passed pawns, and they do not receive less bonus. STC: LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 21806 W: 4320 L: 4158 D: 13328 Ptnml(0-2): 367, 2526, 5001, 2596, 413 https://tests.stockfishchess.org/tests/view/5e86b4724411759d9d098639 LTC: LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 12590 W: 1734 L: 1617 D: 9239 Ptnml(0-2): 96, 1187, 3645, 1238, 129 https://tests.stockfishchess.org/tests/view/5e86d2874411759d9d098640 This PR and commit are dedicated to our colleague Stefan Geschwentner (@locutus2), one of the most respected and accomplished members of the Stockfish developer community. Stockfish is a volunteer project and has always thrived because of Stefan's talent, insight, generosity, and dedication. Welcome back, Stefan! closes https://github.com/official-stockfish/Stockfish/pull/2613 Bench: 4831963 --- src/evaluate.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 57491f343f9..f4a5d4867b0 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -638,11 +638,6 @@ namespace { } } // r > RANK_3 - // Scale down bonus for candidate passers which need more than one - // pawn push to become passed. - if (!pos.pawn_passed(Us, s + Up)) - bonus = bonus / 2; - score += bonus - PassedFile * edge_distance(file_of(s)); } From 85bcf4741e271d9b205ac335f7056ec65a2a6ab7 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sat, 4 Apr 2020 18:06:13 +0300 Subject: [PATCH 0184/1766] Further increase reductions with increasing number of threads This patch doubles the reduction increase with thread count. passed STC https://tests.stockfishchess.org/tests/view/5e874f5a4411759d9d098696 LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 9162 W: 1558 L: 1385 D: 6219 Ptnml(0-2): 90, 958, 2343, 1069, 121 passed LTC https://tests.stockfishchess.org/tests/view/5e8762804411759d9d09869f LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 79364 W: 9541 L: 9159 D: 60664 Ptnml(0-2): 462, 6880, 24661, 7172, 507 closes https://github.com/official-stockfish/Stockfish/pull/2615 bench 4831963 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index eb30d9fac8c..cfda94b8080 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -194,7 +194,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int((24.8 + std::log(Threads.size()) / 2) * std::log(i)); + Reductions[i] = int((24.8 + std::log(Threads.size())) * std::log(i)); } From 195a4fec6d6bd1f9e43f5b3e83a3dcf57dc73744 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Tue, 7 Apr 2020 16:53:24 +0300 Subject: [PATCH 0185/1766] Introduce capture history pruning This patch introduces a heuristic that is similar to countermove based pruning but for captures - capture history pruning. The idea is that we can (almost) safely prune really late captures with negative history if they don't give check so will most likely not produce some king-attacking tactic. passed STC https://tests.stockfishchess.org/tests/view/5e8c60d40ffd2be7f15e5470 LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 23748 W: 4758 L: 4529 D: 14461 Ptnml(0-2): 421, 2712, 5400, 2899, 442 passed LTC https://tests.stockfishchess.org/tests/view/5e8c72bf0ffd2be7f15e547f LLR: 2.96 (-2.94,2.94) {0.25,1.75} Total: 17330 W: 2415 L: 2190 D: 12725 Ptnml(0-2): 126, 1561, 5107, 1704, 167 closes https://github.com/official-stockfish/Stockfish/pull/2618 bench 4417023 --- src/search.cpp | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index cfda94b8080..dba8857efda 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -789,6 +789,8 @@ namespace { } } + CapturePieceToHistory& captureHistory = thisThread->captureHistory; + // Step 6. Static evaluation of the position if (inCheck) { @@ -899,7 +901,7 @@ namespace { && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) { Value raisedBeta = std::min(beta + 189 - 45 * improving, VALUE_INFINITE); - MovePicker mp(pos, ttMove, raisedBeta - ss->staticEval, &thisThread->captureHistory); + MovePicker mp(pos, ttMove, raisedBeta - ss->staticEval, &captureHistory); int probCutCount = 0; while ( (move = mp.next_move()) != MOVE_NONE @@ -954,7 +956,7 @@ namespace { MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->lowPlyHistory, - &thisThread->captureHistory, + &captureHistory, contHist, countermove, ss->killers, @@ -1010,12 +1012,12 @@ namespace { // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold moveCountPruning = moveCount >= futility_move_count(improving, depth); + // Reduced depth of the next LMR search + int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), 0); + if ( !captureOrPromotion && !givesCheck) { - // Reduced depth of the next LMR search - 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) && (*contHist[0])[movedPiece][to_sq(move)] < CounterMovePruneThreshold @@ -1035,8 +1037,16 @@ namespace { if (!pos.see_ge(move, Value(-(32 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth))) continue; } - else if (!pos.see_ge(move, Value(-194) * depth)) // (~25 Elo) - continue; + else + { + if ( !givesCheck + && lmrDepth < 1 + && captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] < 0) + continue; + + if (!pos.see_ge(move, Value(-194) * depth)) // (~25 Elo) + continue; + } } // Step 14. Extensions (~75 Elo) From f83cb95740de019db6ff5567d6f84f218b18cd9e Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 12 Apr 2020 20:30:08 +0200 Subject: [PATCH 0186/1766] Small cleanups closes https://github.com/official-stockfish/Stockfish/pull/2606 No functional change --- src/bitboard.cpp | 29 +++++++++-------------------- src/bitboard.h | 20 ++++++++++++++------ src/evaluate.cpp | 2 +- src/movegen.cpp | 10 ++-------- src/pawns.cpp | 2 +- src/psqt.cpp | 2 +- src/search.cpp | 25 ++++++++++++++----------- src/syzygy/tbprobe.cpp | 2 +- src/thread.cpp | 2 +- src/thread.h | 2 +- src/types.h | 6 +++--- 11 files changed, 48 insertions(+), 54 deletions(-) diff --git a/src/bitboard.cpp b/src/bitboard.cpp index bb03dfebf28..69bbc77bf30 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -84,23 +84,16 @@ void Bitboards::init() { init_magics(RookTable, RookMagics, RookDirections); init_magics(BishopTable, BishopMagics, BishopDirections); - // Helper returning the target bitboard of a step from a square - auto landing_square_bb = [&](Square s, int step) - { - Square to = Square(s + step); - return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0); - }; - for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) { PawnAttacks[WHITE][s1] = pawn_attacks_bb(square_bb(s1)); PawnAttacks[BLACK][s1] = pawn_attacks_bb(square_bb(s1)); for (int step : {-9, -8, -7, -1, 1, 7, 8, 9} ) - PseudoAttacks[KING][s1] |= landing_square_bb(s1, step); + PseudoAttacks[KING][s1] |= safe_destination(s1, step); for (int step : {-17, -15, -10, -6, 6, 10, 15, 17} ) - PseudoAttacks[KNIGHT][s1] |= landing_square_bb(s1, step); + PseudoAttacks[KNIGHT][s1] |= safe_destination(s1, step); PseudoAttacks[QUEEN][s1] = PseudoAttacks[BISHOP][s1] = attacks_bb(s1, 0); PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ ROOK][s1] = attacks_bb< ROOK>(s1, 0); @@ -117,20 +110,16 @@ namespace { Bitboard sliding_attack(Direction directions[], Square sq, Bitboard occupied) { - Bitboard attack = 0; + Bitboard attacks = 0; for (int i = 0; i < 4; ++i) - for (Square s = sq + directions[i]; - is_ok(s) && distance(s, s - directions[i]) == 1; - s += directions[i]) - { - attack |= s; - - if (occupied & s) - break; - } + { + Square s = sq; + while(safe_destination(s, directions[i]) && !(occupied & s)) + attacks |= (s += directions[i]); + } - return attack; + return attacks; } diff --git a/src/bitboard.h b/src/bitboard.h index 529e3dfe5f4..9252c3dc914 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -106,7 +106,7 @@ extern Magic RookMagics[SQUARE_NB]; extern Magic BishopMagics[SQUARE_NB]; inline Bitboard square_bb(Square s) { - assert(s >= SQ_A1 && s <= SQ_H8); + assert(is_ok(s)); return SquareBB[s]; } @@ -123,7 +123,7 @@ inline Bitboard operator&(Square s, Bitboard b) { return b & s; } inline Bitboard operator|(Square s, Bitboard b) { return b | s; } inline Bitboard operator^(Square s, Bitboard b) { return b ^ s; } -inline Bitboard operator|(Square s, Square s2) { return square_bb(s) | square_bb(s2); } +inline Bitboard operator|(Square s, Square s2) { return square_bb(s) | s2; } constexpr bool more_than_one(Bitboard b) { return b & (b - 1); @@ -209,8 +209,8 @@ inline Bitboard between_bb(Square s1, Square s2) { /// forward_ranks_bb(BLACK, SQ_D3) will return the 16 squares on ranks 1 and 2. inline Bitboard forward_ranks_bb(Color c, Square s) { - return c == WHITE ? ~Rank1BB << 8 * (rank_of(s) - RANK_1) - : ~Rank8BB >> 8 * (RANK_8 - rank_of(s)); + return c == WHITE ? ~Rank1BB << 8 * relative_rank(WHITE, s) + : ~Rank8BB >> 8 * relative_rank(BLACK, s); } @@ -255,8 +255,16 @@ template<> inline int distance(Square x, Square y) { return std::abs(file_ template<> inline int distance(Square x, Square y) { return std::abs(rank_of(x) - rank_of(y)); } template<> inline int distance(Square x, Square y) { return SquareDistance[x][y]; } -inline File edge_distance(File f) { return std::min(f, File(FILE_H - f)); } -inline Rank edge_distance(Rank r) { return std::min(r, Rank(RANK_8 - r)); } +inline int edge_distance(File f) { return std::min(f, File(FILE_H - f)); } +inline int edge_distance(Rank r) { return std::min(r, Rank(RANK_8 - r)); } + +/// Return the target square bitboard if we do not step off the board, empty otherwise + +inline Bitboard safe_destination(Square s, int step) +{ + Square to = Square(s + step); + return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0); +} /// attacks_bb() returns a bitboard representing all the squares attacked by a /// piece of type Pt (bishop or rook) placed on 's'. diff --git a/src/evaluate.cpp b/src/evaluate.cpp index f4a5d4867b0..a2e5ef7b571 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -746,7 +746,7 @@ namespace { { if ( pos.opposite_bishops() && pos.non_pawn_material() == 2 * BishopValueMg) - sf = 22 ; + sf = 22; else sf = std::min(sf, 36 + (pos.opposite_bishops() ? 2 : 7) * pos.count(strongSide)); diff --git a/src/movegen.cpp b/src/movegen.cpp index 804ef87b85b..a3abcde8d1e 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -277,16 +277,13 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { assert(!pos.checkers()); Color us = pos.side_to_move(); - Bitboard dc = pos.blockers_for_king(~us) & pos.pieces(us); + Bitboard dc = pos.blockers_for_king(~us) & pos.pieces(us) & ~pos.pieces(PAWN); while (dc) { Square from = pop_lsb(&dc); PieceType pt = type_of(pos.piece_on(from)); - if (pt == PAWN) - continue; // Will be generated together with direct checks - Bitboard b = pos.attacks_from(pt, from) & ~pos.pieces(); if (pt == KING) @@ -317,10 +314,7 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { // the king evasions in order to skip known illegal moves, which avoids any // useless legality checks later on. while (sliders) - { - Square checksq = pop_lsb(&sliders); - sliderAttacks |= LineBB[ksq][checksq] ^ checksq; - } + sliderAttacks |= LineBB[ksq][pop_lsb(&sliders)] & ~pos.checkers(); // Generate evasions for king, capture and non capture moves Bitboard b = pos.attacks_from(ksq) & ~pos.pieces(us) & ~sliderAttacks; diff --git a/src/pawns.cpp b/src/pawns.cpp index 8759631ac6c..0017b8044d8 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -204,7 +204,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; - File d = edge_distance(f); + File d = File(edge_distance(f)); bonus += make_score(ShelterStrength[d][ourRank], 0); if (ourRank && (ourRank == theirRank - 1)) diff --git a/src/psqt.cpp b/src/psqt.cpp index d86e98e4ee3..7fa36ac83dd 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -112,7 +112,7 @@ void init() { for (Square s = SQ_A1; s <= SQ_H8; ++s) { - File f = edge_distance(file_of(s)); + File f = File(edge_distance(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][flip_rank(s)] = -psq[pc][s]; diff --git a/src/search.cpp b/src/search.cpp index dba8857efda..4abc60692e6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -303,7 +303,7 @@ void MainThread::search() { } } - previousScore = bestThread->rootMoves[0].score; + bestPreviousScore = bestThread->rootMoves[0].score; // Send again PV info if we have a new best thread if (bestThread != this) @@ -349,12 +349,12 @@ void Thread::search() { if (mainThread) { - if (mainThread->previousScore == VALUE_INFINITE) + if (mainThread->bestPreviousScore == VALUE_INFINITE) for (int i=0; i<4; ++i) mainThread->iterValue[i] = VALUE_ZERO; else for (int i=0; i<4; ++i) - mainThread->iterValue[i] = mainThread->previousScore; + mainThread->iterValue[i] = mainThread->bestPreviousScore; } size_t multiPV = Options["MultiPV"]; @@ -433,13 +433,13 @@ void Thread::search() { // Reset aspiration window starting size if (rootDepth >= 4) { - Value previousScore = rootMoves[pvIdx].previousScore; + Value prev = rootMoves[pvIdx].previousScore; delta = Value(21); - alpha = std::max(previousScore - delta,-VALUE_INFINITE); - beta = std::min(previousScore + delta, VALUE_INFINITE); + alpha = std::max(prev - delta,-VALUE_INFINITE); + beta = std::min(prev + delta, VALUE_INFINITE); // Adjust contempt based on root move's previousScore (dynamic contempt) - int dct = ct + (102 - ct / 2) * previousScore / (abs(previousScore) + 157); + int dct = ct + (102 - ct / 2) * prev / (abs(prev) + 157); contempt = (us == WHITE ? make_score(dct, dct / 2) : -make_score(dct, dct / 2)); @@ -537,7 +537,7 @@ void Thread::search() { && !Threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (332 + 6 * (mainThread->previousScore - bestValue) + double fallingEval = (332 + 6 * (mainThread->bestPreviousScore - bestValue) + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 704.0; fallingEval = Utility::clamp(fallingEval, 0.5, 1.5); @@ -626,7 +626,7 @@ namespace { Move ttMove, move, excludedMove, bestMove; Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue; - bool ttHit, ttPv, inCheck, givesCheck, improving, didLMR, priorCapture; + bool ttHit, ttPv, formerPv, inCheck, givesCheck, improving, didLMR, priorCapture; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture, singularLMR; Piece movedPiece; int moveCount, captureCount, quietCount; @@ -696,6 +696,7 @@ namespace { ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] : ttHit ? tte->move() : MOVE_NONE; ttPv = PvNode || (ttHit && tte->is_pv()); + formerPv = ttPv && !PvNode; if (ttPv && depth > 12 && ss->ply - 1 < MAX_LPH && !pos.captured_piece() && is_ok((ss-1)->currentMove)) thisThread->lowPlyHistory[ss->ply - 1][from_to((ss-1)->currentMove)] << stat_bonus(depth - 5); @@ -900,7 +901,8 @@ namespace { && depth >= 5 && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) { - Value raisedBeta = std::min(beta + 189 - 45 * improving, VALUE_INFINITE); + Value raisedBeta = beta + 189 - 45 * improving; + assert(raisedBeta < VALUE_INFINITE); MovePicker mp(pos, ttMove, raisedBeta - ss->staticEval, &captureHistory); int probCutCount = 0; @@ -965,7 +967,6 @@ namespace { value = bestValue; singularLMR = moveCountPruning = false; ttCapture = ttMove && pos.capture_or_promotion(ttMove); - bool formerPv = ttPv && !PvNode; // Mark this node as being searched ThreadHolding th(thisThread, posKey, ss->ply); @@ -1039,11 +1040,13 @@ namespace { } else { + // Capture history based pruning when the move doesn't give check if ( !givesCheck && lmrDepth < 1 && captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] < 0) continue; + // See based pruning if (!pos.see_ge(move, Value(-194) * depth)) // (~25 Elo) continue; } diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 34e4331da1c..f1fd695c34a 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -705,7 +705,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu std::swap(squares[0], *std::max_element(squares, squares + leadPawnsCnt, pawns_comp)); - tbFile = edge_distance(file_of(squares[0])); + tbFile = File(edge_distance(file_of(squares[0]))); } // DTZ tables are one-sided, i.e. they store positions only for white to diff --git a/src/thread.cpp b/src/thread.cpp index b5cb87d9356..88331f0674e 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -166,7 +166,7 @@ void ThreadPool::clear() { th->clear(); main()->callsCnt = 0; - main()->previousScore = VALUE_INFINITE; + main()->bestPreviousScore = VALUE_INFINITE; main()->previousTimeReduction = 1.0; } diff --git a/src/thread.h b/src/thread.h index 41d2b8f6d27..79be197bf27 100644 --- a/src/thread.h +++ b/src/thread.h @@ -88,7 +88,7 @@ struct MainThread : public Thread { void check_time(); double previousTimeReduction; - Value previousScore; + Value bestPreviousScore; Value iterValue[4]; int callsCnt; bool stopOnPonderhit; diff --git a/src/types.h b/src/types.h index bfcd3f23f11..cd8d23205b5 100644 --- a/src/types.h +++ b/src/types.h @@ -177,9 +177,9 @@ enum Value : int { VALUE_NONE = 32002, VALUE_TB_WIN_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY, - VALUE_TB_LOSS_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY, + VALUE_TB_LOSS_IN_MAX_PLY = -VALUE_TB_WIN_IN_MAX_PLY, VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY, - VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + MAX_PLY, + VALUE_MATED_IN_MAX_PLY = -VALUE_MATE_IN_MAX_PLY, PawnValueMg = 128, PawnValueEg = 213, KnightValueMg = 781, KnightValueEg = 854, @@ -351,7 +351,7 @@ inline Score operator*(Score s, int i) { /// Multiplication of a Score by a boolean inline Score operator*(Score s, bool b) { - return Score(int(s) * int(b)); + return b ? s : SCORE_ZERO; } constexpr Color operator~(Color c) { From 6596f0eac0c1d25a12bfd923907bfc78beedbc90 Mon Sep 17 00:00:00 2001 From: protonspring Date: Fri, 3 Apr 2020 15:10:50 -0600 Subject: [PATCH 0187/1766] Always remember the ttMove In master, if the received ttMove meets the prescribed conditions in the various MovePicker constructors, it is returned as the first move, otherwise we set it to MOVE_NONE. If set to MOVE_NONE, we no longer track what the ttMove was, and it will might be returned later in a list of generated moves. This may be a waste. With this patch, if the ttMove fails to meet the prescribed conditions, we simply skip the TT stages, but still store the move and make sure it's never returned. STC LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 66424 W: 12903 L: 12806 D: 40715 Ptnml(0-2): 1195, 7730, 15230, 7897, 1160 LTC LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 45682 W: 5989 L: 5926 D: 33767 Ptnml(0-2): 329, 4361, 13443, 4334, 374 closes https://github.com/official-stockfish/Stockfish/pull/2616 Bench 4928928 --- src/movepick.cpp | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 580c6d75c75..b1e10587cf4 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -59,42 +59,36 @@ namespace { MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const LowPlyHistory* lp, const CapturePieceToHistory* cph, const PieceToHistory** ch, Move cm, Move* killers, int pl) : pos(p), mainHistory(mh), lowPlyHistory(lp), captureHistory(cph), continuationHistory(ch), - refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d), ply(pl) { + ttMove(ttm), refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d), ply(pl) { assert(d > 0); - stage = pos.checkers() ? EVASION_TT : MAIN_TT; - ttMove = ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE; - stage += (ttMove == MOVE_NONE); + stage = (pos.checkers() ? EVASION_TT : MAIN_TT) + + !(ttm && pos.pseudo_legal(ttm)); } /// MovePicker constructor for quiescence search MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const CapturePieceToHistory* cph, const PieceToHistory** ch, Square rs) - : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), recaptureSquare(rs), depth(d) { + : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), ttMove(ttm), recaptureSquare(rs), depth(d) { assert(d <= 0); - stage = pos.checkers() ? EVASION_TT : QSEARCH_TT; - ttMove = ttm - && (depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare) - && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE; - stage += (ttMove == MOVE_NONE); + stage = (pos.checkers() ? EVASION_TT : QSEARCH_TT) + + !(ttm && (depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare) + && pos.pseudo_legal(ttm)); } /// MovePicker constructor for ProbCut: we generate captures with SEE greater /// than or equal to the given threshold. MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph) - : pos(p), captureHistory(cph), threshold(th) { + : pos(p), captureHistory(cph), ttMove(ttm), threshold(th) { assert(!pos.checkers()); - stage = PROBCUT_TT; - ttMove = ttm - && pos.capture(ttm) - && pos.pseudo_legal(ttm) - && pos.see_ge(ttm, threshold) ? ttm : MOVE_NONE; - stage += (ttMove == MOVE_NONE); + stage = PROBCUT_TT + !(ttm && pos.capture(ttm) + && pos.pseudo_legal(ttm) + && pos.see_ge(ttm, threshold)); } /// MovePicker::score() assigns a numerical value to each move in a list, used From 2c5f0efa1350ea19d32c599ddd6b9350f1d216ff Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 10 Apr 2020 21:53:00 +0200 Subject: [PATCH 0188/1766] Extend irreversible moves if these are ttMoves and played in positions with a high value of the rule50 counter. The unusual extension of 2 is safe in this context as awarding it will reset the rule50 counter, making sure it is awarded very rarely in a search path. This patch partially addresses https://github.com/official-stockfish/Stockfish/issues/2620 as it should make it less likely to play a move that resets the counter, but that is worse than alternative moves after a slightly deeper search. passed STC: LLR: 2.96 (-2.94,2.94) {-0.50,1.50} Total: 71658 W: 13840 L: 13560 D: 44258 Ptnml(0-2): 1058, 7921, 17643, 8097, 1110 https://tests.stockfishchess.org/tests/view/5e90d0f6754c3424c4cf9f41 passed LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 85082 W: 11069 L: 10680 D: 63333 Ptnml(0-2): 459, 6982, 27259, 7393, 448 https://tests.stockfishchess.org/tests/view/5e917470af0a0143109dc341 closes https://github.com/official-stockfish/Stockfish/pull/2623 Bench: 4432822 --- src/search.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 4abc60692e6..4073f21d828 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1110,6 +1110,12 @@ namespace { if (type_of(move) == CASTLING) extension = 1; + // Late irreversible move extension + if ( move == ttMove + && pos.rule50_count() > 80 + && (captureOrPromotion || type_of(movedPiece) == PAWN)) + extension = 2; + // Add extension to new depth newDepth += extension; From d7a2d5a44588676abf9b49e35a5a567fd57ec3b0 Mon Sep 17 00:00:00 2001 From: Lolligerhans Date: Sat, 11 Apr 2020 17:28:45 +0200 Subject: [PATCH 0189/1766] Remove candidate passers w/o feasible lever +-------+ | o . . | o their pawns | x . . | x our pawns | . x . | <- Can sacrifice to create passer? +-------+ yes 1 2 3 4 5 +-------+ +-------+ +-------+ +-------+ +-------+ | o . . | | o r . | | o r . | | o . b | | o . b | lowercase: theirs | x b . | | x . . | | x . R | | x . R | | x . . | uppercase: ours | . x . | | . x . | | . x . | | . x . | | . x B | +-------+ +-------+ +-------+ +-------+ +-------+ no no yes no yes The value of our top pawn depends on our ability to advance our bottom pawn, levering their blocker. Previously, this pawn configuration was always scored as passer (although a blocked one). Add requirements for the square s above our (possibly) sacrificed pawn: - s must not be occupied by them (1). - If they attack s (2), we must attack s (3). - If they attack s with a minor (4), we must attack s with a minor (5). The attack from their blocker is ignored because it is inherent in the structure; we are ok with sacrificing our bottom pawn. LTC LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 37030 W: 4962 L: 4682 D: 27386 Ptnml(0-2): 266, 3445, 10863, 3625, 316 https://tests.stockfishchess.org/tests/view/5e92a2b4be6ede5b954bf239 STC LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 40874 W: 8066 L: 7813 D: 24995 Ptnml(0-2): 706, 4753, 9324, 4890, 764 https://tests.stockfishchess.org/tests/view/5e922199af0a0143109dc90e closes https://github.com/official-stockfish/Stockfish/pull/2624 Bench: 4828294 --- src/evaluate.cpp | 19 ++++++++++++++++++- src/pawns.cpp | 1 + 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index a2e5ef7b571..2698c813dfa 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -578,16 +578,33 @@ namespace { constexpr Color Them = ~Us; constexpr Direction Up = pawn_push(Us); + constexpr Direction Down = -Up; auto king_proximity = [&](Color c, Square s) { return std::min(distance(pos.square(c), s), 5); }; - Bitboard b, bb, squaresToQueen, unsafeSquares; + Bitboard b, bb, squaresToQueen, unsafeSquares, candidatePassers, leverable; Score score = SCORE_ZERO; b = pe->passed_pawns(Us); + candidatePassers = b & shift(pos.pieces(Them, PAWN)); + if (candidatePassers) + { + // Can we lever the blocker of a candidate passer? + leverable = shift(pos.pieces(Us, PAWN)) + & ~pos.pieces(Them) + & (~attackedBy2[Them] | attackedBy[Us][ALL_PIECES]) + & (~(attackedBy[Them][KNIGHT] | attackedBy[Them][BISHOP]) + | (attackedBy[Us ][KNIGHT] | attackedBy[Us ][BISHOP])); + + // Remove candidate otherwise + b &= ~candidatePassers + | shift(leverable) + | shift(leverable); + } + while (b) { Square s = pop_lsb(&b); diff --git a/src/pawns.cpp b/src/pawns.cpp index 0017b8044d8..63bc596fcba 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -118,6 +118,7 @@ namespace { // (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. + // (Refined in Evaluation::passed) passed = !(stoppers ^ lever) || ( !(stoppers ^ leverPush) && popcount(phalanx) >= popcount(leverPush)) From db59696aaf91641ade911c4a6ca393a1691d78a8 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Mon, 13 Apr 2020 03:48:52 +0300 Subject: [PATCH 0190/1766] Scale up space weight with number of blocked pawns This idea is loosely based on stockfish losses in closed positions in different tournaments. Space weight symmetrically increases for both sides the more blocked position is. passed STC https://tests.stockfishchess.org/tests/view/5e919eefaf0a0143109dc8ce LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 16994 W: 3389 L: 3172 D: 10433 Ptnml(0-2): 277, 1931, 3918, 2040, 331 passed LTC https://tests.stockfishchess.org/tests/view/5e91d04faf0a0143109dc8ea LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 133386 W: 17316 L: 16763 D: 99307 Ptnml(0-2): 945, 12407, 39524, 12784, 1033 closes https://github.com/official-stockfish/Stockfish/pull/2626 Bench: 4966867 --- src/evaluate.cpp | 2 +- src/pawns.cpp | 3 +++ src/pawns.h | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 2698c813dfa..cd535d885fc 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -695,7 +695,7 @@ namespace { behind |= shift(behind); int bonus = popcount(safe) + popcount(behind & safe & ~attackedBy[Them][ALL_PIECES]); - int weight = pos.count(Us) - 1; + int weight = pos.count(Us) - 2 + pe->blocked_count() / 2; Score score = make_score(bonus * weight * weight / 16, 0); if (T) diff --git a/src/pawns.cpp b/src/pawns.cpp index 63bc596fcba..7c4eda0f0b0 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -86,6 +86,7 @@ namespace { e->passedPawns[Us] = 0; e->kingSquares[Us] = SQ_NONE; e->pawnAttacks[Us] = e->pawnAttacksSpan[Us] = pawn_attacks_bb(ourPawns); + e->blockedCount[Us] = 0; // Loop through all pawns of the current color and score each pawn while ((s = *pl++) != SQ_NONE) @@ -105,6 +106,8 @@ namespace { phalanx = neighbours & rank_bb(s); support = neighbours & rank_bb(s - Up); + e->blockedCount[Us] += bool(blocked); + // A pawn is backward when it is behind all pawns of the same color on // the adjacent files and cannot safely advance. backward = !(neighbours & forward_ranks_bb(Them, s + Up)) diff --git a/src/pawns.h b/src/pawns.h index bd17618f5d8..41a88c6f393 100644 --- a/src/pawns.h +++ b/src/pawns.h @@ -38,6 +38,7 @@ struct Entry { Bitboard passed_pawns(Color c) const { return passedPawns[c]; } Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; } int passed_count() const { return popcount(passedPawns[WHITE] | passedPawns[BLACK]); } + int blocked_count() const { return blockedCount[WHITE] + blockedCount[BLACK]; } template Score king_safety(const Position& pos) { @@ -59,6 +60,7 @@ struct Entry { Square kingSquares[COLOR_NB]; Score kingSafety[COLOR_NB]; int castlingRights[COLOR_NB]; + int blockedCount[COLOR_NB]; }; typedef HashTable Table; From de9fc53af57d1620bb913c357630e724c7ea186b Mon Sep 17 00:00:00 2001 From: silversolver1 <61594747+silversolver1@users.noreply.github.com> Date: Sun, 12 Apr 2020 22:23:04 -0500 Subject: [PATCH 0191/1766] Removes evasionPrunable STC: LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 25656 W: 4979 L: 4826 D: 15851 Ptnml(0-2): 414, 2971, 5964, 3006, 473 https://tests.stockfishchess.org/tests/view/5e93dbd72cb65b3059c33819 LTC: LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 43732 W: 5656 L: 5593 D: 32483 Ptnml(0-2): 324, 4072, 13009, 4139, 322 https://tests.stockfishchess.org/tests/view/5e93e37c2cb65b3059c33825 closes https://github.com/official-stockfish/Stockfish/pull/2627 Bench: 4702195 --- src/search.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 4073f21d828..76011840ba5 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1398,7 +1398,7 @@ namespace { Move ttMove, move, bestMove; Depth ttDepth; Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha; - bool ttHit, pvHit, inCheck, givesCheck, captureOrPromotion, evasionPrunable; + bool ttHit, pvHit, inCheck, givesCheck, captureOrPromotion; int moveCount; if (PvNode) @@ -1527,14 +1527,8 @@ namespace { } } - // Detect non-capture evasions that are candidates to be pruned - evasionPrunable = inCheck - && (depth != 0 || moveCount > 2) - && bestValue > VALUE_TB_LOSS_IN_MAX_PLY - && !pos.capture(move); - // Don't search moves with negative SEE values - if ( (!inCheck || evasionPrunable) && !pos.see_ge(move)) + if ( !inCheck && !pos.see_ge(move)) continue; // Speculative prefetch as early as possible From 5c58f6712667076babe9ebaac1689436721c42be Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Mon, 13 Apr 2020 23:01:38 +0200 Subject: [PATCH 0192/1766] less bonus for blocked connected pawn Use less bonus for blocked connected pawns so closed positions are less worth. STC: LLR: 2.96 (-2.94,2.94) {-0.50,1.50} Total: 60004 W: 11904 L: 11619 D: 36481 Ptnml(0-2): 1066, 7083, 13535, 7136, 1182 https://tests.stockfishchess.org/tests/view/5e941a8063d105aebbab23e3 LTC: LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 36606 W: 4831 L: 4556 D: 27219 Ptnml(0-2): 252, 3353, 10872, 3520, 306 https://tests.stockfishchess.org/tests/view/5e9444b963d105aebbab2427 closes https://github.com/official-stockfish/Stockfish/pull/2629 Bench: 4961260 --- src/pawns.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index 7c4eda0f0b0..a2063a8f832 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -138,7 +138,7 @@ namespace { // Score this pawn if (support | phalanx) { - int v = Connected[r] * (2 + bool(phalanx) - bool(opposed)) + int v = Connected[r] * (4 + 2 * bool(phalanx) - 2 * bool(opposed) - bool(blocked)) / 2 + 21 * popcount(support); score += make_score(v, v * (r - 2) / 4); From ca4e399ea6d88f8f71c8fd692566223496b10f78 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Wed, 15 Apr 2020 04:13:50 +0300 Subject: [PATCH 0193/1766] Space bonus and number of blocked pawns This patch refines the recently introduced interaction between the space bonus and the number of blocked pawns in a position. * pawns count as blocked also if their push square is attacked by 2 enemy pawns; * overall dependence is stronger as well as offset; * bonus increase is capped at 9 blocked pawns in position; passed STC https://tests.stockfishchess.org/tests/view/5e94560663d105aebbab243d LLR: 2.96 (-2.94,2.94) {-0.50,1.50} Total: 29500 W: 5842 L: 5603 D: 18055 Ptnml(0-2): 504, 3443, 6677, 3562, 564 passed LTC https://tests.stockfishchess.org/tests/view/5e95b383c2aaa99f75d1a14d LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 63504 W: 8329 L: 7974 D: 47201 Ptnml(0-2): 492, 5848, 18720, 6197, 495 closes https://github.com/official-stockfish/Stockfish/pull/2631 bench 4956028 --- 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 cd535d885fc..8feedfebad7 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -695,7 +695,7 @@ namespace { behind |= shift(behind); int bonus = popcount(safe) + popcount(behind & safe & ~attackedBy[Them][ALL_PIECES]); - int weight = pos.count(Us) - 2 + pe->blocked_count() / 2; + int weight = pos.count(Us) - 3 + std::min(pe->blocked_count(), 9); Score score = make_score(bonus * weight * weight / 16, 0); if (T) diff --git a/src/pawns.cpp b/src/pawns.cpp index a2063a8f832..75e6ad7a7df 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -106,7 +106,7 @@ namespace { phalanx = neighbours & rank_bb(s); support = neighbours & rank_bb(s - Up); - e->blockedCount[Us] += bool(blocked); + e->blockedCount[Us] += blocked || more_than_one(leverPush); // A pawn is backward when it is behind all pawns of the same color on // the adjacent files and cannot safely advance. From 0e51ff1074d5a66495a21990bd1826a8d06447e8 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Wed, 15 Apr 2020 18:22:02 +0300 Subject: [PATCH 0194/1766] Don't attempt probcut if ttMove is not good enough. This idea is loosely based on xoroshiro idea about raisedBeta and ttmoves. If our ttmove have low enough ttvalue and is deep enough (deeper than our probcut depth) it makes little sense to try probcut moves, since the ttMove already more or less failed to produce one according to transposition table. passed STC https://tests.stockfishchess.org/tests/view/5e9673ddc2718dee3c822920 LLR: 2.95 (-2.94,2.94) {-0.50,1.50} Total: 72148 W: 14038 L: 13741 D: 44369 Ptnml(0-2): 1274, 8326, 16615, 8547, 1312 passed LTC https://tests.stockfishchess.org/tests/view/5e96b378c2718dee3c8229bf LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 89054 W: 11418 L: 10996 D: 66640 Ptnml(0-2): 623, 8113, 26643, 8515, 633 closes https://github.com/official-stockfish/Stockfish/pull/2632 bench 4952731 --- src/search.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 76011840ba5..dae1e23c033 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -906,8 +906,12 @@ namespace { MovePicker mp(pos, ttMove, raisedBeta - ss->staticEval, &captureHistory); int probCutCount = 0; - while ( (move = mp.next_move()) != MOVE_NONE - && probCutCount < 2 + 2 * cutNode) + while ( (move = mp.next_move()) != MOVE_NONE + && probCutCount < 2 + 2 * cutNode + && !( move == ttMove + && (tte->bound() & BOUND_LOWER) + && tte->depth() >= depth - 4 + && ttValue < raisedBeta)) if (move != excludedMove && pos.legal(move)) { assert(pos.capture_or_promotion(move)); From d87adcc0062fa9707ba2b0b6ce3369a4ab391b6d Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Thu, 16 Apr 2020 01:33:48 +0300 Subject: [PATCH 0195/1766] Queen and Rook Tuning Tuning for multiple parameters for Queen and Rook. passed STC LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 62790 W: 12033 L: 11754 D: 39003 Ptnml(0-2): 1058, 7186, 14666, 7389, 1096 https://tests.stockfishchess.org/tests/view/5e978c66c9ada107a0370d87 passed LTC LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 89780 W: 11460 L: 11036 D: 67284 Ptnml(0-2): 624, 8151, 26951, 8505, 659 https://tests.stockfishchess.org/tests/view/5e979aaec9ada107a0370d93 closes https://github.com/official-stockfish/Stockfish/pull/2634 Bench: 5111578 --- src/evaluate.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 8feedfebad7..7eafacf8233 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -82,7 +82,7 @@ namespace { // Penalties for enemy's safe checks constexpr int QueenSafeCheck = 780; - constexpr int RookSafeCheck = 1080; + constexpr int RookSafeCheck = 1078; constexpr int BishopSafeCheck = 635; constexpr int KnightSafeCheck = 790; @@ -96,19 +96,19 @@ namespace { { S(-48,-59), S(-20,-23), S( 16, -3), S( 26, 13), S( 38, 24), S( 51, 42), // Bishop S( 55, 54), S( 63, 57), S( 63, 65), S( 68, 73), S( 81, 78), S( 81, 86), S( 91, 88), S( 98, 97) }, - { S(-58,-76), S(-27,-18), S(-15, 28), S(-10, 55), S( -5, 69), S( -2, 82), // Rook - S( 9,112), S( 16,118), S( 30,132), S( 29,142), S( 32,155), S( 38,165), - S( 46,166), S( 48,169), S( 58,171) }, - { S(-39,-36), S(-21,-15), S( 3, 8), S( 3, 18), S( 14, 34), S( 22, 54), // Queen - S( 28, 61), S( 41, 73), S( 43, 79), S( 48, 92), S( 56, 94), S( 60,104), - S( 60,113), S( 66,120), S( 67,123), S( 70,126), S( 71,133), S( 73,136), - S( 79,140), S( 88,143), S( 88,148), S( 99,166), S(102,170), S(102,175), - S(106,184), S(109,191), S(113,206), S(116,212) } + { S(-60,-78), S(-20,-17), S( 2, 23), S( 3, 39), S( 3, 70), S( 11, 99), // Rook + S( 22,103), S( 31,121), S( 40,134), S( 40,139), S( 41,158), S( 48,164), + S( 57,168), S( 57,169), S( 62,172) }, + { S(-34,-36), S(-15,-21), S(-10, -1), S(-10, 22), S( 20, 41), S( 23, 56), // Queen + S( 23, 59), S( 35, 75), S( 38, 78), S( 53, 96), S( 64, 96), S( 65,100), + S( 65,121), S( 66,127), S( 67,131), S( 67,133), S( 72,136), S( 72,141), + S( 77,147), S( 79,150), S( 93,151), S(108,168), S(108,168), S(108,171), + S(110,182), S(114,182), S(114,192), S(116,219) } }; // RookOnFile[semiopen/open] contains bonuses for each rook when there is // no (friendly) pawn on the rook file. - constexpr Score RookOnFile[] = { S(21, 4), S(47, 25) }; + constexpr Score RookOnFile[] = { S(19, 7), S(48, 29) }; // ThreatByMinor/ByRook[attacked PieceType] contains bonuses according to // which piece type attacks which one. Attacks on lesser pieces which are @@ -118,7 +118,7 @@ namespace { }; constexpr Score ThreatByRook[PIECE_TYPE_NB] = { - S(0, 0), S(2, 44), S(36, 71), S(36, 61), S(0, 38), S(51, 38) + S(0, 0), S(3, 46), S(37, 68), S(42, 60), S(0, 38), S(58, 41) }; // PassedRank[Rank] contains a bonus according to the rank of a passed pawn @@ -132,21 +132,21 @@ namespace { constexpr Score FlankAttacks = S( 8, 0); constexpr Score Hanging = S( 69, 36); constexpr Score KingProtector = S( 7, 8); - constexpr Score KnightOnQueen = S( 16, 12); + constexpr Score KnightOnQueen = S( 16, 11); constexpr Score LongDiagonalBishop = S( 45, 0); constexpr Score MinorBehindPawn = S( 18, 3); constexpr Score Outpost = S( 30, 21); constexpr Score PassedFile = S( 11, 8); constexpr Score PawnlessFlank = S( 17, 95); constexpr Score RestrictedPiece = S( 7, 7); - constexpr Score RookOnQueenFile = S( 7, 6); + constexpr Score RookOnQueenFile = S( 5, 9); constexpr Score SliderOnQueen = S( 59, 18); constexpr Score ThreatByKing = S( 24, 89); constexpr Score ThreatByPawnPush = S( 48, 39); constexpr Score ThreatBySafePawn = S(173, 94); - constexpr Score TrappedRook = S( 52, 10); - constexpr Score WeakQueen = S( 49, 15); - constexpr Score WeakQueenProtection = S( 14, 0); + constexpr Score TrappedRook = S( 55, 13); + constexpr Score WeakQueen = S( 51, 14); + constexpr Score WeakQueenProtection = S( 15, 0); #undef S From 6f35af7ad31d55d0f0918742b60f4e672fc43ccf Mon Sep 17 00:00:00 2001 From: Lolligerhans Date: Thu, 16 Apr 2020 03:56:43 +0200 Subject: [PATCH 0196/1766] Increase safe check bonus if multiple safe checks Add 50% "safe checks" bonus when there are multiple safe checks from the same piece type. LTC LLR: 2.97 (-2.94,2.94) {0.25,1.75} Total: 128184 W: 16491 L: 15954 D: 95739 Ptnml(0-2): 884, 11793, 38267, 12198, 950 https://tests.stockfishchess.org/tests/view/5e97d1b6c9ada107a0370e03 STC LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 19022 W: 3733 L: 3514 D: 11775 Ptnml(0-2): 338, 2103, 4414, 2314, 342 https://tests.stockfishchess.org/tests/view/5e97c377c9ada107a0370ddf closes https://github.com/official-stockfish/Stockfish/pull/2636 Bench: 5057329 --- src/evaluate.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 7eafacf8233..0b1956f1082 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -398,9 +398,9 @@ namespace { // Enemy rooks checks rookChecks = b1 & safe & attackedBy[Them][ROOK]; - if (rookChecks) - kingDanger += RookSafeCheck; + kingDanger += more_than_one(rookChecks) ? RookSafeCheck * 3/2 + : RookSafeCheck; else unsafeChecks |= b1 & attackedBy[Them][ROOK]; @@ -411,9 +411,9 @@ namespace { & safe & ~attackedBy[Us][QUEEN] & ~rookChecks; - if (queenChecks) - kingDanger += QueenSafeCheck; + kingDanger += more_than_one(queenChecks) ? QueenSafeCheck * 3/2 + : QueenSafeCheck; // Enemy bishops checks: we count them only if they are from squares from // which we can't give a queen check, because queen checks are more valuable. @@ -421,17 +421,17 @@ namespace { & attackedBy[Them][BISHOP] & safe & ~queenChecks; - if (bishopChecks) - kingDanger += BishopSafeCheck; + kingDanger += more_than_one(bishopChecks) ? BishopSafeCheck * 3/2 + : BishopSafeCheck; else unsafeChecks |= b2 & attackedBy[Them][BISHOP]; // Enemy knights checks knightChecks = pos.attacks_from(ksq) & attackedBy[Them][KNIGHT]; - if (knightChecks & safe) - kingDanger += KnightSafeCheck; + kingDanger += more_than_one(knightChecks & safe) ? KnightSafeCheck * 3/2 + : KnightSafeCheck; else unsafeChecks |= knightChecks; From ecac132bca23c6dfcc697cc3cd069939bc168ed4 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Thu, 16 Apr 2020 18:10:44 +0100 Subject: [PATCH 0197/1766] Scale factor in opposite-color bishop endings This change varies the scale factor with the total number of pieces and pawns on the strongSide. STC : LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 150920 W: 28828 L: 28422 D: 93670 +0.65 Elo Ptnml(0-2): 2507, 17548, 35030, 17782, 2593 https://tests.stockfishchess.org/tests/view/5e983eb2c00499c5410f4951 LTC : LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 69238 W: 8810 L: 8446 D: 51982 +1.58 Elo Ptnml(0-2): 451, 6276, 20879, 6484, 529 https://tests.stockfishchess.org/tests/view/5e985b27c00499c5410f4987 closes https://github.com/official-stockfish/Stockfish/pull/2637 Bench 4821332 --- src/evaluate.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 0b1956f1082..cbcebd045f6 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -761,11 +761,16 @@ namespace { // If scale is not already specific, scale down the endgame via general heuristics if (sf == SCALE_FACTOR_NORMAL) { - if ( pos.opposite_bishops() - && pos.non_pawn_material() == 2 * BishopValueMg) - sf = 22; + if (pos.opposite_bishops()) + { + if ( pos.non_pawn_material(WHITE) == BishopValueMg + && pos.non_pawn_material(BLACK) == BishopValueMg) + sf = 22; + else + sf = 22 + 3 * pos.count(strongSide); + } else - sf = std::min(sf, 36 + (pos.opposite_bishops() ? 2 : 7) * pos.count(strongSide)); + sf = std::min(sf, 36 + 7 * pos.count(strongSide)); sf = std::max(0, sf - (pos.rule50_count() - 12) / 4); } From 345b2d153a8092cff93ad660c9d107cd66fda43b Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 15 Apr 2020 23:34:18 +0200 Subject: [PATCH 0198/1766] Remove one condition in probcut TTmove skipping the removed condition appears illogical and is not needed. passed STC: LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 80418 W: 15217 L: 15144 D: 50057 Ptnml(0-2): 1341, 9399, 18679, 9426, 1364 https://tests.stockfishchess.org/tests/view/5e977eb5c9ada107a0370d6b passed LTC: LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 49878 W: 6299 L: 6247 D: 37332 Ptnml(0-2): 327, 4677, 14897, 4693, 345 https://tests.stockfishchess.org/tests/view/5e97e07dc9ada107a0370e53 closes https://github.com/official-stockfish/Stockfish/pull/2638 Bench: 4958027 --- src/search.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index dae1e23c033..fd690dcd9eb 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -909,7 +909,6 @@ namespace { while ( (move = mp.next_move()) != MOVE_NONE && probCutCount < 2 + 2 * cutNode && !( move == ttMove - && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 4 && ttValue < raisedBeta)) if (move != excludedMove && pos.legal(move)) From bde1506ba56ae566ac4e797e642017fe386f6425 Mon Sep 17 00:00:00 2001 From: protonspring Date: Thu, 16 Apr 2020 23:12:43 -0600 Subject: [PATCH 0199/1766] Simplify minPawnDistance This is a functional simplification which fixes an awkward numerical cliff. With master king_safety, no pawns is scored higher than pawn(s) that is/are far from the king. This may motivate SF to throw away pawns to increase king safety. With this patch, there is a consistent value for minPawnDistance where losing a pawn never increases king safety. STC LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 45548 W: 8624 L: 8525 D: 28399 Ptnml(0-2): 592, 4937, 11587, 5096, 562 https://tests.stockfishchess.org/tests/view/5e98ced630be947a14e9ddc5 LTC LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 42084 W: 5292 L: 5242 D: 31550 Ptnml(0-2): 193, 3703, 13252, 3649, 245 https://tests.stockfishchess.org/tests/view/5e98e22e30be947a14e9de07 closes https://github.com/official-stockfish/Stockfish/pull/2639 bench 4600292 --- src/pawns.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index 75e6ad7a7df..066146e20b4 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -244,7 +244,7 @@ Score Entry::do_king_safety(const Position& pos) { // In endgame we like to bring our king near our closest pawn Bitboard pawns = pos.pieces(Us, PAWN); - int minPawnDist = pawns ? 8 : 0; + int minPawnDist = 6; if (pawns & PseudoAttacks[KING][ksq]) minPawnDist = 1; From 221893bf679f70098e6f751fded2fe843471c6be Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sat, 18 Apr 2020 03:28:47 +0300 Subject: [PATCH 0200/1766] Apply multicut pruning more often This patch increases number of nodes where we produce multicut cutoffs. The idea is that if our ttMove failed to produce a singular extension but ttValue is greater than beta we can afford to do one more reduced search near beta excluding ttMove to see if it will produce a fail high - and if it does so produce muticut by analogy to existing logic. passed STC https://tests.stockfishchess.org/tests/view/5e9a162b5b664cdba0ce6e28 LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 58238 W: 11192 L: 10917 D: 36129 Ptnml(0-2): 1007, 6704, 13442, 6939, 1027 passed LTC https://tests.stockfishchess.org/tests/view/5e9a1e845b664cdba0ce7411 LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 137852 W: 17460 L: 16899 D: 103493 Ptnml(0-2): 916, 12610, 41383, 13031, 986 closes https://github.com/official-stockfish/Stockfish/pull/2640 bench 4881443 --- src/search.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index fd690dcd9eb..a7e90a08552 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1091,6 +1091,18 @@ namespace { // a soft bound. else if (singularBeta >= beta) return singularBeta; + + // If the eval of ttMove is greater than beta we try also if there is an other move that + // pushes it over beta, if so also produce a cutoff + else if (ttValue >= beta) + { + ss->excludedMove = move; + value = search(pos, ss, beta - 1, beta, (depth + 3) / 2, cutNode); + ss->excludedMove = MOVE_NONE; + + if (value >= beta) + return beta; + } } // Check extension (~2 Elo) From bb5589b829b79d7c60a820d4b1634dccbc4bbb3f Mon Sep 17 00:00:00 2001 From: pb00067 Date: Tue, 21 Apr 2020 20:55:41 +0200 Subject: [PATCH 0201/1766] continuation histories when in check If in check, don't write to continuation histories ss-4, ss-6. Adding inCheck to the stack was needed, and might be useful for future patches. Passed STC: https://tests.stockfishchess.org/tests/view/5e9ee24acaaff5d60a50b812 LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 61774 W: 11725 L: 11449 D: 38600 Ptnml(0-2): 971, 7211, 14322, 7337, 1046 Passed LTC: https://tests.stockfishchess.org/tests/view/5e9eecb7caaff5d60a50b831 LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 250822 W: 32067 L: 31179 D: 187576 Ptnml(0-2): 1745, 23126, 74824, 23928, 1788 closes https://github.com/official-stockfish/Stockfish/pull/2645 bench: 4808463 --- src/search.cpp | 40 ++++++++++++++++++++++------------------ src/search.h | 1 + 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index a7e90a08552..a3d4a329a1f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -626,14 +626,14 @@ namespace { Move ttMove, move, excludedMove, bestMove; Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue; - bool ttHit, ttPv, formerPv, inCheck, givesCheck, improving, didLMR, priorCapture; + bool ttHit, ttPv, formerPv, givesCheck, improving, didLMR, priorCapture; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture, singularLMR; Piece movedPiece; int moveCount, captureCount, quietCount; // Step 1. Initialize node Thread* thisThread = pos.this_thread(); - inCheck = pos.checkers(); + ss->inCheck = pos.checkers(); priorCapture = pos.captured_piece(); Color us = pos.side_to_move(); moveCount = captureCount = quietCount = ss->moveCount = 0; @@ -654,7 +654,7 @@ namespace { if ( Threads.stop.load(std::memory_order_relaxed) || pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) - return (ss->ply >= MAX_PLY && !inCheck) ? evaluate(pos) + return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos) : value_draw(pos.this_thread()); // Step 3. Mate distance pruning. Even if we mate at the next move our score @@ -793,7 +793,7 @@ namespace { CapturePieceToHistory& captureHistory = thisThread->captureHistory; // Step 6. Static evaluation of the position - if (inCheck) + if (ss->inCheck) { ss->staticEval = eval = VALUE_NONE; improving = false; @@ -920,7 +920,7 @@ namespace { probCutCount++; ss->currentMove = move; - ss->continuationHistory = &thisThread->continuationHistory[inCheck] + ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck] [captureOrPromotion] [pos.moved_piece(move)] [to_sq(move)]; @@ -1030,7 +1030,7 @@ namespace { // Futility pruning: parent node (~5 Elo) if ( lmrDepth < 6 - && !inCheck + && !ss->inCheck && ss->staticEval + 235 + 172 * lmrDepth <= alpha && (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] @@ -1146,7 +1146,7 @@ namespace { // Update the current move (this must be done after singular extension search) ss->currentMove = move; - ss->continuationHistory = &thisThread->continuationHistory[inCheck] + ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck] [captureOrPromotion] [movedPiece] [to_sq(move)]; @@ -1365,11 +1365,11 @@ namespace { // must be a mate or a stalemate. If we are in a singular extension search then // return a fail low score. - assert(moveCount || !inCheck || excludedMove || !MoveList(pos).size()); + assert(moveCount || !ss->inCheck || excludedMove || !MoveList(pos).size()); if (!moveCount) bestValue = excludedMove ? alpha - : inCheck ? mated_in(ss->ply) : VALUE_DRAW; + : ss->inCheck ? mated_in(ss->ply) : VALUE_DRAW; else if (bestMove) update_all_stats(pos, ss, bestMove, bestValue, beta, prevSq, @@ -1413,7 +1413,7 @@ namespace { Move ttMove, move, bestMove; Depth ttDepth; Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha; - bool ttHit, pvHit, inCheck, givesCheck, captureOrPromotion; + bool ttHit, pvHit, givesCheck, captureOrPromotion; int moveCount; if (PvNode) @@ -1426,20 +1426,20 @@ namespace { Thread* thisThread = pos.this_thread(); (ss+1)->ply = ss->ply + 1; bestMove = MOVE_NONE; - inCheck = pos.checkers(); + ss->inCheck = pos.checkers(); moveCount = 0; // Check for an immediate draw or maximum ply reached if ( pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) - return (ss->ply >= MAX_PLY && !inCheck) ? evaluate(pos) : VALUE_DRAW; + return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos) : VALUE_DRAW; assert(0 <= ss->ply && ss->ply < MAX_PLY); // Decide whether or not to include checks: this fixes also the type of // TT entry depth that we are going to use. Note that in qsearch we use // only two types of depth in TT: DEPTH_QS_CHECKS or DEPTH_QS_NO_CHECKS. - ttDepth = inCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS + ttDepth = ss->inCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS : DEPTH_QS_NO_CHECKS; // Transposition table lookup posKey = pos.key(); @@ -1457,7 +1457,7 @@ namespace { return ttValue; // Evaluate the position statically - if (inCheck) + if (ss->inCheck) { ss->staticEval = VALUE_NONE; bestValue = futilityBase = -VALUE_INFINITE; @@ -1520,7 +1520,7 @@ namespace { moveCount++; // Futility pruning - if ( !inCheck + if ( !ss->inCheck && !givesCheck && futilityBase > -VALUE_KNOWN_WIN && !pos.advanced_pawn_push(move)) @@ -1543,7 +1543,7 @@ namespace { } // Don't search moves with negative SEE values - if ( !inCheck && !pos.see_ge(move)) + if ( !ss->inCheck && !pos.see_ge(move)) continue; // Speculative prefetch as early as possible @@ -1557,7 +1557,7 @@ namespace { } ss->currentMove = move; - ss->continuationHistory = &thisThread->continuationHistory[inCheck] + ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck] [captureOrPromotion] [pos.moved_piece(move)] [to_sq(move)]; @@ -1591,7 +1591,7 @@ namespace { // All legal moves have been searched. A special case: If we're in check // and no legal moves were found, it is checkmate. - if (inCheck && bestValue == -VALUE_INFINITE) + if (ss->inCheck && bestValue == -VALUE_INFINITE) return mated_in(ss->ply); // Plies to mate from the root tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit, @@ -1710,8 +1710,12 @@ namespace { void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { for (int i : {1, 2, 4, 6}) + { + if (ss->inCheck && i > 2) + break; if (is_ok((ss-i)->currentMove)) (*(ss-i)->continuationHistory)[pc][to] << bonus; + } } diff --git a/src/search.h b/src/search.h index a900d094b0c..1653ce9237c 100644 --- a/src/search.h +++ b/src/search.h @@ -49,6 +49,7 @@ struct Stack { Value staticEval; int statScore; int moveCount; + bool inCheck; }; From 4776dc0e126ed311f10f34bfa058a6c86e9d3ef1 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Wed, 29 Apr 2020 02:40:16 +0300 Subject: [PATCH 0202/1766] Introduce futility pruning for captures The idea is somewhat similar to futility pruning for quiet moves - if a late enough capture doesn't give check and the static eval is much lower than alpha we can almost safely assume that this capture wouldn't be a good move. passed STC https://tests.stockfishchess.org/tests/view/5ea8544b53a4548a0348ee5b LLR: 2.95 (-2.94,2.94) {-0.50,1.50} Total: 236040 W: 44420 L: 43894 D: 147726 Ptnml(0-2): 3830, 27202, 55496, 27596, 3896 passed LTC https://tests.stockfishchess.org/tests/view/5ea87c842141237a731f0c7d LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 81336 W: 10429 L: 10028 D: 60879 Ptnml(0-2): 589, 7356, 24404, 7703, 616 closes https://github.com/official-stockfish/Stockfish/pull/2651 bench 4405247 --- src/search.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index a3d4a329a1f..55c520a4b0f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1049,6 +1049,13 @@ namespace { && captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] < 0) continue; + // Futility pruning for captures + if ( !givesCheck + && lmrDepth < 6 + && !ss->inCheck + && ss->staticEval + 270 + 384 * lmrDepth + PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha) + continue; + // See based pruning if (!pos.see_ge(move, Value(-194) * depth)) // (~25 Elo) continue; From 353e20674b2019094059caaa3567e9a44abe9cd1 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 29 Apr 2020 17:39:25 +0200 Subject: [PATCH 0203/1766] Small cleanups closes https://github.com/official-stockfish/Stockfish/pull/2628 No functional change --- src/evaluate.cpp | 6 ++++-- src/pawns.cpp | 4 ++-- src/pawns.h | 4 ++-- src/position.cpp | 2 +- src/position.h | 2 +- src/search.cpp | 14 +++++++------- 6 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index cbcebd045f6..9d7728c4511 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -740,7 +740,7 @@ namespace { // 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 u = ((mg > 0) - (mg < 0)) * Utility::clamp(complexity + 50, -abs(mg), 0); int v = ((eg > 0) - (eg < 0)) * std::max(complexity, -abs(eg)); if (T) @@ -815,7 +815,8 @@ namespace { initialize(); initialize(); - // Pieces should be evaluated first (populate attack tables) + // Pieces evaluated first (also populates attackedBy, attackedBy2). + // Note that the order of evaluation of the terms is left unspecified score += pieces() - pieces() + pieces() - pieces() + pieces() - pieces() @@ -823,6 +824,7 @@ namespace { score += mobility[WHITE] - mobility[BLACK]; + // More complex interactions that require fully populated attack bitboards score += king< WHITE>() - king< BLACK>() + threats() - threats() + passed< WHITE>() - passed< BLACK>() diff --git a/src/pawns.cpp b/src/pawns.cpp index 066146e20b4..7b266e779cc 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -86,7 +86,6 @@ namespace { e->passedPawns[Us] = 0; e->kingSquares[Us] = SQ_NONE; e->pawnAttacks[Us] = e->pawnAttacksSpan[Us] = pawn_attacks_bb(ourPawns); - e->blockedCount[Us] = 0; // Loop through all pawns of the current color and score each pawn while ((s = *pl++) != SQ_NONE) @@ -106,7 +105,7 @@ namespace { phalanx = neighbours & rank_bb(s); support = neighbours & rank_bb(s - Up); - e->blockedCount[Us] += blocked || more_than_one(leverPush); + e->blockedCount += blocked || more_than_one(leverPush); // A pawn is backward when it is behind all pawns of the same color on // the adjacent files and cannot safely advance. @@ -178,6 +177,7 @@ Entry* probe(const Position& pos) { return e; e->key = key; + e->blockedCount = 0; e->scores[WHITE] = evaluate(pos, e); e->scores[BLACK] = evaluate(pos, e); diff --git a/src/pawns.h b/src/pawns.h index 41a88c6f393..a3284a0fc64 100644 --- a/src/pawns.h +++ b/src/pawns.h @@ -38,7 +38,7 @@ struct Entry { Bitboard passed_pawns(Color c) const { return passedPawns[c]; } Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; } int passed_count() const { return popcount(passedPawns[WHITE] | passedPawns[BLACK]); } - int blocked_count() const { return blockedCount[WHITE] + blockedCount[BLACK]; } + int blocked_count() const { return blockedCount; } template Score king_safety(const Position& pos) { @@ -60,7 +60,7 @@ struct Entry { Square kingSquares[COLOR_NB]; Score kingSafety[COLOR_NB]; int castlingRights[COLOR_NB]; - int blockedCount[COLOR_NB]; + int blockedCount; }; typedef HashTable Table; diff --git a/src/position.cpp b/src/position.cpp index 6bbb7914ae8..40ebb959575 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -306,7 +306,7 @@ void Position::set_castling_right(Color c, Square rfrom) { 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); + & ~(kfrom | rfrom); } diff --git a/src/position.h b/src/position.h index f79c5463a7a..34a6abc3b5e 100644 --- a/src/position.h +++ b/src/position.h @@ -269,7 +269,7 @@ inline bool Position::can_castle(CastlingRights cr) const { } inline int Position::castling_rights(Color c) const { - return st->castlingRights & (c == WHITE ? WHITE_CASTLING : BLACK_CASTLING); + return c & CastlingRights(st->castlingRights); } inline bool Position::castling_impeded(CastlingRights cr) const { diff --git a/src/search.cpp b/src/search.cpp index 55c520a4b0f..7f29f771c0a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -61,8 +61,8 @@ namespace { // Different node types, used as a template parameter enum NodeType { NonPV, PV }; - constexpr uint64_t ttHitAverageWindow = 4096; - constexpr uint64_t ttHitAverageResolution = 1024; + constexpr uint64_t TtHitAverageWindow = 4096; + constexpr uint64_t TtHitAverageResolution = 1024; // Razor and futility margins constexpr int RazorMargin = 531; @@ -378,7 +378,7 @@ void Thread::search() { multiPV = std::max(multiPV, (size_t)4); multiPV = std::min(multiPV, rootMoves.size()); - ttHitAverage = ttHitAverageWindow * ttHitAverageResolution / 2; + ttHitAverage = TtHitAverageWindow * TtHitAverageResolution / 2; int ct = int(Options["Contempt"]) * PawnValueEg / 100; // From centipawns @@ -702,8 +702,8 @@ namespace { thisThread->lowPlyHistory[ss->ply - 1][from_to((ss-1)->currentMove)] << stat_bonus(depth - 5); // thisThread->ttHitAverage can be used to approximate the running average of ttHit - thisThread->ttHitAverage = (ttHitAverageWindow - 1) * thisThread->ttHitAverage / ttHitAverageWindow - + ttHitAverageResolution * ttHit; + thisThread->ttHitAverage = (TtHitAverageWindow - 1) * thisThread->ttHitAverage / TtHitAverageWindow + + TtHitAverageResolution * ttHit; // At non-PV nodes we check for an early TT cutoff if ( !PvNode @@ -1170,12 +1170,12 @@ namespace { || moveCountPruning || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha || cutNode - || thisThread->ttHitAverage < 375 * ttHitAverageResolution * ttHitAverageWindow / 1024)) + || thisThread->ttHitAverage < 375 * TtHitAverageResolution * TtHitAverageWindow / 1024)) { Depth r = reduction(improving, depth, moveCount); // Decrease reduction if the ttHit running average is large - if (thisThread->ttHitAverage > 500 * ttHitAverageResolution * ttHitAverageWindow / 1024) + if (thisThread->ttHitAverage > 500 * TtHitAverageResolution * TtHitAverageWindow / 1024) r--; // Reduction if other threads are searching this position. From 7f8166db89120960effa2ddda1a25188e5ab95b8 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Sat, 25 Apr 2020 15:55:35 -0400 Subject: [PATCH 0204/1766] Tuned safe checks and minor piece king protectors A combination of terms related to king safety one tuned safe check weights, the other tuned knight and bishop king protector weights separately with some compensation in the high outpost bonuses given to the minor pieces. passed STC LLR: 2.95 (-2.94,2.94) {-0.50,1.50} Total: 39892 W: 7594 L: 7350 D: 24948 Ptnml(0-2): 643, 4559, 9314, 4771, 659 https://tests.stockfishchess.org/tests/view/5ea49635b908f6dd28f34b82 passed LTC LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 104934 W: 13300 L: 12834 D: 78800 Ptnml(0-2): 697, 9571, 31514, 9939, 746 https://tests.stockfishchess.org/tests/view/5ea4abf6b908f6dd28f34bcb closes https://github.com/official-stockfish/Stockfish/pull/2649 Bench 4800754 --- AUTHORS | 1 + src/evaluate.cpp | 29 ++++++++++++++++------------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/AUTHORS b/AUTHORS index 79eb98a0b53..9fceebf7ee4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -42,6 +42,7 @@ Eelco de Groot (KingDefender) Elvin Liu (solarlight2) erbsenzaehler Ernesto Gatti +Linmiao Xu (linrock) Fabian Beuke (madnight) Fabian Fichter (ianfab) fanon diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 9d7728c4511..874faa6b333 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -81,10 +81,10 @@ namespace { constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 }; // Penalties for enemy's safe checks - constexpr int QueenSafeCheck = 780; - constexpr int RookSafeCheck = 1078; - constexpr int BishopSafeCheck = 635; - constexpr int KnightSafeCheck = 790; + constexpr int QueenSafeCheck = 772; + constexpr int RookSafeCheck = 1084; + constexpr int BishopSafeCheck = 645; + constexpr int KnightSafeCheck = 792; #define S(mg, eg) make_score(mg, eg) @@ -131,11 +131,14 @@ namespace { constexpr Score CorneredBishop = S( 50, 50); constexpr Score FlankAttacks = S( 8, 0); constexpr Score Hanging = S( 69, 36); - constexpr Score KingProtector = S( 7, 8); + constexpr Score BishopKingProtector = S( 6, 9); + constexpr Score KnightKingProtector = S( 8, 9); constexpr Score KnightOnQueen = S( 16, 11); constexpr Score LongDiagonalBishop = S( 45, 0); constexpr Score MinorBehindPawn = S( 18, 3); - constexpr Score Outpost = S( 30, 21); + constexpr Score KnightOutpost = S( 56, 36); + constexpr Score BishopOutpost = S( 30, 23); + constexpr Score ReachableOutpost = S( 31, 22); constexpr Score PassedFile = S( 11, 8); constexpr Score PawnlessFlank = S( 17, 95); constexpr Score RestrictedPiece = S( 7, 7); @@ -293,17 +296,17 @@ 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 ? 2 : 1); - + score += (Pt == KNIGHT) ? KnightOutpost : BishopOutpost; else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us)) - score += Outpost; + score += ReachableOutpost; // Bonus for a knight or bishop shielded by pawn if (shift(pos.pieces(PAWN)) & s) score += MinorBehindPawn; // Penalty if the piece is far from the king - score -= KingProtector * distance(pos.square(Us), s); + score -= (Pt == KNIGHT ? KnightKingProtector + : BishopKingProtector) * distance(pos.square(Us), s); if (Pt == BISHOP) { @@ -399,7 +402,7 @@ namespace { // Enemy rooks checks rookChecks = b1 & safe & attackedBy[Them][ROOK]; if (rookChecks) - kingDanger += more_than_one(rookChecks) ? RookSafeCheck * 3/2 + kingDanger += more_than_one(rookChecks) ? RookSafeCheck * 175/100 : RookSafeCheck; else unsafeChecks |= b1 & attackedBy[Them][ROOK]; @@ -412,7 +415,7 @@ namespace { & ~attackedBy[Us][QUEEN] & ~rookChecks; if (queenChecks) - kingDanger += more_than_one(queenChecks) ? QueenSafeCheck * 3/2 + kingDanger += more_than_one(queenChecks) ? QueenSafeCheck * 145/100 : QueenSafeCheck; // Enemy bishops checks: we count them only if they are from squares from @@ -430,7 +433,7 @@ namespace { // Enemy knights checks knightChecks = pos.attacks_from(ksq) & attackedBy[Them][KNIGHT]; if (knightChecks & safe) - kingDanger += more_than_one(knightChecks & safe) ? KnightSafeCheck * 3/2 + kingDanger += more_than_one(knightChecks & safe) ? KnightSafeCheck * 162/100 : KnightSafeCheck; else unsafeChecks |= knightChecks; From eb4a124b8859a6029b61f403e0a9415fa748e4bd Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sat, 2 May 2020 16:45:20 +0300 Subject: [PATCH 0205/1766] Refine scale factor of opposite colored bishops endgames. This patch makes it dependant on the count of passed pawns of the strong side instead of 22/64 in every case. passed STC https://tests.stockfishchess.org/tests/view/5ead60966ffeed51f6e32591 LLR: 2.96 (-2.94,2.94) {-0.50,1.50} Total: 50336 W: 9473 L: 9241 D: 31622 Ptnml(0-2): 570, 5371, 13098, 5515, 614 passed LTC https://tests.stockfishchess.org/tests/view/5ead6d3b6ffeed51f6e325b0 LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 21952 W: 2810 L: 2603 D: 16539 Ptnml(0-2): 101, 1791, 7005, 1958, 121 closes https://github.com/official-stockfish/Stockfish/pull/2658 bench 4247490 --- src/evaluate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 874faa6b333..67e05921055 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -768,7 +768,7 @@ namespace { { if ( pos.non_pawn_material(WHITE) == BishopValueMg && pos.non_pawn_material(BLACK) == BishopValueMg) - sf = 22; + sf = 18 + 4 * popcount(pe->passed_pawns(strongSide)); else sf = 22 + 3 * pos.count(strongSide); } From c527c3ad44f7465c79cef93f1e8cfebd998dc627 Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sat, 4 Apr 2015 08:54:15 +0200 Subject: [PATCH 0206/1766] Fishtest Tuning Framework The purpose of the code is to allow developers to easily and flexibly setup SF for a tuning session. Mainly you have just to remove 'const' qualifiers from the variables you want to tune and flag them for tuning, so if you have: int myKing = 10; Score myBonus = S(5, 15); Value myValue[][2] = { { V(100), V(20) }, { V(7), V(78) } }; and at the end of the update you may want to call a post update function: void my_post_update(); If instead of default Option's min-max values, you prefer your custom ones, returned by: std::pair my_range(int value) Or you jus want to set the range directly, you can simply add below: TUNE(SetRange(my_range), myKing, SetRange(-200, 200), myBonus, myValue, my_post_update); And all the magic happens :-) At startup all the parameters are printed in a format suitable to be copy-pasted in fishtest. In case the post update function is slow and you have many parameters to tune, you can add: UPDATE_ON_LAST(); And the values update, including post update function call, will be done only once, after the engine receives the last UCI option. The last option is the one defined and created as the last one, so this assumes that the GUI sends the options in the same order in which have been defined. closes https://github.com/official-stockfish/Stockfish/pull/2654 No functional change. --- src/Makefile | 2 +- src/main.cpp | 1 + src/tune.cpp | 146 ++++++++++++++++++++++++++++++++++++++ src/tune.h | 195 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/types.h | 2 + 5 files changed, 345 insertions(+), 1 deletion(-) create mode 100644 src/tune.cpp create mode 100644 src/tune.h diff --git a/src/Makefile b/src/Makefile index 15ad6353c80..0998a551ab5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -38,7 +38,7 @@ PGOBENCH = ./$(EXE) bench ### Object files OBJS = benchmark.o bitbase.o bitboard.o endgame.o evaluate.o main.o \ material.o misc.o movegen.o movepick.o pawns.o position.o psqt.o \ - search.o thread.o timeman.o tt.o uci.o ucioption.o syzygy/tbprobe.o + search.o thread.o timeman.o tt.o uci.o ucioption.o tune.o syzygy/tbprobe.o ### Establish the operating system name KERNEL = $(shell uname -s) diff --git a/src/main.cpp b/src/main.cpp index 182cf105edc..6eeda66dff8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -38,6 +38,7 @@ int main(int argc, char* argv[]) { std::cout << engine_info() << std::endl; UCI::init(Options); + Tune::init(); PSQT::init(); Bitboards::init(); Position::init(); diff --git a/src/tune.cpp b/src/tune.cpp new file mode 100644 index 00000000000..fe61151f319 --- /dev/null +++ b/src/tune.cpp @@ -0,0 +1,146 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include + +#include "types.h" +#include "misc.h" +#include "uci.h" + +using std::string; + +bool Tune::update_on_last; +const UCI::Option* LastOption = nullptr; +BoolConditions Conditions; +static std::map TuneResults; + +string Tune::next(string& names, bool pop) { + + string name; + + do { + string token = names.substr(0, names.find(',')); + + if (pop) + names.erase(0, token.size() + 1); + + std::stringstream ws(token); + name += (ws >> token, token); // Remove trailing whitespace + + } while ( std::count(name.begin(), name.end(), '(') + - std::count(name.begin(), name.end(), ')')); + + return name; +} + +static void on_tune(const UCI::Option& o) { + + if (!Tune::update_on_last || LastOption == &o) + Tune::read_options(); +} + +static void make_option(const string& n, int v, const SetRange& r) { + + // Do not generate option when there is nothing to tune (ie. min = max) + if (r(v).first == r(v).second) + return; + + if (TuneResults.count(n)) + v = TuneResults[n]; + + Options[n] << UCI::Option(v, r(v).first, r(v).second, on_tune); + LastOption = &Options[n]; + + // Print formatted parameters, ready to be copy-pasted in fishtest + std::cout << n << "," + << v << "," + << r(v).first << "," << r(v).second << "," + << (r(v).second - r(v).first) / 20.0 << "," + << "0.0020" + << std::endl; +} + +template<> void Tune::Entry::init_option() { make_option(name, value, range); } + +template<> void Tune::Entry::read_option() { + if (Options.count(name)) + value = Options[name]; +} + +template<> void Tune::Entry::init_option() { make_option(name, value, range); } + +template<> void Tune::Entry::read_option() { + if (Options.count(name)) + value = Value(int(Options[name])); +} + +template<> void Tune::Entry::init_option() { + make_option("m" + name, mg_value(value), range); + make_option("e" + name, eg_value(value), range); +} + +template<> void Tune::Entry::read_option() { + if (Options.count("m" + name)) + value = make_score(Options["m" + name], eg_value(value)); + + if (Options.count("e" + name)) + value = make_score(mg_value(value), Options["e" + name]); +} + +// Instead of a variable here we have a PostUpdate function: just call it +template<> void Tune::Entry::init_option() {} +template<> void Tune::Entry::read_option() { value(); } + + +// Set binary conditions according to a probability that depends +// on the corresponding parameter value. + +void BoolConditions::set() { + + static PRNG rng(now()); + static bool startup = true; // To workaround fishtest bench + + for (size_t i = 0; i < binary.size(); i++) + binary[i] = !startup && (values[i] + int(rng.rand() % variance) > threshold); + + startup = false; + + for (size_t i = 0; i < binary.size(); i++) + sync_cout << binary[i] << sync_endl; +} + + +// Init options with tuning session results instead of default values. Useful to +// get correct bench signature after a tuning session or to test tuned values. +// Just copy fishtest tuning results in a result.txt file and extract the +// values with: +// +// cat results.txt | sed 's/^param: \([^,]*\), best: \([^,]*\).*/ TuneResults["\1"] = int(round(\2));/' +// +// Then paste the output below, as the function body + +#include + +void Tune::read_results() { + + /* ...insert your values here... */ +} diff --git a/src/tune.h b/src/tune.h new file mode 100644 index 00000000000..27c3f961be4 --- /dev/null +++ b/src/tune.h @@ -0,0 +1,195 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2008 Tord Romstad (Glaurung author) + Copyright (C) 2008-2017 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef TUNE_H_INCLUDED +#define TUNE_H_INCLUDED + +#include +#include +#include +#include + +typedef std::pair Range; // Option's min-max values +typedef Range (RangeFun) (int); + +// Default Range function, to calculate Option's min-max values +inline Range default_range(int v) { + return v > 0 ? Range(0, 2 * v) : Range(2 * v, 0); +} + +struct SetRange { + explicit SetRange(RangeFun f) : fun(f) {} + SetRange(int min, int max) : fun(nullptr), range(min, max) {} + Range operator()(int v) const { return fun ? fun(v) : range; } + + RangeFun* fun; + Range range; +}; + +#define SetDefaultRange SetRange(default_range) + + +/// BoolConditions struct is used to tune boolean conditions in the +/// code by toggling them on/off according to a probability that +/// depends on the value of a tuned integer parameter: for high +/// values of the parameter condition is always disabled, for low +/// values is always enabled, otherwise it is enabled with a given +/// probability that depnends on the parameter under tuning. + +struct BoolConditions { + void init(size_t size) { values.resize(size, defaultValue), binary.resize(size, 0); } + void set(); + + std::vector binary, values; + int defaultValue = 465, variance = 40, threshold = 500; + SetRange range = SetRange(0, 1000); +}; + +extern BoolConditions Conditions; + +inline void set_conditions() { Conditions.set(); } + + +/// Tune class implements the 'magic' code that makes the setup of a fishtest +/// tuning session as easy as it can be. Mainly you have just to remove const +/// qualifiers from the variables you want to tune and flag them for tuning, so +/// if you have: +/// +/// const Score myScore = S(10, 15); +/// const Value myValue[][2] = { { V(100), V(20) }, { V(7), V(78) } }; +/// +/// If you have a my_post_update() function to run after values have been updated, +/// and a my_range() function to set custom Option's min-max values, then you just +/// remove the 'const' qualifiers and write somewhere below in the file: +/// +/// TUNE(SetRange(my_range), myScore, myValue, my_post_update); +/// +/// You can also set the range directly, and restore the default at the end +/// +/// TUNE(SetRange(-100, 100), myScore, SetDefaultRange); +/// +/// In case update function is slow and you have many parameters, you can add: +/// +/// UPDATE_ON_LAST(); +/// +/// And the values update, including post update function call, will be done only +/// once, after the engine receives the last UCI option, that is the one defined +/// and created as the last one, so the GUI should send the options in the same +/// order in which have been defined. + +class Tune { + + typedef void (PostUpdate) (); // Post-update function + + Tune() { read_results(); } + Tune(const Tune&) = delete; + void operator=(const Tune&) = delete; + void read_results(); + + static Tune& instance() { static Tune t; return t; } // Singleton + + // Use polymorphism to accomodate Entry of different types in the same vector + struct EntryBase { + virtual ~EntryBase() = default; + virtual void init_option() = 0; + virtual void read_option() = 0; + }; + + template + struct Entry : public EntryBase { + + static_assert(!std::is_const::value, "Parameter cannot be const!"); + + static_assert( std::is_same::value + || std::is_same::value + || std::is_same::value + || std::is_same::value, "Parameter type not supported!"); + + Entry(const std::string& n, T& v, const SetRange& r) : name(n), value(v), range(r) {} + void operator=(const Entry&) = delete; // Because 'value' is a reference + void init_option() override; + void read_option() override; + + std::string name; + T& value; + SetRange range; + }; + + // Our facilty to fill the container, each Entry corresponds to a parameter to tune. + // We use variadic templates to deal with an unspecified number of entries, each one + // of a possible different type. + static std::string next(std::string& names, bool pop = true); + + int add(const SetRange&, std::string&&) { return 0; } + + template + int add(const SetRange& range, std::string&& names, T& value, Args&&... args) { + list.push_back(std::unique_ptr(new Entry(next(names), value, range))); + return add(range, std::move(names), args...); + } + + // Template specialization for arrays: recursively handle multi-dimensional arrays + template + int add(const SetRange& range, std::string&& names, T (&value)[N], Args&&... args) { + for (size_t i = 0; i < N; i++) + add(range, next(names, i == N - 1) + "[" + std::to_string(i) + "]", value[i]); + return add(range, std::move(names), args...); + } + + // Template specialization for SetRange + template + int add(const SetRange&, std::string&& names, SetRange& value, Args&&... args) { + return add(value, (next(names), std::move(names)), args...); + } + + // Template specialization for BoolConditions + template + int add(const SetRange& range, std::string&& names, BoolConditions& cond, Args&&... args) { + for (size_t size = cond.values.size(), i = 0; i < size; i++) + add(cond.range, next(names, i == size - 1) + "_" + std::to_string(i), cond.values[i]); + return add(range, std::move(names), args...); + } + + std::vector> list; + +public: + template + static int add(const std::string& names, Args&&... args) { + return instance().add(SetDefaultRange, names.substr(1, names.size() - 2), args...); // Remove trailing parenthesis + } + static void init() { for (auto& e : instance().list) e->init_option(); read_options(); } // Deferred, due to UCI::Options access + static void read_options() { for (auto& e : instance().list) e->read_option(); } + static bool update_on_last; +}; + +// Some macro magic :-) we define a dummy int variable that compiler initializes calling Tune::add() +#define STRINGIFY(x) #x +#define UNIQUE2(x, y) x ## y +#define UNIQUE(x, y) UNIQUE2(x, y) // Two indirection levels to expand __LINE__ +#define TUNE(...) int UNIQUE(p, __LINE__) = Tune::add(STRINGIFY((__VA_ARGS__)), __VA_ARGS__) + +#define UPDATE_ON_LAST() bool UNIQUE(p, __LINE__) = Tune::update_on_last = true + +// Some macro to tune toggling of boolean conditions +#define CONDITION(x) (Conditions.binary[__COUNTER__] || (x)) +#define TUNE_CONDITIONS() int UNIQUE(c, __LINE__) = (Conditions.init(__COUNTER__), 0); \ + TUNE(Conditions, set_conditions) + +#endif // #ifndef TUNE_H_INCLUDED diff --git a/src/types.h b/src/types.h index cd8d23205b5..7b896803bfc 100644 --- a/src/types.h +++ b/src/types.h @@ -465,3 +465,5 @@ constexpr bool is_ok(Move m) { } #endif // #ifndef TYPES_H_INCLUDED + +#include "tune.h" // Global visibility to tuning setup From a91cb9fc1bc403dd610b3e17f022b5afa94dff49 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Wed, 6 May 2020 08:44:39 +0100 Subject: [PATCH 0207/1766] Penalty for all enemy pawns xrayed by our bishop. STC: LLR: 2.93 (-2.94,2.94) {-0.50,1.50} Total: 159760 W: 30229 L: 29813 D: 99718 Ptnml(0-2): 2659, 18309, 37534, 18713, 2665 https://tests.stockfishchess.org/tests/view/5eb1d5032326444a3b6d33ce LTC: LLR: 2.93 (-2.94,2.94) {0.25,1.75} Total: 26496 W: 3908 L: 3656 D: 18932 Ptnml(0-2): 192, 2512, 7610, 2720, 214 https://tests.stockfishchess.org/tests/view/5eb1e2dd2326444a3b6d33f9 closes https://github.com/official-stockfish/Stockfish/pull/2662 Bench 5185517 --- src/evaluate.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 67e05921055..e663f21f449 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -128,6 +128,7 @@ namespace { // Assorted bonuses and penalties constexpr Score BishopPawns = S( 3, 7); + constexpr Score BishopXRayPawns = S( 4, 5); constexpr Score CorneredBishop = S( 50, 50); constexpr Score FlankAttacks = S( 8, 0); constexpr Score Hanging = S( 69, 36); @@ -318,6 +319,9 @@ namespace { score -= BishopPawns * pos.pawns_on_same_color_squares(Us, s) * (!(attackedBy[Us][PAWN] & s) + popcount(blocked & CenterFiles)); + // Penalty for all enemy pawns x-rayed + score -= BishopXRayPawns * popcount(PseudoAttacks[BISHOP][s] & pos.pieces(Them, PAWN)); + // Bonus for bishop on a long diagonal which can "see" both center squares if (more_than_one(attacks_bb(s, pos.pieces(PAWN)) & Center)) score += LongDiagonalBishop; From fcaf0736feb17f1eb639a7ae071acc920b308f74 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Fri, 8 May 2020 12:07:42 +0100 Subject: [PATCH 0208/1766] Fix syzygy dependencies issue fixes https://github.com/official-stockfish/Stockfish/issues/2660 The problem was caused by .depend being created with a rule for tbprobe.o not for syzygy/tbprobe.o. This patch keeps an explicit list of sources (SRCS), generates OBJS, and compiles all object files to the src/ directory, consistent with .depend. VPATH is used to search the syzygy directory as needed. joint work with @gvreuls closes https://github.com/official-stockfish/Stockfish/pull/2664 No functional change --- src/Makefile | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Makefile b/src/Makefile index 0998a551ab5..016aafec102 100644 --- a/src/Makefile +++ b/src/Makefile @@ -35,10 +35,14 @@ BINDIR = $(PREFIX)/bin ### Built-in benchmark for pgo-builds PGOBENCH = ./$(EXE) bench -### Object files -OBJS = benchmark.o bitbase.o bitboard.o endgame.o evaluate.o main.o \ - material.o misc.o movegen.o movepick.o pawns.o position.o psqt.o \ - search.o thread.o timeman.o tt.o uci.o ucioption.o tune.o syzygy/tbprobe.o +### Source and object files +SRCS = benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.cpp main.cpp \ + material.cpp misc.cpp movegen.cpp movepick.cpp pawns.cpp position.cpp psqt.cpp \ + search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp + +OBJS = $(notdir $(SRCS:.cpp=.o)) + +VPATH = syzygy ### Establish the operating system name KERNEL = $(shell uname -s) @@ -450,7 +454,7 @@ objclean: # clean auxiliary profiling files profileclean: @rm -rf profdir - @rm -f bench.txt *.gcda ./syzygy/*.gcda *.gcno ./syzygy/*.gcno + @rm -f bench.txt *.gcda *.gcno @rm -f stockfish.profdata *.profraw default: @@ -536,7 +540,7 @@ icc-profile-use: all .depend: - -@$(CXX) $(DEPENDFLAGS) -MM $(OBJS:.o=.cpp) > $@ 2> /dev/null + -@$(CXX) $(DEPENDFLAGS) -MM $(SRCS) > $@ 2> /dev/null -include .depend From 8a1de2655ce9790d5f0360e2baefb0f5c0fe2944 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 9 May 2020 19:45:07 +0200 Subject: [PATCH 0209/1766] Use posix_memalign instead of aligned_alloc should be a little more portable to older linux systems (before glibc-2.16). fixes https://github.com/official-stockfish/Stockfish/issues/2665 closes https://github.com/official-stockfish/Stockfish/pull/2668 No functional change. --- src/misc.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index 4d6483e73d2..946810088da 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -303,7 +303,8 @@ void* aligned_ttmem_alloc(size_t allocSize, void*& mem) { constexpr size_t alignment = 2 * 1024 * 1024; // assumed 2MB page sizes size_t size = ((allocSize + alignment - 1) / alignment) * alignment; // multiple of alignment - mem = aligned_alloc(alignment, size); + if (posix_memalign(&mem, alignment, size)) + mem = nullptr; madvise(mem, allocSize, MADV_HUGEPAGE); return mem; } From 66ed8b6c479932f1ec2274b5f567b5a6aecae0a4 Mon Sep 17 00:00:00 2001 From: Moez Jellouli <37274752+MJZ1977@users.noreply.github.com> Date: Fri, 8 May 2020 16:59:06 +0200 Subject: [PATCH 0210/1766] Tune pawn value Small tune of PawnValue parameters -4 / -7 with "closedpos.epd" opening book. STC: LLR: 2.96 (-2.94,2.94) {-0.50,1.50} Total: 58776 W: 11787 L: 11511 D: 35478 Ptnml(0-2): 975, 6876, 13443, 7086, 1008 https://tests.stockfishchess.org/tests/view/5eb5aa712326444a3b6d3e33 LTC: LLR: 2.98 (-2.94,2.94) {0.25,1.75} Total: 137544 W: 19687 L: 19115 D: 98742 Ptnml(0-2): 988, 13219, 39901, 13561, 1103 https://tests.stockfishchess.org/tests/view/5eb67a392326444a3b6d3e9a Non regression STC with "noob_3moves.epd" opening book LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 98168 W: 18545 L: 18499 D: 61124 Ptnml(0-2): 1647, 11396, 22951, 11444, 1646 https://tests.stockfishchess.org/tests/view/5eb7e489e0300e8e8c896203 closes https://github.com/official-stockfish/Stockfish/pull/2670 Bench 4696646 --- src/types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types.h b/src/types.h index 7b896803bfc..580c846a10d 100644 --- a/src/types.h +++ b/src/types.h @@ -181,7 +181,7 @@ enum Value : int { VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY, VALUE_MATED_IN_MAX_PLY = -VALUE_MATE_IN_MAX_PLY, - PawnValueMg = 128, PawnValueEg = 213, + PawnValueMg = 124, PawnValueEg = 206, KnightValueMg = 781, KnightValueEg = 854, BishopValueMg = 825, BishopValueEg = 915, RookValueMg = 1276, RookValueEg = 1380, From 86ee4eb84d54dac3f9de5b455ba41909c7722173 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Tue, 12 May 2020 21:41:55 +0200 Subject: [PATCH 0211/1766] Use a trivially copyable struct for TBTables::Entry instead of a tuple. fixes https://github.com/official-stockfish/Stockfish/issues/2673 which is a warning issued by recent gcc (10.1) closes https://github.com/official-stockfish/Stockfish/pull/2674 No functional change --- AUTHORS | 1 + src/syzygy/tbprobe.cpp | 27 +++++++++++++++++++-------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/AUTHORS b/AUTHORS index 9fceebf7ee4..36c2a47b8ad 100644 --- a/AUTHORS +++ b/AUTHORS @@ -151,6 +151,7 @@ thaspel theo77186 Tom Truscott Tom Vijlbrief (tomtor) +Tomasz Sobczyk (Sopel97) Torsten Franz (torfranz, tfranzer) Tracey Emery (basepr1me) Uri Blass (uriblass) diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index f1fd695c34a..843f049a584 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -60,7 +60,7 @@ namespace { constexpr int TBPIECES = 7; // Max number of supported pieces enum { BigEndian, LittleEndian }; -enum TBType { KEY, WDL, DTZ }; // Used as template parameter +enum TBType { WDL, DTZ }; // Used as template parameter // Each table has a set of flags: all of them refer to DTZ tables, the last one to WDL tables enum TBFlag { STM = 1, Mapped = 2, WinPlies = 4, LossPlies = 8, Wide = 16, SingleValue = 128 }; @@ -403,7 +403,18 @@ TBTable::TBTable(const TBTable& wdl) : TBTable() { // at init time, accessed at probe time. class TBTables { - typedef std::tuple*, TBTable*> Entry; + struct Entry + { + Key key; + TBTable* wdl; + TBTable* dtz; + + template + TBTable* get() const { + return (TBTable*)(Type == WDL ? (void*)wdl : (void*)dtz); + } + }; + static_assert(std::is_trivially_copyable::value, ""); static constexpr int Size = 1 << 12; // 4K table, indexed by key's 12 lsb static constexpr int Overflow = 1; // Number of elements allowed to map to the last bucket @@ -415,12 +426,12 @@ class TBTables { void insert(Key key, TBTable* wdl, TBTable* dtz) { uint32_t homeBucket = (uint32_t)key & (Size - 1); - Entry entry = std::make_tuple(key, wdl, dtz); + Entry entry{ key, wdl, dtz }; // Ensure last element is empty to avoid overflow when looking up for (uint32_t bucket = homeBucket; bucket < Size + Overflow - 1; ++bucket) { - Key otherKey = std::get(hashTable[bucket]); - if (otherKey == key || !std::get(hashTable[bucket])) { + Key otherKey = hashTable[bucket].key; + if (otherKey == key || !hashTable[bucket].get()) { hashTable[bucket] = entry; return; } @@ -429,7 +440,7 @@ class TBTables { // insert here and search for a new spot for the other element instead. uint32_t otherHomeBucket = (uint32_t)otherKey & (Size - 1); if (otherHomeBucket > homeBucket) { - swap(entry, hashTable[bucket]); + std::swap(entry, hashTable[bucket]); key = otherKey; homeBucket = otherHomeBucket; } @@ -442,8 +453,8 @@ class TBTables { template TBTable* get(Key key) { for (const Entry* entry = &hashTable[(uint32_t)key & (Size - 1)]; ; ++entry) { - if (std::get(*entry) == key || !std::get(*entry)) - return std::get(*entry); + if (entry->key == key || !entry->get()) + return entry->get(); } } From d4763424d2728fe2dfd0a6fe747666feb6a2fdbb Mon Sep 17 00:00:00 2001 From: Sami Kiminki Date: Mon, 4 May 2020 20:49:27 +0300 Subject: [PATCH 0212/1766] Add support for Windows large pages for users that set the needed privilige "Lock Pages in Memory" large pages will be automatically enabled (see Readme.md). This expert setting might improve speed, 5% - 30%, depending on the hardware, the number of threads and hash size. More for large hashes, large number of threads and NUMA. If the operating system can not allocate large pages (easier after a reboot), default allocation is used automatically. The engine log provides details. closes https://github.com/official-stockfish/Stockfish/pull/2656 fixes https://github.com/official-stockfish/Stockfish/issues/2619 No functional change --- Readme.md | 26 +++++++++++++++- src/main.cpp | 1 + src/misc.cpp | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/misc.h | 1 + src/tt.cpp | 9 +++++- 5 files changed, 120 insertions(+), 2 deletions(-) diff --git a/Readme.md b/Readme.md index a759eff665b..35ff095d679 100644 --- a/Readme.md +++ b/Readme.md @@ -42,7 +42,7 @@ Currently, Stockfish has the following UCI options: this equal to the number of CPU cores available. * #### Hash - The size of the hash table in MB. + The size of the hash table in MB. It is recommended to set Hash after setting Threads. * #### Clear Hash Clear the hash table. @@ -138,6 +138,30 @@ more compact than Nalimov tablebases, while still storing all information needed for optimal play and in addition being able to take into account the 50-move rule. +## Large Pages + +Stockfish supports large pages on Linux and Windows. Large pages make +the hash access more efficient, improving the engine speed, especially +on large hash sizes. Typical increases are 5..10% in terms of nps, but +speed increases up to 30% have been measured. The support is +automatic. Stockfish attempts to use large pages when available and +will fall back to regular memory allocation when this is not the case. + +### Support on Linux + +Large page support on Linux is obtained by the Linux kernel +transparent huge pages functionality. Typically, transparent huge pages +are already enabled and no configuration is needed. + +### Support on Windows + +The use of large pages requires "Lock Pages in Memory" privilege. See +[Enable the Lock Pages in Memory Option (Windows)](https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/enable-the-lock-pages-in-memory-option-windows) +on how to enable this privilege. Logout/login may be needed +afterwards. Due to memory fragmentation, it may not always be +possible to allocate large pages even when enabled. A reboot +might alleviate this problem. To determine whether large pages +are in use, see the engine log. ## Compiling Stockfish yourself from the sources diff --git a/src/main.cpp b/src/main.cpp index 6eeda66dff8..c7cf2c6f28f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -49,6 +49,7 @@ int main(int argc, char* argv[]) { UCI::loop(argc, argv); + TT.resize(0); Threads.set(0); return 0; } diff --git a/src/misc.cpp b/src/misc.cpp index 946810088da..b1c0feeb9e1 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -309,6 +309,69 @@ void* aligned_ttmem_alloc(size_t allocSize, void*& mem) { return mem; } +#elif defined(_WIN64) + +static void* aligned_ttmem_alloc_large_pages(size_t allocSize) { + + HANDLE hProcessToken { }; + LUID luid { }; + void* mem = nullptr; + + const size_t largePageSize = GetLargePageMinimum(); + if (!largePageSize) + return nullptr; + + // We need SeLockMemoryPrivilege, so try to enable it for the process + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken)) + return nullptr; + + if (LookupPrivilegeValue(NULL, SE_LOCK_MEMORY_NAME, &luid)) + { + TOKEN_PRIVILEGES tp { }; + TOKEN_PRIVILEGES prevTp { }; + DWORD prevTpLen = 0; + + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = luid; + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + // Try to enable SeLockMemoryPrivilege. Note that even if AdjustTokenPrivileges() succeeds, + // we still need to query GetLastError() to ensure that the privileges were actually obtained... + if (AdjustTokenPrivileges( + hProcessToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &prevTp, &prevTpLen) && + GetLastError() == ERROR_SUCCESS) + { + // round up size to full pages and allocate + allocSize = (allocSize + largePageSize - 1) & ~size_t(largePageSize - 1); + mem = VirtualAlloc( + NULL, allocSize, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); + + // privilege no longer needed, restore previous state + AdjustTokenPrivileges(hProcessToken, FALSE, &prevTp, 0, NULL, NULL); + } + } + + CloseHandle(hProcessToken); + + return mem; +} + +void* aligned_ttmem_alloc(size_t allocSize, void*& mem) { + + // try to allocate large pages + mem = aligned_ttmem_alloc_large_pages(allocSize); + if (mem) + sync_cout << "info string Hash table allocation: Windows large pages used." << sync_endl; + else + sync_cout << "info string Hash table allocation: Windows large pages not used." << sync_endl; + + // fall back to regular, page aligned, allocation if necessary + if (!mem) + mem = VirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + + return mem; +} + #else void* aligned_ttmem_alloc(size_t allocSize, void*& mem) { @@ -322,6 +385,28 @@ void* aligned_ttmem_alloc(size_t allocSize, void*& mem) { #endif +/// aligned_ttmem_free will free the previously allocated ttmem +#if defined(_WIN64) + +void aligned_ttmem_free(void* mem) { + + if (!VirtualFree(mem, 0, MEM_RELEASE)) + { + DWORD err = GetLastError(); + std::cerr << "Failed to free transposition table. Error code: 0x" << + std::hex << err << std::dec << std::endl; + exit(EXIT_FAILURE); + } +} + +#else + +void aligned_ttmem_free(void *mem) { + free(mem); +} + +#endif + namespace WinProcGroup { diff --git a/src/misc.h b/src/misc.h index e0e0e98be83..9d53c2dab12 100644 --- a/src/misc.h +++ b/src/misc.h @@ -34,6 +34,7 @@ const std::string compiler_info(); void prefetch(void* addr); void start_logger(const std::string& fname); void* aligned_ttmem_alloc(size_t size, void*& mem); +void aligned_ttmem_free(void* mem); void dbg_hit_on(bool b); void dbg_hit_on(bool c, bool b); diff --git a/src/tt.cpp b/src/tt.cpp index 7e95a2a4e6d..6ee63138d15 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -63,7 +63,14 @@ void TranspositionTable::resize(size_t mbSize) { Threads.main()->wait_for_search_finished(); - free(mem); + if (mem) + aligned_ttmem_free(mem); + + if (!mbSize) + { + mem = nullptr; + return; + } clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster); table = static_cast(aligned_ttmem_alloc(clusterCount * sizeof(Cluster), mem)); From cca643669db016f20c6e91f50892a0b44f297a48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Fri, 8 May 2020 10:32:52 +0200 Subject: [PATCH 0213/1766] Move 50 moves counter to initiative. simplify the usage of the 50 moves counter, moving it frome the scale factor to initiative. This patch was inspired by recent games where a blocked or semi-blocked position was 'blundered', by moving a pawn, into a lost endgame. This patch improves this situation, finding a more robust move more often. for example (1s searches with many threads): ``` FEN 8/p3kp2/Pp2p3/1n2PpP1/5P2/1Kp5/8/R7 b - - 68 143 master: 6 bestmove b5c7 6 bestmove e7e8 12 bestmove e7d8 176 bestmove e7d7 patch: 3 bestmove b5c7 5 bestmove e7d8 192 bestmove e7d7 ``` fixes https://github.com/official-stockfish/Stockfish/issues/2620 the patch also tests well passed STC LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 50168 W: 9508 L: 9392 D: 31268 Ptnml(0-2): 818, 5873, 11616, 5929, 848 https://tests.stockfishchess.org/tests/view/5ebb07287dd5693aad4e680b passed LTC LLR: 2.93 (-2.94,2.94) {-1.50,0.50} Total: 7520 W: 981 L: 870 D: 5669 Ptnml(0-2): 49, 647, 2256, 760, 48 https://tests.stockfishchess.org/tests/view/5ebbff747dd5693aad4e6858 closes https://github.com/official-stockfish/Stockfish/pull/2666 Bench: 4395562 --- src/evaluate.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index e663f21f449..d972db5a1cc 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -739,6 +739,7 @@ namespace { + 24 * infiltration + 51 * !pos.non_pawn_material() - 43 * almostUnwinnable + - 2 * pos.rule50_count() -110 ; Value mg = mg_value(score); @@ -778,8 +779,6 @@ namespace { } else sf = std::min(sf, 36 + 7 * pos.count(strongSide)); - - sf = std::max(0, sf - (pos.rule50_count() - 12) / 4); } return ScaleFactor(sf); @@ -856,7 +855,8 @@ namespace { Trace::add(TOTAL, score); } - return (pos.side_to_move() == WHITE ? v : -v) + Tempo; // Side to move point of view + // Side to move point of view + return (pos.side_to_move() == WHITE ? v : -v) + Tempo; } } // namespace From beb327f910ed782f358d69201643ccd99b982a48 Mon Sep 17 00:00:00 2001 From: Sami Kiminki Date: Thu, 14 May 2020 12:00:35 +0300 Subject: [PATCH 0214/1766] Fix a Windows-only crash on exit without 'quit' There was a bug in commit d4763424d2728fe2dfd0a6fe747666feb6a2fdbb (Add support for Windows large pages) that could result in trying to free memory allocated with VirtualAlloc incorrectly with free(). Fix this by reverting the TT.resize(0) logic in the previous commit, and instead, just call aligned_ttmem_free() in TranspositionTable::~TranspositionTable(). fixes https://github.com/official-stockfish/Stockfish/issues/2677 closes https://github.com/official-stockfish/Stockfish/pull/2679 No functional change --- src/main.cpp | 1 - src/misc.cpp | 2 +- src/misc.h | 2 +- src/tt.cpp | 9 +-------- src/tt.h | 2 +- 5 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index c7cf2c6f28f..6eeda66dff8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -49,7 +49,6 @@ int main(int argc, char* argv[]) { UCI::loop(argc, argv); - TT.resize(0); Threads.set(0); return 0; } diff --git a/src/misc.cpp b/src/misc.cpp index b1c0feeb9e1..e0cc6ed5977 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -390,7 +390,7 @@ void* aligned_ttmem_alloc(size_t allocSize, void*& mem) { void aligned_ttmem_free(void* mem) { - if (!VirtualFree(mem, 0, MEM_RELEASE)) + if (mem && !VirtualFree(mem, 0, MEM_RELEASE)) { DWORD err = GetLastError(); std::cerr << "Failed to free transposition table. Error code: 0x" << diff --git a/src/misc.h b/src/misc.h index 9d53c2dab12..05bfc7de4d5 100644 --- a/src/misc.h +++ b/src/misc.h @@ -34,7 +34,7 @@ const std::string compiler_info(); void prefetch(void* addr); void start_logger(const std::string& fname); void* aligned_ttmem_alloc(size_t size, void*& mem); -void aligned_ttmem_free(void* mem); +void aligned_ttmem_free(void* mem); // nop if mem == nullptr void dbg_hit_on(bool b); void dbg_hit_on(bool c, bool b); diff --git a/src/tt.cpp b/src/tt.cpp index 6ee63138d15..4e06bed9345 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -63,14 +63,7 @@ void TranspositionTable::resize(size_t mbSize) { Threads.main()->wait_for_search_finished(); - if (mem) - aligned_ttmem_free(mem); - - if (!mbSize) - { - mem = nullptr; - return; - } + aligned_ttmem_free(mem); clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster); table = static_cast(aligned_ttmem_alloc(clusterCount * sizeof(Cluster), mem)); diff --git a/src/tt.h b/src/tt.h index 8b70f797876..bd723a86790 100644 --- a/src/tt.h +++ b/src/tt.h @@ -75,7 +75,7 @@ class TranspositionTable { static_assert(sizeof(Cluster) == 32, "Unexpected Cluster size"); public: - ~TranspositionTable() { free(mem); } + ~TranspositionTable() { aligned_ttmem_free(mem); } void new_search() { generation8 += 8; } // Lower 3 bits are used by PV flag and Bound TTEntry* probe(const Key key, bool& found) const; int hashfull() const; From c6ce612f0ada9b5f0d9530128545d1ee7d58b3e5 Mon Sep 17 00:00:00 2001 From: protonspring Date: Wed, 13 May 2020 11:52:41 -0600 Subject: [PATCH 0215/1766] Simplify Time Management This is a functional simplification of the time management system. With this patch, there is a simple equation for each of two distinct time controls: basetime + increment, and x moves in y seconds (+increment). These equations are easy to plot and understand making future modifications or adding additional time controls much easier. SlowMover is reset to 100 so that is has no effect unless a user changes it. There are two scaling variables: * Opt_scale is a scale factor (or percentage) of time to use for this current move. * Max_scale is a scale factor to apply to the resulting optimumTime. There seems to be some elo gain in most scenarios. Better performance is attributable to one of two things: * minThinkingTime was not allowing reasonable time calculations for very short games like 10+0 or 10+0.01. This is because adding almost no increment and substracting move overhead for 50 moves quickly results in almost 0 time very early in the game. Master depended on minThinkingTime to handle these short games instead of good time management. This patch addresses this issue by lowering minThinkingTime to 0 and adjusting moverOverhead if there are very low increments. * Notice that the time distribution curves tail downward for the first 10 moves or so. This causes less time to attribute for very early moves leaving more time available for middle moves where more important decisions happen. Here is a summary of tests for this version at different time controls: SMP 5+0.05 LLR: 2.97 (-2.94,2.94) {-1.50,0.50} Total: 46544 W: 7175 L: 7089 D: 32280 Ptnml(0-2): 508, 4826, 12517, 4914, 507 https://tests.stockfishchess.org/tests/user/protonspring STC LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 20480 W: 3872 L: 3718 D: 12890 Ptnml(0-2): 295, 2364, 4824, 2406, 351 https://tests.stockfishchess.org/tests/view/5ebc343e7dd5693aad4e6873 STC, sudden death LLR: 2.93 (-2.94,2.94) {-1.50,0.50} Total: 7024 W: 1706 L: 1489 D: 3829 Ptnml(0-2): 149, 813, 1417, 938, 195 https://tests.stockfishchess.org/tests/view/5ebc346f7dd5693aad4e6875 STC, TCEC style LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 4192 W: 1014 L: 811 D: 2367 Ptnml(0-2): 66, 446, 912, 563, 109 https://tests.stockfishchess.org/tests/view/5ebc34857dd5693aad4e6877 40/10 LLR: 2.93 (-2.94,2.94) {-1.50,0.50} Total: 54032 W: 10592 L: 10480 D: 32960 Ptnml(0-2): 967, 6148, 12677, 6254, 970 https://tests.stockfishchess.org/tests/view/5ebc50597dd5693aad4e688d LTC, sudden death LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 9152 W: 1391 L: 1263 D: 6498 Ptnml(0-2): 75, 888, 2526, 1008, 79 https://tests.stockfishchess.org/tests/view/5ebc6f5c7dd5693aad4e689b LTC LLR: 2.98 (-2.94,2.94) {-1.50,0.50} Total: 12344 W: 1563 L: 1459 D: 9322 Ptnml(0-2): 70, 1103, 3740, 1171, 88 https://tests.stockfishchess.org/tests/view/5ebc6f4c7dd5693aad4e6899 closes https://github.com/official-stockfish/Stockfish/pull/2678 Bench: 4395562 --- src/timeman.cpp | 107 ++++++++++++++++------------------------------ src/ucioption.cpp | 4 +- 2 files changed, 40 insertions(+), 71 deletions(-) diff --git a/src/timeman.cpp b/src/timeman.cpp index 0848be420c0..f794ab13332 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -28,58 +28,10 @@ TimeManagement Time; // Our global time management object -namespace { - - enum TimeType { OptimumTime, MaxTime }; - - constexpr int MoveHorizon = 50; // Plan time management at most this many moves ahead - constexpr double MaxRatio = 7.3; // When in trouble, we can step over reserved time with this ratio - constexpr double StealRatio = 0.34; // However we must not steal time from remaining moves over this ratio - - - // move_importance() is a skew-logistic function based on naive statistical - // analysis of "how many games are still undecided after n half-moves". Game - // is considered "undecided" as long as neither side has >275cp advantage. - // Data was extracted from the CCRL game database with some simple filtering criteria. - - double move_importance(int ply) { - - constexpr double XScale = 6.85; - constexpr double XShift = 64.5; - constexpr double Skew = 0.171; - - return pow((1 + exp((ply - XShift) / XScale)), -Skew) + DBL_MIN; // Ensure non-zero - } - - template - TimePoint remaining(TimePoint myTime, int movesToGo, int ply, TimePoint slowMover) { - - constexpr double TMaxRatio = (T == OptimumTime ? 1.0 : MaxRatio); - constexpr double TStealRatio = (T == OptimumTime ? 0.0 : StealRatio); - - double moveImportance = (move_importance(ply) * slowMover) / 100.0; - double otherMovesImportance = 0.0; - - for (int i = 1; i < movesToGo; ++i) - otherMovesImportance += move_importance(ply + 2 * i); - - double ratio1 = (TMaxRatio * moveImportance) / (TMaxRatio * moveImportance + otherMovesImportance); - double ratio2 = (moveImportance + TStealRatio * otherMovesImportance) / (moveImportance + otherMovesImportance); - - return TimePoint(myTime * std::min(ratio1, ratio2)); // Intel C++ asks for an explicit cast - } - -} // namespace - - -/// init() is called at the beginning of the search and calculates the allowed -/// thinking time out of the time control and current game ply. We support four -/// different kinds of time controls, passed in 'limits': -/// -/// inc == 0 && movestogo == 0 means: x basetime [sudden death!] -/// inc == 0 && movestogo != 0 means: x moves in y minutes -/// inc > 0 && movestogo == 0 means: x basetime + z increment -/// inc > 0 && movestogo != 0 means: x moves in y minutes + z increment +/// init() is called at the beginning of the search and calculates the bounds +/// of time allowed for the current game ply. We currently support: +// 1) x basetime (+z increment) +// 2) x moves in y seconds (+z increment) void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { @@ -87,7 +39,10 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { TimePoint moveOverhead = Options["Move Overhead"]; TimePoint slowMover = Options["Slow Mover"]; TimePoint npmsec = Options["nodestime"]; - TimePoint hypMyTime; + + // opt_scale is a percentage of available time to use for the current move. + // max_scale is a multiplier applied to optimumTime. + double opt_scale, max_scale; // If we have to play in 'nodes as time' mode, then convert from time // to nodes, and use resulting values in time management formulas. @@ -105,29 +60,43 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { } startTime = limits.startTime; - optimumTime = maximumTime = std::max(limits.time[us], minThinkingTime); - const int maxMTG = limits.movestogo ? std::min(limits.movestogo, MoveHorizon) : MoveHorizon; + //Maximum move horizon of 50 moves + int mtg = limits.movestogo ? std::min(limits.movestogo, 50) : 50; - // We calculate optimum time usage for different hypothetical "moves to go" values - // and choose the minimum of calculated search time values. Usually the greatest - // hypMTG gives the minimum values. - for (int hypMTG = 1; hypMTG <= maxMTG; ++hypMTG) - { - // Calculate thinking time for hypothetical "moves to go"-value - hypMyTime = limits.time[us] - + limits.inc[us] * (hypMTG - 1) - - moveOverhead * (2 + std::min(hypMTG, 40)); + // Adjust moveOverhead if there are tiny increments + moveOverhead = std::max(10, std::min(limits.inc[us] / 2, moveOverhead)); + + // Make sure timeLeft is > 0 since we may use it as a divisor + TimePoint timeLeft = std::max(TimePoint(1), + limits.time[us] + limits.inc[us] * (mtg - 1) - moveOverhead * (2 + mtg)); - hypMyTime = std::max(hypMyTime, TimePoint(0)); + // A user may scale time usage by setting UCI option "Slow Mover" + // Default is 100 and changing this value will probably lose elo. + timeLeft = slowMover * timeLeft / 100; - TimePoint t1 = minThinkingTime + remaining(hypMyTime, hypMTG, ply, slowMover); - TimePoint t2 = minThinkingTime + remaining(hypMyTime, hypMTG, ply, slowMover); + // x basetime (+ z increment) + // If there is a healthy increment, timeLeft can exceed actual available + // game time for the current move, so also cap to 20% of available game time. + if (limits.movestogo == 0) + { + opt_scale = std::min(0.007 + std::pow(ply + 3.0, 0.5) / 250.0, + 0.2 * limits.time[us] / double(timeLeft)); + max_scale = 4 + std::pow(ply + 3, 0.3); + } - optimumTime = std::min(t1, optimumTime); - maximumTime = std::min(t2, maximumTime); + // x moves in y seconds (+ z increment) + else + { + opt_scale = std::min((0.8 + ply / 128.0) / mtg, + 0.8 * limits.time[us] / double(timeLeft)); + max_scale = std::min(6.3, 1.5 + 0.11 * mtg); } + // Never use more than 80% of the available time for this move + optimumTime = std::max(minThinkingTime, opt_scale * timeLeft); + maximumTime = std::min(0.8 * limits.time[us] - moveOverhead, max_scale * optimumTime); + if (Options["Ponder"]) optimumTime += optimumTime / 4; } diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 26fcf30227a..ad576fda2d1 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -69,8 +69,8 @@ void init(OptionsMap& o) { o["MultiPV"] << Option(1, 1, 500); o["Skill Level"] << Option(20, 0, 20); o["Move Overhead"] << Option(30, 0, 5000); - o["Minimum Thinking Time"] << Option(20, 0, 5000); - o["Slow Mover"] << Option(84, 10, 1000); + o["Minimum Thinking Time"] << Option( 0, 0, 5000); + o["Slow Mover"] << Option(100, 10, 1000); o["nodestime"] << Option(0, 0, 10000); o["UCI_Chess960"] << Option(false); o["UCI_AnalyseMode"] << Option(false); From d116e27f0f6c89c887420890ffe61c6708ef5c08 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 15 May 2020 19:37:56 +0200 Subject: [PATCH 0216/1766] Workaround for older compiler gcc < 5 doesn't fully support the c++11 `std::is_trivially_copyable::value` Remove it, as it is not essential. fixes https://github.com/official-stockfish/Stockfish/issues/2681 closes https://github.com/official-stockfish/Stockfish/pull/2682 No functional change. --- src/syzygy/tbprobe.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 843f049a584..adc45d58557 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -414,7 +414,6 @@ class TBTables { return (TBTable*)(Type == WDL ? (void*)wdl : (void*)dtz); } }; - static_assert(std::is_trivially_copyable::value, ""); static constexpr int Size = 1 << 12; // 4K table, indexed by key's 12 lsb static constexpr int Overflow = 1; // Number of elements allowed to map to the last bucket From 83c9e5911ef7fe6ff71dc116856fac85bb9076eb Mon Sep 17 00:00:00 2001 From: protonspring Date: Fri, 15 May 2020 17:23:49 -0600 Subject: [PATCH 0217/1766] Don't adjust MoveOverhead by increment This is a change to address a potential timing issue for slow networks. Move Overhead was limited by TC increment, which might be problematic if small increments (or sudden death) on slow networks (needing high Move Overhead) are used. STC, sudden death. LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 169368 W: 38023 L: 38054 D: 93291 Ptnml(0-2): 3767, 20250, 36595, 20391, 3681 https://tests.stockfishchess.org/tests/view/5ebf25efe9d85f94dc42986f STC, 10+0.1 LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 83896 W: 16092 L: 16026 D: 51778 Ptnml(0-2): 1401, 9697, 19670, 9795, 1385 https://tests.stockfishchess.org/tests/view/5ec0239de9d85f94dc42991e closes https://github.com/official-stockfish/Stockfish/pull/2684 No functional change. --- src/timeman.cpp | 3 --- src/ucioption.cpp | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/timeman.cpp b/src/timeman.cpp index f794ab13332..0021e96b7b7 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -64,9 +64,6 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { //Maximum move horizon of 50 moves int mtg = limits.movestogo ? std::min(limits.movestogo, 50) : 50; - // Adjust moveOverhead if there are tiny increments - moveOverhead = std::max(10, std::min(limits.inc[us] / 2, moveOverhead)); - // Make sure timeLeft is > 0 since we may use it as a divisor TimePoint timeLeft = std::max(TimePoint(1), limits.time[us] + limits.inc[us] * (mtg - 1) - moveOverhead * (2 + mtg)); diff --git a/src/ucioption.cpp b/src/ucioption.cpp index ad576fda2d1..66fd42d1e27 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -68,7 +68,7 @@ void init(OptionsMap& o) { o["Ponder"] << Option(false); o["MultiPV"] << Option(1, 1, 500); o["Skill Level"] << Option(20, 0, 20); - o["Move Overhead"] << Option(30, 0, 5000); + o["Move Overhead"] << Option(10, 0, 5000); o["Minimum Thinking Time"] << Option( 0, 0, 5000); o["Slow Mover"] << Option(100, 10, 1000); o["nodestime"] << Option(0, 0, 10000); From dd1adce7488b20b4125946077bcbbf665b9797f7 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Sun, 17 May 2020 20:46:25 +0100 Subject: [PATCH 0218/1766] Increase base time use and limit max used. This change increases the base part of optimumTime at all depths. It also reduces the size of max_scale and thus maximumTime by using a linear scale instead of pow(x, 0.3) and by limiting max_scale to no more than 7 (previously as high as 8 or 9 at very high depths). Tested using the closedpos book: STC 10+0.1: LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 83696 W: 16813 L: 16508 D: 50375 Ptnml(0-2): 1315, 9649, 19686, 9812, 1386 https://tests.stockfishchess.org/tests/view/5ebfa92de9d85f94dc42989b LTC 60+0.6: LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 39384 W: 5868 L: 5582 D: 27934 Ptnml(0-2): 276, 3697, 11489, 3925, 305 https://tests.stockfishchess.org/tests/view/5ec0a6dce9d85f94dc42995a Test for non-regression: STC Sudden Death 10+0 : LLR: 2.94 (-2.94,2.94) {-2.00,0.00} Total: 111976 W: 25661 L: 25768 D: 60547 Ptnml(0-2): 2567, 13420, 24118, 13319, 2564 https://tests.stockfishchess.org/tests/view/5ec23b3be9d85f94dc429a58 closes https://github.com/official-stockfish/Stockfish/pull/2685 Bench 4395562 --- src/timeman.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/timeman.cpp b/src/timeman.cpp index 0021e96b7b7..45e9db5863e 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -77,9 +77,9 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { // game time for the current move, so also cap to 20% of available game time. if (limits.movestogo == 0) { - opt_scale = std::min(0.007 + std::pow(ply + 3.0, 0.5) / 250.0, + opt_scale = std::min(0.008 + std::pow(ply + 3.0, 0.5) / 250.0, 0.2 * limits.time[us] / double(timeLeft)); - max_scale = 4 + std::pow(ply + 3, 0.3); + max_scale = 4 + std::min(36, ply) / 12.0; } // x moves in y seconds (+ z increment) From b36a1fa1b4ffded06aba53e1003b40827c39803c Mon Sep 17 00:00:00 2001 From: Sami Kiminki Date: Tue, 19 May 2020 12:08:01 +0300 Subject: [PATCH 0219/1766] Avoid sending info strings before 'uci' has been received Do not send the following info string on the first call to aligned_ttmem_alloc() on Windows: info string Hash table allocation: Windows large pages [not] used. The first call occurs before the 'uci' command has been received. This confuses some GUIs, which expect the first engine-sent command to be 'id' as the response to the 'uci' command. (see https://github.com/official-stockfish/Stockfish/issues/2681) closes https://github.com/official-stockfish/Stockfish/pull/2689 No functional change. --- src/misc.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index e0cc6ed5977..c625478462e 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -358,12 +358,21 @@ static void* aligned_ttmem_alloc_large_pages(size_t allocSize) { void* aligned_ttmem_alloc(size_t allocSize, void*& mem) { + static bool firstCall = true; + // try to allocate large pages mem = aligned_ttmem_alloc_large_pages(allocSize); - if (mem) - sync_cout << "info string Hash table allocation: Windows large pages used." << sync_endl; - else - sync_cout << "info string Hash table allocation: Windows large pages not used." << sync_endl; + + // Suppress info strings on the first call. The first call occurs before 'uci' + // is received and in that case this output confuses some GUIs. + if (!firstCall) + { + if (mem) + sync_cout << "info string Hash table allocation: Windows large pages used." << sync_endl; + else + sync_cout << "info string Hash table allocation: Windows large pages not used." << sync_endl; + } + firstCall = false; // fall back to regular, page aligned, allocation if necessary if (!mem) From 20ceeac8b3a3bd13a64d6a224ef190d6cdd94f63 Mon Sep 17 00:00:00 2001 From: protonspring Date: Wed, 20 May 2020 08:33:59 -0600 Subject: [PATCH 0220/1766] Simplify evaluation for blocked passers. This is a functional simplification of the evaluation code for blocked passers. I've also changed a few variable names for clarity. STC LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 141984 W: 27450 L: 27466 D: 87068 Ptnml(0-2): 2414, 16511, 33175, 16461, 2431 https://tests.stockfishchess.org/tests/view/5ec4001b05aa4bc72d9759e7 LTC LLR: 2.93 (-2.94,2.94) {-1.50,0.50} Total: 30536 W: 3966 L: 3885 D: 22685 Ptnml(0-2): 216, 2841, 9073, 2922, 216 https://tests.stockfishchess.org/tests/view/5ec4bd0d377121ac09e101b7 Closes https://github.com/official-stockfish/Stockfish/pull/2690 Bench: 4704681 --- src/evaluate.cpp | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d972db5a1cc..d04d724ae5b 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -591,25 +591,22 @@ namespace { return std::min(distance(pos.square(c), s), 5); }; - Bitboard b, bb, squaresToQueen, unsafeSquares, candidatePassers, leverable; + Bitboard b, bb, squaresToQueen, unsafeSquares, blockedPassers, helpers; Score score = SCORE_ZERO; b = pe->passed_pawns(Us); - candidatePassers = b & shift(pos.pieces(Them, PAWN)); - if (candidatePassers) + blockedPassers = b & shift(pos.pieces(Them, PAWN)); + if (blockedPassers) { - // Can we lever the blocker of a candidate passer? - leverable = shift(pos.pieces(Us, PAWN)) - & ~pos.pieces(Them) - & (~attackedBy2[Them] | attackedBy[Us][ALL_PIECES]) - & (~(attackedBy[Them][KNIGHT] | attackedBy[Them][BISHOP]) - | (attackedBy[Us ][KNIGHT] | attackedBy[Us ][BISHOP])); - - // Remove candidate otherwise - b &= ~candidatePassers - | shift(leverable) - | shift(leverable); + helpers = shift(pos.pieces(Us, PAWN)) + & ~pos.pieces(Them) + & (~attackedBy2[Them] | attackedBy[Us][ALL_PIECES]); + + // Remove blocked candidate passers that don't have help to pass + b &= ~blockedPassers + | shift(helpers) + | shift(helpers); } while (b) From 6c1af710d16c6a358cc09c51a133120605c39e44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Wed, 20 May 2020 17:06:42 +0200 Subject: [PATCH 0221/1766] A combo of parameters tweaks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch is a combinaison of two recent parameters tweaks which had failed narrowly (yellow) at long time control: • improvement in move ordering during search by softening the distinction between bad captures and good captures during move generation, leading to improved awareness of Stockfish of potential piece sacrifices (idea by Rahul Dsilva) • increase in the weight of pawns in the "initiative" part of the evaluation function. With this change Stockfish should have more incentive to exchange pawns when losing, and to keep pawns when winning. STC: LLR: 2.93 (-2.94,2.94) {-0.50,1.50} Total: 10704 W: 2178 L: 1974 D: 6552 Ptnml(0-2): 168, 1185, 2464, 1345, 190 https://tests.stockfishchess.org/tests/view/5ec5553b377121ac09e1023d LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 60592 W: 7835 L: 7494 D: 45263 Ptnml(0-2): 430, 5514, 18086, 5817, 449 https://tests.stockfishchess.org/tests/view/5ec55ca2377121ac09e10249 Closes https://github.com/official-stockfish/Stockfish/pull/2691 Bench: 4519117 --- src/evaluate.cpp | 2 +- src/movepick.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d04d724ae5b..449cf6d7463 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -730,7 +730,7 @@ namespace { // Compute the initiative bonus for the attacking side int complexity = 9 * pe->passed_count() - + 11 * pos.count() + + 12 * pos.count() + 9 * outflanking + 21 * pawnsOnBothFlanks + 24 * infiltration diff --git a/src/movepick.cpp b/src/movepick.cpp index b1e10587cf4..e26f42ef98c 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -169,7 +169,7 @@ Move MovePicker::next_move(bool skipQuiets) { case GOOD_CAPTURE: if (select([&](){ - return pos.see_ge(*cur, Value(-55 * cur->value / 1024)) ? + return pos.see_ge(*cur, Value(-69 * cur->value / 1024)) ? // Move losing capture to endBadCaptures to be tried later true : (*endBadCaptures++ = *cur, false); })) return *(cur - 1); From 09c6917d053582267a2960e8c375883e0d9461da Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Thu, 21 May 2020 12:29:36 +0200 Subject: [PATCH 0222/1766] Tweak knight mobility New tuned values for knight mobility in endgames. STC: LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 112576 W: 21999 L: 21644 D: 68933 Ptnml(0-2): 2009, 13084, 25735, 13463, 1997 https://tests.stockfishchess.org/tests/view/5ec58379377121ac09e10272 LTC: LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 125192 W: 16200 L: 15671 D: 93321 Ptnml(0-2): 891, 11584, 37182, 11983, 956 https://tests.stockfishchess.org/tests/view/5ec5c0b8377121ac09e1028b Closes https://github.com/official-stockfish/Stockfish/pull/2693 Bench: 4778956 --- src/evaluate.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 449cf6d7463..e80e94427ea 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -91,8 +91,8 @@ namespace { // MobilityBonus[PieceType-2][attacked] contains bonuses for middle and end game, // indexed by piece type and number of attacked squares in the mobility area. constexpr Score MobilityBonus[][32] = { - { S(-62,-81), S(-53,-56), S(-12,-30), S( -4,-14), S( 3, 8), S( 13, 15), // Knight - S( 22, 23), S( 28, 27), S( 33, 33) }, + { S(-62,-81), S(-53,-56), S(-12,-31), S( -4,-16), S( 3, 5), S( 13, 11), // Knight + S( 22, 17), S( 28, 20), S( 33, 25) }, { S(-48,-59), S(-20,-23), S( 16, -3), S( 26, 13), S( 38, 24), S( 51, 42), // Bishop S( 55, 54), S( 63, 57), S( 63, 65), S( 68, 73), S( 81, 78), S( 81, 86), S( 91, 88), S( 98, 97) }, From cdf5cfdb92b4ac7df8c2c3d891797787081c1ca2 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Fri, 22 May 2020 11:08:44 +0200 Subject: [PATCH 0223/1766] Add doubled isolated pawn penalty. This patch gives an additional penalty if a doubled isolated pawn is stopped only by a single opponent pawn on the same file. Thanks to NKONSTANTAKIS, who shared this idea on the forum! https://groups.google.com/forum/?fromgroups=#!topic/fishcooking/vC4Qn-PMlS4. STC: LLR: 2.96 (-2.94,2.94) {-0.50,1.50} Total: 84872 W: 16688 L: 16370 D: 51814 Ptnml(0-2): 1507, 9940, 19274, 10158, 1557 https://tests.stockfishchess.org/tests/view/5ec65bd955202b947dc5d4ac LTC: LLR: 2.93 (-2.94,2.94) {0.25,1.75} Total: 58104 W: 7614 L: 7278 D: 43212 Ptnml(0-2): 411, 5369, 17196, 5625, 451 https://tests.stockfishchess.org/tests/view/5ec6e9f2c23f5b0710632b19 Closes https://github.com/official-stockfish/Stockfish/pull/2694 Bench: 5148950 --- src/pawns.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index 7b266e779cc..c20cb5293c0 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -32,12 +32,13 @@ namespace { #define S(mg, eg) make_score(mg, eg) // 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); - constexpr Score WeakUnopposed = S(13, 27); + constexpr Score Backward = S( 9, 24); + constexpr Score BlockedStorm = S(82, 82); + constexpr Score Doubled = S(11, 56); + constexpr Score DoubledIsolated = S(15, 57); + constexpr Score Isolated = S( 5, 15); + constexpr Score WeakLever = S( 0, 56); + constexpr Score WeakUnopposed = S(13, 27); // Connected pawn bonus constexpr int Connected[RANK_NB] = { 0, 7, 8, 12, 29, 48, 86 }; @@ -144,9 +145,16 @@ namespace { } else if (!neighbours) + { score -= Isolated + WeakUnopposed * !opposed; + if ( (ourPawns & forward_file_bb(Them, s)) + && popcount(opposed) == 1 + && !(theirPawns & adjacent_files_bb(s))) + score -= DoubledIsolated; + } + else if (backward) score -= Backward + WeakUnopposed * !opposed; From 669b5d83ef1d930c80854236f324de2fdcecf57c Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 21 May 2020 08:25:37 +0200 Subject: [PATCH 0224/1766] Improve CI testing also enable CXXFLAGS="-D_GLIBCXX_DEBUG" in CI. closes https://github.com/official-stockfish/Stockfish/pull/2692 No functional change. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e2b42e6d5ed..e2ae61bef9a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,7 +50,7 @@ script: - echo "Reference bench:" $benchref # # Verify bench number against various builds - - export CXXFLAGS=-Werror + - export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" - make clean && make -j2 ARCH=x86-64 optimize=no debug=yes build && ../tests/signature.sh $benchref - 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 From d940e59dad51e78d5146bb21c8f792379df64816 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Thu, 21 May 2020 21:17:21 +0100 Subject: [PATCH 0225/1766] Keep low ply history from previous move This patch keeps the low-ply history from the previous move, shifting the data down by 2 ply. Tested with closedpos book: STC: LLR: 2.93 (-2.94,2.94) {-0.50,1.50} Total: 71584 W: 14175 L: 13891 D: 43518 Ptnml(0-2): 1069, 8228, 16993, 8354, 1148 https://tests.stockfishchess.org/tests/view/5ec0eaafe9d85f94dc429974 LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 96552 W: 13946 L: 13498 D: 69108 Ptnml(0-2): 676, 9082, 28375, 9404, 739 https://tests.stockfishchess.org/tests/view/5ec145efe9d85f94dc4299b0 closes https://github.com/official-stockfish/Stockfish/pull/2688 Bench 5148950 --- src/search.cpp | 3 +++ src/thread.cpp | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 7f29f771c0a..5e9cd463b06 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -357,6 +357,9 @@ void Thread::search() { mainThread->iterValue[i] = mainThread->bestPreviousScore; } + std::copy(&lowPlyHistory[2][0], &lowPlyHistory.back().back() + 1, &lowPlyHistory[0][0]); + std::fill(&lowPlyHistory[MAX_LPH - 2][0], &lowPlyHistory.back().back() + 1, 0); + size_t multiPV = Options["MultiPV"]; // Pick integer skill levels, but non-deterministically round up or down diff --git a/src/thread.cpp b/src/thread.cpp index 88331f0674e..2e0c216ee19 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -212,7 +212,6 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states, th->rootDepth = th->completedDepth = 0; th->rootMoves = rootMoves; th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th); - th->lowPlyHistory.fill(0); } setupStates->back() = tmp; From 383b12e1a5cc03a122e9a071eebde87eac85b116 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 23 May 2020 13:26:13 +0200 Subject: [PATCH 0226/1766] small cleanups closes https://github.com/official-stockfish/Stockfish/pull/2653 No functional change --- src/bitboard.cpp | 2 +- src/evaluate.cpp | 2 +- src/main.cpp | 2 +- src/movegen.cpp | 44 ++++++++++++++++++++++++++++-------------- src/pawns.cpp | 5 ++--- src/position.cpp | 2 +- src/position.h | 7 +++---- src/search.cpp | 14 +++++++------- src/syzygy/tbprobe.cpp | 6 +++--- src/thread.cpp | 2 +- src/timeman.cpp | 12 ++++++------ src/tt.cpp | 6 +++--- src/tune.cpp | 6 +++--- src/ucioption.cpp | 4 ++-- 14 files changed, 64 insertions(+), 50 deletions(-) diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 69bbc77bf30..f650eef6862 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -69,7 +69,7 @@ const std::string Bitboards::pretty(Bitboard b) { void Bitboards::init() { for (unsigned i = 0; i < (1 << 16); ++i) - PopCnt16[i] = std::bitset<16>(i).count(); + PopCnt16[i] = uint8_t(std::bitset<16>(i).count()); for (Square s = SQ_A1; s <= SQ_H8; ++s) SquareBB[s] = (1ULL << s); diff --git a/src/evaluate.cpp b/src/evaluate.cpp index e80e94427ea..2d1f4b9e9e1 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -311,7 +311,7 @@ namespace { if (Pt == BISHOP) { - // Penalty according to number of pawns on the same color square as the + // Penalty according to the number of our pawns on the same color square as the // bishop, bigger when the center files are blocked with pawns and smaller // when the bishop is outside the pawn chain. Bitboard blocked = pos.pieces(Us, PAWN) & shift(pos.pieces()); diff --git a/src/main.cpp b/src/main.cpp index 6eeda66dff8..fafefee2bc0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -44,7 +44,7 @@ int main(int argc, char* argv[]) { Position::init(); Bitbases::init(); Endgames::init(); - Threads.set(Options["Threads"]); + Threads.set(size_t(Options["Threads"])); Search::clear(); // After threads are up UCI::loop(argc, argv); diff --git a/src/movegen.cpp b/src/movegen.cpp index a3abcde8d1e..5787d1743cf 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -213,8 +213,31 @@ namespace { template - ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target) { + ExtMove* generate_all(const Position& pos, ExtMove* moveList) { constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantations + Bitboard target; + + switch (Type) + { + case CAPTURES: + target = pos.pieces(~Us); + break; + case QUIETS: + case QUIET_CHECKS: + target = ~pos.pieces(); + break; + case EVASIONS: + { + Square checksq = lsb(pos.checkers()); + target = between_bb(pos.square(Us), checksq) | checksq; + break; + } + case NON_EVASIONS: + target = ~pos.pieces(Us); + break; + default: + static_assert(true, "Unsupported type in generate_all()"); + } moveList = generate_pawn_moves(pos, moveList, target); moveList = generate_moves(pos, moveList, Us, target); @@ -255,12 +278,8 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { Color us = pos.side_to_move(); - Bitboard target = Type == CAPTURES ? pos.pieces(~us) - : Type == QUIETS ? ~pos.pieces() - : Type == NON_EVASIONS ? ~pos.pieces(us) : 0; - - return us == WHITE ? generate_all(pos, moveList, target) - : generate_all(pos, moveList, target); + return us == WHITE ? generate_all(pos, moveList) + : generate_all(pos, moveList); } // Explicit template instantiations @@ -293,8 +312,8 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { *moveList++ = make_move(from, pop_lsb(&b)); } - return us == WHITE ? generate_all(pos, moveList, ~pos.pieces()) - : generate_all(pos, moveList, ~pos.pieces()); + return us == WHITE ? generate_all(pos, moveList) + : generate_all(pos, moveList); } @@ -325,11 +344,8 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { return moveList; // Double check, only a king move can save the day // Generate blocking evasions or captures of the checking piece - Square checksq = lsb(pos.checkers()); - Bitboard target = between_bb(checksq, ksq) | checksq; - - return us == WHITE ? generate_all(pos, moveList, target) - : generate_all(pos, moveList, target); + return us == WHITE ? generate_all(pos, moveList) + : generate_all(pos, moveList); } diff --git a/src/pawns.cpp b/src/pawns.cpp index c20cb5293c0..b883dda2da5 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -87,6 +87,7 @@ namespace { e->passedPawns[Us] = 0; e->kingSquares[Us] = SQ_NONE; e->pawnAttacks[Us] = e->pawnAttacksSpan[Us] = pawn_attacks_bb(ourPawns); + e->blockedCount += popcount(shift(ourPawns) & (theirPawns | doubleAttackThem)); // Loop through all pawns of the current color and score each pawn while ((s = *pl++) != SQ_NONE) @@ -106,8 +107,6 @@ namespace { phalanx = neighbours & rank_bb(s); support = neighbours & rank_bb(s - Up); - e->blockedCount += blocked || more_than_one(leverPush); - // A pawn is backward when it is behind all pawns of the same color on // the adjacent files and cannot safely advance. backward = !(neighbours & forward_ranks_bb(Them, s + Up)) @@ -216,7 +215,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; - File d = File(edge_distance(f)); + int d = edge_distance(f); bonus += make_score(ShelterStrength[d][ourRank], 0); if (ourRank && (ourRank == theirRank - 1)) diff --git a/src/position.cpp b/src/position.cpp index 40ebb959575..f5ff3da1862 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -591,7 +591,7 @@ bool Position::pseudo_legal(const Move m) const { if ( !(attacks_from(from, us) & pieces(~us) & to) // Not a capture && !((from + pawn_push(us) == to) && empty(to)) // Not a single push && !( (from + 2 * pawn_push(us) == to) // Not a double push - && (rank_of(from) == relative_rank(us, RANK_2)) + && (relative_rank(us, from) == RANK_2) && empty(to) && empty(to - pawn_push(us)))) return false; diff --git a/src/position.h b/src/position.h index 34a6abc3b5e..ae624926c51 100644 --- a/src/position.h +++ b/src/position.h @@ -98,7 +98,7 @@ class Position { bool is_on_semiopen_file(Color c, Square s) const; // Castling - int castling_rights(Color c) const; + CastlingRights castling_rights(Color c) const; bool can_castle(CastlingRights cr) const; bool castling_impeded(CastlingRights cr) const; Square castling_rook_square(CastlingRights cr) const; @@ -268,7 +268,7 @@ inline bool Position::can_castle(CastlingRights cr) const { return st->castlingRights & cr; } -inline int Position::castling_rights(Color c) const { +inline CastlingRights Position::castling_rights(Color c) const { return c & CastlingRights(st->castlingRights); } @@ -399,8 +399,7 @@ inline Thread* Position::this_thread() const { inline void Position::put_piece(Piece pc, Square s) { board[s] = pc; - byTypeBB[ALL_PIECES] |= s; - byTypeBB[type_of(pc)] |= s; + byTypeBB[ALL_PIECES] |= byTypeBB[type_of(pc)] |= s; byColorBB[color_of(pc)] |= s; index[s] = pieceCount[pc]++; pieceList[pc][index[s]] = s; diff --git a/src/search.cpp b/src/search.cpp index 5e9cd463b06..3b3c0f2aeb0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -272,9 +272,9 @@ void MainThread::search() { Thread* bestThread = this; // Check if there are threads with a better score than main thread - if ( Options["MultiPV"] == 1 + if ( int(Options["MultiPV"]) == 1 && !Limits.depth - && !(Skill(Options["Skill Level"]).enabled() || Options["UCI_LimitStrength"]) + && !(Skill(Options["Skill Level"]).enabled() || int(Options["UCI_LimitStrength"])) && rootMoves[0].pv[0] != MOVE_NONE) { std::map votes; @@ -350,17 +350,17 @@ void Thread::search() { if (mainThread) { if (mainThread->bestPreviousScore == VALUE_INFINITE) - for (int i=0; i<4; ++i) + for (int i = 0; i < 4; ++i) mainThread->iterValue[i] = VALUE_ZERO; else - for (int i=0; i<4; ++i) + for (int i = 0; i < 4; ++i) mainThread->iterValue[i] = mainThread->bestPreviousScore; } std::copy(&lowPlyHistory[2][0], &lowPlyHistory.back().back() + 1, &lowPlyHistory[0][0]); std::fill(&lowPlyHistory[MAX_LPH - 2][0], &lowPlyHistory.back().back() + 1, 0); - size_t multiPV = Options["MultiPV"]; + size_t multiPV = size_t(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. @@ -540,8 +540,8 @@ void Thread::search() { && !Threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (332 + 6 * (mainThread->bestPreviousScore - bestValue) - + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 704.0; + double fallingEval = (332 + 6 * (mainThread->bestPreviousScore - bestValue) + + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 704.0; fallingEval = Utility::clamp(fallingEval, 0.5, 1.5); // If the bestMove is stable over several iterations, reduce time accordingly diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index adc45d58557..6bfd78ad0df 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -530,7 +530,7 @@ int decompress_pairs(PairsData* d, uint64_t idx) { // I(k) = k * d->span + d->span / 2 (1) // First step is to get the 'k' of the I(k) nearest to our idx, using definition (1) - uint32_t k = idx / d->span; + uint32_t k = uint32_t(idx / d->span); // Then we read the corresponding SparseIndex[] entry uint32_t block = number(&d->sparseIndex[k].block); @@ -576,7 +576,7 @@ int decompress_pairs(PairsData* d, uint64_t idx) { // All the symbols of a given length are consecutive integers (numerical // sequence property), so we can compute the offset of our symbol of // length len, stored at the beginning of buf64. - sym = (buf64 - d->base64[len]) >> (64 - len - d->minSymLen); + sym = Sym((buf64 - d->base64[len]) >> (64 - len - d->minSymLen)); // Now add the value of the lowest symbol of length len to get our symbol sym += number(&d->lowestSym[len]); @@ -984,7 +984,7 @@ uint8_t* set_sizes(PairsData* d, uint8_t* data) { d->sizeofBlock = 1ULL << *data++; d->span = 1ULL << *data++; - d->sparseIndexSize = (tbSize + d->span - 1) / d->span; // Round up + d->sparseIndexSize = size_t((tbSize + d->span - 1) / d->span); // Round up auto padding = number(data++); d->blocksNum = number(data); data += sizeof(uint32_t); d->blockLengthSize = d->blocksNum + padding; // Padded to ensure SparseIndex[] diff --git a/src/thread.cpp b/src/thread.cpp index 2e0c216ee19..c171312203f 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -151,7 +151,7 @@ void ThreadPool::set(size_t requested) { clear(); // Reallocate the hash with the new threadpool size - TT.resize(Options["Hash"]); + TT.resize(size_t(Options["Hash"])); // Init thread number dependent search params. Search::init(); diff --git a/src/timeman.cpp b/src/timeman.cpp index 45e9db5863e..1f598745ab0 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -35,10 +35,10 @@ TimeManagement Time; // Our global time management object void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { - TimePoint minThinkingTime = Options["Minimum Thinking Time"]; - TimePoint moveOverhead = Options["Move Overhead"]; - TimePoint slowMover = Options["Slow Mover"]; - TimePoint npmsec = Options["nodestime"]; + TimePoint minThinkingTime = TimePoint(Options["Minimum Thinking Time"]); + TimePoint moveOverhead = TimePoint(Options["Move Overhead"]); + TimePoint slowMover = TimePoint(Options["Slow Mover"]); + TimePoint npmsec = TimePoint(Options["nodestime"]); // opt_scale is a percentage of available time to use for the current move. // max_scale is a multiplier applied to optimumTime. @@ -91,8 +91,8 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { } // Never use more than 80% of the available time for this move - optimumTime = std::max(minThinkingTime, opt_scale * timeLeft); - maximumTime = std::min(0.8 * limits.time[us] - moveOverhead, max_scale * optimumTime); + optimumTime = std::max(minThinkingTime, TimePoint(opt_scale * timeLeft)); + maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, max_scale * optimumTime)); if (Options["Ponder"]) optimumTime += optimumTime / 4; diff --git a/src/tt.cpp b/src/tt.cpp index 4e06bed9345..0a3c54a1e23 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -94,8 +94,8 @@ void TranspositionTable::clear() { WinProcGroup::bindThisThread(idx); // Each thread will zero its part of the hash table - const size_t stride = clusterCount / Options["Threads"], - start = stride * idx, + const size_t stride = size_t(clusterCount / Options["Threads"]), + start = size_t(stride * idx), len = idx != Options["Threads"] - 1 ? stride : clusterCount - start; @@ -103,7 +103,7 @@ void TranspositionTable::clear() { }); } - for (std::thread& th: threads) + for (std::thread& th : threads) th.join(); } diff --git a/src/tune.cpp b/src/tune.cpp index fe61151f319..696b4cb8f33 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -83,7 +83,7 @@ template<> void Tune::Entry::init_option() { make_option(name, value, range template<> void Tune::Entry::read_option() { if (Options.count(name)) - value = Options[name]; + value = int(Options[name]); } template<> void Tune::Entry::init_option() { make_option(name, value, range); } @@ -100,10 +100,10 @@ template<> void Tune::Entry::init_option() { template<> void Tune::Entry::read_option() { if (Options.count("m" + name)) - value = make_score(Options["m" + name], eg_value(value)); + value = make_score(int(Options["m" + name]), eg_value(value)); if (Options.count("e" + name)) - value = make_score(mg_value(value), Options["e" + name]); + value = make_score(mg_value(value), int(Options["e" + name])); } // Instead of a variable here we have a PostUpdate function: just call it diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 66fd42d1e27..16add76eedd 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -38,9 +38,9 @@ namespace UCI { /// 'On change' actions, triggered by an option's value change void on_clear_hash(const Option&) { Search::clear(); } -void on_hash_size(const Option& o) { TT.resize(o); } +void on_hash_size(const Option& o) { TT.resize(size_t(o)); } void on_logger(const Option& o) { start_logger(o); } -void on_threads(const Option& o) { Threads.set(o); } +void on_threads(const Option& o) { Threads.set(size_t(o)); } void on_tb_path(const Option& o) { Tablebases::init(o); } From 86575bcdd88f6d211b4f182966e44a40faf1e315 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sat, 23 May 2020 12:22:34 +0300 Subject: [PATCH 0227/1766] Queen Mobility Tweak It's ok to have low mobility values for the Queen in the middlegame, but it's absolutely not ok to have low mobility values for the Queen in the endgame. Decrease penalty for bad mobility in MG and increase it in EG. STC: LLR: 2.93 (-2.94,2.94) {-0.50,1.50} Total: 17264 W: 3424 L: 3206 D: 10634 Ptnml(0-2): 279, 2004, 3893, 2132, 324 https://tests.stockfishchess.org/tests/view/5ec8f9c1526edcbe9091eba1 LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 175016 W: 22071 L: 21404 D: 131541 Ptnml(0-2): 1195, 15796, 52914, 16353, 1250 https://tests.stockfishchess.org/tests/view/5ec9057c404591b2793007df closes https://github.com/official-stockfish/Stockfish/pull/2697 Bench: 4487054 --- src/evaluate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 2d1f4b9e9e1..7aa67f26db6 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -99,7 +99,7 @@ namespace { { S(-60,-78), S(-20,-17), S( 2, 23), S( 3, 39), S( 3, 70), S( 11, 99), // Rook S( 22,103), S( 31,121), S( 40,134), S( 40,139), S( 41,158), S( 48,164), S( 57,168), S( 57,169), S( 62,172) }, - { S(-34,-36), S(-15,-21), S(-10, -1), S(-10, 22), S( 20, 41), S( 23, 56), // Queen + { S(-30,-48), S(-12,-30), S( -8, -7), S( -9, 19), S( 20, 40), S( 23, 55), // Queen S( 23, 59), S( 35, 75), S( 38, 78), S( 53, 96), S( 64, 96), S( 65,100), S( 65,121), S( 66,127), S( 67,131), S( 67,133), S( 72,136), S( 72,141), S( 77,147), S( 79,150), S( 93,151), S(108,168), S(108,168), S(108,171), From 81c58855e43572e5493497a9894ac5060936005c Mon Sep 17 00:00:00 2001 From: ElbertoOne Date: Sat, 23 May 2020 13:14:02 +0200 Subject: [PATCH 0228/1766] Remove and replace DoubledIsolated penalty by Doubled The values for both penalties were very close, so DoubledIsolated can be removed and replaced by Doubled. Passed STC (simplification): https://tests.stockfishchess.org/tests/view/5ec7c18e2a585b485af54407 LLR: 2.97 (-2.94,2.94) {-1.50,0.50} Total: 105360 W: 20175 L: 20136 D: 65049 Ptnml(0-2): 1803, 12230, 24572, 12275, 1800 Passed LTC: LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 15440 W: 1978 L: 1877 D: 11585 Ptnml(0-2): 92, 1405, 4667, 1422, 134 closes https://github.com/official-stockfish/Stockfish/pull/2696 Bench: 4668875 --- src/pawns.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index b883dda2da5..f9dbcae2932 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -32,13 +32,12 @@ namespace { #define S(mg, eg) make_score(mg, eg) // Pawn penalties - constexpr Score Backward = S( 9, 24); - constexpr Score BlockedStorm = S(82, 82); - constexpr Score Doubled = S(11, 56); - constexpr Score DoubledIsolated = S(15, 57); - constexpr Score Isolated = S( 5, 15); - constexpr Score WeakLever = S( 0, 56); - constexpr Score WeakUnopposed = S(13, 27); + 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); + constexpr Score WeakUnopposed = S(13, 27); // Connected pawn bonus constexpr int Connected[RANK_NB] = { 0, 7, 8, 12, 29, 48, 86 }; @@ -151,7 +150,7 @@ namespace { if ( (ourPawns & forward_file_bb(Them, s)) && popcount(opposed) == 1 && !(theirPawns & adjacent_files_bb(s))) - score -= DoubledIsolated; + score -= Doubled; } else if (backward) From 7f2c8a2b81af19033a62845408b7ae19ed513053 Mon Sep 17 00:00:00 2001 From: Moez Jellouli <37274752+MJZ1977@users.noreply.github.com> Date: Sun, 24 May 2020 01:54:37 +0200 Subject: [PATCH 0229/1766] Remove attacked pawns from storm evaluation STC: LLR: 2.96 (-2.94,2.94) {-0.50,1.50} Total: 54456 W: 11009 L: 10737 D: 32710 Ptnml(0-2): 929, 6326, 12523, 6444, 1006 https://tests.stockfishchess.org/tests/view/5ec962e4404591b2793008a5 LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 62448 W: 9018 L: 8664 D: 44766 Ptnml(0-2): 462, 5928, 18121, 6220, 493 https://tests.stockfishchess.org/tests/view/5ec976a8a586eee45aa2ab40 Non regression STC with "noob_3moves.epd" opening book LLR: 3.81 (-2.94,2.94) {-1.50,0.50} Total: 91896 W: 17770 L: 17653 D: 56473 Ptnml(0-2): 1598, 10782, 21124, 10793, 1651 https://tests.stockfishchess.org/tests/view/5ec9b83ea586eee45aa2ab96 closes https://github.com/official-stockfish/Stockfish/pull/2698 Bench 4488597 --- src/pawns.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index f9dbcae2932..8354cc1510f 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -33,12 +33,13 @@ 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); constexpr Score WeakUnopposed = S(13, 27); + constexpr Score BlockedStorm[RANK_NB] = {S( 0, 0), S( 0, 0), S( 76, 78), S(-10, 15), S(-7, 10), S(-4, 6), S(-1, 2)}; + // Connected pawn bonus constexpr int Connected[RANK_NB] = { 0, 7, 8, 12, 29, 48, 86 }; @@ -200,8 +201,8 @@ Score Entry::evaluate_shelter(const Position& pos, Square ksq) { constexpr Color Them = ~Us; Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq); - Bitboard ourPawns = b & pos.pieces(Us); - Bitboard theirPawns = b & pos.pieces(Them); + Bitboard ourPawns = b & pos.pieces(Us) & ~pawnAttacks[Them]; + Bitboard theirPawns = b & pos.pieces(Them) & ~pawnAttacks[Us]; Score bonus = make_score(5, 5); @@ -218,7 +219,7 @@ Score Entry::evaluate_shelter(const Position& pos, Square ksq) { bonus += make_score(ShelterStrength[d][ourRank], 0); if (ourRank && (ourRank == theirRank - 1)) - bonus -= BlockedStorm * int(theirRank == RANK_3); + bonus -= BlockedStorm[theirRank]; else bonus -= make_score(UnblockedStorm[d][theirRank], 0); } From d40d04c17ceadff6f15d1cb1d4d469f823a35a02 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Mon, 25 May 2020 21:14:07 +0300 Subject: [PATCH 0230/1766] Give bonus for rooks that are alligned with enemy kingring The idea of this patch is that if rooks are not directly attacking the opponent king, they can support king attacks staying behind pawns or minor pieces and be really deadly if position slightly opens up at enemy king ring ranks. Loosely based on some stockfish games where it underestimated attacks on it king when enemy has one or two rooks supporting pawn pushes towards it king. passed STC https://tests.stockfishchess.org/tests/view/5ecb093680f2c838b96550f9 LLR: 2.93 (-2.94,2.94) {-0.50,1.50} Total: 53672 W: 10535 L: 10265 D: 32872 Ptnml(0-2): 952, 6210, 12258, 6448, 968 passed LTC https://tests.stockfishchess.org/tests/view/5ecb639f80f2c838b9655117 LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 62424 W: 8094 L: 7748 D: 46582 Ptnml(0-2): 426, 5734, 18565, 6042, 445 closes https://github.com/official-stockfish/Stockfish/pull/2700 Bench: 4663220 --- src/evaluate.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 7aa67f26db6..8e8cc0912e5 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -143,6 +143,7 @@ namespace { constexpr Score PassedFile = S( 11, 8); constexpr Score PawnlessFlank = S( 17, 95); constexpr Score RestrictedPiece = S( 7, 7); + constexpr Score RookOnKingRing = S( 16, 0); constexpr Score RookOnQueenFile = S( 5, 9); constexpr Score SliderOnQueen = S( 59, 18); constexpr Score ThreatByKing = S( 24, 89); @@ -287,6 +288,8 @@ namespace { kingAttackersWeight[Us] += KingAttackWeights[Pt]; kingAttacksCount[Us] += popcount(b & attackedBy[Them][KING]); } + else if (Pt == ROOK && (file_bb(s) & kingRing[Them])) + score += RookOnKingRing; int mob = popcount(b & mobilityArea[Us]); From fb8095718bd0789d2743fa6216c6aa522555dc4b Mon Sep 17 00:00:00 2001 From: xoto10 Date: Tue, 26 May 2020 00:27:05 +0100 Subject: [PATCH 0231/1766] In BlockedStorm, theirPawns includes ones attacked by us. Pawns heading towards our king tend to be dangerous whether or not we are attacking them so remove this test. STC: LLR: 2.93 (-2.94,2.94) {-1.50,0.50} Total: 91184 W: 18196 L: 18137 D: 54851 Ptnml(0-2): 1580, 10656, 21092, 10653, 1611 https://tests.stockfishchess.org/tests/view/5ecc3f7080f2c838b9655841 LTC: LLR: 2.93 (-2.94,2.94) {-1.50,0.50} Total: 14152 W: 2045 L: 1937 D: 10170 Ptnml(0-2): 99, 1325, 4130, 1413, 109 https://tests.stockfishchess.org/tests/view/5ecc4f3180f2c838b9655861 closes https://github.com/official-stockfish/Stockfish/pull/2702 Bench 4828973 --- src/pawns.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index 8354cc1510f..3ce896309f5 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -202,7 +202,7 @@ Score Entry::evaluate_shelter(const Position& pos, Square ksq) { Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq); Bitboard ourPawns = b & pos.pieces(Us) & ~pawnAttacks[Them]; - Bitboard theirPawns = b & pos.pieces(Them) & ~pawnAttacks[Us]; + Bitboard theirPawns = b & pos.pieces(Them); Score bonus = make_score(5, 5); From a5e3b4eddede900c1df610e8e25026a79d706500 Mon Sep 17 00:00:00 2001 From: protonspring Date: Thu, 28 May 2020 09:48:31 -0600 Subject: [PATCH 0232/1766] Consolidate all attacks bitboards This is a non-functional simplification that simplifies getting attacks bitboards. * consolidates all attacks to attacks_bb (remove Position::attacks_from(..)). * attacks_bb(square) gets pseudo attacks * attacks_bb(square, bitboard) gets attacks considering occupied squares in the bitboard). * pawn_attacks_bb(Color, Square) gets pawn attacks like other pawn attack bitboards. * Wraps all access to PawnAttacks arrays and PseudoAttacks arrays and adds asserts as appropriate. Passed STC LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 90208 W: 17533 L: 17482 D: 55193 Ptnml(0-2): 1412, 10232, 21798, 10217, 1445 https://tests.stockfishchess.org/tests/view/5ece996275787cc0c05d9790 closes https://github.com/official-stockfish/Stockfish/pull/2703 No functional change --- src/bitbase.cpp | 10 +++++----- src/bitboard.h | 35 ++++++++++++++++++++++++++++++----- src/endgame.cpp | 10 +++++----- src/evaluate.cpp | 16 ++++++++-------- src/movegen.cpp | 20 ++++++++++---------- src/pawns.cpp | 6 +++--- src/position.cpp | 30 +++++++++++++++--------------- src/position.h | 21 --------------------- 8 files changed, 76 insertions(+), 72 deletions(-) diff --git a/src/bitbase.cpp b/src/bitbase.cpp index bef2dc49e2f..be6f0d0afd3 100644 --- a/src/bitbase.cpp +++ b/src/bitbase.cpp @@ -112,7 +112,7 @@ namespace { if ( distance(ksq[WHITE], ksq[BLACK]) <= 1 || ksq[WHITE] == psq || ksq[BLACK] == psq - || (stm == WHITE && (PawnAttacks[WHITE][psq] & ksq[BLACK]))) + || (stm == WHITE && (pawn_attacks_bb(WHITE, psq) & ksq[BLACK]))) result = INVALID; // Immediate win if a pawn can be promoted without getting captured @@ -120,13 +120,13 @@ namespace { && rank_of(psq) == RANK_7 && ksq[stm] != psq + NORTH && ( distance(ksq[~stm], psq + NORTH) > 1 - || (PseudoAttacks[KING][ksq[stm]] & (psq + NORTH)))) + || (attacks_bb(ksq[stm]) & (psq + NORTH)))) result = WIN; // Immediate draw if it is a stalemate or a king captures undefended pawn else if ( stm == BLACK - && ( !(PseudoAttacks[KING][ksq[stm]] & ~(PseudoAttacks[KING][ksq[~stm]] | PawnAttacks[~stm][psq])) - || (PseudoAttacks[KING][ksq[stm]] & psq & ~PseudoAttacks[KING][ksq[~stm]]))) + && ( !(attacks_bb(ksq[stm]) & ~(attacks_bb(ksq[~stm]) | pawn_attacks_bb(~stm, psq))) + || (attacks_bb(ksq[stm]) & psq & ~attacks_bb(ksq[~stm])))) result = DRAW; // Position will be classified later @@ -149,7 +149,7 @@ namespace { const Result Bad = (stm == WHITE ? DRAW : WIN); Result r = INVALID; - Bitboard b = PseudoAttacks[KING][ksq[stm]]; + Bitboard b = attacks_bb(ksq[stm]); while (b) r |= stm == WHITE ? db[index(BLACK, ksq[BLACK] , pop_lsb(&b), psq)] diff --git a/src/bitboard.h b/src/bitboard.h index 9252c3dc914..93f838f8a6a 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -176,6 +176,12 @@ constexpr Bitboard pawn_attacks_bb(Bitboard b) { : shift(b) | shift(b); } +inline Bitboard pawn_attacks_bb(Color c, Square s) { + + assert(is_ok(s)); + return PawnAttacks[c][s]; +} + /// pawn_double_attacks_bb() returns the squares doubly attacked by pawns of the /// given color from the squares in the given bitboard. @@ -266,19 +272,38 @@ inline Bitboard safe_destination(Square s, int step) return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0); } -/// attacks_bb() returns a bitboard representing all the squares attacked by a -/// piece of type Pt (bishop or rook) placed on 's'. +/// attacks_bb(Square) returns the pseudo attacks of the give piece type +/// assuming an empty board. + +template +inline Bitboard attacks_bb(Square s) { + + assert((Pt != PAWN) && (is_ok(s))); + + return PseudoAttacks[Pt][s]; +} + +/// attacks_bb(Square, Bitboard) returns the attacks by the given piece +/// assuming the board is occupied according to the passed Bitboard. +/// Sliding piece attacks do not continue passed an occupied square. template inline Bitboard attacks_bb(Square s, Bitboard occupied) { - const Magic& m = Pt == ROOK ? RookMagics[s] : BishopMagics[s]; - return m.attacks[m.index(occupied)]; + assert((Pt != PAWN) && (is_ok(s))); + + switch (Pt) + { + case BISHOP: return BishopMagics[s].attacks[BishopMagics[s].index(occupied)]; + case ROOK : return RookMagics[s].attacks[ RookMagics[s].index(occupied)]; + case QUEEN : return attacks_bb(s, occupied) | attacks_bb(s, occupied); + default : return PseudoAttacks[Pt][s]; + } } inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) { - assert(pt != PAWN); + assert((pt != PAWN) && (is_ok(s))); switch (pt) { diff --git a/src/endgame.cpp b/src/endgame.cpp index e232da6243a..7b9c145e428 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -391,8 +391,8 @@ ScaleFactor Endgame::operator()(const Position& pos) const { && relative_rank(weakSide, pos.square(strongSide)) >= RANK_4 && relative_rank(weakSide, rsq) == RANK_3 && ( pos.pieces(weakSide, PAWN) - & pos.attacks_from(kingSq) - & pos.attacks_from(rsq, strongSide))) + & attacks_bb(kingSq) + & pawn_attacks_bb(strongSide, rsq))) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; @@ -535,7 +535,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // the corner if ( rk == RANK_6 && distance(psq + 2 * push, ksq) <= 1 - && (PseudoAttacks[BISHOP][bsq] & (psq + push)) + && (attacks_bb(bsq) & (psq + push)) && distance(bsq, psq) >= 2) return ScaleFactor(8); } @@ -670,14 +670,14 @@ ScaleFactor Endgame::operator()(const Position& pos) const { if ( ksq == blockSq1 && opposite_colors(ksq, wbsq) && ( bbsq == blockSq2 - || (pos.attacks_from(blockSq2) & pos.pieces(weakSide, BISHOP)) + || (attacks_bb(blockSq2, pos.pieces()) & pos.pieces(weakSide, BISHOP)) || distance(psq1, psq2) >= 2)) return SCALE_FACTOR_DRAW; else if ( ksq == blockSq2 && opposite_colors(ksq, wbsq) && ( bbsq == blockSq1 - || (pos.attacks_from(blockSq1) & pos.pieces(weakSide, BISHOP)))) + || (attacks_bb(blockSq1, pos.pieces()) & pos.pieces(weakSide, BISHOP)))) return SCALE_FACTOR_DRAW; else return SCALE_FACTOR_NONE; diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 8e8cc0912e5..ad79db5c4dc 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -235,7 +235,7 @@ namespace { mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pos.blockers_for_king(Us) | pe->pawn_attacks(Them)); // Initialize attackedBy[] for king and pawns - attackedBy[Us][KING] = pos.attacks_from(ksq); + attackedBy[Us][KING] = attacks_bb(ksq); attackedBy[Us][PAWN] = pe->pawn_attacks(Us); attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN]; attackedBy2[Us] = dblAttackByPawn | (attackedBy[Us][KING] & attackedBy[Us][PAWN]); @@ -243,7 +243,7 @@ namespace { // Init our king safety tables Square s = make_square(Utility::clamp(file_of(ksq), FILE_B, FILE_G), Utility::clamp(rank_of(ksq), RANK_2, RANK_7)); - kingRing[Us] = PseudoAttacks[KING][s] | s; + kingRing[Us] = attacks_bb(s) | s; kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them)); kingAttacksCount[Them] = kingAttackersWeight[Them] = 0; @@ -273,7 +273,7 @@ namespace { // Find attacked squares, including x-ray attacks for bishops and rooks b = Pt == BISHOP ? attacks_bb(s, pos.pieces() ^ pos.pieces(QUEEN)) : Pt == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(QUEEN) ^ pos.pieces(Us, ROOK)) - : pos.attacks_from(s); + : attacks_bb(s, pos.pieces()); if (pos.blockers_for_king(Us) & s) b &= LineBB[pos.square(Us)][s]; @@ -323,7 +323,7 @@ namespace { * (!(attackedBy[Us][PAWN] & s) + popcount(blocked & CenterFiles)); // Penalty for all enemy pawns x-rayed - score -= BishopXRayPawns * popcount(PseudoAttacks[BISHOP][s] & pos.pieces(Them, PAWN)); + score -= BishopXRayPawns * popcount(attacks_bb(s) & pos.pieces(Them, PAWN)); // Bonus for bishop on a long diagonal which can "see" both center squares if (more_than_one(attacks_bb(s, pos.pieces(PAWN)) & Center)) @@ -438,7 +438,7 @@ namespace { unsafeChecks |= b2 & attackedBy[Them][BISHOP]; // Enemy knights checks - knightChecks = pos.attacks_from(ksq) & attackedBy[Them][KNIGHT]; + knightChecks = attacks_bb(ksq) & attackedBy[Them][KNIGHT]; if (knightChecks & safe) kingDanger += more_than_one(knightChecks & safe) ? KnightSafeCheck * 162/100 : KnightSafeCheck; @@ -564,12 +564,12 @@ namespace { Square s = pos.square(Them); safe = mobilityArea[Us] & ~stronglyProtected; - b = attackedBy[Us][KNIGHT] & pos.attacks_from(s); + b = attackedBy[Us][KNIGHT] & attacks_bb(s); score += KnightOnQueen * popcount(b & safe); - b = (attackedBy[Us][BISHOP] & pos.attacks_from(s)) - | (attackedBy[Us][ROOK ] & pos.attacks_from(s)); + b = (attackedBy[Us][BISHOP] & attacks_bb(s, pos.pieces())) + | (attackedBy[Us][ROOK ] & attacks_bb(s, pos.pieces())); score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]); } diff --git a/src/movegen.cpp b/src/movegen.cpp index 5787d1743cf..81b8c929fed 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -40,7 +40,7 @@ namespace { // Knight promotion is the only promotion that can give a direct check // that's not already included in the queen promotion. - if (Type == QUIET_CHECKS && (PseudoAttacks[KNIGHT][to] & ksq)) + if (Type == QUIET_CHECKS && (attacks_bb(to) & ksq)) *moveList++ = make(to - D, to, KNIGHT); else (void)ksq; // Silence a warning under MSVC @@ -84,8 +84,8 @@ namespace { if (Type == QUIET_CHECKS) { - b1 &= pos.attacks_from(ksq, Them); - b2 &= pos.attacks_from(ksq, Them); + b1 &= pawn_attacks_bb(Them, ksq); + b2 &= pawn_attacks_bb(Them, ksq); // Add pawn pushes which give discovered check. This is possible only // if the pawn is not on the same file as the enemy king, because we @@ -166,7 +166,7 @@ namespace { if (Type == EVASIONS && !(target & (pos.ep_square() - Up))) return moveList; - b1 = pawnsNotOn7 & pos.attacks_from(pos.ep_square(), Them); + b1 = pawnsNotOn7 & pawn_attacks_bb(Them, pos.ep_square()); assert(b1); @@ -192,14 +192,14 @@ namespace { if (Checks) { if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN) - && !(PseudoAttacks[Pt][from] & target & pos.check_squares(Pt))) + && !(attacks_bb(from) & target & pos.check_squares(Pt))) continue; if (pos.blockers_for_king(~us) & from) continue; } - Bitboard b = pos.attacks_from(from) & target; + Bitboard b = attacks_bb(from, pos.pieces()) & target; if (Checks) b &= pos.check_squares(Pt); @@ -248,7 +248,7 @@ namespace { if (Type != QUIET_CHECKS && Type != EVASIONS) { Square ksq = pos.square(Us); - Bitboard b = pos.attacks_from(ksq) & target; + Bitboard b = attacks_bb(ksq) & target; while (b) *moveList++ = make_move(ksq, pop_lsb(&b)); @@ -303,10 +303,10 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { Square from = pop_lsb(&dc); PieceType pt = type_of(pos.piece_on(from)); - Bitboard b = pos.attacks_from(pt, from) & ~pos.pieces(); + Bitboard b = attacks_bb(pt, from, pos.pieces()) & ~pos.pieces(); if (pt == KING) - b &= ~PseudoAttacks[QUEEN][pos.square(~us)]; + b &= ~attacks_bb(pos.square(~us)); while (b) *moveList++ = make_move(from, pop_lsb(&b)); @@ -336,7 +336,7 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { sliderAttacks |= LineBB[ksq][pop_lsb(&sliders)] & ~pos.checkers(); // Generate evasions for king, capture and non capture moves - Bitboard b = pos.attacks_from(ksq) & ~pos.pieces(us) & ~sliderAttacks; + Bitboard b = attacks_bb(ksq) & ~pos.pieces(us) & ~sliderAttacks; while (b) *moveList++ = make_move(ksq, pop_lsb(&b)); diff --git a/src/pawns.cpp b/src/pawns.cpp index 3ce896309f5..467137b3133 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -100,8 +100,8 @@ namespace { 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]; + lever = theirPawns & pawn_attacks_bb(Us, s); + leverPush = theirPawns & pawn_attacks_bb(Us, s + Up); doubled = ourPawns & (s - Up); neighbours = ourPawns & adjacent_files_bb(s); phalanx = neighbours & rank_bb(s); @@ -253,7 +253,7 @@ Score Entry::do_king_safety(const Position& pos) { Bitboard pawns = pos.pieces(Us, PAWN); int minPawnDist = 6; - if (pawns & PseudoAttacks[KING][ksq]) + if (pawns & attacks_bb(ksq)) minPawnDist = 1; else while (pawns) minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(&pawns))); diff --git a/src/position.cpp b/src/position.cpp index f5ff3da1862..d2e33b3022b 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -139,7 +139,7 @@ void Position::init() { for (Piece pc : Pieces) for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) for (Square s2 = Square(s1 + 1); s2 <= SQ_H8; ++s2) - if (PseudoAttacks[type_of(pc)][s1] & s2) + if ((type_of(pc) != PAWN) && (attacks_bb(type_of(pc), s1, 0) & s2)) { Move move = make_move(s1, s2); Key key = Zobrist::psq[pc][s1] ^ Zobrist::psq[pc][s2] ^ Zobrist::side; @@ -319,10 +319,10 @@ void Position::set_check_info(StateInfo* si) const { Square ksq = square(~sideToMove); - si->checkSquares[PAWN] = attacks_from(ksq, ~sideToMove); - si->checkSquares[KNIGHT] = attacks_from(ksq); - si->checkSquares[BISHOP] = attacks_from(ksq); - si->checkSquares[ROOK] = attacks_from(ksq); + si->checkSquares[PAWN] = pawn_attacks_bb(~sideToMove, ksq); + si->checkSquares[KNIGHT] = attacks_bb(ksq); + si->checkSquares[BISHOP] = attacks_bb(ksq, pieces()); + si->checkSquares[ROOK] = attacks_bb(ksq, pieces()); si->checkSquares[QUEEN] = si->checkSquares[BISHOP] | si->checkSquares[ROOK]; si->checkSquares[KING] = 0; } @@ -455,8 +455,8 @@ Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners pinners = 0; // Snipers are sliders that attack 's' when a piece and other snipers are removed - Bitboard snipers = ( (PseudoAttacks[ ROOK][s] & pieces(QUEEN, ROOK)) - | (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders; + Bitboard snipers = ( (attacks_bb< ROOK>(s) & pieces(QUEEN, ROOK)) + | (attacks_bb(s) & pieces(QUEEN, BISHOP))) & sliders; Bitboard occupancy = pieces() ^ snipers; while (snipers) @@ -480,12 +480,12 @@ Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners Bitboard Position::attackers_to(Square s, Bitboard occupied) const { - return (attacks_from(s, BLACK) & pieces(WHITE, PAWN)) - | (attacks_from(s, WHITE) & pieces(BLACK, PAWN)) - | (attacks_from(s) & pieces(KNIGHT)) + return (pawn_attacks_bb(BLACK, s) & pieces(WHITE, PAWN)) + | (pawn_attacks_bb(WHITE, s) & pieces(BLACK, PAWN)) + | (attacks_bb(s) & pieces(KNIGHT)) | (attacks_bb< ROOK>(s, occupied) & pieces( ROOK, QUEEN)) | (attacks_bb(s, occupied) & pieces(BISHOP, QUEEN)) - | (attacks_from(s) & pieces(KING)); + | (attacks_bb(s) & pieces(KING)); } @@ -588,7 +588,7 @@ bool Position::pseudo_legal(const Move m) const { if ((Rank8BB | Rank1BB) & to) return false; - if ( !(attacks_from(from, us) & pieces(~us) & to) // Not a capture + if ( !(pawn_attacks_bb(us, from) & pieces(~us) & to) // Not a capture && !((from + pawn_push(us) == to) && empty(to)) // Not a single push && !( (from + 2 * pawn_push(us) == to) // Not a double push && (relative_rank(us, from) == RANK_2) @@ -596,7 +596,7 @@ bool Position::pseudo_legal(const Move m) const { && empty(to - pawn_push(us)))) return false; } - else if (!(attacks_from(type_of(pc), from) & to)) + else if (!(attacks_bb(type_of(pc), from, pieces()) & to)) return false; // Evasions generator already takes care to avoid some kind of illegal moves @@ -670,7 +670,7 @@ bool Position::gives_check(Move m) const { Square kto = relative_square(sideToMove, rfrom > kfrom ? SQ_G1 : SQ_C1); Square rto = relative_square(sideToMove, rfrom > kfrom ? SQ_F1 : SQ_D1); - return (PseudoAttacks[ROOK][rto] & square(~sideToMove)) + return (attacks_bb(rto) & square(~sideToMove)) && (attacks_bb(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & square(~sideToMove)); } default: @@ -794,7 +794,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { { // Set en-passant square if the moved pawn can be captured if ( (int(to) ^ int(from)) == 16 - && (attacks_from(to - pawn_push(us), us) & pieces(them, PAWN))) + && (pawn_attacks_bb(us, to - pawn_push(us)) & pieces(them, PAWN))) { st->epSquare = to - pawn_push(us); k ^= Zobrist::enpassant[file_of(st->epSquare)]; diff --git a/src/position.h b/src/position.h index ae624926c51..8f8c8f7ab9c 100644 --- a/src/position.h +++ b/src/position.h @@ -112,9 +112,6 @@ class Position { // Attacks to/from a given square Bitboard attackers_to(Square s) const; Bitboard attackers_to(Square s, Bitboard occupied) const; - Bitboard attacks_from(PieceType pt, Square s) const; - template Bitboard attacks_from(Square s) const; - template Bitboard attacks_from(Square s, Color c) const; Bitboard slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const; // Properties of moves @@ -284,24 +281,6 @@ inline Square Position::castling_rook_square(CastlingRights cr) const { return castlingRookSquare[cr]; } -template -inline Bitboard Position::attacks_from(Square s) const { - static_assert(Pt != PAWN, "Pawn attacks need color"); - - return Pt == BISHOP || Pt == ROOK ? attacks_bb(s, pieces()) - : Pt == QUEEN ? attacks_from(s) | attacks_from(s) - : PseudoAttacks[Pt][s]; -} - -template<> -inline Bitboard Position::attacks_from(Square s, Color c) const { - return PawnAttacks[c][s]; -} - -inline Bitboard Position::attacks_from(PieceType pt, Square s) const { - return attacks_bb(pt, s, pieces()); -} - inline Bitboard Position::attackers_to(Square s) const { return attackers_to(s, pieces()); } From 616eb60008308f686930c0c94116aab170398dc1 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 28 May 2020 22:34:43 +0200 Subject: [PATCH 0233/1766] Less pruning in draw PV lines. no futility pruning for certain captures if the PvNode has a draw eval. passed STC: LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 59392 W: 11576 L: 11302 D: 36514 Ptnml(0-2): 977, 6816, 13920, 6922, 1061 https://tests.stockfishchess.org/tests/view/5ed0b1bb042fa6d77c355295 passed LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 64040 W: 8273 L: 7923 D: 47844 Ptnml(0-2): 424, 5842, 19220, 6028, 506 https://tests.stockfishchess.org/tests/view/5ed145e0042fa6d77c35531c closes https://github.com/official-stockfish/Stockfish/pull/2705 Bench: 4704615 --- src/search.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/search.cpp b/src/search.cpp index 3b3c0f2aeb0..4747beb2e94 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1055,6 +1055,7 @@ namespace { // Futility pruning for captures if ( !givesCheck && lmrDepth < 6 + && !(PvNode && abs(bestValue) < 2) && !ss->inCheck && ss->staticEval + 270 + 384 * lmrDepth + PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha) continue; From 8c3d9d996af7aa34f019785818eecbeb9338b95f Mon Sep 17 00:00:00 2001 From: ElbertoOne Date: Sun, 31 May 2020 16:39:03 +0200 Subject: [PATCH 0234/1766] Isolated pawns tweak Give opposed doubled isolated pawns only the Doubled penalty. The other isolated pawns get the Isolated penalty and the WeakUnopposed penalty. The popcount condition has been replaced with an opposed check, which is non-functional, but probably gives a speed-up. Passed STC (https://tests.stockfishchess.org/tests/view/5ed0f0f0042fa6d77c3552f5): LLR: 2.96 (-2.94,2.94) {-0.50,1.50} Total: 121832 W: 23562 L: 23195 D: 75075 Ptnml(0-2): 2092, 14064, 28313, 14279, 2168 LTC: (https://tests.stockfishchess.org/tests/view/5ed22e40042fa6d77c355387) LLR: 2.96 (-2.94,2.94) {0.25,1.75} Total: 103368 W: 13232 L: 12768 D: 77368 Ptnml(0-2): 693, 9484, 30919, 9842, 746 closes https://github.com/official-stockfish/Stockfish/pull/2706 Bench: 4085694 --- src/pawns.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index 467137b3133..c1119a4102c 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -145,13 +145,13 @@ namespace { else if (!neighbours) { - score -= Isolated - + WeakUnopposed * !opposed; - - if ( (ourPawns & forward_file_bb(Them, s)) - && popcount(opposed) == 1 + if ( opposed + && (ourPawns & forward_file_bb(Them, s)) && !(theirPawns & adjacent_files_bb(s))) score -= Doubled; + else + score -= Isolated + + WeakUnopposed * !opposed; } else if (backward) From d1ec10cd4fe1e67114178f444cfebd2ff1183408 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Sun, 31 May 2020 17:00:47 +0200 Subject: [PATCH 0235/1766] Give bonus for bishops that are alligned with enemy kingring. Inspired by the succesful patch "Give bonus for rooks that are alligned with enemy kingring" from Vizvezdenec, this idea has been reused for bishops. Here, we only consider attacks that are not blocked by any pawn. Also we have a 50% higher bonus than for the rooks. STC: LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 68960 W: 13495 L: 13202 D: 42263 Ptnml(0-2): 1213, 8018, 15802, 8157, 1290 https://tests.stockfishchess.org/tests/view/5ed27495042fa6d77c3553aa LTC: LLR: 2.96 (-2.94,2.94) {0.25,1.75} Total: 54560 W: 7105 L: 6780 D: 40675 Ptnml(0-2): 379, 4986, 16254, 5253, 408 https://tests.stockfishchess.org/tests/view/5ed30375596e6dc1e1f97425 closes https://github.com/official-stockfish/Stockfish/pull/2708 Bench: 4860021 --- src/evaluate.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index ad79db5c4dc..dc7134a8818 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -128,6 +128,7 @@ namespace { // Assorted bonuses and penalties constexpr Score BishopPawns = S( 3, 7); + constexpr Score BishopOnKingRing = S( 24, 0); constexpr Score BishopXRayPawns = S( 4, 5); constexpr Score CorneredBishop = S( 50, 50); constexpr Score FlankAttacks = S( 8, 0); @@ -288,9 +289,13 @@ namespace { kingAttackersWeight[Us] += KingAttackWeights[Pt]; kingAttacksCount[Us] += popcount(b & attackedBy[Them][KING]); } + else if (Pt == ROOK && (file_bb(s) & kingRing[Them])) score += RookOnKingRing; + else if (Pt == BISHOP && (attacks_bb(s, pos.pieces(PAWN)) & kingRing[Them])) + score += BishopOnKingRing; + int mob = popcount(b & mobilityArea[Us]); mobility[Us] += MobilityBonus[Pt - 2][mob]; From 16566a8fcf76b9b72b6e746f318f77045df90017 Mon Sep 17 00:00:00 2001 From: Moez Jellouli <37274752+MJZ1977@users.noreply.github.com> Date: Tue, 2 Jun 2020 22:27:11 +0200 Subject: [PATCH 0236/1766] Singular quiet LMR If ttMove is a capture and had a singular extension, it is probably the best move. No need to make a decrease of LMR on other moves. STC LLR: 2.96 (-2.94,2.94) {-0.50,1.50} Total: 41968 W: 8170 L: 7918 D: 25880 Ptnml(0-2): 733, 4770, 9726, 5022, 733 https://tests.stockfishchess.org/tests/view/5ed6b666f29b40b0fc95a884 LTC LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 71376 W: 9200 L: 8827 D: 53349 Ptnml(0-2): 486, 6544, 21342, 6743, 573 https://tests.stockfishchess.org/tests/view/5ed7578bf29b40b0fc95a8c9 closes https://github.com/official-stockfish/Stockfish/pull/2713 Bench: 4733799 --- src/search.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 4747beb2e94..1e133447d38 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -630,7 +630,8 @@ namespace { Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue; bool ttHit, ttPv, formerPv, givesCheck, improving, didLMR, priorCapture; - bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture, singularLMR; + bool captureOrPromotion, doFullDepthSearch, moveCountPruning, + ttCapture, singularQuietLMR; Piece movedPiece; int moveCount, captureCount, quietCount; @@ -971,7 +972,7 @@ namespace { depth > 12 ? ss->ply : MAX_PLY); value = bestValue; - singularLMR = moveCountPruning = false; + singularQuietLMR = moveCountPruning = false; ttCapture = ttMove && pos.capture_or_promotion(ttMove); // Mark this node as being searched @@ -1092,7 +1093,7 @@ namespace { if (value < singularBeta) { extension = 1; - singularLMR = true; + singularQuietLMR = !ttCapture; } // Multi-cut pruning @@ -1198,7 +1199,7 @@ namespace { r--; // Decrease reduction if ttMove has been singularly extended (~3 Elo) - if (singularLMR) + if (singularQuietLMR) r -= 1 + formerPv; if (!captureOrPromotion) From 784263596ff9b01187341274b6f3cbd8971a2d2c Mon Sep 17 00:00:00 2001 From: Moez Jellouli <37274752+MJZ1977@users.noreply.github.com> Date: Sun, 31 May 2020 16:51:38 +0200 Subject: [PATCH 0237/1766] Minimal thinking time, even if only one rootMove. without search, the eval returned can be misleading (e.g. mate instead of draw), leading to wrong adjudication. With a minimal search, this is avoided. This patch leads to 1ms long searches if there is only 1 move, similar patches all indicate a small Elo gain. Fixes https://github.com/official-stockfish/Stockfish/issues/2707 Passed non-regression STC: LLR: 2.93 (-2.94,2.94) {-1.50,0.50} Total: 22312 W: 4350 L: 4204 D: 13758 Ptnml(0-2): 323, 2488, 5437, 2536, 372 https://tests.stockfishchess.org/tests/view/5ed562b0f29b40b0fc95a7d0 closes https://github.com/official-stockfish/Stockfish/pull/2709 Bench: 4733799 --- src/search.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 1e133447d38..35110538170 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -556,9 +556,11 @@ void Thread::search() { } double bestMoveInstability = 1 + totBestMoveChanges / Threads.size(); - // Stop the search if we have only one legal move, or if available time elapsed - if ( rootMoves.size() == 1 - || Time.elapsed() > Time.optimum() * fallingEval * reduction * bestMoveInstability) + double totalTime = rootMoves.size() == 1 ? 0 : + Time.optimum() * fallingEval * reduction * bestMoveInstability; + + // Stop the search if we have exceeded the totalTime, at least 1ms search. + if (Time.elapsed() > totalTime) { // If we are allowed to ponder do not stop the search now but // keep pondering until the GUI sends "ponderhit" or "stop". @@ -569,7 +571,7 @@ void Thread::search() { } else if ( Threads.increaseDepth && !mainThread->ponder - && Time.elapsed() > Time.optimum() * fallingEval * reduction * bestMoveInstability * 0.6) + && Time.elapsed() > totalTime * 0.6) Threads.increaseDepth = false; else Threads.increaseDepth = true; From fd8e88427b1268bfddc0b2ab72f639f758b7be0b Mon Sep 17 00:00:00 2001 From: protonspring Date: Wed, 3 Jun 2020 18:06:49 -0600 Subject: [PATCH 0238/1766] small speed-up in movegen pass color as a template parameter. closes https://github.com/official-stockfish/Stockfish/pull/2715 No functional change. --- src/movegen.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index 81b8c929fed..b57f41a9829 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -179,13 +179,12 @@ namespace { } - template - ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Color us, - Bitboard target) { + template + ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) { static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()"); - const Square* pl = pos.squares(us); + const Square* pl = pos.squares(Us); for (Square from = *pl; from != SQ_NONE; from = *++pl) { @@ -195,7 +194,7 @@ namespace { && !(attacks_bb(from) & target & pos.check_squares(Pt))) continue; - if (pos.blockers_for_king(~us) & from) + if (pos.blockers_for_king(~Us) & from) continue; } @@ -240,10 +239,10 @@ namespace { } moveList = generate_pawn_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, Us, target); - moveList = generate_moves(pos, moveList, Us, target); - moveList = generate_moves< ROOK, Checks>(pos, moveList, Us, target); - moveList = generate_moves< QUEEN, Checks>(pos, moveList, Us, target); + moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); if (Type != QUIET_CHECKS && Type != EVASIONS) { From 15e190e9428b21fbfe29ce020c456077dc5fdd04 Mon Sep 17 00:00:00 2001 From: pb00067 Date: Sat, 6 Jun 2020 12:56:38 +0200 Subject: [PATCH 0239/1766] Use lowply-history also on low depths STC: https://tests.stockfishchess.org/tests/view/5ed75078f29b40b0fc95a8b9 LLR: 2.93 (-2.94,2.94) {-0.50,1.50} Total: 73928 W: 14301 L: 14005 D: 45622 Ptnml(0-2): 1243, 8572, 17096, 8752, 1301 LTC: https://tests.stockfishchess.org/tests/view/5ed895e0f29b40b0fc95a976 LLR: 2.93 (-2.94,2.94) {0.25,1.75} Total: 154848 W: 19684 L: 19074 D: 116090 Ptnml(0-2): 1048, 14108, 46627, 14468, 1173 closes https://github.com/official-stockfish/Stockfish/pull/2718 bench: 4582693 --- src/movepick.cpp | 2 +- src/search.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index e26f42ef98c..78102c52a34 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -110,7 +110,7 @@ void MovePicker::score() { + 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)] - + (ply < MAX_LPH ? 4 * (*lowPlyHistory)[ply][from_to(m)] : 0); + + (ply < MAX_LPH ? std::min(4, depth / 3) * (*lowPlyHistory)[ply][from_to(m)] : 0); else // Type == EVASIONS { diff --git a/src/search.cpp b/src/search.cpp index 35110538170..efa7b9c5db9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -971,7 +971,7 @@ namespace { contHist, countermove, ss->killers, - depth > 12 ? ss->ply : MAX_PLY); + ss->ply); value = bestValue; singularQuietLMR = moveCountPruning = false; From 902309020a8ebf97a649cacfdc2dc2881b630966 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Wed, 3 Jun 2020 11:05:58 +0100 Subject: [PATCH 0240/1766] join scale_factor, initiative and mg+eg reduction Merging this code into one function `winnable()`. Should allow common concepts used to adjust the eg value, either by addition or scaling, to be combined more effectively. Improve trace function. closes https://github.com/official-stockfish/Stockfish/pull/2710 No functional change. --- src/evaluate.cpp | 54 ++++++++++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index dc7134a8818..12a4c7bff10 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -35,7 +35,7 @@ namespace Trace { enum Tracing { NO_TRACE, TRACE }; enum Term { // The first 8 entries are reserved for PieceType - MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, INITIATIVE, TOTAL, TERM_NB + MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, WINNABLE, TOTAL, TERM_NB }; Score scores[TERM_NB][COLOR_NB]; @@ -59,7 +59,7 @@ namespace Trace { std::ostream& operator<<(std::ostream& os, Term t) { - if (t == MATERIAL || t == IMBALANCE || t == INITIATIVE || t == TOTAL) + if (t == MATERIAL || t == IMBALANCE || t == WINNABLE || t == TOTAL) os << " ---- ----" << " | " << " ---- ----"; else os << scores[t][WHITE] << " | " << scores[t][BLACK]; @@ -173,8 +173,7 @@ namespace { template Score threats() const; template Score passed() const; template Score space() const; - ScaleFactor scale_factor(Value eg) const; - Score initiative(Score score) const; + Value winnable(Score score) const; const Position& pos; Material::Entry* me; @@ -717,12 +716,12 @@ namespace { } - // Evaluation::initiative() computes the initiative correction value - // for the position. It is a second order bonus/malus based on the + // Evaluation::winnable() adjusts the mg and eg score components based on the // known attacking/defending status of the players. + // A single value is derived from the mg and eg values and returned. template - Score Evaluation::initiative(Score score) const { + Value Evaluation::winnable(Score score) const { int outflanking = distance(pos.square(WHITE), pos.square(BLACK)) - distance(pos.square(WHITE), pos.square(BLACK)); @@ -756,17 +755,10 @@ namespace { int u = ((mg > 0) - (mg < 0)) * Utility::clamp(complexity + 50, -abs(mg), 0); int v = ((eg > 0) - (eg < 0)) * std::max(complexity, -abs(eg)); - if (T) - Trace::add(INITIATIVE, make_score(u, v)); - - return make_score(u, v); - } - + mg += u; + eg += v; - // Evaluation::scale_factor() computes the scale factor for the winning side - - template - ScaleFactor Evaluation::scale_factor(Value eg) const { + // Compute the scale factor for the winning side Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK; int sf = me->scale_factor(pos, strongSide); @@ -786,7 +778,18 @@ namespace { sf = std::min(sf, 36 + 7 * pos.count(strongSide)); } - return ScaleFactor(sf); + // Interpolate between the middlegame and (scaled by 'sf') endgame score + v = mg * int(me->game_phase()) + + eg * int(PHASE_MIDGAME - me->game_phase()) * ScaleFactor(sf) / SCALE_FACTOR_NORMAL; + v /= PHASE_MIDGAME; + + if (T) + { + Trace::add(WINNABLE, make_score(u, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL - eg_value(score))); + Trace::add(TOTAL, make_score(mg, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL)); + } + + return Value(v); } @@ -841,14 +844,8 @@ namespace { + passed< WHITE>() - passed< BLACK>() + space< WHITE>() - space< BLACK>(); - score += initiative(score); - - // Interpolate between a middlegame and a (scaled by 'sf') endgame score - ScaleFactor sf = scale_factor(eg_value(score)); - v = mg_value(score) * int(me->game_phase()) - + eg_value(score) * int(PHASE_MIDGAME - me->game_phase()) * sf / SCALE_FACTOR_NORMAL; - - v /= PHASE_MIDGAME; + // Derive single value from mg and eg parts of score + v = winnable(score); // In case of tracing add all remaining individual evaluation terms if (T) @@ -857,7 +854,6 @@ namespace { Trace::add(IMBALANCE, me->imbalance()); Trace::add(PAWN, pe->pawn_score(WHITE), pe->pawn_score(BLACK)); Trace::add(MOBILITY, mobility[WHITE], mobility[BLACK]); - Trace::add(TOTAL, score); } // Side to move point of view @@ -909,11 +905,11 @@ std::string Eval::trace(const Position& pos) { << " Threats | " << Term(THREAT) << " Passed | " << Term(PASSED) << " Space | " << Term(SPACE) - << " Initiative | " << Term(INITIATIVE) + << " Winnable | " << Term(WINNABLE) << " ------------+-------------+-------------+------------\n" << " Total | " << Term(TOTAL); - ss << "\nTotal evaluation: " << to_cp(v) << " (white side)\n"; + ss << "\nFinal evaluation: " << to_cp(v) << " (white side)\n"; return ss.str(); } From b0eb5a1ba3d094a1d2236db6f33e0d2164ec3480 Mon Sep 17 00:00:00 2001 From: protonspring Date: Sat, 6 Jun 2020 21:25:32 -0600 Subject: [PATCH 0241/1766] Wrap all access to LineBB and add assert This is a non-functional code style change which provides a safe access handler for LineBB. Also includes an assert in debug mode to verify square correctness. closes https://github.com/official-stockfish/Stockfish/pull/2719 No functional change --- src/bitboard.h | 18 +++++++++++++++--- src/evaluate.cpp | 2 +- src/movegen.cpp | 2 +- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/bitboard.h b/src/bitboard.h index 93f838f8a6a..704f4bb4e8a 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -200,12 +200,24 @@ inline Bitboard adjacent_files_bb(Square s) { return shift(file_bb(s)) | shift(file_bb(s)); } +/// line_bb(Square, Square) returns a Bitboard representing an entire line +/// (from board edge to board edge) that intersects the given squares. +/// If the given squares are not on a same file/rank/diagonal, return 0. +/// Ex. line_bb(SQ_C4, SQ_F7) returns a bitboard with the A2-G8 diagonal. + +inline Bitboard line_bb(Square s1, Square s2) { + + assert(is_ok(s1) && is_ok(s2)); + return LineBB[s1][s2]; +} -/// between_bb() returns squares that are linearly between the given squares +/// between_bb() returns a Bitboard representing squares that are linearly +/// between the given squares (excluding the given squares). /// If the given squares are not on a same file/rank/diagonal, return 0. +/// Ex. between_bb(SQ_C4, SQ_F7) returns a bitboard with squares D5 and E6. inline Bitboard between_bb(Square s1, Square s2) { - Bitboard b = LineBB[s1][s2] & ((AllSquares << s1) ^ (AllSquares << s2)); + Bitboard b = line_bb(s1, s2) & ((AllSquares << s1) ^ (AllSquares << s2)); return b & (b - 1); //exclude lsb } @@ -249,7 +261,7 @@ inline Bitboard passed_pawn_span(Color c, Square s) { /// straight or on a diagonal line. inline bool aligned(Square s1, Square s2, Square s3) { - return LineBB[s1][s2] & s3; + return line_bb(s1, s2) & s3; } diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 12a4c7bff10..c042c016ed3 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -276,7 +276,7 @@ namespace { : attacks_bb(s, pos.pieces()); if (pos.blockers_for_king(Us) & s) - b &= LineBB[pos.square(Us)][s]; + b &= line_bb(pos.square(Us), s); attackedBy2[Us] |= attackedBy[Us][ALL_PIECES] & b; attackedBy[Us][Pt] |= b; diff --git a/src/movegen.cpp b/src/movegen.cpp index b57f41a9829..17203a95938 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -332,7 +332,7 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { // the king evasions in order to skip known illegal moves, which avoids any // useless legality checks later on. while (sliders) - sliderAttacks |= LineBB[ksq][pop_lsb(&sliders)] & ~pos.checkers(); + sliderAttacks |= line_bb(ksq, pop_lsb(&sliders)) & ~pos.checkers(); // Generate evasions for king, capture and non capture moves Bitboard b = attacks_bb(ksq) & ~pos.pieces(us) & ~sliderAttacks; From 1c65310c0e5ac639a51c3d6b9b114d48aa57bdd8 Mon Sep 17 00:00:00 2001 From: protonspring Date: Sun, 31 May 2020 23:31:14 -0600 Subject: [PATCH 0242/1766] Refactor some threads related code. This is a code style change that moves some pure thread code into the threads class. It is a bit more code, but it makes search.cpp cleaner and easier to read by hiding some thread specific functionality. STC (SMP) LLR: 2.93 (-2.94,2.94) {-1.50,0.50} Total: 75896 W: 12073 L: 12026 D: 51797 Ptnml(0-2): 828, 8224, 19872, 8121, 903 https://tests.stockfishchess.org/tests/view/5ed492e8f29b40b0fc95a74c closes https://github.com/official-stockfish/Stockfish/pull/2720 No functional change. --- src/search.cpp | 50 ++++++++----------------------------------------- src/thread.cpp | 51 +++++++++++++++++++++++++++++++++++++++++++++++++- src/thread.h | 3 +++ 3 files changed, 61 insertions(+), 43 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index efa7b9c5db9..e3a5a92e6c7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -236,14 +236,8 @@ void MainThread::search() { } else { - for (Thread* th : Threads) - { - th->bestMoveChanges = 0; - if (th != this) - th->start_searching(); - } - - Thread::search(); // Let's start searching! + Threads.start_searching(); // start non-main threads + Thread::search(); // main thread start searching } // When we reach the maximum depth, we can arrive here without a raise of @@ -260,9 +254,7 @@ void MainThread::search() { Threads.stop = true; // Wait until all threads have finished - for (Thread* th : Threads) - if (th != this) - th->wait_for_search_finished(); + Threads.wait_for_search_finished(); // When playing in 'nodes as time' mode, subtract the searched nodes from // the available ones before exiting. @@ -271,37 +263,11 @@ void MainThread::search() { Thread* bestThread = this; - // Check if there are threads with a better score than main thread - if ( int(Options["MultiPV"]) == 1 - && !Limits.depth - && !(Skill(Options["Skill Level"]).enabled() || int(Options["UCI_LimitStrength"])) - && rootMoves[0].pv[0] != MOVE_NONE) - { - std::map votes; - Value minScore = this->rootMoves[0].score; - - // Find minimum score - for (Thread* th: Threads) - minScore = std::min(minScore, th->rootMoves[0].score); - - // Vote according to score and depth, and select the best thread - for (Thread* th : Threads) - { - votes[th->rootMoves[0].pv[0]] += - (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth); - - if (abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY) - { - // Make sure we pick the shortest mate / TB conversion or stave off mate the longest - if (th->rootMoves[0].score > bestThread->rootMoves[0].score) - bestThread = th; - } - else if ( th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY - || ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY - && votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]])) - bestThread = th; - } - } + if (int(Options["MultiPV"]) == 1 && + !Limits.depth && + !(Skill(Options["Skill Level"]).enabled() || int(Options["UCI_LimitStrength"])) && + rootMoves[0].pv[0] != MOVE_NONE) + bestThread = Threads.get_best_thread(); bestPreviousScore = bestThread->rootMoves[0].score; diff --git a/src/thread.cpp b/src/thread.cpp index c171312203f..a27a60c6e24 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -208,7 +208,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states, for (Thread* th : *this) { - th->nodes = th->tbHits = th->nmpMinPly = 0; + th->nodes = th->tbHits = th->nmpMinPly = th->bestMoveChanges = 0; th->rootDepth = th->completedDepth = 0; th->rootMoves = rootMoves; th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th); @@ -218,3 +218,52 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states, main()->start_searching(); } + +Thread* ThreadPool::get_best_thread() const { + + Thread* bestThread = front(); + std::map votes; + Value minScore = VALUE_NONE; + + // Find minimum score of all threads + for (Thread* th: *this) + minScore = std::min(minScore, th->rootMoves[0].score); + + // Vote according to score and depth, and select the best thread + for (Thread* th : *this) + { + votes[th->rootMoves[0].pv[0]] += + (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth); + + if (abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY) + { + // Make sure we pick the shortest mate / TB conversion or stave off mate the longest + if (th->rootMoves[0].score > bestThread->rootMoves[0].score) + bestThread = th; + } + else if ( th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY + || ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY + && votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]])) + bestThread = th; + } + + return bestThread; +} + +/// Start non-main threads. + +void ThreadPool::start_searching() { + + for (Thread* th : *this) + if (th != front()) + th->start_searching(); +} + +/// Wait for non-main threads. + +void ThreadPool::wait_for_search_finished() const { + + for (Thread* th : *this) + if (th != front()) + th->wait_for_search_finished(); +} diff --git a/src/thread.h b/src/thread.h index 79be197bf27..a69e1d10e45 100644 --- a/src/thread.h +++ b/src/thread.h @@ -109,6 +109,9 @@ struct ThreadPool : public std::vector { MainThread* main() const { return static_cast(front()); } uint64_t nodes_searched() const { return accumulate(&Thread::nodes); } uint64_t tb_hits() const { return accumulate(&Thread::tbHits); } + Thread* get_best_thread() const; + void start_searching(); + void wait_for_search_finished() const; std::atomic_bool stop, increaseDepth; From d0cb9b286f4d7415be002855201e75340c8adef0 Mon Sep 17 00:00:00 2001 From: NguyenPham Date: Mon, 8 Jun 2020 07:48:38 +1000 Subject: [PATCH 0243/1766] show coordinates when displaying board closes https://github.com/official-stockfish/Stockfish/pull/2723 No functional change --- AUTHORS | 1 + src/bitboard.cpp | 3 ++- src/position.cpp | 5 +++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index 36c2a47b8ad..1cd7ff54b0f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -115,6 +115,7 @@ Nick Pelling (nickpelling) Nicklas Persson (NicklasPersson) Niklas Fiekas (niklasf) Nikolay Kostov (NikolayIT) +Nguyen Pham Ondrej Mosnáček (WOnder93) Oskar Werkelin Ahlin Pablo Vazquez diff --git a/src/bitboard.cpp b/src/bitboard.cpp index f650eef6862..3bb3ff8f8d5 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -56,8 +56,9 @@ const std::string Bitboards::pretty(Bitboard b) { for (File f = FILE_A; f <= FILE_H; ++f) s += b & make_square(f, r) ? "| X " : "| "; - s += "|\n+---+---+---+---+---+---+---+---+\n"; + s += "| " + std::to_string(1 + r) + "\n+---+---+---+---+---+---+---+---+\n"; } + s += " a b c d e f g h\n"; return s; } diff --git a/src/position.cpp b/src/position.cpp index d2e33b3022b..c9db6224abf 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -64,10 +64,11 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) { for (File f = FILE_A; f <= FILE_H; ++f) os << " | " << PieceToChar[pos.piece_on(make_square(f, r))]; - os << " |\n +---+---+---+---+---+---+---+---+\n"; + os << " | " << (1 + r) << "\n +---+---+---+---+---+---+---+---+\n"; } - os << "\nFen: " << pos.fen() << "\nKey: " << std::hex << std::uppercase + os << " a b c d e f g h\n" + << "\nFen: " << pos.fen() << "\nKey: " << std::hex << std::uppercase << std::setfill('0') << std::setw(16) << pos.key() << std::setfill(' ') << std::dec << "\nCheckers: "; From b081e52239e6496070a376452ca04dcc6d1993c5 Mon Sep 17 00:00:00 2001 From: nguyenpham Date: Mon, 8 Jun 2020 08:49:27 +1000 Subject: [PATCH 0244/1766] Improve Readme.md about compiling Reparagraph, add an example how to compile on Unix-like systems closes https://github.com/official-stockfish/Stockfish/pull/2724 No functional change --- Readme.md | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/Readme.md b/Readme.md index 35ff095d679..2b1de86be65 100644 --- a/Readme.md +++ b/Readme.md @@ -165,17 +165,23 @@ are in use, see the engine log. ## Compiling Stockfish yourself from the sources -On Unix-like systems, it should be possible to compile Stockfish -directly from the source code with the included Makefile. +Stockfish has support for 32 or 64-bit CPUs, certain hardware +instructions, big-endian machines such as Power PC, and other platforms. -Stockfish has support for 32 or 64-bit CPUs, the hardware POPCNT -instruction, big-endian machines such as Power PC, and other platforms. +On Unix-like systems, it should be easy to compile Stockfish +directly from the source code with the included Makefile in the folder +`src`. In general it is recommended to run `make help` to see a list of make +targets with corresponding descriptions. -In general it is recommended to run `make help` to see a list of make -targets with corresponding descriptions. When not using the Makefile to -compile (for instance with Microsoft MSVC) you need to manually -set/unset some switches in the compiler command line; see file *types.h* -for a quick reference. +``` + cd src + make help + make build ARCH=x86-64-modern +``` + +When not using the Makefile to compile (for instance with Microsoft MSVC) you +need to manually set/unset some switches in the compiler command line; see +file *types.h* for a quick reference. When reporting an issue or a bug, please tell us which version and compiler you used to create your executable. These informations can From 4b10578acbe099482ed40200478df4d775c01af5 Mon Sep 17 00:00:00 2001 From: Sami Kiminki Date: Fri, 5 Jun 2020 20:17:00 +0300 Subject: [PATCH 0245/1766] Increase the maximum hash size by a factor of 256 Conceptually group hash clusters into super clusters of 256 clusters. This scheme allows us to use hash sizes up to 32 TB (= 2^32 super clusters = 2^40 clusters). Use 48 bits of the Zobrist key to choose the cluster index. We use 8 extra bits to mitigate the quantization error for very large hashes when scaling the hash key to cluster index. The hash index computation is organized to be compatible with the existing scheme for power-of-two hash sizes up to 128 GB. Fixes https://github.com/official-stockfish/Stockfish/issues/1349 closes https://github.com/official-stockfish/Stockfish/pull/2722 Passed non-regression STC: LLR: 2.93 (-2.94,2.94) {-1.50,0.50} Total: 37976 W: 7336 L: 7211 D: 23429 Ptnml(0-2): 578, 4295, 9149, 4356, 610 https://tests.stockfishchess.org/tests/view/5edcbaaef29b40b0fc95abc5 No functional change. --- src/tt.cpp | 8 ++++++-- src/tt.h | 13 ++++++++++--- src/ucioption.cpp | 4 ++-- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/tt.cpp b/src/tt.cpp index 0a3c54a1e23..92aaee00182 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -65,8 +65,10 @@ void TranspositionTable::resize(size_t mbSize) { aligned_ttmem_free(mem); - clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster); - table = static_cast(aligned_ttmem_alloc(clusterCount * sizeof(Cluster), mem)); + superClusterCount = mbSize * 1024 * 1024 / (sizeof(Cluster) * ClustersPerSuperCluster); + + table = static_cast( + aligned_ttmem_alloc(superClusterCount * ClustersPerSuperCluster * sizeof(Cluster), mem)); if (!mem) { std::cerr << "Failed to allocate " << mbSize @@ -89,6 +91,8 @@ void TranspositionTable::clear() { { threads.emplace_back([this, idx]() { + const size_t clusterCount = superClusterCount * ClustersPerSuperCluster; + // Thread binding gives faster search on systems with a first-touch policy if (Options["Threads"] > 8) WinProcGroup::bindThisThread(idx); diff --git a/src/tt.h b/src/tt.h index bd723a86790..76db03dabaf 100644 --- a/src/tt.h +++ b/src/tt.h @@ -66,6 +66,7 @@ struct TTEntry { class TranspositionTable { static constexpr int ClusterSize = 3; + static constexpr int ClustersPerSuperCluster = 256; struct Cluster { TTEntry entry[ClusterSize]; @@ -82,15 +83,21 @@ class TranspositionTable { void resize(size_t mbSize); void clear(); - // The 32 lowest order bits of the key are used to get the index of the cluster TTEntry* first_entry(const Key key) const { - return &table[(uint32_t(key) * uint64_t(clusterCount)) >> 32].entry[0]; + + // The index is computed from + // Idx = (K48 * SCC) / 2^40, with K48 the 48 lowest bits swizzled. + + const uint64_t firstTerm = uint32_t(key) * uint64_t(superClusterCount); + const uint64_t secondTerm = (uint16_t(key >> 32) * uint64_t(superClusterCount)) >> 16; + + return &table[(firstTerm + secondTerm) >> 24].entry[0]; } private: friend struct TTEntry; - size_t clusterCount; + size_t superClusterCount; Cluster* table; void* mem; uint8_t generation8; // Size must be not bigger than TTEntry::genBound8 diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 16add76eedd..90190b53b6c 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -56,8 +56,8 @@ bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const void init(OptionsMap& o) { - // at most 2^32 clusters. - constexpr int MaxHashMB = Is64Bit ? 131072 : 2048; + // At most 2^32 superclusters. Supercluster = 8 kB + constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048; o["Debug Log File"] << Option("", on_logger); o["Contempt"] << Option(24, -100, 100); From 3af083a7cd9be1659f1d8a39a65e33b87608f762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Wed, 10 Jun 2020 00:10:07 +0200 Subject: [PATCH 0246/1766] Improve the anti-shuffling policy We replace the current decrease of the complexity term in initiative when shuffling by a direct damping of the evaluation. This scheme may have two benefits over the initiative approach: a) the damping effect is more brutal for fortresses with heavy pieces on the board, because the initiative term is almost an endgame term; b) the initiative implementation had a funny side effect, almost a bug, in the rare positions where mg > 0, eg < 0 and the tampered eval returned a positive value (ie with heavy pieces still on the board): sending eg to zero via shuffling would **increase** the tampered eval instead of decreasing it, which is somewhat illogical. This patch avoids this phenomenon. STC: LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 43072 W: 8373 L: 8121 D: 26578 Ptnml(0-2): 729, 4954, 9940, 5162, 751 https://tests.stockfishchess.org/tests/view/5ee008ebf29b40b0fc95ade2 LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 37376 W: 4816 L: 4543 D: 28017 Ptnml(0-2): 259, 3329, 11286, 3508, 306 https://tests.stockfishchess.org/tests/view/5ee03b06f29b40b0fc95ae0c Closes https://github.com/official-stockfish/Stockfish/pull/2727 Bench: 4757174 --- src/evaluate.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index c042c016ed3..b173cd3bb32 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -743,7 +743,6 @@ namespace { + 24 * infiltration + 51 * !pos.non_pawn_material() - 43 * almostUnwinnable - - 2 * pos.rule50_count() -110 ; Value mg = mg_value(score); @@ -857,7 +856,12 @@ namespace { } // Side to move point of view - return (pos.side_to_move() == WHITE ? v : -v) + Tempo; + v = (pos.side_to_move() == WHITE ? v : -v) + Tempo; + + // Damp down the evaluation linearly when shuffling + v = v * (100 - pos.rule50_count()) / 100; + + return v; } } // namespace From c44c62efc24fbe6355a9c19e287b2c78e6fd6c1d Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sat, 13 Jun 2020 05:03:59 +0300 Subject: [PATCH 0247/1766] Adjust history threshold for quiet moves futility pruning This patch adjusts the threshold for futility pruning of quiet moves using the continuation history array contHist[5], in the same way as it is used in movepicker. passed STC: https://tests.stockfishchess.org/tests/view/5ee3f88bca6c451633a9959f LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 55984 W: 10822 L: 10552 D: 34610 Ptnml(0-2): 952, 6435, 12941, 6719, 945 passed LTC: https://tests.stockfishchess.org/tests/view/5ee4186dca6c451633a995cf LLR: 2.96 (-2.94,2.94) {0.25,1.75} Total: 41712 W: 5402 L: 5114 D: 31196 Ptnml(0-2): 293, 3766, 12469, 4016, 312 closes https://github.com/official-stockfish/Stockfish/pull/2734 Bench: 4715960 --- src/search.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index e3a5a92e6c7..f5887f3fda7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1006,7 +1006,8 @@ namespace { && ss->staticEval + 235 + 172 * lmrDepth <= alpha && (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] - + (*contHist[3])[movedPiece][to_sq(move)] < 27400) + + (*contHist[3])[movedPiece][to_sq(move)] + + (*contHist[5])[movedPiece][to_sq(move)] / 2 < 31400) continue; // Prune moves with negative SEE (~20 Elo) From 4d657618e956decdd51bceca77c2c5489dfcf6af Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 10 Jun 2020 13:19:21 +0200 Subject: [PATCH 0248/1766] Quantize eval to multiples of 16 Removes some excess precision, helps searchs. Effectively reintroduces evaluation grain, with a slightly different context. https://github.com/official-stockfish/Stockfish/commit/45dbd9cd0303d0db469670af8ec3598731a4eace passed STC LLR: 2.97 (-2.94,2.94) {-0.50,1.50} Total: 197032 W: 37938 L: 37462 D: 121632 Ptnml(0-2): 3359, 22994, 45446, 23246, 3471 https://tests.stockfishchess.org/tests/view/5ee0c228f29b40b0fc95ae53 passed LTC LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 77696 W: 9970 L: 9581 D: 58145 Ptnml(0-2): 530, 7075, 23311, 7340, 592 https://tests.stockfishchess.org/tests/view/5ee21426f29b40b0fc95af43 passed LTC SMP LLR: 2.96 (-2.94,2.94) {0.25,1.75} Total: 64136 W: 7425 L: 7091 D: 49620 Ptnml(0-2): 345, 5416, 20228, 5718, 361 https://tests.stockfishchess.org/tests/view/5ee387bbf29b40b0fc95b04c closes https://github.com/official-stockfish/Stockfish/pull/2733 Bench: 4939103 --- src/evaluate.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index b173cd3bb32..036b93a933c 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -855,6 +855,9 @@ namespace { Trace::add(MOBILITY, mobility[WHITE], mobility[BLACK]); } + // Evaluation grain + v = (v / 16) * 16; + // Side to move point of view v = (pos.side_to_move() == WHITE ? v : -v) + Tempo; From 42b7dbcb5e20ae9015122601522be8b455787a4a Mon Sep 17 00:00:00 2001 From: xoto10 Date: Sat, 13 Jun 2020 09:54:07 +0100 Subject: [PATCH 0249/1766] Tuned values for search constants Tuned search constants after many search patches since the last successful tune. 1st LTC @ 60+0.6 th 1 : LLR: 2.97 (-2.94,2.94) {0.25,1.75} Total: 57656 W: 7369 L: 7036 D: 43251 Ptnml(0-2): 393, 5214, 17336, 5437, 448 https://tests.stockfishchess.org/tests/view/5ee1e074f29b40b0fc95af19 SMP LTC @ 20+0.2 th 8 : LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 83576 W: 9731 L: 9341 D: 64504 Ptnml(0-2): 464, 7062, 26369, 7406, 487 https://tests.stockfishchess.org/tests/view/5ee35a21f29b40b0fc95b008 The changes were rebased on top of a successful patch by Viz (see #2734) and two different ways of doing this were tested. The successful test modified the constants in the patch by Viz in a similar manner to the tuning run: LTC (rebased) @ 60+0.6 th 1 : LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 193384 W: 24241 L: 23521 D: 145622 Ptnml(0-2): 1309, 17497, 58472, 17993, 1421 https://tests.stockfishchess.org/tests/view/5ee43319ca6c451633a995f9 Further work: the recent patch to quantize eval #2733 affects search quit quite a bit, so doing another tune in, say, three months time might be a good idea. closes https://github.com/official-stockfish/Stockfish/pull/2735 Bench 4246971 --- src/search.cpp | 58 +++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index f5887f3fda7..5ad650d2dab 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -65,9 +65,9 @@ namespace { constexpr uint64_t TtHitAverageResolution = 1024; // Razor and futility margins - constexpr int RazorMargin = 531; + constexpr int RazorMargin = 516; Value futility_margin(Depth d, bool improving) { - return Value(217 * (d - improving)); + return Value(224 * (d - improving)); } // Reductions lookup table, initialized at startup @@ -75,16 +75,16 @@ namespace { Depth reduction(bool i, Depth d, int mn) { int r = Reductions[d] * Reductions[mn]; - return (r + 511) / 1024 + (!i && r > 1007); + return (r + 529) / 1024 + (!i && r > 1050); } constexpr int futility_move_count(bool improving, Depth depth) { - return (4 + depth * depth) / (2 - improving); + return (3 + depth * depth) / (2 - improving); } // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return d > 15 ? -8 : 19 * d * d + 155 * d - 132; + return d > 15 ? 28 : 19 * d * d + 135 * d - 136; } // Add a small random component to draw evaluations to avoid 3fold-blindness @@ -194,7 +194,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int((24.8 + std::log(Threads.size())) * std::log(i)); + Reductions[i] = int((24.9 + std::log(Threads.size())) * std::log(i)); } @@ -408,7 +408,7 @@ void Thread::search() { beta = std::min(prev + delta, VALUE_INFINITE); // Adjust contempt based on root move's previousScore (dynamic contempt) - int dct = ct + (102 - ct / 2) * prev / (abs(prev) + 157); + int dct = ct + (104 - ct / 2) * prev / (abs(prev) + 143); contempt = (us == WHITE ? make_score(dct, dct / 2) : -make_score(dct, dct / 2)); @@ -506,13 +506,13 @@ void Thread::search() { && !Threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (332 + 6 * (mainThread->bestPreviousScore - bestValue) - + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 704.0; + double fallingEval = (293 + 6 * (mainThread->bestPreviousScore - bestValue) + + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 742.0; fallingEval = Utility::clamp(fallingEval, 0.5, 1.5); // If the bestMove is stable over several iterations, reduce time accordingly - timeReduction = lastBestMoveDepth + 9 < completedDepth ? 1.94 : 0.91; - double reduction = (1.41 + mainThread->previousTimeReduction) / (2.27 * timeReduction); + timeReduction = lastBestMoveDepth + 10 < completedDepth ? 1.93 : 0.96; + double reduction = (1.36 + mainThread->previousTimeReduction) / (2.21 * timeReduction); // Use part of the gained time from a previous stable move for the current move for (Thread* th : Threads) @@ -537,7 +537,7 @@ void Thread::search() { } else if ( Threads.increaseDepth && !mainThread->ponder - && Time.elapsed() > totalTime * 0.6) + && Time.elapsed() > totalTime * 0.57) Threads.increaseDepth = false; else Threads.increaseDepth = true; @@ -819,10 +819,10 @@ namespace { // Step 9. Null move search with verification search (~40 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 23397 + && (ss-1)->statScore < 24714 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 32 * depth - 30 * improving + 120 * ttPv + 292 + && ss->staticEval >= beta - 29 * depth - 31 * improving + 119 * ttPv + 299 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -830,7 +830,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and value - Depth R = (854 + 68 * depth) / 258 + std::min(int(eval - beta) / 192, 3); + Depth R = (793 + 70 * depth) / 252 + std::min(int(eval - beta) / 192, 3); ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -870,10 +870,10 @@ 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 + && depth > 5 && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) { - Value raisedBeta = beta + 189 - 45 * improving; + Value raisedBeta = beta + 182 - 48 * improving; assert(raisedBeta < VALUE_INFINITE); MovePicker mp(pos, ttMove, raisedBeta - ss->staticEval, &captureHistory); int probCutCount = 0; @@ -904,7 +904,7 @@ namespace { // If the qsearch held, perform the regular search if (value >= raisedBeta) - value = -search(pos, ss+1, -raisedBeta, -raisedBeta+1, depth - 4, !cutNode); + value = -search(pos, ss+1, -raisedBeta, -raisedBeta+1, depth - 5, !cutNode); pos.undo_move(move); @@ -1003,15 +1003,15 @@ namespace { // Futility pruning: parent node (~5 Elo) if ( lmrDepth < 6 && !ss->inCheck - && ss->staticEval + 235 + 172 * lmrDepth <= alpha + && ss->staticEval + 252 + 176 * lmrDepth <= alpha && (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - + (*contHist[5])[movedPiece][to_sq(move)] / 2 < 31400) + + (*contHist[5])[movedPiece][to_sq(move)] / 2 < 30251) continue; // Prune moves with negative SEE (~20 Elo) - if (!pos.see_ge(move, Value(-(32 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth))) + if (!pos.see_ge(move, Value(-(31 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth))) continue; } else @@ -1027,11 +1027,11 @@ namespace { && lmrDepth < 6 && !(PvNode && abs(bestValue) < 2) && !ss->inCheck - && ss->staticEval + 270 + 384 * lmrDepth + PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha) + && ss->staticEval + 264 + 397 * lmrDepth + PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha) continue; // See based pruning - if (!pos.see_ge(move, Value(-194) * depth)) // (~25 Elo) + if (!pos.see_ge(move, Value(-192) * depth)) // (~25 Elo) continue; } } @@ -1144,12 +1144,12 @@ namespace { || moveCountPruning || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha || cutNode - || thisThread->ttHitAverage < 375 * TtHitAverageResolution * TtHitAverageWindow / 1024)) + || thisThread->ttHitAverage < 399 * TtHitAverageResolution * TtHitAverageWindow / 1024)) { Depth r = reduction(improving, depth, moveCount); // Decrease reduction if the ttHit running average is large - if (thisThread->ttHitAverage > 500 * TtHitAverageResolution * TtHitAverageWindow / 1024) + if (thisThread->ttHitAverage > 492 * TtHitAverageResolution * TtHitAverageWindow / 1024) r--; // Reduction if other threads are searching this position. @@ -1195,14 +1195,14 @@ namespace { - 4926; // Decrease/increase reduction by comparing opponent's stat score (~10 Elo) - if (ss->statScore >= -102 && (ss-1)->statScore < -114) + if (ss->statScore >= -99 && (ss-1)->statScore < -116) r--; - else if ((ss-1)->statScore >= -116 && ss->statScore < -154) + else if ((ss-1)->statScore >= -117 && ss->statScore < -150) r++; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - r -= ss->statScore / 16434; + r -= ss->statScore / 15896; } else { @@ -1474,7 +1474,7 @@ namespace { if (PvNode && bestValue > alpha) alpha = bestValue; - futilityBase = bestValue + 154; + futilityBase = bestValue + 138; } const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, From 995ee4b31105ad8c7976cc68c11fabfdc5108e63 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Sun, 14 Jun 2020 21:50:27 +0100 Subject: [PATCH 0250/1766] Retuned values after eval quantize patch. The last search tune patch was tested before the implementation of #2733 which presumably changed the search characteristics noticeably. Another tuning run was done, see https://tests.stockfishchess.org/tests/view/5ee5b434ca6c451633a9a08c and the updated values passed these tests: STC: LLR: 2.93 (-2.94,2.94) {-0.50,1.50} Total: 34352 W: 6600 L: 6360 D: 21392 Ptnml(0-2): 581, 3947, 7914, 4119, 615 https://tests.stockfishchess.org/tests/view/5ee62f05ca6c451633a9a15f LTC 60+0.6 th 1 : LLR: 2.97 (-2.94,2.94) {0.25,1.75} Total: 11176 W: 1499 L: 1304 D: 8373 Ptnml(0-2): 69, 933, 3403, 1100, 83 https://tests.stockfishchess.org/tests/view/5ee6205bca6c451633a9a147 SMP LTC 20+0.2 th 8 : LLR: 2.93 (-2.94,2.94) {0.25,1.75} Total: 54032 W: 6126 L: 5826 D: 42080 Ptnml(0-2): 278, 4454, 17280, 4698, 306 https://tests.stockfishchess.org/tests/view/5ee62f25ca6c451633a9a162 Closes https://github.com/official-stockfish/Stockfish/pull/2742 Bench 4957812 --- src/search.cpp | 68 +++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5ad650d2dab..cf89a892965 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -65,9 +65,9 @@ namespace { constexpr uint64_t TtHitAverageResolution = 1024; // Razor and futility margins - constexpr int RazorMargin = 516; + constexpr int RazorMargin = 527; Value futility_margin(Depth d, bool improving) { - return Value(224 * (d - improving)); + return Value(227 * (d - improving)); } // Reductions lookup table, initialized at startup @@ -75,7 +75,7 @@ namespace { Depth reduction(bool i, Depth d, int mn) { int r = Reductions[d] * Reductions[mn]; - return (r + 529) / 1024 + (!i && r > 1050); + return (r + 570) / 1024 + (!i && r > 1018); } constexpr int futility_move_count(bool improving, Depth depth) { @@ -84,7 +84,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return d > 15 ? 28 : 19 * d * d + 135 * d - 136; + return d > 15 ? 27 : 17 * d * d + 133 * d - 134; } // Add a small random component to draw evaluations to avoid 3fold-blindness @@ -194,7 +194,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int((24.9 + std::log(Threads.size())) * std::log(i)); + Reductions[i] = int((24.8 + std::log(Threads.size())) * std::log(i)); } @@ -403,12 +403,12 @@ void Thread::search() { if (rootDepth >= 4) { Value prev = rootMoves[pvIdx].previousScore; - delta = Value(21); + delta = Value(19); alpha = std::max(prev - delta,-VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE); // Adjust contempt based on root move's previousScore (dynamic contempt) - int dct = ct + (104 - ct / 2) * prev / (abs(prev) + 143); + int dct = ct + (110 - ct / 2) * prev / (abs(prev) + 140); contempt = (us == WHITE ? make_score(dct, dct / 2) : -make_score(dct, dct / 2)); @@ -506,13 +506,13 @@ void Thread::search() { && !Threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (293 + 6 * (mainThread->bestPreviousScore - bestValue) - + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 742.0; + double fallingEval = (296 + 6 * (mainThread->bestPreviousScore - bestValue) + + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 725.0; fallingEval = Utility::clamp(fallingEval, 0.5, 1.5); // If the bestMove is stable over several iterations, reduce time accordingly - timeReduction = lastBestMoveDepth + 10 < completedDepth ? 1.93 : 0.96; - double reduction = (1.36 + mainThread->previousTimeReduction) / (2.21 * timeReduction); + timeReduction = lastBestMoveDepth + 10 < completedDepth ? 1.92 : 0.95; + double reduction = (1.47 + mainThread->previousTimeReduction) / (2.22 * timeReduction); // Use part of the gained time from a previous stable move for the current move for (Thread* th : Threads) @@ -537,7 +537,7 @@ void Thread::search() { } else if ( Threads.increaseDepth && !mainThread->ponder - && Time.elapsed() > totalTime * 0.57) + && Time.elapsed() > totalTime * 0.56) Threads.increaseDepth = false; else Threads.increaseDepth = true; @@ -819,10 +819,10 @@ namespace { // Step 9. Null move search with verification search (~40 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 24714 + && (ss-1)->statScore < 23824 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 29 * depth - 31 * improving + 119 * ttPv + 299 + && ss->staticEval >= beta - 33 * depth - 33 * improving + 112 * ttPv + 311 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -830,7 +830,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and value - Depth R = (793 + 70 * depth) / 252 + std::min(int(eval - beta) / 192, 3); + Depth R = (737 + 77 * depth) / 246 + std::min(int(eval - beta) / 192, 3); ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -870,10 +870,10 @@ 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 + && depth > 4 && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) { - Value raisedBeta = beta + 182 - 48 * improving; + Value raisedBeta = beta + 176 - 49 * improving; assert(raisedBeta < VALUE_INFINITE); MovePicker mp(pos, ttMove, raisedBeta - ss->staticEval, &captureHistory); int probCutCount = 0; @@ -904,7 +904,7 @@ namespace { // If the qsearch held, perform the regular search if (value >= raisedBeta) - value = -search(pos, ss+1, -raisedBeta, -raisedBeta+1, depth - 5, !cutNode); + value = -search(pos, ss+1, -raisedBeta, -raisedBeta+1, depth - 4, !cutNode); pos.undo_move(move); @@ -1003,15 +1003,15 @@ namespace { // Futility pruning: parent node (~5 Elo) if ( lmrDepth < 6 && !ss->inCheck - && ss->staticEval + 252 + 176 * lmrDepth <= alpha + && ss->staticEval + 284 + 188 * lmrDepth <= alpha && (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - + (*contHist[5])[movedPiece][to_sq(move)] / 2 < 30251) + + (*contHist[5])[movedPiece][to_sq(move)] / 2 < 28388) continue; // Prune moves with negative SEE (~20 Elo) - if (!pos.see_ge(move, Value(-(31 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth))) + if (!pos.see_ge(move, Value(-(29 - std::min(lmrDepth, 17)) * lmrDepth * lmrDepth))) continue; } else @@ -1027,11 +1027,11 @@ namespace { && lmrDepth < 6 && !(PvNode && abs(bestValue) < 2) && !ss->inCheck - && ss->staticEval + 264 + 397 * lmrDepth + PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha) + && ss->staticEval + 267 + 391 * lmrDepth + PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha) continue; // See based pruning - if (!pos.see_ge(move, Value(-192) * depth)) // (~25 Elo) + if (!pos.see_ge(move, Value(-202) * depth)) // (~25 Elo) continue; } } @@ -1144,12 +1144,12 @@ namespace { || moveCountPruning || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha || cutNode - || thisThread->ttHitAverage < 399 * TtHitAverageResolution * TtHitAverageWindow / 1024)) + || thisThread->ttHitAverage < 415 * TtHitAverageResolution * TtHitAverageWindow / 1024)) { Depth r = reduction(improving, depth, moveCount); // Decrease reduction if the ttHit running average is large - if (thisThread->ttHitAverage > 492 * TtHitAverageResolution * TtHitAverageWindow / 1024) + if (thisThread->ttHitAverage > 473 * TtHitAverageResolution * TtHitAverageWindow / 1024) r--; // Reduction if other threads are searching this position. @@ -1164,7 +1164,7 @@ namespace { r++; // Decrease reduction if opponent's move count is high (~5 Elo) - if ((ss-1)->moveCount > 14) + if ((ss-1)->moveCount > 13) r--; // Decrease reduction if ttMove has been singularly extended (~3 Elo) @@ -1192,17 +1192,17 @@ namespace { + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 4926; + - 4826; // Decrease/increase reduction by comparing opponent's stat score (~10 Elo) - if (ss->statScore >= -99 && (ss-1)->statScore < -116) + if (ss->statScore >= -100 && (ss-1)->statScore < -112) r--; - else if ((ss-1)->statScore >= -117 && ss->statScore < -150) + else if ((ss-1)->statScore >= -125 && ss->statScore < -138) r++; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - r -= ss->statScore / 15896; + r -= ss->statScore / 14615; } else { @@ -1212,7 +1212,7 @@ namespace { // Unless giving check, this capture is likely bad if ( !givesCheck - && ss->staticEval + PieceValue[EG][pos.captured_piece()] + 200 * depth <= alpha) + && ss->staticEval + PieceValue[EG][pos.captured_piece()] + 211 * depth <= alpha) r++; } @@ -1474,7 +1474,7 @@ namespace { if (PvNode && bestValue > alpha) alpha = bestValue; - futilityBase = bestValue + 138; + futilityBase = bestValue + 141; } const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, @@ -1724,8 +1724,8 @@ namespace { thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] = move; } - if (depth > 12 && ss->ply < MAX_LPH) - thisThread->lowPlyHistory[ss->ply][from_to(move)] << stat_bonus(depth - 7); + if (depth > 11 && ss->ply < MAX_LPH) + thisThread->lowPlyHistory[ss->ply][from_to(move)] << stat_bonus(depth - 6); } // When playing with strength handicap, choose best move among a set of RootMoves From 1ea488d34c0b6a03fa3d89d289fe72fd1408cafd Mon Sep 17 00:00:00 2001 From: mstembera Date: Sun, 14 Jun 2020 23:35:07 -0700 Subject: [PATCH 0251/1766] Use 128 bit multiply for TT index Remove super cluster stuff from TT and just use a 128 bit multiply. STC https://tests.stockfishchess.org/tests/view/5ee719b3aae8aec816ab7548 LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 12736 W: 2502 L: 2333 D: 7901 Ptnml(0-2): 191, 1452, 2944, 1559, 222 LTC https://tests.stockfishchess.org/tests/view/5ee732d1aae8aec816ab7556 LLR: 2.93 (-2.94,2.94) {-1.50,0.50} Total: 27584 W: 3431 L: 3350 D: 20803 Ptnml(0-2): 173, 2500, 8400, 2511, 208 Scheme back to being derived from https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ Also the default optimized version of the index calculation now uses fewer instructions. https://godbolt.org/z/Tktxbv Might benefit from mulx (requires -mbmi2) closes https://github.com/official-stockfish/Stockfish/pull/2744 bench: 4320954 --- src/misc.h | 13 +++++++++++++ src/search.cpp | 2 +- src/tt.cpp | 16 ++++++---------- src/tt.h | 12 ++---------- src/ucioption.cpp | 1 - 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/misc.h b/src/misc.h index 05bfc7de4d5..373f1b77c76 100644 --- a/src/misc.h +++ b/src/misc.h @@ -110,6 +110,19 @@ class PRNG { { return T(rand64() & rand64() & rand64()); } }; +inline uint64_t mul_hi64(uint64_t a, uint64_t b) { +#if defined(__GNUC__) && defined(IS_64BIT) + __extension__ typedef unsigned __int128 uint128; + return ((uint128)a * (uint128)b) >> 64; +#else + uint64_t aL = (uint32_t)a, aH = a >> 32; + uint64_t bL = (uint32_t)b, bH = b >> 32; + uint64_t c1 = (aL * bL) >> 32; + uint64_t c2 = aH * bL + c1; + uint64_t c3 = aL * bH + (uint32_t)c2; + return aH * bH + (c2 >> 32) + (c3 >> 32); +#endif +} /// Under Windows it is not possible for a process to run on more than one /// logical processor group. This usually means to be limited to use max 64 diff --git a/src/search.cpp b/src/search.cpp index cf89a892965..67339ed7898 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -662,7 +662,7 @@ namespace { // search to overwrite a previous full search TT value, so we use a different // position key in case of an excluded move. excludedMove = ss->excludedMove; - posKey = pos.key() ^ Key(excludedMove << 16); // Isn't a very good hash + posKey = pos.key() ^ (Key(excludedMove) << 48); // Isn't a very good hash tte = TT.probe(posKey, ttHit); ttValue = ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] diff --git a/src/tt.cpp b/src/tt.cpp index 92aaee00182..d0a5d4e0aa9 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -36,17 +36,17 @@ TranspositionTable TT; // Our global transposition table void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) { // Preserve any existing move for the same position - if (m || (k >> 48) != key16) + if (m || (uint16_t)k != key16) move16 = (uint16_t)m; // Overwrite less valuable entries - if ( (k >> 48) != key16 + if ((uint16_t)k != key16 || d - DEPTH_OFFSET > depth8 - 4 || b == BOUND_EXACT) { assert(d >= DEPTH_OFFSET); - key16 = (uint16_t)(k >> 48); + key16 = (uint16_t)k; value16 = (int16_t)v; eval16 = (int16_t)ev; genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b); @@ -65,10 +65,8 @@ void TranspositionTable::resize(size_t mbSize) { aligned_ttmem_free(mem); - superClusterCount = mbSize * 1024 * 1024 / (sizeof(Cluster) * ClustersPerSuperCluster); - - table = static_cast( - aligned_ttmem_alloc(superClusterCount * ClustersPerSuperCluster * sizeof(Cluster), mem)); + clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster); + table = static_cast(aligned_ttmem_alloc(clusterCount * sizeof(Cluster), mem)); if (!mem) { std::cerr << "Failed to allocate " << mbSize @@ -91,8 +89,6 @@ void TranspositionTable::clear() { { threads.emplace_back([this, idx]() { - const size_t clusterCount = superClusterCount * ClustersPerSuperCluster; - // Thread binding gives faster search on systems with a first-touch policy if (Options["Threads"] > 8) WinProcGroup::bindThisThread(idx); @@ -121,7 +117,7 @@ void TranspositionTable::clear() { TTEntry* TranspositionTable::probe(const Key key, bool& found) const { TTEntry* const tte = first_entry(key); - const uint16_t key16 = key >> 48; // Use the high 16 bits as key inside the cluster + const uint16_t key16 = (uint16_t)key; // Use the low 16 bits as key inside the cluster for (int i = 0; i < ClusterSize; ++i) if (!tte[i].key16 || tte[i].key16 == key16) diff --git a/src/tt.h b/src/tt.h index 76db03dabaf..3e1d0e99827 100644 --- a/src/tt.h +++ b/src/tt.h @@ -66,7 +66,6 @@ struct TTEntry { class TranspositionTable { static constexpr int ClusterSize = 3; - static constexpr int ClustersPerSuperCluster = 256; struct Cluster { TTEntry entry[ClusterSize]; @@ -84,20 +83,13 @@ class TranspositionTable { void clear(); TTEntry* first_entry(const Key key) const { - - // The index is computed from - // Idx = (K48 * SCC) / 2^40, with K48 the 48 lowest bits swizzled. - - const uint64_t firstTerm = uint32_t(key) * uint64_t(superClusterCount); - const uint64_t secondTerm = (uint16_t(key >> 32) * uint64_t(superClusterCount)) >> 16; - - return &table[(firstTerm + secondTerm) >> 24].entry[0]; + return &table[mul_hi64(key, clusterCount)].entry[0]; } private: friend struct TTEntry; - size_t superClusterCount; + size_t clusterCount; Cluster* table; void* mem; uint8_t generation8; // Size must be not bigger than TTEntry::genBound8 diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 90190b53b6c..7037ea57317 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -56,7 +56,6 @@ bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const void init(OptionsMap& o) { - // At most 2^32 superclusters. Supercluster = 8 kB constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048; o["Debug Log File"] << Option("", on_logger); From 4c72c95359e28ea3e5a4357a7679de794ebd3e55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Tue, 16 Jun 2020 13:21:24 +0200 Subject: [PATCH 0252/1766] Small bonus to favor thorn pawns We increase a little bit the midgame value of pawns on a4, h4, a6 and h6. Original idea by Malcolm Campbell, who tried the version restricted to the pawns on the H column a couple of weeks ago and got a patch which almost passed LTC. The current pull request just adds the same idea for pawns on the A column. Possible follow-ups: maybe tweak the a5/h5 pawn values, and/or add a malus for very low king mobility in midgame? STC: LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 33416 W: 6516 L: 6275 D: 20625 Ptnml(0-2): 575, 3847, 7659, 4016, 611 https://tests.stockfishchess.org/tests/view/5ee6c4e687586124bc2c10d4 LTC: LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 134368 W: 16869 L: 16319 D: 101180 Ptnml(0-2): 908, 12083, 40708, 12521, 964 https://tests.stockfishchess.org/tests/view/5ee74e60aae8aec816ab756a closes https://github.com/official-stockfish/Stockfish/pull/2747 Bench: 5299456 --- src/psqt.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/psqt.cpp b/src/psqt.cpp index 7fa36ac83dd..abd23547af3 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -91,9 +91,9 @@ constexpr Score PBonus[RANK_NB][FILE_NB] = { }, { S( 3,-10), S( 3, -6), S( 10, 10), S( 19, 0), S( 16, 14), S( 19, 7), S( 7, -5), S( -5,-19) }, { S( -9,-10), S(-15,-10), S( 11,-10), S( 15, 4), S( 32, 4), S( 22, 3), S( 5, -6), S(-22, -4) }, - { S( -8, 6), S(-23, -2), S( 6, -8), S( 20, -4), S( 40,-13), S( 17,-12), S( 4,-10), S(-12, -9) }, + { S( -4, 6), S(-23, -2), S( 6, -8), S( 20, -4), S( 40,-13), S( 17,-12), S( 4,-10), S( -8, -9) }, { S( 13, 9), S( 0, 4), S(-13, 3), S( 1,-12), S( 11,-12), S( -2, -6), S(-13, 13), S( 5, 8) }, - { S( -5, 28), S(-12, 20), S( -7, 21), S( 22, 28), S( -8, 30), S( -5, 7), S(-15, 6), S(-18, 13) }, + { S( 5, 28), S(-12, 20), S( -7, 21), S( 22, 28), S( -8, 30), S( -5, 7), S(-15, 6), S( -8, 13) }, { S( -7, 0), S( 7,-11), S( -3, 12), S(-13, 21), S( 5, 25), S(-16, 19), S( 10, 4), S( -8, 7) } }; From bc3c215490edf24cef0ff87d74ab01eeb91ae1bc Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Wed, 17 Jun 2020 05:36:30 +0300 Subject: [PATCH 0253/1766] More reduction for evading pawn moves. pawn moves are irreversable unlike other evading moves; pawn is the least valuable piece in the game. So it makes a lot of sence to assume that evading pawn moves are on average not as good as other evading moves thus can be reduced more. Passed STC https://tests.stockfishchess.org/tests/view/5ee9602e563bc7aa756002dc LLR: 2.95 (-2.94,2.94) {-0.50,1.50} Total: 94176 W: 17993 L: 17668 D: 58515 Ptnml(0-2): 1634, 10742, 21989, 11111, 1612 Passed LTC https://tests.stockfishchess.org/tests/view/5ee97342563bc7aa75600301 LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 20432 W: 2572 L: 2354 D: 15506 Ptnml(0-2): 146, 1707, 6280, 1949, 134 closes https://github.com/official-stockfish/Stockfish/pull/2749 Bench: 5073064 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 67339ed7898..d96ed7daac5 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1186,7 +1186,7 @@ namespace { // hence break make_move(). (~2 Elo) else if ( type_of(move) == NORMAL && !pos.see_ge(reverse_move(move))) - r -= 2 + ttPv; + r -= 2 + ttPv - (type_of(movedPiece) == PAWN); ss->statScore = thisThread->mainHistory[us][from_to(move)] + (*contHist[0])[movedPiece][to_sq(move)] From 6f15e7fab277c2595633ad08fdc25bdd7e0ab166 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 21 Jun 2020 15:21:46 +0200 Subject: [PATCH 0254/1766] small cleanups closes https://github.com/official-stockfish/Stockfish/pull/2695 No functional change --- src/Makefile | 2 +- src/bitbase.cpp | 16 ++++++++-------- src/bitboard.cpp | 25 ++++++++++++------------- src/bitboard.h | 2 +- src/evaluate.cpp | 11 +++++------ src/movepick.cpp | 2 +- src/movepick.h | 8 ++++---- src/pawns.cpp | 14 +++++++------- src/pawns.h | 2 +- src/psqt.cpp | 2 +- src/search.cpp | 2 +- src/syzygy/tbprobe.cpp | 6 +++--- src/timeman.cpp | 2 +- src/types.h | 17 ++++------------- 14 files changed, 50 insertions(+), 61 deletions(-) diff --git a/src/Makefile b/src/Makefile index 016aafec102..41c2aff6b64 100644 --- a/src/Makefile +++ b/src/Makefile @@ -336,7 +336,7 @@ ifeq ($(pext),yes) endif endif -### 3.8 Link Time Optimization, it works since gcc 4.5 but not on mingw under Windows. +### 3.8 Link Time Optimization ### This is a mix of compile and link time options because the lto link phase ### needs access to the optimization flags. ifeq ($(optimize),yes) diff --git a/src/bitbase.cpp b/src/bitbase.cpp index be6f0d0afd3..7e27eb96a10 100644 --- a/src/bitbase.cpp +++ b/src/bitbase.cpp @@ -108,25 +108,25 @@ namespace { stm = Color ((idx >> 12) & 0x01); psq = make_square(File((idx >> 13) & 0x3), Rank(RANK_7 - ((idx >> 15) & 0x7))); - // Check if two pieces are on the same square or if a king can be captured + // Invalid if two pieces are on the same square or if a king can be captured if ( distance(ksq[WHITE], ksq[BLACK]) <= 1 || ksq[WHITE] == psq || ksq[BLACK] == psq || (stm == WHITE && (pawn_attacks_bb(WHITE, psq) & ksq[BLACK]))) result = INVALID; - // Immediate win if a pawn can be promoted without getting captured + // Win if the pawn can be promoted without getting captured else if ( stm == WHITE && rank_of(psq) == RANK_7 - && ksq[stm] != psq + NORTH - && ( distance(ksq[~stm], psq + NORTH) > 1 - || (attacks_bb(ksq[stm]) & (psq + NORTH)))) + && ksq[WHITE] != psq + NORTH + && ( distance(ksq[BLACK], psq + NORTH) > 1 + || (distance(ksq[WHITE], psq + NORTH) == 1))) result = WIN; - // Immediate draw if it is a stalemate or a king captures undefended pawn + // Draw if it is stalemate or the black king can capture the pawn else if ( stm == BLACK - && ( !(attacks_bb(ksq[stm]) & ~(attacks_bb(ksq[~stm]) | pawn_attacks_bb(~stm, psq))) - || (attacks_bb(ksq[stm]) & psq & ~attacks_bb(ksq[~stm])))) + && ( !(attacks_bb(ksq[BLACK]) & ~(attacks_bb(ksq[WHITE]) | pawn_attacks_bb(WHITE, psq))) + || (attacks_bb(ksq[BLACK]) & ~attacks_bb(ksq[WHITE]) & psq))) result = DRAW; // Position will be classified later diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 3bb3ff8f8d5..0bf7eef91fd 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -40,7 +40,7 @@ namespace { Bitboard RookTable[0x19000]; // To store rook attacks Bitboard BishopTable[0x1480]; // To store bishop attacks - void init_magics(Bitboard table[], Magic magics[], Direction directions[]); + void init_magics(PieceType pt, Bitboard table[], Magic magics[]); } @@ -79,11 +79,8 @@ void Bitboards::init() { for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) SquareDistance[s1][s2] = std::max(distance(s1, s2), distance(s1, s2)); - Direction RookDirections[] = { NORTH, EAST, SOUTH, WEST }; - Direction BishopDirections[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }; - - init_magics(RookTable, RookMagics, RookDirections); - init_magics(BishopTable, BishopMagics, BishopDirections); + init_magics(ROOK, RookTable, RookMagics); + init_magics(BISHOP, BishopTable, BishopMagics); for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) { @@ -109,15 +106,17 @@ void Bitboards::init() { namespace { - Bitboard sliding_attack(Direction directions[], Square sq, Bitboard occupied) { + Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) { Bitboard attacks = 0; + Direction RookDirections[4] = {NORTH, SOUTH, EAST, WEST}; + Direction BishopDirections[4] = {NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST}; - for (int i = 0; i < 4; ++i) + for(Direction d : (pt == ROOK ? RookDirections : BishopDirections)) { Square s = sq; - while(safe_destination(s, directions[i]) && !(occupied & s)) - attacks |= (s += directions[i]); + while(safe_destination(s, d) && !(occupied & s)) + attacks |= (s += d); } return attacks; @@ -129,7 +128,7 @@ namespace { // www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so // called "fancy" approach. - void init_magics(Bitboard table[], Magic magics[], Direction directions[]) { + void init_magics(PieceType pt, Bitboard table[], Magic magics[]) { // Optimal PRNG seeds to pick the correct magics in the shortest time int seeds[][RANK_NB] = { { 8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020 }, @@ -149,7 +148,7 @@ namespace { // the number of 1s of the mask. Hence we deduce the size of the shift to // apply to the 64 or 32 bits word to get the index. Magic& m = magics[s]; - m.mask = sliding_attack(directions, s, 0) & ~edges; + m.mask = sliding_attack(pt, s, 0) & ~edges; m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask); // Set the offset for the attacks table of the square. We have individual @@ -161,7 +160,7 @@ namespace { b = size = 0; do { occupancy[size] = b; - reference[size] = sliding_attack(directions, s, b); + reference[size] = sliding_attack(pt, s, b); if (HasPext) m.attacks[pext(b, m.mask)] = reference[size]; diff --git a/src/bitboard.h b/src/bitboard.h index 704f4bb4e8a..0f55810cece 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -253,7 +253,7 @@ inline Bitboard pawn_attack_span(Color c, Square s) { /// the given color and on the given square is a passed pawn. inline Bitboard passed_pawn_span(Color c, Square s) { - return forward_ranks_bb(c, s) & (adjacent_files_bb(s) | file_bb(s)); + return pawn_attack_span(c, s) | forward_file_bb(c, s); } diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 036b93a933c..3b0891a2d4f 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -676,16 +676,15 @@ namespace { } - // Evaluation::space() computes the space evaluation for a given side. The - // space evaluation is a simple bonus based on the number of safe squares - // available for minor pieces on the central four files on ranks 2--4. Safe - // squares one, two or three squares behind a friendly pawn are counted - // twice. Finally, the space bonus is multiplied by a weight. The aim is to - // improve play on game opening. + // Evaluation::space() computes a space evaluation for a given side, aiming to improve game + // play in the opening. It is based on the number of safe squares on the 4 central files + // on ranks 2 to 4. Completely safe squares behind a friendly pawn are counted twice. + // Finally, the space bonus is multiplied by a weight which decreases according to occupancy. template template Score Evaluation::space() const { + // Early exit if, for example, both queens or 6 minor pieces have been exchanged if (pos.non_pawn_material() < SpaceThreshold) return SCORE_ZERO; diff --git a/src/movepick.cpp b/src/movepick.cpp index 78102c52a34..5775f810600 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -57,7 +57,7 @@ namespace { /// MovePicker constructor for the main search MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const LowPlyHistory* lp, - const CapturePieceToHistory* cph, const PieceToHistory** ch, Move cm, Move* killers, int pl) + const CapturePieceToHistory* cph, const PieceToHistory** ch, Move cm, const Move* killers, int pl) : pos(p), mainHistory(mh), lowPlyHistory(lp), captureHistory(cph), continuationHistory(ch), ttMove(ttm), refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d), ply(pl) { diff --git a/src/movepick.h b/src/movepick.h index 33c4b08602b..aaff388f641 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -88,9 +88,9 @@ enum StatsType { NoCaptures, Captures }; /// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards typedef Stats ButterflyHistory; -/// LowPlyHistory at higher depths records successful quiet moves on plies 0 to 3 -/// and quiet moves which are/were in the PV (ttPv) -/// It get cleared with each new search and get filled during iterative deepening +/// At higher depths LowPlyHistory records successful quiet moves near the root and quiet +/// moves which are/were in the PV (ttPv) +/// It is cleared with each new search and filled during iterative deepening constexpr int MAX_LPH = 4; typedef Stats LowPlyHistory; @@ -133,7 +133,7 @@ class MovePicker { const CapturePieceToHistory*, const PieceToHistory**, Move, - Move*, + const Move*, int); Move next_move(bool skipQuiets = false); diff --git a/src/pawns.cpp b/src/pawns.cpp index c1119a4102c..597dff2b0fb 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -150,17 +150,17 @@ namespace { && !(theirPawns & adjacent_files_bb(s))) score -= Doubled; else - score -= Isolated - + WeakUnopposed * !opposed; + score -= Isolated + + WeakUnopposed * !opposed; } else if (backward) - score -= Backward - + WeakUnopposed * !opposed; + score -= Backward + + WeakUnopposed * !opposed; if (!support) - score -= Doubled * doubled - + WeakLever * more_than_one(lever); + score -= Doubled * doubled + + WeakLever * more_than_one(lever); } return score; @@ -196,7 +196,7 @@ Entry* probe(const Position& pos) { /// penalty for a king, looking at the king file and the two closest files. template -Score Entry::evaluate_shelter(const Position& pos, Square ksq) { +Score Entry::evaluate_shelter(const Position& pos, Square ksq) const { constexpr Color Them = ~Us; diff --git a/src/pawns.h b/src/pawns.h index a3284a0fc64..e6098069025 100644 --- a/src/pawns.h +++ b/src/pawns.h @@ -50,7 +50,7 @@ struct Entry { Score do_king_safety(const Position& pos); template - Score evaluate_shelter(const Position& pos, Square ksq); + Score evaluate_shelter(const Position& pos, Square ksq) const; Key key; Score scores[COLOR_NB]; diff --git a/src/psqt.cpp b/src/psqt.cpp index abd23547af3..27c7a36f9f3 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -106,7 +106,7 @@ Score psq[PIECE_NB][SQUARE_NB]; // tables are initialized by flipping and changing the sign of the white scores. void init() { - for (Piece pc = W_PAWN; pc <= W_KING; ++pc) + for (Piece pc : {W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING}) { Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]); diff --git a/src/search.cpp b/src/search.cpp index d96ed7daac5..563d1aab410 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -670,7 +670,7 @@ namespace { ttPv = PvNode || (ttHit && tte->is_pv()); formerPv = ttPv && !PvNode; - if (ttPv && depth > 12 && ss->ply - 1 < MAX_LPH && !pos.captured_piece() && is_ok((ss-1)->currentMove)) + if (ttPv && depth > 12 && ss->ply - 1 < MAX_LPH && !priorCapture && is_ok((ss-1)->currentMove)) thisThread->lowPlyHistory[ss->ply - 1][from_to((ss-1)->currentMove)] << stat_bonus(depth - 5); // thisThread->ttHitAverage can be used to approximate the running average of ttHit diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 6bfd78ad0df..95d58945d72 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1200,7 +1200,7 @@ WDLScore search(Position& pos, ProbeState* result) { auto moveList = MoveList(pos); size_t totalCount = moveList.size(), moveCount = 0; - for (const Move& move : moveList) + for (const Move move : moveList) { if ( !pos.capture(move) && (!CheckZeroingMoves || type_of(pos.moved_piece(move)) != PAWN)) @@ -1362,7 +1362,7 @@ void Tablebases::init(const std::string& paths) { LeadPawnsSize[leadPawnsCnt][f] = idx; } - // Add entries in TB tables if the corresponding ".rtbw" file exsists + // Add entries in TB tables if the corresponding ".rtbw" file exists for (PieceType p1 = PAWN; p1 < KING; ++p1) { TBTables.add({KING, p1, KING}); @@ -1469,7 +1469,7 @@ int Tablebases::probe_dtz(Position& pos, ProbeState* result) { StateInfo st; int minDTZ = 0xFFFF; - for (const Move& move : MoveList(pos)) + for (const Move move : MoveList(pos)) { bool zeroing = pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN; diff --git a/src/timeman.cpp b/src/timeman.cpp index 1f598745ab0..d27962b7fc0 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -79,7 +79,7 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { { opt_scale = std::min(0.008 + std::pow(ply + 3.0, 0.5) / 250.0, 0.2 * limits.time[us] / double(timeLeft)); - max_scale = 4 + std::min(36, ply) / 12.0; + max_scale = std::min(7.0, 4.0 + ply / 12.0); } // x moves in y seconds (+ z increment) diff --git a/src/types.h b/src/types.h index 580c846a10d..969d4e65e08 100644 --- a/src/types.h +++ b/src/types.h @@ -40,7 +40,6 @@ #include #include -#include #include #include #include @@ -214,7 +213,6 @@ constexpr Value PieceValue[PHASE_NB][PIECE_NB] = { typedef int Depth; enum : int { - DEPTH_QS_CHECKS = 0, DEPTH_QS_NO_CHECKS = -1, DEPTH_QS_RECAPTURES = -5, @@ -282,11 +280,11 @@ inline Value mg_value(Score s) { } #define ENABLE_BASE_OPERATORS_ON(T) \ -constexpr T operator+(T d1, T d2) { return T(int(d1) + int(d2)); } \ -constexpr T operator-(T d1, T d2) { return T(int(d1) - int(d2)); } \ +constexpr T operator+(T d1, int d2) { return T(int(d1) + d2); } \ +constexpr T operator-(T d1, int d2) { return T(int(d1) - d2); } \ constexpr T operator-(T d) { return T(-int(d)); } \ -inline T& operator+=(T& d1, T d2) { return d1 = d1 + d2; } \ -inline T& operator-=(T& d1, T d2) { return d1 = d1 - d2; } +inline T& operator+=(T& d1, int d2) { return d1 = d1 + d2; } \ +inline T& operator-=(T& d1, int d2) { return d1 = d1 - d2; } #define ENABLE_INCR_OPERATORS_ON(T) \ inline T& operator++(T& d) { return d = T(int(d) + 1); } \ @@ -305,7 +303,6 @@ ENABLE_FULL_OPERATORS_ON(Value) ENABLE_FULL_OPERATORS_ON(Direction) ENABLE_INCR_OPERATORS_ON(PieceType) -ENABLE_INCR_OPERATORS_ON(Piece) ENABLE_INCR_OPERATORS_ON(Square) ENABLE_INCR_OPERATORS_ON(File) ENABLE_INCR_OPERATORS_ON(Rank) @@ -316,12 +313,6 @@ ENABLE_BASE_OPERATORS_ON(Score) #undef ENABLE_INCR_OPERATORS_ON #undef ENABLE_BASE_OPERATORS_ON -/// Additional operators to add integers to a Value -constexpr Value operator+(Value v, int i) { return Value(int(v) + i); } -constexpr Value operator-(Value v, int i) { return Value(int(v) - i); } -inline Value& operator+=(Value& v, int i) { return v = v + i; } -inline Value& operator-=(Value& v, int i) { return v = v - i; } - /// Additional operators to add a Direction to a Square constexpr Square operator+(Square s, Direction d) { return Square(int(s) + int(d)); } constexpr Square operator-(Square s, Direction d) { return Square(int(s) - int(d)); } From 8a3f155b1cc1175b33ddc97d0572b5557269b0fa Mon Sep 17 00:00:00 2001 From: protonspring Date: Wed, 17 Jun 2020 15:15:54 -0600 Subject: [PATCH 0255/1766] Make endgames consistent Changes variable names and occasionally consolidated variable declarations. Piece squares are consistently prefixed with "weak" or "strong." passed STC LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 29008 W: 5532 L: 5416 D: 18060 Ptnml(0-2): 355, 2983, 7723, 3077, 366 https://tests.stockfishchess.org/tests/view/5eea88d3563bc7aa75600689 closes https://github.com/official-stockfish/Stockfish/pull/2752 No functional change --- src/endgame.cpp | 386 ++++++++++++++++++++++++------------------------ 1 file changed, 195 insertions(+), 191 deletions(-) diff --git a/src/endgame.cpp b/src/endgame.cpp index 7b9c145e428..d9e76348ecd 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -28,12 +28,14 @@ namespace { // Used to drive the king towards the edge of the board // in KX vs K and KQ vs KR endgames. + // Values range from 27 (center squares) to 90 (in the corners) inline int push_to_edge(Square s) { int rd = edge_distance(rank_of(s)), fd = edge_distance(file_of(s)); return 90 - (7 * fd * fd / 2 + 7 * rd * rd / 2); } // Used to drive the king towards A1H8 corners in KBN vs K endgames. + // Values range from 0 on A8H1 diagonal to 7 in A1H8 corners inline int push_to_corner(Square s) { return abs(7 - rank_of(s) - file_of(s)); } @@ -103,13 +105,13 @@ Value Endgame::operator()(const Position& pos) const { if (pos.side_to_move() == weakSide && !MoveList(pos).size()) return VALUE_DRAW; - Square winnerKSq = pos.square(strongSide); - Square loserKSq = pos.square(weakSide); + Square strongKing = pos.square(strongSide); + Square weakKing = pos.square(weakSide); Value result = pos.non_pawn_material(strongSide) + pos.count(strongSide) * PawnValueEg - + push_to_edge(loserKSq) - + push_close(winnerKSq, loserKSq); + + push_to_edge(weakKing) + + push_close(strongKing, weakKing); if ( pos.count(strongSide) || pos.count(strongSide) @@ -130,16 +132,16 @@ Value Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, KnightValueMg + BishopValueMg, 0)); assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); - Square winnerKSq = pos.square(strongSide); - Square loserKSq = pos.square(weakSide); - Square bishopSq = pos.square(strongSide); + Square strongKing = pos.square(strongSide); + Square strongBishop = pos.square(strongSide); + Square weakKing = pos.square(weakSide); // If our bishop does not attack A1/H8, we flip the enemy king square // to drive to opposite corners (A8/H1). Value result = (VALUE_KNOWN_WIN + 3520) - + push_close(winnerKSq, loserKSq) - + 420 * push_to_corner(opposite_colors(bishopSq, SQ_A1) ? flip_file(loserKSq) : loserKSq); + + push_close(strongKing, weakKing) + + 420 * push_to_corner(opposite_colors(strongBishop, SQ_A1) ? flip_file(weakKing) : weakKing); assert(abs(result) < VALUE_TB_WIN_IN_MAX_PLY); return strongSide == pos.side_to_move() ? result : -result; @@ -154,16 +156,16 @@ Value Endgame::operator()(const Position& pos) const { assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); // Assume strongSide is white and the pawn is on files A-D - Square wksq = normalize(pos, strongSide, pos.square(strongSide)); - Square bksq = normalize(pos, strongSide, pos.square(weakSide)); - Square psq = normalize(pos, strongSide, pos.square(strongSide)); + Square strongKing = normalize(pos, strongSide, pos.square(strongSide)); + Square strongPawn = normalize(pos, strongSide, pos.square(strongSide)); + Square weakKing = normalize(pos, strongSide, pos.square(weakSide)); Color us = strongSide == pos.side_to_move() ? WHITE : BLACK; - if (!Bitbases::probe(wksq, psq, bksq, us)) + if (!Bitbases::probe(strongKing, strongPawn, weakKing, us)) return VALUE_DRAW; - Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(psq)); + Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(strongPawn)); return strongSide == pos.side_to_move() ? result : -result; } @@ -179,36 +181,35 @@ Value Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, RookValueMg, 0)); assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); - Square wksq = relative_square(strongSide, pos.square(strongSide)); - Square bksq = relative_square(strongSide, pos.square(weakSide)); - Square rsq = relative_square(strongSide, pos.square(strongSide)); - Square psq = relative_square(strongSide, pos.square(weakSide)); - - Square queeningSq = make_square(file_of(psq), RANK_1); + Square strongKing = relative_square(strongSide, pos.square(strongSide)); + Square weakKing = relative_square(strongSide, pos.square(weakSide)); + Square strongRook = relative_square(strongSide, pos.square(strongSide)); + Square weakPawn = relative_square(strongSide, pos.square(weakSide)); + Square queeningSquare = make_square(file_of(weakPawn), RANK_1); Value result; // If the stronger side's king is in front of the pawn, it's a win - if (forward_file_bb(WHITE, wksq) & psq) - result = RookValueEg - distance(wksq, psq); + if (forward_file_bb(WHITE, strongKing) & weakPawn) + result = RookValueEg - distance(strongKing, weakPawn); // If the weaker side's king is too far from the pawn and the rook, // it's a win. - else if ( distance(bksq, psq) >= 3 + (pos.side_to_move() == weakSide) - && distance(bksq, rsq) >= 3) - result = RookValueEg - distance(wksq, psq); + else if ( distance(weakKing, weakPawn) >= 3 + (pos.side_to_move() == weakSide) + && distance(weakKing, strongRook) >= 3) + result = RookValueEg - distance(strongKing, weakPawn); // If the pawn is far advanced and supported by the defending king, // the position is drawish - else if ( rank_of(bksq) <= RANK_3 - && distance(bksq, psq) == 1 - && rank_of(wksq) >= RANK_4 - && distance(wksq, psq) > 2 + (pos.side_to_move() == strongSide)) - result = Value(80) - 8 * distance(wksq, psq); + else if ( rank_of(weakKing) <= RANK_3 + && distance(weakKing, weakPawn) == 1 + && rank_of(strongKing) >= RANK_4 + && distance(strongKing, weakPawn) > 2 + (pos.side_to_move() == strongSide)) + result = Value(80) - 8 * distance(strongKing, weakPawn); else - result = Value(200) - 8 * ( distance(wksq, psq + SOUTH) - - distance(bksq, psq + SOUTH) - - distance(psq, queeningSq)); + result = Value(200) - 8 * ( distance(strongKing, weakPawn + SOUTH) + - distance(weakKing, weakPawn + SOUTH) + - distance(weakPawn, queeningSquare)); return strongSide == pos.side_to_move() ? result : -result; } @@ -235,9 +236,9 @@ Value Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, RookValueMg, 0)); assert(verify_material(pos, weakSide, KnightValueMg, 0)); - Square bksq = pos.square(weakSide); - Square bnsq = pos.square(weakSide); - Value result = Value(push_to_edge(bksq) + push_away(bksq, bnsq)); + Square weakKing = pos.square(weakSide); + Square weakKnight = pos.square(weakSide); + Value result = Value(push_to_edge(weakKing) + push_away(weakKing, weakKnight)); return strongSide == pos.side_to_move() ? result : -result; } @@ -252,15 +253,15 @@ Value Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, QueenValueMg, 0)); assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); - Square winnerKSq = pos.square(strongSide); - Square loserKSq = pos.square(weakSide); - Square pawnSq = pos.square(weakSide); + Square strongKing = pos.square(strongSide); + Square weakKing = pos.square(weakSide); + Square weakPawn = pos.square(weakSide); - Value result = Value(push_close(winnerKSq, loserKSq)); + Value result = Value(push_close(strongKing, weakKing)); - if ( relative_rank(weakSide, pawnSq) != RANK_7 - || distance(loserKSq, pawnSq) != 1 - || ((FileBBB | FileDBB | FileEBB | FileGBB) & pawnSq)) + if ( relative_rank(weakSide, weakPawn) != RANK_7 + || distance(weakKing, weakPawn) != 1 + || ((FileBBB | FileDBB | FileEBB | FileGBB) & weakPawn)) result += QueenValueEg - PawnValueEg; return strongSide == pos.side_to_move() ? result : -result; @@ -277,13 +278,13 @@ Value Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, QueenValueMg, 0)); assert(verify_material(pos, weakSide, RookValueMg, 0)); - Square winnerKSq = pos.square(strongSide); - Square loserKSq = pos.square(weakSide); + Square strongKing = pos.square(strongSide); + Square weakKing = pos.square(weakSide); Value result = QueenValueEg - RookValueEg - + push_to_edge(loserKSq) - + push_close(winnerKSq, loserKSq); + + push_to_edge(weakKing) + + push_close(strongKing, weakKing); return strongSide == pos.side_to_move() ? result : -result; } @@ -297,9 +298,12 @@ Value Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, 2 * KnightValueMg, 0)); assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); + Square weakKing = pos.square(weakSide); + Square weakPawn = pos.square(weakSide); + Value result = PawnValueEg - + 2 * push_to_edge(pos.square(weakSide)) - - 10 * relative_rank(weakSide, pos.square(weakSide)); + + 2 * push_to_edge(weakKing) + - 10 * relative_rank(weakSide, weakPawn); return strongSide == pos.side_to_move() ? result : -result; } @@ -325,15 +329,17 @@ ScaleFactor Endgame::operator()(const Position& pos) const { Bitboard strongPawns = pos.pieces(strongSide, PAWN); Bitboard allPawns = pos.pieces(PAWN); + Square strongBishop = pos.square(strongSide); + Square weakKing = pos.square(weakSide); + Square strongKing = pos.square(strongSide); + // All strongSide pawns are on a single rook file? if (!(strongPawns & ~FileABB) || !(strongPawns & ~FileHBB)) { - Square bishopSq = pos.square(strongSide); - Square queeningSq = relative_square(strongSide, make_square(file_of(lsb(strongPawns)), RANK_8)); - Square weakKingSq = pos.square(weakSide); + Square queeningSquare = relative_square(strongSide, make_square(file_of(lsb(strongPawns)), RANK_8)); - if ( opposite_colors(queeningSq, bishopSq) - && distance(queeningSq, weakKingSq) <= 1) + if ( opposite_colors(queeningSquare, strongBishop) + && distance(queeningSquare, weakKing) <= 1) return SCALE_FACTOR_DRAW; } @@ -343,20 +349,16 @@ ScaleFactor Endgame::operator()(const Position& pos) const { && pos.count(weakSide) >= 1) { // Get the least advanced weakSide pawn - Square weakPawnSq = frontmost_sq(strongSide, pos.pieces(weakSide, PAWN)); - - Square strongKingSq = pos.square(strongSide); - Square weakKingSq = pos.square(weakSide); - Square bishopSq = pos.square(strongSide); + Square weakPawn = frontmost_sq(strongSide, pos.pieces(weakSide, PAWN)); // There's potential for a draw if our pawn is blocked on the 7th rank, // the bishop cannot attack it or they only have one pawn left - if ( relative_rank(strongSide, weakPawnSq) == RANK_7 - && (strongPawns & (weakPawnSq + pawn_push(weakSide))) - && (opposite_colors(bishopSq, weakPawnSq) || !more_than_one(strongPawns))) + if ( relative_rank(strongSide, weakPawn) == RANK_7 + && (strongPawns & (weakPawn + pawn_push(weakSide))) + && (opposite_colors(strongBishop, weakPawn) || !more_than_one(strongPawns))) { - int strongKingDist = distance(weakPawnSq, strongKingSq); - int weakKingDist = distance(weakPawnSq, weakKingSq); + int strongKingDist = distance(weakPawn, strongKing); + int weakKingDist = distance(weakPawn, weakKing); // It's a draw if the weak king is on its back two ranks, within 2 // squares of the blocking pawn and the strong king is not @@ -364,7 +366,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // unreachable positions such as 5k1K/6p1/6P1/8/8/3B4/8/8 w // and positions where qsearch will immediately correct the // problem such as 8/4k1p1/6P1/1K6/3B4/8/8/8 w) - if ( relative_rank(strongSide, weakKingSq) >= RANK_7 + if ( relative_rank(strongSide, weakKing) >= RANK_7 && weakKingDist <= 2 && weakKingDist <= strongKingDist) return SCALE_FACTOR_DRAW; @@ -384,15 +386,16 @@ ScaleFactor Endgame::operator()(const Position& pos) const { assert(pos.count(weakSide) == 1); assert(pos.count(weakSide) >= 1); - Square kingSq = pos.square(weakSide); - Square rsq = pos.square(weakSide); + Square strongKing = pos.square(strongSide); + Square weakKing = pos.square(weakSide); + Square weakRook = pos.square(weakSide); - if ( relative_rank(weakSide, kingSq) <= RANK_2 - && relative_rank(weakSide, pos.square(strongSide)) >= RANK_4 - && relative_rank(weakSide, rsq) == RANK_3 + if ( relative_rank(weakSide, weakKing) <= RANK_2 + && relative_rank(weakSide, strongKing) >= RANK_4 + && relative_rank(weakSide, weakRook) == RANK_3 && ( pos.pieces(weakSide, PAWN) - & attacks_bb(kingSq) - & pawn_attacks_bb(strongSide, rsq))) + & attacks_bb(weakKing) + & pawn_attacks_bb(strongSide, weakRook))) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; @@ -412,89 +415,89 @@ ScaleFactor Endgame::operator()(const Position& pos) const { assert(verify_material(pos, weakSide, RookValueMg, 0)); // Assume strongSide is white and the pawn is on files A-D - Square wksq = normalize(pos, strongSide, pos.square(strongSide)); - Square bksq = normalize(pos, strongSide, pos.square(weakSide)); - Square wrsq = normalize(pos, strongSide, pos.square(strongSide)); - Square wpsq = normalize(pos, strongSide, pos.square(strongSide)); - Square brsq = normalize(pos, strongSide, pos.square(weakSide)); - - File f = file_of(wpsq); - Rank r = rank_of(wpsq); - Square queeningSq = make_square(f, RANK_8); + Square strongKing = normalize(pos, strongSide, pos.square(strongSide)); + Square strongRook = normalize(pos, strongSide, pos.square(strongSide)); + Square strongPawn = normalize(pos, strongSide, pos.square(strongSide)); + Square weakKing = normalize(pos, strongSide, pos.square(weakSide)); + Square weakRook = normalize(pos, strongSide, pos.square(weakSide)); + + File pawnFile = file_of(strongPawn); + Rank pawnRank = rank_of(strongPawn); + Square queeningSquare = make_square(pawnFile, RANK_8); int tempo = (pos.side_to_move() == strongSide); // If the pawn is not too far advanced and the defending king defends the // queening square, use the third-rank defence. - if ( r <= RANK_5 - && distance(bksq, queeningSq) <= 1 - && wksq <= SQ_H5 - && (rank_of(brsq) == RANK_6 || (r <= RANK_3 && rank_of(wrsq) != RANK_6))) + if ( pawnRank <= RANK_5 + && distance(weakKing, queeningSquare) <= 1 + && strongKing <= SQ_H5 + && (rank_of(weakRook) == RANK_6 || (pawnRank <= RANK_3 && rank_of(strongRook) != RANK_6))) return SCALE_FACTOR_DRAW; // The defending side saves a draw by checking from behind in case the pawn // has advanced to the 6th rank with the king behind. - if ( r == RANK_6 - && distance(bksq, queeningSq) <= 1 - && rank_of(wksq) + tempo <= RANK_6 - && (rank_of(brsq) == RANK_1 || (!tempo && distance(brsq, wpsq) >= 3))) + if ( pawnRank == RANK_6 + && distance(weakKing, queeningSquare) <= 1 + && rank_of(strongKing) + tempo <= RANK_6 + && (rank_of(weakRook) == RANK_1 || (!tempo && distance(weakRook, strongPawn) >= 3))) return SCALE_FACTOR_DRAW; - if ( r >= RANK_6 - && bksq == queeningSq - && rank_of(brsq) == RANK_1 - && (!tempo || distance(wksq, wpsq) >= 2)) + if ( pawnRank >= RANK_6 + && weakKing == queeningSquare + && rank_of(weakRook) == RANK_1 + && (!tempo || distance(strongKing, strongPawn) >= 2)) return SCALE_FACTOR_DRAW; // White pawn on a7 and rook on a8 is a draw if black's king is on g7 or h7 // and the black rook is behind the pawn. - if ( wpsq == SQ_A7 - && wrsq == SQ_A8 - && (bksq == SQ_H7 || bksq == SQ_G7) - && file_of(brsq) == FILE_A - && (rank_of(brsq) <= RANK_3 || file_of(wksq) >= FILE_D || rank_of(wksq) <= RANK_5)) + if ( strongPawn == SQ_A7 + && strongRook == SQ_A8 + && (weakKing == SQ_H7 || weakKing == SQ_G7) + && file_of(weakRook) == FILE_A + && (rank_of(weakRook) <= RANK_3 || file_of(strongKing) >= FILE_D || rank_of(strongKing) <= RANK_5)) return SCALE_FACTOR_DRAW; // If the defending king blocks the pawn and the attacking king is too far // away, it's a draw. - if ( r <= RANK_5 - && bksq == wpsq + NORTH - && distance(wksq, wpsq) - tempo >= 2 - && distance(wksq, brsq) - tempo >= 2) + if ( pawnRank <= RANK_5 + && weakKing == strongPawn + NORTH + && distance(strongKing, strongPawn) - tempo >= 2 + && distance(strongKing, weakRook) - tempo >= 2) return SCALE_FACTOR_DRAW; // Pawn on the 7th rank supported by the rook from behind usually wins if the // attacking king is closer to the queening square than the defending king, // and the defending king cannot gain tempi by threatening the attacking rook. - if ( r == RANK_7 - && f != FILE_A - && file_of(wrsq) == f - && wrsq != queeningSq - && (distance(wksq, queeningSq) < distance(bksq, queeningSq) - 2 + tempo) - && (distance(wksq, queeningSq) < distance(bksq, wrsq) + tempo)) - return ScaleFactor(SCALE_FACTOR_MAX - 2 * distance(wksq, queeningSq)); + if ( pawnRank == RANK_7 + && pawnFile != FILE_A + && file_of(strongRook) == pawnFile + && strongRook != queeningSquare + && (distance(strongKing, queeningSquare) < distance(weakKing, queeningSquare) - 2 + tempo) + && (distance(strongKing, queeningSquare) < distance(weakKing, strongRook) + tempo)) + return ScaleFactor(SCALE_FACTOR_MAX - 2 * distance(strongKing, queeningSquare)); // Similar to the above, but with the pawn further back - if ( f != FILE_A - && file_of(wrsq) == f - && wrsq < wpsq - && (distance(wksq, queeningSq) < distance(bksq, queeningSq) - 2 + tempo) - && (distance(wksq, wpsq + NORTH) < distance(bksq, wpsq + NORTH) - 2 + tempo) - && ( distance(bksq, wrsq) + tempo >= 3 - || ( distance(wksq, queeningSq) < distance(bksq, wrsq) + tempo - && (distance(wksq, wpsq + NORTH) < distance(bksq, wrsq) + tempo)))) + if ( pawnFile != FILE_A + && file_of(strongRook) == pawnFile + && strongRook < strongPawn + && (distance(strongKing, queeningSquare) < distance(weakKing, queeningSquare) - 2 + tempo) + && (distance(strongKing, strongPawn + NORTH) < distance(weakKing, strongPawn + NORTH) - 2 + tempo) + && ( distance(weakKing, strongRook) + tempo >= 3 + || ( distance(strongKing, queeningSquare) < distance(weakKing, strongRook) + tempo + && (distance(strongKing, strongPawn + NORTH) < distance(weakKing, strongPawn) + tempo)))) return ScaleFactor( SCALE_FACTOR_MAX - - 8 * distance(wpsq, queeningSq) - - 2 * distance(wksq, queeningSq)); + - 8 * distance(strongPawn, queeningSquare) + - 2 * distance(strongKing, queeningSquare)); // If the pawn is not far advanced and the defending king is somewhere in // the pawn's path, it's probably a draw. - if (r <= RANK_4 && bksq > wpsq) + if (pawnRank <= RANK_4 && weakKing > strongPawn) { - if (file_of(bksq) == file_of(wpsq)) + if (file_of(weakKing) == file_of(strongPawn)) return ScaleFactor(10); - if ( distance(bksq, wpsq) == 1 - && distance(wksq, bksq) > 2) - return ScaleFactor(24 - 2 * distance(wksq, bksq)); + if ( distance(weakKing, strongPawn) == 1 + && distance(strongKing, weakKing) > 2) + return ScaleFactor(24 - 2 * distance(strongKing, weakKing)); } return SCALE_FACTOR_NONE; } @@ -508,10 +511,11 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // Test for a rook pawn if (pos.pieces(PAWN) & (FileABB | FileHBB)) { - Square ksq = pos.square(weakSide); - Square bsq = pos.square(weakSide); - Square psq = pos.square(strongSide); - Rank rk = relative_rank(strongSide, psq); + Square weakKing = pos.square(weakSide); + Square weakBishop = pos.square(weakSide); + Square strongKing = pos.square(strongSide); + Square strongPawn = pos.square(strongSide); + Rank pawnRank = relative_rank(strongSide, strongPawn); Direction push = pawn_push(strongSide); // If the pawn is on the 5th rank and the pawn (currently) is on @@ -519,11 +523,11 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // a fortress. Depending on the king position give a moderate // reduction or a stronger one if the defending king is near the // corner but not trapped there. - if (rk == RANK_5 && !opposite_colors(bsq, psq)) + if (pawnRank == RANK_5 && !opposite_colors(weakBishop, strongPawn)) { - int d = distance(psq + 3 * push, ksq); + int d = distance(strongPawn + 3 * push, weakKing); - if (d <= 2 && !(d == 0 && ksq == pos.square(strongSide) + 2 * push)) + if (d <= 2 && !(d == 0 && weakKing == strongKing + 2 * push)) return ScaleFactor(24); else return ScaleFactor(48); @@ -533,10 +537,10 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // it's drawn if the bishop attacks the square in front of the // pawn from a reasonable distance and the defending king is near // the corner - if ( rk == RANK_6 - && distance(psq + 2 * push, ksq) <= 1 - && (attacks_bb(bsq) & (psq + push)) - && distance(bsq, psq) >= 2) + if ( pawnRank == RANK_6 + && distance(strongPawn + 2 * push, weakKing) <= 1 + && (attacks_bb(weakBishop) & (strongPawn + push)) + && distance(weakBishop, strongPawn) >= 2) return ScaleFactor(8); } @@ -551,22 +555,22 @@ ScaleFactor Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, RookValueMg, 2)); assert(verify_material(pos, weakSide, RookValueMg, 1)); - Square wpsq1 = pos.squares(strongSide)[0]; - Square wpsq2 = pos.squares(strongSide)[1]; - Square bksq = pos.square(weakSide); + Square strongPawn1 = pos.squares(strongSide)[0]; + Square strongPawn2 = pos.squares(strongSide)[1]; + Square weakKing = pos.square(weakSide); // Does the stronger side have a passed pawn? - if (pos.pawn_passed(strongSide, wpsq1) || pos.pawn_passed(strongSide, wpsq2)) + if (pos.pawn_passed(strongSide, strongPawn1) || pos.pawn_passed(strongSide, strongPawn2)) return SCALE_FACTOR_NONE; - Rank r = std::max(relative_rank(strongSide, wpsq1), relative_rank(strongSide, wpsq2)); + Rank pawnRank = std::max(relative_rank(strongSide, strongPawn1), relative_rank(strongSide, strongPawn2)); - if ( distance(bksq, wpsq1) <= 1 - && distance(bksq, wpsq2) <= 1 - && relative_rank(strongSide, bksq) > r) + if ( distance(weakKing, strongPawn1) <= 1 + && distance(weakKing, strongPawn2) <= 1 + && relative_rank(strongSide, weakKing) > pawnRank) { - assert(r > RANK_1 && r < RANK_7); - return ScaleFactor(7 * r); + assert(pawnRank > RANK_1 && pawnRank < RANK_7); + return ScaleFactor(7 * pawnRank); } return SCALE_FACTOR_NONE; } @@ -581,12 +585,12 @@ ScaleFactor Endgame::operator()(const Position& pos) const { assert(pos.count(strongSide) >= 2); assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); - Square ksq = pos.square(weakSide); - Bitboard pawns = pos.pieces(strongSide, PAWN); + Square weakKing = pos.square(weakSide); + Bitboard strongPawns = pos.pieces(strongSide, PAWN); // If all pawns are ahead of the king on a single rook file, it's a draw. - if (!((pawns & ~FileABB) || (pawns & ~FileHBB)) && - !(pawns & ~passed_pawn_span(weakSide, ksq))) + if (!((strongPawns & ~FileABB) || (strongPawns & ~FileHBB)) && + !(strongPawns & ~passed_pawn_span(weakSide, weakKing))) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; @@ -603,19 +607,19 @@ ScaleFactor Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, BishopValueMg, 1)); assert(verify_material(pos, weakSide, BishopValueMg, 0)); - Square pawnSq = pos.square(strongSide); - Square strongBishopSq = pos.square(strongSide); - Square weakBishopSq = pos.square(weakSide); - Square weakKingSq = pos.square(weakSide); + Square strongPawn = pos.square(strongSide); + Square strongBishop = pos.square(strongSide); + Square weakBishop = pos.square(weakSide); + Square weakKing = pos.square(weakSide); // Case 1: Defending king blocks the pawn, and cannot be driven away - if ( (forward_file_bb(strongSide, pawnSq) & weakKingSq) - && ( opposite_colors(weakKingSq, strongBishopSq) - || relative_rank(strongSide, weakKingSq) <= RANK_6)) + if ( (forward_file_bb(strongSide, strongPawn) & weakKing) + && ( opposite_colors(weakKing, strongBishop) + || relative_rank(strongSide, weakKing) <= RANK_6)) return SCALE_FACTOR_DRAW; // Case 2: Opposite colored bishops - if (opposite_colors(strongBishopSq, weakBishopSq)) + if (opposite_colors(strongBishop, weakBishop)) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; @@ -629,36 +633,36 @@ ScaleFactor Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, BishopValueMg, 2)); assert(verify_material(pos, weakSide, BishopValueMg, 0)); - Square wbsq = pos.square(strongSide); - Square bbsq = pos.square(weakSide); + Square strongBishop = pos.square(strongSide); + Square weakBishop = pos.square(weakSide); - if (!opposite_colors(wbsq, bbsq)) + if (!opposite_colors(strongBishop, weakBishop)) return SCALE_FACTOR_NONE; - Square ksq = pos.square(weakSide); - Square psq1 = pos.squares(strongSide)[0]; - Square psq2 = pos.squares(strongSide)[1]; + Square weakKing = pos.square(weakSide); + Square strongPawn1 = pos.squares(strongSide)[0]; + Square strongPawn2 = pos.squares(strongSide)[1]; Square blockSq1, blockSq2; - if (relative_rank(strongSide, psq1) > relative_rank(strongSide, psq2)) + if (relative_rank(strongSide, strongPawn1) > relative_rank(strongSide, strongPawn2)) { - blockSq1 = psq1 + pawn_push(strongSide); - blockSq2 = make_square(file_of(psq2), rank_of(psq1)); + blockSq1 = strongPawn1 + pawn_push(strongSide); + blockSq2 = make_square(file_of(strongPawn2), rank_of(strongPawn1)); } else { - blockSq1 = psq2 + pawn_push(strongSide); - blockSq2 = make_square(file_of(psq1), rank_of(psq2)); + blockSq1 = strongPawn2 + pawn_push(strongSide); + blockSq2 = make_square(file_of(strongPawn1), rank_of(strongPawn2)); } - switch (distance(psq1, psq2)) + switch (distance(strongPawn1, strongPawn2)) { case 0: // Both pawns are on the same file. It's an easy draw if the defender firmly // controls some square in the frontmost pawn's path. - if ( file_of(ksq) == file_of(blockSq1) - && relative_rank(strongSide, ksq) >= relative_rank(strongSide, blockSq1) - && opposite_colors(ksq, wbsq)) + if ( file_of(weakKing) == file_of(blockSq1) + && relative_rank(strongSide, weakKing) >= relative_rank(strongSide, blockSq1) + && opposite_colors(weakKing, strongBishop)) return SCALE_FACTOR_DRAW; else return SCALE_FACTOR_NONE; @@ -667,16 +671,16 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // Pawns on adjacent files. It's a draw if the defender firmly controls the // square in front of the frontmost pawn's path, and the square diagonally // behind this square on the file of the other pawn. - if ( ksq == blockSq1 - && opposite_colors(ksq, wbsq) - && ( bbsq == blockSq2 + if ( weakKing == blockSq1 + && opposite_colors(weakKing, strongBishop) + && ( weakBishop == blockSq2 || (attacks_bb(blockSq2, pos.pieces()) & pos.pieces(weakSide, BISHOP)) - || distance(psq1, psq2) >= 2)) + || distance(strongPawn1, strongPawn2) >= 2)) return SCALE_FACTOR_DRAW; - else if ( ksq == blockSq2 - && opposite_colors(ksq, wbsq) - && ( bbsq == blockSq1 + else if ( weakKing == blockSq2 + && opposite_colors(weakKing, strongBishop) + && ( weakBishop == blockSq1 || (attacks_bb(blockSq1, pos.pieces()) & pos.pieces(weakSide, BISHOP)))) return SCALE_FACTOR_DRAW; else @@ -698,14 +702,14 @@ ScaleFactor Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, BishopValueMg, 1)); assert(verify_material(pos, weakSide, KnightValueMg, 0)); - Square pawnSq = pos.square(strongSide); - Square strongBishopSq = pos.square(strongSide); - Square weakKingSq = pos.square(weakSide); + Square strongPawn = pos.square(strongSide); + Square strongBishop = pos.square(strongSide); + Square weakKing = pos.square(weakSide); - if ( file_of(weakKingSq) == file_of(pawnSq) - && relative_rank(strongSide, pawnSq) < relative_rank(strongSide, weakKingSq) - && ( opposite_colors(weakKingSq, strongBishopSq) - || relative_rank(strongSide, weakKingSq) <= RANK_6)) + if ( file_of(weakKing) == file_of(strongPawn) + && relative_rank(strongSide, strongPawn) < relative_rank(strongSide, weakKing) + && ( opposite_colors(weakKing, strongBishop) + || relative_rank(strongSide, weakKing) <= RANK_6)) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; @@ -724,18 +728,18 @@ ScaleFactor Endgame::operator()(const Position& pos) const { assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); // Assume strongSide is white and the pawn is on files A-D - Square wksq = normalize(pos, strongSide, pos.square(strongSide)); - Square bksq = normalize(pos, strongSide, pos.square(weakSide)); - Square psq = normalize(pos, strongSide, pos.square(strongSide)); + Square strongKing = normalize(pos, strongSide, pos.square(strongSide)); + Square weakKing = normalize(pos, strongSide, pos.square(weakSide)); + Square strongPawn = normalize(pos, strongSide, pos.square(strongSide)); Color us = strongSide == pos.side_to_move() ? WHITE : BLACK; // If the pawn has advanced to the fifth rank or further, and is not a // rook pawn, it's too dangerous to assume that it's at least a draw. - if (rank_of(psq) >= RANK_5 && file_of(psq) != FILE_A) + if (rank_of(strongPawn) >= RANK_5 && file_of(strongPawn) != FILE_A) return SCALE_FACTOR_NONE; // Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw, // it's probably at least a draw even with the pawn. - return Bitbases::probe(wksq, psq, bksq, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; + return Bitbases::probe(strongKing, strongPawn, weakKing, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; } From e9966d9a8ec371477f49bfa0eb69fa756e078fda Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Mon, 22 Jun 2020 12:52:31 +0300 Subject: [PATCH 0256/1766] Introduce bonus for queen infiltration Idea is that queen feels much better when it can't be kicked away now or later by pawn moves, especially in endgame. Special thanks to Linmiao Xu for the original idea of this patch. passed STC: LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 84008 W: 16271 L: 15958 D: 51779 Ptnml(0-2): 1476, 9688, 19420, 9887, 1533 https://tests.stockfishchess.org/tests/view/5eee7ca0447c5b640047a439 passed LTC: LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 11720 W: 1522 L: 1328 D: 8870 Ptnml(0-2): 52, 1021, 3574, 1107, 106 https://tests.stockfishchess.org/tests/view/5eefc588122d6514328d75f9 closes https://github.com/official-stockfish/Stockfish/pull/2759 Bench: 4471740 --- src/evaluate.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 3b0891a2d4f..8b4a27bc112 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -143,16 +143,17 @@ namespace { constexpr Score ReachableOutpost = S( 31, 22); constexpr Score PassedFile = S( 11, 8); constexpr Score PawnlessFlank = S( 17, 95); + constexpr Score QueenInfiltration = S( -2, 14); constexpr Score RestrictedPiece = S( 7, 7); constexpr Score RookOnKingRing = S( 16, 0); - constexpr Score RookOnQueenFile = S( 5, 9); - constexpr Score SliderOnQueen = S( 59, 18); + constexpr Score RookOnQueenFile = S( 6, 11); + constexpr Score SliderOnQueen = S( 60, 18); constexpr Score ThreatByKing = S( 24, 89); constexpr Score ThreatByPawnPush = S( 48, 39); constexpr Score ThreatBySafePawn = S(173, 94); constexpr Score TrappedRook = S( 55, 13); - constexpr Score WeakQueen = S( 51, 14); - constexpr Score WeakQueenProtection = S( 15, 0); + constexpr Score WeakQueen = S( 56, 15); + constexpr Score WeakQueenProtection = S( 14, 0); #undef S @@ -373,6 +374,10 @@ namespace { Bitboard queenPinners; if (pos.slider_blockers(pos.pieces(Them, ROOK, BISHOP), s, queenPinners)) score -= WeakQueen; + + // Bonus for queen on weak square in enemy camp + if (relative_rank(Us, s) > RANK_4 && (~pe->pawn_attacks_span(Them) & s)) + score += QueenInfiltration; } } if (T) From bbe98576846ad44ca531cdb6f42bf3eefa7d47c5 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Tue, 23 Jun 2020 14:55:52 +0300 Subject: [PATCH 0257/1766] Do less futility pruning for captures. The idea of this patch is that if capture can be described as "less valuable piece takes more valuable piece" it's not really correct to add only piece value of captured piece to static evaluation since there can be more threats in other places and opponent can't really do much but recapture our capturing piece which leaves us space for more captures thus winning more material and increasing static eval. passed STC https://tests.stockfishchess.org/tests/view/5ef0167b122d6514328d760f LLR: 2.96 (-2.94,2.94) {-0.50,1.50} Total: 24736 W: 4838 L: 4607 D: 15291 Ptnml(0-2): 438, 2812, 5648, 3021, 449 passed LTC https://tests.stockfishchess.org/tests/view/5ef073bc122d6514328d7693 LLR: 2.93 (-2.94,2.94) {0.25,1.75} Total: 46152 W: 5865 L: 5567 D: 34720 Ptnml(0-2): 312, 4160, 13886, 4354, 364 closes https://github.com/official-stockfish/Stockfish/pull/2761 bench 4789930 --- src/search.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/search.cpp b/src/search.cpp index 563d1aab410..671ac489e82 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1026,6 +1026,7 @@ namespace { if ( !givesCheck && lmrDepth < 6 && !(PvNode && abs(bestValue) < 2) + && PieceValue[MG][type_of(movedPiece)] >= PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] && !ss->inCheck && ss->staticEval + 267 + 391 * lmrDepth + PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha) continue; From 527d832a6de81c455cc8818e85c309fa1443f862 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Tue, 23 Jun 2020 10:41:53 +0200 Subject: [PATCH 0258/1766] Support ARCH=armv8 in Makefile (#2355) Tested with bench run after compiling with - g++ (Debian 6.3.0-18+deb9u1) 6.3.0 20170516 - clang version 3.8.1-24 on ThunderX CN8890. closes https://github.com/official-stockfish/Stockfish/pull/2760 fixes https://github.com/official-stockfish/Stockfish/issues/2355 No functional change. --- src/Makefile | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Makefile b/src/Makefile index 41c2aff6b64..83e0bb14651 100644 --- a/src/Makefile +++ b/src/Makefile @@ -133,6 +133,12 @@ ifeq ($(ARCH),armv7) prefetch = yes endif +ifeq ($(ARCH),armv8) + arch = armv8-a + bits = 64 + prefetch = yes +endif + ifeq ($(ARCH),ppc-32) arch = ppc endif @@ -164,7 +170,7 @@ ifeq ($(COMP),gcc) CXX=g++ CXXFLAGS += -pedantic -Wextra -Wshadow - ifeq ($(ARCH),armv7) + ifeq ($(ARCH),$(filter $(ARCH),armv7 armv8)) ifeq ($(OS),Android) CXXFLAGS += -m$(bits) LDFLAGS += -m$(bits) @@ -221,7 +227,7 @@ ifeq ($(COMP),clang) endif endif - ifeq ($(ARCH),armv7) + ifeq ($(ARCH),$(filter $(ARCH),armv7 armv8)) ifeq ($(OS),Android) CXXFLAGS += -m$(bits) LDFLAGS += -m$(bits) @@ -391,6 +397,7 @@ help: @echo "ppc-64 > PPC 64-bit" @echo "ppc-32 > PPC 32-bit" @echo "armv7 > ARMv7 32-bit" + @echo "armv8 > ARMv8 64-bit" @echo "general-64 > unspecified 64-bit" @echo "general-32 > unspecified 32-bit" @echo "" @@ -492,7 +499,8 @@ config-sanity: @test "$(sanitize)" = "undefined" || test "$(sanitize)" = "thread" || test "$(sanitize)" = "address" || test "$(sanitize)" = "no" @test "$(optimize)" = "yes" || test "$(optimize)" = "no" @test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \ - test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || test "$(arch)" = "armv7" + test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || \ + test "$(arch)" = "armv7" || test "$(arch)" = "armv8-a" @test "$(bits)" = "32" || test "$(bits)" = "64" @test "$(prefetch)" = "yes" || test "$(prefetch)" = "no" @test "$(popcnt)" = "yes" || test "$(popcnt)" = "no" From 208c53df0fc289466def7deae58f687957efb734 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 24 Jun 2020 16:23:31 +0200 Subject: [PATCH 0259/1766] Remove 'Minimum Thinking Time' UCI option. the option was, since at least 2014, not correctly implemented, ignoring all dynamic adjustments to optimum time in search. Instead of fixing it, remove it, no need to expose an option that will influence time management negatively. closes https://github.com/official-stockfish/Stockfish/pull/2765 No functional change. --- src/timeman.cpp | 3 +-- src/ucioption.cpp | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/timeman.cpp b/src/timeman.cpp index d27962b7fc0..a61c36d74d9 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -35,7 +35,6 @@ TimeManagement Time; // Our global time management object void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { - TimePoint minThinkingTime = TimePoint(Options["Minimum Thinking Time"]); TimePoint moveOverhead = TimePoint(Options["Move Overhead"]); TimePoint slowMover = TimePoint(Options["Slow Mover"]); TimePoint npmsec = TimePoint(Options["nodestime"]); @@ -91,7 +90,7 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { } // Never use more than 80% of the available time for this move - optimumTime = std::max(minThinkingTime, TimePoint(opt_scale * timeLeft)); + optimumTime = TimePoint(opt_scale * timeLeft); maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, max_scale * optimumTime)); if (Options["Ponder"]) diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 7037ea57317..871edb29294 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -68,7 +68,6 @@ void init(OptionsMap& o) { o["MultiPV"] << Option(1, 1, 500); o["Skill Level"] << Option(20, 0, 20); o["Move Overhead"] << Option(10, 0, 5000); - o["Minimum Thinking Time"] << Option( 0, 0, 5000); o["Slow Mover"] << Option(100, 10, 1000); o["nodestime"] << Option(0, 0, 10000); o["UCI_Chess960"] << Option(false); From 11483fe6d942a4fee6fa272f72251d6b6d6d7454 Mon Sep 17 00:00:00 2001 From: UnaiCorzo Date: Tue, 23 Jun 2020 17:56:38 +0200 Subject: [PATCH 0260/1766] Makefile: support lto on mingw, default to 64bits Clean and organize uppercase and spaces fixes https://github.com/official-stockfish/Stockfish/issues/2731 closes https://github.com/official-stockfish/Stockfish/pull/2763 No functional change --- AUTHORS | 1 + src/Makefile | 34 +++++++++++----------------------- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/AUTHORS b/AUTHORS index 1cd7ff54b0f..f08d71d301e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -155,6 +155,7 @@ Tom Vijlbrief (tomtor) Tomasz Sobczyk (Sopel97) Torsten Franz (torfranz, tfranzer) Tracey Emery (basepr1me) +Unai Corzo (unaiic) Uri Blass (uriblass) Vince Negri (cuddlestmonkey) diff --git a/src/Makefile b/src/Makefile index 83e0bb14651..81731e6675c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -54,7 +54,7 @@ endif ### Section 2. High-level Configuration ### ========================================================================== # -# flag --- Comp switch --- Description +# flag --- Comp switch --- Description # ---------------------------------------------------------------------------- # # debug = yes/no --- -DNDEBUG --- Enable/Disable debug mode @@ -77,43 +77,42 @@ endif optimize = yes debug = no sanitize = no -bits = 32 +bits = 64 prefetch = no popcnt = no sse = no pext = no ### 2.2 Architecture specific - ifeq ($(ARCH),general-32) arch = any + bits = 32 endif ifeq ($(ARCH),x86-32-old) arch = i386 + bits = 32 endif ifeq ($(ARCH),x86-32) arch = i386 + bits = 32 prefetch = yes sse = yes endif ifeq ($(ARCH),general-64) arch = any - bits = 64 endif ifeq ($(ARCH),x86-64) arch = x86_64 - bits = 64 prefetch = yes sse = yes endif ifeq ($(ARCH),x86-64-modern) arch = x86_64 - bits = 64 prefetch = yes popcnt = yes sse = yes @@ -121,7 +120,6 @@ endif ifeq ($(ARCH),x86-64-bmi2) arch = x86_64 - bits = 64 prefetch = yes popcnt = yes sse = yes @@ -131,6 +129,7 @@ endif ifeq ($(ARCH),armv7) arch = armv7 prefetch = yes + bits = 32 endif ifeq ($(ARCH),armv8) @@ -141,22 +140,20 @@ endif ifeq ($(ARCH),ppc-32) arch = ppc + bits = 32 endif ifeq ($(ARCH),ppc-64) arch = ppc64 - bits = 64 popcnt = yes prefetch = yes endif - ### ========================================================================== -### Section 3. Low-level configuration +### Section 3. Low-level Configuration ### ========================================================================== ### 3.1 Selecting compiler (default = gcc) - CXXFLAGS += -Wall -Wcast-qual -fno-exceptions -std=c++11 $(EXTRACXXFLAGS) DEPENDFLAGS += -std=c++11 LDFLAGS += $(EXTRALDFLAGS) @@ -347,17 +344,10 @@ endif ### needs access to the optimization flags. ifeq ($(optimize),yes) ifeq ($(debug), no) - ifeq ($(comp),$(filter $(comp),gcc clang)) - CXXFLAGS += -flto - LDFLAGS += $(CXXFLAGS) - endif - - ifeq ($(comp),mingw) - ifeq ($(KERNEL),Linux) + ifeq ($(comp),$(filter $(comp),gcc clang mingw)) CXXFLAGS += -flto LDFLAGS += $(CXXFLAGS) endif - endif endif endif @@ -368,9 +358,8 @@ ifeq ($(OS), Android) LDFLAGS += -fPIE -pie endif - ### ========================================================================== -### Section 4. Public targets +### Section 4. Public Targets ### ========================================================================== help: @@ -468,7 +457,7 @@ default: help ### ========================================================================== -### Section 5. Private targets +### Section 5. Private Targets ### ========================================================================== all: $(EXE) .depend @@ -551,4 +540,3 @@ icc-profile-use: -@$(CXX) $(DEPENDFLAGS) -MM $(SRCS) > $@ 2> /dev/null -include .depend - From ab5cd8340f2f7f8730aa7c77476edf4a98a166e4 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 24 Jun 2020 22:19:58 +0200 Subject: [PATCH 0261/1766] Small cleanups closes https://github.com/official-stockfish/Stockfish/pull/2756 No functional change --- src/bitboard.h | 29 ++++++++++++++++++----------- src/endgame.cpp | 14 +++++++------- src/evaluate.cpp | 26 +++++++++++++++----------- src/material.cpp | 19 +++++++++++-------- src/material.h | 2 +- src/misc.cpp | 21 ++++++++++++--------- src/pawns.cpp | 7 +++++++ src/position.cpp | 4 ++-- src/position.h | 1 + src/psqt.cpp | 11 ++++++----- src/search.cpp | 35 +++++++++++++++++++---------------- src/thread.cpp | 13 ++++++++++--- src/timeman.cpp | 11 ++++++----- src/tt.cpp | 3 ++- src/tt.h | 4 ++-- src/tune.cpp | 2 +- src/types.h | 6 +++--- src/ucioption.cpp | 2 +- 18 files changed, 124 insertions(+), 86 deletions(-) diff --git a/src/bitboard.h b/src/bitboard.h index 0f55810cece..1c598108057 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -110,6 +110,7 @@ inline Bitboard square_bb(Square s) { return SquareBB[s]; } + /// Overloads of bitwise operators between a Bitboard and a Square for testing /// whether a given bit is set in a bitboard, and for setting and clearing bits. @@ -200,10 +201,11 @@ inline Bitboard adjacent_files_bb(Square s) { return shift(file_bb(s)) | shift(file_bb(s)); } -/// line_bb(Square, Square) returns a Bitboard representing an entire line -/// (from board edge to board edge) that intersects the given squares. -/// If the given squares are not on a same file/rank/diagonal, return 0. -/// Ex. line_bb(SQ_C4, SQ_F7) returns a bitboard with the A2-G8 diagonal. + +/// line_bb(Square, Square) returns a bitboard representing an entire line, +/// from board edge to board edge, that intersects the given squares. If the +/// given squares are not on a same file/rank/diagonal, returns 0. For instance, +/// line_bb(SQ_C4, SQ_F7) will return a bitboard with the A2-G8 diagonal. inline Bitboard line_bb(Square s1, Square s2) { @@ -211,10 +213,11 @@ inline Bitboard line_bb(Square s1, Square s2) { return LineBB[s1][s2]; } -/// between_bb() returns a Bitboard representing squares that are linearly -/// between the given squares (excluding the given squares). -/// If the given squares are not on a same file/rank/diagonal, return 0. -/// Ex. between_bb(SQ_C4, SQ_F7) returns a bitboard with squares D5 and E6. + +/// between_bb() returns a bitboard representing squares that are linearly +/// between the given squares (excluding the given squares). If the given +/// squares are not on a same file/rank/diagonal, return 0. For instance, +/// between_bb(SQ_C4, SQ_F7) will return a bitboard with squares D5 and E6. inline Bitboard between_bb(Square s1, Square s2) { Bitboard b = line_bb(s1, s2) & ((AllSquares << s1) ^ (AllSquares << s2)); @@ -241,8 +244,8 @@ inline Bitboard forward_file_bb(Color c, Square s) { /// pawn_attack_span() returns a bitboard representing all the squares that can -/// be attacked by a pawn of the given color when it moves along its file, -/// starting from the given square. +/// be attacked by a pawn of the given color when it moves along its file, starting +/// from the given square. inline Bitboard pawn_attack_span(Color c, Square s) { return forward_ranks_bb(c, s) & adjacent_files_bb(s); @@ -276,7 +279,9 @@ template<> inline int distance(Square x, Square y) { return SquareDistan inline int edge_distance(File f) { return std::min(f, File(FILE_H - f)); } inline int edge_distance(Rank r) { return std::min(r, Rank(RANK_8 - r)); } -/// Return the target square bitboard if we do not step off the board, empty otherwise + +/// safe_destination() returns the bitboard of target square for the given step +/// from the given square. If the step is off the board, returns empty bitboard. inline Bitboard safe_destination(Square s, int step) { @@ -284,6 +289,7 @@ inline Bitboard safe_destination(Square s, int step) return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0); } + /// attacks_bb(Square) returns the pseudo attacks of the give piece type /// assuming an empty board. @@ -295,6 +301,7 @@ inline Bitboard attacks_bb(Square s) { return PseudoAttacks[Pt][s]; } + /// attacks_bb(Square, Bitboard) returns the attacks by the given piece /// assuming the board is occupied according to the passed Bitboard. /// Sliding piece attacks do not continue passed an occupied square. diff --git a/src/endgame.cpp b/src/endgame.cpp index d9e76348ecd..be0755a866f 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -268,7 +268,7 @@ Value Endgame::operator()(const Position& pos) const { } -/// KQ vs KR. This is almost identical to KX vs K: We give the attacking +/// KQ vs KR. This is almost identical to KX vs K: we give the attacking /// king a bonus for having the kings close together, and for forcing the /// defending king towards the edge. If we also take care to avoid null move for /// the defending side in the search, this is usually sufficient to win KQ vs KR. @@ -291,7 +291,7 @@ Value Endgame::operator()(const Position& pos) const { /// KNN vs KP. Very drawish, but there are some mate opportunities if we can -// press the weakSide King to a corner before the pawn advances too much. +/// press the weakSide King to a corner before the pawn advances too much. template<> Value Endgame::operator()(const Position& pos) const { @@ -352,7 +352,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { Square weakPawn = frontmost_sq(strongSide, pos.pieces(weakSide, PAWN)); // There's potential for a draw if our pawn is blocked on the 7th rank, - // the bishop cannot attack it or they only have one pawn left + // the bishop cannot attack it or they only have one pawn left. if ( relative_rank(strongSide, weakPawn) == RANK_7 && (strongPawns & (weakPawn + pawn_push(weakSide))) && (opposite_colors(strongBishop, weakPawn) || !more_than_one(strongPawns))) @@ -365,7 +365,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // closer. (I think this rule only fails in practically // unreachable positions such as 5k1K/6p1/6P1/8/8/3B4/8/8 w // and positions where qsearch will immediately correct the - // problem such as 8/4k1p1/6P1/1K6/3B4/8/8/8 w) + // problem such as 8/4k1p1/6P1/1K6/3B4/8/8/8 w). if ( relative_rank(strongSide, weakKing) >= RANK_7 && weakKingDist <= 2 && weakKingDist <= strongKingDist) @@ -576,7 +576,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { } -/// K and two or more pawns vs K. There is just a single rule here: If all pawns +/// K and two or more pawns vs K. There is just a single rule here: if all pawns /// are on the same rook file and are blocked by the defending king, it's a draw. template<> ScaleFactor Endgame::operator()(const Position& pos) const { @@ -693,7 +693,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { } -/// KBP vs KN. There is a single rule: If the defending king is somewhere along +/// KBP vs KN. There is a single rule: if the defending king is somewhere along /// the path of the pawn, and the square of the king is not of the same color as /// the stronger side's bishop, it's a draw. template<> @@ -717,7 +717,7 @@ ScaleFactor Endgame::operator()(const Position& pos) const { /// KP vs KP. This is done by removing the weakest side's pawn and probing the -/// KP vs K bitbase: If the weakest side has a draw without the pawn, it probably +/// KP vs K bitbase: if the weakest side has a draw without the pawn, it probably /// has at least a draw with the pawn as well. The exception is when the stronger /// side's pawn is far advanced and not on a rook file; in this case it is often /// possible to win (e.g. 8/4k3/3p4/3P4/6K1/8/8/8 w - - 0 1). diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 8b4a27bc112..60ec9c72a5b 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -127,23 +127,23 @@ namespace { }; // Assorted bonuses and penalties - constexpr Score BishopPawns = S( 3, 7); + constexpr Score BishopKingProtector = S( 6, 9); constexpr Score BishopOnKingRing = S( 24, 0); + constexpr Score BishopOutpost = S( 30, 23); + constexpr Score BishopPawns = S( 3, 7); constexpr Score BishopXRayPawns = S( 4, 5); constexpr Score CorneredBishop = S( 50, 50); constexpr Score FlankAttacks = S( 8, 0); constexpr Score Hanging = S( 69, 36); - constexpr Score BishopKingProtector = S( 6, 9); constexpr Score KnightKingProtector = S( 8, 9); constexpr Score KnightOnQueen = S( 16, 11); + constexpr Score KnightOutpost = S( 56, 36); constexpr Score LongDiagonalBishop = S( 45, 0); constexpr Score MinorBehindPawn = S( 18, 3); - constexpr Score KnightOutpost = S( 56, 36); - constexpr Score BishopOutpost = S( 30, 23); - constexpr Score ReachableOutpost = S( 31, 22); constexpr Score PassedFile = S( 11, 8); constexpr Score PawnlessFlank = S( 17, 95); constexpr Score QueenInfiltration = S( -2, 14); + constexpr Score ReachableOutpost = S( 31, 22); constexpr Score RestrictedPiece = S( 7, 7); constexpr Score RookOnKingRing = S( 16, 0); constexpr Score RookOnQueenFile = S( 6, 11); @@ -152,8 +152,9 @@ namespace { constexpr Score ThreatByPawnPush = S( 48, 39); constexpr Score ThreatBySafePawn = S(173, 94); constexpr Score TrappedRook = S( 55, 13); - constexpr Score WeakQueen = S( 56, 15); constexpr Score WeakQueenProtection = S( 14, 0); + constexpr Score WeakQueen = S( 56, 15); + #undef S @@ -216,6 +217,7 @@ namespace { // Evaluation::initialize() computes king and pawn attacks, and the king ring // bitboard for a given color. This is done at the beginning of the evaluation. + template template void Evaluation::initialize() { @@ -255,6 +257,7 @@ namespace { // Evaluation::pieces() scores pieces of a given color and type + template template Score Evaluation::pieces() { @@ -377,7 +380,7 @@ namespace { // Bonus for queen on weak square in enemy camp if (relative_rank(Us, s) > RANK_4 && (~pe->pawn_attacks_span(Them) & s)) - score += QueenInfiltration; + score += QueenInfiltration; } } if (T) @@ -388,6 +391,7 @@ namespace { // Evaluation::king() assigns bonuses and penalties to a king of a given color + template template Score Evaluation::king() const { @@ -496,6 +500,7 @@ namespace { // Evaluation::threats() assigns bonuses according to the types of the // attacking and the attacked pieces. + template template Score Evaluation::threats() const { @@ -721,8 +726,8 @@ namespace { // Evaluation::winnable() adjusts the mg and eg score components based on the - // known attacking/defending status of the players. - // A single value is derived from the mg and eg values and returned. + // known attacking/defending status of the players. A single value is derived + // by interpolation from the mg and eg values and returned. template Value Evaluation::winnable(Score score) const { @@ -828,12 +833,11 @@ namespace { return pos.side_to_move() == WHITE ? v : -v; // Main evaluation begins here - initialize(); initialize(); // Pieces evaluated first (also populates attackedBy, attackedBy2). - // Note that the order of evaluation of the terms is left unspecified + // Note that the order of evaluation of the terms is left unspecified. score += pieces() - pieces() + pieces() - pieces() + pieces() - pieces() diff --git a/src/material.cpp b/src/material.cpp index 93699f5f60b..bb25d3caa7e 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -44,12 +44,12 @@ namespace { constexpr int QuadraticTheirs[][PIECE_TYPE_NB] = { // THEIR PIECES // pair pawn knight bishop rook queen - { 0 }, // Bishop pair - { 36, 0 }, // Pawn - { 9, 63, 0 }, // Knight OUR PIECES - { 59, 65, 42, 0 }, // Bishop - { 46, 39, 24, -24, 0 }, // Rook - { 97, 100, -42, 137, 268, 0 } // Queen + { }, // Bishop pair + { 36, }, // Pawn + { 9, 63, }, // Knight OUR PIECES + { 59, 65, 42, }, // Bishop + { 46, 39, 24, -24, }, // Rook + { 97, 100, -42, 137, 268, } // Queen }; // Endgame evaluation and scaling functions are accessed directly and not through @@ -79,8 +79,10 @@ namespace { && pos.count(~us) >= 1; } + /// imbalance() calculates the imbalance by comparing the piece count of each /// piece type for both colors. + template int imbalance(const int pieceCount[][PIECE_TYPE_NB]) { @@ -94,9 +96,9 @@ namespace { if (!pieceCount[Us][pt1]) continue; - int v = 0; + int v = QuadraticOurs[pt1][pt1] * pieceCount[Us][pt1]; - for (int pt2 = NO_PIECE_TYPE; pt2 <= pt1; ++pt2) + for (int pt2 = NO_PIECE_TYPE; pt2 < pt1; ++pt2) v += QuadraticOurs[pt1][pt2] * pieceCount[Us][pt2] + QuadraticTheirs[pt1][pt2] * pieceCount[Them][pt2]; @@ -110,6 +112,7 @@ namespace { namespace Material { + /// Material::probe() looks up the current position's material configuration in /// the material hash table. It returns a pointer to the Entry if the position /// is found. Otherwise a new Entry is computed and stored there, so we don't diff --git a/src/material.h b/src/material.h index 9ab1d81c5e7..21647f23b21 100644 --- a/src/material.h +++ b/src/material.h @@ -44,7 +44,7 @@ struct Entry { bool specialized_eval_exists() const { return evaluationFunction != nullptr; } Value evaluate(const Position& pos) const { return (*evaluationFunction)(pos); } - // scale_factor takes a position and a color as input and returns a scale factor + // scale_factor() takes a position and a color as input and returns a scale factor // for the given color. We have to provide the position in addition to the color // because the scale factor may also be a function which should be applied to // the position. For instance, in KBP vs K endgames, the scaling function looks diff --git a/src/misc.cpp b/src/misc.cpp index c625478462e..2bc05c5b736 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -294,9 +294,10 @@ void prefetch(void* addr) { #endif -/// aligned_ttmem_alloc will return suitably aligned memory, and if possible use large pages. -/// The returned pointer is the aligned one, while the mem argument is the one that needs to be passed to free. -/// With c++17 some of this functionality can be simplified. +/// aligned_ttmem_alloc() will return suitably aligned memory, and if possible use large pages. +/// The returned pointer is the aligned one, while the mem argument is the one that needs +/// to be passed to free. With c++17 some of this functionality could be simplified. + #if defined(__linux__) && !defined(__ANDROID__) void* aligned_ttmem_alloc(size_t allocSize, void*& mem) { @@ -336,17 +337,17 @@ static void* aligned_ttmem_alloc_large_pages(size_t allocSize) { tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // Try to enable SeLockMemoryPrivilege. Note that even if AdjustTokenPrivileges() succeeds, - // we still need to query GetLastError() to ensure that the privileges were actually obtained... + // we still need to query GetLastError() to ensure that the privileges were actually obtained. if (AdjustTokenPrivileges( hProcessToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &prevTp, &prevTpLen) && GetLastError() == ERROR_SUCCESS) { - // round up size to full pages and allocate + // Round up size to full pages and allocate allocSize = (allocSize + largePageSize - 1) & ~size_t(largePageSize - 1); mem = VirtualAlloc( NULL, allocSize, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); - // privilege no longer needed, restore previous state + // Privilege no longer needed, restore previous state AdjustTokenPrivileges(hProcessToken, FALSE, &prevTp, 0, NULL, NULL); } } @@ -360,7 +361,7 @@ void* aligned_ttmem_alloc(size_t allocSize, void*& mem) { static bool firstCall = true; - // try to allocate large pages + // Try to allocate large pages mem = aligned_ttmem_alloc_large_pages(allocSize); // Suppress info strings on the first call. The first call occurs before 'uci' @@ -374,7 +375,7 @@ void* aligned_ttmem_alloc(size_t allocSize, void*& mem) { } firstCall = false; - // fall back to regular, page aligned, allocation if necessary + // Fall back to regular, page aligned, allocation if necessary if (!mem) mem = VirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); @@ -394,7 +395,9 @@ void* aligned_ttmem_alloc(size_t allocSize, void*& mem) { #endif -/// aligned_ttmem_free will free the previously allocated ttmem + +/// aligned_ttmem_free() will free the previously allocated ttmem + #if defined(_WIN64) void aligned_ttmem_free(void* mem) { diff --git a/src/pawns.cpp b/src/pawns.cpp index 597dff2b0fb..d741b2ef1b3 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -66,6 +66,12 @@ namespace { #undef S #undef V + + /// evaluate() calculates a score for the static pawn structure of the given position. + /// We cannot use the location of pieces or king in this function, as the evaluation + /// of the pawn structure will be stored in a small cache for speed reasons, and will + /// be re-used even when the pieces have moved. + template Score evaluate(const Position& pos, Pawns::Entry* e) { @@ -170,6 +176,7 @@ namespace { namespace Pawns { + /// Pawns::probe() looks up the current position's pawns configuration in /// the pawns hash table. It returns a pointer to the Entry if the position /// is found. Otherwise a new Entry is computed and stored there, so we don't diff --git a/src/position.cpp b/src/position.cpp index c9db6224abf..471ef01f24f 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -105,8 +105,7 @@ Key cuckoo[8192]; Move cuckooMove[8192]; -/// Position::init() initializes at startup the various arrays used to compute -/// hash keys. +/// Position::init() initializes at startup the various arrays used to compute hash keys void Position::init() { @@ -1112,6 +1111,7 @@ bool Position::see_ge(Move m, Value threshold) const { return bool(res); } + /// Position::is_draw() tests whether the position is drawn by 50-move rule /// or by repetition. It does not detect stalemates. diff --git a/src/position.h b/src/position.h index 8f8c8f7ab9c..8cfa39201e4 100644 --- a/src/position.h +++ b/src/position.h @@ -56,6 +56,7 @@ struct StateInfo { int repetition; }; + /// A list to keep track of the position states along the setup moves (from the /// start position to the position just before the search starts). Needed by /// 'draw by repetition' detection. Use a std::deque because pointers to diff --git a/src/psqt.cpp b/src/psqt.cpp index 27c7a36f9f3..c5da9785bc9 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -101,9 +101,10 @@ constexpr Score PBonus[RANK_NB][FILE_NB] = Score psq[PIECE_NB][SQUARE_NB]; -// init() initializes piece-square tables: the white halves of the tables are -// copied from Bonus[] adding the piece value, then the black halves of the -// tables are initialized by flipping and changing the sign of the white scores. + +// PSQT::init() initializes piece-square tables: the white halves of the tables are +// copied from Bonus[] and PBonus[], adding the piece value, then the black halves of +// the tables are initialized by flipping and changing the sign of the white scores. void init() { for (Piece pc : {W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING}) @@ -113,8 +114,8 @@ void init() { for (Square s = SQ_A1; s <= SQ_H8; ++s) { File f = File(edge_distance(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] = score + (type_of(pc) == PAWN ? PBonus[rank_of(s)][file_of(s)] + : Bonus[pc][rank_of(s)][f]); psq[~pc][flip_rank(s)] = -psq[pc][s]; } } diff --git a/src/search.cpp b/src/search.cpp index 671ac489e82..1e2980cb0cd 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -525,7 +525,7 @@ void Thread::search() { double totalTime = rootMoves.size() == 1 ? 0 : Time.optimum() * fallingEval * reduction * bestMoveInstability; - // Stop the search if we have exceeded the totalTime, at least 1ms search. + // Stop the search if we have exceeded the totalTime, at least 1ms search if (Time.elapsed() > totalTime) { // If we are allowed to ponder do not stop the search now but @@ -627,7 +627,7 @@ namespace { || pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos) - : value_draw(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 @@ -767,9 +767,10 @@ namespace { // Step 6. Static evaluation of the position if (ss->inCheck) { + // Skip early pruning when in check ss->staticEval = eval = VALUE_NONE; improving = false; - goto moves_loop; // Skip early pruning when in check + goto moves_loop; } else if (ttHit) { @@ -1028,7 +1029,8 @@ namespace { && !(PvNode && abs(bestValue) < 2) && PieceValue[MG][type_of(movedPiece)] >= PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] && !ss->inCheck - && ss->staticEval + 267 + 391 * lmrDepth + PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha) + && ss->staticEval + 267 + 391 * lmrDepth + + PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha) continue; // See based pruning @@ -1074,8 +1076,8 @@ namespace { else if (singularBeta >= beta) return singularBeta; - // If the eval of ttMove is greater than beta we try also if there is an other move that - // pushes it over beta, if so also produce a cutoff + // If the eval of ttMove is greater than beta we try also if there is another + // move that pushes it over beta, if so also produce a cutoff. else if (ttValue >= beta) { ss->excludedMove = move; @@ -1153,7 +1155,7 @@ namespace { if (thisThread->ttHitAverage > 473 * TtHitAverageResolution * TtHitAverageWindow / 1024) r--; - // Reduction if other threads are searching this position. + // Reduction if other threads are searching this position if (th.marked()) r++; @@ -1290,7 +1292,7 @@ namespace { rm.pv.push_back(*m); // We record how often the best move has been changed in each - // iteration. This information is used for time management: When + // iteration. This information is used for time management: when // the best move changes frequently, we allocate some more time. if (moveCount > 1) ++thisThread->bestMoveChanges; @@ -1524,7 +1526,7 @@ namespace { } } - // Don't search moves with negative SEE values + // Do not search moves with negative SEE values if ( !ss->inCheck && !pos.see_ge(move)) continue; @@ -1571,7 +1573,7 @@ namespace { } } - // All legal moves have been searched. A special case: If we're in check + // All legal moves have been searched. A special case: if we're in check // and no legal moves were found, it is checkmate. if (ss->inCheck && bestValue == -VALUE_INFINITE) return mated_in(ss->ply); // Plies to mate from the root @@ -1588,7 +1590,7 @@ namespace { // value_to_tt() adjusts a mate or TB score from "plies to mate from the root" to - // "plies to mate from the current position". standard scores are unchanged. + // "plies to mate from the current position". Standard scores are unchanged. // The function is called before storing a value in the transposition table. Value value_to_tt(Value v, int ply) { @@ -1600,11 +1602,11 @@ namespace { } - // value_from_tt() is the inverse of value_to_tt(): It adjusts a mate or TB score - // from the transposition table (which refers to the plies to mate/be mated - // from current position) to "plies to mate/be mated (TB win/loss) from the root". - // However, for mate scores, to avoid potentially false mate scores related to the 50 moves rule, - // and the graph history interaction, return an optimal TB score instead. + // value_from_tt() is the inverse of value_to_tt(): it adjusts a mate or TB score + // from the transposition table (which refers to the plies to mate/be mated from + // current position) to "plies to mate/be mated (TB win/loss) from the root". However, + // for mate scores, to avoid potentially false mate scores related to the 50 moves rule + // and the graph history interaction, we return an optimal TB score instead. Value value_from_tt(Value v, int ply, int r50c) { @@ -1764,6 +1766,7 @@ namespace { } // namespace + /// MainThread::check_time() is used to print debug info and, more importantly, /// to detect when we are out of available time and thus stop the search. diff --git a/src/thread.cpp b/src/thread.cpp index a27a60c6e24..a0ee2b252fe 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -52,6 +52,7 @@ Thread::~Thread() { stdThread.join(); } + /// Thread::bestMoveCount(Move move) return best move counter for the given root move int Thread::best_move_count(Move move) const { @@ -62,6 +63,7 @@ int Thread::best_move_count(Move move) const { return rm != rootMoves.begin() + pvLast ? rm->bestMoveCount : 0; } + /// Thread::clear() reset histories, usually before a new game void Thread::clear() { @@ -81,6 +83,7 @@ void Thread::clear() { } } + /// Thread::start_searching() wakes up the thread that will start the search void Thread::start_searching() { @@ -158,7 +161,8 @@ void ThreadPool::set(size_t requested) { } } -/// ThreadPool::clear() sets threadPool data to initial values. + +/// ThreadPool::clear() sets threadPool data to initial values void ThreadPool::clear() { @@ -170,6 +174,7 @@ void ThreadPool::clear() { main()->previousTimeReduction = 1.0; } + /// ThreadPool::start_thinking() wakes up main thread waiting in idle_loop() and /// returns immediately. Main thread will wake up other threads and start the search. @@ -250,7 +255,8 @@ Thread* ThreadPool::get_best_thread() const { return bestThread; } -/// Start non-main threads. + +/// Start non-main threads void ThreadPool::start_searching() { @@ -259,7 +265,8 @@ void ThreadPool::start_searching() { th->start_searching(); } -/// Wait for non-main threads. + +/// Wait for non-main threads void ThreadPool::wait_for_search_finished() const { diff --git a/src/timeman.cpp b/src/timeman.cpp index a61c36d74d9..546eadd29e7 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -28,10 +28,11 @@ TimeManagement Time; // Our global time management object -/// init() is called at the beginning of the search and calculates the bounds -/// of time allowed for the current game ply. We currently support: -// 1) x basetime (+z increment) -// 2) x moves in y seconds (+z increment) + +/// TimeManagement::init() is called at the beginning of the search and calculates +/// the bounds of time allowed for the current game ply. We currently support: +// 1) x basetime (+ z increment) +// 2) x moves in y seconds (+ z increment) void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { @@ -60,7 +61,7 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { startTime = limits.startTime; - //Maximum move horizon of 50 moves + // Maximum move horizon of 50 moves int mtg = limits.movestogo ? std::min(limits.movestogo, 50) : 50; // Make sure timeLeft is > 0 since we may use it as a divisor diff --git a/src/tt.cpp b/src/tt.cpp index d0a5d4e0aa9..34590903676 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -30,7 +30,7 @@ TranspositionTable TT; // Our global transposition table -/// TTEntry::save populates the TTEntry with a new node's data, possibly +/// TTEntry::save() populates the TTEntry with a new node's data, possibly /// overwriting an old position. Update is not atomic and can be racy. void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) { @@ -107,6 +107,7 @@ void TranspositionTable::clear() { th.join(); } + /// TranspositionTable::probe() looks up the current position in the transposition /// table. It returns true and a pointer to the TTEntry if the position is found. /// Otherwise, it returns false and a pointer to an empty or least valuable TTEntry diff --git a/src/tt.h b/src/tt.h index 3e1d0e99827..e18db8cef7b 100644 --- a/src/tt.h +++ b/src/tt.h @@ -60,8 +60,8 @@ struct TTEntry { /// A TranspositionTable is an array of Cluster, of size clusterCount. Each /// cluster consists of ClusterSize number of TTEntry. Each non-empty TTEntry /// contains information on exactly one position. The size of a Cluster should -/// divide the size of a cache line for best performance, -/// as the cacheline is prefetched when possible. +/// divide the size of a cache line for best performance, as the cacheline is +/// prefetched when possible. class TranspositionTable { diff --git a/src/tune.cpp b/src/tune.cpp index 696b4cb8f33..c1b1c76bcc1 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -70,7 +70,7 @@ static void make_option(const string& n, int v, const SetRange& r) { Options[n] << UCI::Option(v, r(v).first, r(v).second, on_tune); LastOption = &Options[n]; - // Print formatted parameters, ready to be copy-pasted in fishtest + // Print formatted parameters, ready to be copy-pasted in Fishtest std::cout << n << "," << v << "," << r(v).first << "," << r(v).second << "," diff --git a/src/types.h b/src/types.h index 969d4e65e08..0c512f5bf3f 100644 --- a/src/types.h +++ b/src/types.h @@ -349,16 +349,16 @@ constexpr Color operator~(Color c) { return Color(c ^ BLACK); // Toggle color } -constexpr Square flip_rank(Square s) { +constexpr Square flip_rank(Square s) { // Swap A1 <-> A8 return Square(s ^ SQ_A8); } -constexpr Square flip_file(Square s) { +constexpr Square flip_file(Square s) { // Swap A1 <-> H1 return Square(s ^ SQ_H1); } constexpr Piece operator~(Piece pc) { - return Piece(pc ^ 8); // Swap color of piece B_KNIGHT -> W_KNIGHT + return Piece(pc ^ 8); // Swap color of piece B_KNIGHT <-> W_KNIGHT } constexpr CastlingRights operator&(Color c, CastlingRights cr) { diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 871edb29294..c268c9755f9 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -52,7 +52,7 @@ bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const } -/// init() initializes the UCI options to their hard-coded default values +/// UCI::init() initializes the UCI options to their hard-coded default values void init(OptionsMap& o) { From a84e3ac287fa2c2db5ee58faabdb31943acc78d0 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Wed, 24 Jun 2020 23:03:00 +0200 Subject: [PATCH 0262/1766] Simplify use_time_management() and allow composing like other `go` commands wtime/btime can now be composed. `go depth 10 wtime 100` will let the engine use standard time management but stop if depth 10 is reached. fixes https://github.com/official-stockfish/Stockfish/issues/2767 closes https://github.com/official-stockfish/Stockfish/pull/2768 No functional change --- src/search.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.h b/src/search.h index 1653ce9237c..3e855c8bf34 100644 --- a/src/search.h +++ b/src/search.h @@ -91,7 +91,7 @@ struct LimitsType { } bool use_time_management() const { - return !(mate | movetime | depth | nodes | perft | infinite); + return time[WHITE] || time[BLACK]; } std::vector searchmoves; From aecfca2dc2d760df257e18cd6b29d266a3c3e68a Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Thu, 25 Jun 2020 12:42:25 +0200 Subject: [PATCH 0263/1766] support popcnt on armv8 * Supports popcnt (thanks @daylen) * bits = 64 is now the default Tested with g++ (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04) 7.5.0 on ThunderX CN8890, yields about 9% speedup. Also tested with clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final). closes https://github.com/official-stockfish/Stockfish/pull/2770 No functional change. --- src/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index 81731e6675c..492403d3ce8 100644 --- a/src/Makefile +++ b/src/Makefile @@ -134,8 +134,8 @@ endif ifeq ($(ARCH),armv8) arch = armv8-a - bits = 64 prefetch = yes + popcnt = yes endif ifeq ($(ARCH),ppc-32) @@ -322,7 +322,7 @@ endif ### 3.6 popcnt ifeq ($(popcnt),yes) - ifeq ($(arch),ppc64) + ifeq ($(arch),$(filter $(arch),ppc64 armv8-a)) CXXFLAGS += -DUSE_POPCNT else ifeq ($(comp),icc) CXXFLAGS += -msse3 -DUSE_POPCNT From ca41ee6632368676a2fb98fd2970ac9b183f0aa9 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 27 Jun 2020 08:23:46 +0200 Subject: [PATCH 0264/1766] Revert LTO for mingw on windows. LTO with static linking is still only working with the latest versions of gcc, causing problems for some devs. on a modern mingw toolchain LTO optimizations can still be enabled as: ``` CXXFLAGS='-flto' make -j ARCH=x86-64-modern COMP=mingw profile-build ``` fixes https://github.com/official-stockfish/Stockfish/issues/2769 closes https://github.com/official-stockfish/Stockfish/pull/2774 No functional change. --- src/Makefile | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 492403d3ce8..c3660a20fa6 100644 --- a/src/Makefile +++ b/src/Makefile @@ -344,10 +344,20 @@ endif ### needs access to the optimization flags. ifeq ($(optimize),yes) ifeq ($(debug), no) - ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + ifeq ($(comp),$(filter $(comp),gcc clang)) CXXFLAGS += -flto LDFLAGS += $(CXXFLAGS) endif + +# To use LTO and static linking on windows, the tool chain requires a recent gcc: +# gcc version 10.1 in msys2 or TDM-GCC version 9.2 are know to work, older might not. +# So, only enable it for a cross from Linux by default. + ifeq ($(comp),mingw) + ifeq ($(KERNEL),Linux) + CXXFLAGS += -flto + LDFLAGS += $(CXXFLAGS) + endif + endif endif endif From de24fcebc873ce2d65b30e039745dbc2e851f443 Mon Sep 17 00:00:00 2001 From: mstembera Date: Fri, 26 Jun 2020 17:26:46 -0700 Subject: [PATCH 0265/1766] Fix fragile code to use proper random 64 bit keys. This fixes an old issue where we want to make a position unique but only change a small number of bits in the key instead of all 64 of them randomly. This is fragile and can lead to non uniqueness issues in the TT. Key make_key(uint64_t seed) takes any integer and produces a unique random 64 bit key. It is computationally efficient and is based on a congruential pseudo random number generator using well tested constants by Donald Knuth (see https://en.wikipedia.org/wiki/Linear_congruential_generator) STC https://tests.stockfishchess.org/tests/view/5ef6c78f761b685b4c724bb6 LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 154320 W: 29343 L: 29376 D: 95601 Ptnml(0-2): 2543, 18170, 35891, 17889, 2667 LTC https://tests.stockfishchess.org/tests/view/5ef7d1a9020eec13834a940e LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 53488 W: 6629 L: 6584 D: 40275 Ptnml(0-2): 372, 4878, 16183, 4955, 356 closes https://github.com/official-stockfish/Stockfish/pull/2773 bench: 4626776 --- src/search.cpp | 2 +- src/types.h | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 1e2980cb0cd..0fa399882c2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -662,7 +662,7 @@ namespace { // search to overwrite a previous full search TT value, so we use a different // position key in case of an excluded move. excludedMove = ss->excludedMove; - posKey = pos.key() ^ (Key(excludedMove) << 48); // Isn't a very good hash + posKey = excludedMove == MOVE_NONE ? pos.key() : pos.key() ^ make_key(excludedMove); tte = TT.probe(posKey, ttHit); ttValue = ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] diff --git a/src/types.h b/src/types.h index 0c512f5bf3f..c1598561812 100644 --- a/src/types.h +++ b/src/types.h @@ -455,6 +455,11 @@ constexpr bool is_ok(Move m) { return from_sq(m) != to_sq(m); // Catch MOVE_NULL and MOVE_NONE } +/// Based on a congruential pseudo random number generator +constexpr Key make_key(uint64_t seed) { + return seed * 6364136223846793005ULL + 1442695040888963407ULL; +} + #endif // #ifndef TYPES_H_INCLUDED #include "tune.h" // Global visibility to tuning setup From 547c4a216a9931e4d5ff95414f146cb6eb877611 Mon Sep 17 00:00:00 2001 From: mstembera Date: Thu, 25 Jun 2020 22:08:17 -0700 Subject: [PATCH 0266/1766] Remove old zobrist trick for castling rights Removes an 8 year old micro optimization aimed at 32-bit architectures because back then doing an xor of a Key could not be done in one instruction. See original commit here 821e1c7 STC https://tests.stockfishchess.org/tests/view/5ef5833dde213bf647527d0c LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 162648 W: 31053 L: 31097 D: 100498 Ptnml(0-2): 2841, 18966, 37715, 19000, 2802 LTC https://tests.stockfishchess.org/tests/view/5ef7b1bbf993893290cc1489 LLR: 2.93 (-2.94,2.94) {-1.50,0.50} Total: 62360 W: 7617 L: 7586 D: 47157 Ptnml(0-2): 423, 5662, 18994, 5663, 438 closes https://github.com/official-stockfish/Stockfish/pull/2775 bench: 4591425 --- src/position.cpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index 471ef01f24f..6ef7aedc23f 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -119,15 +119,7 @@ void Position::init() { Zobrist::enpassant[f] = rng.rand(); for (int cr = NO_CASTLING; cr <= ANY_CASTLING; ++cr) - { - Zobrist::castling[cr] = 0; - Bitboard b = cr; - while (b) - { - Key k = Zobrist::castling[1ULL << pop_lsb(&b)]; - Zobrist::castling[cr] ^= k ? k : rng.rand(); - } - } + Zobrist::castling[cr] = rng.rand(); Zobrist::side = rng.rand(); Zobrist::noPawns = rng.rand(); @@ -780,9 +772,9 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // Update castling rights if needed if (st->castlingRights && (castlingRightsMask[from] | castlingRightsMask[to])) { - int cr = castlingRightsMask[from] | castlingRightsMask[to]; - k ^= Zobrist::castling[st->castlingRights & cr]; - st->castlingRights &= ~cr; + k ^= Zobrist::castling[st->castlingRights]; + st->castlingRights &= ~(castlingRightsMask[from] | castlingRightsMask[to]); + k ^= Zobrist::castling[st->castlingRights]; } // Move the piece. The tricky Chess960 castling is handled earlier From 2810a1ea85b3fbe62095fcb24442c08306f00af3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sun, 28 Jun 2020 06:00:28 +0200 Subject: [PATCH 0267/1766] Increase value of pawns on fifth rank This patch increases the endgame value of pawns on the fifth rank. The increase is very small (+1 evaluation point, about 0.005 pawn) for the pawns on external columns (a-b-c-f-g-h) and a bit bigger (+7 evaluation points, about 0.033 pawn) for the pawns on d5/e5. STC: LLR: 2.93 (-2.94,2.94) {-0.50,1.50} Total: 79864 W: 15331 L: 15027 D: 49506 Ptnml(0-2): 1336, 9284, 18433, 9498, 1381 https://tests.stockfishchess.org/tests/view/5ef73e2ef993893290cc0c47 LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 47240 W: 5927 L: 5630 D: 35683 Ptnml(0-2): 320, 4133, 14440, 4384, 343 https://tests.stockfishchess.org/tests/view/5ef7c0c4f993893290cc14b7 closes https://github.com/official-stockfish/Stockfish/pull/2776 Bench: 4794633 --- src/psqt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/psqt.cpp b/src/psqt.cpp index c5da9785bc9..5e8dd2c7229 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -92,7 +92,7 @@ constexpr Score PBonus[RANK_NB][FILE_NB] = { S( 3,-10), S( 3, -6), S( 10, 10), S( 19, 0), S( 16, 14), S( 19, 7), S( 7, -5), S( -5,-19) }, { S( -9,-10), S(-15,-10), S( 11,-10), S( 15, 4), S( 32, 4), S( 22, 3), S( 5, -6), S(-22, -4) }, { S( -4, 6), S(-23, -2), S( 6, -8), S( 20, -4), S( 40,-13), S( 17,-12), S( 4,-10), S( -8, -9) }, - { S( 13, 9), S( 0, 4), S(-13, 3), S( 1,-12), S( 11,-12), S( -2, -6), S(-13, 13), S( 5, 8) }, + { S( 13, 10), S( 0, 5), S(-13, 4), S( 1, -5), S( 11, -5), S( -2, -5), S(-13, 14), S( 5, 9) }, { S( 5, 28), S(-12, 20), S( -7, 21), S( 22, 28), S( -8, 30), S( -5, 7), S(-15, 6), S( -8, 13) }, { S( -7, 0), S( 7,-11), S( -3, 12), S(-13, 21), S( 5, 25), S(-16, 19), S( 10, 4), S( -8, 7) } }; From 16836f39b295ec635c9883498400f7006ac2869f Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Sun, 28 Jun 2020 16:28:55 +0200 Subject: [PATCH 0268/1766] Scale down eval for drawish rook endgames. STC: LLR: 2.96 (-2.94,2.94) {-0.50,1.50} Total: 82136 W: 15694 L: 15407 D: 51035 Ptnml(0-2): 1076, 8960, 20767, 9131, 1134 https://tests.stockfishchess.org/tests/view/5ef86cf8020eec13834a94dd LTC: LLR: 2.93 (-2.94,2.94) {0.25,1.75} Total: 70200 W: 8787 L: 8440 D: 52973 Ptnml(0-2): 325, 5983, 22170, 6264, 358 https://tests.stockfishchess.org/tests/view/5ef88225020eec13834a950a closes https://github.com/official-stockfish/Stockfish/pull/2780 Bench: 4478869 --- src/evaluate.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 60ec9c72a5b..65f7bddc76e 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -782,6 +782,13 @@ namespace { else sf = 22 + 3 * pos.count(strongSide); } + else if( pos.non_pawn_material(WHITE) == RookValueMg + && pos.non_pawn_material(BLACK) == RookValueMg + && !pe->passed_pawns(strongSide) + && pos.count(strongSide) - pos.count(~strongSide) <= 1 + && bool(KingSide & pos.pieces(strongSide, PAWN)) != bool(QueenSide & pos.pieces(strongSide, PAWN)) + && (attacks_bb(pos.square(~strongSide)) & pos.pieces(~strongSide, PAWN))) + sf = 36; else sf = std::min(sf, 36 + 7 * pos.count(strongSide)); } From c7194bd924a606ab75d582d30cb41749312ea94e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sun, 28 Jun 2020 22:24:57 +0200 Subject: [PATCH 0269/1766] Scale down eval for queen imbalance We lower the endgame value of the evaluation when we detect that there is only one queen left on the board (more precisely, we use a scale factor of 37/64, or about 0.58, for the endgame part of the evaluation). Hopefully this helps a little bit for the assessment of positions with queen imbalance, which are one of the well-known Stockfish weaknesses. STC: LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 21600 W: 4176 L: 3955 D: 13469 Ptnml(0-2): 351, 2457, 5003, 2598, 391 https://tests.stockfishchess.org/tests/view/5ef871b6020eec13834a94e8 LTC: LLR: 2.97 (-2.94,2.94) {0.25,1.75} Total: 248328 W: 30596 L: 29720 D: 188012 Ptnml(0-2): 1544, 22345, 75665, 22911, 1699 https://tests.stockfishchess.org/tests/view/5ef87aec020eec13834a94fe Closes https://github.com/official-stockfish/Stockfish/pull/2781 Bench: 4441323 --- src/evaluate.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 65f7bddc76e..d19cf34e31e 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -767,7 +767,6 @@ namespace { eg += v; // Compute the scale factor for the winning side - Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK; int sf = me->scale_factor(pos, strongSide); @@ -782,13 +781,15 @@ namespace { else sf = 22 + 3 * pos.count(strongSide); } - else if( pos.non_pawn_material(WHITE) == RookValueMg + else if ( pos.non_pawn_material(WHITE) == RookValueMg && pos.non_pawn_material(BLACK) == RookValueMg && !pe->passed_pawns(strongSide) && pos.count(strongSide) - pos.count(~strongSide) <= 1 && bool(KingSide & pos.pieces(strongSide, PAWN)) != bool(QueenSide & pos.pieces(strongSide, PAWN)) && (attacks_bb(pos.square(~strongSide)) & pos.pieces(~strongSide, PAWN))) sf = 36; + else if (pos.count() == 1) + sf = 37; else sf = std::min(sf, 36 + 7 * pos.count(strongSide)); } From 69d3be42a112645a9e599df615f730d61a5dca8c Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Mon, 29 Jun 2020 19:35:24 +0200 Subject: [PATCH 0270/1766] Tweak single queen endgame scaling. Increase scaling factor for each minor of the opponent side of the queen. STC: LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 14528 W: 2860 L: 2653 D: 9015 Ptnml(0-2): 217, 1632, 3408, 1741, 266 https://tests.stockfishchess.org/tests/view/5ef98384020eec13834a96a0 LTC: LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 34584 W: 4371 L: 4111 D: 26102 Ptnml(0-2): 205, 3080, 10501, 3262, 244 https://tests.stockfishchess.org/tests/view/5ef99972020eec13834a96c9 closes https://github.com/official-stockfish/Stockfish/pull/2782 Bench: 4523573 --- src/evaluate.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d19cf34e31e..615df1bacd1 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -789,7 +789,8 @@ namespace { && (attacks_bb(pos.square(~strongSide)) & pos.pieces(~strongSide, PAWN))) sf = 36; else if (pos.count() == 1) - sf = 37; + sf = 37 + 3 * (pos.count(WHITE) == 1 ? pos.count(BLACK) + pos.count(BLACK) + : pos.count(WHITE) + pos.count(WHITE)); else sf = std::min(sf, 36 + 7 * pos.count(strongSide)); } From 110068808b51344ac59f8c6a0846f5dfdf670392 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 27 Jun 2020 21:29:29 +0200 Subject: [PATCH 0271/1766] Provide WDL statistics A number of engines, GUIs and tournaments start to report WDL estimates along or instead of scores. This patch enables reporting of those stats in a more or less standard way (http://www.talkchess.com/forum3/viewtopic.php?t=72140) The model this reporting uses is based on data derived from a few million fishtest LTC games, given a score and a game ply, a win rate is provided that matches rather closely, especially in the intermediate range [0.05, 0.95] that data. Some data is shown at https://github.com/glinscott/fishtest/wiki/UsefulData#win-loss-draw-statistics-of-ltc-games-on-fishtest Making the conversion game ply dependent is important for a good fit, and is in line with experience that a +1 score in the early midgame is more likely a win than in the late endgame. Even when enabled, the printing of the info causes no significant overhead. Passed STC: LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 197112 W: 37226 L: 37347 D: 122539 Ptnml(0-2): 2591, 21025, 51464, 20866, 2610 https://tests.stockfishchess.org/tests/view/5ef79ef4f993893290cc146b closes https://github.com/official-stockfish/Stockfish/pull/2778 No functional change --- Readme.md | 5 +++++ src/search.cpp | 3 +++ src/uci.cpp | 39 +++++++++++++++++++++++++++++++++++++++ src/uci.h | 1 + src/ucioption.cpp | 1 + 5 files changed, 49 insertions(+) diff --git a/Readme.md b/Readme.md index 2b1de86be65..e60ac7185d7 100644 --- a/Readme.md +++ b/Readme.md @@ -66,6 +66,11 @@ Currently, Stockfish has the following UCI options: 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. + * #### UCI_ShowWDL + If enabled, show approximate WDL statistics as part of the engine output. + These WDL numbers model expected game outcomes for a given evaluation and + game ply for engine self-play at fishtest LTC conditions (60+0.6s per game). + * #### Move Overhead Assume a time delay of x ms due to network and GUI overheads. This is useful to avoid losses on time in those cases. diff --git a/src/search.cpp b/src/search.cpp index 0fa399882c2..f14bdf77055 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1835,6 +1835,9 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) { << " multipv " << i + 1 << " score " << UCI::value(v); + if (Options["UCI_ShowWDL"]) + ss << UCI::wdl(v, pos.game_ply()); + if (!tb && i == pvIdx) ss << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : ""); diff --git a/src/uci.cpp b/src/uci.cpp index 11d5adc644e..bb57c80b5f6 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -19,6 +19,7 @@ */ #include +#include #include #include #include @@ -182,6 +183,28 @@ namespace { << "\nNodes/second : " << 1000 * nodes / elapsed << endl; } + // The win rate model returns the probability (per mille) of winning given an eval + // and a game-ply. The model fits rather accurately the LTC fishtest statistics. + int win_rate_model(Value v, int ply) { + + // The model captures only up to 240 plies, so limit input (and rescale) + double m = std::min(240, ply) / 64.0; + + // Coefficients of a 3rd order polynomial fit based on fishtest data + // for two parameters needed to transform eval to the argument of a + // logistic function. + double as[] = {-8.24404295, 64.23892342, -95.73056462, 153.86478679}; + double bs[] = {-3.37154371, 28.44489198, -56.67657741, 72.05858751}; + double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3]; + double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3]; + + // Transform eval to centipawns with limited range + double x = Utility::clamp(double(100 * v) / PawnValueEg, -1000.0, 1000.0); + + // Return win rate in per mille (rounded to nearest) + return int(0.5 + 1000 / (1 + std::exp((a - x) / b))); + } + } // namespace @@ -269,6 +292,22 @@ string UCI::value(Value v) { } +/// UCI::wdl() report WDL statistics given an evaluation and a game ply, based on +/// data gathered for fishtest LTC games. + +string UCI::wdl(Value v, int ply) { + + stringstream ss; + + int wdl_w = win_rate_model( v, ply); + int wdl_l = win_rate_model(-v, ply); + int wdl_d = 1000 - wdl_w - wdl_l; + ss << " wdl " << wdl_w << " " << wdl_d << " " << wdl_l; + + return ss.str(); +} + + /// UCI::square() converts a Square to a string in algebraic notation (g1, a7, etc.) std::string UCI::square(Square s) { diff --git a/src/uci.h b/src/uci.h index b845889bcc1..ad954d9f712 100644 --- a/src/uci.h +++ b/src/uci.h @@ -73,6 +73,7 @@ std::string value(Value v); std::string square(Square s); std::string move(Move m, bool chess960); std::string pv(const Position& pos, Depth depth, Value alpha, Value beta); +std::string wdl(Value v, int ply); Move to_move(const Position& pos, std::string& str); } // namespace UCI diff --git a/src/ucioption.cpp b/src/ucioption.cpp index c268c9755f9..4befa6ac7c2 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -74,6 +74,7 @@ void init(OptionsMap& o) { o["UCI_AnalyseMode"] << Option(false); o["UCI_LimitStrength"] << Option(false); o["UCI_Elo"] << Option(1350, 1350, 2850); + o["UCI_ShowWDL"] << Option(true); o["SyzygyPath"] << Option("", on_tb_path); o["SyzygyProbeDepth"] << Option(1, 1, 100); o["Syzygy50MoveRule"] << Option(true); From 268c00b648ba4a48be79a849dde5733e6705ddbf Mon Sep 17 00:00:00 2001 From: Alain SAVARD Date: Wed, 1 Jul 2020 02:12:59 -0400 Subject: [PATCH 0272/1766] Use arrays for safe checks, outposts and king protectors in evaluate.cpp Tested for non regression on the safe checks https://tests.stockfishchess.org/tests/view/5ef8b75c020eec13834a9596 LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 22256 W: 4283 L: 4143 D: 13830 Ptnml(0-2): 291, 2439, 5588, 2459, 351 Tested for non regression on the safe checks, outposts and king protectors https://tests.stockfishchess.org/tests/view/5ef8e543020eec13834a95e7 LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 28400 W: 5382 L: 5253 D: 17765 Ptnml(0-2): 394, 3078, 7119, 3223, 386 closes https://github.com/official-stockfish/Stockfish/pull/2785 No functional change --- src/evaluate.cpp | 78 ++++++++++++++++++++++-------------------------- src/pawns.cpp | 4 ++- 2 files changed, 39 insertions(+), 43 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 615df1bacd1..48db2b3be43 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -80,11 +80,11 @@ namespace { // KingAttackWeights[PieceType] contains king attack weights by piece type constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 }; - // Penalties for enemy's safe checks - constexpr int QueenSafeCheck = 772; - constexpr int RookSafeCheck = 1084; - constexpr int BishopSafeCheck = 645; - constexpr int KnightSafeCheck = 792; + // SafeCheck[PieceType][single/multiple] contains safe check bonus by piece type, + // higher if multiple safe checks are possible for that piece type. + constexpr int SafeCheck[][2] = { + {}, {}, {792, 1283}, {645, 967}, {1084, 1897}, {772, 1119} + }; #define S(mg, eg) make_score(mg, eg) @@ -106,6 +106,18 @@ namespace { S(110,182), S(114,182), S(114,192), S(116,219) } }; + // KingProtector[knight/bishop] contains penalty for each distance unit to own king + constexpr Score KingProtector[] = { S(8, 9), S(6, 9) }; + + // Outpost[knight/bishop] contains bonuses for each knight or bishop occupying a + // pawn protected square on rank 4 to 6 which is also safe from a pawn attack. + constexpr Score Outpost[] = { S(56, 36), S(30, 23) }; + + // PassedRank[Rank] contains a bonus according to the rank of a passed pawn + constexpr Score PassedRank[RANK_NB] = { + S(0, 0), S(10, 28), S(17, 33), S(15, 41), S(62, 72), S(168, 177), S(276, 260) + }; + // RookOnFile[semiopen/open] contains bonuses for each rook when there is // no (friendly) pawn on the rook file. constexpr Score RookOnFile[] = { S(19, 7), S(48, 29) }; @@ -121,23 +133,14 @@ namespace { S(0, 0), S(3, 46), S(37, 68), S(42, 60), S(0, 38), S(58, 41) }; - // PassedRank[Rank] contains a bonus according to the rank of a passed pawn - constexpr Score PassedRank[RANK_NB] = { - 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 - constexpr Score BishopKingProtector = S( 6, 9); constexpr Score BishopOnKingRing = S( 24, 0); - constexpr Score BishopOutpost = S( 30, 23); constexpr Score BishopPawns = S( 3, 7); constexpr Score BishopXRayPawns = S( 4, 5); constexpr Score CorneredBishop = S( 50, 50); constexpr Score FlankAttacks = S( 8, 0); constexpr Score Hanging = S( 69, 36); - constexpr Score KnightKingProtector = S( 8, 9); constexpr Score KnightOnQueen = S( 16, 11); - constexpr Score KnightOutpost = S( 56, 36); constexpr Score LongDiagonalBishop = S( 45, 0); constexpr Score MinorBehindPawn = S( 18, 3); constexpr Score PassedFile = S( 11, 8); @@ -308,7 +311,7 @@ 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 += (Pt == KNIGHT) ? KnightOutpost : BishopOutpost; + score += Outpost[Pt == BISHOP]; else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us)) score += ReachableOutpost; @@ -317,8 +320,7 @@ namespace { score += MinorBehindPawn; // Penalty if the piece is far from the king - score -= (Pt == KNIGHT ? KnightKingProtector - : BishopKingProtector) * distance(pos.square(Us), s); + score -= KingProtector[Pt == BISHOP] * distance(pos.square(Us), s); if (Pt == BISHOP) { @@ -420,41 +422,33 @@ namespace { b2 = attacks_bb(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN)); // Enemy rooks checks - rookChecks = b1 & safe & attackedBy[Them][ROOK]; + rookChecks = b1 & attackedBy[Them][ROOK] & safe; if (rookChecks) - kingDanger += more_than_one(rookChecks) ? RookSafeCheck * 175/100 - : RookSafeCheck; + kingDanger += SafeCheck[ROOK][more_than_one(rookChecks)]; else unsafeChecks |= b1 & attackedBy[Them][ROOK]; - // Enemy queen safe checks: we count them only if they are from squares from - // which we can't give a rook check, because rook checks are more valuable. - queenChecks = (b1 | b2) - & attackedBy[Them][QUEEN] - & safe - & ~attackedBy[Us][QUEEN] - & ~rookChecks; + // Enemy queen safe checks: count them only if the checks are from squares from + // which opponent cannot give a rook check, because rook checks are more valuable. + queenChecks = (b1 | b2) & attackedBy[Them][QUEEN] & safe + & ~(attackedBy[Us][QUEEN] | rookChecks); if (queenChecks) - kingDanger += more_than_one(queenChecks) ? QueenSafeCheck * 145/100 - : QueenSafeCheck; - - // Enemy bishops checks: we count them only if they are from squares from - // which we can't give a queen check, because queen checks are more valuable. - bishopChecks = b2 - & attackedBy[Them][BISHOP] - & safe + kingDanger += SafeCheck[QUEEN][more_than_one(queenChecks)]; + + // Enemy bishops checks: count them only if they are from squares from which + // opponent cannot give a queen check, because queen checks are more valuable. + bishopChecks = b2 & attackedBy[Them][BISHOP] & safe & ~queenChecks; if (bishopChecks) - kingDanger += more_than_one(bishopChecks) ? BishopSafeCheck * 3/2 - : BishopSafeCheck; + kingDanger += SafeCheck[BISHOP][more_than_one(bishopChecks)]; + else unsafeChecks |= b2 & attackedBy[Them][BISHOP]; // Enemy knights checks knightChecks = attacks_bb(ksq) & attackedBy[Them][KNIGHT]; if (knightChecks & safe) - kingDanger += more_than_one(knightChecks & safe) ? KnightSafeCheck * 162/100 - : KnightSafeCheck; + kingDanger += SafeCheck[KNIGHT][more_than_one(knightChecks & safe)]; else unsafeChecks |= knightChecks; @@ -464,7 +458,7 @@ namespace { b2 = b1 & attackedBy2[Them]; b3 = attackedBy[Us][ALL_PIECES] & KingFlank[file_of(ksq)] & Camp; - int kingFlankAttack = popcount(b1) + popcount(b2); + int kingFlankAttack = popcount(b1) + popcount(b2); int kingFlankDefense = popcount(b3); kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them] @@ -741,8 +735,8 @@ namespace { bool almostUnwinnable = outflanking < 0 && !pawnsOnBothFlanks; - bool infiltration = rank_of(pos.square(WHITE)) > RANK_4 - || rank_of(pos.square(BLACK)) < RANK_5; + bool infiltration = rank_of(pos.square(WHITE)) > RANK_4 + || rank_of(pos.square(BLACK)) < RANK_5; // Compute the initiative bonus for the attacking side int complexity = 9 * pe->passed_count() diff --git a/src/pawns.cpp b/src/pawns.cpp index d741b2ef1b3..d365ba121c3 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -38,7 +38,9 @@ namespace { constexpr Score WeakLever = S( 0, 56); constexpr Score WeakUnopposed = S(13, 27); - constexpr Score BlockedStorm[RANK_NB] = {S( 0, 0), S( 0, 0), S( 76, 78), S(-10, 15), S(-7, 10), S(-4, 6), S(-1, 2)}; + constexpr Score BlockedStorm[RANK_NB] = { + S(0, 0), S(0, 0), S(76, 78), S(-10, 15), S(-7, 10), S(-4, 6), S(-1, 2) + }; // Connected pawn bonus constexpr int Connected[RANK_NB] = { 0, 7, 8, 12, 29, 48, 86 }; From fb83da0892c183690ddeb1f7c3dbf6779b12707a Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 2 Jul 2020 18:58:37 +0200 Subject: [PATCH 0273/1766] Set UCI_ShowWDL by default to false UCI_ShowWDL might not be shown by GUIs that don't know the option, but crash on the WDL output, effectively making it hard for users to turn it off and run the engine. This sets it by default to false. fixes https://github.com/official-stockfish/Stockfish/issues/2787 closes https://github.com/official-stockfish/Stockfish/pull/2788 No functional change. --- src/ucioption.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 4befa6ac7c2..ef54ef4e5be 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -74,7 +74,7 @@ void init(OptionsMap& o) { o["UCI_AnalyseMode"] << Option(false); o["UCI_LimitStrength"] << Option(false); o["UCI_Elo"] << Option(1350, 1350, 2850); - o["UCI_ShowWDL"] << Option(true); + o["UCI_ShowWDL"] << Option(false); o["SyzygyPath"] << Option("", on_tb_path); o["SyzygyProbeDepth"] << Option(1, 1, 100); o["Syzygy50MoveRule"] << Option(true); From 67818ee9481ba99369fa8a8d92e5c50428fb300e Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Thu, 2 Jul 2020 00:11:23 +0800 Subject: [PATCH 0274/1766] Remove passed pawn condition. This will help scale down relatively high eval in drawish rook endgames with passed pawn like in TCEC S18 Superfinal Game 90. Passed STC LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 50456 W: 9644 L: 9540 D: 31272 Ptnml(0-2): 760, 5637, 12332, 5737, 762 https://tests.stockfishchess.org/tests/view/5efcb76e59f6f035328940ed Passed LTC LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 77264 W: 9518 L: 9518 D: 58228 Ptnml(0-2): 402, 6766, 24321, 6716, 427 https://tests.stockfishchess.org/tests/view/5efd2ad759f6f03532894143 closes https://github.com/official-stockfish/Stockfish/pull/2792 Bench: 4431626 --- src/evaluate.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 48db2b3be43..bb1724a4e3b 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -777,7 +777,6 @@ namespace { } else if ( pos.non_pawn_material(WHITE) == RookValueMg && pos.non_pawn_material(BLACK) == RookValueMg - && !pe->passed_pawns(strongSide) && pos.count(strongSide) - pos.count(~strongSide) <= 1 && bool(KingSide & pos.pieces(strongSide, PAWN)) != bool(QueenSide & pos.pieces(strongSide, PAWN)) && (attacks_bb(pos.square(~strongSide)) & pos.pieces(~strongSide, PAWN))) From c5b2a92cd17c65a639ec6739dd511767f65e188d Mon Sep 17 00:00:00 2001 From: protonspring Date: Tue, 30 Jun 2020 10:17:50 -0600 Subject: [PATCH 0275/1766] denormalize KRKP. a non-functional code style change that denormalizes the KRKP endgame, making it somewhat easier to read. closes https://github.com/official-stockfish/Stockfish/pull/2786 No functional change --- src/endgame.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/endgame.cpp b/src/endgame.cpp index be0755a866f..40f49dce501 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -181,15 +181,15 @@ Value Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, RookValueMg, 0)); assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); - Square strongKing = relative_square(strongSide, pos.square(strongSide)); - Square weakKing = relative_square(strongSide, pos.square(weakSide)); - Square strongRook = relative_square(strongSide, pos.square(strongSide)); - Square weakPawn = relative_square(strongSide, pos.square(weakSide)); - Square queeningSquare = make_square(file_of(weakPawn), RANK_1); + Square strongKing = pos.square(strongSide); + Square weakKing = pos.square(weakSide); + Square strongRook = pos.square(strongSide); + Square weakPawn = pos.square(weakSide); + Square queeningSquare = make_square(file_of(weakPawn), relative_rank(weakSide, RANK_8)); Value result; // If the stronger side's king is in front of the pawn, it's a win - if (forward_file_bb(WHITE, strongKing) & weakPawn) + if (forward_file_bb(strongSide, strongKing) & weakPawn) result = RookValueEg - distance(strongKing, weakPawn); // If the weaker side's king is too far from the pawn and the rook, @@ -200,15 +200,15 @@ Value Endgame::operator()(const Position& pos) const { // If the pawn is far advanced and supported by the defending king, // the position is drawish - else if ( rank_of(weakKing) <= RANK_3 + else if ( relative_rank(strongSide, weakKing) <= RANK_3 && distance(weakKing, weakPawn) == 1 - && rank_of(strongKing) >= RANK_4 + && relative_rank(strongSide, strongKing) >= RANK_4 && distance(strongKing, weakPawn) > 2 + (pos.side_to_move() == strongSide)) result = Value(80) - 8 * distance(strongKing, weakPawn); else - result = Value(200) - 8 * ( distance(strongKing, weakPawn + SOUTH) - - distance(weakKing, weakPawn + SOUTH) + result = Value(200) - 8 * ( distance(strongKing, weakPawn + pawn_push(weakSide)) + - distance(weakKing, weakPawn + pawn_push(weakSide)) - distance(weakPawn, queeningSquare)); return strongSide == pos.side_to_move() ? result : -result; From 7225d254f90c7b9d64d4adf85ec2d319c6cf75a0 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Mon, 6 Jul 2020 09:30:23 +0200 Subject: [PATCH 0276/1766] Add a rank based bonus for blocked pawns. Fix for overevaluated blocked pawns on the 5th and 6th rank. This is a rewrite of the original idea that uses only two parameters. Thanks to rocky640 for pointing this out. STC: LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 50800 W: 9707 L: 9446 D: 31647 Ptnml(0-2): 831, 5851, 11822, 6018, 878 https://tests.stockfishchess.org/tests/view/5f00b4f359f6f03532894304 LTC: LLR: 2.93 (-2.94,2.94) {0.25,1.75} Total: 52064 W: 6477 L: 6167 D: 39420 Ptnml(0-2): 331, 4628, 15834, 4878, 361 https://tests.stockfishchess.org/tests/view/5f0115fe59f6f03532894345 closes https://github.com/official-stockfish/Stockfish/pull/2794 Bench: 4882833 --- src/pawns.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/pawns.cpp b/src/pawns.cpp index d365ba121c3..f18e0315504 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -38,6 +38,9 @@ namespace { constexpr Score WeakLever = S( 0, 56); constexpr Score WeakUnopposed = S(13, 27); + // Bonus for blocked pawns at 5th or 6th rank + constexpr Score BlockedPawn[2] = { S(-10, -3), S(-3, 3) }; + constexpr Score BlockedStorm[RANK_NB] = { S(0, 0), S(0, 0), S(76, 78), S(-10, 15), S(-7, 10), S(-4, 6), S(-1, 2) }; @@ -169,6 +172,9 @@ namespace { if (!support) score -= Doubled * doubled + WeakLever * more_than_one(lever); + + if (blocked && r > RANK_4) + score += BlockedPawn[r-4]; } return score; From 76a039027d14640852f60bda6d62ca16bdac3b9e Mon Sep 17 00:00:00 2001 From: Alain SAVARD Date: Mon, 6 Jul 2020 22:43:54 -0400 Subject: [PATCH 0277/1766] Clean-up en passant processing the goal of this PR is to better document how we process the ep square (if any) given position fen command, and to output more meaningful (and consistent) debug fen on the "d" command. The implementation follows https://en.wikipedia.org/wiki/X-FEN#Encoding_en-passant following x-fen, it is "valid" to record ep even if ep would put king en prise. fixes #2784 closes https://github.com/official-stockfish/Stockfish/pull/2797 No functional change --- src/position.cpp | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index 6ef7aedc23f..396bff5f545 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -178,9 +178,9 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th 4) En passant target square (in algebraic notation). If there's no en passant target square, this is "-". If a pawn has just made a 2-square move, this - is the position "behind" the pawn. This is recorded only if there is a pawn - in position to make an en passant capture, and if there really is a pawn - that might have advanced two squares. + is the position "behind" the pawn. Following X-FEN standard, this is recorded only + if there is a pawn in position to make an en passant capture, and if there really + is a pawn that might have advanced two squares. 5) Halfmove clock. This is the number of halfmoves since the last pawn advance or capture. This is used to determine if a draw can be claimed under the @@ -251,17 +251,25 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th set_castling_right(c, rsq); } - // 4. En passant square. Ignore if no pawn capture is possible + // 4. En passant square. + // Ignore if square is invalid or not on side to move relative rank 6. + bool enpassant = false; + if ( ((ss >> col) && (col >= 'a' && col <= 'h')) - && ((ss >> row) && (row == '3' || row == '6'))) + && ((ss >> row) && (row == (sideToMove == WHITE ? '6' : '3')))) { st->epSquare = make_square(File(col - 'a'), Rank(row - '1')); - if ( !(attackers_to(st->epSquare) & pieces(sideToMove, PAWN)) - || !(pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove)))) - st->epSquare = SQ_NONE; + // En passant square will be considered only if + // a) side to move have a pawn threatening epSquare + // b) there is an enemy pawn in front of epSquare + // c) there is no piece on epSquare or behind epSquare + enpassant = pawn_attacks_bb(~sideToMove, st->epSquare) & pieces(sideToMove, PAWN) + && (pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove))) + && !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove)))); } - else + + if (!enpassant) st->epSquare = SQ_NONE; // 5-6. Halfmove clock and fullmove number From 804a29c738847b7ea5f8a4bff001964bd234d332 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Wed, 8 Jul 2020 01:29:03 +0300 Subject: [PATCH 0278/1766] Connected / blocked pawns simplification There is no need to score blocked pawns at many places. The idea originated from: Rocky Tuning and testing by: Fauzi Passed STC: https://tests.stockfishchess.org/tests/view/5f04f8fd59f6f035328945d4 LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 6352 W: 1299 L: 1118 D: 3935 Ptnml(0-2): 89, 695, 1469, 792, 131 Passed LTC: https://tests.stockfishchess.org/tests/view/5f0527bd59f6f035328945e3 LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 27648 W: 3517 L: 3433 D: 20698 Ptnml(0-2): 177, 2561, 8301, 2571, 214 closes https://github.com/official-stockfish/Stockfish/pull/2799 Bench: 4734746 --- src/pawns.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index f18e0315504..7f8d451a6d4 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -39,7 +39,7 @@ namespace { constexpr Score WeakUnopposed = S(13, 27); // Bonus for blocked pawns at 5th or 6th rank - constexpr Score BlockedPawn[2] = { S(-10, -3), S(-3, 3) }; + constexpr Score BlockedPawn[2] = { S(-11, -4), S(-3, 4) }; constexpr Score BlockedStorm[RANK_NB] = { S(0, 0), S(0, 0), S(76, 78), S(-10, 15), S(-7, 10), S(-4, 6), S(-1, 2) @@ -148,7 +148,7 @@ namespace { // Score this pawn if (support | phalanx) { - int v = Connected[r] * (4 + 2 * bool(phalanx) - 2 * bool(opposed) - bool(blocked)) / 2 + int v = Connected[r] * (2 + bool(phalanx) - bool(opposed)) + 21 * popcount(support); score += make_score(v, v * (r - 2) / 4); From bf5ce1c214f8f8e3f98e5e3ac43db0dd28617e35 Mon Sep 17 00:00:00 2001 From: mstembera Date: Sun, 5 Jul 2020 15:17:04 -0700 Subject: [PATCH 0279/1766] Simplify make_promotions() Remove special case handling of QUIET_CHECKS in make_promotions() STC https://tests.stockfishchess.org/tests/view/5f055dbb59f6f035328945fb LLR: 2.98 (-2.94,2.94) {-1.50,0.50} Total: 42808 W: 8177 L: 8054 D: 26577 Ptnml(0-2): 665, 4890, 10201, 4953, 695 LTC https://tests.stockfishchess.org/tests/view/5f06231a59f6f03532894661 LLR: 2.96 (-2.94,2.94) {-1.50,0.50} Total: 9616 W: 1214 L: 1111 D: 7291 Ptnml(0-2): 53, 821, 2965, 908, 61 closes https://github.com/official-stockfish/Stockfish/pull/2800 Bench: 4576410 --- src/movegen.cpp | 22 ++++++++++------------ src/search.cpp | 4 ++-- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index 17203a95938..4ff12fc6452 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -29,22 +29,20 @@ namespace { ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) { if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) + { *moveList++ = make(to - D, to, QUEEN); + if (attacks_bb(to) & ksq) + *moveList++ = make(to - D, to, KNIGHT); + } if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS) { *moveList++ = make(to - D, to, ROOK); *moveList++ = make(to - D, to, BISHOP); - *moveList++ = make(to - D, to, KNIGHT); + if (!(attacks_bb(to) & ksq)) + *moveList++ = make(to - D, to, KNIGHT); } - // Knight promotion is the only promotion that can give a direct check - // that's not already included in the queen promotion. - if (Type == QUIET_CHECKS && (attacks_bb(to) & ksq)) - *moveList++ = make(to - D, to, KNIGHT); - else - (void)ksq; // Silence a warning under MSVC - return moveList; } @@ -263,8 +261,8 @@ namespace { } // namespace -/// Generates all pseudo-legal captures and queen promotions -/// Generates all pseudo-legal non-captures and underpromotions +/// Generates all pseudo-legal captures plus queen and checking knight promotions +/// Generates all pseudo-legal non-captures and underpromotions(except checking knight) /// Generates all pseudo-legal captures and non-captures /// /// Returns a pointer to the end of the move list. @@ -287,8 +285,8 @@ template ExtMove* generate(const Position&, ExtMove*); template ExtMove* generate(const Position&, ExtMove*); -/// generate generates all pseudo-legal non-captures and knight -/// underpromotions that give check. Returns a pointer to the end of the move list. +/// generate generates all pseudo-legal non-captures. +/// Returns a pointer to the end of the move list. template<> ExtMove* generate(const Position& pos, ExtMove* moveList) { diff --git a/src/search.cpp b/src/search.cpp index f14bdf77055..1610c206dd7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1486,8 +1486,8 @@ namespace { // Initialize a MovePicker object for the current position, and prepare // to search the moves. Because the depth is <= 0 here, only captures, - // queen promotions and checks (only if depth >= DEPTH_QS_CHECKS) will - // be generated. + // queen and checking knight promotions, and other checks(only if depth >= DEPTH_QS_CHECKS) + // will be generated. MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory, contHist, From 4006f2c9132db034a27a94be33070d6aaab75b24 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 9 Jul 2020 22:01:06 +0200 Subject: [PATCH 0280/1766] Small cleanups closes https://github.com/official-stockfish/Stockfish/pull/2772 No functional change --- Readme.md | 3 --- src/benchmark.cpp | 2 +- src/bitboard.h | 32 ++++++++++++++++---------------- src/evaluate.cpp | 10 +++++----- src/search.cpp | 14 +++++++++----- 5 files changed, 31 insertions(+), 30 deletions(-) diff --git a/Readme.md b/Readme.md index e60ac7185d7..823518d176a 100644 --- a/Readme.md +++ b/Readme.md @@ -75,9 +75,6 @@ Currently, Stockfish has the following UCI options: Assume a time delay of x ms due to network and GUI overheads. This is useful to avoid losses on time in those cases. - * #### Minimum Thinking Time - Search for at least x ms per move. - * #### Slow Mover Lower values will make Stockfish take less time in games, higher values will make it think longer. diff --git a/src/benchmark.cpp b/src/benchmark.cpp index f338cdda859..3299f3737f8 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -88,7 +88,7 @@ const vector Defaults = { // Chess 960 "setoption name UCI_Chess960 value true", - "bbqnnrkr/pppppppp/8/8/8/8/PPPPPPPP/BBQNNRKR w KQkq - 0 1 moves g2g3 d7d5 d2d4 c8h3 c1g5 e8d6 g5e7 f7f6", + "bbqnnrkr/pppppppp/8/8/8/8/PPPPPPPP/BBQNNRKR w HFhf - 0 1 moves g2g3 d7d5 d2d4 c8h3 c1g5 e8d6 g5e7 f7f6", "setoption name UCI_Chess960 value false" }; diff --git a/src/bitboard.h b/src/bitboard.h index 1c598108057..afeb40ec146 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -124,7 +124,7 @@ inline Bitboard operator&(Square s, Bitboard b) { return b & s; } inline Bitboard operator|(Square s, Bitboard b) { return b | s; } inline Bitboard operator^(Square s, Bitboard b) { return b ^ s; } -inline Bitboard operator|(Square s, Square s2) { return square_bb(s) | s2; } +inline Bitboard operator|(Square s1, Square s2) { return square_bb(s1) | s2; } constexpr bool more_than_one(Bitboard b) { return b & (b - 1); @@ -138,19 +138,19 @@ constexpr bool opposite_colors(Square s1, Square s2) { /// rank_bb() and file_bb() return a bitboard representing all the squares on /// the given file or rank. -inline Bitboard rank_bb(Rank r) { +constexpr Bitboard rank_bb(Rank r) { return Rank1BB << (8 * r); } -inline Bitboard rank_bb(Square s) { +constexpr Bitboard rank_bb(Square s) { return rank_bb(rank_of(s)); } -inline Bitboard file_bb(File f) { +constexpr Bitboard file_bb(File f) { return FileABB << f; } -inline Bitboard file_bb(Square s) { +constexpr Bitboard file_bb(Square s) { return file_bb(file_of(s)); } @@ -195,16 +195,16 @@ constexpr Bitboard pawn_double_attacks_bb(Bitboard b) { /// adjacent_files_bb() returns a bitboard representing all the squares on the -/// adjacent files of the given one. +/// adjacent files of a given square. -inline Bitboard adjacent_files_bb(Square s) { +constexpr Bitboard adjacent_files_bb(Square s) { return shift(file_bb(s)) | shift(file_bb(s)); } -/// line_bb(Square, Square) returns a bitboard representing an entire line, -/// from board edge to board edge, that intersects the given squares. If the -/// given squares are not on a same file/rank/diagonal, returns 0. For instance, +/// line_bb() returns a bitboard representing an entire line (from board edge +/// to board edge) that intersects the two given squares. If the given squares +/// are not on a same file/rank/diagonal, the function returns 0. For instance, /// line_bb(SQ_C4, SQ_F7) will return a bitboard with the A2-G8 diagonal. inline Bitboard line_bb(Square s1, Square s2) { @@ -215,8 +215,8 @@ inline Bitboard line_bb(Square s1, Square s2) { /// between_bb() returns a bitboard representing squares that are linearly -/// between the given squares (excluding the given squares). If the given -/// squares are not on a same file/rank/diagonal, return 0. For instance, +/// between the two given squares (excluding the given squares). If the given +/// squares are not on a same file/rank/diagonal, we return 0. For instance, /// between_bb(SQ_C4, SQ_F7) will return a bitboard with squares D5 and E6. inline Bitboard between_bb(Square s1, Square s2) { @@ -229,7 +229,7 @@ inline Bitboard between_bb(Square s1, Square s2) { /// in front of the given one, from the point of view of the given color. For instance, /// forward_ranks_bb(BLACK, SQ_D3) will return the 16 squares on ranks 1 and 2. -inline Bitboard forward_ranks_bb(Color c, Square s) { +constexpr Bitboard forward_ranks_bb(Color c, Square s) { return c == WHITE ? ~Rank1BB << 8 * relative_rank(WHITE, s) : ~Rank8BB >> 8 * relative_rank(BLACK, s); } @@ -238,7 +238,7 @@ inline Bitboard forward_ranks_bb(Color c, Square s) { /// forward_file_bb() returns a bitboard representing all the squares along the /// line in front of the given one, from the point of view of the given color. -inline Bitboard forward_file_bb(Color c, Square s) { +constexpr Bitboard forward_file_bb(Color c, Square s) { return forward_ranks_bb(c, s) & file_bb(s); } @@ -247,7 +247,7 @@ inline Bitboard forward_file_bb(Color c, Square s) { /// be attacked by a pawn of the given color when it moves along its file, starting /// from the given square. -inline Bitboard pawn_attack_span(Color c, Square s) { +constexpr Bitboard pawn_attack_span(Color c, Square s) { return forward_ranks_bb(c, s) & adjacent_files_bb(s); } @@ -255,7 +255,7 @@ inline Bitboard pawn_attack_span(Color c, Square s) { /// passed_pawn_span() returns a bitboard which can be used to test if a pawn of /// the given color and on the given square is a passed pawn. -inline Bitboard passed_pawn_span(Color c, Square s) { +constexpr Bitboard passed_pawn_span(Color c, Square s) { return pawn_attack_span(c, s) | forward_file_bb(c, s); } diff --git a/src/evaluate.cpp b/src/evaluate.cpp index bb1724a4e3b..6f2dd69ba33 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -719,9 +719,9 @@ namespace { } - // Evaluation::winnable() adjusts the mg and eg score components based on the - // known attacking/defending status of the players. A single value is derived - // by interpolation from the mg and eg values and returned. + // Evaluation::winnable() adjusts the midgame and endgame score components, based on + // the known attacking/defending status of the players. The final value is derived + // by interpolation from the midgame and endgame values. template Value Evaluation::winnable(Score score) const { @@ -764,7 +764,7 @@ namespace { Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK; int sf = me->scale_factor(pos, strongSide); - // If scale is not already specific, scale down the endgame via general heuristics + // If scale factor is not already specific, scale down via general heuristics if (sf == SCALE_FACTOR_NORMAL) { if (pos.opposite_bishops()) @@ -779,7 +779,7 @@ namespace { && pos.non_pawn_material(BLACK) == RookValueMg && pos.count(strongSide) - pos.count(~strongSide) <= 1 && bool(KingSide & pos.pieces(strongSide, PAWN)) != bool(QueenSide & pos.pieces(strongSide, PAWN)) - && (attacks_bb(pos.square(~strongSide)) & pos.pieces(~strongSide, PAWN))) + && (attackedBy[~strongSide][KING] & pos.pieces(~strongSide, PAWN))) sf = 36; else if (pos.count() == 1) sf = 37 + 3 * (pos.count(WHITE) == 1 ? pos.count(BLACK) + pos.count(BLACK) diff --git a/src/search.cpp b/src/search.cpp index 1610c206dd7..720a9100932 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -263,10 +263,10 @@ void MainThread::search() { Thread* bestThread = this; - if (int(Options["MultiPV"]) == 1 && - !Limits.depth && - !(Skill(Options["Skill Level"]).enabled() || int(Options["UCI_LimitStrength"])) && - rootMoves[0].pv[0] != MOVE_NONE) + if ( int(Options["MultiPV"]) == 1 + && !Limits.depth + && !(Skill(Options["Skill Level"]).enabled() || int(Options["UCI_LimitStrength"])) + && rootMoves[0].pv[0] != MOVE_NONE) bestThread = Threads.get_best_thread(); bestPreviousScore = bestThread->rootMoves[0].score; @@ -670,7 +670,11 @@ namespace { ttPv = PvNode || (ttHit && tte->is_pv()); formerPv = ttPv && !PvNode; - if (ttPv && depth > 12 && ss->ply - 1 < MAX_LPH && !priorCapture && is_ok((ss-1)->currentMove)) + if ( ttPv + && depth > 12 + && ss->ply - 1 < MAX_LPH + && !priorCapture + && is_ok((ss-1)->currentMove)) thisThread->lowPlyHistory[ss->ply - 1][from_to((ss-1)->currentMove)] << stat_bonus(depth - 5); // thisThread->ttHitAverage can be used to approximate the running average of ttHit From 5e91c5dcc8066e9f346a10010ddce70f2d317ef6 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sat, 11 Jul 2020 00:06:55 +0300 Subject: [PATCH 0281/1766] Maximize usage of transposition table in probcut Probcut is a heuristic that wasn't changed a lot in past years, all attempts to change it using information / writing info to transposition table failed. This patch has a number of differences that can be summarized as follows: * For TT write/read we use depth - 3. Because probcut search is depth - 4 but we actually do the move prior to it so effectively we do depth - 3 search; * In any case of depth of eval from transposition table being >= depth - 3 we either produce cutoff or refuse to even do probcut search, this is allowing us to write info of probcut to transposition table because we know that we wouldn't be overwriting some deeper data with our depth - 3 search - this is an important aspect of this patch; * For some not really known reason this patch completely ignores tte->bound() - which was the case for previous patch that made probcut interact with TT, maybe 2) is the reason, although it's unproven. A first version of this patch passed STC and LTC passed STC https://tests.stockfishchess.org/tests/view/5f05908a59f6f03532894613 LLR: 2.95 (-2.94,2.94) {-0.50,1.50} Total: 95776 W: 18300 L: 17973 D: 59503 Ptnml(0-2): 1646, 10944, 22377, 11279, 1642 passed LTC https://tests.stockfishchess.org/tests/view/5f06b54059f6f035328946bb LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 57128 W: 7266 L: 6938 D: 42924 Ptnml(0-2): 372, 5163, 17217, 5389, 423 However, an additional bugfix was needed to avoid checking a condition on ttMove if was not available. This passed non-regression bounds on top of the first version: at STC https://tests.stockfishchess.org/tests/view/5f080e5059f6f03532894766 LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 14096 W: 2800 L: 2628 D: 8668 Ptnml(0-2): 225, 1620, 3238, 1688, 277 at LTC https://tests.stockfishchess.org/tests/view/5f0836a559f6f0353289479c LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 25352 W: 3228 L: 3139 D: 18985 Ptnml(0-2): 175, 2350, 7549, 2415, 187 closes https://github.com/official-stockfish/Stockfish/pull/2804 Bench 4540940 --- src/search.cpp | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 720a9100932..6cf2f90d409 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -596,7 +596,7 @@ namespace { Key posKey; Move ttMove, move, excludedMove, bestMove; Depth extension, newDepth; - Value bestValue, value, ttValue, eval, maxValue; + Value bestValue, value, ttValue, eval, maxValue, probcutBeta; bool ttHit, ttPv, formerPv, givesCheck, improving, didLMR, priorCapture; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture, singularQuietLMR; @@ -871,23 +871,33 @@ namespace { } } + probcutBeta = beta + 176 - 49 * improving; + // Step 10. ProbCut (~10 Elo) // 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 > 4 - && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) + && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY + && !( ttHit + && tte->depth() >= depth - 3 + && ttValue != VALUE_NONE + && ttValue < probcutBeta)) { - Value raisedBeta = beta + 176 - 49 * improving; - assert(raisedBeta < VALUE_INFINITE); - MovePicker mp(pos, ttMove, raisedBeta - ss->staticEval, &captureHistory); + if ( ttHit + && tte->depth() >= depth - 3 + && ttValue != VALUE_NONE + && ttValue >= probcutBeta + && ttMove + && pos.capture_or_promotion(ttMove)) + return probcutBeta; + + assert(probcutBeta < VALUE_INFINITE); + MovePicker mp(pos, ttMove, probcutBeta - ss->staticEval, &captureHistory); int probCutCount = 0; while ( (move = mp.next_move()) != MOVE_NONE - && probCutCount < 2 + 2 * cutNode - && !( move == ttMove - && tte->depth() >= depth - 4 - && ttValue < raisedBeta)) + && probCutCount < 2 + 2 * cutNode) if (move != excludedMove && pos.legal(move)) { assert(pos.capture_or_promotion(move)); @@ -905,16 +915,21 @@ namespace { pos.do_move(move, st); // Perform a preliminary qsearch to verify that the move holds - value = -qsearch(pos, ss+1, -raisedBeta, -raisedBeta+1); + value = -qsearch(pos, ss+1, -probcutBeta, -probcutBeta+1); // If the qsearch held, perform the regular search - if (value >= raisedBeta) - value = -search(pos, ss+1, -raisedBeta, -raisedBeta+1, depth - 4, !cutNode); + if (value >= probcutBeta) + value = -search(pos, ss+1, -probcutBeta, -probcutBeta+1, depth - 4, !cutNode); pos.undo_move(move); - if (value >= raisedBeta) + if (value >= probcutBeta) + { + tte->save(posKey, value_to_tt(value, ss->ply), ttPv, + BOUND_LOWER, + depth - 3, move, ss->staticEval); return value; + } } } From 1f3bd968bb194a1f42af661cca9ec445c13978e8 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Wed, 8 Jul 2020 10:09:32 +0800 Subject: [PATCH 0282/1766] Introduce bad outpost penalty In some French games, Stockfish likes to bring the Knight to a bad outpost spot. This is evident in TCEC S18 Superfinal Game 63, where there is a Knight outpost on the queenside but is actually useless. Stockfish is effectively playing a piece down while holding ground against Leela's break on the kingside. This patch turns the +56 mg bonus for a Knight outpost into a -7 mg penalty if it satisfies the following conditions: * The outpost square is not on the CenterFiles (i.e. not on files C,D,E and F) * The knight is not attacking non pawn enemies. * The side where the outpost is located contains only few enemies, with a particular conditional_more_than_two() implementation Thank you to apospa...@gmail.com for bringing this to our attention and for providing insights. See https://groups.google.com/forum/?fromgroups=#!topic/fishcooking/dEXNzSIBgZU Reference game: https://tcec-chess.com/#div=sf&game=63&season=18 Passed STC: LLR: 2.93 (-2.94,2.94) {-0.50,1.50} Total: 6960 W: 1454 L: 1247 D: 4259 Ptnml(0-2): 115, 739, 1610, 856, 160 https://tests.stockfishchess.org/tests/view/5f08221059f6f0353289477e Passed LTC: LLR: 2.98 (-2.94,2.94) {0.25,1.75} Total: 21440 W: 2767 L: 2543 D: 16130 Ptnml(0-2): 122, 1904, 6462, 2092, 140 https://tests.stockfishchess.org/tests/view/5f0838ed59f6f035328947a2 various related tests show strong test results, but so far no generalizations or simplifications of conditional_more_than_two() are found. See PR for details. closes https://github.com/official-stockfish/Stockfish/pull/2803 Bench: 4366686 --- src/bitboard.h | 7 +++++++ src/evaluate.cpp | 9 ++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/bitboard.h b/src/bitboard.h index afeb40ec146..15ec4153362 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -130,6 +130,13 @@ constexpr bool more_than_one(Bitboard b) { return b & (b - 1); } +/// Counts the occupation of the bitboard depending on the occupation of SQ_A1 +/// as in `b & (1ULL << SQ_A1) ? more_than_two(b) : more_than_one(b)` + +constexpr bool conditional_more_than_two(Bitboard b) { + return b & (b - 1) & (b - 2); +} + constexpr bool opposite_colors(Square s1, Square s2) { return (s1 + rank_of(s1) + s2 + rank_of(s2)) & 1; } diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 6f2dd69ba33..ca6ea5c44fd 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -134,6 +134,7 @@ namespace { }; // Assorted bonuses and penalties + constexpr Score BadOutpost = S( -7, 36); constexpr Score BishopOnKingRing = S( 24, 0); constexpr Score BishopPawns = S( 3, 7); constexpr Score BishopXRayPawns = S( 4, 5); @@ -310,7 +311,13 @@ 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) + if ( Pt == KNIGHT + && bb & s & ~CenterFiles + && !(b & pos.pieces(Them) & ~pos.pieces(PAWN)) + && !conditional_more_than_two( + pos.pieces(Them) & ~pos.pieces(PAWN) & (s & QueenSide ? QueenSide : KingSide))) + score += BadOutpost; + else if (bb & s) score += Outpost[Pt == BISHOP]; else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us)) score += ReachableOutpost; From 6c197c3964ca0c637ff1f646dc7e6653b1bb4b45 Mon Sep 17 00:00:00 2001 From: syzygy1 <3028851+syzygy1@users.noreply.github.com> Date: Sat, 11 Jul 2020 16:25:34 +0200 Subject: [PATCH 0283/1766] Corrects a functional change in a cleanup patch. This corrects a functional change in https://github.com/official-stockfish/Stockfish/commit/ddcbacd04d1c860e808202ce8c1206c8acdca627 changing evaluation of KPPvK. Bench remains unchanged at low depth With this patch, 8/8/5k1p/8/7p/7K/8/8 b - - 1 11 is again correctly evaluated as a draw. closes https://github.com/official-stockfish/Stockfish/pull/2807 Bench: 4366686 --- src/endgame.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/endgame.cpp b/src/endgame.cpp index 40f49dce501..a8ceb648776 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -589,8 +589,8 @@ ScaleFactor Endgame::operator()(const Position& pos) const { Bitboard strongPawns = pos.pieces(strongSide, PAWN); // If all pawns are ahead of the king on a single rook file, it's a draw. - if (!((strongPawns & ~FileABB) || (strongPawns & ~FileHBB)) && - !(strongPawns & ~passed_pawn_span(weakSide, weakKing))) + if ( !(strongPawns & ~(FileABB | FileHBB)) + && !(strongPawns & ~passed_pawn_span(weakSide, weakKing))) return SCALE_FACTOR_DRAW; return SCALE_FACTOR_NONE; From c3092c54bc6fb837137365fc60eb57bd188deaca Mon Sep 17 00:00:00 2001 From: mstembera Date: Sun, 12 Jul 2020 13:58:00 -0700 Subject: [PATCH 0284/1766] Multiple lazy stages. An extension of the lazy eval idea: when the score is sufficiently large we now skip more granular parts of the eval. Inspired by an original patch by Moez Jellouli https://tests.stockfishchess.org/tests/view/5f03b2a159f6f03532894529 Credit to him! STC https://tests.stockfishchess.org/tests/view/5f0a862c59f6f03532894924 LLR: 2.95 (-2.94,2.94) {-0.50,1.50} Total: 13504 W: 2684 L: 2472 D: 8348 Ptnml(0-2): 229, 1496, 3111, 1666, 250 LTC https://tests.stockfishchess.org/tests/view/5f0ac0e159f6f0353289495b LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 31312 W: 3926 L: 3677 D: 23709 Ptnml(0-2): 185, 2773, 9509, 2986, 203 closes https://github.com/official-stockfish/Stockfish/pull/2814 bench: 4541608 --- src/evaluate.cpp | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index ca6ea5c44fd..dbb725d4dbf 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -74,7 +74,8 @@ using namespace Trace; namespace { // Threshold for lazy and space evaluation - constexpr Value LazyThreshold = Value(1400); + constexpr Value LazyThreshold1 = Value(1400); + constexpr Value LazyThreshold2 = Value(1300); constexpr Value SpaceThreshold = Value(12222); // KingAttackWeights[PieceType] contains king attack weights by piece type @@ -786,7 +787,7 @@ namespace { && pos.non_pawn_material(BLACK) == RookValueMg && pos.count(strongSide) - pos.count(~strongSide) <= 1 && bool(KingSide & pos.pieces(strongSide, PAWN)) != bool(QueenSide & pos.pieces(strongSide, PAWN)) - && (attackedBy[~strongSide][KING] & pos.pieces(~strongSide, PAWN))) + && (attacks_bb(pos.square(~strongSide)) & pos.pieces(~strongSide, PAWN))) sf = 36; else if (pos.count() == 1) sf = 37 + 3 * (pos.count(WHITE) == 1 ? pos.count(BLACK) + pos.count(BLACK) @@ -837,9 +838,12 @@ namespace { score += pe->pawn_score(WHITE) - pe->pawn_score(BLACK); // Early exit if score is high - Value v = (mg_value(score) + eg_value(score)) / 2; - if (abs(v) > LazyThreshold + pos.non_pawn_material() / 64) - return pos.side_to_move() == WHITE ? v : -v; + auto lazy_skip = [&](Value lazyThreshold) { + return abs(mg_value(score) + eg_value(score)) / 2 > lazyThreshold + pos.non_pawn_material() / 64; + }; + + if (lazy_skip(LazyThreshold1)) + goto make_v; // Main evaluation begins here initialize(); @@ -856,12 +860,17 @@ namespace { // More complex interactions that require fully populated attack bitboards score += king< WHITE>() - king< BLACK>() - + threats() - threats() - + passed< WHITE>() - passed< BLACK>() + + passed< WHITE>() - passed< BLACK>(); + + if (lazy_skip(LazyThreshold2)) + goto make_v; + + score += threats() - threats() + space< WHITE>() - space< BLACK>(); +make_v: // Derive single value from mg and eg parts of score - v = winnable(score); + Value v = winnable(score); // In case of tracing add all remaining individual evaluation terms if (T) From d89730d5c8dcf10eb9e1d91a81f903d9fc3c949a Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Mon, 13 Jul 2020 20:30:58 +0300 Subject: [PATCH 0285/1766] Do not overwrite valuable TT data after probcut. This patch allows an engine to write probcut data only in case the probcut search depth is greater than transposition table depth. passed STC https://tests.stockfishchess.org/tests/view/5f0b52e959f6f035328949a6 LLR: 2.97 (-2.94,2.94) {-0.50,1.50} Total: 52544 W: 10145 L: 9880 D: 32519 Ptnml(0-2): 853, 6097, 12121, 6334, 867 passed LTC https://tests.stockfishchess.org/tests/view/5f0bd94c59f6f035328949f3 LLR: 2.93 (-2.94,2.94) {0.25,1.75} Total: 49576 W: 6164 L: 5863 D: 37549 Ptnml(0-2): 297, 4371, 15218, 4538, 364 closes https://github.com/official-stockfish/Stockfish/pull/2815 bench 4578298 --- src/search.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 6cf2f90d409..17ccab9251a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -925,9 +925,12 @@ namespace { if (value >= probcutBeta) { - tte->save(posKey, value_to_tt(value, ss->ply), ttPv, - BOUND_LOWER, - depth - 3, move, ss->staticEval); + if ( !(ttHit + && tte->depth() >= depth - 3 + && ttValue != VALUE_NONE)) + tte->save(posKey, value_to_tt(value, ss->ply), ttPv, + BOUND_LOWER, + depth - 3, move, ss->staticEval); return value; } } From f0abde241d39ee4507778bf41b392492c5391652 Mon Sep 17 00:00:00 2001 From: protonspring Date: Sat, 25 Jul 2020 07:32:19 -0600 Subject: [PATCH 0286/1766] Remove conditional_more_than_two(). This is a functional simplification that removes the conditional_more_than_two() function, which was quite strange and kooky. Note the very minor change to the bench value. See this thread for relevant comments on the passing branch: protonspring/Stockfish@d89730d...ff35b50 STC LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 59760 W: 11411 L: 11311 D: 37038 Ptnml(0-2): 992, 6863, 14044, 7015, 966 https://tests.stockfishchess.org/tests/view/5f179988c09435d870cb9b9a LTC LLR: 2.93 (-2.94,2.94) {-1.50,0.50} Total: 45208 W: 5553 L: 5497 D: 34158 Ptnml(0-2): 315, 4081, 13761, 4127, 320 https://tests.stockfishchess.org/tests/view/5f184847c09435d870cb9bee closes https://github.com/official-stockfish/Stockfish/pull/2826 Bench: 4578290 --- src/bitboard.h | 6 ------ src/evaluate.cpp | 12 +++++++----- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/bitboard.h b/src/bitboard.h index 15ec4153362..8c95de8c66c 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -130,12 +130,6 @@ constexpr bool more_than_one(Bitboard b) { return b & (b - 1); } -/// Counts the occupation of the bitboard depending on the occupation of SQ_A1 -/// as in `b & (1ULL << SQ_A1) ? more_than_two(b) : more_than_one(b)` - -constexpr bool conditional_more_than_two(Bitboard b) { - return b & (b - 1) & (b - 2); -} constexpr bool opposite_colors(Square s1, Square s2) { return (s1 + rank_of(s1) + s2 + rank_of(s2)) & 1; diff --git a/src/evaluate.cpp b/src/evaluate.cpp index dbb725d4dbf..d16648a84ea 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -310,13 +310,15 @@ namespace { if (Pt == BISHOP || Pt == KNIGHT) { - // Bonus if piece is on an outpost square or can reach one + // Bonus if the piece is on an outpost square or can reach one + // Reduced bonus for knights (BadOutpost) if few relevant targets bb = OutpostRanks & attackedBy[Us][PAWN] & ~pe->pawn_attacks_span(Them); + Bitboard targets = pos.pieces(Them) & ~pos.pieces(PAWN); + if ( Pt == KNIGHT - && bb & s & ~CenterFiles - && !(b & pos.pieces(Them) & ~pos.pieces(PAWN)) - && !conditional_more_than_two( - pos.pieces(Them) & ~pos.pieces(PAWN) & (s & QueenSide ? QueenSide : KingSide))) + && bb & s & ~CenterFiles // on a side outpost + && !(b & targets) // no relevant attacks + && (!more_than_one(targets & (s & QueenSide ? QueenSide : KingSide)))) score += BadOutpost; else if (bb & s) score += Outpost[Pt == BISHOP]; From 62d3106caa2f5acf5ba32500cc19912b8f10612c Mon Sep 17 00:00:00 2001 From: UnaiCorzo Date: Sat, 25 Jul 2020 22:30:05 +0200 Subject: [PATCH 0287/1766] Remove late irreversible move extension We simplify away the late irreversible move extension, which does not seem to be necessary in the current master. STC LLR: 2.93 (-2.94,2.94) {-1.50,0.50} Total: 38584 W: 7464 L: 7342 D: 23778 Ptnml(0-2): 581, 4328, 9365, 4424, 594 https://tests.stockfishchess.org/tests/view/5f1c9669c09435d870cb9de9 LTC LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 27840 W: 3417 L: 3353 D: 21070 Ptnml(0-2): 120, 2315, 8994, 2363, 128 https://tests.stockfishchess.org/tests/view/5f1d2e22c09435d870cb9e21 closes https://github.com/official-stockfish/Stockfish/pull/2836 bench: 4829420 --- src/search.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 17ccab9251a..6ec4d803223 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1067,7 +1067,7 @@ namespace { // search of (alpha-s, beta-s), and just one fails high on (alpha, beta), // 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. + // result is lower than ttValue minus a margin, then we will extend the ttMove. if ( depth >= 6 && move == ttMove && !rootNode @@ -1131,12 +1131,6 @@ namespace { if (type_of(move) == CASTLING) extension = 1; - // Late irreversible move extension - if ( move == ttMove - && pos.rule50_count() > 80 - && (captureOrPromotion || type_of(movedPiece) == PAWN)) - extension = 2; - // Add extension to new depth newDepth += extension; From 33f3cfae0093b934563e1eca78486261f18e4650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicolet?= Date: Tue, 28 Jul 2020 10:08:09 +0200 Subject: [PATCH 0288/1766] Improve handling of queen imbalance We double the bonus for potential threats by minors and rooks against our queen, in case of "queen vs pieces imbalance". Hopefully this will improve a little bit the evaluation for this well-known Stockfish weakness. passed STC: LLR: 2.93 (-2.94,2.94) {-0.50,1.50} Total: 72976 W: 14003 L: 13710 D: 45263 Ptnml(0-2): 1218, 8370, 17094, 8513, 1293 https://tests.stockfishchess.org/tests/view/5efa50eb020eec13834a977d passed LTC: LLR: 2.93 (-2.94,2.94) {0.25,1.75} Total: 22232 W: 2779 L: 2560 D: 16893 Ptnml(0-2): 129, 1885, 6896, 2050, 156 https://tests.stockfishchess.org/tests/view/5f1fdd2dc09435d870cb9f13 closes https://github.com/official-stockfish/Stockfish/pull/2864 Bench: 4367349 --- src/evaluate.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d16648a84ea..b34d82f66d4 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -579,17 +579,21 @@ namespace { // Bonus for threats on the next moves against enemy queen if (pos.count(Them) == 1) { + bool queenImbalance = pos.count() == 1; + Square s = pos.square(Them); - safe = mobilityArea[Us] & ~stronglyProtected; + safe = mobilityArea[Us] + & ~pos.pieces(Us, PAWN) + & ~stronglyProtected; b = attackedBy[Us][KNIGHT] & attacks_bb(s); - score += KnightOnQueen * popcount(b & safe); + score += KnightOnQueen * popcount(b & safe) * (1 + queenImbalance); b = (attackedBy[Us][BISHOP] & attacks_bb(s, pos.pieces())) | (attackedBy[Us][ROOK ] & attacks_bb(s, pos.pieces())); - score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]); + score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]) * (1 + queenImbalance); } if (T) From 9587eeeb5ed29f834d4f956b92e0e732877c47a7 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Thu, 30 Jul 2020 18:56:11 +0200 Subject: [PATCH 0289/1766] Tweak cutnode reduction Less reduction for second move at non-check CUT node with depth <= 10. STC: LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 38680 W: 7490 L: 7245 D: 23945 Ptnml(0-2): 643, 4441, 8967, 4606, 683 https://tests.stockfishchess.org/tests/view/5f21e1782f7e63962b99f451 LTC: LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 71976 W: 9003 L: 8636 D: 54337 Ptnml(0-2): 440, 6414, 21972, 6663, 499 https://tests.stockfishchess.org/tests/view/5f2245762f7e63962b99f4bd closes https://github.com/official-stockfish/Stockfish/pull/2868 Bench: 4746616 --- src/search.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 6ec4d803223..91ac60ad4b2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1167,6 +1167,13 @@ namespace { { Depth r = reduction(improving, depth, moveCount); + // Decrease reduction at non-check cut nodes for second move at low depths + if ( cutNode + && depth <= 10 + && moveCount <= 2 + && !ss->inCheck) + r--; + // Decrease reduction if the ttHit running average is large if (thisThread->ttHitAverage > 473 * TtHitAverageResolution * TtHitAverageWindow / 1024) r--; From 84f3e867903f62480c33243dd0ecbffd342796fc Mon Sep 17 00:00:00 2001 From: nodchip Date: Wed, 5 Aug 2020 17:11:15 +0200 Subject: [PATCH 0290/1766] Add NNUE evaluation This patch ports the efficiently updatable neural network (NNUE) evaluation to Stockfish. Both the NNUE and the classical evaluations are available, and can be used to assign a value to a position that is later used in alpha-beta (PVS) search to find the best move. The classical evaluation computes this value as a function of various chess concepts, handcrafted by experts, tested and tuned using fishtest. The NNUE evaluation computes this value with a neural network based on basic inputs. The network is optimized and trained on the evalutions of millions of positions at moderate search depth. The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward. It can be evaluated efficiently on CPUs, and exploits the fact that only parts of the neural network need to be updated after a typical chess move. [The nodchip repository](https://github.com/nodchip/Stockfish) provides additional tools to train and develop the NNUE networks. This patch is the result of contributions of various authors, from various communities, including: nodchip, ynasu87, yaneurao (initial port and NNUE authors), domschl, FireFather, rqs, xXH4CKST3RXx, tttak, zz4032, joergoster, mstembera, nguyenpham, erbsenzaehler, dorzechowski, and vondele. This new evaluation needed various changes to fishtest and the corresponding infrastructure, for which tomtor, ppigazzini, noobpwnftw, daylen, and vondele are gratefully acknowledged. The first networks have been provided by gekkehenker and sergiovieri, with the latter net (nn-97f742aaefcd.nnue) being the current default. The evaluation function can be selected at run time with the `Use NNUE` (true/false) UCI option, provided the `EvalFile` option points the the network file (depending on the GUI, with full path). The performance of the NNUE evaluation relative to the classical evaluation depends somewhat on the hardware, and is expected to improve quickly, but is currently on > 80 Elo on fishtest: 60000 @ 10+0.1 th 1 https://tests.stockfishchess.org/tests/view/5f28fe6ea5abc164f05e4c4c ELO: 92.77 +-2.1 (95%) LOS: 100.0% Total: 60000 W: 24193 L: 8543 D: 27264 Ptnml(0-2): 609, 3850, 9708, 10948, 4885 40000 @ 20+0.2 th 8 https://tests.stockfishchess.org/tests/view/5f290229a5abc164f05e4c58 ELO: 89.47 +-2.0 (95%) LOS: 100.0% Total: 40000 W: 12756 L: 2677 D: 24567 Ptnml(0-2): 74, 1583, 8550, 7776, 2017 At the same time, the impact on the classical evaluation remains minimal, causing no significant regression: sprt @ 10+0.1 th 1 https://tests.stockfishchess.org/tests/view/5f2906a2a5abc164f05e4c5b LLR: 2.94 (-2.94,2.94) {-6.00,-4.00} Total: 34936 W: 6502 L: 6825 D: 21609 Ptnml(0-2): 571, 4082, 8434, 3861, 520 sprt @ 60+0.6 th 1 https://tests.stockfishchess.org/tests/view/5f2906cfa5abc164f05e4c5d LLR: 2.93 (-2.94,2.94) {-6.00,-4.00} Total: 10088 W: 1232 L: 1265 D: 7591 Ptnml(0-2): 49, 914, 3170, 843, 68 The needed networks can be found at https://tests.stockfishchess.org/nns It is recommended to use the default one as indicated by the `EvalFile` UCI option. Guidelines for testing new nets can be found at https://github.com/glinscott/fishtest/wiki/Creating-my-first-test#nnue-net-tests Integration has been discussed in various issues: https://github.com/official-stockfish/Stockfish/issues/2823 https://github.com/official-stockfish/Stockfish/issues/2728 The integration branch will be closed after the merge: https://github.com/official-stockfish/Stockfish/pull/2825 https://github.com/official-stockfish/Stockfish/tree/nnue-player-wip closes https://github.com/official-stockfish/Stockfish/pull/2912 This will be an exciting time for computer chess, looking forward to seeing the evolution of this approach. Bench: 4746616 --- .travis.yml | 31 +- AUTHORS | 17 +- Readme.md => README.md | 125 ++++--- appveyor.yml | 17 +- src/Makefile | 227 +++++++++++-- src/benchmark.cpp | 4 +- src/bitbase.cpp | 4 +- src/bitboard.cpp | 4 +- src/bitboard.h | 4 +- src/endgame.cpp | 4 +- src/endgame.h | 4 +- src/evaluate.cpp | 110 ++++-- src/evaluate.h | 24 +- src/main.cpp | 5 +- src/material.cpp | 4 +- src/material.h | 4 +- src/misc.cpp | 62 +++- src/misc.h | 6 +- src/movegen.cpp | 4 +- src/movegen.h | 4 +- src/movepick.cpp | 4 +- src/movepick.h | 4 +- src/nnue/architectures/halfkp_256x2-32-32.h | 54 +++ src/nnue/evaluate_nnue.cpp | 178 ++++++++++ src/nnue/evaluate_nnue.h | 48 +++ src/nnue/features/feature_set.h | 135 ++++++++ src/nnue/features/features_common.h | 45 +++ src/nnue/features/half_kp.cpp | 92 +++++ src/nnue/features/half_kp.h | 67 ++++ src/nnue/features/index_list.h | 64 ++++ src/nnue/layers/affine_transform.h | 215 ++++++++++++ src/nnue/layers/clipped_relu.h | 186 ++++++++++ src/nnue/layers/input_slice.h | 68 ++++ src/nnue/nnue_accumulator.h | 39 +++ src/nnue/nnue_architecture.h | 38 +++ src/nnue/nnue_common.h | 77 +++++ src/nnue/nnue_feature_transformer.h | 355 ++++++++++++++++++++ src/pawns.cpp | 4 +- src/pawns.h | 4 +- src/position.cpp | 108 +++++- src/position.h | 42 ++- src/psqt.cpp | 4 +- src/search.cpp | 6 +- src/search.h | 4 +- src/syzygy/tbprobe.cpp | 3 +- src/syzygy/tbprobe.h | 3 +- src/thread.cpp | 4 +- src/thread.h | 4 +- src/thread_win32_osx.h | 4 +- src/timeman.cpp | 4 +- src/timeman.h | 4 +- src/tt.cpp | 4 +- src/tt.h | 4 +- src/tune.cpp | 4 +- src/tune.h | 4 +- src/types.h | 129 ++++++- src/uci.cpp | 22 +- src/uci.h | 4 +- src/ucioption.cpp | 9 +- 59 files changed, 2470 insertions(+), 241 deletions(-) rename Readme.md => README.md (79%) create mode 100644 src/nnue/architectures/halfkp_256x2-32-32.h create mode 100644 src/nnue/evaluate_nnue.cpp create mode 100644 src/nnue/evaluate_nnue.h create mode 100644 src/nnue/features/feature_set.h create mode 100644 src/nnue/features/features_common.h create mode 100644 src/nnue/features/half_kp.cpp create mode 100644 src/nnue/features/half_kp.h create mode 100644 src/nnue/features/index_list.h create mode 100644 src/nnue/layers/affine_transform.h create mode 100644 src/nnue/layers/clipped_relu.h create mode 100644 src/nnue/layers/input_slice.h create mode 100644 src/nnue/nnue_accumulator.h create mode 100644 src/nnue/nnue_architecture.h create mode 100644 src/nnue/nnue_common.h create mode 100644 src/nnue/nnue_feature_transformer.h diff --git a/.travis.yml b/.travis.yml index e2ae61bef9a..d563a1e1992 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: cpp -dist: xenial +dist: bionic matrix: include: @@ -7,7 +7,6 @@ matrix: compiler: gcc addons: apt: - sources: ['ubuntu-toolchain-r-test'] packages: ['g++-8', 'g++-8-multilib', 'g++-multilib', 'valgrind', 'expect', 'curl'] env: - COMPILER=g++-8 @@ -17,23 +16,23 @@ matrix: compiler: clang addons: apt: - sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-xenial-6.0'] - packages: ['clang-6.0', 'llvm-6.0-dev', 'g++-multilib', 'valgrind', 'expect', 'curl'] + packages: ['clang-10', 'llvm-10-dev', 'g++-multilib', 'valgrind', 'expect', 'curl'] env: - - COMPILER=clang++-6.0 + - COMPILER=clang++-10 - COMP=clang - - LDFLAGS=-fuse-ld=lld - os: osx + osx_image: xcode12 compiler: gcc env: - COMPILER=g++ - COMP=gcc - os: osx + osx_image: xcode12 compiler: clang env: - - COMPILER=clang++ V='Apple LLVM 9.4.1' # Apple LLVM version 9.1.0 (clang-902.0.39.2) + - COMPILER=clang++ - COMP=clang branches: @@ -48,26 +47,34 @@ script: - git log HEAD | grep "\b[Bb]ench[ :]\+[0-9]\{7\}" | head -n 1 | sed "s/[^0-9]*\([0-9]*\).*/\1/g" > git_sig - export benchref=$(cat git_sig) - echo "Reference bench:" $benchref + + # + # Compiler version string + - $COMPILER -v + # # Verify bench number against various builds - export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" - make clean && make -j2 ARCH=x86-64 optimize=no debug=yes build && ../tests/signature.sh $benchref - - 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 + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32 optimize=no debug=yes build && ../tests/signature.sh $benchref; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32 build && ../tests/signature.sh $benchref; fi # # Check perft and reproducible search + - export CXXFLAGS="-Werror" + - make clean && make -j2 ARCH=x86-64 build - ../tests/perft.sh - ../tests/reprosearch.sh + # # Valgrind # - export CXXFLAGS="-O1 -fno-inline" - if [ -x "$(command -v valgrind )" ]; then make clean && make -j2 ARCH=x86-64 debug=yes optimize=no build > /dev/null && ../tests/instrumented.sh --valgrind; fi - if [ -x "$(command -v valgrind )" ]; then ../tests/instrumented.sh --valgrind-thread; fi + # # Sanitizer # - # Use g++-8 as a proxy for having sanitizers, might need revision as they become available for more recent versions of clang/gcc - - if [[ "$COMPILER" == "g++-8" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=undefined optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-undefined; fi - - if [[ "$COMPILER" == "g++-8" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=thread optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-thread; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=undefined optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-undefined; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=thread optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-thread; fi diff --git a/AUTHORS b/AUTHORS index f08d71d301e..2e080e61dc1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,10 +1,17 @@ -# List of authors for Stockfish, as of March 30, 2020 +# List of authors for Stockfish, as of August 4, 2020 +# Founders of the Stockfish project and fishtest infrastructure Tord Romstad (romstad) Marco Costalba (mcostalba) Joona Kiiski (zamar) Gary Linscott (glinscott) +# Authors and inventors of NNUE, training, NNUE port +Yu Nasu (ynasu87) +Motohiro Isozaki (yaneurao) +Hisayori Noda (nodchip) + +# all other authors of the code in alphabetical order Aditya (absimaldata) Adrian Petrescu (apetresc) Ajith Chandy Jose (ajithcj) @@ -36,6 +43,7 @@ Dariusz Orzechowski David Zar Daylen Yang (daylen) DiscanX +Dominik Schlösser (domschl) double-beep Eduardo Cáceres (eduherminio) Eelco de Groot (KingDefender) @@ -115,7 +123,8 @@ Nick Pelling (nickpelling) Nicklas Persson (NicklasPersson) Niklas Fiekas (niklasf) Nikolay Kostov (NikolayIT) -Nguyen Pham +Nguyen Pham (nguyenpham) +Norman Schmidt (FireFather) Ondrej Mosnáček (WOnder93) Oskar Werkelin Ahlin Pablo Vazquez @@ -135,6 +144,7 @@ Richard Lloyd Rodrigo Exterckötter Tjäder Ron Britvich (Britvich) Ronald de Man (syzygy1, syzygy) +rqs Ryan Schmitt Ryan Takker Sami Kiminki (skiminki) @@ -143,6 +153,7 @@ Sergei Antonov (saproj) Sergei Ivanov (svivanov72) sf-x Shane Booth (shane31) +Shawn Varghese (xXH4CKST3RXx) Stefan Geschwentner (locutus2) Stefano Cardanobile (Stefano80) Steinar Gunderson (sesse) @@ -155,9 +166,11 @@ Tom Vijlbrief (tomtor) Tomasz Sobczyk (Sopel97) Torsten Franz (torfranz, tfranzer) Tracey Emery (basepr1me) +tttak Unai Corzo (unaiic) Uri Blass (uriblass) Vince Negri (cuddlestmonkey) +zz4032 # Additionally, we acknowledge the authors and maintainers of fishtest, diff --git a/Readme.md b/README.md similarity index 79% rename from Readme.md rename to README.md index 823518d176a..f71a8b34722 100644 --- a/Readme.md +++ b/README.md @@ -4,7 +4,13 @@ [![Build Status](https://ci.appveyor.com/api/projects/status/github/official-stockfish/Stockfish?branch=master&svg=true)](https://ci.appveyor.com/project/mcostalba/stockfish/branch/master) [Stockfish](https://stockfishchess.org) is a free, powerful UCI chess engine -derived from Glaurung 2.1. It is not a complete chess program and requires a +derived from Glaurung 2.1. It features two evaluation functions, the classical +evaluation based on handcrafted terms, and the NNUE evaluation based on +efficiently updateable neural networks. The classical evaluation runs efficiently +on most 64bit CPU architectures, while the NNUE evaluation benefits strongly from the +vector intrinsics available on modern CPUs (avx2 or similar). + +Stockfish is not a complete chess program and requires a UCI-compatible GUI (e.g. XBoard with PolyGlot, Scid, Cute Chess, eboard, Arena, Sigma Chess, Shredder, Chess Partner or Fritz) in order to be used comfortably. Read the documentation for your GUI of choice for information about how to use @@ -22,20 +28,19 @@ This distribution of Stockfish consists of the following files: * src, a subdirectory containing the full source code, including a Makefile that can be used to compile Stockfish on Unix-like systems. +To use the NNUE evaluation an additional data file with neural network parameters +needs to be downloaded. The filename for the default set can be found as the default +value of the `EvalFile` UCI option, with the format +`nn-[SHA256 first 12 digits].nnue` (e.g. nn-c157e0a5755b.nnue). This file can be downloaded from +``` +https://tests.stockfishchess.org/api/nn/[filename] +``` +replacing `[filename]` as needed. -## UCI parameters - -Currently, Stockfish has the following UCI options: - - * #### Debug Log File - Write all communication to and from the engine into a text file. - * #### Contempt - A positive value for contempt favors middle game positions and avoids draws. +## UCI options - * #### Analysis Contempt - By default, contempt is set to prefer the side to move. Set this option to "White" - or "Black" to analyse with contempt for that side, or "Off" to disable contempt. +Currently, Stockfish has the following UCI options: * #### Threads The number of CPU threads used for searching a position. For best performance, set @@ -44,9 +49,6 @@ Currently, Stockfish has the following UCI options: * #### Hash The size of the hash table in MB. It is recommended to set Hash after setting Threads. - * #### Clear Hash - Clear the hash table. - * #### Ponder Let Stockfish ponder its next move while the opponent is thinking. @@ -54,40 +56,44 @@ Currently, Stockfish has the following UCI options: Output the N best lines (principal variations, PVs) when searching. Leave at 1 for best performance. - * #### Skill Level - 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. + * #### Use NNUE + Toggle between the NNUE and classical evaluation functions. If set to "true", + the network parameters must be availabe to load from file (see also EvalFile). - * #### UCI_LimitStrength - Enable weaker play aiming for an Elo rating as set by UCI_Elo. This option overrides Skill Level. + * #### EvalFile + The name of the file of the NNUE evaluation parameters. Depending on the GUI the + filename should include the full path to the folder/directory that contains the file. - * #### 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. + * #### Contempt + A positive value for contempt favors middle game positions and avoids draws, + effective for the classical evaluation only. + + * #### Analysis Contempt + By default, contempt is set to prefer the side to move. Set this option to "White" + or "Black" to analyse with contempt for that side, or "Off" to disable contempt. + + * #### UCI_AnalyseMode + An option handled by your GUI. + + * #### UCI_Chess960 + An option handled by your GUI. If true, Stockfish will play Chess960. * #### UCI_ShowWDL If enabled, show approximate WDL statistics as part of the engine output. These WDL numbers model expected game outcomes for a given evaluation and game ply for engine self-play at fishtest LTC conditions (60+0.6s per game). - * #### Move Overhead - Assume a time delay of x ms due to network and GUI overheads. This is useful to - avoid losses on time in those cases. - - * #### Slow Mover - Lower values will make Stockfish take less time in games, higher values will - make it think longer. - - * #### nodestime - Tells the engine to use nodes searched instead of wall time to account for - elapsed time. Useful for engine testing. + * #### UCI_LimitStrength + Enable weaker play aiming for an Elo rating as set by UCI_Elo. This option overrides Skill Level. - * #### UCI_Chess960 - An option handled by your GUI. If true, Stockfish will play Chess960. + * #### 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. - * #### UCI_AnalyseMode - An option handled by your GUI. + * #### Skill Level + 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. * #### SyzygyPath Path to the folders/directories storing the Syzygy tablebase files. Multiple @@ -114,6 +120,47 @@ Currently, Stockfish has the following UCI options: Limit Syzygy tablebase probing to positions with at most this many pieces left (including kings and pawns). + * #### Move Overhead + Assume a time delay of x ms due to network and GUI overheads. This is useful to + avoid losses on time in those cases. + + * #### Slow Mover + Lower values will make Stockfish take less time in games, higher values will + make it think longer. + + * #### nodestime + Tells the engine to use nodes searched instead of wall time to account for + elapsed time. Useful for engine testing. + + * #### Clear Hash + Clear the hash table. + + * #### Debug Log File + Write all communication to and from the engine into a text file. + +## classical and NNUE evaluation + +Both approaches assign a value to a position that is used in alpha-beta (PVS) search +to find the best move. The classical evaluation computes this value as a function +of various chess concepts, handcrafted by experts, tested and tuned using fishtest. +The NNUE evaluation computes this value with a neural network based on basic +inputs (e.g. piece positions only). The network is optimized and trained +on the evalutions of millions of positions at moderate search depth. + +The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward. +It can be evaluated efficiently on CPUs, and exploits the fact that only parts +of the neural network need to be updated after a typical chess move. +[The nodchip repository](https://github.com/nodchip/Stockfish) provides additional +tools to train and develop the NNUE networks. + +On CPUs supporting modern vector instructions (avx2 and similar), the NNUE evaluation +results in stronger playing strength, even if the nodes per second computed by the engine +is somewhat lower (roughly 60% of nps is typical). + +Note that the NNUE evaluation depends on the Stockfish binary and the network parameter +file (see EvalFile). Not every parameter file is compatible with a given Stockfish binary. +The default value of the EvalFile UCI option is the name of a network that is guaranteed +to be compatible with that binary. ## What to expect from Syzygybases? diff --git a/appveyor.yml b/appveyor.yml index 21f3bbe326b..d356ba2f101 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,10 +4,9 @@ clone_depth: 50 branches: only: - master - - appveyor # Operating system (build VM template) -os: Visual Studio 2017 +os: Visual Studio 2019 # Build platform, i.e. x86, x64, AnyCPU. This setting is optional. platform: @@ -36,8 +35,11 @@ before_build: $src = $src.Replace("\", "/") # Build CMakeLists.txt - $t = 'cmake_minimum_required(VERSION 3.8)', + $t = 'cmake_minimum_required(VERSION 3.17)', 'project(Stockfish)', + 'set(CMAKE_CXX_STANDARD 17)', + 'set(CMAKE_CXX_STANDARD_REQUIRED ON)', + 'set (CMAKE_CXX_EXTENSIONS OFF)', 'set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/src)', 'set(source_files', $src, ')', 'add_executable(stockfish ${source_files})' @@ -51,10 +53,11 @@ before_build: $b = git log HEAD | sls "\b[Bb]ench[ :]+[0-9]{7}" | select -first 1 $bench = $b -match '\D+(\d+)' | % { $matches[1] } Write-Host "Reference bench:" $bench - $g = "Visual Studio 15 2017" - If (${env:PLATFORM} -eq 'x64') { $g = $g + ' Win64' } - cmake -G "${g}" . - Write-Host "Generated files for: " $g + $g = "Visual Studio 16 2019" + If (${env:PLATFORM} -eq 'x64') { $a = "x64" } + If (${env:PLATFORM} -eq 'x86') { $a = "Win32" } + cmake -G "${g}" -A ${a} . + Write-Host "Generated files for: " $g $a build_script: - cmake --build . --config %CONFIGURATION% -- /verbosity:minimal diff --git a/src/Makefile b/src/Makefile index c3660a20fa6..4741e722bfd 100644 --- a/src/Makefile +++ b/src/Makefile @@ -38,11 +38,12 @@ PGOBENCH = ./$(EXE) bench ### Source and object files SRCS = benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.cpp main.cpp \ material.cpp misc.cpp movegen.cpp movepick.cpp pawns.cpp position.cpp psqt.cpp \ - search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp + search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \ + nnue/evaluate_nnue.cpp nnue/features/half_kp.cpp OBJS = $(notdir $(SRCS:.cpp=.o)) -VPATH = syzygy +VPATH = syzygy:nnue:nnue/features ### Establish the operating system name KERNEL = $(shell uname -s) @@ -67,7 +68,14 @@ endif # prefetch = yes/no --- -DUSE_PREFETCH --- Use prefetch asm-instruction # popcnt = yes/no --- -DUSE_POPCNT --- Use popcnt asm-instruction # sse = yes/no --- -msse --- Use Intel Streaming SIMD Extensions +# sse3 = yes/no --- -msse3 --- Use Intel Streaming SIMD Extensions 3 +# ssse3 = yes/no --- -mssse3 --- Use Intel Supplemental Streaming SIMD Extensions 3 +# sse41 = yes/no --- -msse4.1 --- Use Intel Streaming SIMD Extensions 4.1 +# sse42 = yes/no --- -msse4.2 --- Use Intel Streaming SIMD Extensions 4.2 +# avx2 = yes/no --- -mavx2 --- Use Intel Advanced Vector Extensions 2 # pext = yes/no --- -DUSE_PEXT --- Use pext x86_64 asm-instruction +# avx512 = yes/no --- -mavx512bw --- Use Intel Advanced Vector Extensions 512 +# neon = yes/no --- -DUSE_NEON --- Use ARM SIMD architecture # # Note that Makefile is space sensitive, so when adding new architectures # or modifying existing flags, you have to make sure there are no extra spaces @@ -81,7 +89,15 @@ bits = 64 prefetch = no popcnt = no sse = no +sse3 = no +ssse3 = no +sse41 = no +sse42 = no +avx2 = no pext = no +avx512 = no +neon = no +ARCH = x86-64-modern ### 2.2 Architecture specific ifeq ($(ARCH),general-32) @@ -111,11 +127,70 @@ ifeq ($(ARCH),x86-64) sse = yes endif +ifeq ($(ARCH),x86-64-sse3) + arch = x86_64 + prefetch = yes + sse = yes + sse3 = yes +endif + +ifeq ($(ARCH),x86-64-sse3-popcnt) + arch = x86_64 + prefetch = yes + sse = yes + sse3 = yes + popcnt = yes +endif + +ifeq ($(ARCH),x86-64-ssse3) + arch = x86_64 + prefetch = yes + sse = yes + sse3 = yes + ssse3 = yes +endif + +ifeq ($(ARCH),x86-64-sse41) + arch = x86_64 + prefetch = yes + popcnt = yes + sse = yes + sse3 = yes + ssse3 = yes + sse41 = yes +endif + ifeq ($(ARCH),x86-64-modern) arch = x86_64 prefetch = yes popcnt = yes sse = yes + sse3 = yes + ssse3 = yes + sse41 = yes +endif + +ifeq ($(ARCH),x86-64-sse42) + arch = x86_64 + prefetch = yes + popcnt = yes + sse = yes + sse3 = yes + ssse3 = yes + sse41 = yes + sse42 = yes +endif + +ifeq ($(ARCH),x86-64-avx2) + arch = x86_64 + prefetch = yes + popcnt = yes + sse = yes + sse3 = yes + ssse3 = yes + sse41 = yes + sse42 = yes + avx2 = yes endif ifeq ($(ARCH),x86-64-bmi2) @@ -123,9 +198,28 @@ ifeq ($(ARCH),x86-64-bmi2) prefetch = yes popcnt = yes sse = yes + sse3 = yes + ssse3 = yes + sse41 = yes + sse42 = yes + avx2 = yes pext = yes endif +ifeq ($(ARCH),x86-64-avx512) + arch = x86_64 + prefetch = yes + popcnt = yes + sse = yes + sse3 = yes + ssse3 = yes + sse41 = yes + sse42 = yes + avx2 = yes + pext = yes + avx512 = yes +endif + ifeq ($(ARCH),armv7) arch = armv7 prefetch = yes @@ -136,6 +230,14 @@ ifeq ($(ARCH),armv8) arch = armv8-a prefetch = yes popcnt = yes + neon = yes +endif + +ifeq ($(ARCH),apple-silicon) + arch = arm64 + prefetch = yes + popcnt = yes + neon = yes endif ifeq ($(ARCH),ppc-32) @@ -154,8 +256,8 @@ endif ### ========================================================================== ### 3.1 Selecting compiler (default = gcc) -CXXFLAGS += -Wall -Wcast-qual -fno-exceptions -std=c++11 $(EXTRACXXFLAGS) -DEPENDFLAGS += -std=c++11 +CXXFLAGS += -Wall -Wcast-qual -fno-exceptions -std=c++17 $(EXTRACXXFLAGS) +DEPENDFLAGS += -std=c++17 LDFLAGS += $(EXTRALDFLAGS) ifeq ($(COMP),) @@ -249,8 +351,8 @@ endif endif ifeq ($(KERNEL),Darwin) - CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.9 - LDFLAGS += -arch $(arch) -mmacosx-version-min=10.9 + CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.15 + LDFLAGS += -arch $(arch) -mmacosx-version-min=10.15 endif ### Travis CI script uses COMPILER to overwrite CXX @@ -283,8 +385,8 @@ endif ### 3.2.2 Debugging with undefined behavior sanitizers ifneq ($(sanitize),no) - CXXFLAGS += -g3 -fsanitize=$(sanitize) -fuse-ld=gold - LDFLAGS += -fsanitize=$(sanitize) -fuse-ld=gold + CXXFLAGS += -g3 -fsanitize=$(sanitize) + LDFLAGS += -fsanitize=$(sanitize) endif ### 3.3 Optimization @@ -322,7 +424,7 @@ endif ### 3.6 popcnt ifeq ($(popcnt),yes) - ifeq ($(arch),$(filter $(arch),ppc64 armv8-a)) + ifeq ($(arch),$(filter $(arch),ppc64 armv8-a arm64)) CXXFLAGS += -DUSE_POPCNT else ifeq ($(comp),icc) CXXFLAGS += -msse3 -DUSE_POPCNT @@ -331,11 +433,61 @@ ifeq ($(popcnt),yes) endif endif +ifeq ($(avx2),yes) + CXXFLAGS += -DUSE_AVX2 + ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + CXXFLAGS += -mavx2 + endif +endif + +ifeq ($(avx512),yes) + CXXFLAGS += -DUSE_AVX512 + ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + CXXFLAGS += -mavx512bw + endif +endif + +ifeq ($(sse42),yes) + CXXFLAGS += -DUSE_SSE42 + ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + CXXFLAGS += -msse4.2 + endif +endif + +ifeq ($(sse41),yes) + CXXFLAGS += -DUSE_SSE41 + ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + CXXFLAGS += -msse4.1 + endif +endif + +ifeq ($(ssse3),yes) + CXXFLAGS += -DUSE_SSSE3 + ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + CXXFLAGS += -mssse3 + endif +endif + +ifeq ($(sse3),yes) + CXXFLAGS += -DUSE_SSE3 + ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + CXXFLAGS += -msse3 + endif +endif + +ifeq ($(neon),yes) + CXXFLAGS += -DUSE_NEON +endif + +ifeq ($(arch),x86_64) + CXXFLAGS += -DUSE_SSE2 +endif + ### 3.7 pext ifeq ($(pext),yes) CXXFLAGS += -DUSE_PEXT ifeq ($(comp),$(filter $(comp),gcc clang mingw)) - CXXFLAGS += -msse4 -mbmi2 + CXXFLAGS += -mbmi2 endif endif @@ -381,15 +533,23 @@ help: @echo "Supported targets:" @echo "" @echo "build > Standard build" - @echo "profile-build > PGO build" + @echo "profile-build > Standard build with PGO" @echo "strip > Strip executable" @echo "install > Install executable" @echo "clean > Clean up" + @echo "net > Download the default nnue net" @echo "" @echo "Supported archs:" @echo "" - @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-avx512 > x86 64-bit with avx512 support" + @echo "x86-64-bmi2 > x86 64-bit with bmi2 support" + @echo "x86-64-avx2 > x86 64-bit with avx2 support" + @echo "x86-64-sse42 > x86 64-bit with sse42 support" + @echo "x86-64-modern > x86 64-bit with sse41 support (x86-64-sse41)" + @echo "x86-64-sse41 > x86 64-bit with sse41 support" + @echo "x86-64-ssse3 > x86 64-bit with ssse3 support" + @echo "x86-64-sse3-popcnt > x86 64-bit with sse3 and popcnt support" + @echo "x86-64-sse3 > x86 64-bit with sse3 support" @echo "x86-64 > x86 64-bit generic" @echo "x86-32 > x86 32-bit (also enables SSE)" @echo "x86-32-old > x86 32-bit fall back for old hardware" @@ -397,6 +557,7 @@ help: @echo "ppc-32 > PPC 32-bit" @echo "armv7 > ARMv7 32-bit" @echo "armv8 > ARMv8 64-bit" + @echo "apple-silicon > Apple silicon ARM64" @echo "general-64 > unspecified 64-bit" @echo "general-32 > unspecified 32-bit" @echo "" @@ -409,17 +570,20 @@ help: @echo "" @echo "Simple examples. If you don't know what to do, you likely want to run: " @echo "" - @echo "make build ARCH=x86-64 (This is for 64-bit systems)" - @echo "make build ARCH=x86-32 (This is for 32-bit systems)" + @echo "make -j build ARCH=x86-64 (This is for 64-bit systems)" + @echo "make -j build ARCH=x86-32 (This is for 32-bit systems)" @echo "" @echo "Advanced examples, for experienced users: " @echo "" - @echo "make build ARCH=x86-64 COMP=clang" - @echo "make profile-build ARCH=x86-64-bmi2 COMP=gcc COMPCXX=g++-4.8" + @echo "make -j build ARCH=x86-64-modern COMP=clang" + @echo "make -j profile-build ARCH=x86-64-bmi2 COMP=gcc COMPCXX=g++-4.8" @echo "" + @echo "The selected architecture $(ARCH) enables the following configuration: " + @echo "" + @$(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity -.PHONY: help build profile-build strip install clean objclean profileclean \ +.PHONY: help build profile-build strip install clean net objclean profileclean \ config-sanity icc-profile-use icc-profile-make gcc-profile-use gcc-profile-make \ clang-profile-use clang-profile-make @@ -453,14 +617,21 @@ install: clean: objclean profileclean @rm -f .depend *~ core +net: + $(eval nnuenet := $(shell grep EvalFile ucioption.cpp | grep Option | sed 's/.*\(nn-[a-z0-9]\{12\}.nnue\).*/\1/')) + @echo "Default net: $(nnuenet)" + $(eval nnuedownloadurl := https://tests.stockfishchess.org/api/nn/$(nnuenet)) + $(eval curl_or_wget := $(shell if hash curl 2>/dev/null; then echo "curl -sL"; elif hash wget 2>/dev/null; then echo "wget -qO-"; fi)) + @if test -f "$(nnuenet)"; then echo "Already available."; else echo "Downloading $(nnuedownloadurl)"; $(curl_or_wget) $(nnuedownloadurl) > $(nnuenet); fi + # clean binaries and objects objclean: - @rm -f $(EXE) *.o ./syzygy/*.o + @rm -f $(EXE) *.o ./syzygy/*.o ./nnue/*.o ./nnue/features/*.o # clean auxiliary profiling files profileclean: @rm -rf profdir - @rm -f bench.txt *.gcda *.gcno + @rm -f bench.txt *.gcda *.gcno ./syzygy/*.gcda ./nnue/*.gcda ./nnue/features/*.gcda @rm -f stockfish.profdata *.profraw default: @@ -485,7 +656,14 @@ config-sanity: @echo "prefetch: '$(prefetch)'" @echo "popcnt: '$(popcnt)'" @echo "sse: '$(sse)'" + @echo "sse3: '$(sse3)'" + @echo "ssse3: '$(ssse3)'" + @echo "sse41: '$(sse41)'" + @echo "sse42: '$(sse42)'" + @echo "avx2: '$(avx2)'" @echo "pext: '$(pext)'" + @echo "avx512: '$(avx512)'" + @echo "neon: '$(neon)'" @echo "" @echo "Flags:" @echo "CXX: $(CXX)" @@ -499,12 +677,19 @@ config-sanity: @test "$(optimize)" = "yes" || test "$(optimize)" = "no" @test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \ test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || \ - test "$(arch)" = "armv7" || test "$(arch)" = "armv8-a" + test "$(arch)" = "armv7" || test "$(arch)" = "armv8-a" || test "$(arch)" = "arm64" @test "$(bits)" = "32" || test "$(bits)" = "64" @test "$(prefetch)" = "yes" || test "$(prefetch)" = "no" @test "$(popcnt)" = "yes" || test "$(popcnt)" = "no" @test "$(sse)" = "yes" || test "$(sse)" = "no" + @test "$(sse3)" = "yes" || test "$(sse3)" = "no" + @test "$(ssse3)" = "yes" || test "$(ssse3)" = "no" + @test "$(sse41)" = "yes" || test "$(sse41)" = "no" + @test "$(sse42)" = "yes" || test "$(sse42)" = "no" + @test "$(avx2)" = "yes" || test "$(avx2)" = "no" @test "$(pext)" = "yes" || test "$(pext)" = "no" + @test "$(avx512)" = "yes" || test "$(avx512)" = "no" + @test "$(neon)" = "yes" || test "$(neon)" = "no" @test "$(comp)" = "gcc" || test "$(comp)" = "icc" || test "$(comp)" = "mingw" || test "$(comp)" = "clang" $(EXE): $(OBJS) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 3299f3737f8..6041d64287d 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/bitbase.cpp b/src/bitbase.cpp index 7e27eb96a10..bbe8e9a7e33 100644 --- a/src/bitbase.cpp +++ b/src/bitbase.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 0bf7eef91fd..f531010c79d 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/bitboard.h b/src/bitboard.h index 8c95de8c66c..a899d879818 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/endgame.cpp b/src/endgame.cpp index a8ceb648776..c8be21983dd 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/endgame.h b/src/endgame.h index fd1aba2dfbc..1351d88ab1c 100644 --- a/src/endgame.h +++ b/src/endgame.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/evaluate.cpp b/src/evaluate.cpp index b34d82f66d4..f43c62d6871 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,15 +18,50 @@ #include #include +#include #include // For std::memset #include #include +#include #include "bitboard.h" #include "evaluate.h" #include "material.h" #include "pawns.h" #include "thread.h" +#include "uci.h" + +namespace Eval { + + bool useNNUE; + std::string eval_file_loaded="None"; + + void init_NNUE() { + + useNNUE = Options["Use NNUE"]; + std::string eval_file = std::string(Options["EvalFile"]); + if (useNNUE && eval_file_loaded != eval_file) + if (Eval::NNUE::load_eval_file(eval_file)) + eval_file_loaded = eval_file; + } + + void verify_NNUE() { + + std::string eval_file = std::string(Options["EvalFile"]); + if (useNNUE && eval_file_loaded != eval_file) + { + std::cerr << "Use of NNUE evaluation, but the file " << eval_file << " was not loaded successfully. " + << "These network evaluation parameters must be available, compatible with this version of the code. " + << "The UCI option EvalFile might need to specify the full path, including the directory/folder name, to the file." << std::endl; + std::exit(EXIT_FAILURE); + } + + if (useNNUE) + sync_cout << "info string NNUE evaluation using " << eval_file << " enabled." << sync_endl; + else + sync_cout << "info string classical evaluation enabled." << sync_endl; + } +} namespace Trace { @@ -906,47 +939,62 @@ namespace { /// evaluation of the position from the point of view of the side to move. Value Eval::evaluate(const Position& pos) { - return Evaluation(pos).value(); -} + if (Eval::useNNUE) + return NNUE::evaluate(pos); + else + return Evaluation(pos).value(); +} /// trace() is like evaluate(), but instead of returning a value, it returns /// a string (suitable for outputting to stdout) that contains the detailed /// descriptions and values of each evaluation term. Useful for debugging. +/// Trace scores are from white's point of view std::string Eval::trace(const Position& pos) { if (pos.checkers()) - return "Total evaluation: none (in check)"; + return "Final evaluation: none (in check)"; - std::memset(scores, 0, sizeof(scores)); - - pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt + std::stringstream ss; + ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2); - Value v = Evaluation(pos).value(); + Value v; - v = pos.side_to_move() == WHITE ? v : -v; // Trace scores are from white's point of view + if (Eval::useNNUE) + { + v = NNUE::evaluate(pos); + } + else + { + std::memset(scores, 0, sizeof(scores)); + + pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt + + v = Evaluation(pos).value(); + + ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2) + << " Term | White | Black | Total \n" + << " | MG EG | MG EG | MG EG \n" + << " ------------+-------------+-------------+------------\n" + << " Material | " << Term(MATERIAL) + << " Imbalance | " << Term(IMBALANCE) + << " Pawns | " << Term(PAWN) + << " Knights | " << Term(KNIGHT) + << " Bishops | " << Term(BISHOP) + << " Rooks | " << Term(ROOK) + << " Queens | " << Term(QUEEN) + << " Mobility | " << Term(MOBILITY) + << " King safety | " << Term(KING) + << " Threats | " << Term(THREAT) + << " Passed | " << Term(PASSED) + << " Space | " << Term(SPACE) + << " Winnable | " << Term(WINNABLE) + << " ------------+-------------+-------------+------------\n" + << " Total | " << Term(TOTAL); + } - std::stringstream ss; - ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2) - << " Term | White | Black | Total \n" - << " | MG EG | MG EG | MG EG \n" - << " ------------+-------------+-------------+------------\n" - << " Material | " << Term(MATERIAL) - << " Imbalance | " << Term(IMBALANCE) - << " Pawns | " << Term(PAWN) - << " Knights | " << Term(KNIGHT) - << " Bishops | " << Term(BISHOP) - << " Rooks | " << Term(ROOK) - << " Queens | " << Term(QUEEN) - << " Mobility | " << Term(MOBILITY) - << " King safety | " << Term(KING) - << " Threats | " << Term(THREAT) - << " Passed | " << Term(PASSED) - << " Space | " << Term(SPACE) - << " Winnable | " << Term(WINNABLE) - << " ------------+-------------+-------------+------------\n" - << " Total | " << Term(TOTAL); + v = pos.side_to_move() == WHITE ? v : -v; ss << "\nFinal evaluation: " << to_cp(v) << " (white side)\n"; diff --git a/src/evaluate.h b/src/evaluate.h index 7c8a2a6f77e..e808068da2f 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -29,9 +27,23 @@ class Position; namespace Eval { -std::string trace(const Position& pos); + std::string trace(const Position& pos); + Value evaluate(const Position& pos); -Value evaluate(const Position& pos); -} + extern bool useNNUE; + extern std::string eval_file_loaded; + void init_NNUE(); + void verify_NNUE(); + + namespace NNUE { + + Value evaluate(const Position& pos); + Value compute_eval(const Position& pos); + void update_eval(const Position& pos); + bool load_eval_file(const std::string& evalFile); + + } // namespace NNUE + +} // namespace Eval #endif // #ifndef EVALUATE_H_INCLUDED diff --git a/src/main.cpp b/src/main.cpp index fafefee2bc0..fbad6622974 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -46,6 +44,7 @@ int main(int argc, char* argv[]) { Endgames::init(); Threads.set(size_t(Options["Threads"])); Search::clear(); // After threads are up + Eval::init_NNUE(); UCI::loop(argc, argv); diff --git a/src/material.cpp b/src/material.cpp index bb25d3caa7e..0ef9926f022 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/material.h b/src/material.h index 21647f23b21..80d01655111 100644 --- a/src/material.h +++ b/src/material.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/misc.cpp b/src/misc.cpp index 2bc05c5b736..3d7c75e558f 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -46,6 +44,7 @@ typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY); #include #include #include +#include #if defined(__linux__) && !defined(__ANDROID__) #include @@ -147,10 +146,8 @@ const string engine_info(bool to_uci) { ss << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2); } - ss << (Is64Bit ? " 64" : "") - << (HasPext ? " BMI2" : (HasPopCnt ? " POPCNT" : "")) - << (to_uci ? "\nid author ": " by ") - << "T. Romstad, M. Costalba, J. Kiiski, G. Linscott"; + ss << (to_uci ? "\nid author ": " by ") + << "the Stockfish developers (see AUTHORS file)"; return ss.str(); } @@ -215,7 +212,33 @@ const std::string compiler_info() { compiler += " on unknown system"; #endif - compiler += "\n __VERSION__ macro expands to: "; + compiler += "\nCompilation settings include: "; + compiler += (Is64Bit ? " 64bit" : " 32bit"); + #if defined(USE_AVX512) + compiler += " AVX512"; + #endif + #if defined(USE_AVX2) + compiler += " AVX2"; + #endif + #if defined(USE_SSE42) + compiler += " SSE42"; + #endif + #if defined(USE_SSE41) + compiler += " SSE41"; + #endif + #if defined(USE_SSSE3) + compiler += " SSSE3"; + #endif + #if defined(USE_SSE3) + compiler += " SSE3"; + #endif + compiler += (HasPext ? " BMI2" : ""); + compiler += (HasPopCnt ? " POPCNT" : ""); + #if !defined(NDEBUG) + compiler += " DEBUG"; + #endif + + compiler += "\n__VERSION__ macro expands to: "; #ifdef __VERSION__ compiler += __VERSION__; #else @@ -293,6 +316,29 @@ void prefetch(void* addr) { #endif +/// Wrappers for systems where the c++17 implementation doesn't guarantee the availability of aligned_alloc. +/// Memory allocated with std_aligned_alloc must be freed with std_aligned_free. +/// + +void* std_aligned_alloc(size_t alignment, size_t size) { +#if defined(__APPLE__) + return aligned_alloc(alignment, size); +#elif defined(_WIN32) + return _mm_malloc(size, alignment); +#else + return std::aligned_alloc(alignment, size); +#endif +} + +void std_aligned_free(void* ptr) { +#if defined(__APPLE__) + free(ptr); +#elif defined(_WIN32) + _mm_free(ptr); +#else + free(ptr); +#endif +} /// aligned_ttmem_alloc() will return suitably aligned memory, and if possible use large pages. /// The returned pointer is the aligned one, while the mem argument is the one that needs diff --git a/src/misc.h b/src/misc.h index 373f1b77c76..eb4e05c083b 100644 --- a/src/misc.h +++ b/src/misc.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -33,6 +31,8 @@ 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); +void* std_aligned_alloc(size_t alignment, size_t size); +void std_aligned_free(void* ptr); void* aligned_ttmem_alloc(size_t size, void*& mem); void aligned_ttmem_free(void* mem); // nop if mem == nullptr diff --git a/src/movegen.cpp b/src/movegen.cpp index 4ff12fc6452..d74df4c32cf 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/movegen.h b/src/movegen.h index c2e7c3f1101..fb616d000e1 100644 --- a/src/movegen.h +++ b/src/movegen.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/movepick.cpp b/src/movepick.cpp index 5775f810600..96a44449087 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/movepick.h b/src/movepick.h index aaff388f641..f080935afb1 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/architectures/halfkp_256x2-32-32.h b/src/nnue/architectures/halfkp_256x2-32-32.h new file mode 100644 index 00000000000..9216bd41746 --- /dev/null +++ b/src/nnue/architectures/halfkp_256x2-32-32.h @@ -0,0 +1,54 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// Definition of input features and network structure used in NNUE evaluation function + +#ifndef NNUE_HALFKP_256X2_32_32_H_INCLUDED +#define NNUE_HALFKP_256X2_32_32_H_INCLUDED + +#include "../features/feature_set.h" +#include "../features/half_kp.h" + +#include "../layers/input_slice.h" +#include "../layers/affine_transform.h" +#include "../layers/clipped_relu.h" + +namespace Eval::NNUE { + +// Input features used in evaluation function +using RawFeatures = Features::FeatureSet< + Features::HalfKP>; + +// Number of input feature dimensions after conversion +constexpr IndexType kTransformedFeatureDimensions = 256; + +namespace Layers { + +// Define network structure +using InputLayer = InputSlice; +using HiddenLayer1 = ClippedReLU>; +using HiddenLayer2 = ClippedReLU>; +using OutputLayer = AffineTransform; + +} // namespace Layers + +using Network = Layers::OutputLayer; + +} // namespace Eval::NNUE + +#endif // #ifndef NNUE_HALFKP_256X2_32_32_H_INCLUDED diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp new file mode 100644 index 00000000000..af0894b2f9e --- /dev/null +++ b/src/nnue/evaluate_nnue.cpp @@ -0,0 +1,178 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// Code for calculating NNUE evaluation function + +#include +#include +#include + +#include "../evaluate.h" +#include "../position.h" +#include "../misc.h" +#include "../uci.h" + +#include "evaluate_nnue.h" + +ExtPieceSquare kpp_board_index[PIECE_NB] = { + // convention: W - us, B - them + // viewed from other side, W and B are reversed + { PS_NONE, PS_NONE }, + { PS_W_PAWN, PS_B_PAWN }, + { PS_W_KNIGHT, PS_B_KNIGHT }, + { PS_W_BISHOP, PS_B_BISHOP }, + { PS_W_ROOK, PS_B_ROOK }, + { PS_W_QUEEN, PS_B_QUEEN }, + { PS_W_KING, PS_B_KING }, + { PS_NONE, PS_NONE }, + { PS_NONE, PS_NONE }, + { PS_B_PAWN, PS_W_PAWN }, + { PS_B_KNIGHT, PS_W_KNIGHT }, + { PS_B_BISHOP, PS_W_BISHOP }, + { PS_B_ROOK, PS_W_ROOK }, + { PS_B_QUEEN, PS_W_QUEEN }, + { PS_B_KING, PS_W_KING }, + { PS_NONE, PS_NONE } +}; + + +namespace Eval::NNUE { + + // Input feature converter + AlignedPtr feature_transformer; + + // Evaluation function + AlignedPtr network; + + // Evaluation function file name + std::string fileName; + + namespace Detail { + + // Initialize the evaluation function parameters + template + void Initialize(AlignedPtr& pointer) { + + pointer.reset(reinterpret_cast(std_aligned_alloc(alignof(T), sizeof(T)))); + std::memset(pointer.get(), 0, sizeof(T)); + } + + // Read evaluation function parameters + template + bool ReadParameters(std::istream& stream, const AlignedPtr& pointer) { + + std::uint32_t header; + stream.read(reinterpret_cast(&header), sizeof(header)); + if (!stream || header != T::GetHashValue()) return false; + return pointer->ReadParameters(stream); + } + + } // namespace Detail + + // Initialize the evaluation function parameters + void Initialize() { + + Detail::Initialize(feature_transformer); + Detail::Initialize(network); + } + + // Read network header + bool ReadHeader(std::istream& stream, + std::uint32_t* hash_value, std::string* architecture) { + + std::uint32_t version, size; + stream.read(reinterpret_cast(&version), sizeof(version)); + stream.read(reinterpret_cast(hash_value), sizeof(*hash_value)); + stream.read(reinterpret_cast(&size), sizeof(size)); + if (!stream || version != kVersion) return false; + architecture->resize(size); + stream.read(&(*architecture)[0], size); + return !stream.fail(); + } + + // Read network parameters + bool ReadParameters(std::istream& stream) { + + std::uint32_t hash_value; + std::string architecture; + if (!ReadHeader(stream, &hash_value, &architecture)) return false; + if (hash_value != kHashValue) return false; + if (!Detail::ReadParameters(stream, feature_transformer)) return false; + if (!Detail::ReadParameters(stream, network)) return false; + return stream && stream.peek() == std::ios::traits_type::eof(); + } + + // Proceed with the difference calculation if possible + static void UpdateAccumulatorIfPossible(const Position& pos) { + + feature_transformer->UpdateAccumulatorIfPossible(pos); + } + + // Calculate the evaluation value + static Value ComputeScore(const Position& pos, bool refresh) { + + auto& accumulator = pos.state()->accumulator; + if (!refresh && accumulator.computed_score) { + return accumulator.score; + } + + alignas(kCacheLineSize) TransformedFeatureType + transformed_features[FeatureTransformer::kBufferSize]; + feature_transformer->Transform(pos, transformed_features, refresh); + alignas(kCacheLineSize) char buffer[Network::kBufferSize]; + const auto output = network->Propagate(transformed_features, buffer); + + auto score = static_cast(output[0] / FV_SCALE); + + accumulator.score = score; + accumulator.computed_score = true; + return accumulator.score; + } + + // Load the evaluation function file + bool load_eval_file(const std::string& evalFile) { + + Initialize(); + fileName = evalFile; + + std::ifstream stream(evalFile, std::ios::binary); + + const bool result = ReadParameters(stream); + + return result; + } + + // Evaluation function. Perform differential calculation. + Value evaluate(const Position& pos) { + Value v = ComputeScore(pos, false); + v = Utility::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); + + return v; + } + + // Evaluation function. Perform full calculation. + Value compute_eval(const Position& pos) { + return ComputeScore(pos, true); + } + + // Proceed with the difference calculation if possible + void update_eval(const Position& pos) { + UpdateAccumulatorIfPossible(pos); + } + +} // namespace Eval::NNUE diff --git a/src/nnue/evaluate_nnue.h b/src/nnue/evaluate_nnue.h new file mode 100644 index 00000000000..5f0d185589e --- /dev/null +++ b/src/nnue/evaluate_nnue.h @@ -0,0 +1,48 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// header used in NNUE evaluation function + +#ifndef NNUE_EVALUATE_NNUE_H_INCLUDED +#define NNUE_EVALUATE_NNUE_H_INCLUDED + +#include "nnue_feature_transformer.h" + +#include + +namespace Eval::NNUE { + + // Hash value of evaluation function structure + constexpr std::uint32_t kHashValue = + FeatureTransformer::GetHashValue() ^ Network::GetHashValue(); + + // Deleter for automating release of memory area + template + struct AlignedDeleter { + void operator()(T* ptr) const { + ptr->~T(); + std_aligned_free(ptr); + } + }; + + template + using AlignedPtr = std::unique_ptr>; + +} // namespace Eval::NNUE + +#endif // #ifndef NNUE_EVALUATE_NNUE_H_INCLUDED diff --git a/src/nnue/features/feature_set.h b/src/nnue/features/feature_set.h new file mode 100644 index 00000000000..79ca83aed0b --- /dev/null +++ b/src/nnue/features/feature_set.h @@ -0,0 +1,135 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// A class template that represents the input feature set of the NNUE evaluation function + +#ifndef NNUE_FEATURE_SET_H_INCLUDED +#define NNUE_FEATURE_SET_H_INCLUDED + +#include "features_common.h" +#include + +namespace Eval::NNUE::Features { + + // Class template that represents a list of values + template + struct CompileTimeList; + + template + struct CompileTimeList { + static constexpr bool Contains(T value) { + return value == First || CompileTimeList::Contains(value); + } + static constexpr std::array + kValues = {{First, Remaining...}}; + }; + + // Base class of feature set + template + class FeatureSetBase { + + public: + // Get a list of indices for active features + template + static void AppendActiveIndices( + const Position& pos, TriggerEvent trigger, IndexListType active[2]) { + + for (Color perspective : { WHITE, BLACK }) { + Derived::CollectActiveIndices( + pos, trigger, perspective, &active[perspective]); + } + } + + // Get a list of indices for recently changed features + template + static void AppendChangedIndices( + const PositionType& pos, TriggerEvent trigger, + IndexListType removed[2], IndexListType added[2], bool reset[2]) { + + const auto& dp = pos.state()->dirtyPiece; + if (dp.dirty_num == 0) return; + + for (Color perspective : { WHITE, BLACK }) { + reset[perspective] = false; + switch (trigger) { + case TriggerEvent::kFriendKingMoved: + reset[perspective] = + dp.pieceId[0] == PIECE_ID_KING + perspective; + break; + default: + assert(false); + break; + } + if (reset[perspective]) { + Derived::CollectActiveIndices( + pos, trigger, perspective, &added[perspective]); + } else { + Derived::CollectChangedIndices( + pos, trigger, perspective, + &removed[perspective], &added[perspective]); + } + } + } + }; + + // Class template that represents the feature set + template + class FeatureSet : public FeatureSetBase> { + + public: + // Hash value embedded in the evaluation file + static constexpr std::uint32_t kHashValue = FeatureType::kHashValue; + // Number of feature dimensions + static constexpr IndexType kDimensions = FeatureType::kDimensions; + // Maximum number of simultaneously active features + static constexpr IndexType kMaxActiveDimensions = + FeatureType::kMaxActiveDimensions; + // Trigger for full calculation instead of difference calculation + using SortedTriggerSet = + CompileTimeList; + static constexpr auto kRefreshTriggers = SortedTriggerSet::kValues; + + private: + // Get a list of indices for active features + static void CollectActiveIndices( + const Position& pos, const TriggerEvent trigger, const Color perspective, + IndexList* const active) { + if (FeatureType::kRefreshTrigger == trigger) { + FeatureType::AppendActiveIndices(pos, perspective, active); + } + } + + // Get a list of indices for recently changed features + static void CollectChangedIndices( + const Position& pos, const TriggerEvent trigger, const Color perspective, + IndexList* const removed, IndexList* const added) { + + if (FeatureType::kRefreshTrigger == trigger) { + FeatureType::AppendChangedIndices(pos, perspective, removed, added); + } + } + + // Make the base class and the class template that recursively uses itself a friend + friend class FeatureSetBase; + template + friend class FeatureSet; + }; + +} // namespace Eval::NNUE::Features + +#endif // #ifndef NNUE_FEATURE_SET_H_INCLUDED diff --git a/src/nnue/features/features_common.h b/src/nnue/features/features_common.h new file mode 100644 index 00000000000..d00a35df918 --- /dev/null +++ b/src/nnue/features/features_common.h @@ -0,0 +1,45 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +//Common header of input features of NNUE evaluation function + +#ifndef NNUE_FEATURES_COMMON_H_INCLUDED +#define NNUE_FEATURES_COMMON_H_INCLUDED + +#include "../../evaluate.h" +#include "../nnue_common.h" + +namespace Eval::NNUE::Features { + + class IndexList; + + template + class FeatureSet; + + // Trigger to perform full calculations instead of difference only + enum class TriggerEvent { + kFriendKingMoved // calculate full evaluation when own king moves + }; + + enum class Side { + kFriend // side to move + }; + +} // namespace Eval::NNUE::Features + +#endif // #ifndef NNUE_FEATURES_COMMON_H_INCLUDED diff --git a/src/nnue/features/half_kp.cpp b/src/nnue/features/half_kp.cpp new file mode 100644 index 00000000000..628add6ed12 --- /dev/null +++ b/src/nnue/features/half_kp.cpp @@ -0,0 +1,92 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +//Definition of input features HalfKP of NNUE evaluation function + +#include "half_kp.h" +#include "index_list.h" + +namespace Eval::NNUE::Features { + + // Find the index of the feature quantity from the king position and PieceSquare + template + inline IndexType HalfKP::MakeIndex(Square sq_k, PieceSquare p) { + return static_cast(PS_END) * static_cast(sq_k) + p; + } + + // Get pieces information + template + inline void HalfKP::GetPieces( + const Position& pos, Color perspective, + PieceSquare** pieces, Square* sq_target_k) { + + *pieces = (perspective == BLACK) ? + pos.eval_list()->piece_list_fb() : + pos.eval_list()->piece_list_fw(); + const PieceId target = (AssociatedKing == Side::kFriend) ? + static_cast(PIECE_ID_KING + perspective) : + static_cast(PIECE_ID_KING + ~perspective); + *sq_target_k = static_cast(((*pieces)[target] - PS_W_KING) % SQUARE_NB); + } + + // Get a list of indices for active features + template + void HalfKP::AppendActiveIndices( + const Position& pos, Color perspective, IndexList* active) { + + // Do nothing if array size is small to avoid compiler warning + if (RawFeatures::kMaxActiveDimensions < kMaxActiveDimensions) return; + + PieceSquare* pieces; + Square sq_target_k; + GetPieces(pos, perspective, &pieces, &sq_target_k); + for (PieceId i = PIECE_ID_ZERO; i < PIECE_ID_KING; ++i) { + if (pieces[i] != PS_NONE) { + active->push_back(MakeIndex(sq_target_k, pieces[i])); + } + } + } + + // Get a list of indices for recently changed features + template + void HalfKP::AppendChangedIndices( + const Position& pos, Color perspective, + IndexList* removed, IndexList* added) { + + PieceSquare* pieces; + Square sq_target_k; + GetPieces(pos, perspective, &pieces, &sq_target_k); + const auto& dp = pos.state()->dirtyPiece; + for (int i = 0; i < dp.dirty_num; ++i) { + if (dp.pieceId[i] >= PIECE_ID_KING) continue; + const auto old_p = static_cast( + dp.old_piece[i].from[perspective]); + if (old_p != PS_NONE) { + removed->push_back(MakeIndex(sq_target_k, old_p)); + } + const auto new_p = static_cast( + dp.new_piece[i].from[perspective]); + if (new_p != PS_NONE) { + added->push_back(MakeIndex(sq_target_k, new_p)); + } + } + } + + template class HalfKP; + +} // namespace Eval::NNUE::Features diff --git a/src/nnue/features/half_kp.h b/src/nnue/features/half_kp.h new file mode 100644 index 00000000000..99842eea777 --- /dev/null +++ b/src/nnue/features/half_kp.h @@ -0,0 +1,67 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +//Definition of input features HalfKP of NNUE evaluation function + +#ifndef NNUE_FEATURES_HALF_KP_H_INCLUDED +#define NNUE_FEATURES_HALF_KP_H_INCLUDED + +#include "../../evaluate.h" +#include "features_common.h" + +namespace Eval::NNUE::Features { + + // Feature HalfKP: Combination of the position of own king + // and the position of pieces other than kings + template + class HalfKP { + + public: + // Feature name + static constexpr const char* kName = "HalfKP(Friend)"; + // Hash value embedded in the evaluation file + static constexpr std::uint32_t kHashValue = + 0x5D69D5B9u ^ (AssociatedKing == Side::kFriend); + // Number of feature dimensions + static constexpr IndexType kDimensions = + static_cast(SQUARE_NB) * static_cast(PS_END); + // Maximum number of simultaneously active features + static constexpr IndexType kMaxActiveDimensions = PIECE_ID_KING; + // Trigger for full calculation instead of difference calculation + static constexpr TriggerEvent kRefreshTrigger = TriggerEvent::kFriendKingMoved; + + // Get a list of indices for active features + static void AppendActiveIndices(const Position& pos, Color perspective, + IndexList* active); + + // Get a list of indices for recently changed features + static void AppendChangedIndices(const Position& pos, Color perspective, + IndexList* removed, IndexList* added); + + // Index of a feature for a given king position and another piece on some square + static IndexType MakeIndex(Square sq_k, PieceSquare p); + + private: + // Get pieces information + static void GetPieces(const Position& pos, Color perspective, + PieceSquare** pieces, Square* sq_target_k); + }; + +} // namespace Eval::NNUE::Features + +#endif // #ifndef NNUE_FEATURES_HALF_KP_H_INCLUDED diff --git a/src/nnue/features/index_list.h b/src/nnue/features/index_list.h new file mode 100644 index 00000000000..d9ad680adf7 --- /dev/null +++ b/src/nnue/features/index_list.h @@ -0,0 +1,64 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// Definition of index list of input features + +#ifndef NNUE_FEATURES_INDEX_LIST_H_INCLUDED +#define NNUE_FEATURES_INDEX_LIST_H_INCLUDED + +#include "../../position.h" +#include "../nnue_architecture.h" + +namespace Eval::NNUE::Features { + + // Class template used for feature index list + template + class ValueList { + + public: + std::size_t size() const { return size_; } + void resize(std::size_t size) { size_ = size; } + void push_back(const T& value) { values_[size_++] = value; } + T& operator[](std::size_t index) { return values_[index]; } + T* begin() { return values_; } + T* end() { return values_ + size_; } + const T& operator[](std::size_t index) const { return values_[index]; } + const T* begin() const { return values_; } + const T* end() const { return values_ + size_; } + + void swap(ValueList& other) { + const std::size_t max_size = std::max(size_, other.size_); + for (std::size_t i = 0; i < max_size; ++i) { + std::swap(values_[i], other.values_[i]); + } + std::swap(size_, other.size_); + } + + private: + T values_[MaxSize]; + std::size_t size_ = 0; + }; + + //Type of feature index list + class IndexList + : public ValueList { + }; + +} // namespace Eval::NNUE::Features + +#endif // NNUE_FEATURES_INDEX_LIST_H_INCLUDED diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h new file mode 100644 index 00000000000..b585bc87819 --- /dev/null +++ b/src/nnue/layers/affine_transform.h @@ -0,0 +1,215 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// Definition of layer AffineTransform of NNUE evaluation function + +#ifndef NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED +#define NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED + +#include +#include "../nnue_common.h" + +namespace Eval::NNUE::Layers { + + // Affine transformation layer + template + class AffineTransform { + public: + // Input/output type + using InputType = typename PreviousLayer::OutputType; + using OutputType = std::int32_t; + static_assert(std::is_same::value, ""); + + // Number of input/output dimensions + static constexpr IndexType kInputDimensions = + PreviousLayer::kOutputDimensions; + static constexpr IndexType kOutputDimensions = OutputDimensions; + static constexpr IndexType kPaddedInputDimensions = + CeilToMultiple(kInputDimensions, kMaxSimdWidth); + + // Size of forward propagation buffer used in this layer + static constexpr std::size_t kSelfBufferSize = + CeilToMultiple(kOutputDimensions * sizeof(OutputType), kCacheLineSize); + + // Size of the forward propagation buffer used from the input layer to this layer + static constexpr std::size_t kBufferSize = + PreviousLayer::kBufferSize + kSelfBufferSize; + + // Hash value embedded in the evaluation file + static constexpr std::uint32_t GetHashValue() { + std::uint32_t hash_value = 0xCC03DAE4u; + hash_value += kOutputDimensions; + hash_value ^= PreviousLayer::GetHashValue() >> 1; + hash_value ^= PreviousLayer::GetHashValue() << 31; + return hash_value; + } + + // Read network parameters + bool ReadParameters(std::istream& stream) { + if (!previous_layer_.ReadParameters(stream)) return false; + stream.read(reinterpret_cast(biases_), + kOutputDimensions * sizeof(BiasType)); + stream.read(reinterpret_cast(weights_), + kOutputDimensions * kPaddedInputDimensions * + sizeof(WeightType)); + return !stream.fail(); + } + + // Forward propagation + const OutputType* Propagate( + const TransformedFeatureType* transformed_features, char* buffer) const { + const auto input = previous_layer_.Propagate( + transformed_features, buffer + kSelfBufferSize); + const auto output = reinterpret_cast(buffer); + + #if defined(USE_AVX512) + constexpr IndexType kNumChunks = kPaddedInputDimensions / (kSimdWidth * 2); + const __m512i kOnes = _mm512_set1_epi16(1); + const auto input_vector = reinterpret_cast(input); + + #elif defined(USE_AVX2) + constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; + const __m256i kOnes = _mm256_set1_epi16(1); + const auto input_vector = reinterpret_cast(input); + + #elif defined(USE_SSSE3) + constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; + const __m128i kOnes = _mm_set1_epi16(1); + const auto input_vector = reinterpret_cast(input); + + #elif defined(USE_NEON) + constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; + const auto input_vector = reinterpret_cast(input); + #endif + + for (IndexType i = 0; i < kOutputDimensions; ++i) { + const IndexType offset = i * kPaddedInputDimensions; + + #if defined(USE_AVX512) + __m512i sum = _mm512_setzero_si512(); + const auto row = reinterpret_cast(&weights_[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + + #if defined(__MINGW32__) || defined(__MINGW64__) + __m512i product = _mm512_maddubs_epi16(_mm512_loadu_si512(&input_vector[j]), _mm512_load_si512(&row[j])); + #else + __m512i product = _mm512_maddubs_epi16(_mm512_load_si512(&input_vector[j]), _mm512_load_si512(&row[j])); + #endif + + product = _mm512_madd_epi16(product, kOnes); + sum = _mm512_add_epi32(sum, product); + } + output[i] = _mm512_reduce_add_epi32(sum) + biases_[i]; + + // Note: Changing kMaxSimdWidth from 32 to 64 breaks loading existing networks. + // As a result kPaddedInputDimensions may not be an even multiple of 64(512bit) + // and we have to do one more 256bit chunk. + if (kPaddedInputDimensions != kNumChunks * kSimdWidth * 2) + { + const auto iv_256 = reinterpret_cast(input); + const auto row_256 = reinterpret_cast(&weights_[offset]); + int j = kNumChunks * 2; + + #if defined(__MINGW32__) || defined(__MINGW64__) // See HACK comment below in AVX2. + __m256i sum256 = _mm256_maddubs_epi16(_mm256_loadu_si256(&iv_256[j]), _mm256_load_si256(&row_256[j])); + #else + __m256i sum256 = _mm256_maddubs_epi16(_mm256_load_si256(&iv_256[j]), _mm256_load_si256(&row_256[j])); + #endif + + sum256 = _mm256_madd_epi16(sum256, _mm256_set1_epi16(1)); + sum256 = _mm256_hadd_epi32(sum256, sum256); + sum256 = _mm256_hadd_epi32(sum256, sum256); + const __m128i lo = _mm256_extracti128_si256(sum256, 0); + const __m128i hi = _mm256_extracti128_si256(sum256, 1); + output[i] += _mm_cvtsi128_si32(lo) + _mm_cvtsi128_si32(hi); + } + + #elif defined(USE_AVX2) + __m256i sum = _mm256_setzero_si256(); + const auto row = reinterpret_cast(&weights_[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + __m256i product = _mm256_maddubs_epi16( + + #if defined(__MINGW32__) || defined(__MINGW64__) + // HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Because the binary + // compiled with g++ in MSYS2 crashes here because the output memory is not aligned + // even though alignas is specified. + _mm256_loadu_si256 + #else + _mm256_load_si256 + #endif + + (&input_vector[j]), _mm256_load_si256(&row[j])); + product = _mm256_madd_epi16(product, kOnes); + sum = _mm256_add_epi32(sum, product); + } + sum = _mm256_hadd_epi32(sum, sum); + sum = _mm256_hadd_epi32(sum, sum); + const __m128i lo = _mm256_extracti128_si256(sum, 0); + const __m128i hi = _mm256_extracti128_si256(sum, 1); + output[i] = _mm_cvtsi128_si32(lo) + _mm_cvtsi128_si32(hi) + biases_[i]; + + #elif defined(USE_SSSE3) + __m128i sum = _mm_cvtsi32_si128(biases_[i]); + const auto row = reinterpret_cast(&weights_[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + __m128i product = _mm_maddubs_epi16( + _mm_load_si128(&input_vector[j]), _mm_load_si128(&row[j])); + product = _mm_madd_epi16(product, kOnes); + sum = _mm_add_epi32(sum, product); + } + sum = _mm_hadd_epi32(sum, sum); + sum = _mm_hadd_epi32(sum, sum); + output[i] = _mm_cvtsi128_si32(sum); + + #elif defined(USE_NEON) + int32x4_t sum = {biases_[i]}; + const auto row = reinterpret_cast(&weights_[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + int16x8_t product = vmull_s8(input_vector[j * 2], row[j * 2]); + product = vmlal_s8(product, input_vector[j * 2 + 1], row[j * 2 + 1]); + sum = vpadalq_s16(sum, product); + } + output[i] = sum[0] + sum[1] + sum[2] + sum[3]; + + #else + OutputType sum = biases_[i]; + for (IndexType j = 0; j < kInputDimensions; ++j) { + sum += weights_[offset + j] * input[j]; + } + output[i] = sum; + #endif + + } + return output; + } + + private: + using BiasType = OutputType; + using WeightType = std::int8_t; + + PreviousLayer previous_layer_; + + alignas(kCacheLineSize) BiasType biases_[kOutputDimensions]; + alignas(kCacheLineSize) + WeightType weights_[kOutputDimensions * kPaddedInputDimensions]; + }; + +} // namespace Eval::NNUE::Layers + +#endif // #ifndef NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h new file mode 100644 index 00000000000..7ade598f4d2 --- /dev/null +++ b/src/nnue/layers/clipped_relu.h @@ -0,0 +1,186 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// Definition of layer ClippedReLU of NNUE evaluation function + +#ifndef NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED +#define NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED + +#include "../nnue_common.h" + +namespace Eval::NNUE::Layers { + + // Clipped ReLU + template + class ClippedReLU { + public: + // Input/output type + using InputType = typename PreviousLayer::OutputType; + using OutputType = std::uint8_t; + static_assert(std::is_same::value, ""); + + // Number of input/output dimensions + static constexpr IndexType kInputDimensions = + PreviousLayer::kOutputDimensions; + static constexpr IndexType kOutputDimensions = kInputDimensions; + + // Size of forward propagation buffer used in this layer + static constexpr std::size_t kSelfBufferSize = + CeilToMultiple(kOutputDimensions * sizeof(OutputType), kCacheLineSize); + + // Size of the forward propagation buffer used from the input layer to this layer + static constexpr std::size_t kBufferSize = + PreviousLayer::kBufferSize + kSelfBufferSize; + + // Hash value embedded in the evaluation file + static constexpr std::uint32_t GetHashValue() { + std::uint32_t hash_value = 0x538D24C7u; + hash_value += PreviousLayer::GetHashValue(); + return hash_value; + } + + // Read network parameters + bool ReadParameters(std::istream& stream) { + return previous_layer_.ReadParameters(stream); + } + + // Forward propagation + const OutputType* Propagate( + const TransformedFeatureType* transformed_features, char* buffer) const { + const auto input = previous_layer_.Propagate( + transformed_features, buffer + kSelfBufferSize); + const auto output = reinterpret_cast(buffer); + + #if defined(USE_AVX2) + constexpr IndexType kNumChunks = kInputDimensions / kSimdWidth; + const __m256i kZero = _mm256_setzero_si256(); + const __m256i kOffsets = _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0); + const auto in = reinterpret_cast(input); + const auto out = reinterpret_cast<__m256i*>(output); + for (IndexType i = 0; i < kNumChunks; ++i) { + const __m256i words0 = _mm256_srai_epi16(_mm256_packs_epi32( + + #if defined(__MINGW32__) || defined(__MINGW64__) + // HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Because the binary + // compiled with g++ in MSYS2 crashes here because the output memory is not aligned + // even though alignas is specified. + _mm256_loadu_si256 + #else + _mm256_load_si256 + #endif + + (&in[i * 4 + 0]), + + #if defined(__MINGW32__) || defined(__MINGW64__) + _mm256_loadu_si256 + #else + _mm256_load_si256 + #endif + + (&in[i * 4 + 1])), kWeightScaleBits); + const __m256i words1 = _mm256_srai_epi16(_mm256_packs_epi32( + + #if defined(__MINGW32__) || defined(__MINGW64__) + _mm256_loadu_si256 + #else + _mm256_load_si256 + #endif + + (&in[i * 4 + 2]), + + #if defined(__MINGW32__) || defined(__MINGW64__) + _mm256_loadu_si256 + #else + _mm256_load_si256 + #endif + + (&in[i * 4 + 3])), kWeightScaleBits); + + #if defined(__MINGW32__) || defined(__MINGW64__) + _mm256_storeu_si256 + #else + _mm256_store_si256 + #endif + + (&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8( + _mm256_packs_epi16(words0, words1), kZero), kOffsets)); + } + constexpr IndexType kStart = kNumChunks * kSimdWidth; + + #elif defined(USE_SSSE3) + constexpr IndexType kNumChunks = kInputDimensions / kSimdWidth; + + #ifdef USE_SSE41 + const __m128i kZero = _mm_setzero_si128(); + #else + const __m128i k0x80s = _mm_set1_epi8(-128); + #endif + + const auto in = reinterpret_cast(input); + const auto out = reinterpret_cast<__m128i*>(output); + for (IndexType i = 0; i < kNumChunks; ++i) { + const __m128i words0 = _mm_srai_epi16(_mm_packs_epi32( + _mm_load_si128(&in[i * 4 + 0]), + _mm_load_si128(&in[i * 4 + 1])), kWeightScaleBits); + const __m128i words1 = _mm_srai_epi16(_mm_packs_epi32( + _mm_load_si128(&in[i * 4 + 2]), + _mm_load_si128(&in[i * 4 + 3])), kWeightScaleBits); + const __m128i packedbytes = _mm_packs_epi16(words0, words1); + _mm_store_si128(&out[i], + + #ifdef USE_SSE41 + _mm_max_epi8(packedbytes, kZero) + #else + _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s) + #endif + + ); + } + constexpr IndexType kStart = kNumChunks * kSimdWidth; + + #elif defined(USE_NEON) + constexpr IndexType kNumChunks = kInputDimensions / (kSimdWidth / 2); + const int8x8_t kZero = {0}; + const auto in = reinterpret_cast(input); + const auto out = reinterpret_cast(output); + for (IndexType i = 0; i < kNumChunks; ++i) { + int16x8_t shifted; + const auto pack = reinterpret_cast(&shifted); + pack[0] = vqshrn_n_s32(in[i * 2 + 0], kWeightScaleBits); + pack[1] = vqshrn_n_s32(in[i * 2 + 1], kWeightScaleBits); + out[i] = vmax_s8(vqmovn_s16(shifted), kZero); + } + constexpr IndexType kStart = kNumChunks * (kSimdWidth / 2); + #else + constexpr IndexType kStart = 0; + #endif + + for (IndexType i = kStart; i < kInputDimensions; ++i) { + output[i] = static_cast( + std::max(0, std::min(127, input[i] >> kWeightScaleBits))); + } + return output; + } + + private: + PreviousLayer previous_layer_; + }; + +} // namespace Eval::NNUE::Layers + +#endif // NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED diff --git a/src/nnue/layers/input_slice.h b/src/nnue/layers/input_slice.h new file mode 100644 index 00000000000..afca14c82c8 --- /dev/null +++ b/src/nnue/layers/input_slice.h @@ -0,0 +1,68 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// NNUE evaluation function layer InputSlice definition + +#ifndef NNUE_LAYERS_INPUT_SLICE_H_INCLUDED +#define NNUE_LAYERS_INPUT_SLICE_H_INCLUDED + +#include "../nnue_common.h" + +namespace Eval::NNUE::Layers { + +// Input layer +template +class InputSlice { + public: + // Need to maintain alignment + static_assert(Offset % kMaxSimdWidth == 0, ""); + + // Output type + using OutputType = TransformedFeatureType; + + // Output dimensionality + static constexpr IndexType kOutputDimensions = OutputDimensions; + + // Size of forward propagation buffer used from the input layer to this layer + static constexpr std::size_t kBufferSize = 0; + + // Hash value embedded in the evaluation file + static constexpr std::uint32_t GetHashValue() { + std::uint32_t hash_value = 0xEC42E90Du; + hash_value ^= kOutputDimensions ^ (Offset << 10); + return hash_value; + } + + // Read network parameters + bool ReadParameters(std::istream& /*stream*/) { + return true; + } + + // Forward propagation + const OutputType* Propagate( + const TransformedFeatureType* transformed_features, + char* /*buffer*/) const { + return transformed_features + Offset; + } + + private: +}; + +} // namespace Layers + +#endif // #ifndef NNUE_LAYERS_INPUT_SLICE_H_INCLUDED diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h new file mode 100644 index 00000000000..2a354a3c284 --- /dev/null +++ b/src/nnue/nnue_accumulator.h @@ -0,0 +1,39 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// Class for difference calculation of NNUE evaluation function + +#ifndef NNUE_ACCUMULATOR_H_INCLUDED +#define NNUE_ACCUMULATOR_H_INCLUDED + +#include "nnue_architecture.h" + +namespace Eval::NNUE { + + // Class that holds the result of affine transformation of input features + struct alignas(32) Accumulator { + std::int16_t + accumulation[2][kRefreshTriggers.size()][kTransformedFeatureDimensions]; + Value score; + bool computed_accumulation; + bool computed_score; + }; + +} // namespace Eval::NNUE + +#endif // NNUE_ACCUMULATOR_H_INCLUDED diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h new file mode 100644 index 00000000000..91cdc4bda23 --- /dev/null +++ b/src/nnue/nnue_architecture.h @@ -0,0 +1,38 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// Input features and network structure used in NNUE evaluation function + +#ifndef NNUE_ARCHITECTURE_H_INCLUDED +#define NNUE_ARCHITECTURE_H_INCLUDED + +// Defines the network structure +#include "architectures/halfkp_256x2-32-32.h" + +namespace Eval::NNUE { + + static_assert(kTransformedFeatureDimensions % kMaxSimdWidth == 0, ""); + static_assert(Network::kOutputDimensions == 1, ""); + static_assert(std::is_same::value, ""); + + // Trigger for full calculation instead of difference calculation + constexpr auto kRefreshTriggers = RawFeatures::kRefreshTriggers; + +} // namespace Eval::NNUE + +#endif // #ifndef NNUE_ARCHITECTURE_H_INCLUDED diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h new file mode 100644 index 00000000000..972ef3e50c6 --- /dev/null +++ b/src/nnue/nnue_common.h @@ -0,0 +1,77 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// Constants used in NNUE evaluation function + +#ifndef NNUE_COMMON_H_INCLUDED +#define NNUE_COMMON_H_INCLUDED + +#if defined(USE_AVX2) +#include + +#elif defined(USE_SSE41) +#include + +#elif defined(USE_SSSE3) +#include + +#elif defined(USE_SSE2) +#include + +#elif defined(USE_NEON) +#include +#endif + +namespace Eval::NNUE { + + // Version of the evaluation file + constexpr std::uint32_t kVersion = 0x7AF32F16u; + + // Constant used in evaluation value calculation + constexpr int FV_SCALE = 16; + constexpr int kWeightScaleBits = 6; + + // Size of cache line (in bytes) + constexpr std::size_t kCacheLineSize = 64; + + // SIMD width (in bytes) + #if defined(USE_AVX2) + constexpr std::size_t kSimdWidth = 32; + + #elif defined(USE_SSE2) + constexpr std::size_t kSimdWidth = 16; + + #elif defined(USE_NEON) + constexpr std::size_t kSimdWidth = 16; + #endif + + constexpr std::size_t kMaxSimdWidth = 32; + + // Type of input feature after conversion + using TransformedFeatureType = std::uint8_t; + using IndexType = std::uint32_t; + + // Round n up to be a multiple of base + template + constexpr IntType CeilToMultiple(IntType n, IntType base) { + return (n + base - 1) / base * base; + } + +} // namespace Eval::NNUE + +#endif // #ifndef NNUE_COMMON_H_INCLUDED diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h new file mode 100644 index 00000000000..1cfebbe4cbe --- /dev/null +++ b/src/nnue/nnue_feature_transformer.h @@ -0,0 +1,355 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// A class that converts the input features of the NNUE evaluation function + +#ifndef NNUE_FEATURE_TRANSFORMER_H_INCLUDED +#define NNUE_FEATURE_TRANSFORMER_H_INCLUDED + +#include "nnue_common.h" +#include "nnue_architecture.h" +#include "features/index_list.h" + +#include // std::memset() + +namespace Eval::NNUE { + + // Input feature converter + class FeatureTransformer { + + private: + // Number of output dimensions for one side + static constexpr IndexType kHalfDimensions = kTransformedFeatureDimensions; + + public: + // Output type + using OutputType = TransformedFeatureType; + + // Number of input/output dimensions + static constexpr IndexType kInputDimensions = RawFeatures::kDimensions; + static constexpr IndexType kOutputDimensions = kHalfDimensions * 2; + + // Size of forward propagation buffer + static constexpr std::size_t kBufferSize = + kOutputDimensions * sizeof(OutputType); + + // Hash value embedded in the evaluation file + static constexpr std::uint32_t GetHashValue() { + return RawFeatures::kHashValue ^ kOutputDimensions; + } + + // Read network parameters + bool ReadParameters(std::istream& stream) { + stream.read(reinterpret_cast(biases_), + kHalfDimensions * sizeof(BiasType)); + stream.read(reinterpret_cast(weights_), + kHalfDimensions * kInputDimensions * sizeof(WeightType)); + return !stream.fail(); + } + + // Proceed with the difference calculation if possible + bool UpdateAccumulatorIfPossible(const Position& pos) const { + const auto now = pos.state(); + if (now->accumulator.computed_accumulation) { + return true; + } + const auto prev = now->previous; + if (prev && prev->accumulator.computed_accumulation) { + UpdateAccumulator(pos); + return true; + } + return false; + } + + // Convert input features + void Transform(const Position& pos, OutputType* output, bool refresh) const { + if (refresh || !UpdateAccumulatorIfPossible(pos)) { + RefreshAccumulator(pos); + } + const auto& accumulation = pos.state()->accumulator.accumulation; + + #if defined(USE_AVX2) + constexpr IndexType kNumChunks = kHalfDimensions / kSimdWidth; + constexpr int kControl = 0b11011000; + const __m256i kZero = _mm256_setzero_si256(); + + #elif defined(USE_SSSE3) + constexpr IndexType kNumChunks = kHalfDimensions / kSimdWidth; + + #ifdef USE_SSE41 + const __m128i kZero = _mm_setzero_si128(); + #else + const __m128i k0x80s = _mm_set1_epi8(-128); + #endif + + #elif defined(USE_NEON) + constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); + const int8x8_t kZero = {0}; + #endif + + const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()}; + for (IndexType p = 0; p < 2; ++p) { + const IndexType offset = kHalfDimensions * p; + + #if defined(USE_AVX2) + auto out = reinterpret_cast<__m256i*>(&output[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + __m256i sum0 = + + #if defined(__MINGW32__) || defined(__MINGW64__) + // HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Because the binary + // compiled with g++ in MSYS2 crashes here because the output memory is not aligned + // even though alignas is specified. + _mm256_loadu_si256 + #else + _mm256_load_si256 + #endif + + (&reinterpret_cast( + accumulation[perspectives[p]][0])[j * 2 + 0]); + __m256i sum1 = + + #if defined(__MINGW32__) || defined(__MINGW64__) + _mm256_loadu_si256 + #else + _mm256_load_si256 + #endif + + (&reinterpret_cast( + accumulation[perspectives[p]][0])[j * 2 + 1]); + + #if defined(__MINGW32__) || defined(__MINGW64__) + _mm256_storeu_si256 + #else + _mm256_store_si256 + #endif + + (&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8( + _mm256_packs_epi16(sum0, sum1), kZero), kControl)); + } + + #elif defined(USE_SSSE3) + auto out = reinterpret_cast<__m128i*>(&output[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + __m128i sum0 = _mm_load_si128(&reinterpret_cast( + accumulation[perspectives[p]][0])[j * 2 + 0]); + __m128i sum1 = _mm_load_si128(&reinterpret_cast( + accumulation[perspectives[p]][0])[j * 2 + 1]); + const __m128i packedbytes = _mm_packs_epi16(sum0, sum1); + + _mm_store_si128(&out[j], + + #ifdef USE_SSE41 + _mm_max_epi8(packedbytes, kZero) + #else + _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s) + #endif + + ); + } + + #elif defined(USE_NEON) + const auto out = reinterpret_cast(&output[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + int16x8_t sum = reinterpret_cast( + accumulation[perspectives[p]][0])[j]; + out[j] = vmax_s8(vqmovn_s16(sum), kZero); + } + + #else + for (IndexType j = 0; j < kHalfDimensions; ++j) { + BiasType sum = accumulation[static_cast(perspectives[p])][0][j]; + output[offset + j] = static_cast( + std::max(0, std::min(127, sum))); + } + #endif + + } + } + + private: + // Calculate cumulative value without using difference calculation + void RefreshAccumulator(const Position& pos) const { + auto& accumulator = pos.state()->accumulator; + IndexType i = 0; + Features::IndexList active_indices[2]; + RawFeatures::AppendActiveIndices(pos, kRefreshTriggers[i], + active_indices); + for (Color perspective : { WHITE, BLACK }) { + std::memcpy(accumulator.accumulation[perspective][i], biases_, + kHalfDimensions * sizeof(BiasType)); + for (const auto index : active_indices[perspective]) { + const IndexType offset = kHalfDimensions * index; + + #if defined(USE_AVX2) + auto accumulation = reinterpret_cast<__m256i*>( + &accumulator.accumulation[perspective][i][0]); + auto column = reinterpret_cast(&weights_[offset]); + constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); + for (IndexType j = 0; j < kNumChunks; ++j) { + #if defined(__MINGW32__) || defined(__MINGW64__) + _mm256_storeu_si256(&accumulation[j], _mm256_add_epi16(_mm256_loadu_si256(&accumulation[j]), column[j])); + #else + accumulation[j] = _mm256_add_epi16(accumulation[j], column[j]); + #endif + } + + #elif defined(USE_SSE2) + auto accumulation = reinterpret_cast<__m128i*>( + &accumulator.accumulation[perspective][i][0]); + auto column = reinterpret_cast(&weights_[offset]); + constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); + for (IndexType j = 0; j < kNumChunks; ++j) { + accumulation[j] = _mm_add_epi16(accumulation[j], column[j]); + } + + #elif defined(USE_NEON) + auto accumulation = reinterpret_cast( + &accumulator.accumulation[perspective][i][0]); + auto column = reinterpret_cast(&weights_[offset]); + constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); + for (IndexType j = 0; j < kNumChunks; ++j) { + accumulation[j] = vaddq_s16(accumulation[j], column[j]); + } + + #else + for (IndexType j = 0; j < kHalfDimensions; ++j) { + accumulator.accumulation[perspective][i][j] += weights_[offset + j]; + } + #endif + + } + } + + accumulator.computed_accumulation = true; + accumulator.computed_score = false; + } + + // Calculate cumulative value using difference calculation + void UpdateAccumulator(const Position& pos) const { + const auto prev_accumulator = pos.state()->previous->accumulator; + auto& accumulator = pos.state()->accumulator; + IndexType i = 0; + Features::IndexList removed_indices[2], added_indices[2]; + bool reset[2]; + RawFeatures::AppendChangedIndices(pos, kRefreshTriggers[i], + removed_indices, added_indices, reset); + for (Color perspective : { WHITE, BLACK }) { + + #if defined(USE_AVX2) + constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); + auto accumulation = reinterpret_cast<__m256i*>( + &accumulator.accumulation[perspective][i][0]); + + #elif defined(USE_SSE2) + constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); + auto accumulation = reinterpret_cast<__m128i*>( + &accumulator.accumulation[perspective][i][0]); + + #elif defined(USE_NEON) + constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); + auto accumulation = reinterpret_cast( + &accumulator.accumulation[perspective][i][0]); + #endif + + if (reset[perspective]) { + std::memcpy(accumulator.accumulation[perspective][i], biases_, + kHalfDimensions * sizeof(BiasType)); + } else { + std::memcpy(accumulator.accumulation[perspective][i], + prev_accumulator.accumulation[perspective][i], + kHalfDimensions * sizeof(BiasType)); + // Difference calculation for the deactivated features + for (const auto index : removed_indices[perspective]) { + const IndexType offset = kHalfDimensions * index; + + #if defined(USE_AVX2) + auto column = reinterpret_cast(&weights_[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + accumulation[j] = _mm256_sub_epi16(accumulation[j], column[j]); + } + + #elif defined(USE_SSE2) + auto column = reinterpret_cast(&weights_[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + accumulation[j] = _mm_sub_epi16(accumulation[j], column[j]); + } + + #elif defined(USE_NEON) + auto column = reinterpret_cast(&weights_[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + accumulation[j] = vsubq_s16(accumulation[j], column[j]); + } + + #else + for (IndexType j = 0; j < kHalfDimensions; ++j) { + accumulator.accumulation[perspective][i][j] -= + weights_[offset + j]; + } + #endif + + } + } + { // Difference calculation for the activated features + for (const auto index : added_indices[perspective]) { + const IndexType offset = kHalfDimensions * index; + + #if defined(USE_AVX2) + auto column = reinterpret_cast(&weights_[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + accumulation[j] = _mm256_add_epi16(accumulation[j], column[j]); + } + + #elif defined(USE_SSE2) + auto column = reinterpret_cast(&weights_[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + accumulation[j] = _mm_add_epi16(accumulation[j], column[j]); + } + + #elif defined(USE_NEON) + auto column = reinterpret_cast(&weights_[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + accumulation[j] = vaddq_s16(accumulation[j], column[j]); + } + + #else + for (IndexType j = 0; j < kHalfDimensions; ++j) { + accumulator.accumulation[perspective][i][j] += + weights_[offset + j]; + } + #endif + + } + } + } + + accumulator.computed_accumulation = true; + accumulator.computed_score = false; + } + + using BiasType = std::int16_t; + using WeightType = std::int16_t; + + alignas(kCacheLineSize) BiasType biases_[kHalfDimensions]; + alignas(kCacheLineSize) + WeightType weights_[kHalfDimensions * kInputDimensions]; + }; + +} // namespace Eval::NNUE + +#endif // #ifndef NNUE_FEATURE_TRANSFORMER_H_INCLUDED diff --git a/src/pawns.cpp b/src/pawns.cpp index 7f8d451a6d4..73682529a25 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/pawns.h b/src/pawns.h index e6098069025..5499826e8d9 100644 --- a/src/pawns.h +++ b/src/pawns.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/position.cpp b/src/position.cpp index 396bff5f545..46e5d78b9ae 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -200,6 +198,9 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th std::fill_n(&pieceList[0][0], sizeof(pieceList) / sizeof(Square), SQ_NONE); st = si; + // Each piece on board gets a unique ID used to track the piece later + PieceId piece_id, next_piece_id = PIECE_ID_ZERO; + ss >> std::noskipws; // 1. Piece placement @@ -213,7 +214,19 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th else if ((idx = PieceToChar.find(token)) != string::npos) { - put_piece(Piece(idx), sq); + auto pc = Piece(idx); + put_piece(pc, sq); + + if (Eval::useNNUE) + { + // Kings get a fixed ID, other pieces get ID in order of placement + piece_id = + (idx == W_KING) ? PIECE_ID_WKING : + (idx == B_KING) ? PIECE_ID_BKING : + next_piece_id++; + evalList.put_piece(piece_id, sq, pc); + } + ++sq; } } @@ -705,6 +718,14 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { ++st->rule50; ++st->pliesFromNull; + // Used by NNUE + st->accumulator.computed_accumulation = false; + st->accumulator.computed_score = false; + PieceId dp0 = PIECE_ID_NONE; + PieceId dp1 = PIECE_ID_NONE; + auto& dp = st->dirtyPiece; + dp.dirty_num = 1; + Color us = sideToMove; Color them = ~us; Square from = from_sq(m); @@ -752,6 +773,16 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { else st->nonPawnMaterial[them] -= PieceValue[MG][captured]; + if (Eval::useNNUE) + { + dp.dirty_num = 2; // 2 pieces moved + dp1 = piece_id_on(capsq); + dp.pieceId[1] = dp1; + dp.old_piece[1] = evalList.piece_with_id(dp1); + evalList.put_piece(dp1, capsq, NO_PIECE); + dp.new_piece[1] = evalList.piece_with_id(dp1); + } + // Update board and piece lists remove_piece(capsq); @@ -787,7 +818,18 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // Move the piece. The tricky Chess960 castling is handled earlier if (type_of(m) != CASTLING) + { + if (Eval::useNNUE) + { + dp0 = piece_id_on(from); + dp.pieceId[0] = dp0; + dp.old_piece[0] = evalList.piece_with_id(dp0); + evalList.put_piece(dp0, to, pc); + dp.new_piece[0] = evalList.piece_with_id(dp0); + } + move_piece(from, to); + } // If the moving piece is a pawn do some special extra work if (type_of(pc) == PAWN) @@ -810,6 +852,13 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { remove_piece(to); put_piece(promotion, to); + if (Eval::useNNUE) + { + dp0 = piece_id_on(to); + evalList.put_piece(dp0, to, promotion); + dp.new_piece[0] = evalList.piece_with_id(dp0); + } + // Update hash keys k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[promotion][to]; st->pawnKey ^= Zobrist::psq[pc][to]; @@ -901,6 +950,12 @@ void Position::undo_move(Move m) { { move_piece(to, from); // Put the piece back at the source square + if (Eval::useNNUE) + { + PieceId dp0 = st->dirtyPiece.pieceId[0]; + evalList.put_piece(dp0, from, pc); + } + if (st->capturedPiece) { Square capsq = to; @@ -917,6 +972,14 @@ void Position::undo_move(Move m) { } put_piece(st->capturedPiece, capsq); // Restore the captured piece + + if (Eval::useNNUE) + { + PieceId dp1 = st->dirtyPiece.pieceId[1]; + assert(evalList.piece_with_id(dp1).from[WHITE] == PS_NONE); + assert(evalList.piece_with_id(dp1).from[BLACK] == PS_NONE); + evalList.put_piece(dp1, capsq, st->capturedPiece); + } } } @@ -938,6 +1001,34 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1); to = relative_square(us, kingSide ? SQ_G1 : SQ_C1); + if (Eval::useNNUE) + { + PieceId dp0, dp1; + auto& dp = st->dirtyPiece; + dp.dirty_num = 2; // 2 pieces moved + + if (Do) + { + dp0 = piece_id_on(from); + dp1 = piece_id_on(rfrom); + dp.pieceId[0] = dp0; + dp.old_piece[0] = evalList.piece_with_id(dp0); + evalList.put_piece(dp0, to, make_piece(us, KING)); + dp.new_piece[0] = evalList.piece_with_id(dp0); + dp.pieceId[1] = dp1; + dp.old_piece[1] = evalList.piece_with_id(dp1); + evalList.put_piece(dp1, rto, make_piece(us, ROOK)); + dp.new_piece[1] = evalList.piece_with_id(dp1); + } + else + { + dp0 = piece_id_on(to); + dp1 = piece_id_on(rto); + evalList.put_piece(dp0, from, make_piece(us, KING)); + evalList.put_piece(dp1, rfrom, make_piece(us, ROOK)); + } + } + // Remove both pieces first since squares could overlap in Chess960 remove_piece(Do ? from : to); remove_piece(Do ? rfrom : rto); @@ -955,7 +1046,14 @@ void Position::do_null_move(StateInfo& newSt) { assert(!checkers()); assert(&newSt != st); - std::memcpy(&newSt, st, sizeof(StateInfo)); + if (Eval::useNNUE) + { + std::memcpy(&newSt, st, sizeof(StateInfo)); + st->accumulator.computed_score = false; + } + else + std::memcpy(&newSt, st, offsetof(StateInfo, accumulator)); + newSt.previous = st; st = &newSt; diff --git a/src/position.h b/src/position.h index 8cfa39201e4..a77050eb5b5 100644 --- a/src/position.h +++ b/src/position.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -27,8 +25,11 @@ #include #include "bitboard.h" +#include "evaluate.h" #include "types.h" +#include "nnue/nnue_accumulator.h" + /// StateInfo struct stores information needed to restore a Position object to /// its previous state when we retract a move. Whenever a move is made on the @@ -54,6 +55,10 @@ struct StateInfo { Bitboard pinners[COLOR_NB]; Bitboard checkSquares[PIECE_TYPE_NB]; int repetition; + + // Used by NNUE + Eval::NNUE::Accumulator accumulator; + DirtyPiece dirtyPiece; }; @@ -163,6 +168,10 @@ class Position { bool pos_is_ok() const; void flip(); + // Used by NNUE + StateInfo* state() const; + const EvalList* eval_list() const; + private: // Initialization helpers (used while setting up a position) void set_castling_right(Color c, Square rfrom); @@ -176,6 +185,9 @@ class Position { template void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto); + // ID of a piece on a given square + PieceId piece_id_on(Square sq) const; + // Data members Piece board[SQUARE_NB]; Bitboard byTypeBB[PIECE_TYPE_NB]; @@ -192,6 +204,9 @@ class Position { Thread* thisThread; StateInfo* st; bool chess960; + + // List of pieces used in NNUE evaluation function + EvalList evalList; }; namespace PSQT { @@ -426,4 +441,25 @@ inline void Position::do_move(Move m, StateInfo& newSt) { do_move(m, newSt, gives_check(m)); } +inline StateInfo* Position::state() const { + + return st; +} + +inline const EvalList* Position::eval_list() const { + + return &evalList; +} + +inline PieceId Position::piece_id_on(Square sq) const +{ + + assert(piece_on(sq) != NO_PIECE); + + PieceId pid = evalList.piece_id_list[sq]; + assert(is_ok(pid)); + + return pid; +} + #endif // #ifndef POSITION_H_INCLUDED diff --git a/src/psqt.cpp b/src/psqt.cpp index 5e8dd2c7229..eb36e75e903 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/search.cpp b/src/search.cpp index 91ac60ad4b2..d1dc4489133 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -227,6 +225,8 @@ void MainThread::search() { Time.init(Limits, us, rootPos.game_ply()); TT.new_search(); + Eval::verify_NNUE(); + if (rootMoves.empty()) { rootMoves.emplace_back(MOVE_NONE); diff --git a/src/search.h b/src/search.h index 3e855c8bf34..2554f3fbd8a 100644 --- a/src/search.h +++ b/src/search.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 95d58945d72..20215b96d1f 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1,7 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (c) 2013 Ronald de Man - Copyright (C) 2016-2020 Marco Costalba, Lucas Braesch + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/syzygy/tbprobe.h b/src/syzygy/tbprobe.h index df3ca4feccc..b998989b3de 100644 --- a/src/syzygy/tbprobe.h +++ b/src/syzygy/tbprobe.h @@ -1,7 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (c) 2013 Ronald de Man - Copyright (C) 2016-2020 Marco Costalba, Lucas Braesch + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/thread.cpp b/src/thread.cpp index a0ee2b252fe..44aea14e6e0 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/thread.h b/src/thread.h index a69e1d10e45..46da1e348d1 100644 --- a/src/thread.h +++ b/src/thread.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/thread_win32_osx.h b/src/thread_win32_osx.h index 0ef5c98132a..c4b55a4812b 100644 --- a/src/thread_win32_osx.h +++ b/src/thread_win32_osx.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/timeman.cpp b/src/timeman.cpp index 546eadd29e7..df4ba9b23b9 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/timeman.h b/src/timeman.h index 9301dc946f7..5ad72b32ff8 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/tt.cpp b/src/tt.cpp index 34590903676..d494c27d4ec 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/tt.h b/src/tt.h index e18db8cef7b..c177ca52deb 100644 --- a/src/tt.h +++ b/src/tt.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/tune.cpp b/src/tune.cpp index c1b1c76bcc1..e94f67f816f 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/tune.h b/src/tune.h index 27c3f961be4..1489fa328c0 100644 --- a/src/tune.h +++ b/src/tune.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2017 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/types.h b/src/types.h index c1598561812..379859f731e 100644 --- a/src/types.h +++ b/src/types.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -203,6 +201,22 @@ enum Piece { PIECE_NB = 16 }; +// An ID used to track the pieces. Max. 32 pieces on board. +enum PieceId { + PIECE_ID_ZERO = 0, + PIECE_ID_KING = 30, + PIECE_ID_WKING = 30, + PIECE_ID_BKING = 31, + PIECE_ID_NONE = 32 +}; + +inline PieceId operator++(PieceId& d, int) { + + PieceId x = d; + d = PieceId(int(d) + 1); + return x; +} + constexpr Value PieceValue[PHASE_NB][PIECE_NB] = { { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, VALUE_ZERO, VALUE_ZERO }, @@ -232,7 +246,8 @@ enum Square : int { SQ_A8, SQ_B8, SQ_C8, SQ_D8, SQ_E8, SQ_F8, SQ_G8, SQ_H8, SQ_NONE, - SQUARE_NB = 64 + SQUARE_ZERO = 0, + SQUARE_NB = 64 }; enum Direction : int { @@ -255,6 +270,94 @@ enum Rank : int { RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB }; +// unique number for each piece type on each square +enum PieceSquare : uint32_t { + PS_NONE = 0, + PS_W_PAWN = 1, + PS_B_PAWN = 1 * SQUARE_NB + 1, + PS_W_KNIGHT = 2 * SQUARE_NB + 1, + PS_B_KNIGHT = 3 * SQUARE_NB + 1, + PS_W_BISHOP = 4 * SQUARE_NB + 1, + PS_B_BISHOP = 5 * SQUARE_NB + 1, + PS_W_ROOK = 6 * SQUARE_NB + 1, + PS_B_ROOK = 7 * SQUARE_NB + 1, + PS_W_QUEEN = 8 * SQUARE_NB + 1, + PS_B_QUEEN = 9 * SQUARE_NB + 1, + PS_W_KING = 10 * SQUARE_NB + 1, + PS_END = PS_W_KING, // pieces without kings (pawns included) + PS_B_KING = 11 * SQUARE_NB + 1, + PS_END2 = 12 * SQUARE_NB + 1 +}; + +struct ExtPieceSquare { + PieceSquare from[COLOR_NB]; +}; + +// Array for finding the PieceSquare corresponding to the piece on the board +extern ExtPieceSquare kpp_board_index[PIECE_NB]; + +constexpr bool is_ok(PieceId pid); +constexpr Square rotate180(Square sq); + +// Structure holding which tracked piece (PieceId) is where (PieceSquare) +class EvalList { + +public: + // Max. number of pieces without kings is 30 but must be a multiple of 4 in AVX2 + static const int MAX_LENGTH = 32; + + // Array that holds the piece id for the pieces on the board + PieceId piece_id_list[SQUARE_NB]; + + // List of pieces, separate from White and Black POV + PieceSquare* piece_list_fw() const { return const_cast(pieceListFw); } + PieceSquare* piece_list_fb() const { return const_cast(pieceListFb); } + + // Place the piece pc with piece_id on the square sq on the board + void put_piece(PieceId piece_id, Square sq, Piece pc) + { + assert(is_ok(piece_id)); + if (pc != NO_PIECE) + { + pieceListFw[piece_id] = PieceSquare(kpp_board_index[pc].from[WHITE] + sq); + pieceListFb[piece_id] = PieceSquare(kpp_board_index[pc].from[BLACK] + rotate180(sq)); + piece_id_list[sq] = piece_id; + } + else + { + pieceListFw[piece_id] = PS_NONE; + pieceListFb[piece_id] = PS_NONE; + piece_id_list[sq] = piece_id; + } + } + + // Convert the specified piece_id piece to ExtPieceSquare type and return it + ExtPieceSquare piece_with_id(PieceId piece_id) const + { + ExtPieceSquare eps; + eps.from[WHITE] = pieceListFw[piece_id]; + eps.from[BLACK] = pieceListFb[piece_id]; + return eps; + } + +private: + PieceSquare pieceListFw[MAX_LENGTH]; + PieceSquare pieceListFb[MAX_LENGTH]; +}; + +// For differential evaluation of pieces that changed since last turn +struct DirtyPiece { + + // Number of changed pieces + int dirty_num; + + // The ids of changed pieces, max. 2 pieces can change in one move + PieceId pieceId[2]; + + // What changed from the piece with that piece number + ExtPieceSquare old_piece[2]; + ExtPieceSquare new_piece[2]; +}; /// Score enum stores a middlegame and an endgame value in a single integer (enum). /// The least significant 16 bits are used to store the middlegame value and the @@ -280,10 +383,10 @@ inline Value mg_value(Score s) { } #define ENABLE_BASE_OPERATORS_ON(T) \ -constexpr T operator+(T d1, int d2) { return T(int(d1) + d2); } \ -constexpr T operator-(T d1, int d2) { return T(int(d1) - d2); } \ +constexpr T operator+(T d1, int d2) { return T(int(d1) + d2); } \ +constexpr T operator-(T d1, int d2) { return T(int(d1) - d2); } \ constexpr T operator-(T d) { return T(-int(d)); } \ -inline T& operator+=(T& d1, int d2) { return d1 = d1 + d2; } \ +inline T& operator+=(T& d1, int d2) { return d1 = d1 + d2; } \ inline T& operator-=(T& d1, int d2) { return d1 = d1 - d2; } #define ENABLE_INCR_OPERATORS_ON(T) \ @@ -302,6 +405,9 @@ inline T& operator/=(T& d, int i) { return d = T(int(d) / i); } ENABLE_FULL_OPERATORS_ON(Value) ENABLE_FULL_OPERATORS_ON(Direction) +ENABLE_INCR_OPERATORS_ON(Piece) +ENABLE_INCR_OPERATORS_ON(PieceSquare) +ENABLE_INCR_OPERATORS_ON(PieceId) ENABLE_INCR_OPERATORS_ON(PieceType) ENABLE_INCR_OPERATORS_ON(Square) ENABLE_INCR_OPERATORS_ON(File) @@ -390,6 +496,10 @@ inline Color color_of(Piece pc) { return Color(pc >> 3); } +constexpr bool is_ok(PieceId pid) { + return pid < PIECE_ID_NONE; +} + constexpr bool is_ok(Square s) { return s >= SQ_A1 && s <= SQ_H8; } @@ -426,6 +536,11 @@ constexpr Square to_sq(Move m) { return Square(m & 0x3F); } +// Return relative square when turning the board 180 degrees +constexpr Square rotate180(Square sq) { + return (Square)(sq ^ 0x3F); +} + constexpr int from_to(Move m) { return m & 0xFFF; } diff --git a/src/uci.cpp b/src/uci.cpp index bb57c80b5f6..d64863201cc 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -78,6 +76,20 @@ namespace { } } + // trace_eval() prints the evaluation for the current position, consistent with the UCI + // options set so far. + + void trace_eval(Position& pos) { + + StateListPtr states(new std::deque(1)); + Position p; + p.set(pos.fen(), Options["UCI_Chess960"], &states->back(), Threads.main()); + + Eval::verify_NNUE(); + + sync_cout << "\n" << Eval::trace(p) << sync_endl; + } + // setoption() is called when engine receives the "setoption" UCI command. The // function updates the UCI option ("name") to the given value ("value"). @@ -166,7 +178,7 @@ namespace { nodes += Threads.nodes_searched(); } else - sync_cout << "\n" << Eval::trace(pos) << sync_endl; + trace_eval(pos); } else if (token == "setoption") setoption(is); else if (token == "position") position(pos, is, states); @@ -261,7 +273,7 @@ void UCI::loop(int argc, char* argv[]) { 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 == "eval") trace_eval(pos); else if (token == "compiler") sync_cout << compiler_info() << sync_endl; else sync_cout << "Unknown command: " << cmd << sync_endl; diff --git a/src/uci.h b/src/uci.h index ad954d9f712..eb0b390ba92 100644 --- a/src/uci.h +++ b/src/uci.h @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/ucioption.cpp b/src/ucioption.cpp index ef54ef4e5be..788aed17c21 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -1,8 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad - Copyright (C) 2015-2020 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad + Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -42,7 +40,8 @@ void on_hash_size(const Option& o) { TT.resize(size_t(o)); } void on_logger(const Option& o) { start_logger(o); } void on_threads(const Option& o) { Threads.set(size_t(o)); } void on_tb_path(const Option& o) { Tablebases::init(o); } - +void on_use_NNUE(const Option& ) { Eval::init_NNUE(); } +void on_eval_file(const Option& ) { Eval::init_NNUE(); } /// Our case insensitive less() function as required by UCI protocol bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const { @@ -79,6 +78,8 @@ void init(OptionsMap& o) { o["SyzygyProbeDepth"] << Option(1, 1, 100); o["Syzygy50MoveRule"] << Option(true); o["SyzygyProbeLimit"] << Option(7, 0, 7); + o["Use NNUE"] << Option(false, on_use_NNUE); + o["EvalFile"] << Option("nn-97f742aaefcd.nnue", on_eval_file); } From 3dca13a958cd0dfea1cdea91da230c5aac9e322f Mon Sep 17 00:00:00 2001 From: MJZ1977 <37274752+MJZ1977@users.noreply.github.com> Date: Thu, 6 Aug 2020 17:39:10 +0200 Subject: [PATCH 0291/1766] NNUE evaluation threshold The idea is to use NNUE only on quite balanced material positions. This bring a big speedup on research since NNUE eval is slower than classical eval for most of the hardwares and specially on unbalanced positions with LazyEval. STC: https://tests.stockfishchess.org/tests/view/5f2c2680b3ebe5cbfee85b61 LLR: 2.95 (-2.94,2.94) {-0.50,1.50} Total: 3168 W: 560 L: 400 D: 2208 Ptnml(0-2): 21, 294, 819, 404, 46 LTC: https://tests.stockfishchess.org/tests/view/5f2c2ca6b3ebe5cbfee85b69 LLR: 2.98 (-2.94,2.94) {0.25,1.75} Total: 3200 W: 287 L: 183 D: 2730 Ptnml(0-2): 4, 149, 1191, 251, 5 closes https://github.com/official-stockfish/Stockfish/pull/2916 Bench 4746616 --- src/evaluate.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index f43c62d6871..09496fdcab7 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -107,9 +107,10 @@ using namespace Trace; namespace { // Threshold for lazy and space evaluation - constexpr Value LazyThreshold1 = Value(1400); - constexpr Value LazyThreshold2 = Value(1300); + constexpr Value LazyThreshold1 = Value(1400); + constexpr Value LazyThreshold2 = Value(1300); constexpr Value SpaceThreshold = Value(12222); + constexpr Value NNUEThreshold = Value(500); // KingAttackWeights[PieceType] contains king attack weights by piece type constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 }; @@ -941,9 +942,14 @@ namespace { Value Eval::evaluate(const Position& pos) { if (Eval::useNNUE) - return NNUE::evaluate(pos); - else - return Evaluation(pos).value(); + { + Value balance = pos.non_pawn_material(WHITE) - pos.non_pawn_material(BLACK); + balance += 200 * (pos.count(WHITE) - pos.count(BLACK)); + // Take NNUE eval only on balanced positions + if (abs(balance) < NNUEThreshold) + return NNUE::evaluate(pos); + } + return Evaluation(pos).value(); } /// trace() is like evaluate(), but instead of returning a value, it returns From 8b8412ef87825d8e341e160585307dc89843b7f6 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Fri, 7 Aug 2020 01:08:15 +0200 Subject: [PATCH 0292/1766] Add tempo also to NNUE eval. STC: LLR: 2.93 (-2.94,2.94) {-0.50,1.50} Total: 10608 W: 1507 L: 1358 D: 7743 Ptnml(0-2): 94, 945, 3074, 1100, 91 https://tests.stockfishchess.org/tests/view/5f2c5921b3ebe5cbfee85b8b LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 7536 W: 556 L: 448 D: 6532 Ptnml(0-2): 9, 383, 2881, 481, 14 https://tests.stockfishchess.org/tests/view/5f2c6f4461e3b6af64881e95 closes https://github.com/official-stockfish/Stockfish/pull/2919 Bench: 4746616 --- src/evaluate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 09496fdcab7..015efa487f9 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -947,7 +947,7 @@ Value Eval::evaluate(const Position& pos) { balance += 200 * (pos.count(WHITE) - pos.count(BLACK)); // Take NNUE eval only on balanced positions if (abs(balance) < NNUEThreshold) - return NNUE::evaluate(pos); + return NNUE::evaluate(pos) + Tempo; } return Evaluation(pos).value(); } From af935365e3e528f445c1c0f48bb43b8cf685719c Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Thu, 6 Aug 2020 17:37:54 -0700 Subject: [PATCH 0293/1766] Tuned pawn values Passed STC: https://tests.stockfishchess.org/tests/view/5f2aa49fa5abc164f05e4d1b LLR: 2.95 (-2.94,2.94) {-0.50,1.50} Total: 40888 W: 7977 L: 7726 D: 25185 Ptnml(0-2): 665, 4806, 9333, 4893, 747 Passed LTC: https://tests.stockfishchess.org/tests/view/5f2b1059b3ebe5cbfee85ae7 LLR: 2.98 (-2.94,2.94) {0.25,1.75} Total: 51264 W: 6445 L: 6134 D: 38685 Ptnml(0-2): 328, 4564, 15580, 4789, 371 closes https://github.com/official-stockfish/Stockfish/pull/2920 bench: 4314943 --- src/pawns.cpp | 14 +++++++------- src/types.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index 73682529a25..868d0c8ef0f 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -30,21 +30,21 @@ namespace { #define S(mg, eg) make_score(mg, eg) // Pawn penalties - 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 Backward = S( 8, 27); + constexpr Score Doubled = S(11, 55); + constexpr Score Isolated = S( 5, 17); + constexpr Score WeakLever = S( 2, 54); + constexpr Score WeakUnopposed = S(15, 25); // Bonus for blocked pawns at 5th or 6th rank - constexpr Score BlockedPawn[2] = { S(-11, -4), S(-3, 4) }; + constexpr Score BlockedPawn[2] = { S(-13, -4), S(-4, 3) }; constexpr Score BlockedStorm[RANK_NB] = { S(0, 0), S(0, 0), S(76, 78), S(-10, 15), S(-7, 10), S(-4, 6), S(-1, 2) }; // Connected pawn bonus - constexpr int Connected[RANK_NB] = { 0, 7, 8, 12, 29, 48, 86 }; + constexpr int Connected[RANK_NB] = { 0, 7, 8, 11, 24, 45, 85 }; // Strength of pawn shelter for our king by [distance from edge][rank]. // RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king. diff --git a/src/types.h b/src/types.h index 379859f731e..73da41e28f0 100644 --- a/src/types.h +++ b/src/types.h @@ -178,7 +178,7 @@ enum Value : int { VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY, VALUE_MATED_IN_MAX_PLY = -VALUE_MATE_IN_MAX_PLY, - PawnValueMg = 124, PawnValueEg = 206, + PawnValueMg = 126, PawnValueEg = 208, KnightValueMg = 781, KnightValueEg = 854, BishopValueMg = 825, BishopValueEg = 915, RookValueMg = 1276, RookValueEg = 1380, From 7f336dd59b3b1365943d73ee706a9610e18108bb Mon Sep 17 00:00:00 2001 From: UnaiCorzo Date: Tue, 4 Aug 2020 14:32:52 +0200 Subject: [PATCH 0294/1766] Remove QueenInfiltration STC https://tests.stockfishchess.org/tests/view/5f2955b1a5abc164f05e4c85 LLR: 2.96 (-2.94,2.94) {-1.50,0.50} Total: 29216 W: 5560 L: 5416 D: 18240 Ptnml(0-2): 466, 3329, 6902, 3417, 494 LTC https://tests.stockfishchess.org/tests/view/5f299154a5abc164f05e4ca1 LLR: 2.92 (-2.94,2.94) {-1.50,0.50} Total: 54144 W: 6635 L: 6594 D: 40915 Ptnml(0-2): 372, 4859, 16536, 4966, 339 closes https://github.com/official-stockfish/Stockfish/pull/2910 Bench: 4609008 --- src/evaluate.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 015efa487f9..d20c7b70f1e 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -181,7 +181,6 @@ namespace { constexpr Score MinorBehindPawn = S( 18, 3); constexpr Score PassedFile = S( 11, 8); constexpr Score PawnlessFlank = S( 17, 95); - constexpr Score QueenInfiltration = S( -2, 14); constexpr Score ReachableOutpost = S( 31, 22); constexpr Score RestrictedPiece = S( 7, 7); constexpr Score RookOnKingRing = S( 16, 0); @@ -423,10 +422,6 @@ namespace { Bitboard queenPinners; if (pos.slider_blockers(pos.pieces(Them, ROOK, BISHOP), s, queenPinners)) score -= WeakQueen; - - // Bonus for queen on weak square in enemy camp - if (relative_rank(Us, s) > RANK_4 && (~pe->pawn_attacks_span(Them) & s)) - score += QueenInfiltration; } } if (T) From 615d98da2447e79ceceae205e0cd4e878115acc3 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Wed, 5 Aug 2020 09:29:27 +0200 Subject: [PATCH 0295/1766] Do move legality check before pruning. This alllows to simplify the code because the move counter haven't to be decremented later if a move isn't legal. As a side effect now illegal pruned moves doesn't included anymore in move counter. So slightly less pruning and reductions are done. STC: LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 111016 W: 21106 L: 21077 D: 68833 Ptnml(0-2): 1830, 13083, 25736, 12946, 1913 https://tests.stockfishchess.org/tests/view/5f28816fa5abc164f05e4c26 LTC: LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 39264 W: 4909 L: 4843 D: 29512 Ptnml(0-2): 263, 3601, 11854, 3635, 279 https://tests.stockfishchess.org/tests/view/5f297902a5abc164f05e4c8e closes https://github.com/official-stockfish/Stockfish/pull/2906 Bench: 4390086 --- src/search.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index d1dc4489133..2f83f4f45b0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -986,6 +986,10 @@ namespace { thisThread->rootMoves.begin() + thisThread->pvLast, move)) continue; + // Check for legality + if (!rootNode && !pos.legal(move)) + continue; + ss->moveCount = ++moveCount; if (rootNode && thisThread == Threads.main() && Time.elapsed() > 3000) @@ -1137,13 +1141,6 @@ namespace { // Speculative prefetch as early as possible prefetch(TT.first_entry(pos.key_after(move))); - // Check for legality just before making the move - if (!rootNode && !pos.legal(move)) - { - ss->moveCount = --moveCount; - continue; - } - // Update the current move (this must be done after singular extension search) ss->currentMove = move; ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck] From 857e045ced9e20f202e15d825e47b3ab8241dcef Mon Sep 17 00:00:00 2001 From: Sergio Vieri Date: Fri, 7 Aug 2020 15:15:04 +0800 Subject: [PATCH 0296/1766] Update default net to nn-9931db908a9b.nnue Net created at 20200806-1802 passed STC: https://tests.stockfishchess.org/tests/view/5f2d00b461e3b6af64881f21 LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 6672 W: 1052 L: 898 D: 4722 Ptnml(0-2): 63, 600, 1868, 730, 75 passed LTC: https://tests.stockfishchess.org/tests/view/5f2d052a61e3b6af64881f29 LLR: 2.96 (-2.94,2.94) {0.25,1.75} Total: 7576 W: 573 L: 463 D: 6540 Ptnml(0-2): 8, 392, 2889, 480, 19 closes https://github.com/official-stockfish/Stockfish/pull/2923 Bench: 4390086 --- AUTHORS | 1 + src/ucioption.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 2e080e61dc1..07e07297af8 100644 --- a/AUTHORS +++ b/AUTHORS @@ -151,6 +151,7 @@ Sami Kiminki (skiminki) Sebastian Buchwald (UniQP) Sergei Antonov (saproj) Sergei Ivanov (svivanov72) +Sergio Vieri (sergiovieri) sf-x Shane Booth (shane31) Shawn Varghese (xXH4CKST3RXx) diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 788aed17c21..faeb78aef54 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -79,7 +79,7 @@ void init(OptionsMap& o) { o["Syzygy50MoveRule"] << Option(true); o["SyzygyProbeLimit"] << Option(7, 0, 7); o["Use NNUE"] << Option(false, on_use_NNUE); - o["EvalFile"] << Option("nn-97f742aaefcd.nnue", on_eval_file); + o["EvalFile"] << Option("nn-9931db908a9b.nnue", on_eval_file); } From dc5af66eadf3cbe3c3ef106657e561c1aa8ac97f Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Sat, 8 Aug 2020 08:24:20 +0200 Subject: [PATCH 0297/1766] Tweak futility pruning depth. STC https://tests.stockfishchess.org/tests/view/5f2d237161e3b6af64881f43 LLR: 2.96 (-2.94,2.94) {-0.50,1.50} Total: 12712 W: 1823 L: 1664 D: 9225 Ptnml(0-2): 122, 1166, 3627, 1313, 128 LTC https://tests.stockfishchess.org/tests/view/5f2d473061e3b6af64881f6f LLR: 2.96 (-2.94,2.94) {0.25,1.75} Total: 12104 W: 912 L: 788 D: 10404 Ptnml(0-2): 13, 665, 4582, 769, 23 closes https://github.com/official-stockfish/Stockfish/pull/2930 bench: 4271421 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 2f83f4f45b0..886ed52ca0b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -816,7 +816,7 @@ namespace { // Step 8. Futility pruning: child node (~50 Elo) if ( !PvNode - && depth < 6 + && depth < 8 && eval - futility_margin(depth, improving) >= beta && eval < VALUE_KNOWN_WIN) // Do not return unproven wins return eval; From 5ccff25df2e8fcbee3d4c1428bbc101afa88e700 Mon Sep 17 00:00:00 2001 From: Lolligerhans Date: Fri, 7 Aug 2020 11:24:37 +0200 Subject: [PATCH 0298/1766] Expand outposts to minors shielded by pawns Allow any pawn in front of a minor piece to replace the pawn protection requirement for outposts. +-------+ +-------+ | . . o | | o . . | o Their pawns | . o x | | o . . | x Our pawns | o N . | | x o B | N,B New (reachable) outpost | . . . | | . _ . | _ Reachable square behind a pawn +-------+ +-------+ N outpost B reaches outpost We want outposts to be secured by pawns against major pieces. If a minor is shielded by any pawn from above, it is rarely at the same time protected by our pawn attacks from below. However, the pawn shield in itself offers some degree of protection. A pawn shield will now suffice to replace the pawn protection for the outpost (and reachable outpost) bonus. This effect stacks with the existing "minor behind pawn" bonus. STC https://tests.stockfishchess.org/tests/view/5f2bcd14b3ebe5cbfee85b2c LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 27248 W: 5353 L: 5119 D: 16776 Ptnml(0-2): 462, 3174, 6185, 3274, 529 LTC https://tests.stockfishchess.org/tests/view/5f2bfef5b3ebe5cbfee85b5a LLR: 2.96 (-2.94,2.94) {0.25,1.75} Total: 99432 W: 12580 L: 12130 D: 74722 Ptnml(0-2): 696, 8903, 30049, 9391, 677 Closes #2935 Bench: 4143673 --- src/evaluate.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d20c7b70f1e..47b84ee6284 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -345,7 +345,8 @@ namespace { { // Bonus if the piece is on an outpost square or can reach one // Reduced bonus for knights (BadOutpost) if few relevant targets - bb = OutpostRanks & attackedBy[Us][PAWN] & ~pe->pawn_attacks_span(Them); + bb = OutpostRanks & (attackedBy[Us][PAWN] | shift(pos.pieces(PAWN))) + & ~pe->pawn_attacks_span(Them); Bitboard targets = pos.pieces(Them) & ~pos.pieces(PAWN); if ( Pt == KNIGHT From f4c27cda1a6874550fcbf6cf991b0b9abe43ff39 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Sat, 8 Aug 2020 03:45:08 +0800 Subject: [PATCH 0299/1766] Reintroduce late irreversible move extension Reintroduce vondele's late irreversible move extension for fortress keeping. This was removed when we only had classical eval. Now that we have the NNUE net, it seems that this is useful again. STC: LLR: 2.93 (-2.94,2.94) {-0.50,1.50} Total: 5352 W: 787 L: 653 D: 3912 Ptnml(0-2): 34, 451, 1579, 571, 41 https://tests.stockfishchess.org/tests/view/5f2dc8ad61e3b6af64881ff0 LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 14416 W: 1013 L: 891 D: 12512 Ptnml(0-2): 15, 722, 5623, 822, 26 https://tests.stockfishchess.org/tests/view/5f2e0e3661e3b6af6488201e closes https://github.com/official-stockfish/Stockfish/pull/2936 Bench: 4154696 --- src/search.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 886ed52ca0b..8be96e298ec 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1134,6 +1134,12 @@ namespace { // Castling extension if (type_of(move) == CASTLING) extension = 1; + + // Late irreversible move extension + if ( move == ttMove + && pos.rule50_count() > 80 + && (captureOrPromotion || type_of(movedPiece) == PAWN)) + extension = 2; // Add extension to new depth newDepth += extension; From 910f779eb1f432c3f90fc19c7824840e02cac837 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sat, 8 Aug 2020 05:51:26 +0300 Subject: [PATCH 0300/1766] Do more futility pruning for parent nodes. This patch increases LMRdepth threshold for futility pruning at parent nodes so it can apply more often. With radical change to evaluation approach it seems that search is really far from optimal state, especially it parts that use static evaluation of position. passed STC https://tests.stockfishchess.org/tests/view/5f2da75661e3b6af64881fd0 LLR: 2.93 (-2.94,2.94) {-0.50,1.50} Total: 8744 W: 1305 L: 1156 D: 6283 Ptnml(0-2): 75, 789, 2500, 928, 80 passed LTC https://tests.stockfishchess.org/tests/view/5f2dcb2a61e3b6af64881ff3 LLR: 2.98 (-2.94,2.94) {0.25,1.75} Total: 17728 W: 1256 L: 1117 D: 15355 Ptnml(0-2): 22, 961, 6774, 1070, 37 Bench: 4067325 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 8be96e298ec..4a9bd7de047 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1028,7 +1028,7 @@ namespace { continue; // Futility pruning: parent node (~5 Elo) - if ( lmrDepth < 6 + if ( lmrDepth < 8 && !ss->inCheck && ss->staticEval + 284 + 188 * lmrDepth <= alpha && (*contHist[0])[movedPiece][to_sq(move)] From 23ecf3d5c6ffbcfbe45acd2afcf503929474a4db Mon Sep 17 00:00:00 2001 From: "U-DESKTOP-3900\\Mark" Date: Fri, 7 Aug 2020 19:53:18 -0400 Subject: [PATCH 0301/1766] simplified and increased threshold to switch between NNUE and classical STC https://tests.stockfishchess.org/tests/view/5f2deb1661e3b6af6488200f LLR: 2.96 (-2.94,2.94) {-1.50,0.50} Total: 10376 W: 1481 L: 1359 D: 7536 Ptnml(0-2): 91, 953, 2981, 1069, 94 LTC: https://tests.stockfishchess.org/html/live_elo.html?5f2e0a0461e3b6af64882019 LLR: 2.99 (-2.94,2.94) {-1.50,0.50} Total: 5040 W: 375 L: 315 D: 4350 Ptnml(0-2): 7, 263, 1926, 311, 13 closes https://github.com/official-stockfish/Stockfish/pull/2934 Bench: 4067325 --- src/evaluate.cpp | 7 +++---- src/search.cpp | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 47b84ee6284..1ae6cb3ac56 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -110,7 +110,7 @@ namespace { constexpr Value LazyThreshold1 = Value(1400); constexpr Value LazyThreshold2 = Value(1300); constexpr Value SpaceThreshold = Value(12222); - constexpr Value NNUEThreshold = Value(500); + constexpr Value NNUEThreshold = Value(520); // KingAttackWeights[PieceType] contains king attack weights by piece type constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 }; @@ -939,10 +939,9 @@ Value Eval::evaluate(const Position& pos) { if (Eval::useNNUE) { - Value balance = pos.non_pawn_material(WHITE) - pos.non_pawn_material(BLACK); - balance += 200 * (pos.count(WHITE) - pos.count(BLACK)); + Value v = eg_value(pos.psq_score()); // Take NNUE eval only on balanced positions - if (abs(balance) < NNUEThreshold) + if (abs(v) < NNUEThreshold) return NNUE::evaluate(pos) + Tempo; } return Evaluation(pos).value(); diff --git a/src/search.cpp b/src/search.cpp index 4a9bd7de047..4a993b01754 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1134,7 +1134,7 @@ namespace { // Castling extension if (type_of(move) == CASTLING) extension = 1; - + // Late irreversible move extension if ( move == ttMove && pos.rule50_count() > 80 From 450b60a303b0c59b0cc5dd22d95b9a983dfc4f96 Mon Sep 17 00:00:00 2001 From: mckx00 Date: Sat, 8 Aug 2020 03:07:07 -0700 Subject: [PATCH 0302/1766] Remove unnecessay legality check Possible after the recent reording pos.legal(move) check https://github.com/official-stockfish/Stockfish/pull/2941 No functional change. --- AUTHORS | 1 + src/search.cpp | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index 07e07297af8..21ef3e50971 100644 --- a/AUTHORS +++ b/AUTHORS @@ -79,6 +79,7 @@ Jean Gauthier (OuaisBla) Jean-Francois Romang (jromang) Jekaa Jerry Donald Watson (jerrydonaldwatson) +jjoshua2 Jonathan Calovski (Mysseno) Jonathan Dumale (SFisGOD) Joost VandeVondele (vondele) diff --git a/src/search.cpp b/src/search.cpp index 4a993b01754..e5d18f776f1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1079,8 +1079,7 @@ namespace { /* && ttValue != VALUE_NONE Already implicit in the next condition */ && abs(ttValue) < VALUE_KNOWN_WIN && (tte->bound() & BOUND_LOWER) - && tte->depth() >= depth - 3 - && pos.legal(move)) + && tte->depth() >= depth - 3) { Value singularBeta = ttValue - ((formerPv + 4) * depth) / 2; Depth singularDepth = (depth - 1 + 3 * formerPv) / 2; From 3368d0328591b2741ca32e57cfa0a35a7144fdd1 Mon Sep 17 00:00:00 2001 From: Moez Jellouli <37274752+MJZ1977@users.noreply.github.com> Date: Sat, 8 Aug 2020 12:35:34 +0200 Subject: [PATCH 0303/1766] update Null Move Pruning parameters STC: https://tests.stockfishchess.org/tests/view/5f2dc38561e3b6af64881fec LLR: 2.99 (-2.94,2.94) {-0.50,1.50} Total: 6120 W: 903 L: 758 D: 4459 Ptnml(0-2): 44, 535, 1775, 644, 62 LTC: https://tests.stockfishchess.org/tests/view/5f2dd55f61e3b6af64882003 LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 7424 W: 577 L: 463 D: 6384 Ptnml(0-2): 16, 375, 2824, 473, 24 closes https://github.com/official-stockfish/Stockfish/pull/2942 bench 4107833 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index e5d18f776f1..9cdc7046ef7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -827,7 +827,7 @@ namespace { && (ss-1)->statScore < 23824 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 33 * depth - 33 * improving + 112 * ttPv + 311 + && ss->staticEval >= beta - 28 * depth - 28 * improving + 94 * ttPv + 200 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) From e663bc533020183c0c52eaf877a91422c9c80742 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sat, 8 Aug 2020 17:43:41 +0300 Subject: [PATCH 0304/1766] Do more aggressive futility pruning for captures This patch lines up with other patches which use better eval to produce more aggressive cutoffs based on static evaluation of position, it allows more aggressive futility pruning for captures - so now we will be producing them with bigger evaluation of position, so more often. passed STC https://tests.stockfishchess.org/tests/view/5f2da79e61e3b6af64881fd2 LLR: 3.87 (-2.94,2.94) {-0.50,1.50} Total: 27256 W: 3809 L: 3593 D: 19854 Ptnml(0-2): 221, 2578, 7830, 2762, 237 passed LTC https://tests.stockfishchess.org/tests/view/5f2df92061e3b6af64882012 LLR: 4.97 (-2.94,2.94) {0.25,1.75} Total: 43624 W: 3095 L: 2820 D: 37709 Ptnml(0-2): 66, 2410, 16608, 2639, 89 closes https://github.com/official-stockfish/Stockfish/pull/2946 Bench: 4272280 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 9cdc7046ef7..201cd974d58 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1055,7 +1055,7 @@ namespace { && !(PvNode && abs(bestValue) < 2) && PieceValue[MG][type_of(movedPiece)] >= PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] && !ss->inCheck - && ss->staticEval + 267 + 391 * lmrDepth + && ss->staticEval + 178 + 261 * lmrDepth + PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha) continue; From 6d6267c378aa0aa354e203e5025361d9a4e0d449 Mon Sep 17 00:00:00 2001 From: Guy Vreuls Date: Sat, 8 Aug 2020 12:45:10 +0200 Subject: [PATCH 0305/1766] Parallelize Link Time Optimization for GCC, CLANG and MINGW This patch tries to run multiple LTO threads in parallel, speeding up the build process of optimized builds if the -j make parameter is used. This mitigates the longer linking times of optimized builds since the integration of the NNUE code. Roughly 2x build speedup. I've tried a similar patch some two years ago but it ran into trouble with old compiler versions then. Since we're on the C++17 standard now these old compilers should be obsolete. closes https://github.com/official-stockfish/Stockfish/pull/2943 No functional change. --- src/Makefile | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/Makefile b/src/Makefile index 4741e722bfd..cab7a7e5062 100644 --- a/src/Makefile +++ b/src/Makefile @@ -282,6 +282,9 @@ ifeq ($(COMP),gcc) ifneq ($(KERNEL),Darwin) LDFLAGS += -Wl,--no-as-needed endif + + gccversion = $(shell $(CXX) --version) + gccisclang = $(findstring clang,$(gccversion)) endif ifeq ($(COMP),mingw) @@ -496,18 +499,28 @@ endif ### needs access to the optimization flags. ifeq ($(optimize),yes) ifeq ($(debug), no) - ifeq ($(comp),$(filter $(comp),gcc clang)) + ifeq ($(comp),clang) + CXXFLAGS += -flto=thin + LDFLAGS += $(CXXFLAGS) + +# GCC and CLANG use different methods for parallelizing LTO and CLANG pretends to be +# GCC on some systems. + else ifeq ($(comp),gcc) + ifeq ($(gccisclang),) CXXFLAGS += -flto + LDFLAGS += $(CXXFLAGS) -flto=jobserver + else + CXXFLAGS += -flto=thin LDFLAGS += $(CXXFLAGS) endif # To use LTO and static linking on windows, the tool chain requires a recent gcc: # gcc version 10.1 in msys2 or TDM-GCC version 9.2 are know to work, older might not. # So, only enable it for a cross from Linux by default. - ifeq ($(comp),mingw) + else ifeq ($(comp),mingw) ifeq ($(KERNEL),Linux) CXXFLAGS += -flto - LDFLAGS += $(CXXFLAGS) + LDFLAGS += $(CXXFLAGS) -flto=jobserver endif endif endif @@ -693,7 +706,7 @@ config-sanity: @test "$(comp)" = "gcc" || test "$(comp)" = "icc" || test "$(comp)" = "mingw" || test "$(comp)" = "clang" $(EXE): $(OBJS) - $(CXX) -o $@ $(OBJS) $(LDFLAGS) + +$(CXX) -o $@ $(OBJS) $(LDFLAGS) clang-profile-make: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ From 1949eb8604853e2ad8f85400590e6a1e2ce7e451 Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Sat, 8 Aug 2020 22:03:37 +0200 Subject: [PATCH 0306/1766] Singular extension search tweak Tweak depth. STC https://tests.stockfishchess.org/tests/view/5f2d22ec61e3b6af64881f40 LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 17984 W: 2603 L: 2441 D: 12940 Ptnml(0-2): 133, 1751, 5094, 1849, 165 LTC https://tests.stockfishchess.org/tests/view/5f2d5a6a61e3b6af64881f7f LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 85808 W: 5956 L: 5621 D: 74231 Ptnml(0-2): 149, 4748, 32785, 5063, 159 closes https://github.com/official-stockfish/Stockfish/pull/2950 fixes two README.md typos: fixes https://github.com/official-stockfish/Stockfish/issues/2932 bench: 4022669 --- README.md | 4 ++-- src/search.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f71a8b34722..7b6ddf4cec3 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ Currently, Stockfish has the following UCI options: * #### Use NNUE Toggle between the NNUE and classical evaluation functions. If set to "true", - the network parameters must be availabe to load from file (see also EvalFile). + the network parameters must be available to load from file (see also EvalFile). * #### EvalFile The name of the file of the NNUE evaluation parameters. Depending on the GUI the @@ -138,7 +138,7 @@ Currently, Stockfish has the following UCI options: * #### Debug Log File Write all communication to and from the engine into a text file. -## classical and NNUE evaluation +## Classical and NNUE evaluation Both approaches assign a value to a position that is used in alpha-beta (PVS) search to find the best move. The classical evaluation computes this value as a function diff --git a/src/search.cpp b/src/search.cpp index 201cd974d58..37e3ff22b27 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1072,7 +1072,7 @@ namespace { // 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 + if ( depth >= 7 && move == ttMove && !rootNode && !excludedMove // Avoid recursive singular search From add890a10b8fe03e091520cd0af7383615c6c386 Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Sat, 8 Aug 2020 22:08:40 +0200 Subject: [PATCH 0307/1766] LMR search tweak All credit to Vizvezdenec, the original author of the idea. STC https://tests.stockfishchess.org/tests/view/5f2d606a61e3b6af64881f88 LLR: 2.95 (-2.94,2.94) {-0.50,1.50} Total: 8440 W: 1191 L: 1048 D: 6201 Ptnml(0-2): 59, 754, 2467, 865, 75 LTC https://tests.stockfishchess.org/tests/view/5f2d84ad61e3b6af64881fbd LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 21896 W: 1557 L: 1406 D: 18933 Ptnml(0-2): 33, 1185, 8378, 1302, 50 closes https://github.com/official-stockfish/Stockfish/pull/2951 bench: 4084753 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 37e3ff22b27..0a2519b6250 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1159,7 +1159,7 @@ namespace { // Step 16. Reduced depth search (LMR, ~200 Elo). If the move fails high it will be // re-searched at full depth. if ( depth >= 3 - && moveCount > 1 + 2 * rootNode + && moveCount > 1 + 2 * rootNode + 2 * (PvNode && abs(bestValue) < 2) && (!rootNode || thisThread->best_move_count(move) == 0) && ( !captureOrPromotion || moveCountPruning From d7a26899a973536ab9d3ce4771d8276d1a4dc55c Mon Sep 17 00:00:00 2001 From: Daniel Dugovic Date: Sat, 8 Aug 2020 15:39:29 -0500 Subject: [PATCH 0308/1766] Use fallback implementation for C++ aligned_alloc fixes https://github.com/official-stockfish/Stockfish/issues/2921 closes https://github.com/official-stockfish/Stockfish/pull/2927 No functional change --- src/Makefile | 4 ++-- src/misc.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Makefile b/src/Makefile index cab7a7e5062..b7585a174c6 100644 --- a/src/Makefile +++ b/src/Makefile @@ -354,8 +354,8 @@ endif endif ifeq ($(KERNEL),Darwin) - CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.15 - LDFLAGS += -arch $(arch) -mmacosx-version-min=10.15 + CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.13 + LDFLAGS += -arch $(arch) -mmacosx-version-min=10.13 endif ### Travis CI script uses COMPILER to overwrite CXX diff --git a/src/misc.cpp b/src/misc.cpp index 3d7c75e558f..05f79b450b0 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -321,9 +321,9 @@ void prefetch(void* addr) { /// void* std_aligned_alloc(size_t alignment, size_t size) { -#if defined(__APPLE__) +#if (defined(__APPLE__) && defined(_LIBCPP_HAS_C11_FEATURES)) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) ) return aligned_alloc(alignment, size); -#elif defined(_WIN32) +#elif (defined(_WIN32) || (defined(__APPLE__) && !defined(_LIBCPP_HAS_C11_FEATURES))) return _mm_malloc(size, alignment); #else return std::aligned_alloc(alignment, size); @@ -331,9 +331,9 @@ void* std_aligned_alloc(size_t alignment, size_t size) { } void std_aligned_free(void* ptr) { -#if defined(__APPLE__) +#if (defined(__APPLE__) && defined(_LIBCPP_HAS_C11_FEATURES)) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) ) free(ptr); -#elif defined(_WIN32) +#elif (defined(_WIN32) || (defined(__APPLE__) && !defined(_LIBCPP_HAS_C11_FEATURES))) _mm_free(ptr); #else free(ptr); From 320fa1b2f082a7db67363e468e7e241d7cedcc64 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 9 Aug 2020 11:05:07 +0200 Subject: [PATCH 0309/1766] Improve error message on missing net. small rewording, but also print the download url for the default net. closes https://github.com/official-stockfish/Stockfish/pull/2954 No functional change --- src/evaluate.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 1ae6cb3ac56..a642357e3c4 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -50,9 +50,13 @@ namespace Eval { std::string eval_file = std::string(Options["EvalFile"]); if (useNNUE && eval_file_loaded != eval_file) { - std::cerr << "Use of NNUE evaluation, but the file " << eval_file << " was not loaded successfully. " - << "These network evaluation parameters must be available, compatible with this version of the code. " - << "The UCI option EvalFile might need to specify the full path, including the directory/folder name, to the file." << std::endl; + UCI::OptionsMap defaults; + UCI::init(defaults); + + std::cerr << "NNUE evaluation used, but the network file " << eval_file << " was not loaded successfully. " + << "These network evaluation parameters must be available, and compatible with this version of the code. " + << "The UCI option EvalFile might need to specify the full path, including the directory/folder name, to the file. " + << "The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/"+std::string(defaults["EvalFile"]) << std::endl; std::exit(EXIT_FAILURE); } From cd1bb27dd452f336d434a45131bfbe43f8a8c5b3 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 9 Aug 2020 19:08:47 +0200 Subject: [PATCH 0310/1766] Fix aligned_alloc on MinGW introduced with d7a26899a973536ab9d3ce4771d8276d1a4dc55c closes https://github.com/official-stockfish/Stockfish/pull/2959 No functional change. --- src/misc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index 05f79b450b0..bdd7bccbc1d 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -321,7 +321,7 @@ void prefetch(void* addr) { /// void* std_aligned_alloc(size_t alignment, size_t size) { -#if (defined(__APPLE__) && defined(_LIBCPP_HAS_C11_FEATURES)) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) ) +#if (defined(__APPLE__) && defined(_LIBCPP_HAS_C11_FEATURES)) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) && !defined(_WIN32)) return aligned_alloc(alignment, size); #elif (defined(_WIN32) || (defined(__APPLE__) && !defined(_LIBCPP_HAS_C11_FEATURES))) return _mm_malloc(size, alignment); @@ -331,7 +331,7 @@ void* std_aligned_alloc(size_t alignment, size_t size) { } void std_aligned_free(void* ptr) { -#if (defined(__APPLE__) && defined(_LIBCPP_HAS_C11_FEATURES)) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) ) +#if (defined(__APPLE__) && defined(_LIBCPP_HAS_C11_FEATURES)) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) && !defined(_WIN32)) free(ptr); #elif (defined(_WIN32) || (defined(__APPLE__) && !defined(_LIBCPP_HAS_C11_FEATURES))) _mm_free(ptr); From 2bfde5542919c2ed624b5b62883616e325ccb942 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sun, 9 Aug 2020 21:39:46 +0300 Subject: [PATCH 0311/1766] Adjust NNUE usage based on number of pawns in position The idea of this patch is that positions are usually more complex and hard to evaluate even if there are more pawns. This patch adjusts NNUE threshold usage depending on number of pawns in position, if pawn count is <3 we use the classical evaluation more often, for pawn count = 3 patch the is non-functional, with pawn count > 3 NNUE evaluation is used more often. passed STC https://tests.stockfishchess.org/tests/view/5f2f02d09081672066536b1f LLR: 2.96 (-2.94,2.94) {-0.50,1.50} Total: 36520 W: 5011 L: 4823 D: 26686 Ptnml(0-2): 299, 3482, 10548, 3594, 337 passed LTC https://tests.stockfishchess.org/tests/view/5f2f4c329081672066536b5c LLR: 2.98 (-2.94,2.94) {0.25,1.75} Total: 39272 W: 2630 L: 2433 D: 34209 Ptnml(0-2): 53, 2066, 15218, 2229, 70 closes https://github.com/official-stockfish/Stockfish/pull/2960 bench 4084753 --- src/evaluate.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index a642357e3c4..ce35c6306c4 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -114,7 +114,7 @@ namespace { constexpr Value LazyThreshold1 = Value(1400); constexpr Value LazyThreshold2 = Value(1300); constexpr Value SpaceThreshold = Value(12222); - constexpr Value NNUEThreshold = Value(520); + constexpr Value NNUEThreshold = Value(460); // KingAttackWeights[PieceType] contains king attack weights by piece type constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 }; @@ -945,7 +945,7 @@ Value Eval::evaluate(const Position& pos) { { Value v = eg_value(pos.psq_score()); // Take NNUE eval only on balanced positions - if (abs(v) < NNUEThreshold) + if (abs(v) < NNUEThreshold + 20 * pos.count()) return NNUE::evaluate(pos) + Tempo; } return Evaluation(pos).value(); From a6e89293df5af35931b61d86b6de3872a981c100 Mon Sep 17 00:00:00 2001 From: Dariusz Orzechowski Date: Sun, 9 Aug 2020 14:32:24 -0700 Subject: [PATCH 0312/1766] Avoid special casing for MinGW after some testing, no version of MinGW/gcc has been found where this code is still necessary. Probably older code (pre-c++17?) closes https://github.com/official-stockfish/Stockfish/pull/2891 No functional change --- src/nnue/layers/affine_transform.h | 29 +++-------------- src/nnue/layers/clipped_relu.h | 49 ++++------------------------- src/nnue/nnue_feature_transformer.h | 34 ++------------------ 3 files changed, 14 insertions(+), 98 deletions(-) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index b585bc87819..ecc3008a6cc 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -104,13 +104,8 @@ namespace Eval::NNUE::Layers { __m512i sum = _mm512_setzero_si512(); const auto row = reinterpret_cast(&weights_[offset]); for (IndexType j = 0; j < kNumChunks; ++j) { - - #if defined(__MINGW32__) || defined(__MINGW64__) - __m512i product = _mm512_maddubs_epi16(_mm512_loadu_si512(&input_vector[j]), _mm512_load_si512(&row[j])); - #else - __m512i product = _mm512_maddubs_epi16(_mm512_load_si512(&input_vector[j]), _mm512_load_si512(&row[j])); - #endif - + __m512i product = _mm512_maddubs_epi16( + _mm512_load_si512(&input_vector[j]), _mm512_load_si512(&row[j])); product = _mm512_madd_epi16(product, kOnes); sum = _mm512_add_epi32(sum, product); } @@ -125,12 +120,8 @@ namespace Eval::NNUE::Layers { const auto row_256 = reinterpret_cast(&weights_[offset]); int j = kNumChunks * 2; - #if defined(__MINGW32__) || defined(__MINGW64__) // See HACK comment below in AVX2. - __m256i sum256 = _mm256_maddubs_epi16(_mm256_loadu_si256(&iv_256[j]), _mm256_load_si256(&row_256[j])); - #else - __m256i sum256 = _mm256_maddubs_epi16(_mm256_load_si256(&iv_256[j]), _mm256_load_si256(&row_256[j])); - #endif - + __m256i sum256 = _mm256_maddubs_epi16( + _mm256_load_si256(&iv_256[j]), _mm256_load_si256(&row_256[j])); sum256 = _mm256_madd_epi16(sum256, _mm256_set1_epi16(1)); sum256 = _mm256_hadd_epi32(sum256, sum256); sum256 = _mm256_hadd_epi32(sum256, sum256); @@ -144,17 +135,7 @@ namespace Eval::NNUE::Layers { const auto row = reinterpret_cast(&weights_[offset]); for (IndexType j = 0; j < kNumChunks; ++j) { __m256i product = _mm256_maddubs_epi16( - - #if defined(__MINGW32__) || defined(__MINGW64__) - // HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Because the binary - // compiled with g++ in MSYS2 crashes here because the output memory is not aligned - // even though alignas is specified. - _mm256_loadu_si256 - #else - _mm256_load_si256 - #endif - - (&input_vector[j]), _mm256_load_si256(&row[j])); + _mm256_load_si256(&input_vector[j]), _mm256_load_si256(&row[j])); product = _mm256_madd_epi16(product, kOnes); sum = _mm256_add_epi32(sum, product); } diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index 7ade598f4d2..7e5fcf4ac59 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -74,50 +74,13 @@ namespace Eval::NNUE::Layers { const auto out = reinterpret_cast<__m256i*>(output); for (IndexType i = 0; i < kNumChunks; ++i) { const __m256i words0 = _mm256_srai_epi16(_mm256_packs_epi32( - - #if defined(__MINGW32__) || defined(__MINGW64__) - // HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Because the binary - // compiled with g++ in MSYS2 crashes here because the output memory is not aligned - // even though alignas is specified. - _mm256_loadu_si256 - #else - _mm256_load_si256 - #endif - - (&in[i * 4 + 0]), - - #if defined(__MINGW32__) || defined(__MINGW64__) - _mm256_loadu_si256 - #else - _mm256_load_si256 - #endif - - (&in[i * 4 + 1])), kWeightScaleBits); + _mm256_load_si256(&in[i * 4 + 0]), + _mm256_load_si256(&in[i * 4 + 1])), kWeightScaleBits); const __m256i words1 = _mm256_srai_epi16(_mm256_packs_epi32( - - #if defined(__MINGW32__) || defined(__MINGW64__) - _mm256_loadu_si256 - #else - _mm256_load_si256 - #endif - - (&in[i * 4 + 2]), - - #if defined(__MINGW32__) || defined(__MINGW64__) - _mm256_loadu_si256 - #else - _mm256_load_si256 - #endif - - (&in[i * 4 + 3])), kWeightScaleBits); - - #if defined(__MINGW32__) || defined(__MINGW64__) - _mm256_storeu_si256 - #else - _mm256_store_si256 - #endif - - (&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8( + _mm256_load_si256(&in[i * 4 + 2]), + _mm256_load_si256(&in[i * 4 + 3])), kWeightScaleBits); + _mm256_store_si256( + &out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8( _mm256_packs_epi16(words0, words1), kZero), kOffsets)); } constexpr IndexType kStart = kNumChunks * kSimdWidth; diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 1cfebbe4cbe..f899d761765 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -110,36 +110,12 @@ namespace Eval::NNUE { auto out = reinterpret_cast<__m256i*>(&output[offset]); for (IndexType j = 0; j < kNumChunks; ++j) { __m256i sum0 = - - #if defined(__MINGW32__) || defined(__MINGW64__) - // HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Because the binary - // compiled with g++ in MSYS2 crashes here because the output memory is not aligned - // even though alignas is specified. - _mm256_loadu_si256 - #else - _mm256_load_si256 - #endif - - (&reinterpret_cast( + _mm256_load_si256(&reinterpret_cast( accumulation[perspectives[p]][0])[j * 2 + 0]); __m256i sum1 = - - #if defined(__MINGW32__) || defined(__MINGW64__) - _mm256_loadu_si256 - #else - _mm256_load_si256 - #endif - - (&reinterpret_cast( + _mm256_load_si256(&reinterpret_cast( accumulation[perspectives[p]][0])[j * 2 + 1]); - - #if defined(__MINGW32__) || defined(__MINGW64__) - _mm256_storeu_si256 - #else - _mm256_store_si256 - #endif - - (&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8( + _mm256_store_si256(&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8( _mm256_packs_epi16(sum0, sum1), kZero), kControl)); } @@ -202,11 +178,7 @@ namespace Eval::NNUE { auto column = reinterpret_cast(&weights_[offset]); constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); for (IndexType j = 0; j < kNumChunks; ++j) { - #if defined(__MINGW32__) || defined(__MINGW64__) - _mm256_storeu_si256(&accumulation[j], _mm256_add_epi16(_mm256_loadu_si256(&accumulation[j]), column[j])); - #else accumulation[j] = _mm256_add_epi16(accumulation[j], column[j]); - #endif } #elif defined(USE_SSE2) From 27b593a94477a821f80a041320683f805114d4a3 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 9 Aug 2020 18:11:38 +0200 Subject: [PATCH 0313/1766] Fix a data race for NNUE the stateInfo at the rootPos is no longer read-only, as the NNUE accumulator is part of it. Threads can thus not share this object and need their own copy. tested for no regression https://tests.stockfishchess.org/tests/view/5f3022239081672066536bce LLR: 2.96 (-2.94,2.94) {-1.50,0.50} Total: 52800 W: 6843 L: 6802 D: 39155 Ptnml(0-2): 336, 4646, 16399, 4679, 340 closes https://github.com/official-stockfish/Stockfish/pull/2957 fixes https://github.com/official-stockfish/Stockfish/issues/2933 No functional change --- src/Makefile | 4 ++-- src/thread.cpp | 13 +++++-------- src/thread.h | 1 + 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/Makefile b/src/Makefile index b7585a174c6..571172b29e0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -354,8 +354,8 @@ endif endif ifeq ($(KERNEL),Darwin) - CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.13 - LDFLAGS += -arch $(arch) -mmacosx-version-min=10.13 + CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.14 + LDFLAGS += -arch $(arch) -mmacosx-version-min=10.14 endif ### Travis CI script uses COMPILER to overwrite CXX diff --git a/src/thread.cpp b/src/thread.cpp index 44aea14e6e0..1aa66a81661 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -204,21 +204,18 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states, // We use Position::set() to set root position across threads. But there are // some StateInfo fields (previous, pliesFromNull, capturedPiece) that cannot - // be deduced from a fen string, so set() clears them and to not lose the info - // we need to backup and later restore setupStates->back(). Note that setupStates - // is shared by threads but is accessed in read-only mode. - StateInfo tmp = setupStates->back(); - + // be deduced from a fen string, so set() clears them and they are set from + // setupStates->back() later. The rootState is per thread, earlier states are shared + // since they are read-only. for (Thread* th : *this) { th->nodes = th->tbHits = th->nmpMinPly = th->bestMoveChanges = 0; th->rootDepth = th->completedDepth = 0; th->rootMoves = rootMoves; - th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th); + th->rootPos.set(pos.fen(), pos.is_chess960(), &th->rootState, th); + th->rootState = setupStates->back(); } - setupStates->back() = tmp; - main()->start_searching(); } diff --git a/src/thread.h b/src/thread.h index 46da1e348d1..042bc2e9231 100644 --- a/src/thread.h +++ b/src/thread.h @@ -65,6 +65,7 @@ class Thread { std::atomic nodes, tbHits, bestMoveChanges; Position rootPos; + StateInfo rootState; Search::RootMoves rootMoves; Depth rootDepth, completedDepth; CounterMoveHistory counterMoves; From 651ec3b31ee68db50f38ccd8fcdedbd6673cd9ed Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 10 Aug 2020 07:18:15 +0200 Subject: [PATCH 0314/1766] Revert "Avoid special casing for MinGW" This reverts commit a6e89293df5af35931b61d86b6de3872a981c100. The offending setup has been found as gcc/mingw 7.3 (on Ubuntu 18.04). fixes https://github.com/official-stockfish/Stockfish/issues/2963 closes https://github.com/official-stockfish/Stockfish/issues/2968 No functional change. --- src/nnue/layers/affine_transform.h | 29 ++++++++++++++--- src/nnue/layers/clipped_relu.h | 49 +++++++++++++++++++++++++---- src/nnue/nnue_feature_transformer.h | 34 ++++++++++++++++++-- 3 files changed, 98 insertions(+), 14 deletions(-) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index ecc3008a6cc..b585bc87819 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -104,8 +104,13 @@ namespace Eval::NNUE::Layers { __m512i sum = _mm512_setzero_si512(); const auto row = reinterpret_cast(&weights_[offset]); for (IndexType j = 0; j < kNumChunks; ++j) { - __m512i product = _mm512_maddubs_epi16( - _mm512_load_si512(&input_vector[j]), _mm512_load_si512(&row[j])); + + #if defined(__MINGW32__) || defined(__MINGW64__) + __m512i product = _mm512_maddubs_epi16(_mm512_loadu_si512(&input_vector[j]), _mm512_load_si512(&row[j])); + #else + __m512i product = _mm512_maddubs_epi16(_mm512_load_si512(&input_vector[j]), _mm512_load_si512(&row[j])); + #endif + product = _mm512_madd_epi16(product, kOnes); sum = _mm512_add_epi32(sum, product); } @@ -120,8 +125,12 @@ namespace Eval::NNUE::Layers { const auto row_256 = reinterpret_cast(&weights_[offset]); int j = kNumChunks * 2; - __m256i sum256 = _mm256_maddubs_epi16( - _mm256_load_si256(&iv_256[j]), _mm256_load_si256(&row_256[j])); + #if defined(__MINGW32__) || defined(__MINGW64__) // See HACK comment below in AVX2. + __m256i sum256 = _mm256_maddubs_epi16(_mm256_loadu_si256(&iv_256[j]), _mm256_load_si256(&row_256[j])); + #else + __m256i sum256 = _mm256_maddubs_epi16(_mm256_load_si256(&iv_256[j]), _mm256_load_si256(&row_256[j])); + #endif + sum256 = _mm256_madd_epi16(sum256, _mm256_set1_epi16(1)); sum256 = _mm256_hadd_epi32(sum256, sum256); sum256 = _mm256_hadd_epi32(sum256, sum256); @@ -135,7 +144,17 @@ namespace Eval::NNUE::Layers { const auto row = reinterpret_cast(&weights_[offset]); for (IndexType j = 0; j < kNumChunks; ++j) { __m256i product = _mm256_maddubs_epi16( - _mm256_load_si256(&input_vector[j]), _mm256_load_si256(&row[j])); + + #if defined(__MINGW32__) || defined(__MINGW64__) + // HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Because the binary + // compiled with g++ in MSYS2 crashes here because the output memory is not aligned + // even though alignas is specified. + _mm256_loadu_si256 + #else + _mm256_load_si256 + #endif + + (&input_vector[j]), _mm256_load_si256(&row[j])); product = _mm256_madd_epi16(product, kOnes); sum = _mm256_add_epi32(sum, product); } diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index 7e5fcf4ac59..7ade598f4d2 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -74,13 +74,50 @@ namespace Eval::NNUE::Layers { const auto out = reinterpret_cast<__m256i*>(output); for (IndexType i = 0; i < kNumChunks; ++i) { const __m256i words0 = _mm256_srai_epi16(_mm256_packs_epi32( - _mm256_load_si256(&in[i * 4 + 0]), - _mm256_load_si256(&in[i * 4 + 1])), kWeightScaleBits); + + #if defined(__MINGW32__) || defined(__MINGW64__) + // HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Because the binary + // compiled with g++ in MSYS2 crashes here because the output memory is not aligned + // even though alignas is specified. + _mm256_loadu_si256 + #else + _mm256_load_si256 + #endif + + (&in[i * 4 + 0]), + + #if defined(__MINGW32__) || defined(__MINGW64__) + _mm256_loadu_si256 + #else + _mm256_load_si256 + #endif + + (&in[i * 4 + 1])), kWeightScaleBits); const __m256i words1 = _mm256_srai_epi16(_mm256_packs_epi32( - _mm256_load_si256(&in[i * 4 + 2]), - _mm256_load_si256(&in[i * 4 + 3])), kWeightScaleBits); - _mm256_store_si256( - &out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8( + + #if defined(__MINGW32__) || defined(__MINGW64__) + _mm256_loadu_si256 + #else + _mm256_load_si256 + #endif + + (&in[i * 4 + 2]), + + #if defined(__MINGW32__) || defined(__MINGW64__) + _mm256_loadu_si256 + #else + _mm256_load_si256 + #endif + + (&in[i * 4 + 3])), kWeightScaleBits); + + #if defined(__MINGW32__) || defined(__MINGW64__) + _mm256_storeu_si256 + #else + _mm256_store_si256 + #endif + + (&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8( _mm256_packs_epi16(words0, words1), kZero), kOffsets)); } constexpr IndexType kStart = kNumChunks * kSimdWidth; diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index f899d761765..1cfebbe4cbe 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -110,12 +110,36 @@ namespace Eval::NNUE { auto out = reinterpret_cast<__m256i*>(&output[offset]); for (IndexType j = 0; j < kNumChunks; ++j) { __m256i sum0 = - _mm256_load_si256(&reinterpret_cast( + + #if defined(__MINGW32__) || defined(__MINGW64__) + // HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Because the binary + // compiled with g++ in MSYS2 crashes here because the output memory is not aligned + // even though alignas is specified. + _mm256_loadu_si256 + #else + _mm256_load_si256 + #endif + + (&reinterpret_cast( accumulation[perspectives[p]][0])[j * 2 + 0]); __m256i sum1 = - _mm256_load_si256(&reinterpret_cast( + + #if defined(__MINGW32__) || defined(__MINGW64__) + _mm256_loadu_si256 + #else + _mm256_load_si256 + #endif + + (&reinterpret_cast( accumulation[perspectives[p]][0])[j * 2 + 1]); - _mm256_store_si256(&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8( + + #if defined(__MINGW32__) || defined(__MINGW64__) + _mm256_storeu_si256 + #else + _mm256_store_si256 + #endif + + (&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8( _mm256_packs_epi16(sum0, sum1), kZero), kControl)); } @@ -178,7 +202,11 @@ namespace Eval::NNUE { auto column = reinterpret_cast(&weights_[offset]); constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); for (IndexType j = 0; j < kNumChunks; ++j) { + #if defined(__MINGW32__) || defined(__MINGW64__) + _mm256_storeu_si256(&accumulation[j], _mm256_add_epi16(_mm256_loadu_si256(&accumulation[j]), column[j])); + #else accumulation[j] = _mm256_add_epi16(accumulation[j], column[j]); + #endif } #elif defined(USE_SSE2) From bcdf41dadc8a5f8a23116236a0f449a08b46dc6b Mon Sep 17 00:00:00 2001 From: Sergio Vieri Date: Mon, 10 Aug 2020 08:47:52 +0800 Subject: [PATCH 0315/1766] Update default net to nn-112bb1c8cdb5.nnue First trained net using search eval instead of pv leaf static eval. Net created at: 20200810-0744 passed STC: https://tests.stockfishchess.org/tests/view/5f30995d90816720665373f8 LLR: 2.93 (-2.94,2.94) {-0.50,1.50} Total: 15416 W: 2071 L: 1920 D: 11425 Ptnml(0-2): 123, 1376, 4563, 1519, 127 passed LTC: https://tests.stockfishchess.org/tests/view/5f30a104908167206653742b LLR: 2.93 (-2.94,2.94) {0.25,1.75} Total: 29792 W: 2003 L: 1834 D: 25955 Ptnml(0-2): 50, 1541, 11550, 1700, 55 closes https://github.com/official-stockfish/Stockfish/pull/2966 Bench: 4084753 --- src/ucioption.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ucioption.cpp b/src/ucioption.cpp index faeb78aef54..b0689d6dd36 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -79,7 +79,7 @@ void init(OptionsMap& o) { o["Syzygy50MoveRule"] << Option(true); o["SyzygyProbeLimit"] << Option(7, 0, 7); o["Use NNUE"] << Option(false, on_use_NNUE); - o["EvalFile"] << Option("nn-9931db908a9b.nnue", on_eval_file); + o["EvalFile"] << Option("nn-112bb1c8cdb5.nnue", on_eval_file); } From a54f9011c3bf3581fe7daffef6be2d586e6662c1 Mon Sep 17 00:00:00 2001 From: jjoshua2 Date: Sun, 9 Aug 2020 16:16:04 -0400 Subject: [PATCH 0316/1766] simplying hybrid condition STC https://tests.stockfishchess.org/tests/view/5f3059d1908167206653736b: LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 12520 W: 766 L: 727 D: 11027 Ptnml(0-2): 13, 624, 4949, 659, 15 LTC: https://tests.stockfishchess.org/tests/view/5f30863a90816720665373d1 LLR: 2.94 (-2.94,2.94) {-1.50,0.50} Total: 12520 W: 766 L: 727 D: 11027 Ptnml(0-2): 13, 624, 4949, 659, 15 closes: https://github.com/official-stockfish/Stockfish/pull/2965 Bench: 4084753 --- src/evaluate.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index ce35c6306c4..caab2979d35 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -114,7 +114,7 @@ namespace { constexpr Value LazyThreshold1 = Value(1400); constexpr Value LazyThreshold2 = Value(1300); constexpr Value SpaceThreshold = Value(12222); - constexpr Value NNUEThreshold = Value(460); + constexpr Value NNUEThreshold = Value(575); // KingAttackWeights[PieceType] contains king attack weights by piece type constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 }; @@ -945,7 +945,7 @@ Value Eval::evaluate(const Position& pos) { { Value v = eg_value(pos.psq_score()); // Take NNUE eval only on balanced positions - if (abs(v) < NNUEThreshold + 20 * pos.count()) + if (abs(v) < NNUEThreshold) return NNUE::evaluate(pos) + Tempo; } return Evaluation(pos).value(); From 875183b310a8249922c2155e82cb4cecfae2097e Mon Sep 17 00:00:00 2001 From: mstembera Date: Sun, 9 Aug 2020 23:50:59 -0700 Subject: [PATCH 0317/1766] Workaround using unaligned loads for gcc < 9 despite usage of alignas, the generated (avx2/avx512) code with older compilers needs to use unaligned loads with older gcc (e.g. confirmed crash with gcc 7.3/mingw on abrok). Better performance thus requires gcc >= 9 on hardware supporting avx2/avx512 closes https://github.com/official-stockfish/Stockfish/pull/2969 No functional change --- src/nnue/layers/affine_transform.h | 32 +++---------------- src/nnue/layers/clipped_relu.h | 48 +++-------------------------- src/nnue/nnue_common.h | 21 +++++++++++++ src/nnue/nnue_feature_transformer.h | 42 ++++--------------------- 4 files changed, 36 insertions(+), 107 deletions(-) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index b585bc87819..20ec2f1234b 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -104,13 +104,7 @@ namespace Eval::NNUE::Layers { __m512i sum = _mm512_setzero_si512(); const auto row = reinterpret_cast(&weights_[offset]); for (IndexType j = 0; j < kNumChunks; ++j) { - - #if defined(__MINGW32__) || defined(__MINGW64__) - __m512i product = _mm512_maddubs_epi16(_mm512_loadu_si512(&input_vector[j]), _mm512_load_si512(&row[j])); - #else - __m512i product = _mm512_maddubs_epi16(_mm512_load_si512(&input_vector[j]), _mm512_load_si512(&row[j])); - #endif - + __m512i product = _mm512_maddubs_epi16(_mm512_loadA_si512(&input_vector[j]), _mm512_load_si512(&row[j])); product = _mm512_madd_epi16(product, kOnes); sum = _mm512_add_epi32(sum, product); } @@ -124,13 +118,7 @@ namespace Eval::NNUE::Layers { const auto iv_256 = reinterpret_cast(input); const auto row_256 = reinterpret_cast(&weights_[offset]); int j = kNumChunks * 2; - - #if defined(__MINGW32__) || defined(__MINGW64__) // See HACK comment below in AVX2. - __m256i sum256 = _mm256_maddubs_epi16(_mm256_loadu_si256(&iv_256[j]), _mm256_load_si256(&row_256[j])); - #else - __m256i sum256 = _mm256_maddubs_epi16(_mm256_load_si256(&iv_256[j]), _mm256_load_si256(&row_256[j])); - #endif - + __m256i sum256 = _mm256_maddubs_epi16(_mm256_loadA_si256(&iv_256[j]), _mm256_load_si256(&row_256[j])); sum256 = _mm256_madd_epi16(sum256, _mm256_set1_epi16(1)); sum256 = _mm256_hadd_epi32(sum256, sum256); sum256 = _mm256_hadd_epi32(sum256, sum256); @@ -143,18 +131,7 @@ namespace Eval::NNUE::Layers { __m256i sum = _mm256_setzero_si256(); const auto row = reinterpret_cast(&weights_[offset]); for (IndexType j = 0; j < kNumChunks; ++j) { - __m256i product = _mm256_maddubs_epi16( - - #if defined(__MINGW32__) || defined(__MINGW64__) - // HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Because the binary - // compiled with g++ in MSYS2 crashes here because the output memory is not aligned - // even though alignas is specified. - _mm256_loadu_si256 - #else - _mm256_load_si256 - #endif - - (&input_vector[j]), _mm256_load_si256(&row[j])); + __m256i product = _mm256_maddubs_epi16(_mm256_loadA_si256(&input_vector[j]), _mm256_load_si256(&row[j])); product = _mm256_madd_epi16(product, kOnes); sum = _mm256_add_epi32(sum, product); } @@ -168,8 +145,7 @@ namespace Eval::NNUE::Layers { __m128i sum = _mm_cvtsi32_si128(biases_[i]); const auto row = reinterpret_cast(&weights_[offset]); for (IndexType j = 0; j < kNumChunks; ++j) { - __m128i product = _mm_maddubs_epi16( - _mm_load_si128(&input_vector[j]), _mm_load_si128(&row[j])); + __m128i product = _mm_maddubs_epi16(_mm_load_si128(&input_vector[j]), _mm_load_si128(&row[j])); product = _mm_madd_epi16(product, kOnes); sum = _mm_add_epi32(sum, product); } diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index 7ade598f4d2..13196ec28b4 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -74,50 +74,12 @@ namespace Eval::NNUE::Layers { const auto out = reinterpret_cast<__m256i*>(output); for (IndexType i = 0; i < kNumChunks; ++i) { const __m256i words0 = _mm256_srai_epi16(_mm256_packs_epi32( - - #if defined(__MINGW32__) || defined(__MINGW64__) - // HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Because the binary - // compiled with g++ in MSYS2 crashes here because the output memory is not aligned - // even though alignas is specified. - _mm256_loadu_si256 - #else - _mm256_load_si256 - #endif - - (&in[i * 4 + 0]), - - #if defined(__MINGW32__) || defined(__MINGW64__) - _mm256_loadu_si256 - #else - _mm256_load_si256 - #endif - - (&in[i * 4 + 1])), kWeightScaleBits); + _mm256_loadA_si256(&in[i * 4 + 0]), + _mm256_loadA_si256(&in[i * 4 + 1])), kWeightScaleBits); const __m256i words1 = _mm256_srai_epi16(_mm256_packs_epi32( - - #if defined(__MINGW32__) || defined(__MINGW64__) - _mm256_loadu_si256 - #else - _mm256_load_si256 - #endif - - (&in[i * 4 + 2]), - - #if defined(__MINGW32__) || defined(__MINGW64__) - _mm256_loadu_si256 - #else - _mm256_load_si256 - #endif - - (&in[i * 4 + 3])), kWeightScaleBits); - - #if defined(__MINGW32__) || defined(__MINGW64__) - _mm256_storeu_si256 - #else - _mm256_store_si256 - #endif - - (&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8( + _mm256_loadA_si256(&in[i * 4 + 2]), + _mm256_loadA_si256(&in[i * 4 + 3])), kWeightScaleBits); + _mm256_storeA_si256(&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8( _mm256_packs_epi16(words0, words1), kZero), kOffsets)); } constexpr IndexType kStart = kNumChunks * kSimdWidth; diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index 972ef3e50c6..e7ce84f7b9f 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -37,6 +37,27 @@ #include #endif +// HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Otherwise a binary +// compiled with older g++ crashes because the output memory is not aligned +// even though alignas is specified. +#if defined(USE_AVX2) +#if defined(__GNUC__ ) && (__GNUC__ < 9) +#define _mm256_loadA_si256 _mm256_loadu_si256 +#define _mm256_storeA_si256 _mm256_storeu_si256 +#else +#define _mm256_loadA_si256 _mm256_load_si256 +#define _mm256_storeA_si256 _mm256_store_si256 +#endif +#endif + +#if defined(USE_AVX512) +#if defined(__GNUC__ ) && (__GNUC__ < 9) +#define _mm512_loadA_si512 _mm512_loadu_si512 +#else +#define _mm512_loadA_si512 _mm512_load_si512 +#endif +#endif + namespace Eval::NNUE { // Version of the evaluation file diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 1cfebbe4cbe..cbcc26f3efa 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -109,37 +109,11 @@ namespace Eval::NNUE { #if defined(USE_AVX2) auto out = reinterpret_cast<__m256i*>(&output[offset]); for (IndexType j = 0; j < kNumChunks; ++j) { - __m256i sum0 = - - #if defined(__MINGW32__) || defined(__MINGW64__) - // HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Because the binary - // compiled with g++ in MSYS2 crashes here because the output memory is not aligned - // even though alignas is specified. - _mm256_loadu_si256 - #else - _mm256_load_si256 - #endif - - (&reinterpret_cast( - accumulation[perspectives[p]][0])[j * 2 + 0]); - __m256i sum1 = - - #if defined(__MINGW32__) || defined(__MINGW64__) - _mm256_loadu_si256 - #else - _mm256_load_si256 - #endif - - (&reinterpret_cast( - accumulation[perspectives[p]][0])[j * 2 + 1]); - - #if defined(__MINGW32__) || defined(__MINGW64__) - _mm256_storeu_si256 - #else - _mm256_store_si256 - #endif - - (&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8( + __m256i sum0 = _mm256_loadA_si256( + &reinterpret_cast(accumulation[perspectives[p]][0])[j * 2 + 0]); + __m256i sum1 = _mm256_loadA_si256( + &reinterpret_cast(accumulation[perspectives[p]][0])[j * 2 + 1]); + _mm256_storeA_si256(&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8( _mm256_packs_epi16(sum0, sum1), kZero), kControl)); } @@ -202,11 +176,7 @@ namespace Eval::NNUE { auto column = reinterpret_cast(&weights_[offset]); constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); for (IndexType j = 0; j < kNumChunks; ++j) { - #if defined(__MINGW32__) || defined(__MINGW64__) - _mm256_storeu_si256(&accumulation[j], _mm256_add_epi16(_mm256_loadu_si256(&accumulation[j]), column[j])); - #else - accumulation[j] = _mm256_add_epi16(accumulation[j], column[j]); - #endif + _mm256_storeA_si256(&accumulation[j], _mm256_add_epi16(_mm256_loadA_si256(&accumulation[j]), column[j])); } #elif defined(USE_SSE2) From ad2ad4c65706c18a5383506d361f1f23fc6a26ab Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Mon, 10 Aug 2020 15:39:22 +0800 Subject: [PATCH 0318/1766] Modify castling extension Extend castling only if there are few friendly pieces on the castling side. Inspired by silversolver1's (Rahul Dsilva) test https://tests.stockfishchess.org/tests/view/5f0fef560640035f9d2978cf STC: LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 7096 W: 947 L: 818 D: 5331 Ptnml(0-2): 32, 604, 2181, 665, 66 https://tests.stockfishchess.org/tests/view/5f309f729081672066537426 LTC: LLR: 2.96 (-2.94,2.94) {0.25,1.75} Total: 4712 W: 300 L: 215 D: 4197 Ptnml(0-2): 2, 190, 1895, 259, 10 https://tests.stockfishchess.org/tests/view/5f30a2039081672066537430 closes https://github.com/official-stockfish/Stockfish/pull/2970 Bench: 4094850 --- src/search.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 0a2519b6250..3d2bb422295 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1131,7 +1131,8 @@ namespace { extension = 1; // Castling extension - if (type_of(move) == CASTLING) + if ( type_of(move) == CASTLING + && popcount(pos.pieces(us) & ~pos.pieces(PAWN) & (to_sq(move) & KingSide ? KingSide : QueenSide)) <= 3) extension = 1; // Late irreversible move extension From cb0504028e8830dbc71be53cbd701d78c3d562a1 Mon Sep 17 00:00:00 2001 From: sf-x Date: Sun, 9 Aug 2020 18:01:18 +0300 Subject: [PATCH 0319/1766] Makefile rework/cleanup Makefile targets x86-64-sse42, x86-sse3 are removed; x86-64-sse41 is renamed to x86-64-sse41-popcnt (it did enable popcnt). Makefile variables sse3, sse42, their associated compilation flags and code in misc.cpp are removed. closes https://github.com/official-stockfish/Stockfish/pull/2922 No functional change --- src/Makefile | 58 +++------------------------------------------------- src/misc.cpp | 6 ------ 2 files changed, 3 insertions(+), 61 deletions(-) diff --git a/src/Makefile b/src/Makefile index 571172b29e0..a48e7dcb3cc 100644 --- a/src/Makefile +++ b/src/Makefile @@ -68,10 +68,8 @@ endif # prefetch = yes/no --- -DUSE_PREFETCH --- Use prefetch asm-instruction # popcnt = yes/no --- -DUSE_POPCNT --- Use popcnt asm-instruction # sse = yes/no --- -msse --- Use Intel Streaming SIMD Extensions -# sse3 = yes/no --- -msse3 --- Use Intel Streaming SIMD Extensions 3 # ssse3 = yes/no --- -mssse3 --- Use Intel Supplemental Streaming SIMD Extensions 3 # sse41 = yes/no --- -msse4.1 --- Use Intel Streaming SIMD Extensions 4.1 -# sse42 = yes/no --- -msse4.2 --- Use Intel Streaming SIMD Extensions 4.2 # avx2 = yes/no --- -mavx2 --- Use Intel Advanced Vector Extensions 2 # pext = yes/no --- -DUSE_PEXT --- Use pext x86_64 asm-instruction # avx512 = yes/no --- -mavx512bw --- Use Intel Advanced Vector Extensions 512 @@ -89,10 +87,8 @@ bits = 64 prefetch = no popcnt = no sse = no -sse3 = no ssse3 = no sse41 = no -sse42 = no avx2 = no pext = no avx512 = no @@ -127,18 +123,10 @@ ifeq ($(ARCH),x86-64) sse = yes endif -ifeq ($(ARCH),x86-64-sse3) - arch = x86_64 - prefetch = yes - sse = yes - sse3 = yes -endif - ifeq ($(ARCH),x86-64-sse3-popcnt) arch = x86_64 prefetch = yes sse = yes - sse3 = yes popcnt = yes endif @@ -146,39 +134,25 @@ ifeq ($(ARCH),x86-64-ssse3) arch = x86_64 prefetch = yes sse = yes - sse3 = yes ssse3 = yes endif -ifeq ($(ARCH),x86-64-sse41) - arch = x86_64 - prefetch = yes - popcnt = yes - sse = yes - sse3 = yes - ssse3 = yes - sse41 = yes -endif - ifeq ($(ARCH),x86-64-modern) arch = x86_64 prefetch = yes popcnt = yes sse = yes - sse3 = yes ssse3 = yes sse41 = yes endif -ifeq ($(ARCH),x86-64-sse42) +ifeq ($(ARCH),x86-64-sse41-popcnt) arch = x86_64 prefetch = yes popcnt = yes sse = yes - sse3 = yes ssse3 = yes sse41 = yes - sse42 = yes endif ifeq ($(ARCH),x86-64-avx2) @@ -186,10 +160,8 @@ ifeq ($(ARCH),x86-64-avx2) prefetch = yes popcnt = yes sse = yes - sse3 = yes ssse3 = yes sse41 = yes - sse42 = yes avx2 = yes endif @@ -198,10 +170,8 @@ ifeq ($(ARCH),x86-64-bmi2) prefetch = yes popcnt = yes sse = yes - sse3 = yes ssse3 = yes sse41 = yes - sse42 = yes avx2 = yes pext = yes endif @@ -211,10 +181,8 @@ ifeq ($(ARCH),x86-64-avx512) prefetch = yes popcnt = yes sse = yes - sse3 = yes ssse3 = yes sse41 = yes - sse42 = yes avx2 = yes pext = yes avx512 = yes @@ -450,13 +418,6 @@ ifeq ($(avx512),yes) endif endif -ifeq ($(sse42),yes) - CXXFLAGS += -DUSE_SSE42 - ifeq ($(comp),$(filter $(comp),gcc clang mingw)) - CXXFLAGS += -msse4.2 - endif -endif - ifeq ($(sse41),yes) CXXFLAGS += -DUSE_SSE41 ifeq ($(comp),$(filter $(comp),gcc clang mingw)) @@ -471,13 +432,6 @@ ifeq ($(ssse3),yes) endif endif -ifeq ($(sse3),yes) - CXXFLAGS += -DUSE_SSE3 - ifeq ($(comp),$(filter $(comp),gcc clang mingw)) - CXXFLAGS += -msse3 - endif -endif - ifeq ($(neon),yes) CXXFLAGS += -DUSE_NEON endif @@ -557,12 +511,10 @@ help: @echo "x86-64-avx512 > x86 64-bit with avx512 support" @echo "x86-64-bmi2 > x86 64-bit with bmi2 support" @echo "x86-64-avx2 > x86 64-bit with avx2 support" - @echo "x86-64-sse42 > x86 64-bit with sse42 support" - @echo "x86-64-modern > x86 64-bit with sse41 support (x86-64-sse41)" - @echo "x86-64-sse41 > x86 64-bit with sse41 support" + @echo "x86-64-sse41-popcnt > x86 64-bit with sse41 and popcnt support" + @echo "x86-64-modern > the same as previous (x86-64-sse41-popcnt)" @echo "x86-64-ssse3 > x86 64-bit with ssse3 support" @echo "x86-64-sse3-popcnt > x86 64-bit with sse3 and popcnt support" - @echo "x86-64-sse3 > x86 64-bit with sse3 support" @echo "x86-64 > x86 64-bit generic" @echo "x86-32 > x86 32-bit (also enables SSE)" @echo "x86-32-old > x86 32-bit fall back for old hardware" @@ -669,10 +621,8 @@ config-sanity: @echo "prefetch: '$(prefetch)'" @echo "popcnt: '$(popcnt)'" @echo "sse: '$(sse)'" - @echo "sse3: '$(sse3)'" @echo "ssse3: '$(ssse3)'" @echo "sse41: '$(sse41)'" - @echo "sse42: '$(sse42)'" @echo "avx2: '$(avx2)'" @echo "pext: '$(pext)'" @echo "avx512: '$(avx512)'" @@ -695,10 +645,8 @@ config-sanity: @test "$(prefetch)" = "yes" || test "$(prefetch)" = "no" @test "$(popcnt)" = "yes" || test "$(popcnt)" = "no" @test "$(sse)" = "yes" || test "$(sse)" = "no" - @test "$(sse3)" = "yes" || test "$(sse3)" = "no" @test "$(ssse3)" = "yes" || test "$(ssse3)" = "no" @test "$(sse41)" = "yes" || test "$(sse41)" = "no" - @test "$(sse42)" = "yes" || test "$(sse42)" = "no" @test "$(avx2)" = "yes" || test "$(avx2)" = "no" @test "$(pext)" = "yes" || test "$(pext)" = "no" @test "$(avx512)" = "yes" || test "$(avx512)" = "no" diff --git a/src/misc.cpp b/src/misc.cpp index bdd7bccbc1d..5061ae13a17 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -220,17 +220,11 @@ const std::string compiler_info() { #if defined(USE_AVX2) compiler += " AVX2"; #endif - #if defined(USE_SSE42) - compiler += " SSE42"; - #endif #if defined(USE_SSE41) compiler += " SSE41"; #endif #if defined(USE_SSSE3) compiler += " SSSE3"; - #endif - #if defined(USE_SSE3) - compiler += " SSE3"; #endif compiler += (HasPext ? " BMI2" : ""); compiler += (HasPopCnt ? " POPCNT" : ""); From f948cd008d3a289ebbadc463271f84888e8069ba Mon Sep 17 00:00:00 2001 From: mstembera Date: Sun, 9 Aug 2020 16:23:33 -0700 Subject: [PATCH 0320/1766] Cleanup and optimize SSE/AVX code AVX512 +4% faster AVX2 +1% faster SSSE3 +5% faster passed non-regression STC: STC https://tests.stockfishchess.org/tests/view/5f31249f90816720665374f6 LLR: 2.96 (-2.94,2.94) {-1.50,0.50} Total: 17576 W: 2344 L: 2245 D: 12987 Ptnml(0-2): 127, 1570, 5292, 1675, 124 closes https://github.com/official-stockfish/Stockfish/pull/2962 No functional change --- src/nnue/layers/affine_transform.h | 46 +++++++++++++++-------------- src/nnue/nnue_accumulator.h | 2 +- src/nnue/nnue_common.h | 6 ++-- src/nnue/nnue_feature_transformer.h | 21 +++++++------ 4 files changed, 41 insertions(+), 34 deletions(-) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 20ec2f1234b..89cfaad7dbd 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -108,24 +108,19 @@ namespace Eval::NNUE::Layers { product = _mm512_madd_epi16(product, kOnes); sum = _mm512_add_epi32(sum, product); } - output[i] = _mm512_reduce_add_epi32(sum) + biases_[i]; // Note: Changing kMaxSimdWidth from 32 to 64 breaks loading existing networks. // As a result kPaddedInputDimensions may not be an even multiple of 64(512bit) // and we have to do one more 256bit chunk. if (kPaddedInputDimensions != kNumChunks * kSimdWidth * 2) { - const auto iv_256 = reinterpret_cast(input); - const auto row_256 = reinterpret_cast(&weights_[offset]); - int j = kNumChunks * 2; - __m256i sum256 = _mm256_maddubs_epi16(_mm256_loadA_si256(&iv_256[j]), _mm256_load_si256(&row_256[j])); - sum256 = _mm256_madd_epi16(sum256, _mm256_set1_epi16(1)); - sum256 = _mm256_hadd_epi32(sum256, sum256); - sum256 = _mm256_hadd_epi32(sum256, sum256); - const __m128i lo = _mm256_extracti128_si256(sum256, 0); - const __m128i hi = _mm256_extracti128_si256(sum256, 1); - output[i] += _mm_cvtsi128_si32(lo) + _mm_cvtsi128_si32(hi); + const auto iv256 = reinterpret_cast(&input_vector[kNumChunks]); + const auto row256 = reinterpret_cast(&row[kNumChunks]); + __m256i product256 = _mm256_maddubs_epi16(_mm256_loadA_si256(&iv256[0]), _mm256_load_si256(&row256[0])); + product256 = _mm256_madd_epi16(product256, _mm256_set1_epi16(1)); + sum = _mm512_add_epi32(sum, _mm512_zextsi256_si512(product256)); } + output[i] = _mm512_reduce_add_epi32(sum) + biases_[i]; #elif defined(USE_AVX2) __m256i sum = _mm256_setzero_si256(); @@ -135,23 +130,30 @@ namespace Eval::NNUE::Layers { product = _mm256_madd_epi16(product, kOnes); sum = _mm256_add_epi32(sum, product); } - sum = _mm256_hadd_epi32(sum, sum); - sum = _mm256_hadd_epi32(sum, sum); - const __m128i lo = _mm256_extracti128_si256(sum, 0); - const __m128i hi = _mm256_extracti128_si256(sum, 1); - output[i] = _mm_cvtsi128_si32(lo) + _mm_cvtsi128_si32(hi) + biases_[i]; + __m128i sum128 = _mm_add_epi32(_mm256_castsi256_si128(sum), _mm256_extracti128_si256(sum, 1)); + sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_BADC)); + sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_CDAB)); + output[i] = _mm_cvtsi128_si32(sum128) + biases_[i]; #elif defined(USE_SSSE3) - __m128i sum = _mm_cvtsi32_si128(biases_[i]); + __m128i sum = _mm_setzero_si128(); const auto row = reinterpret_cast(&weights_[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { - __m128i product = _mm_maddubs_epi16(_mm_load_si128(&input_vector[j]), _mm_load_si128(&row[j])); + for (int j = 0; j < (int)kNumChunks - 1; j += 2) { + __m128i product0 = _mm_maddubs_epi16(_mm_load_si128(&input_vector[j]), _mm_load_si128(&row[j])); + product0 = _mm_madd_epi16(product0, kOnes); + sum = _mm_add_epi32(sum, product0); + __m128i product1 = _mm_maddubs_epi16(_mm_load_si128(&input_vector[j+1]), _mm_load_si128(&row[j+1])); + product1 = _mm_madd_epi16(product1, kOnes); + sum = _mm_add_epi32(sum, product1); + } + if (kNumChunks & 0x1) { + __m128i product = _mm_maddubs_epi16(_mm_load_si128(&input_vector[kNumChunks-1]), _mm_load_si128(&row[kNumChunks-1])); product = _mm_madd_epi16(product, kOnes); sum = _mm_add_epi32(sum, product); } - sum = _mm_hadd_epi32(sum, sum); - sum = _mm_hadd_epi32(sum, sum); - output[i] = _mm_cvtsi128_si32(sum); + sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0x4E)); //_MM_PERM_BADC + sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0xB1)); //_MM_PERM_CDAB + output[i] = _mm_cvtsi128_si32(sum) + biases_[i]; #elif defined(USE_NEON) int32x4_t sum = {biases_[i]}; diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index 2a354a3c284..69dfaad2147 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -26,7 +26,7 @@ namespace Eval::NNUE { // Class that holds the result of affine transformation of input features - struct alignas(32) Accumulator { + struct alignas(kCacheLineSize) Accumulator { std::int16_t accumulation[2][kRefreshTriggers.size()][kTransformedFeatureDimensions]; Value score; diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index e7ce84f7b9f..ff33cc7974b 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -52,9 +52,11 @@ #if defined(USE_AVX512) #if defined(__GNUC__ ) && (__GNUC__ < 9) -#define _mm512_loadA_si512 _mm512_loadu_si512 +#define _mm512_loadA_si512 _mm512_loadu_si512 +#define _mm512_storeA_si512 _mm512_storeu_si512 #else -#define _mm512_loadA_si512 _mm512_load_si512 +#define _mm512_loadA_si512 _mm512_load_si512 +#define _mm512_storeA_si512 _mm512_store_si512 #endif #endif diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index cbcc26f3efa..3818e444b6a 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -169,38 +169,41 @@ namespace Eval::NNUE { kHalfDimensions * sizeof(BiasType)); for (const auto index : active_indices[perspective]) { const IndexType offset = kHalfDimensions * index; + #if defined(USE_AVX512) + auto accumulation = reinterpret_cast<__m512i*>( + &accumulator.accumulation[perspective][i][0]); + auto column = reinterpret_cast(&weights_[offset]); + constexpr IndexType kNumChunks = kHalfDimensions / kSimdWidth; + for (IndexType j = 0; j < kNumChunks; ++j) + _mm512_storeA_si512(&accumulation[j], _mm512_add_epi16(_mm512_loadA_si512(&accumulation[j]), column[j])); - #if defined(USE_AVX2) + #elif defined(USE_AVX2) auto accumulation = reinterpret_cast<__m256i*>( &accumulator.accumulation[perspective][i][0]); auto column = reinterpret_cast(&weights_[offset]); constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); - for (IndexType j = 0; j < kNumChunks; ++j) { + for (IndexType j = 0; j < kNumChunks; ++j) _mm256_storeA_si256(&accumulation[j], _mm256_add_epi16(_mm256_loadA_si256(&accumulation[j]), column[j])); - } #elif defined(USE_SSE2) auto accumulation = reinterpret_cast<__m128i*>( &accumulator.accumulation[perspective][i][0]); auto column = reinterpret_cast(&weights_[offset]); constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); - for (IndexType j = 0; j < kNumChunks; ++j) { + for (IndexType j = 0; j < kNumChunks; ++j) accumulation[j] = _mm_add_epi16(accumulation[j], column[j]); - } #elif defined(USE_NEON) auto accumulation = reinterpret_cast( &accumulator.accumulation[perspective][i][0]); auto column = reinterpret_cast(&weights_[offset]); constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); - for (IndexType j = 0; j < kNumChunks; ++j) { + for (IndexType j = 0; j < kNumChunks; ++j) accumulation[j] = vaddq_s16(accumulation[j], column[j]); - } #else - for (IndexType j = 0; j < kHalfDimensions; ++j) { + for (IndexType j = 0; j < kHalfDimensions; ++j) accumulator.accumulation[perspective][i][j] += weights_[offset + j]; - } #endif } From 21df37d7fd4dcc9b4a9c319382cc43685c0259c8 Mon Sep 17 00:00:00 2001 From: Fanael Linithien Date: Sun, 9 Aug 2020 16:20:45 +0200 Subject: [PATCH 0321/1766] Provide vectorized NNUE code for SSE2 and MMX targets This patch allows old x86 CPUs, from AMD K8 (which the x86-64 baseline targets) all the way down to the Pentium MMX, to benefit from NNUE with comparable performance hit versus hand-written eval as on more modern processors. NPS of the bench with NNUE enabled on a Pentium III 1.13 GHz (using the MMX code): master: 38951 this patch: 80586 NPS of the bench with NNUE enabled using baseline x86-64 arch, which is how linux distros are likely to package stockfish, on a modern CPU (using the SSE2 code): master: 882584 this patch: 1203945 closes https://github.com/official-stockfish/Stockfish/pull/2956 No functional change. --- AUTHORS | 1 + src/Makefile | 13 ++++++- src/misc.cpp | 3 ++ src/nnue/layers/affine_transform.h | 59 ++++++++++++++++++++++++++++- src/nnue/layers/clipped_relu.h | 20 +++++++++- src/nnue/nnue_common.h | 6 +++ src/nnue/nnue_feature_transformer.h | 54 +++++++++++++++++++++++++- 7 files changed, 150 insertions(+), 6 deletions(-) diff --git a/AUTHORS b/AUTHORS index 21ef3e50971..41b89705c97 100644 --- a/AUTHORS +++ b/AUTHORS @@ -53,6 +53,7 @@ Ernesto Gatti Linmiao Xu (linrock) Fabian Beuke (madnight) Fabian Fichter (ianfab) +Fanael Linithien (Fanael) fanon Fauzi Akram Dabat (FauziAkram) Felix Wittmann diff --git a/src/Makefile b/src/Makefile index a48e7dcb3cc..3d84f482654 100644 --- a/src/Makefile +++ b/src/Makefile @@ -86,6 +86,7 @@ sanitize = no bits = 64 prefetch = no popcnt = no +mmx = no sse = no ssse3 = no sse41 = no @@ -110,6 +111,7 @@ ifeq ($(ARCH),x86-32) arch = i386 bits = 32 prefetch = yes + mmx = yes sse = yes endif @@ -250,7 +252,7 @@ ifeq ($(COMP),gcc) ifneq ($(KERNEL),Darwin) LDFLAGS += -Wl,--no-as-needed endif - + gccversion = $(shell $(CXX) --version) gccisclang = $(findstring clang,$(gccversion)) endif @@ -432,6 +434,13 @@ ifeq ($(ssse3),yes) endif endif +ifeq ($(mmx),yes) + CXXFLAGS += -DUSE_MMX + ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + CXXFLAGS += -mmmx + endif +endif + ifeq ($(neon),yes) CXXFLAGS += -DUSE_NEON endif @@ -516,7 +525,7 @@ help: @echo "x86-64-ssse3 > x86 64-bit with ssse3 support" @echo "x86-64-sse3-popcnt > x86 64-bit with sse3 and popcnt support" @echo "x86-64 > x86 64-bit generic" - @echo "x86-32 > x86 32-bit (also enables SSE)" + @echo "x86-32 > x86 32-bit (also enables MMX and 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" diff --git a/src/misc.cpp b/src/misc.cpp index 5061ae13a17..401a65058fc 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -228,6 +228,9 @@ const std::string compiler_info() { #endif compiler += (HasPext ? " BMI2" : ""); compiler += (HasPopCnt ? " POPCNT" : ""); + #if defined(USE_MMX) + compiler += " MMX"; + #endif #if !defined(NDEBUG) compiler += " DEBUG"; #endif diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 89cfaad7dbd..985ee71a419 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -87,11 +87,20 @@ namespace Eval::NNUE::Layers { const __m256i kOnes = _mm256_set1_epi16(1); const auto input_vector = reinterpret_cast(input); - #elif defined(USE_SSSE3) + #elif defined(USE_SSE2) constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; + #ifndef USE_SSSE3 + const __m128i kZeros = _mm_setzero_si128(); + #else const __m128i kOnes = _mm_set1_epi16(1); + #endif const auto input_vector = reinterpret_cast(input); + #elif defined(USE_MMX) + constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; + const __m64 kZeros = _mm_setzero_si64(); + const auto input_vector = reinterpret_cast(input); + #elif defined(USE_NEON) constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; const auto input_vector = reinterpret_cast(input); @@ -155,6 +164,51 @@ namespace Eval::NNUE::Layers { sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0xB1)); //_MM_PERM_CDAB output[i] = _mm_cvtsi128_si32(sum) + biases_[i]; + #elif defined(USE_SSE2) + __m128i sum_lo = _mm_cvtsi32_si128(biases_[i]); + __m128i sum_hi = kZeros; + const auto row = reinterpret_cast(&weights_[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + __m128i row_j = _mm_load_si128(&row[j]); + __m128i input_j = _mm_load_si128(&input_vector[j]); + __m128i row_signs = _mm_cmpgt_epi8(kZeros, row_j); + __m128i extended_row_lo = _mm_unpacklo_epi8(row_j, row_signs); + __m128i extended_row_hi = _mm_unpackhi_epi8(row_j, row_signs); + __m128i extended_input_lo = _mm_unpacklo_epi8(input_j, kZeros); + __m128i extended_input_hi = _mm_unpackhi_epi8(input_j, kZeros); + __m128i product_lo = _mm_madd_epi16(extended_row_lo, extended_input_lo); + __m128i product_hi = _mm_madd_epi16(extended_row_hi, extended_input_hi); + sum_lo = _mm_add_epi32(sum_lo, product_lo); + sum_hi = _mm_add_epi32(sum_hi, product_hi); + } + __m128i sum = _mm_add_epi32(sum_lo, sum_hi); + __m128i sum_high_64 = _mm_shuffle_epi32(sum, _MM_SHUFFLE(1, 0, 3, 2)); + sum = _mm_add_epi32(sum, sum_high_64); + __m128i sum_second_32 = _mm_shufflelo_epi16(sum, _MM_SHUFFLE(1, 0, 3, 2)); + sum = _mm_add_epi32(sum, sum_second_32); + output[i] = _mm_cvtsi128_si32(sum); + + #elif defined(USE_MMX) + __m64 sum_lo = _mm_cvtsi32_si64(biases_[i]); + __m64 sum_hi = kZeros; + const auto row = reinterpret_cast(&weights_[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + __m64 row_j = row[j]; + __m64 input_j = input_vector[j]; + __m64 row_signs = _mm_cmpgt_pi8(kZeros, row_j); + __m64 extended_row_lo = _mm_unpacklo_pi8(row_j, row_signs); + __m64 extended_row_hi = _mm_unpackhi_pi8(row_j, row_signs); + __m64 extended_input_lo = _mm_unpacklo_pi8(input_j, kZeros); + __m64 extended_input_hi = _mm_unpackhi_pi8(input_j, kZeros); + __m64 product_lo = _mm_madd_pi16(extended_row_lo, extended_input_lo); + __m64 product_hi = _mm_madd_pi16(extended_row_hi, extended_input_hi); + sum_lo = _mm_add_pi32(sum_lo, product_lo); + sum_hi = _mm_add_pi32(sum_hi, product_hi); + } + __m64 sum = _mm_add_pi32(sum_lo, sum_hi); + sum = _mm_add_pi32(sum, _mm_unpackhi_pi32(sum, sum)); + output[i] = _mm_cvtsi64_si32(sum); + #elif defined(USE_NEON) int32x4_t sum = {biases_[i]}; const auto row = reinterpret_cast(&weights_[offset]); @@ -174,6 +228,9 @@ namespace Eval::NNUE::Layers { #endif } + #if defined(USE_MMX) + _mm_empty(); + #endif return output; } diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index 13196ec28b4..44d8a7def4c 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -84,7 +84,7 @@ namespace Eval::NNUE::Layers { } constexpr IndexType kStart = kNumChunks * kSimdWidth; - #elif defined(USE_SSSE3) + #elif defined(USE_SSE2) constexpr IndexType kNumChunks = kInputDimensions / kSimdWidth; #ifdef USE_SSE41 @@ -115,6 +115,24 @@ namespace Eval::NNUE::Layers { } constexpr IndexType kStart = kNumChunks * kSimdWidth; + #elif defined(USE_MMX) + constexpr IndexType kNumChunks = kInputDimensions / kSimdWidth; + const __m64 k0x80s = _mm_set1_pi8(-128); + const auto in = reinterpret_cast(input); + const auto out = reinterpret_cast<__m64*>(output); + for (IndexType i = 0; i < kNumChunks; ++i) { + const __m64 words0 = _mm_srai_pi16( + _mm_packs_pi32(in[i * 4 + 0], in[i * 4 + 1]), + kWeightScaleBits); + const __m64 words1 = _mm_srai_pi16( + _mm_packs_pi32(in[i * 4 + 2], in[i * 4 + 3]), + kWeightScaleBits); + const __m64 packedbytes = _mm_packs_pi16(words0, words1); + out[i] = _mm_subs_pi8(_mm_adds_pi8(packedbytes, k0x80s), k0x80s); + } + _mm_empty(); + constexpr IndexType kStart = kNumChunks * kSimdWidth; + #elif defined(USE_NEON) constexpr IndexType kNumChunks = kInputDimensions / (kSimdWidth / 2); const int8x8_t kZero = {0}; diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index ff33cc7974b..cb1251c58aa 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -33,6 +33,9 @@ #elif defined(USE_SSE2) #include +#elif defined(USE_MMX) +#include + #elif defined(USE_NEON) #include #endif @@ -79,6 +82,9 @@ namespace Eval::NNUE { #elif defined(USE_SSE2) constexpr std::size_t kSimdWidth = 16; + #elif defined(USE_MMX) + constexpr std::size_t kSimdWidth = 8; + #elif defined(USE_NEON) constexpr std::size_t kSimdWidth = 16; #endif diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 3818e444b6a..40f2603d9d5 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -88,7 +88,7 @@ namespace Eval::NNUE { constexpr int kControl = 0b11011000; const __m256i kZero = _mm256_setzero_si256(); - #elif defined(USE_SSSE3) + #elif defined(USE_SSE2) constexpr IndexType kNumChunks = kHalfDimensions / kSimdWidth; #ifdef USE_SSE41 @@ -97,6 +97,10 @@ namespace Eval::NNUE { const __m128i k0x80s = _mm_set1_epi8(-128); #endif + #elif defined(USE_MMX) + constexpr IndexType kNumChunks = kHalfDimensions / kSimdWidth; + const __m64 k0x80s = _mm_set1_pi8(-128); + #elif defined(USE_NEON) constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); const int8x8_t kZero = {0}; @@ -117,7 +121,7 @@ namespace Eval::NNUE { _mm256_packs_epi16(sum0, sum1), kZero), kControl)); } - #elif defined(USE_SSSE3) + #elif defined(USE_SSE2) auto out = reinterpret_cast<__m128i*>(&output[offset]); for (IndexType j = 0; j < kNumChunks; ++j) { __m128i sum0 = _mm_load_si128(&reinterpret_cast( @@ -137,6 +141,17 @@ namespace Eval::NNUE { ); } + #elif defined(USE_MMX) + auto out = reinterpret_cast<__m64*>(&output[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + __m64 sum0 = *(&reinterpret_cast( + accumulation[perspectives[p]][0])[j * 2 + 0]); + __m64 sum1 = *(&reinterpret_cast( + accumulation[perspectives[p]][0])[j * 2 + 1]); + const __m64 packedbytes = _mm_packs_pi16(sum0, sum1); + out[j] = _mm_subs_pi8(_mm_adds_pi8(packedbytes, k0x80s), k0x80s); + } + #elif defined(USE_NEON) const auto out = reinterpret_cast(&output[offset]); for (IndexType j = 0; j < kNumChunks; ++j) { @@ -154,6 +169,9 @@ namespace Eval::NNUE { #endif } + #if defined(USE_MMX) + _mm_empty(); + #endif } private: @@ -193,6 +211,15 @@ namespace Eval::NNUE { for (IndexType j = 0; j < kNumChunks; ++j) accumulation[j] = _mm_add_epi16(accumulation[j], column[j]); + #elif defined(USE_MMX) + auto accumulation = reinterpret_cast<__m64*>( + &accumulator.accumulation[perspective][i][0]); + auto column = reinterpret_cast(&weights_[offset]); + constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); + for (IndexType j = 0; j < kNumChunks; ++j) { + accumulation[j] = _mm_add_pi16(accumulation[j], column[j]); + } + #elif defined(USE_NEON) auto accumulation = reinterpret_cast( &accumulator.accumulation[perspective][i][0]); @@ -208,6 +235,9 @@ namespace Eval::NNUE { } } + #if defined(USE_MMX) + _mm_empty(); + #endif accumulator.computed_accumulation = true; accumulator.computed_score = false; @@ -234,6 +264,11 @@ namespace Eval::NNUE { auto accumulation = reinterpret_cast<__m128i*>( &accumulator.accumulation[perspective][i][0]); + #elif defined(USE_MMX) + constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); + auto accumulation = reinterpret_cast<__m64*>( + &accumulator.accumulation[perspective][i][0]); + #elif defined(USE_NEON) constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); auto accumulation = reinterpret_cast( @@ -263,6 +298,12 @@ namespace Eval::NNUE { accumulation[j] = _mm_sub_epi16(accumulation[j], column[j]); } + #elif defined(USE_MMX) + auto column = reinterpret_cast(&weights_[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + accumulation[j] = _mm_sub_pi16(accumulation[j], column[j]); + } + #elif defined(USE_NEON) auto column = reinterpret_cast(&weights_[offset]); for (IndexType j = 0; j < kNumChunks; ++j) { @@ -294,6 +335,12 @@ namespace Eval::NNUE { accumulation[j] = _mm_add_epi16(accumulation[j], column[j]); } + #elif defined(USE_MMX) + auto column = reinterpret_cast(&weights_[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + accumulation[j] = _mm_add_pi16(accumulation[j], column[j]); + } + #elif defined(USE_NEON) auto column = reinterpret_cast(&weights_[offset]); for (IndexType j = 0; j < kNumChunks; ++j) { @@ -310,6 +357,9 @@ namespace Eval::NNUE { } } } + #if defined(USE_MMX) + _mm_empty(); + #endif accumulator.computed_accumulation = true; accumulator.computed_score = false; From 220ef1d27d9cd006a30b07ab726999c8181d10f0 Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Mon, 10 Aug 2020 15:38:44 +0200 Subject: [PATCH 0322/1766] Assorted search parameter tune STC https://tests.stockfishchess.org/tests/view/5f31219090816720665374ec LLR: 2.96 (-2.94,2.94) {-0.50,1.50} Total: 3376 W: 487 L: 359 D: 2530 Ptnml(0-2): 17, 253, 1042, 337, 39 LTC https://tests.stockfishchess.org/tests/view/5f3127f79081672066537502 LLR: 2.93 (-2.94,2.94) {0.25,1.75} Total: 8360 W: 581 L: 475 D: 7304 Ptnml(0-2): 11, 407, 3238, 513, 11 closes https://github.com/official-stockfish/Stockfish/pull/2971 bench: 4733874 --- src/search.cpp | 60 +++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 3d2bb422295..676427f7042 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -63,9 +63,9 @@ namespace { constexpr uint64_t TtHitAverageResolution = 1024; // Razor and futility margins - constexpr int RazorMargin = 527; + constexpr int RazorMargin = 510; Value futility_margin(Depth d, bool improving) { - return Value(227 * (d - improving)); + return Value(223 * (d - improving)); } // Reductions lookup table, initialized at startup @@ -73,7 +73,7 @@ namespace { Depth reduction(bool i, Depth d, int mn) { int r = Reductions[d] * Reductions[mn]; - return (r + 570) / 1024 + (!i && r > 1018); + return (r + 509) / 1024 + (!i && r > 894); } constexpr int futility_move_count(bool improving, Depth depth) { @@ -82,7 +82,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return d > 15 ? 27 : 17 * d * d + 133 * d - 134; + return d > 13 ? 29 : 17 * d * d + 134 * d - 134; } // Add a small random component to draw evaluations to avoid 3fold-blindness @@ -192,7 +192,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int((24.8 + std::log(Threads.size())) * std::log(i)); + Reductions[i] = int((22.0 + std::log(Threads.size())) * std::log(i)); } @@ -403,12 +403,12 @@ void Thread::search() { if (rootDepth >= 4) { Value prev = rootMoves[pvIdx].previousScore; - delta = Value(19); + delta = Value(17); alpha = std::max(prev - delta,-VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE); // Adjust contempt based on root move's previousScore (dynamic contempt) - int dct = ct + (110 - ct / 2) * prev / (abs(prev) + 140); + int dct = ct + (105 - ct / 2) * prev / (abs(prev) + 149); contempt = (us == WHITE ? make_score(dct, dct / 2) : -make_score(dct, dct / 2)); @@ -506,13 +506,13 @@ void Thread::search() { && !Threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (296 + 6 * (mainThread->bestPreviousScore - bestValue) - + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 725.0; + double fallingEval = (318 + 6 * (mainThread->bestPreviousScore - bestValue) + + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 825.0; fallingEval = Utility::clamp(fallingEval, 0.5, 1.5); // If the bestMove is stable over several iterations, reduce time accordingly - timeReduction = lastBestMoveDepth + 10 < completedDepth ? 1.92 : 0.95; - double reduction = (1.47 + mainThread->previousTimeReduction) / (2.22 * timeReduction); + timeReduction = lastBestMoveDepth + 9 < completedDepth ? 1.92 : 0.95; + double reduction = (1.47 + mainThread->previousTimeReduction) / (2.32 * timeReduction); // Use part of the gained time from a previous stable move for the current move for (Thread* th : Threads) @@ -537,7 +537,7 @@ void Thread::search() { } else if ( Threads.increaseDepth && !mainThread->ponder - && Time.elapsed() > totalTime * 0.56) + && Time.elapsed() > totalTime * 0.58) Threads.increaseDepth = false; else Threads.increaseDepth = true; @@ -824,10 +824,10 @@ namespace { // Step 9. Null move search with verification search (~40 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 23824 + && (ss-1)->statScore < 22977 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 28 * depth - 28 * improving + 94 * ttPv + 200 + && ss->staticEval >= beta - 30 * depth - 28 * improving + 84 * ttPv + 182 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -835,7 +835,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and value - Depth R = (737 + 77 * depth) / 246 + std::min(int(eval - beta) / 192, 3); + Depth R = (817 + 71 * depth) / 213 + std::min(int(eval - beta) / 192, 3); ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -1028,17 +1028,17 @@ namespace { continue; // Futility pruning: parent node (~5 Elo) - if ( lmrDepth < 8 + if ( lmrDepth < 7 && !ss->inCheck - && ss->staticEval + 284 + 188 * lmrDepth <= alpha + && ss->staticEval + 283 + 170 * lmrDepth <= alpha && (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - + (*contHist[5])[movedPiece][to_sq(move)] / 2 < 28388) + + (*contHist[5])[movedPiece][to_sq(move)] / 2 < 27376) continue; // Prune moves with negative SEE (~20 Elo) - if (!pos.see_ge(move, Value(-(29 - std::min(lmrDepth, 17)) * lmrDepth * lmrDepth))) + if (!pos.see_ge(move, Value(-(29 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth))) continue; } else @@ -1055,12 +1055,12 @@ namespace { && !(PvNode && abs(bestValue) < 2) && PieceValue[MG][type_of(movedPiece)] >= PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] && !ss->inCheck - && ss->staticEval + 178 + 261 * lmrDepth + && ss->staticEval + 169 + 244 * lmrDepth + PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha) continue; // See based pruning - if (!pos.see_ge(move, Value(-202) * depth)) // (~25 Elo) + if (!pos.see_ge(move, Value(-221) * depth)) // (~25 Elo) continue; } } @@ -1166,7 +1166,7 @@ namespace { || moveCountPruning || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha || cutNode - || thisThread->ttHitAverage < 415 * TtHitAverageResolution * TtHitAverageWindow / 1024)) + || thisThread->ttHitAverage < 427 * TtHitAverageResolution * TtHitAverageWindow / 1024)) { Depth r = reduction(improving, depth, moveCount); @@ -1178,7 +1178,7 @@ namespace { r--; // Decrease reduction if the ttHit running average is large - if (thisThread->ttHitAverage > 473 * TtHitAverageResolution * TtHitAverageWindow / 1024) + if (thisThread->ttHitAverage > 509 * TtHitAverageResolution * TtHitAverageWindow / 1024) r--; // Reduction if other threads are searching this position @@ -1221,17 +1221,17 @@ namespace { + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 4826; + - 5287; // Decrease/increase reduction by comparing opponent's stat score (~10 Elo) - if (ss->statScore >= -100 && (ss-1)->statScore < -112) + if (ss->statScore >= -106 && (ss-1)->statScore < -104) r--; - else if ((ss-1)->statScore >= -125 && ss->statScore < -138) + else if ((ss-1)->statScore >= -119 && ss->statScore < -140) r++; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - r -= ss->statScore / 14615; + r -= ss->statScore / 14884; } else { @@ -1241,7 +1241,7 @@ namespace { // Unless giving check, this capture is likely bad if ( !givesCheck - && ss->staticEval + PieceValue[EG][pos.captured_piece()] + 211 * depth <= alpha) + && ss->staticEval + PieceValue[EG][pos.captured_piece()] + 213 * depth <= alpha) r++; } @@ -1503,7 +1503,7 @@ namespace { if (PvNode && bestValue > alpha) alpha = bestValue; - futilityBase = bestValue + 141; + futilityBase = bestValue + 145; } const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, @@ -1754,7 +1754,7 @@ namespace { } if (depth > 11 && ss->ply < MAX_LPH) - thisThread->lowPlyHistory[ss->ply][from_to(move)] << stat_bonus(depth - 6); + thisThread->lowPlyHistory[ss->ply][from_to(move)] << stat_bonus(depth - 7); } // When playing with strength handicap, choose best move among a set of RootMoves From a72cec1ff854a77a92452c2afe2001e05f06e6d4 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sat, 18 Jul 2020 16:30:00 +0300 Subject: [PATCH 0323/1766] Add comments to probCut code and rename a variable closes https://github.com/official-stockfish/Stockfish/pull/2819 No functional change --- src/search.cpp | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 676427f7042..ef47fd22307 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -596,7 +596,7 @@ namespace { Key posKey; Move ttMove, move, excludedMove, bestMove; Depth extension, newDepth; - Value bestValue, value, ttValue, eval, maxValue, probcutBeta; + Value bestValue, value, ttValue, eval, maxValue, probCutBeta; bool ttHit, ttPv, formerPv, givesCheck, improving, didLMR, priorCapture; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture, singularQuietLMR; @@ -871,7 +871,7 @@ namespace { } } - probcutBeta = beta + 176 - 49 * improving; + probCutBeta = beta + 176 - 49 * improving; // Step 10. ProbCut (~10 Elo) // If we have a good enough capture and a reduced search returns a value @@ -879,21 +879,27 @@ namespace { if ( !PvNode && depth > 4 && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY - && !( ttHit - && tte->depth() >= depth - 3 + // if value from transposition table is lower than probCutBeta, don't attempt probCut + // there and in further interactions with transposition table cutoff depth is set to depth - 3 + // because probCut search has depth set to depth - 4 but we also do a move before it + // so effective depth is equal to depth - 3 + && !( ttHit + && tte->depth() >= depth - 3 && ttValue != VALUE_NONE - && ttValue < probcutBeta)) + && ttValue < probCutBeta)) { + // if ttMove is a capture and value from transposition table is good enough produce probCut + // cutoff without digging into actual probCut search if ( ttHit && tte->depth() >= depth - 3 && ttValue != VALUE_NONE - && ttValue >= probcutBeta + && ttValue >= probCutBeta && ttMove && pos.capture_or_promotion(ttMove)) - return probcutBeta; + return probCutBeta; - assert(probcutBeta < VALUE_INFINITE); - MovePicker mp(pos, ttMove, probcutBeta - ss->staticEval, &captureHistory); + assert(probCutBeta < VALUE_INFINITE); + MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, &captureHistory); int probCutCount = 0; while ( (move = mp.next_move()) != MOVE_NONE @@ -915,16 +921,17 @@ namespace { pos.do_move(move, st); // Perform a preliminary qsearch to verify that the move holds - value = -qsearch(pos, ss+1, -probcutBeta, -probcutBeta+1); + value = -qsearch(pos, ss+1, -probCutBeta, -probCutBeta+1); // If the qsearch held, perform the regular search - if (value >= probcutBeta) - value = -search(pos, ss+1, -probcutBeta, -probcutBeta+1, depth - 4, !cutNode); + if (value >= probCutBeta) + value = -search(pos, ss+1, -probCutBeta, -probCutBeta+1, depth - 4, !cutNode); pos.undo_move(move); - if (value >= probcutBeta) + if (value >= probCutBeta) { + // if transposition table doesn't have equal or more deep info write probCut data into it if ( !(ttHit && tte->depth() >= depth - 3 && ttValue != VALUE_NONE)) From 4ab8b0b738fe4ae58588efb421fd7b1643b2ef66 Mon Sep 17 00:00:00 2001 From: Guy Vreuls Date: Tue, 11 Aug 2020 04:38:38 +0200 Subject: [PATCH 0324/1766] Fix parallel LTO issues on Windows This adds -save-temps to the linker flags when parallel LTO is used on MinGW/MSYS. fixes #2977 closes https://github.com/official-stockfish/Stockfish/pull/2978 No functional change. --- src/Makefile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 3d84f482654..fd2618a47b2 100644 --- a/src/Makefile +++ b/src/Makefile @@ -472,6 +472,11 @@ ifeq ($(debug), no) ifeq ($(gccisclang),) CXXFLAGS += -flto LDFLAGS += $(CXXFLAGS) -flto=jobserver + ifneq ($(findstring MINGW,$(KERNEL)),) + LDFLAGS += -save-temps + else ifneq ($(findstring MSYS,$(KERNEL)),) + LDFLAGS += -save-temps + endif else CXXFLAGS += -flto=thin LDFLAGS += $(CXXFLAGS) @@ -605,7 +610,7 @@ objclean: # clean auxiliary profiling files profileclean: @rm -rf profdir - @rm -f bench.txt *.gcda *.gcno ./syzygy/*.gcda ./nnue/*.gcda ./nnue/features/*.gcda + @rm -f bench.txt *.gcda *.gcno ./syzygy/*.gcda ./nnue/*.gcda ./nnue/features/*.gcda *.s @rm -f stockfish.profdata *.profraw default: From 399cddf444666cf1671c5281f7a8e78887b4f400 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 10 Aug 2020 16:14:17 +0200 Subject: [PATCH 0325/1766] More aligned_alloc changes to support Android Move to posix_memalign for those platforms, in particular android, that do not fully support c++17 std::aligned_alloc() (and are not windows) see https://github.com/official-stockfish/Stockfish/issues/2860 closes https://github.com/official-stockfish/Stockfish/pull/2973 No functional change --- src/misc.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index 401a65058fc..fc3746cf69a 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -51,6 +51,11 @@ typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY); #include #endif +#if (defined(__APPLE__) && defined(_LIBCPP_HAS_C11_FEATURES)) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) && !defined(_WIN32)) +#define POSIXALIGNEDALLOC +#include +#endif + #include "misc.h" #include "thread.h" @@ -318,8 +323,11 @@ void prefetch(void* addr) { /// void* std_aligned_alloc(size_t alignment, size_t size) { -#if (defined(__APPLE__) && defined(_LIBCPP_HAS_C11_FEATURES)) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) && !defined(_WIN32)) - return aligned_alloc(alignment, size); +#if defined(POSIXALIGNEDALLOC) + void *pointer; + if(posix_memalign(&pointer, alignment, size) == 0) + return pointer; + return nullptr; #elif (defined(_WIN32) || (defined(__APPLE__) && !defined(_LIBCPP_HAS_C11_FEATURES))) return _mm_malloc(size, alignment); #else @@ -328,7 +336,7 @@ void* std_aligned_alloc(size_t alignment, size_t size) { } void std_aligned_free(void* ptr) { -#if (defined(__APPLE__) && defined(_LIBCPP_HAS_C11_FEATURES)) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) && !defined(_WIN32)) +#if defined(POSIXALIGNEDALLOC) free(ptr); #elif (defined(_WIN32) || (defined(__APPLE__) && !defined(_LIBCPP_HAS_C11_FEATURES))) _mm_free(ptr); From f46c73040c16a078b884825c203feee6b0a8850b Mon Sep 17 00:00:00 2001 From: mstembera Date: Mon, 10 Aug 2020 12:52:46 -0700 Subject: [PATCH 0326/1766] Fix AVX512 build with older compilers avoids an intrinsic that is missing in gcc < 10. For this target, might trigger another gcc bug on windows that requires up-to-date gcc 8, 9, or 10, or usage of clang. Fixes https://github.com/official-stockfish/Stockfish/issues/2975 closes https://github.com/official-stockfish/Stockfish/pull/2976 No functional change --- src/Makefile | 2 +- src/nnue/layers/affine_transform.h | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Makefile b/src/Makefile index fd2618a47b2..e34fbf615e1 100644 --- a/src/Makefile +++ b/src/Makefile @@ -416,7 +416,7 @@ endif ifeq ($(avx512),yes) CXXFLAGS += -DUSE_AVX512 ifeq ($(comp),$(filter $(comp),gcc clang mingw)) - CXXFLAGS += -mavx512bw + CXXFLAGS += -mavx512f -mavx512bw endif endif diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 985ee71a419..8d2acd1852e 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -126,8 +126,7 @@ namespace Eval::NNUE::Layers { const auto iv256 = reinterpret_cast(&input_vector[kNumChunks]); const auto row256 = reinterpret_cast(&row[kNumChunks]); __m256i product256 = _mm256_maddubs_epi16(_mm256_loadA_si256(&iv256[0]), _mm256_load_si256(&row256[0])); - product256 = _mm256_madd_epi16(product256, _mm256_set1_epi16(1)); - sum = _mm512_add_epi32(sum, _mm512_zextsi256_si512(product256)); + sum = _mm512_add_epi32(sum, _mm512_cvtepi16_epi32(product256)); } output[i] = _mm512_reduce_add_epi32(sum) + biases_[i]; From ea6220f3813e5b76b444a02905eaf2c556bdb368 Mon Sep 17 00:00:00 2001 From: Guy Vreuls Date: Fri, 7 Aug 2020 17:07:46 +0200 Subject: [PATCH 0327/1766] This commit enables a mixed bench, to improve CI and allow for PGO (profile-build) of the NNUE part of the code. Joint work gvreuls / vondele * Download the default NNUE net in AppVeyor * Download net in travis CI `make net` * Adjust tests to cover more archs, speedup instrumented testing * Introduce 'mixed' bench as default, with further options: classical, NNUE, mixed. mixed (default) and NNUE require the default net to be present, which can be obtained with ``` make net ``` Further examples (first is equivalent to `./stockfish bench`): ``` ./stockfish bench 16 1 13 default depth mixed ./stockfish bench 16 1 13 default depth classical ./stockfish bench 16 1 13 default depth NNUE ``` The net is now downloaded automatically if needed for `profile-build` (usual `build` works fine without net present) PGO gives a nice speedup on fishtest: passed STC: LLR: 2.93 (-2.94,2.94) {-0.50,1.50} Total: 3360 W: 469 L: 343 D: 2548 Ptnml(0-2): 20, 246, 1030, 356, 28 https://tests.stockfishchess.org/tests/view/5f31b5499081672066537569 passed LTC: LLR: 2.97 (-2.94,2.94) {0.25,1.75} Total: 8824 W: 609 L: 502 D: 7713 Ptnml(0-2): 8, 430, 3438, 519, 17 https://tests.stockfishchess.org/tests/view/5f31c87b908167206653757c closes https://github.com/official-stockfish/Stockfish/pull/2931 fixes https://github.com/official-stockfish/Stockfish/issues/2907 requires fishtest updates before commit Bench: 4290577 --- .travis.yml | 27 +++++++++++++++++++++------ appveyor.yml | 14 ++++++++++++++ src/Makefile | 2 +- src/benchmark.cpp | 13 +++++++++++-- tests/instrumented.sh | 8 ++++---- 5 files changed, 51 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index d563a1e1992..0dd380479cc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,6 +43,9 @@ before_script: - cd src script: + # Download net + - make net + # Obtain bench reference from git log - git log HEAD | grep "\b[Bb]ench[ :]\+[0-9]\{7\}" | head -n 1 | sed "s/[^0-9]*\([0-9]*\).*/\1/g" > git_sig - export benchref=$(cat git_sig) @@ -55,14 +58,26 @@ script: # # Verify bench number against various builds - export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" - - make clean && make -j2 ARCH=x86-64 optimize=no debug=yes build && ../tests/signature.sh $benchref + - make clean && make -j2 ARCH=x86-64-modern optimize=no debug=yes build && ../tests/signature.sh $benchref + - export CXXFLAGS="-Werror" + - make clean && make -j2 ARCH=x86-64-modern build && ../tests/signature.sh $benchref + - make clean && make -j2 ARCH=x86-64-ssse3 build && ../tests/signature.sh $benchref + - make clean && make -j2 ARCH=x86-64-sse3-popcnt build && ../tests/signature.sh $benchref + - make clean && make -j2 ARCH=x86-64 build && ../tests/signature.sh $benchref - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32 optimize=no debug=yes build && ../tests/signature.sh $benchref; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32 build && ../tests/signature.sh $benchref; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32-old build && ../tests/signature.sh $benchref; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" && "$COMP" == "gcc" ]]; then make clean && make -j2 ARCH=x86-64-modern profile-build && ../tests/signature.sh $benchref; fi + + # compile only for some more advanced architectures (might not run in travis) + - make clean && make -j2 ARCH=x86-64-avx2 build + - make clean && make -j2 ARCH=x86-64-bmi2 build + # needs gcc 10 to compile + - if [[ "$COMPILER" != "g++-8" ]]; then make clean && make -j2 ARCH=x86-64-avx512 build; fi # # Check perft and reproducible search - - export CXXFLAGS="-Werror" - - make clean && make -j2 ARCH=x86-64 build + - make clean && make -j2 ARCH=x86-64-modern build - ../tests/perft.sh - ../tests/reprosearch.sh @@ -70,11 +85,11 @@ script: # Valgrind # - export CXXFLAGS="-O1 -fno-inline" - - if [ -x "$(command -v valgrind )" ]; then make clean && make -j2 ARCH=x86-64 debug=yes optimize=no build > /dev/null && ../tests/instrumented.sh --valgrind; fi + - if [ -x "$(command -v valgrind )" ]; then make clean && make -j2 ARCH=x86-64-modern debug=yes optimize=no build > /dev/null && ../tests/instrumented.sh --valgrind; fi - if [ -x "$(command -v valgrind )" ]; then ../tests/instrumented.sh --valgrind-thread; fi # # Sanitizer # - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=undefined optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-undefined; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-64 sanitize=thread optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-thread; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-64-modern sanitize=undefined optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-undefined; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-64-modern sanitize=thread optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-thread; fi diff --git a/appveyor.yml b/appveyor.yml index d356ba2f101..a3732a23f4a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -61,6 +61,20 @@ before_build: build_script: - cmake --build . --config %CONFIGURATION% -- /verbosity:minimal + - ps: | + # Download default NNUE net from fishtest + $nnuenet = Get-Content -Path src\ucioption.cpp | Select-String -CaseSensitive -Pattern "Option" | Select-String -CaseSensitive -Pattern "nn-[a-z0-9]{12}.nnue" + $dummy = $nnuenet -match "(?nn-[a-z0-9]{12}.nnue)" + $nnuenet = $Matches.nnuenet + Write-Host "Default net:" $nnuenet + $nnuedownloadurl = "https://tests.stockfishchess.org/api/nn/$nnuenet" + $nnuefilepath = "src\${env:CONFIGURATION}\$nnuenet" + if (Test-Path -Path $nnuefilepath) { + Write-Host "Already available." + } else { + Write-Host "Downloading $nnuedownloadurl to $nnuefilepath" + Invoke-WebRequest -Uri $nnuedownloadurl -OutFile $nnuefilepath + } before_test: - cd src/%CONFIGURATION% diff --git a/src/Makefile b/src/Makefile index e34fbf615e1..c00b60b5a56 100644 --- a/src/Makefile +++ b/src/Makefile @@ -569,7 +569,7 @@ help: build: config-sanity $(MAKE) ARCH=$(ARCH) COMP=$(COMP) all -profile-build: config-sanity objclean profileclean +profile-build: config-sanity objclean profileclean net @echo "" @echo "Step 1/4. Building instrumented executable ..." $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_make) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 6041d64287d..806e98401a4 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -95,8 +95,9 @@ const vector Defaults = { /// setup_bench() builds a list of UCI commands to be run by bench. There /// are five parameters: TT size in MB, number of search threads that /// should be used, the limit value spent for each position, a file name -/// where to look for positions in FEN format and the type of the limit: -/// depth, perft, nodes and movetime (in millisecs). +/// where to look for positions in FEN format, the type of the limit: +/// depth, perft, nodes and movetime (in millisecs), and evaluation type +/// mixed (default), classical, NNUE. /// /// bench -> search default positions up to depth 13 /// bench 64 1 15 -> search default positions up to depth 15 (TT = 64MB) @@ -115,6 +116,7 @@ vector setup_bench(const Position& current, istream& is) { string limit = (is >> token) ? token : "13"; string fenFile = (is >> token) ? token : "default"; string limitType = (is >> token) ? token : "depth"; + string evalType = (is >> token) ? token : "mixed"; go = limitType == "eval" ? "eval" : "go " + limitType + " " + limit; @@ -146,13 +148,20 @@ vector setup_bench(const Position& current, istream& is) { list.emplace_back("setoption name Hash value " + ttSize); list.emplace_back("ucinewgame"); + size_t posCounter = 0; + for (const string& fen : fens) if (fen.find("setoption") != string::npos) list.emplace_back(fen); else { + if (evalType == "classical" || (evalType == "mixed" && posCounter % 2 == 0)) + list.emplace_back("setoption name Use NNUE value false"); + else if (evalType == "NNUE" || (evalType == "mixed" && posCounter % 2 != 0)) + list.emplace_back("setoption name Use NNUE value true"); list.emplace_back("position fen " + fen); list.emplace_back(go); + ++posCounter; } return list; diff --git a/tests/instrumented.sh b/tests/instrumented.sh index ae6d5c4b905..03ded74abfd 100755 --- a/tests/instrumented.sh +++ b/tests/instrumented.sh @@ -70,7 +70,7 @@ for args in "eval" \ "go depth 10" \ "go movetime 1000" \ "go wtime 8000 btime 8000 winc 500 binc 500" \ - "bench 128 $threads 10 default depth" + "bench 128 $threads 8 default depth" do echo "$prefix $exeprefix ./stockfish $args $postfix" @@ -80,7 +80,7 @@ done # more general testing, following an uci protocol exchange cat << EOF > game.exp - set timeout 10 + set timeout 240 spawn $exeprefix ./stockfish send "uci\n" @@ -98,7 +98,7 @@ cat << EOF > game.exp expect "bestmove" send "position fen 5rk1/1K4p1/8/8/3B4/8/8/8 b - - 0 1\n" - send "go depth 30\n" + send "go depth 20\n" expect "bestmove" send "quit\n" @@ -121,7 +121,7 @@ cat << EOF > syzygy.exp send "uci\n" send "setoption name SyzygyPath value ../tests/syzygy/\n" expect "info string Found 35 tablebases" {} timeout {exit 1} - send "bench 128 1 10 default depth\n" + send "bench 128 1 8 default depth\n" send "quit\n" expect eof From ee060464129f8d3af184efa013177a4ef387a394 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Mon, 10 Aug 2020 21:13:56 +0800 Subject: [PATCH 0328/1766] Tweak castling extension Change condition from three friendly pieces to two. This now means that we only extend castling on the king side if there are no other friendly pieces aside from king and rook. For the queen side, we only extend if there is only a rook and another friendly piece or if there is only a single rook and no other friendly piece but this is very rare. STC: LLR: 3.20 (-2.94,2.94) {-0.50,1.50} Total: 31144 W: 4086 L: 3903 D: 23155 Ptnml(0-2): 227, 2843, 9278, 2968, 256 https://tests.stockfishchess.org/tests/view/5f31487f9081672066537516 LTC: LLR: 2.93 (-2.94,2.94) {0.25,1.75} Total: 57816 W: 3786 L: 3538 D: 50492 Ptnml(0-2): 92, 2991, 22488, 3251, 86 https://tests.stockfishchess.org/tests/view/5f3167c3908167206653753d closes https://github.com/official-stockfish/Stockfish/pull/2980 Bench: 4244812 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index ef47fd22307..c5b4332f721 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1139,7 +1139,7 @@ namespace { // Castling extension if ( type_of(move) == CASTLING - && popcount(pos.pieces(us) & ~pos.pieces(PAWN) & (to_sq(move) & KingSide ? KingSide : QueenSide)) <= 3) + && popcount(pos.pieces(us) & ~pos.pieces(PAWN) & (to_sq(move) & KingSide ? KingSide : QueenSide)) <= 2) extension = 1; // Late irreversible move extension From 992f549ae7f4f73b025429c44bdbbc65de917f6c Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Tue, 11 Aug 2020 21:11:17 +0200 Subject: [PATCH 0329/1766] Restrict avx2 hack to windows target this workaround is possibly rather a windows & gcc specific problem. See e.g. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54412#c25 on Linux with gcc 8 this patch brings roughly a 8% speedup. However, probably needs some testing in the wild. includes a workaround for an old msys make (3.81) installation (fixes #2984) No functional change --- src/Makefile | 2 +- src/nnue/nnue_common.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Makefile b/src/Makefile index c00b60b5a56..e82b066b630 100644 --- a/src/Makefile +++ b/src/Makefile @@ -569,7 +569,7 @@ help: build: config-sanity $(MAKE) ARCH=$(ARCH) COMP=$(COMP) all -profile-build: config-sanity objclean profileclean net +profile-build: net config-sanity objclean profileclean @echo "" @echo "Step 1/4. Building instrumented executable ..." $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_make) diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index cb1251c58aa..eab7d258816 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -44,7 +44,7 @@ // compiled with older g++ crashes because the output memory is not aligned // even though alignas is specified. #if defined(USE_AVX2) -#if defined(__GNUC__ ) && (__GNUC__ < 9) +#if defined(__GNUC__ ) && (__GNUC__ < 9) && defined(_WIN32) #define _mm256_loadA_si256 _mm256_loadu_si256 #define _mm256_storeA_si256 _mm256_storeu_si256 #else @@ -54,7 +54,7 @@ #endif #if defined(USE_AVX512) -#if defined(__GNUC__ ) && (__GNUC__ < 9) +#if defined(__GNUC__ ) && (__GNUC__ < 9) && defined(_WIN32) #define _mm512_loadA_si512 _mm512_loadu_si512 #define _mm512_storeA_si512 _mm512_storeu_si512 #else From 6bc0256292cf51d390fee0cb78963da884dc2677 Mon Sep 17 00:00:00 2001 From: Daylen Yang Date: Tue, 11 Aug 2020 12:02:48 -0700 Subject: [PATCH 0330/1766] Use posix_memalign for Apple Silicon instead of _mm_malloc fails to build on that target, because of missing Intel Intrinsics. macOS has posix_memalign() since ~2014 so we can simplify the code and just use that for all Apple platforms. closes https://github.com/official-stockfish/Stockfish/pull/2985 No functional change. --- src/misc.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index fc3746cf69a..aeb3c912bf2 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -51,7 +51,7 @@ typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY); #include #endif -#if (defined(__APPLE__) && defined(_LIBCPP_HAS_C11_FEATURES)) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) && !defined(_WIN32)) +#if defined(__APPLE__) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) && !defined(_WIN32)) #define POSIXALIGNEDALLOC #include #endif @@ -328,7 +328,7 @@ void* std_aligned_alloc(size_t alignment, size_t size) { if(posix_memalign(&pointer, alignment, size) == 0) return pointer; return nullptr; -#elif (defined(_WIN32) || (defined(__APPLE__) && !defined(_LIBCPP_HAS_C11_FEATURES))) +#elif defined(_WIN32) return _mm_malloc(size, alignment); #else return std::aligned_alloc(alignment, size); @@ -338,7 +338,7 @@ void* std_aligned_alloc(size_t alignment, size_t size) { void std_aligned_free(void* ptr) { #if defined(POSIXALIGNEDALLOC) free(ptr); -#elif (defined(_WIN32) || (defined(__APPLE__) && !defined(_LIBCPP_HAS_C11_FEATURES))) +#elif defined(_WIN32) _mm_free(ptr); #else free(ptr); From dd63b98fb06e050aa961fbad6fd1f9316f2b17df Mon Sep 17 00:00:00 2001 From: mstembera Date: Tue, 11 Aug 2020 12:59:39 -0700 Subject: [PATCH 0331/1766] Add support for VNNI Adds support for Vector Neural Network Instructions (avx512), as available on Intel Cascade Lake The _mm512_dpbusd_epi32() intrinsic (vpdpbusd instruction) is taylor made for NNUE. on a cascade lake CPU (AWS C5.24x.large, gcc 10) NNUE eval is at roughly 78% nps of classical (single core test) bench 1024 1 24 default depth: target classical NNUE ratio vnni 2207232 1725987 78.20 avx512 2216789 1671734 75.41 avx2 2194006 1611263 73.44 modern 2185001 1352469 61.90 closes https://github.com/official-stockfish/Stockfish/pull/2987 No functional change --- src/Makefile | 25 +++++++++++++++++++++++++ src/misc.cpp | 3 +++ src/nnue/layers/affine_transform.h | 14 +++++++++++++- 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index e82b066b630..0804cdd51a9 100644 --- a/src/Makefile +++ b/src/Makefile @@ -73,6 +73,7 @@ endif # avx2 = yes/no --- -mavx2 --- Use Intel Advanced Vector Extensions 2 # pext = yes/no --- -DUSE_PEXT --- Use pext x86_64 asm-instruction # avx512 = yes/no --- -mavx512bw --- Use Intel Advanced Vector Extensions 512 +# vnni = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 512 # neon = yes/no --- -DUSE_NEON --- Use ARM SIMD architecture # # Note that Makefile is space sensitive, so when adding new architectures @@ -93,6 +94,7 @@ sse41 = no avx2 = no pext = no avx512 = no +vnni = no neon = no ARCH = x86-64-modern @@ -190,6 +192,19 @@ ifeq ($(ARCH),x86-64-avx512) avx512 = yes endif +ifeq ($(ARCH),x86-64-vnni) + arch = x86_64 + prefetch = yes + popcnt = yes + sse = yes + ssse3 = yes + sse41 = yes + avx2 = yes + pext = yes + avx512 = yes + vnni = yes +endif + ifeq ($(ARCH),armv7) arch = armv7 prefetch = yes @@ -420,6 +435,13 @@ ifeq ($(avx512),yes) endif endif +ifeq ($(vnni),yes) + CXXFLAGS += -DUSE_VNNI + ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + CXXFLAGS += -mavx512vnni -mavx512dq -mavx512vl + endif +endif + ifeq ($(sse41),yes) CXXFLAGS += -DUSE_SSE41 ifeq ($(comp),$(filter $(comp),gcc clang mingw)) @@ -522,6 +544,7 @@ help: @echo "" @echo "Supported archs:" @echo "" + @echo "x86-64-vnni > x86 64-bit with vnni support" @echo "x86-64-avx512 > x86 64-bit with avx512 support" @echo "x86-64-bmi2 > x86 64-bit with bmi2 support" @echo "x86-64-avx2 > x86 64-bit with avx2 support" @@ -640,6 +663,7 @@ config-sanity: @echo "avx2: '$(avx2)'" @echo "pext: '$(pext)'" @echo "avx512: '$(avx512)'" + @echo "vnni: '$(vnni)'" @echo "neon: '$(neon)'" @echo "" @echo "Flags:" @@ -664,6 +688,7 @@ config-sanity: @test "$(avx2)" = "yes" || test "$(avx2)" = "no" @test "$(pext)" = "yes" || test "$(pext)" = "no" @test "$(avx512)" = "yes" || test "$(avx512)" = "no" + @test "$(vnni)" = "yes" || test "$(vnni)" = "no" @test "$(neon)" = "yes" || test "$(neon)" = "no" @test "$(comp)" = "gcc" || test "$(comp)" = "icc" || test "$(comp)" = "mingw" || test "$(comp)" = "clang" diff --git a/src/misc.cpp b/src/misc.cpp index aeb3c912bf2..ab52d30bb15 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -219,6 +219,9 @@ const std::string compiler_info() { compiler += "\nCompilation settings include: "; compiler += (Is64Bit ? " 64bit" : " 32bit"); + #if defined(USE_VNNI) + compiler += " VNNI"; + #endif #if defined(USE_AVX512) compiler += " AVX512"; #endif diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 8d2acd1852e..322e3240250 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -79,8 +79,10 @@ namespace Eval::NNUE::Layers { #if defined(USE_AVX512) constexpr IndexType kNumChunks = kPaddedInputDimensions / (kSimdWidth * 2); - const __m512i kOnes = _mm512_set1_epi16(1); const auto input_vector = reinterpret_cast(input); + #if !defined(USE_VNNI) + const __m512i kOnes = _mm512_set1_epi16(1); + #endif #elif defined(USE_AVX2) constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; @@ -113,9 +115,13 @@ namespace Eval::NNUE::Layers { __m512i sum = _mm512_setzero_si512(); const auto row = reinterpret_cast(&weights_[offset]); for (IndexType j = 0; j < kNumChunks; ++j) { + #if defined(USE_VNNI) + sum = _mm512_dpbusd_epi32(sum, _mm512_loadA_si512(&input_vector[j]), _mm512_load_si512(&row[j])); + #else __m512i product = _mm512_maddubs_epi16(_mm512_loadA_si512(&input_vector[j]), _mm512_load_si512(&row[j])); product = _mm512_madd_epi16(product, kOnes); sum = _mm512_add_epi32(sum, product); + #endif } // Note: Changing kMaxSimdWidth from 32 to 64 breaks loading existing networks. @@ -125,8 +131,14 @@ namespace Eval::NNUE::Layers { { const auto iv256 = reinterpret_cast(&input_vector[kNumChunks]); const auto row256 = reinterpret_cast(&row[kNumChunks]); + #if defined(USE_VNNI) + __m256i product256 = _mm256_dpbusd_epi32( + _mm512_castsi512_si256(sum), _mm256_loadA_si256(&iv256[0]), _mm256_load_si256(&row256[0])); + sum = _mm512_inserti32x8(sum, product256, 0); + #else __m256i product256 = _mm256_maddubs_epi16(_mm256_loadA_si256(&iv256[0]), _mm256_load_si256(&row256[0])); sum = _mm512_add_epi32(sum, _mm512_cvtepi16_epi32(product256)); + #endif } output[i] = _mm512_reduce_add_epi32(sum) + biases_[i]; From 69cfe28f315b559cb1a07c0806266aa2850b5d4b Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 12 Aug 2020 17:21:12 +0200 Subject: [PATCH 0332/1766] Output the SSE2 flag in compiler_info was missing in the list of outputs, slightly reorder flags. explicitly add -msse2 if USE_SSE2 (is implicit already, -msse -m64). closes https://github.com/official-stockfish/Stockfish/pull/2990 No functional change. --- src/Makefile | 2 +- src/misc.cpp | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Makefile b/src/Makefile index 0804cdd51a9..027cc3e3390 100644 --- a/src/Makefile +++ b/src/Makefile @@ -468,7 +468,7 @@ ifeq ($(neon),yes) endif ifeq ($(arch),x86_64) - CXXFLAGS += -DUSE_SSE2 + CXXFLAGS += -msse2 -DUSE_SSE2 endif ### 3.7 pext diff --git a/src/misc.cpp b/src/misc.cpp index ab52d30bb15..1cee4726f3b 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -225,6 +225,7 @@ const std::string compiler_info() { #if defined(USE_AVX512) compiler += " AVX512"; #endif + compiler += (HasPext ? " BMI2" : ""); #if defined(USE_AVX2) compiler += " AVX2"; #endif @@ -234,11 +235,14 @@ const std::string compiler_info() { #if defined(USE_SSSE3) compiler += " SSSE3"; #endif - compiler += (HasPext ? " BMI2" : ""); - compiler += (HasPopCnt ? " POPCNT" : ""); + #if defined(USE_SSE2) + compiler += " SSE2"; + #endif + compiler += (HasPopCnt ? " POPCNT" : ""); #if defined(USE_MMX) compiler += " MMX"; #endif + #if !defined(NDEBUG) compiler += " DEBUG"; #endif From 67e48418afd58dd69708dcd67dea6161f61ef76f Mon Sep 17 00:00:00 2001 From: Sergio Vieri Date: Wed, 12 Aug 2020 23:21:21 +0800 Subject: [PATCH 0333/1766] Update default net to nn-82215d0fd0df.nnue Net created at: 20200812-2257 passed STC: https://tests.stockfishchess.org/tests/view/5f340ca99e5f2effc089da17 LLR: 2.96 (-2.94,2.94) {-0.50,1.50} Total: 5744 W: 756 L: 627 D: 4361 Ptnml(0-2): 28, 485, 1731, 586, 42 passed LTC: https://tests.stockfishchess.org/tests/view/5f341eba9e5f2effc089da23 LLR: 2.94 (-2.94,2.94) {0.25,1.75} Total: 17136 W: 1041 L: 917 D: 15178 Ptnml(0-2): 13, 813, 6807, 907, 28 closes https://github.com/official-stockfish/Stockfish/pull/2992 Bench: 3935117 --- src/ucioption.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ucioption.cpp b/src/ucioption.cpp index b0689d6dd36..0a35d01bbf0 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -79,7 +79,7 @@ void init(OptionsMap& o) { o["Syzygy50MoveRule"] << Option(true); o["SyzygyProbeLimit"] << Option(7, 0, 7); o["Use NNUE"] << Option(false, on_use_NNUE); - o["EvalFile"] << Option("nn-112bb1c8cdb5.nnue", on_eval_file); + o["EvalFile"] << Option("nn-82215d0fd0df.nnue", on_eval_file); } From e8ea215a13e009b78a148fda831392eb3224107e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Thu, 13 Aug 2020 13:40:06 +0200 Subject: [PATCH 0334/1766] Clean-up Makefile help Do not show the details of the default architecture for a simple "make help" invocation, as the details are most likely to confuse beginners. Instead we make it clear which architecture is the default and put an example at the end of the Makefile as an incentative to use "make help ARCH=blah" to discover the flags used by the different architectures. ``` make help make help ARCH=x86-64-ssse3 ``` Also clean-up and modernize a bit the Makefile examples while at it. closes https://github.com/official-stockfish/Stockfish/pull/2996 No functional change --- src/Makefile | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/Makefile b/src/Makefile index 027cc3e3390..a9fb7b817da 100644 --- a/src/Makefile +++ b/src/Makefile @@ -81,6 +81,11 @@ endif # at the end of the line for flag values. ### 2.1. General and architecture defaults + +ifeq ($(ARCH),) + empty_arch = yes +endif + optimize = yes debug = no sanitize = no @@ -99,6 +104,7 @@ neon = no ARCH = x86-64-modern ### 2.2 Architecture specific + ifeq ($(ARCH),general-32) arch = any bits = 32 @@ -141,16 +147,7 @@ ifeq ($(ARCH),x86-64-ssse3) ssse3 = yes endif -ifeq ($(ARCH),x86-64-modern) - arch = x86_64 - prefetch = yes - popcnt = yes - sse = yes - ssse3 = yes - sse41 = yes -endif - -ifeq ($(ARCH),x86-64-sse41-popcnt) +ifeq ($(ARCH),$(filter $(ARCH),x86-64-sse41-popcnt x86-64-modern)) arch = x86_64 prefetch = yes popcnt = yes @@ -535,12 +532,13 @@ help: @echo "" @echo "Supported targets:" @echo "" + @echo "help > Display architecture details" @echo "build > Standard build" - @echo "profile-build > Standard build with PGO" + @echo "net > Download the default nnue net" + @echo "profile-build > Faster build (with profile-guided optimization)" @echo "strip > Strip executable" @echo "install > Install executable" @echo "clean > Clean up" - @echo "net > Download the default nnue net" @echo "" @echo "Supported archs:" @echo "" @@ -549,7 +547,7 @@ help: @echo "x86-64-bmi2 > x86 64-bit with bmi2 support" @echo "x86-64-avx2 > x86 64-bit with avx2 support" @echo "x86-64-sse41-popcnt > x86 64-bit with sse41 and popcnt support" - @echo "x86-64-modern > the same as previous (x86-64-sse41-popcnt)" + @echo "x86-64-modern > common modern CPU, currently x86-64-sse41-popcnt" @echo "x86-64-ssse3 > x86 64-bit with ssse3 support" @echo "x86-64-sse3-popcnt > x86 64-bit with sse3 and popcnt support" @echo "x86-64 > x86 64-bit generic" @@ -572,17 +570,20 @@ help: @echo "" @echo "Simple examples. If you don't know what to do, you likely want to run: " @echo "" - @echo "make -j build ARCH=x86-64 (This is for 64-bit systems)" - @echo "make -j build ARCH=x86-32 (This is for 32-bit systems)" + @echo "make -j build ARCH=x86-64 (A portable, slow compile for 64-bit systems)" + @echo "make -j build ARCH=x86-32 (A portable, slow compile for 32-bit systems)" @echo "" - @echo "Advanced examples, for experienced users: " + @echo "Advanced examples, for experienced users looking for performance: " @echo "" - @echo "make -j build ARCH=x86-64-modern COMP=clang" - @echo "make -j profile-build ARCH=x86-64-bmi2 COMP=gcc COMPCXX=g++-4.8" - @echo "" - @echo "The selected architecture $(ARCH) enables the following configuration: " + @echo "make help ARCH=x86-64-bmi2" + @echo "make -j profile-build ARCH=x86-64-bmi2 COMP=gcc COMPCXX=g++-9.0" + @echo "make -j build ARCH=x86-64-ssse3 COMP=clang" @echo "" +ifneq ($(empty_arch), yes) + @echo "-------------------------------\n" + @echo "The selected architecture $(ARCH) will enable the following configuration: " @$(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity +endif .PHONY: help build profile-build strip install clean net objclean profileclean \ From ce009ea1aaecc577bbdf208cef8e61dd1827a18e Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 13 Aug 2020 22:54:13 +0200 Subject: [PATCH 0335/1766] Verify SHA of downloaded net file check SHA of the available and downloaded file. Document the format requirement on the default net. Also allow curl to make possibly insecure connections, as needed for old curl. fixes https://github.com/official-stockfish/Stockfish/issues/2998 closes https://github.com/official-stockfish/Stockfish/pull/3000 No functional change. --- src/Makefile | 4 +++- src/ucioption.cpp | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index a9fb7b817da..38f607cb24f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -624,8 +624,10 @@ net: $(eval nnuenet := $(shell grep EvalFile ucioption.cpp | grep Option | sed 's/.*\(nn-[a-z0-9]\{12\}.nnue\).*/\1/')) @echo "Default net: $(nnuenet)" $(eval nnuedownloadurl := https://tests.stockfishchess.org/api/nn/$(nnuenet)) - $(eval curl_or_wget := $(shell if hash curl 2>/dev/null; then echo "curl -sL"; elif hash wget 2>/dev/null; then echo "wget -qO-"; fi)) + $(eval curl_or_wget := $(shell if hash curl 2>/dev/null; then echo "curl -skL"; elif hash wget 2>/dev/null; then echo "wget -qO-"; fi)) @if test -f "$(nnuenet)"; then echo "Already available."; else echo "Downloading $(nnuedownloadurl)"; $(curl_or_wget) $(nnuedownloadurl) > $(nnuenet); fi + $(eval shasum_command := $(shell if hash shasum 2>/dev/null; then echo "shasum -a 256 "; elif hash sha256sum 2>/dev/null; then echo "sha256sum "; fi)) + @if [ "$(nnuenet)" != "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then echo "Failed download or $(nnuenet) corrupted, please delete!"; exit 1; fi # clean binaries and objects objclean: diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 0a35d01bbf0..2b66a47545a 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -79,6 +79,8 @@ void init(OptionsMap& o) { o["Syzygy50MoveRule"] << Option(true); o["SyzygyProbeLimit"] << Option(7, 0, 7); o["Use NNUE"] << Option(false, on_use_NNUE); + // The default must follow the format nn-[SHA256 first 12 digits].nnue + // for the build process (profile-build and fishtest) to work. o["EvalFile"] << Option("nn-82215d0fd0df.nnue", on_eval_file); } From e5f450cf0bfe5a34dd4ea51a5592a71be4514601 Mon Sep 17 00:00:00 2001 From: Miguel Lahoz Date: Mon, 10 Aug 2020 22:57:11 +0800 Subject: [PATCH 0336/1766] Also dampen NNUE eval with 50 move rule Move the existing dampening function last so that NNUE evaluations are also handled as we approach the 50 move rule. STC: LLR: 2.95 (-2.94,2.94) {-0.50,1.50} Total: 4792 W: 695 L: 561 D: 3536 Ptnml(0-2): 19, 420, 1422, 478, 57 https://tests.stockfishchess.org/tests/view/5f3164179081672066537534 LTC: LLR: 8.62 (-2.94,2.94) {0.25,1.75} Total: 286744 W: 18494 L: 17430 D: 250820 Ptnml(0-2): 418, 14886, 111745, 15860, 463 https://tests.stockfishchess.org/tests/view/5f316b039081672066537541 closes https://github.com/official-stockfish/Stockfish/pull/3004 Bench: 4001800 --- src/evaluate.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index caab2979d35..00fd2005ed7 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -927,9 +927,6 @@ namespace { // Side to move point of view v = (pos.side_to_move() == WHITE ? v : -v) + Tempo; - // Damp down the evaluation linearly when shuffling - v = v * (100 - pos.rule50_count()) / 100; - return v; } @@ -941,14 +938,15 @@ namespace { Value Eval::evaluate(const Position& pos) { - if (Eval::useNNUE) - { - Value v = eg_value(pos.psq_score()); - // Take NNUE eval only on balanced positions - if (abs(v) < NNUEThreshold) - return NNUE::evaluate(pos) + Tempo; - } - return Evaluation(pos).value(); + bool classical = !Eval::useNNUE + || abs(eg_value(pos.psq_score())) >= NNUEThreshold; + Value v = classical ? Evaluation(pos).value() + : NNUE::evaluate(pos) + Tempo; + + // Damp down the evaluation linearly when shuffling + v = v * (100 - pos.rule50_count()) / 100; + + return v; } /// trace() is like evaluate(), but instead of returning a value, it returns From 6eb186c97e9d808970d0b1369bcd7aca60612e26 Mon Sep 17 00:00:00 2001 From: mstembera Date: Fri, 14 Aug 2020 04:49:33 -0700 Subject: [PATCH 0337/1766] Try to match relative magnitude of NNUE eval to classical The idea is that since we are mixing NNUE and classical evals matching their magnitudes closer allows for better comparisons. STC https://tests.stockfishchess.org/tests/view/5f35a65411a9b1a1dbf18e2b LLR: 2.94 (-2.94,2.94) {-0.50,1.50} Total: 9840 W: 1150 L: 1027 D: 7663 Ptnml(0-2): 49, 772, 3175, 855, 69 LTC https://tests.stockfishchess.org/tests/view/5f35bcbe11a9b1a1dbf18e47 LLR: 2.93 (-2.94,2.94) {0.25,1.75} Total: 44424 W: 2492 L: 2294 D: 39638 Ptnml(0-2): 42, 2015, 17915, 2183, 57 also corrects the location to clamp the evaluation (non-function on bench). closes https://github.com/official-stockfish/Stockfish/pull/3003 bench: 3905447 --- src/evaluate.cpp | 5 ++++- src/nnue/evaluate_nnue.cpp | 5 +---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 00fd2005ed7..a453fa0f7f4 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -941,11 +941,14 @@ Value Eval::evaluate(const Position& pos) { bool classical = !Eval::useNNUE || abs(eg_value(pos.psq_score())) >= NNUEThreshold; Value v = classical ? Evaluation(pos).value() - : NNUE::evaluate(pos) + Tempo; + : NNUE::evaluate(pos) * 5 / 4 + Tempo; // Damp down the evaluation linearly when shuffling v = v * (100 - pos.rule50_count()) / 100; + // Guarantee evalution outside of TB range + v = Utility::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); + return v; } diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index af0894b2f9e..a6ece8e2427 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -159,10 +159,7 @@ namespace Eval::NNUE { // Evaluation function. Perform differential calculation. Value evaluate(const Position& pos) { - Value v = ComputeScore(pos, false); - v = Utility::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); - - return v; + return ComputeScore(pos, false); } // Evaluation function. Perform full calculation. From cd0b8b4cf28208fffef931322749205a0ddc6066 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 14 Aug 2020 22:18:12 +0200 Subject: [PATCH 0338/1766] Use NNUE more for fortresses Increases the use of NNUE evaluation in positions without captures/pawn moves, by increasing the NNUEThreshold threshold with rule50_count. This patch will force Stockfish to use NNUE eval more and more in materially unbalanced positions, when it seems that the classical eval is struggling to win and only manages to shuffle. This will ask the (slower) NNUE eval to double-check the potential fortress branches of the search tree, but only when necessary. passed STC: https://tests.stockfishchess.org/tests/view/5f36f1bf11a9b1a1dbf192d8 LLR: 2.93 (-2.94,2.94) {-0.50,1.50} Total: 51824 W: 5836 L: 5653 D: 40335 Ptnml(0-2): 264, 4356, 16512, 4493, 287 passed LTC: https://tests.stockfishchess.org/tests/view/5f37836111a9b1a1dbf1936d LLR: 2.93 (-2.94,2.94) {0.25,1.75} Total: 29768 W: 1747 L: 1590 D: 26431 Ptnml(0-2): 33, 1347, 11977, 1484, 43 closes https://github.com/official-stockfish/Stockfish/pull/3011 Bench: 4173967 --- src/evaluate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index a453fa0f7f4..3a620a78305 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -939,7 +939,7 @@ namespace { Value Eval::evaluate(const Position& pos) { bool classical = !Eval::useNNUE - || abs(eg_value(pos.psq_score())) >= NNUEThreshold; + || abs(eg_value(pos.psq_score())) >= NNUEThreshold * (16 + pos.rule50_count()) / 16; Value v = classical ? Evaluation(pos).value() : NNUE::evaluate(pos) * 5 / 4 + Tempo; From 8cf43c6317665295eece747ed1589ee33a435d2c Mon Sep 17 00:00:00 2001 From: Daylen Yang Date: Fri, 14 Aug 2020 19:53:46 -0700 Subject: [PATCH 0339/1766] Display NEON in compiler string if NEON intrinsics are being used and USE_NEON is defined. closes https://github.com/official-stockfish/Stockfish/pull/3008 No functional change --- src/misc.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/misc.cpp b/src/misc.cpp index 1cee4726f3b..459ea10035b 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -242,6 +242,9 @@ const std::string compiler_info() { #if defined(USE_MMX) compiler += " MMX"; #endif + #if defined(USE_NEON) + compiler += " NEON"; + #endif #if !defined(NDEBUG) compiler += " DEBUG"; From 72dc7a5c54554a8c7c4bf68aa7de2d4de05f3294 Mon Sep 17 00:00:00 2001 From: syzygy1 <3028851+syzygy1@users.noreply.github.com> Date: Sat, 15 Aug 2020 16:50:39 +0200 Subject: [PATCH 0340/1766] Assume network file is in little-endian byte order This patch fixes the byte order when reading 16- and 32-bit values from the network file on a big-endian machine. Bytes are ordered in read_le() using unsigned arithmetic, which doesn't need tricks to determine the endianness of the machine. Unfortunately the compiler doesn't seem to be able to optimise the ordering operation, but reading in the weights is not a time-critical operation and the extra time it takes should not be noticeable. Big endian systems are still untested with NNUE. fixes #3007 closes https://github.com/official-stockfish/Stockfish/pull/3009 No functional change. --- src/nnue/evaluate_nnue.cpp | 8 ++++---- src/nnue/layers/affine_transform.h | 9 ++++----- src/nnue/nnue_common.h | 19 +++++++++++++++++++ src/nnue/nnue_feature_transformer.h | 8 ++++---- 4 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index a6ece8e2427..3aa85943944 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -77,7 +77,7 @@ namespace Eval::NNUE { bool ReadParameters(std::istream& stream, const AlignedPtr& pointer) { std::uint32_t header; - stream.read(reinterpret_cast(&header), sizeof(header)); + header = read_le(stream); if (!stream || header != T::GetHashValue()) return false; return pointer->ReadParameters(stream); } @@ -96,9 +96,9 @@ namespace Eval::NNUE { std::uint32_t* hash_value, std::string* architecture) { std::uint32_t version, size; - stream.read(reinterpret_cast(&version), sizeof(version)); - stream.read(reinterpret_cast(hash_value), sizeof(*hash_value)); - stream.read(reinterpret_cast(&size), sizeof(size)); + version = read_le(stream); + *hash_value = read_le(stream); + size = read_le(stream); if (!stream || version != kVersion) return false; architecture->resize(size); stream.read(&(*architecture)[0], size); diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 322e3240250..bac258e8acf 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -62,11 +62,10 @@ namespace Eval::NNUE::Layers { // Read network parameters bool ReadParameters(std::istream& stream) { if (!previous_layer_.ReadParameters(stream)) return false; - stream.read(reinterpret_cast(biases_), - kOutputDimensions * sizeof(BiasType)); - stream.read(reinterpret_cast(weights_), - kOutputDimensions * kPaddedInputDimensions * - sizeof(WeightType)); + for (std::size_t i = 0; i < kOutputDimensions; ++i) + biases_[i] = read_le(stream); + for (std::size_t i = 0; i < kOutputDimensions * kPaddedInputDimensions; ++i) + weights_[i] = read_le(stream); return !stream.fail(); } diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index eab7d258816..61f18aeec84 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -21,6 +21,9 @@ #ifndef NNUE_COMMON_H_INCLUDED #define NNUE_COMMON_H_INCLUDED +#include +#include + #if defined(USE_AVX2) #include @@ -101,6 +104,22 @@ namespace Eval::NNUE { return (n + base - 1) / base * base; } + // Read a signed or unsigned integer from a stream in little-endian order + template + inline IntType read_le(std::istream& stream) { + // Read the relevant bytes from the stream in little-endian order + std::uint8_t u[sizeof(IntType)]; + stream.read(reinterpret_cast(u), sizeof(IntType)); + // Use unsigned arithmetic to convert to machine order + typename std::make_unsigned::type v = 0; + for (std::size_t i = 0; i < sizeof(IntType); ++i) + v = (v << 8) | u[sizeof(IntType) - i - 1]; + // Copy the machine-ordered bytes into a potentially signed value + IntType w; + std::memcpy(&w, &v, sizeof(IntType)); + return w; + } + } // namespace Eval::NNUE #endif // #ifndef NNUE_COMMON_H_INCLUDED diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 40f2603d9d5..4db9be9f0f0 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -55,10 +55,10 @@ namespace Eval::NNUE { // Read network parameters bool ReadParameters(std::istream& stream) { - stream.read(reinterpret_cast(biases_), - kHalfDimensions * sizeof(BiasType)); - stream.read(reinterpret_cast(weights_), - kHalfDimensions * kInputDimensions * sizeof(WeightType)); + for (std::size_t i = 0; i < kHalfDimensions; ++i) + biases_[i] = read_le(stream); + for (std::size_t i = 0; i < kHalfDimensions * kInputDimensions; ++i) + weights_[i] = read_le(stream); return !stream.fail(); } From 65572de4a79ab017c19d85eacee865afe7bfc7c1 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 16 Aug 2020 13:21:07 +0200 Subject: [PATCH 0341/1766] Add further targets to travis testing general-32, general-64 and help closes https://github.com/official-stockfish/Stockfish/pull/3014 No functional change --- .travis.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0dd380479cc..45f1bd3d34f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -51,11 +51,12 @@ script: - export benchref=$(cat git_sig) - echo "Reference bench:" $benchref - # # Compiler version string - $COMPILER -v - # + # test help target + - make help + # Verify bench number against various builds - export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" - make clean && make -j2 ARCH=x86-64-modern optimize=no debug=yes build && ../tests/signature.sh $benchref @@ -64,8 +65,10 @@ script: - make clean && make -j2 ARCH=x86-64-ssse3 build && ../tests/signature.sh $benchref - make clean && make -j2 ARCH=x86-64-sse3-popcnt build && ../tests/signature.sh $benchref - make clean && make -j2 ARCH=x86-64 build && ../tests/signature.sh $benchref + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=general-64 build && ../tests/signature.sh $benchref; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32 optimize=no debug=yes build && ../tests/signature.sh $benchref; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32 build && ../tests/signature.sh $benchref; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=general-32 build && ../tests/signature.sh $benchref; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32-old build && ../tests/signature.sh $benchref; fi - if [[ "$TRAVIS_OS_NAME" == "linux" && "$COMP" == "gcc" ]]; then make clean && make -j2 ARCH=x86-64-modern profile-build && ../tests/signature.sh $benchref; fi From 81d716f5ccff3f0898ae985b9ef69f79d014bdc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sun, 16 Aug 2020 21:46:54 +0200 Subject: [PATCH 0342/1766] Reformat code in little-endian patch Reformat code and rename the function to "read_little_endian()" in the recent commit by Ronald de Man for support of big endian systems. closes https://github.com/official-stockfish/Stockfish/pull/3016 No functional change ----- Recommended net: https://tests.stockfishchess.org/api/nn/nn-82215d0fd0df.nnue --- src/nnue/evaluate_nnue.cpp | 14 +++++++------- src/nnue/layers/affine_transform.h | 4 ++-- src/nnue/nnue_common.h | 30 +++++++++++++++-------------- src/nnue/nnue_feature_transformer.h | 4 ++-- 4 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 3aa85943944..dfbb1ac2562 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -77,7 +77,7 @@ namespace Eval::NNUE { bool ReadParameters(std::istream& stream, const AlignedPtr& pointer) { std::uint32_t header; - header = read_le(stream); + header = read_little_endian(stream); if (!stream || header != T::GetHashValue()) return false; return pointer->ReadParameters(stream); } @@ -92,13 +92,13 @@ namespace Eval::NNUE { } // Read network header - bool ReadHeader(std::istream& stream, - std::uint32_t* hash_value, std::string* architecture) { - + bool ReadHeader(std::istream& stream, std::uint32_t* hash_value, std::string* architecture) + { std::uint32_t version, size; - version = read_le(stream); - *hash_value = read_le(stream); - size = read_le(stream); + + version = read_little_endian(stream); + *hash_value = read_little_endian(stream); + size = read_little_endian(stream); if (!stream || version != kVersion) return false; architecture->resize(size); stream.read(&(*architecture)[0], size); diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index bac258e8acf..7ac5a1c099f 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -63,9 +63,9 @@ namespace Eval::NNUE::Layers { bool ReadParameters(std::istream& stream) { if (!previous_layer_.ReadParameters(stream)) return false; for (std::size_t i = 0; i < kOutputDimensions; ++i) - biases_[i] = read_le(stream); + biases_[i] = read_little_endian(stream); for (std::size_t i = 0; i < kOutputDimensions * kPaddedInputDimensions; ++i) - weights_[i] = read_le(stream); + weights_[i] = read_little_endian(stream); return !stream.fail(); } diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index 61f18aeec84..4c93e3d164d 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -101,23 +101,25 @@ namespace Eval::NNUE { // Round n up to be a multiple of base template constexpr IntType CeilToMultiple(IntType n, IntType base) { - return (n + base - 1) / base * base; + return (n + base - 1) / base * base; } - // Read a signed or unsigned integer from a stream in little-endian order + // read_little_endian() is our utility to read an integer (signed or unsigned, any size) + // from a stream in little-endian order. We swap the byte order after the read if + // necessary to return a result with the byte ordering of the compiling machine. template - inline IntType read_le(std::istream& stream) { - // Read the relevant bytes from the stream in little-endian order - std::uint8_t u[sizeof(IntType)]; - stream.read(reinterpret_cast(u), sizeof(IntType)); - // Use unsigned arithmetic to convert to machine order - typename std::make_unsigned::type v = 0; - for (std::size_t i = 0; i < sizeof(IntType); ++i) - v = (v << 8) | u[sizeof(IntType) - i - 1]; - // Copy the machine-ordered bytes into a potentially signed value - IntType w; - std::memcpy(&w, &v, sizeof(IntType)); - return w; + inline IntType read_little_endian(std::istream& stream) { + + IntType result; + std::uint8_t u[sizeof(IntType)]; + typename std::make_unsigned::type v = 0; + + stream.read(reinterpret_cast(u), sizeof(IntType)); + for (std::size_t i = 0; i < sizeof(IntType); ++i) + v = (v << 8) | u[sizeof(IntType) - i - 1]; + + std::memcpy(&result, &v, sizeof(IntType)); + return result; } } // namespace Eval::NNUE diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 4db9be9f0f0..43707610231 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -56,9 +56,9 @@ namespace Eval::NNUE { // Read network parameters bool ReadParameters(std::istream& stream) { for (std::size_t i = 0; i < kHalfDimensions; ++i) - biases_[i] = read_le(stream); + biases_[i] = read_little_endian(stream); for (std::size_t i = 0; i < kHalfDimensions * kInputDimensions; ++i) - weights_[i] = read_le(stream); + weights_[i] = read_little_endian(stream); return !stream.fail(); } From 0e17a89e4dee73bd46e496cf6bed467432f116e6 Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Mon, 17 Aug 2020 09:22:15 +0200 Subject: [PATCH 0343/1766] Simplify away the passed pawn extension STC https://tests.stockfishchess.org/tests/view/5f3955f0e98b6c64b3df41d7 LLR: 2.96 (-2.94,2.94) {-1.50,0.50} Total: 31992 W: 3611 L: 3548 D: 24833 Ptnml(0-2): 174, 2658, 10273, 2713, 178 LTC https://tests.stockfishchess.org/tests/view/5f399e41e98b6c64b3df4210 LLR: 3.01 (-2.94,2.94) {-1.50,0.50} Total: 29568 W: 1488 L: 1480 D: 26600 Ptnml(0-2): 40, 1272, 12142, 1300, 30 closes https://github.com/official-stockfish/Stockfish/pull/3017 bench: 3844671 ----- Recommended net: https://tests.stockfishchess.org/api/nn/nn-82215d0fd0df.nnue --- src/search.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c5b4332f721..83fb722faf6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1126,12 +1126,6 @@ namespace { && (pos.is_discovery_check_on_king(~us, move) || pos.see_ge(move))) extension = 1; - // Passed pawn extension - else if ( move == ss->killers[0] - && pos.advanced_pawn_push(move) - && pos.pawn_passed(us, to_sq(move))) - extension = 1; - // Last captures extension else if ( PieceValue[EG][pos.captured_piece()] > PawnValueEg && pos.non_pawn_material() <= 2 * RookValueMg) From 65b976439f8867e81682c0b66da6796ad3176177 Mon Sep 17 00:00:00 2001 From: notruck <56622488+notruck@users.noreply.github.com> Date: Sun, 16 Aug 2020 08:59:13 -0700 Subject: [PATCH 0344/1766] Support building for Android using NDK The easiest way to use the NDK in conjunction with this Makefile (tested on linux-x86_64): 1. Download the latest NDK (r21d) from Google from https://developer.android.com/ndk/downloads 2. Place and unzip the NDK in $HOME/ndk folder 3. Export the path variable e.g., `export PATH=$PATH:$HOME/ndk/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin` 4. cd to your Stockfish/src dir 5. Issue `make -j ARCH=armv8 COMP=ndk build` (use `ARCH=armv7` or `ARCH=armv7-neon` for older CPUs) 6. Optionally `make -j ARCH=armv8 COMP=ndk strip` 7. That's all. Enjoy! Improves support from Raspberry Pi (incomplete?) and compiling on arm in general closes https://github.com/official-stockfish/Stockfish/pull/3015 fixes https://github.com/official-stockfish/Stockfish/issues/2860 fixes https://github.com/official-stockfish/Stockfish/issues/2641 Support is still fragile as we're missing CI on these targets. Nevertheless tested with: ```bash # build crosses from ubuntu 20.04 on x86 to various arch/OS combos # tested with suitable packages installed # (build-essentials, mingw-w64, g++-arm-linux-gnueabihf, NDK (r21d) from google) # cross to Android export PATH=$HOME/ndk/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH make clean && make -j build ARCH=armv7 COMP=ndk && make -j build ARCH=armv7 COMP=ndk strip make clean && make -j build ARCH=armv7-neon COMP=ndk && make -j build ARCH=armv7-neon COMP=ndk strip make clean && make -j build ARCH=armv8 COMP=ndk && make -j build ARCH=armv8 COMP=ndk strip # cross to Raspberry Pi make clean && make -j build ARCH=armv7 COMP=gcc COMPILER=arm-linux-gnueabihf-g++ make clean && make -j build ARCH=armv7-neon COMP=gcc COMPILER=arm-linux-gnueabihf-g++ # cross to Windows make clean && make -j build ARCH=x86-64-modern COMP=mingw ``` No functional change --- AUTHORS | 1 + src/Makefile | 65 ++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/AUTHORS b/AUTHORS index 41b89705c97..d8f4d30e7c2 100644 --- a/AUTHORS +++ b/AUTHORS @@ -127,6 +127,7 @@ Niklas Fiekas (niklasf) Nikolay Kostov (NikolayIT) Nguyen Pham (nguyenpham) Norman Schmidt (FireFather) +notruck Ondrej Mosnáček (WOnder93) Oskar Werkelin Ahlin Pablo Vazquez diff --git a/src/Makefile b/src/Makefile index 38f607cb24f..0f458aa185d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -102,6 +102,7 @@ avx512 = no vnni = no neon = no ARCH = x86-64-modern +STRIP = strip ### 2.2 Architecture specific @@ -208,6 +209,14 @@ ifeq ($(ARCH),armv7) bits = 32 endif +ifeq ($(ARCH),armv7-neon) + arch = armv7 + prefetch = yes + popcnt = yes + neon = yes + bits = 32 +endif + ifeq ($(ARCH),armv8) arch = armv8-a prefetch = yes @@ -251,7 +260,7 @@ ifeq ($(COMP),gcc) CXX=g++ CXXFLAGS += -pedantic -Wextra -Wshadow - ifeq ($(ARCH),$(filter $(ARCH),armv7 armv8)) + ifeq ($(arch),$(filter $(arch),armv7 armv8-a)) ifeq ($(OS),Android) CXXFLAGS += -m$(bits) LDFLAGS += -m$(bits) @@ -261,6 +270,10 @@ ifeq ($(COMP),gcc) LDFLAGS += -m$(bits) endif + ifeq ($(arch),$(filter $(arch),armv7)) + LDFLAGS += -latomic + endif + ifneq ($(KERNEL),Darwin) LDFLAGS += -Wl,--no-as-needed endif @@ -311,7 +324,7 @@ ifeq ($(COMP),clang) endif endif - ifeq ($(ARCH),$(filter $(ARCH),armv7 armv8)) + ifeq ($(arch),$(filter $(arch),armv7 armv8)) ifeq ($(OS),Android) CXXFLAGS += -m$(bits) LDFLAGS += -m$(bits) @@ -340,6 +353,25 @@ ifeq ($(KERNEL),Darwin) LDFLAGS += -arch $(arch) -mmacosx-version-min=10.14 endif +# To cross-compile for Android, NDK version r21 or later is recommended. +# In earlier NDK versions, you'll need to pass -fno-addrsig if using GNU binutils. +# Currently we don't know how to make PGO builds with the NDK yet. +ifeq ($(COMP),ndk) + CXXFLAGS += -stdlib=libc++ -fPIE + ifeq ($(arch),armv7) + comp=armv7a-linux-androideabi16-clang + CXX=armv7a-linux-androideabi16-clang++ + CXXFLAGS += -mthumb -march=armv7-a -mfloat-abi=softfp -mfpu=neon + STRIP=arm-linux-androideabi-strip + endif + ifeq ($(arch),armv8-a) + comp=aarch64-linux-android21-clang + CXX=aarch64-linux-android21-clang++ + STRIP=aarch64-linux-android-strip + endif + LDFLAGS += -static-libstdc++ -pie -lm -latomic +endif + ### Travis CI script uses COMPILER to overwrite CXX ifdef COMPILER COMPCXX=$(COMPILER) @@ -356,7 +388,9 @@ ifneq ($(comp),mingw) ifneq ($(OS),Android) # Haiku has pthreads in its libroot, so only link it in on other platforms ifneq ($(KERNEL),Haiku) - LDFLAGS += -lpthread + ifneq ($(COMP),ndk) + LDFLAGS += -lpthread + endif endif endif endif @@ -401,7 +435,6 @@ endif ifeq ($(prefetch),yes) ifeq ($(sse),yes) CXXFLAGS += -msse - DEPENDFLAGS += -msse endif else CXXFLAGS += -DNO_PREFETCH @@ -409,7 +442,7 @@ endif ### 3.6 popcnt ifeq ($(popcnt),yes) - ifeq ($(arch),$(filter $(arch),ppc64 armv8-a arm64)) + ifeq ($(arch),$(filter $(arch),ppc64 armv7 armv8-a arm64)) CXXFLAGS += -DUSE_POPCNT else ifeq ($(comp),icc) CXXFLAGS += -msse3 -DUSE_POPCNT @@ -418,6 +451,7 @@ ifeq ($(popcnt),yes) endif endif + ifeq ($(avx2),yes) CXXFLAGS += -DUSE_AVX2 ifeq ($(comp),$(filter $(comp),gcc clang mingw)) @@ -462,6 +496,11 @@ endif ifeq ($(neon),yes) CXXFLAGS += -DUSE_NEON + ifeq ($(KERNEL),Linux) + ifneq ($(COMP),ndk) + CXXFLAGS += -mfpu=neon + endif + endif endif ifeq ($(arch),x86_64) @@ -481,7 +520,10 @@ endif ### needs access to the optimization flags. ifeq ($(optimize),yes) ifeq ($(debug), no) - ifeq ($(comp),clang) + ifeq ($(COMP),ndk) + CXXFLAGS += -flto=thin + LDFLAGS += $(CXXFLAGS) + else ifeq ($(comp),clang) CXXFLAGS += -flto=thin LDFLAGS += $(CXXFLAGS) @@ -502,7 +544,7 @@ ifeq ($(debug), no) endif # To use LTO and static linking on windows, the tool chain requires a recent gcc: -# gcc version 10.1 in msys2 or TDM-GCC version 9.2 are know to work, older might not. +# gcc version 10.1 in msys2 or TDM-GCC version 9.2 are known to work, older might not. # So, only enable it for a cross from Linux by default. else ifeq ($(comp),mingw) ifeq ($(KERNEL),Linux) @@ -556,7 +598,8 @@ help: @echo "ppc-64 > PPC 64-bit" @echo "ppc-32 > PPC 32-bit" @echo "armv7 > ARMv7 32-bit" - @echo "armv8 > ARMv8 64-bit" + @echo "armv7-neon" > ARMv7 32-bit with popcnt and neon" + @echo "armv8 > ARMv8 64-bit with popcnt and neon" @echo "apple-silicon > Apple silicon ARM64" @echo "general-64 > unspecified 64-bit" @echo "general-32 > unspecified 32-bit" @@ -567,6 +610,7 @@ help: @echo "mingw > Gnu compiler with MinGW under Windows" @echo "clang > LLVM Clang compiler" @echo "icc > Intel compiler" + @echo "ndk > Google NDK to cross-compile for Android" @echo "" @echo "Simple examples. If you don't know what to do, you likely want to run: " @echo "" @@ -609,7 +653,7 @@ profile-build: net config-sanity objclean profileclean $(MAKE) ARCH=$(ARCH) COMP=$(COMP) profileclean strip: - strip $(EXE) + $(STRIP) $(EXE) install: -mkdir -p -m 755 $(BINDIR) @@ -693,7 +737,8 @@ config-sanity: @test "$(avx512)" = "yes" || test "$(avx512)" = "no" @test "$(vnni)" = "yes" || test "$(vnni)" = "no" @test "$(neon)" = "yes" || test "$(neon)" = "no" - @test "$(comp)" = "gcc" || test "$(comp)" = "icc" || test "$(comp)" = "mingw" || test "$(comp)" = "clang" + @test "$(comp)" = "gcc" || test "$(comp)" = "icc" || test "$(comp)" = "mingw" || test "$(comp)" = "clang" \ + || test "$(comp)" = "armv7a-linux-androideabi16-clang" || test "$(comp)" = "aarch64-linux-android21-clang" $(EXE): $(OBJS) +$(CXX) -o $@ $(OBJS) $(LDFLAGS) From 1c0b7bdf4f77b8160cebe8af96b28230e870a136 Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Mon, 17 Aug 2020 08:58:03 -0400 Subject: [PATCH 0345/1766] Remove history bonus from Eval STC: LLR: 2.92 (-2.94,2.94) {-1.50,0.50} Total: 26776 W: 2787 L: 2725 D: 21264 https://tests.stockfishchess.org/tests/view/5f39d6beb38d442594aabd9b LTC: LLR: 2.93 (-2.94,2.94) {-1.50,0.50} Total: 12968 W: 635 L: 608 D: 11725 https://tests.stockfishchess.org/tests/view/5f39decfb38d442594aabda7 closes https://github.com/official-stockfish/Stockfish/pull/3019 Bench: 4335100 --- src/search.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 83fb722faf6..7c839dfc89f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -794,11 +794,7 @@ namespace { else { if ((ss-1)->currentMove != MOVE_NULL) - { - int bonus = -(ss-1)->statScore / 512; - - ss->staticEval = eval = evaluate(pos) + bonus; - } + ss->staticEval = eval = evaluate(pos); else ss->staticEval = eval = -(ss-1)->staticEval + 2 * Tempo; From 581b92e4a70b99fa5a22f7a1a38f2c8d2099769f Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Mon, 17 Aug 2020 18:22:32 +0200 Subject: [PATCH 0346/1766] Remove last captures extension STC https://tests.stockfishchess.org/tests/view/5f395657e98b6c64b3df41dd LLR: 2.95 (-2.94,2.94) {-1.50,0.50} Total: 144664 W: 15426 L: 15537 D: 113701 Ptnml(0-2): 612, 11341, 48537, 11230, 612 LTC https://tests.stockfishchess.org/tests/view/5f3a2ec7b38d442594aabdd7 LLR: 2.96 (-2.94,2.94) {-1.50,0.50} Total: 22728 W: 1161 L: 1146 D: 20421 Ptnml(0-2): 21, 960, 9388, 973, 22 closes https://github.com/official-stockfish/Stockfish/pull/3020 bench: 3832662 --- src/search.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 7c839dfc89f..1d5bc5f7c84 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1122,11 +1122,6 @@ namespace { && (pos.is_discovery_check_on_king(~us, move) || pos.see_ge(move))) extension = 1; - // Last captures extension - else if ( PieceValue[EG][pos.captured_piece()] > PawnValueEg - && pos.non_pawn_material() <= 2 * RookValueMg) - extension = 1; - // Castling extension if ( type_of(move) == CASTLING && popcount(pos.pieces(us) & ~pos.pieces(PAWN) & (to_sq(move) & KingSide ? KingSide : QueenSide)) <= 2) From 1bcc981a5a70e3065b4ff588644f270136fd7e3c Mon Sep 17 00:00:00 2001 From: mstembera Date: Sun, 16 Aug 2020 15:23:50 -0700 Subject: [PATCH 0347/1766] Fallback to NNUE If the classical eval ends up much smaller than estimated fall back to NNUE. Also use multiply instead of divide for the threshold comparison for smoother transitions without rounding. STC https://tests.stockfishchess.org/tests/view/5f3a5011b38d442594aabdfe LLR: 2.96 (-2.94,2.94) {-0.50,1.50} Total: 57352 W: 6325 L: 6135 D: 44892 Ptnml(0-2): 277, 4748, 18482, 4846, 323 LTC https://tests.stockfishchess.org/tests/view/5f3aee9db38d442594aabe82 LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 16232 W: 897 L: 781 D: 14554 Ptnml(0-2): 19, 679, 6616, 771, 31 closes https://github.com/official-stockfish/Stockfish/pull/3023 bench: 4026216 ----- Recommended net: https://tests.stockfishchess.org/api/nn/nn-82215d0fd0df.nnue --- src/evaluate.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 3a620a78305..1bd893533bb 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -114,7 +114,8 @@ namespace { constexpr Value LazyThreshold1 = Value(1400); constexpr Value LazyThreshold2 = Value(1300); constexpr Value SpaceThreshold = Value(12222); - constexpr Value NNUEThreshold = Value(575); + constexpr Value NNUEThreshold1 = Value(550); + constexpr Value NNUEThreshold2 = Value(150); // KingAttackWeights[PieceType] contains king attack weights by piece type constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 }; @@ -939,10 +940,13 @@ namespace { Value Eval::evaluate(const Position& pos) { bool classical = !Eval::useNNUE - || abs(eg_value(pos.psq_score())) >= NNUEThreshold * (16 + pos.rule50_count()) / 16; + || abs(eg_value(pos.psq_score())) * 16 > NNUEThreshold1 * (16 + pos.rule50_count()); Value v = classical ? Evaluation(pos).value() : NNUE::evaluate(pos) * 5 / 4 + Tempo; + if (classical && Eval::useNNUE && abs(v) * 16 < NNUEThreshold2 * (16 + pos.rule50_count())) + v = NNUE::evaluate(pos) * 5 / 4 + Tempo; + // Damp down the evaluation linearly when shuffling v = v * (100 - pos.rule50_count()) / 100; From fbae5614eb1e82bccd37fbcfb0d2ca388b7a9a7d Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Tue, 18 Aug 2020 08:49:06 +0200 Subject: [PATCH 0348/1766] Fix Makefile typo remove stray quote, shown with `make help` No functional change --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 0f458aa185d..1f8ba4559dc 100644 --- a/src/Makefile +++ b/src/Makefile @@ -598,7 +598,7 @@ help: @echo "ppc-64 > PPC 64-bit" @echo "ppc-32 > PPC 32-bit" @echo "armv7 > ARMv7 32-bit" - @echo "armv7-neon" > ARMv7 32-bit with popcnt and neon" + @echo "armv7-neon > ARMv7 32-bit with popcnt and neon" @echo "armv8 > ARMv8 64-bit with popcnt and neon" @echo "apple-silicon > Apple silicon ARM64" @echo "general-64 > unspecified 64-bit" From 384d6844841e9f2da8f5a913c7620440f9e05ab5 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Tue, 18 Aug 2020 18:06:28 +0200 Subject: [PATCH 0349/1766] Better error message on missing curl/wget provide clean error/warning message for missing curl/wget, sha256sum/shasum fixes https://github.com/official-stockfish/Stockfish/issues/3025 closes https://github.com/official-stockfish/Stockfish/pull/3026 No functional change --- src/Makefile | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index 1f8ba4559dc..a3feb68e3e3 100644 --- a/src/Makefile +++ b/src/Makefile @@ -669,9 +669,24 @@ net: @echo "Default net: $(nnuenet)" $(eval nnuedownloadurl := https://tests.stockfishchess.org/api/nn/$(nnuenet)) $(eval curl_or_wget := $(shell if hash curl 2>/dev/null; then echo "curl -skL"; elif hash wget 2>/dev/null; then echo "wget -qO-"; fi)) - @if test -f "$(nnuenet)"; then echo "Already available."; else echo "Downloading $(nnuedownloadurl)"; $(curl_or_wget) $(nnuedownloadurl) > $(nnuenet); fi + @if test -f "$(nnuenet)"; then \ + echo "Already available."; \ + else \ + if [ "x$(curl_or_wget)" = "x" ]; then \ + echo "Automatic download failed: neither curl nor wget is installed. Install one of these tools or download the net manually"; exit 1; \ + else \ + echo "Downloading $(nnuedownloadurl)"; $(curl_or_wget) $(nnuedownloadurl) > $(nnuenet);\ + fi; \ + fi; $(eval shasum_command := $(shell if hash shasum 2>/dev/null; then echo "shasum -a 256 "; elif hash sha256sum 2>/dev/null; then echo "sha256sum "; fi)) - @if [ "$(nnuenet)" != "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then echo "Failed download or $(nnuenet) corrupted, please delete!"; exit 1; fi + @if [ "x$(shasum_command)" != "x" ]; then \ + if [ "$(nnuenet)" != "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then \ + echo "Failed download or $(nnuenet) corrupted, please delete!"; exit 1; \ + fi \ + else \ + echo "shasum / sha256sum not found, skipping net validation"; \ + fi + # clean binaries and objects objclean: From 42e8789f0b3935b7ea389b3aa929e05e0a016872 Mon Sep 17 00:00:00 2001 From: syzygy1 <3028851+syzygy1@users.noreply.github.com> Date: Tue, 18 Aug 2020 01:56:12 +0200 Subject: [PATCH 0350/1766] Expanded support for x86-32 architectures. add new ARCH targets x86-32-sse41-popcnt > x86 32-bit with sse41 and popcnt support x86-32-sse2 > x86 32-bit with sse2 support x86-32 > x86 32-bit generic (with mmx and sse support) retire x86-32-old (use general-32) closes https://github.com/official-stockfish/Stockfish/pull/3022 No functional change. --- .travis.yml | 3 +- src/Makefile | 133 ++++++++++++++++++++++++++++++++------------------- 2 files changed, 85 insertions(+), 51 deletions(-) diff --git a/.travis.yml b/.travis.yml index 45f1bd3d34f..12596f1e327 100644 --- a/.travis.yml +++ b/.travis.yml @@ -67,9 +67,10 @@ script: - make clean && make -j2 ARCH=x86-64 build && ../tests/signature.sh $benchref - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=general-64 build && ../tests/signature.sh $benchref; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32 optimize=no debug=yes build && ../tests/signature.sh $benchref; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32-sse41-popcnt build && ../tests/signature.sh $benchref; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32-sse2 build && ../tests/signature.sh $benchref; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32 build && ../tests/signature.sh $benchref; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=general-32 build && ../tests/signature.sh $benchref; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32-old build && ../tests/signature.sh $benchref; fi - if [[ "$TRAVIS_OS_NAME" == "linux" && "$COMP" == "gcc" ]]; then make clean && make -j2 ARCH=x86-64-modern profile-build && ../tests/signature.sh $benchref; fi # compile only for some more advanced architectures (might not run in travis) diff --git a/src/Makefile b/src/Makefile index a3feb68e3e3..79c7333ac26 100644 --- a/src/Makefile +++ b/src/Makefile @@ -67,11 +67,13 @@ endif # bits = 64/32 --- -DIS_64BIT --- 64-/32-bit operating system # prefetch = yes/no --- -DUSE_PREFETCH --- Use prefetch asm-instruction # popcnt = yes/no --- -DUSE_POPCNT --- Use popcnt asm-instruction +# pext = yes/no --- -DUSE_PEXT --- Use pext x86_64 asm-instruction # sse = yes/no --- -msse --- Use Intel Streaming SIMD Extensions +# mmx = yes/no --- -mmmx --- Use Intel MMX instructions +# sse2 = yes/no --- -msse2 --- Use Intel Streaming SIMD Extensions 2 # ssse3 = yes/no --- -mssse3 --- Use Intel Supplemental Streaming SIMD Extensions 3 # sse41 = yes/no --- -msse4.1 --- Use Intel Streaming SIMD Extensions 4.1 # avx2 = yes/no --- -mavx2 --- Use Intel Advanced Vector Extensions 2 -# pext = yes/no --- -DUSE_PEXT --- Use pext x86_64 asm-instruction # avx512 = yes/no --- -mavx512bw --- Use Intel Advanced Vector Extensions 512 # vnni = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 512 # neon = yes/no --- -DUSE_NEON --- Use ARM SIMD architecture @@ -92,12 +94,13 @@ sanitize = no bits = 64 prefetch = no popcnt = no -mmx = no +pext = no sse = no +mmx = no +sse2 = no ssse3 = no sse41 = no avx2 = no -pext = no avx512 = no vnni = no neon = no @@ -106,83 +109,82 @@ STRIP = strip ### 2.2 Architecture specific -ifeq ($(ARCH),general-32) - arch = any - bits = 32 -endif +ifeq ($(findstring x86,$(ARCH)),x86) -ifeq ($(ARCH),x86-32-old) - arch = i386 - bits = 32 -endif +# x86-32/64 -ifeq ($(ARCH),x86-32) +ifeq ($(findstring x86-32,$(ARCH)),x86-32) arch = i386 bits = 32 - prefetch = yes + sse = yes mmx = yes +else + arch = x86_64 sse = yes + sse2 = yes endif -ifeq ($(ARCH),general-64) - arch = any +ifeq ($(findstring -sse,$(ARCH)),-sse) + sse = yes endif -ifeq ($(ARCH),x86-64) - arch = x86_64 - prefetch = yes +ifeq ($(findstring -popcnt,$(ARCH)),-popcnt) + popcnt = yes +endif + +ifeq ($(findstring -mmx,$(ARCH)),-mmx) + mmx = yes +endif + +ifeq ($(findstring -sse2,$(ARCH)),-sse2) sse = yes + sse2 = yes endif -ifeq ($(ARCH),x86-64-sse3-popcnt) - arch = x86_64 - prefetch = yes +ifeq ($(findstring -ssse3,$(ARCH)),-ssse3) sse = yes - popcnt = yes + sse2 = yes + ssse3 = yes endif -ifeq ($(ARCH),x86-64-ssse3) - arch = x86_64 - prefetch = yes +ifeq ($(findstring -sse41,$(ARCH)),-sse41) sse = yes + sse2 = yes ssse3 = yes + sse41 = yes endif -ifeq ($(ARCH),$(filter $(ARCH),x86-64-sse41-popcnt x86-64-modern)) - arch = x86_64 - prefetch = yes +ifeq ($(findstring -modern,$(ARCH)),-modern) popcnt = yes sse = yes + sse2 = yes ssse3 = yes sse41 = yes endif -ifeq ($(ARCH),x86-64-avx2) - arch = x86_64 - prefetch = yes +ifeq ($(findstring -avx2,$(ARCH)),-avx2) popcnt = yes sse = yes + sse2 = yes ssse3 = yes sse41 = yes avx2 = yes endif -ifeq ($(ARCH),x86-64-bmi2) - arch = x86_64 - prefetch = yes +ifeq ($(findstring -bmi2,$(ARCH)),-bmi2) popcnt = yes sse = yes + sse2 = yes ssse3 = yes sse41 = yes avx2 = yes pext = yes endif -ifeq ($(ARCH),x86-64-avx512) - arch = x86_64 - prefetch = yes +ifeq ($(findstring -avx512,$(ARCH)),-avx512) popcnt = yes sse = yes + sse2 = yes ssse3 = yes sse41 = yes avx2 = yes @@ -190,11 +192,10 @@ ifeq ($(ARCH),x86-64-avx512) avx512 = yes endif -ifeq ($(ARCH),x86-64-vnni) - arch = x86_64 - prefetch = yes +ifeq ($(findstring -vnni,$(ARCH)),-vnni) popcnt = yes sse = yes + sse2 = yes ssse3 = yes sse41 = yes avx2 = yes @@ -203,6 +204,28 @@ ifeq ($(ARCH),x86-64-vnni) vnni = yes endif +ifeq ($(sse),yes) + prefetch = yes +endif + +# 64-bit pext is not available on x86-32 +ifeq ($(bits),32) + pext = no +endif + +else + +# all other architectures + +ifeq ($(ARCH),general-32) + arch = any + bits = 32 +endif + +ifeq ($(ARCH),general-64) + arch = any +endif + ifeq ($(ARCH),armv7) arch = armv7 prefetch = yes @@ -242,6 +265,8 @@ ifeq ($(ARCH),ppc-64) prefetch = yes endif +endif + ### ========================================================================== ### Section 3. Low-level Configuration ### ========================================================================== @@ -487,6 +512,13 @@ ifeq ($(ssse3),yes) endif endif +ifeq ($(sse2),yes) + CXXFLAGS += -DUSE_SSE2 + ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + CXXFLAGS += -msse2 + endif +endif + ifeq ($(mmx),yes) CXXFLAGS += -DUSE_MMX ifeq ($(comp),$(filter $(comp),gcc clang mingw)) @@ -503,10 +535,6 @@ ifeq ($(neon),yes) endif endif -ifeq ($(arch),x86_64) - CXXFLAGS += -msse2 -DUSE_SSE2 -endif - ### 3.7 pext ifeq ($(pext),yes) CXXFLAGS += -DUSE_PEXT @@ -592,9 +620,10 @@ help: @echo "x86-64-modern > common modern CPU, currently x86-64-sse41-popcnt" @echo "x86-64-ssse3 > x86 64-bit with ssse3 support" @echo "x86-64-sse3-popcnt > x86 64-bit with sse3 and popcnt support" - @echo "x86-64 > x86 64-bit generic" - @echo "x86-32 > x86 32-bit (also enables MMX and SSE)" - @echo "x86-32-old > x86 32-bit fall back for old hardware" + @echo "x86-64 > x86 64-bit generic (with sse2 support)" + @echo "x86-32-sse41-popcnt > x86 32-bit with sse41 and popcnt support" + @echo "x86-32-sse2 > x86 32-bit with sse2 support" + @echo "x86-32 > x86 32-bit generic (with mmx and sse support)" @echo "ppc-64 > PPC 64-bit" @echo "ppc-32 > PPC 32-bit" @echo "armv7 > ARMv7 32-bit" @@ -624,7 +653,7 @@ help: @echo "make -j build ARCH=x86-64-ssse3 COMP=clang" @echo "" ifneq ($(empty_arch), yes) - @echo "-------------------------------\n" + @echo "-------------------------------" @echo "The selected architecture $(ARCH) will enable the following configuration: " @$(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity endif @@ -719,11 +748,13 @@ config-sanity: @echo "os: '$(OS)'" @echo "prefetch: '$(prefetch)'" @echo "popcnt: '$(popcnt)'" + @echo "pext: '$(pext)'" @echo "sse: '$(sse)'" + @echo "mmx: '$(mmx)'" + @echo "sse2: '$(sse2)'" @echo "ssse3: '$(ssse3)'" @echo "sse41: '$(sse41)'" @echo "avx2: '$(avx2)'" - @echo "pext: '$(pext)'" @echo "avx512: '$(avx512)'" @echo "vnni: '$(vnni)'" @echo "neon: '$(neon)'" @@ -744,11 +775,13 @@ config-sanity: @test "$(bits)" = "32" || test "$(bits)" = "64" @test "$(prefetch)" = "yes" || test "$(prefetch)" = "no" @test "$(popcnt)" = "yes" || test "$(popcnt)" = "no" + @test "$(pext)" = "yes" || test "$(pext)" = "no" @test "$(sse)" = "yes" || test "$(sse)" = "no" + @test "$(mmx)" = "yes" || test "$(mmx)" = "no" + @test "$(sse2)" = "yes" || test "$(sse2)" = "no" @test "$(ssse3)" = "yes" || test "$(ssse3)" = "no" @test "$(sse41)" = "yes" || test "$(sse41)" = "no" @test "$(avx2)" = "yes" || test "$(avx2)" = "no" - @test "$(pext)" = "yes" || test "$(pext)" = "no" @test "$(avx512)" = "yes" || test "$(avx512)" = "no" @test "$(vnni)" = "yes" || test "$(vnni)" = "no" @test "$(neon)" = "yes" || test "$(neon)" = "no" From 2deb08a52946379d4cebb1082e5d740d1d027122 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Tue, 18 Aug 2020 18:54:28 +0800 Subject: [PATCH 0351/1766] Reintroduce last captures extension STC: LLR: 2.93 (-2.94,2.94) {-0.50,1.50} Total: 34840 W: 3834 L: 3682 D: 27324 Ptnml(0-2): 153, 2767, 11455, 2865, 180 https://tests.stockfishchess.org/tests/view/5f3bb380b38d442594aabefc LTC: LLR: 2.95 (-2.94,2.94) {0.25,1.75} Total: 15832 W: 890 L: 776 D: 14166 Ptnml(0-2): 17, 669, 6429, 785, 16 https://tests.stockfishchess.org/tests/view/5f3c46a0a95672ddd56c632a closes https://github.com/official-stockfish/Stockfish/pull/3028 see also https://github.com/official-stockfish/Stockfish/pull/3020 Bench: 4348811 --- src/search.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 1d5bc5f7c84..7c839dfc89f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1122,6 +1122,11 @@ namespace { && (pos.is_discovery_check_on_king(~us, move) || pos.see_ge(move))) extension = 1; + // Last captures extension + else if ( PieceValue[EG][pos.captured_piece()] > PawnValueEg + && pos.non_pawn_material() <= 2 * RookValueMg) + extension = 1; + // Castling extension if ( type_of(move) == CASTLING && popcount(pos.pieces(us) & ~pos.pieces(PAWN) & (to_sq(move) & KingSide ? KingSide : QueenSide)) <= 2) From a1ad8604a11459a94189f857e368d0fbb72da25d Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 19 Aug 2020 19:21:41 +0200 Subject: [PATCH 0352/1766] Send error message as an UCI info string some GUIs do not show the error message when the engine terminates in the no-net case, as it is send to cerr. Instead send it as an info string, which the GUI will more likely display. closes https://github.com/official-stockfish/Stockfish/pull/3031 No functional change. --- src/evaluate.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 1bd893533bb..c84d894f9c8 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -53,10 +53,11 @@ namespace Eval { UCI::OptionsMap defaults; UCI::init(defaults); - std::cerr << "NNUE evaluation used, but the network file " << eval_file << " was not loaded successfully. " - << "These network evaluation parameters must be available, and compatible with this version of the code. " - << "The UCI option EvalFile might need to specify the full path, including the directory/folder name, to the file. " - << "The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/"+std::string(defaults["EvalFile"]) << std::endl; + sync_cout << "info string ERROR: NNUE evaluation used, but the network file " << eval_file << " was not loaded successfully." << sync_endl; + sync_cout << "info string ERROR: The UCI option EvalFile might need to specify the full path, including the directory/folder name, to the file." << sync_endl; + sync_cout << "info string ERROR: The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/"+std::string(defaults["EvalFile"]) << sync_endl; + sync_cout << "info string ERROR: If the UCI option Use NNUE is set to true, network evaluation parameters compatible with the program must be available." << sync_endl; + sync_cout << "info string ERROR: The engine will be terminated now." << sync_endl; std::exit(EXIT_FAILURE); } From daac86691de55fe388b9b727794c7d27f2b90d5c Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 20 Aug 2020 14:24:49 +0200 Subject: [PATCH 0353/1766] Set Use NNUE by default to true Since the initial stages of the merge, progress has been made so that this seems the best option now: * NNUE is clearly stronger on most relevant hardware and time controls * All of our CI and testing infrastructure has been adjusted * The default net is easy to get (further ideas #3030) fixes https://github.com/official-stockfish/Stockfish/issues/2861 closes https://github.com/official-stockfish/Stockfish/pull/3033 No functional change. --- src/ucioption.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 2b66a47545a..ec83c7c88dc 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -78,7 +78,7 @@ void init(OptionsMap& o) { o["SyzygyProbeDepth"] << Option(1, 1, 100); o["Syzygy50MoveRule"] << Option(true); o["SyzygyProbeLimit"] << Option(7, 0, 7); - o["Use NNUE"] << Option(false, on_use_NNUE); + o["Use NNUE"] << Option(true, on_use_NNUE); // The default must follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. o["EvalFile"] << Option("nn-82215d0fd0df.nnue", on_eval_file); From 8b45b1c4907b4b2186441e02edd3b0c37f8b3269 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 21 Aug 2020 07:42:19 +0200 Subject: [PATCH 0354/1766] Deal with very old linux kernels MADV_HUGEPAGE might not be available, for kernels before 2.6.38 (released 2011). Just skip the madvise. closes https://github.com/official-stockfish/Stockfish/pull/3039 No functional change --- src/misc.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/misc.cpp b/src/misc.cpp index 459ea10035b..56a3dcad42d 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -367,7 +367,9 @@ void* aligned_ttmem_alloc(size_t allocSize, void*& mem) { size_t size = ((allocSize + alignment - 1) / alignment) * alignment; // multiple of alignment if (posix_memalign(&mem, alignment, size)) mem = nullptr; +#if defined(MADV_HUGEPAGE) madvise(mem, allocSize, MADV_HUGEPAGE); +#endif return mem; } From 15abcaedc1e32d4de913a2f7dea12578912371b7 Mon Sep 17 00:00:00 2001 From: gsobala Date: Fri, 21 Aug 2020 11:28:53 +0100 Subject: [PATCH 0355/1766] Update Makefile for macOS Changes to deal with compilation (particularly profile-build) on macOS. (1) The default toolchain has gcc masquerading as clang, the previous Makefile was not picking up the required changes to the different profiling tools. (2) The previous Makefile test for gccisclang occurred before a potential overwrite of CXX by COMPCXX (3) llvm-profdata no longer runs as a command on macOS and instead is invoked by ``xcrun llvm-profdata`` (4) Needs to support use of true gcc using e.g. ``make build ... COMPCXX=g++-10`` (5) enable profile-build in travis for macOS closes https://github.com/official-stockfish/Stockfish/pull/3043 No functional change --- .travis.yml | 3 ++- AUTHORS | 1 + src/Makefile | 16 ++++++++++++---- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 12596f1e327..a029c4fc818 100644 --- a/.travis.yml +++ b/.travis.yml @@ -71,7 +71,8 @@ script: - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32-sse2 build && ../tests/signature.sh $benchref; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32 build && ../tests/signature.sh $benchref; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=general-32 build && ../tests/signature.sh $benchref; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" && "$COMP" == "gcc" ]]; then make clean && make -j2 ARCH=x86-64-modern profile-build && ../tests/signature.sh $benchref; fi + # workaround: exclude a custom version of llvm+clang, which doesn't find llvm-profdata on ubuntu + - if [[ "$TRAVIS_OS_NAME" != "linux" || "$COMP" == "gcc" ]]; then make clean && make -j2 ARCH=x86-64-modern profile-build && ../tests/signature.sh $benchref; fi # compile only for some more advanced architectures (might not run in travis) - make clean && make -j2 ARCH=x86-64-avx2 build diff --git a/AUTHORS b/AUTHORS index d8f4d30e7c2..c96f870a6a6 100644 --- a/AUTHORS +++ b/AUTHORS @@ -59,6 +59,7 @@ Fauzi Akram Dabat (FauziAkram) Felix Wittmann gamander Gary Heckman (gheckman) +George Sobala (gsobala) gguliash Gian-Carlo Pascutto (gcp) Gontran Lemaire (gonlem) diff --git a/src/Makefile b/src/Makefile index 79c7333ac26..b969ba04665 100644 --- a/src/Makefile +++ b/src/Makefile @@ -302,9 +302,6 @@ ifeq ($(COMP),gcc) ifneq ($(KERNEL),Darwin) LDFLAGS += -Wl,--no-as-needed endif - - gccversion = $(shell $(CXX) --version) - gccisclang = $(findstring clang,$(gccversion)) endif ifeq ($(COMP),mingw) @@ -376,6 +373,7 @@ endif ifeq ($(KERNEL),Darwin) CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.14 LDFLAGS += -arch $(arch) -mmacosx-version-min=10.14 + XCRUN = xcrun endif # To cross-compile for Android, NDK version r21 or later is recommended. @@ -407,6 +405,16 @@ ifdef COMPCXX CXX=$(COMPCXX) endif +### Sometimes gcc is really clang +ifeq ($(COMP),gcc) + gccversion = $(shell $(CXX) --version) + gccisclang = $(findstring clang,$(gccversion)) + ifneq ($(gccisclang),) + profile_make = clang-profile-make + profile_use = clang-profile-use + endif +endif + ### On mingw use Windows threads, otherwise POSIX ifneq ($(comp),mingw) # On Android Bionic's C library comes with its own pthread implementation bundled in @@ -798,7 +806,7 @@ clang-profile-make: all clang-profile-use: - llvm-profdata merge -output=stockfish.profdata *.profraw + $(XCRUN) llvm-profdata merge -output=stockfish.profdata *.profraw $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ EXTRACXXFLAGS='-fprofile-instr-use=stockfish.profdata' \ EXTRALDFLAGS='-fprofile-use ' \ From e64b957274b94e89ad1a6e3ec4571c9082246a0a Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Fri, 21 Aug 2020 09:24:25 +0200 Subject: [PATCH 0356/1766] Simplify away internal iterative deepening Remove the iterative deepening step. Instead, employ a depth reduction if the position is not in TT and on the PV. STC https://tests.stockfishchess.org/tests/view/5f3ce6eaa95672ddd56c637e LLR: 2.97 (-2.94,2.94) {-0.50,1.50} Total: 41096 W: 4421 L: 4257 D: 32418 Ptnml(0-2): 207, 3259, 13460, 3407, 215 LTC (old) https://tests.stockfishchess.org/tests/view/5f3d7d4fa95672ddd56c640b LLR: 2.92 (-2.94,2.94) {-1.50,0.50} Total: 26032 W: 1320 L: 1309 D: 23403 Ptnml(0-2): 22, 1152, 10654, 1169, 19 LTC (new) https://tests.stockfishchess.org/tests/view/5f3e31e0a95672ddd56c6464 LLR: 2.95 (-2.94,2.94) {-0.75,0.25} Total: 34160 W: 1844 L: 1766 D: 30550 Ptnml(0-2): 33, 1533, 13876, 1599, 39 bench: 3849173 --- src/search.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 7c839dfc89f..ba13680cecb 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -939,15 +939,11 @@ namespace { } } - // Step 11. Internal iterative deepening (~1 Elo) - if (depth >= 7 && !ttMove) - { - search(pos, ss, alpha, beta, depth - 7, cutNode); - - tte = TT.probe(posKey, ttHit); - ttValue = ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; - ttMove = ttHit ? tte->move() : MOVE_NONE; - } + // Step 11. If the position is not in TT, decrease depth by 2 + if ( PvNode + && depth >= 6 + && !ttMove) + depth -= 2; moves_loop: // When in check, search starts from here From cbcb05ca092160137c166f84e7e9da3d6bb4e2d3 Mon Sep 17 00:00:00 2001 From: MJZ1977 <37274752+MJZ1977@users.noreply.github.com> Date: Fri, 21 Aug 2020 10:57:34 +0200 Subject: [PATCH 0357/1766] Display classic and NNUE evaluation in trace mode show both the classical and NNUE evaluation, as well as the Final evaluation. closes https://github.com/official-stockfish/Stockfish/pull/3042 No functional change. --- src/evaluate.cpp | 63 ++++++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index c84d894f9c8..c66938d6ac7 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -972,42 +972,47 @@ std::string Eval::trace(const Position& pos) { Value v; + std::memset(scores, 0, sizeof(scores)); + + pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt + + v = Evaluation(pos).value(); + + ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2) + << " Term | White | Black | Total \n" + << " | MG EG | MG EG | MG EG \n" + << " ------------+-------------+-------------+------------\n" + << " Material | " << Term(MATERIAL) + << " Imbalance | " << Term(IMBALANCE) + << " Pawns | " << Term(PAWN) + << " Knights | " << Term(KNIGHT) + << " Bishops | " << Term(BISHOP) + << " Rooks | " << Term(ROOK) + << " Queens | " << Term(QUEEN) + << " Mobility | " << Term(MOBILITY) + << " King safety | " << Term(KING) + << " Threats | " << Term(THREAT) + << " Passed | " << Term(PASSED) + << " Space | " << Term(SPACE) + << " Winnable | " << Term(WINNABLE) + << " ------------+-------------+-------------+------------\n" + << " Total | " << Term(TOTAL); + + v = pos.side_to_move() == WHITE ? v : -v; + + ss << "\nClassical evaluation: " << to_cp(v) << " (white side)\n"; + if (Eval::useNNUE) { v = NNUE::evaluate(pos); - } - else - { - std::memset(scores, 0, sizeof(scores)); - - pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt - - v = Evaluation(pos).value(); - - ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2) - << " Term | White | Black | Total \n" - << " | MG EG | MG EG | MG EG \n" - << " ------------+-------------+-------------+------------\n" - << " Material | " << Term(MATERIAL) - << " Imbalance | " << Term(IMBALANCE) - << " Pawns | " << Term(PAWN) - << " Knights | " << Term(KNIGHT) - << " Bishops | " << Term(BISHOP) - << " Rooks | " << Term(ROOK) - << " Queens | " << Term(QUEEN) - << " Mobility | " << Term(MOBILITY) - << " King safety | " << Term(KING) - << " Threats | " << Term(THREAT) - << " Passed | " << Term(PASSED) - << " Space | " << Term(SPACE) - << " Winnable | " << Term(WINNABLE) - << " ------------+-------------+-------------+------------\n" - << " Total | " << Term(TOTAL); + v = pos.side_to_move() == WHITE ? v : -v; + ss << "\nNNUE evaluation: " << to_cp(v) << " (white side)\n"; } + v = evaluate(pos); v = pos.side_to_move() == WHITE ? v : -v; + ss << "\nFinal evaluation: " << to_cp(v) << " (white side)\n"; - ss << "\nFinal evaluation: " << to_cp(v) << " (white side)\n"; return ss.str(); } From 34f67c57223d73ad40d583ccc033c75eb0df2453 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 21 Aug 2020 22:10:55 +0200 Subject: [PATCH 0358/1766] Explicitly rely on pthreads if possible allows us to set the needed stacksize on thread creation. Useful for environments with too small a default stack size (e.g. Alpine Linux with musl). Passed STC, no regression: LLR: 2.96 (-2.94,2.94) {-1.25,0.25} Total: 17816 W: 1344 L: 1275 D: 15197 Ptnml(0-2): 30, 1057, 6682, 1092, 47 https://tests.stockfishchess.org/tests/view/5f402b5587a5c3c63d8f534d closes https://github.com/official-stockfish/Stockfish/pull/3047 fixes https://github.com/official-stockfish/Stockfish/issues/3041 No functional change. --- src/Makefile | 1 + src/thread_win32_osx.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index b969ba04665..74ef87b94ed 100644 --- a/src/Makefile +++ b/src/Makefile @@ -417,6 +417,7 @@ endif ### On mingw use Windows threads, otherwise POSIX ifneq ($(comp),mingw) + CXXFLAGS += -DUSE_PTHREADS # On Android Bionic's C library comes with its own pthread implementation bundled in ifneq ($(OS),Android) # Haiku has pthreads in its libroot, so only link it in on other platforms diff --git a/src/thread_win32_osx.h b/src/thread_win32_osx.h index c4b55a4812b..75ef5d9a328 100644 --- a/src/thread_win32_osx.h +++ b/src/thread_win32_osx.h @@ -27,7 +27,7 @@ /// 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__) +#if defined(__APPLE__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(USE_PTHREADS) #include From 3542033342f15625f808013b69aa8c2d274a2f91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicolet?= Date: Sat, 22 Aug 2020 11:37:53 +0200 Subject: [PATCH 0359/1766] Instructions to build on older Macintosh In recent Macs, it is possible to use the Clang compiler provided by Apple to compile Stockfish out of the box, and this is the method used by default in our Makefile (the Makefile sets the macosx-version-min=10.14 flag to select the right libc++ library for the Clang compiler with recent c++17 support). But it is quite possible to compile and run Stockfish on older Macs! Below we describe a method to install a recent GNU compiler on these Macs, to get the c++17 support. We have tested the following procedure to install gcc10 on machines running Mac OS 10.7, Mac OS 10.9 and Mac OS 10.13: 1) install XCode for your machine. 2) install Apple command-line developer tools for XCode, by typing the following command in a Terminal: ``` sudo xcode-select --install ``` 3) go to the Stockfish "src" directory, then try a default build and run Stockfish: ``` make clean make build make net ./stockfish ``` 4) if step 3 worked, congrats! You have a compiler recent enough on your Mac to compile Stockfish. If not, continue with step 5 to install GNU gcc10 :-) 5) install the MacPorts package manager (https://www.macports.org/install.php), for instance using the fast method in the "macOS Package (.pkg) Installer" section of the page. 6) use the "port" command to install the gcc10 package of MacPorts by typing the following command: ``` sudo port install gcc10 ``` With this step, MacPorts will install the gcc10 compiler under the name "g++-mp-10" in the /opt/local/bin directory: ``` which g++-mp-10 /opt/local/bin/g++-mp-10 <--- answer ``` 7) You can now go back to the "src" directory of Stockfish, and try to build Stockfish by pointing at the right compiler: ``` make clean make build COMP=gcc COMPCXX=/opt/local/bin/g++-mp-10 make net ./stockfish ``` 8) Enjoy Stockfish on Macintosh! See this pull request for further discussion: https://github.com/official-stockfish/Stockfish/pull/3049 No functional change --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 74ef87b94ed..b0274504227 100644 --- a/src/Makefile +++ b/src/Makefile @@ -370,7 +370,7 @@ else endif endif -ifeq ($(KERNEL),Darwin) +ifeq ($(KERNEL),Darwin) CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.14 LDFLAGS += -arch $(arch) -mmacosx-version-min=10.14 XCRUN = xcrun From 5f1843c9cb55afcd3fb1da9e9dc4b0092f25d9f0 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 11 Jul 2020 16:59:33 +0200 Subject: [PATCH 0360/1766] Small trivial cleanups closes https://github.com/official-stockfish/Stockfish/pull/2801 No functional change --- README.md | 53 +++++++++++++++++++++++++----------------------- src/Makefile | 2 +- src/bitboard.cpp | 12 ++++++++++- src/bitboard.h | 10 --------- src/evaluate.cpp | 17 ++++++++-------- src/material.cpp | 2 +- src/misc.cpp | 17 ++++++++-------- src/misc.h | 8 -------- src/movegen.cpp | 2 +- src/movepick.cpp | 14 ++++++------- src/movepick.h | 8 ++++---- src/pawns.cpp | 2 +- src/position.cpp | 4 ++-- src/position.h | 5 +++++ src/search.cpp | 15 +++++++------- src/timeman.cpp | 18 ++++++++-------- src/uci.cpp | 2 +- 17 files changed, 96 insertions(+), 95 deletions(-) diff --git a/README.md b/README.md index 7b6ddf4cec3..2cc88bf4279 100644 --- a/README.md +++ b/README.md @@ -4,17 +4,17 @@ [![Build Status](https://ci.appveyor.com/api/projects/status/github/official-stockfish/Stockfish?branch=master&svg=true)](https://ci.appveyor.com/project/mcostalba/stockfish/branch/master) [Stockfish](https://stockfishchess.org) is a free, powerful UCI chess engine -derived from Glaurung 2.1. It features two evaluation functions, the classical -evaluation based on handcrafted terms, and the NNUE evaluation based on -efficiently updateable neural networks. The classical evaluation runs efficiently -on most 64bit CPU architectures, while the NNUE evaluation benefits strongly from the -vector intrinsics available on modern CPUs (avx2 or similar). +derived from Glaurung 2.1. Stockfish is not a complete chess program and requires a +UCI-compatible graphical user interface (GUI) (e.g. XBoard with PolyGlot, Scid, +Cute Chess, eboard, Arena, Sigma Chess, Shredder, Chess Partner or Fritz) in order +to be used comfortably. Read the documentation for your GUI of choice for information +about how to use Stockfish with it. -Stockfish is not a complete chess program and requires a -UCI-compatible GUI (e.g. XBoard with PolyGlot, Scid, Cute Chess, eboard, Arena, -Sigma Chess, Shredder, Chess Partner or Fritz) in order to be used comfortably. -Read the documentation for your GUI of choice for information about how to use -Stockfish with it. +The Stockfish engine features two evaluation functions for chess, the classical +evaluation based on handcrafted terms, and the NNUE evaluation based on efficiently +updateable neural networks. The classical evaluation runs efficiently on most 64bit +CPU architectures, while the NNUE evaluation benefits strongly from the vector +intrinsics available on modern CPUs (avx2 or similar). ## Files @@ -28,10 +28,13 @@ This distribution of Stockfish consists of the following files: * src, a subdirectory containing the full source code, including a Makefile that can be used to compile Stockfish on Unix-like systems. -To use the NNUE evaluation an additional data file with neural network parameters -needs to be downloaded. The filename for the default set can be found as the default -value of the `EvalFile` UCI option, with the format -`nn-[SHA256 first 12 digits].nnue` (e.g. nn-c157e0a5755b.nnue). This file can be downloaded from + * a file with the .nnue extension, storing the neural network for the NNUE + evaluation. + +Note: to use the NNUE evaluation, the additional data file with neural network parameters +needs to be downloaded. The filename for the default net can be found as the default +value of the `EvalFile` UCI option, with the format `nn-[SHA256 first 12 digits].nnue` +(for instance, `nn-c157e0a5755b.nnue`). This file can be downloaded from ``` https://tests.stockfishchess.org/api/nn/[filename] ``` @@ -64,14 +67,6 @@ Currently, Stockfish has the following UCI options: The name of the file of the NNUE evaluation parameters. Depending on the GUI the filename should include the full path to the folder/directory that contains the file. - * #### Contempt - A positive value for contempt favors middle game positions and avoids draws, - effective for the classical evaluation only. - - * #### Analysis Contempt - By default, contempt is set to prefer the side to move. Set this option to "White" - or "Black" to analyse with contempt for that side, or "Off" to disable contempt. - * #### UCI_AnalyseMode An option handled by your GUI. @@ -120,6 +115,14 @@ Currently, Stockfish has the following UCI options: Limit Syzygy tablebase probing to positions with at most this many pieces left (including kings and pawns). + * #### Contempt + A positive value for contempt favors middle game positions and avoids draws, + effective for the classical evaluation only. + + * #### Analysis Contempt + By default, contempt is set to prefer the side to move. Set this option to "White" + or "Black" to analyse with contempt for that side, or "Off" to disable contempt. + * #### Move Overhead Assume a time delay of x ms due to network and GUI overheads. This is useful to avoid losses on time in those cases. @@ -138,7 +141,7 @@ Currently, Stockfish has the following UCI options: * #### Debug Log File Write all communication to and from the engine into a text file. -## Classical and NNUE evaluation +## A note on classical and NNUE evaluation Both approaches assign a value to a position that is used in alpha-beta (PVS) search to find the best move. The classical evaluation computes this value as a function @@ -226,6 +229,7 @@ targets with corresponding descriptions. cd src make help make build ARCH=x86-64-modern + make net ``` When not using the Makefile to compile (for instance with Microsoft MSVC) you @@ -237,8 +241,7 @@ compiler you used to create your executable. These informations can be found by typing the following commands in a console: ``` - ./stockfish - compiler + ./stockfish compiler ``` ## Understanding the code base and participating in the project diff --git a/src/Makefile b/src/Makefile index b0274504227..74ef87b94ed 100644 --- a/src/Makefile +++ b/src/Makefile @@ -370,7 +370,7 @@ else endif endif -ifeq ($(KERNEL),Darwin) +ifeq ($(KERNEL),Darwin) CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.14 LDFLAGS += -arch $(arch) -mmacosx-version-min=10.14 XCRUN = xcrun diff --git a/src/bitboard.cpp b/src/bitboard.cpp index f531010c79d..80206b58af9 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -39,6 +39,16 @@ namespace { Bitboard BishopTable[0x1480]; // To store bishop attacks void init_magics(PieceType pt, Bitboard table[], Magic magics[]); + +} + + +/// safe_destination() returns the bitboard of target square for the given step +/// from the given square. If the step is off the board, returns empty bitboard. + +inline Bitboard safe_destination(Square s, int step) { + Square to = Square(s + step); + return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0); } @@ -110,7 +120,7 @@ namespace { Direction RookDirections[4] = {NORTH, SOUTH, EAST, WEST}; Direction BishopDirections[4] = {NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST}; - for(Direction d : (pt == ROOK ? RookDirections : BishopDirections)) + for (Direction d : (pt == ROOK ? RookDirections : BishopDirections)) { Square s = sq; while(safe_destination(s, d) && !(occupied & s)) diff --git a/src/bitboard.h b/src/bitboard.h index a899d879818..29d8f66d415 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -279,16 +279,6 @@ inline int edge_distance(File f) { return std::min(f, File(FILE_H - f)); } inline int edge_distance(Rank r) { return std::min(r, Rank(RANK_8 - r)); } -/// safe_destination() returns the bitboard of target square for the given step -/// from the given square. If the step is off the board, returns empty bitboard. - -inline Bitboard safe_destination(Square s, int step) -{ - Square to = Square(s + step); - return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0); -} - - /// attacks_bb(Square) returns the pseudo attacks of the give piece type /// assuming an empty board. diff --git a/src/evaluate.cpp b/src/evaluate.cpp index c66938d6ac7..ce92db9a733 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -288,8 +288,8 @@ namespace { attackedBy2[Us] = dblAttackByPawn | (attackedBy[Us][KING] & attackedBy[Us][PAWN]); // Init our king safety tables - Square s = make_square(Utility::clamp(file_of(ksq), FILE_B, FILE_G), - Utility::clamp(rank_of(ksq), RANK_2, RANK_7)); + Square s = make_square(std::clamp(file_of(ksq), FILE_B, FILE_G), + std::clamp(rank_of(ksq), RANK_2, RANK_7)); kingRing[Us] = attacks_bb(s) | s; kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them)); @@ -686,8 +686,8 @@ namespace { Square blockSq = s + Up; // Adjust bonus based on the king's proximity - bonus += make_score(0, ( (king_proximity(Them, blockSq) * 19) / 4 - - king_proximity(Us, blockSq) * 2) * w); + bonus += make_score(0, ( king_proximity(Them, blockSq) * 19 / 4 + - king_proximity(Us, blockSq) * 2) * w); // If blockSq is not the queening square then consider also a second push if (r != RANK_7) @@ -731,7 +731,7 @@ namespace { // Evaluation::space() computes a space evaluation for a given side, aiming to improve game - // play in the opening. It is based on the number of safe squares on the 4 central files + // play in the opening. It is based on the number of safe squares on the four central files // on ranks 2 to 4. Completely safe squares behind a friendly pawn are counted twice. // Finally, the space bonus is multiplied by a weight which decreases according to occupancy. @@ -804,7 +804,7 @@ namespace { // 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)) * Utility::clamp(complexity + 50, -abs(mg), 0); + int u = ((mg > 0) - (mg < 0)) * std::clamp(complexity + 50, -abs(mg), 0); int v = ((eg > 0) - (eg < 0)) * std::max(complexity, -abs(eg)); mg += u; @@ -951,8 +951,8 @@ Value Eval::evaluate(const Position& pos) { // Damp down the evaluation linearly when shuffling v = v * (100 - pos.rule50_count()) / 100; - // Guarantee evalution outside of TB range - v = Utility::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); + // Guarantee evaluation does not hit the tablebase range + v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); return v; } @@ -1013,6 +1013,5 @@ std::string Eval::trace(const Position& pos) { v = pos.side_to_move() == WHITE ? v : -v; ss << "\nFinal evaluation: " << to_cp(v) << " (white side)\n"; - return ss.str(); } diff --git a/src/material.cpp b/src/material.cpp index 0ef9926f022..870a5e112cb 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -130,7 +130,7 @@ Entry* probe(const Position& pos) { Value npm_w = pos.non_pawn_material(WHITE); Value npm_b = pos.non_pawn_material(BLACK); - Value npm = Utility::clamp(npm_w + npm_b, EndgameLimit, MidgameLimit); + Value npm = std::clamp(npm_w + npm_b, EndgameLimit, MidgameLimit); // Map total non-pawn material into [PHASE_ENDGAME, PHASE_MIDGAME] e->gamePhase = Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit)); diff --git a/src/misc.cpp b/src/misc.cpp index 56a3dcad42d..80c436ac0f2 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -328,16 +328,16 @@ void prefetch(void* addr) { #endif -/// Wrappers for systems where the c++17 implementation doesn't guarantee the availability of aligned_alloc. -/// Memory allocated with std_aligned_alloc must be freed with std_aligned_free. -/// + +/// std_aligned_alloc() is our wrapper for systems where the c++17 implementation +/// does not guarantee the availability of aligned_alloc(). Memory allocated with +/// std_aligned_alloc() must be freed with std_aligned_free(). void* std_aligned_alloc(size_t alignment, size_t size) { + #if defined(POSIXALIGNEDALLOC) - void *pointer; - if(posix_memalign(&pointer, alignment, size) == 0) - return pointer; - return nullptr; + void *mem; + return posix_memalign(&mem, alignment, size) ? nullptr : mem; #elif defined(_WIN32) return _mm_malloc(size, alignment); #else @@ -346,6 +346,7 @@ void* std_aligned_alloc(size_t alignment, size_t size) { } void std_aligned_free(void* ptr) { + #if defined(POSIXALIGNEDALLOC) free(ptr); #elif defined(_WIN32) @@ -355,7 +356,7 @@ void std_aligned_free(void* ptr) { #endif } -/// aligned_ttmem_alloc() will return suitably aligned memory, and if possible use large pages. +/// aligned_ttmem_alloc() will return suitably aligned memory, if possible using large pages. /// The returned pointer is the aligned one, while the mem argument is the one that needs /// to be passed to free. With c++17 some of this functionality could be simplified. diff --git a/src/misc.h b/src/misc.h index eb4e05c083b..8ad17b502f4 100644 --- a/src/misc.h +++ b/src/misc.h @@ -65,14 +65,6 @@ std::ostream& operator<<(std::ostream&, SyncCout); #define sync_cout std::cout << IO_LOCK #define sync_endl std::endl << IO_UNLOCK -namespace Utility { - -/// Clamp a value between lo and hi. Available in c++17. -template constexpr const T& clamp(const T& v, const T& lo, const T& hi) { - return v < lo ? lo : v > hi ? hi : v; -} - -} /// xorshift64star Pseudo-Random Number Generator /// This class is based on original code written and dedicated diff --git a/src/movegen.cpp b/src/movegen.cpp index d74df4c32cf..3340f65cf10 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -248,7 +248,7 @@ namespace { *moveList++ = make_move(ksq, pop_lsb(&b)); if ((Type != CAPTURES) && pos.can_castle(Us & ANY_CASTLING)) - for(CastlingRights cr : { Us & KING_SIDE, Us & QUEEN_SIDE } ) + for (CastlingRights cr : { Us & KING_SIDE, Us & QUEEN_SIDE } ) if (!pos.castling_impeded(cr) && pos.can_castle(cr)) *moveList++ = make(ksq, pos.castling_rook_square(cr)); } diff --git a/src/movepick.cpp b/src/movepick.cpp index 96a44449087..153d323e8be 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -182,7 +182,7 @@ Move MovePicker::next_move(bool skipQuiets) { --endMoves; ++stage; - /* fallthrough */ + [[fallthrough]]; case REFUTATION: if (select([&](){ return *cur != MOVE_NONE @@ -190,7 +190,7 @@ Move MovePicker::next_move(bool skipQuiets) { && pos.pseudo_legal(*cur); })) return *(cur - 1); ++stage; - /* fallthrough */ + [[fallthrough]]; case QUIET_INIT: if (!skipQuiets) @@ -203,7 +203,7 @@ Move MovePicker::next_move(bool skipQuiets) { } ++stage; - /* fallthrough */ + [[fallthrough]]; case QUIET: if ( !skipQuiets @@ -217,7 +217,7 @@ Move MovePicker::next_move(bool skipQuiets) { endMoves = endBadCaptures; ++stage; - /* fallthrough */ + [[fallthrough]]; case BAD_CAPTURE: return select([](){ return true; }); @@ -228,7 +228,7 @@ Move MovePicker::next_move(bool skipQuiets) { score(); ++stage; - /* fallthrough */ + [[fallthrough]]; case EVASION: return select([](){ return true; }); @@ -246,14 +246,14 @@ Move MovePicker::next_move(bool skipQuiets) { return MOVE_NONE; ++stage; - /* fallthrough */ + [[fallthrough]]; case QCHECK_INIT: cur = moves; endMoves = generate(pos, cur); ++stage; - /* fallthrough */ + [[fallthrough]]; case QCHECK: return select([](){ return true; }); diff --git a/src/movepick.h b/src/movepick.h index f080935afb1..97ea5bec75f 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -86,14 +86,14 @@ enum StatsType { NoCaptures, Captures }; /// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards typedef Stats ButterflyHistory; -/// At higher depths LowPlyHistory records successful quiet moves near the root and quiet -/// moves which are/were in the PV (ttPv) -/// It is cleared with each new search and filled during iterative deepening +/// At higher depths LowPlyHistory records successful quiet moves near the root +/// and quiet moves which are/were in the PV (ttPv). It is cleared with each new +/// search and filled during iterative deepening. constexpr int MAX_LPH = 4; typedef Stats LowPlyHistory; /// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous -/// move, see www.chessprogramming.org/Countermove_Heuristic +/// move, see www.chessprogramming.org/Countermove_Heuristic typedef Stats CounterMoveHistory; /// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type] diff --git a/src/pawns.cpp b/src/pawns.cpp index 868d0c8ef0f..af0f6618103 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -219,7 +219,7 @@ Score Entry::evaluate_shelter(const Position& pos, Square ksq) const { Score bonus = make_score(5, 5); - File center = Utility::clamp(file_of(ksq), FILE_B, FILE_G); + File center = std::clamp(file_of(ksq), FILE_B, FILE_G); for (File f = File(center - 1); f <= File(center + 1); ++f) { b = ourPawns & file_bb(f); diff --git a/src/position.cpp b/src/position.cpp index 46e5d78b9ae..0289854792f 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1145,8 +1145,8 @@ bool Position::see_ge(Move m, Value threshold) const { // Don't allow pinned pieces to attack (except the king) as long as // there are pinners on their original square. - if (st->pinners[~stm] & occupied) - stmAttackers &= ~st->blockersForKing[stm]; + if (pinners(~stm) & occupied) + stmAttackers &= ~blockers_for_king(stm); if (!stmAttackers) break; diff --git a/src/position.h b/src/position.h index a77050eb5b5..5ce172774da 100644 --- a/src/position.h +++ b/src/position.h @@ -113,6 +113,7 @@ class Position { Bitboard checkers() const; Bitboard blockers_for_king(Color c) const; Bitboard check_squares(PieceType pt) const; + Bitboard pinners(Color c) const; bool is_discovery_check_on_king(Color c, Move m) const; // Attacks to/from a given square @@ -309,6 +310,10 @@ inline Bitboard Position::blockers_for_king(Color c) const { return st->blockersForKing[c]; } +inline Bitboard Position::pinners(Color c) const { + return st->pinners[c]; +} + inline Bitboard Position::check_squares(PieceType pt) const { return st->checkSquares[pt]; } diff --git a/src/search.cpp b/src/search.cpp index ba13680cecb..82d8bb9dfa9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -335,7 +335,7 @@ void Thread::search() { // for match (TC 60+0.6) results spanning a wide range of k values. PRNG rng(now()); double floatLevel = Options["UCI_LimitStrength"] ? - Utility::clamp(std::pow((Options["UCI_Elo"] - 1346.6) / 143.4, 1 / 0.806), 0.0, 20.0) : + std::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); @@ -508,7 +508,7 @@ void Thread::search() { { double fallingEval = (318 + 6 * (mainThread->bestPreviousScore - bestValue) + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 825.0; - fallingEval = Utility::clamp(fallingEval, 0.5, 1.5); + fallingEval = std::clamp(fallingEval, 0.5, 1.5); // If the bestMove is stable over several iterations, reduce time accordingly timeReduction = lastBestMoveDepth + 9 < completedDepth ? 1.92 : 0.95; @@ -807,8 +807,9 @@ namespace { && eval <= alpha - RazorMargin) return qsearch(pos, ss, alpha, beta); - improving = (ss-2)->staticEval == VALUE_NONE ? (ss->staticEval > (ss-4)->staticEval - || (ss-4)->staticEval == VALUE_NONE) : ss->staticEval > (ss-2)->staticEval; + improving = (ss-2)->staticEval == VALUE_NONE + ? ss->staticEval > (ss-4)->staticEval || (ss-4)->staticEval == VALUE_NONE + : ss->staticEval > (ss-2)->staticEval; // Step 8. Futility pruning: child node (~50 Elo) if ( !PvNode @@ -879,8 +880,8 @@ namespace { // there and in further interactions with transposition table cutoff depth is set to depth - 3 // because probCut search has depth set to depth - 4 but we also do a move before it // so effective depth is equal to depth - 3 - && !( ttHit - && tte->depth() >= depth - 3 + && !( ttHit + && tte->depth() >= depth - 3 && ttValue != VALUE_NONE && ttValue < probCutBeta)) { @@ -1238,7 +1239,7 @@ namespace { r++; } - Depth d = Utility::clamp(newDepth - r, 1, newDepth); + Depth d = std::clamp(newDepth - r, 1, newDepth); value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); diff --git a/src/timeman.cpp b/src/timeman.cpp index df4ba9b23b9..6d9c95ef691 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -38,9 +38,9 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { TimePoint slowMover = TimePoint(Options["Slow Mover"]); TimePoint npmsec = TimePoint(Options["nodestime"]); - // opt_scale is a percentage of available time to use for the current move. - // max_scale is a multiplier applied to optimumTime. - double opt_scale, max_scale; + // optScale is a percentage of available time to use for the current move. + // maxScale is a multiplier applied to optimumTime. + double optScale, maxScale; // If we have to play in 'nodes as time' mode, then convert from time // to nodes, and use resulting values in time management formulas. @@ -75,22 +75,22 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { // game time for the current move, so also cap to 20% of available game time. if (limits.movestogo == 0) { - opt_scale = std::min(0.008 + std::pow(ply + 3.0, 0.5) / 250.0, + optScale = std::min(0.008 + std::pow(ply + 3.0, 0.5) / 250.0, 0.2 * limits.time[us] / double(timeLeft)); - max_scale = std::min(7.0, 4.0 + ply / 12.0); + maxScale = std::min(7.0, 4.0 + ply / 12.0); } // x moves in y seconds (+ z increment) else { - opt_scale = std::min((0.8 + ply / 128.0) / mtg, + optScale = std::min((0.8 + ply / 128.0) / mtg, 0.8 * limits.time[us] / double(timeLeft)); - max_scale = std::min(6.3, 1.5 + 0.11 * mtg); + maxScale = std::min(6.3, 1.5 + 0.11 * mtg); } // Never use more than 80% of the available time for this move - optimumTime = TimePoint(opt_scale * timeLeft); - maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, max_scale * optimumTime)); + optimumTime = TimePoint(optScale * timeLeft); + maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, maxScale * optimumTime)); if (Options["Ponder"]) optimumTime += optimumTime / 4; diff --git a/src/uci.cpp b/src/uci.cpp index d64863201cc..bc0ee0a0ce8 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -211,7 +211,7 @@ namespace { double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3]; // Transform eval to centipawns with limited range - double x = Utility::clamp(double(100 * v) / PawnValueEg, -1000.0, 1000.0); + double x = std::clamp(double(100 * v) / PawnValueEg, -1000.0, 1000.0); // Return win rate in per mille (rounded to nearest) return int(0.5 + 1000 / (1 + std::exp((a - x) / b))); From cc9d503ddea998890112efd08fae3705f2727e37 Mon Sep 17 00:00:00 2001 From: syzygy1 <3028851+syzygy1@users.noreply.github.com> Date: Sat, 22 Aug 2020 13:36:34 +0200 Subject: [PATCH 0361/1766] Skip the alignment bug workaround for Clang Clang-10.0.0 poses as gcc-4.2: $ clang++ -E -dM - Date: Sun, 23 Aug 2020 14:22:32 +0300 Subject: [PATCH 0362/1766] Introduce movecount pruning for qsearch() If in quiescence search, we assume that me can prune late moves when: a) the move ordering count of the move is : moveCount > abs(depth) + 2 b) we are not in check c) the late move does not give check d) the late move is not an advanced pawn push Modification of an original idea by @VoyagerOne. STC https://tests.stockfishchess.org/tests/view/5f40581787a5c3c63d8f535f LLR: 2.96 (-2.94,2.94) {-0.25,1.25} Total: 132848 W: 14999 L: 14661 D: 103188 Ptnml(0-2): 684, 11242, 42309, 11430, 759 LTC https://tests.stockfishchess.org/tests/view/5f4226da87a5c3c63d8f5412 LLR: 2.98 (-2.94,2.94) {0.25,1.25} Total: 12008 W: 678 L: 551 D: 10779 Ptnml(0-2): 8, 485, 4899, 596, 16 closes https://github.com/official-stockfish/Stockfish/pull/3053 Bench: 3749974 --- src/movepick.h | 2 +- src/search.cpp | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/movepick.h b/src/movepick.h index 97ea5bec75f..4c0ad55172d 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -93,7 +93,7 @@ constexpr int MAX_LPH = 4; typedef Stats LowPlyHistory; /// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous -/// move, see www.chessprogramming.org/Countermove_Heuristic +/// move, see www.chessprogramming.org/Countermove_Heuristic typedef Stats CounterMoveHistory; /// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type] diff --git a/src/search.cpp b/src/search.cpp index 82d8bb9dfa9..266e2db30d0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1531,6 +1531,10 @@ namespace { { assert(type_of(move) != ENPASSANT); // Due to !pos.advanced_pawn_push + // moveCount pruning + if (moveCount > abs(depth) + 2) + continue; + futilityValue = futilityBase + PieceValue[EG][pos.piece_on(to_sq(move))]; if (futilityValue <= alpha) @@ -1547,7 +1551,7 @@ namespace { } // Do not search moves with negative SEE values - if ( !ss->inCheck && !pos.see_ge(move)) + if (!ss->inCheck && !pos.see_ge(move)) continue; // Speculative prefetch as early as possible From e453f09f06f41680ef96f594f593f8de33e62b8f Mon Sep 17 00:00:00 2001 From: George Sobala Date: Mon, 24 Aug 2020 06:37:42 +0100 Subject: [PATCH 0363/1766] armv8 AArch64 does not require -mfpu=neon -mpfu is not required on AArch64 / armv8 architecture on Linux and throws an error if present. This PR has been tested on gcc and clang on Gentoo-64 and Raspian-64 on a Raspberry Pi 4, as well as with a cross from Ubuntu (`make clean && make -j build ARCH=armv8 COMP=gcc COMPILER=aarch64-linux-gnu-g++`) fixes https://github.com/official-stockfish/Stockfish/issues/3056 closes https://github.com/official-stockfish/Stockfish/pull/3059 No functional change --- src/Makefile | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Makefile b/src/Makefile index 74ef87b94ed..3e1b7c351bc 100644 --- a/src/Makefile +++ b/src/Makefile @@ -241,7 +241,7 @@ ifeq ($(ARCH),armv7-neon) endif ifeq ($(ARCH),armv8) - arch = armv8-a + arch = armv8 prefetch = yes popcnt = yes neon = yes @@ -285,7 +285,7 @@ ifeq ($(COMP),gcc) CXX=g++ CXXFLAGS += -pedantic -Wextra -Wshadow - ifeq ($(arch),$(filter $(arch),armv7 armv8-a)) + ifeq ($(arch),$(filter $(arch),armv7 armv8)) ifeq ($(OS),Android) CXXFLAGS += -m$(bits) LDFLAGS += -m$(bits) @@ -387,7 +387,7 @@ ifeq ($(COMP),ndk) CXXFLAGS += -mthumb -march=armv7-a -mfloat-abi=softfp -mfpu=neon STRIP=arm-linux-androideabi-strip endif - ifeq ($(arch),armv8-a) + ifeq ($(arch),armv8) comp=aarch64-linux-android21-clang CXX=aarch64-linux-android21-clang++ STRIP=aarch64-linux-android-strip @@ -476,7 +476,7 @@ endif ### 3.6 popcnt ifeq ($(popcnt),yes) - ifeq ($(arch),$(filter $(arch),ppc64 armv7 armv8-a arm64)) + ifeq ($(arch),$(filter $(arch),ppc64 armv7 armv8 arm64)) CXXFLAGS += -DUSE_POPCNT else ifeq ($(comp),icc) CXXFLAGS += -msse3 -DUSE_POPCNT @@ -539,9 +539,11 @@ ifeq ($(neon),yes) CXXFLAGS += -DUSE_NEON ifeq ($(KERNEL),Linux) ifneq ($(COMP),ndk) + ifneq ($(arch),armv8) CXXFLAGS += -mfpu=neon endif endif + endif endif ### 3.7 pext @@ -780,7 +782,7 @@ config-sanity: @test "$(optimize)" = "yes" || test "$(optimize)" = "no" @test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \ test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || \ - test "$(arch)" = "armv7" || test "$(arch)" = "armv8-a" || test "$(arch)" = "arm64" + test "$(arch)" = "armv7" || test "$(arch)" = "armv8" || test "$(arch)" = "arm64" @test "$(bits)" = "32" || test "$(bits)" = "64" @test "$(prefetch)" = "yes" || test "$(prefetch)" = "no" @test "$(popcnt)" = "yes" || test "$(popcnt)" = "no" From 701b2427bd84d112376ce858b66befc5b66c4bb2 Mon Sep 17 00:00:00 2001 From: mstembera Date: Thu, 20 Aug 2020 16:59:27 -0700 Subject: [PATCH 0364/1766] Support VNNI on 256bit vectors due to downclocking on current chips (tested up to cascade lake) supporting avx512 and vnni512, it is better to use avx2 or vnni256 in multithreaded (in particular hyperthreaded) engine use. In single threaded use, the picture is different. gcc compilation for vnni256 requires a toolchain for gcc >= 9. closes https://github.com/official-stockfish/Stockfish/pull/3038 No functional change --- .travis.yml | 6 +++-- src/Makefile | 39 ++++++++++++++++++++++++------ src/nnue/layers/affine_transform.h | 8 +++++- 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index a029c4fc818..c1e6d6df41f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -77,8 +77,10 @@ script: # compile only for some more advanced architectures (might not run in travis) - make clean && make -j2 ARCH=x86-64-avx2 build - make clean && make -j2 ARCH=x86-64-bmi2 build - # needs gcc 10 to compile - - if [[ "$COMPILER" != "g++-8" ]]; then make clean && make -j2 ARCH=x86-64-avx512 build; fi + - make clean && make -j2 ARCH=x86-64-avx512 build + - make clean && make -j2 ARCH=x86-64-vnni512 build + # requires gcc 9 or higher + - if [[ "$COMPILER" != "g++-8" ]]; make clean && make -j2 ARCH=x86-64-vnni256 build; fi # # Check perft and reproducible search diff --git a/src/Makefile b/src/Makefile index 3e1b7c351bc..228ea851f20 100644 --- a/src/Makefile +++ b/src/Makefile @@ -75,7 +75,8 @@ endif # sse41 = yes/no --- -msse4.1 --- Use Intel Streaming SIMD Extensions 4.1 # avx2 = yes/no --- -mavx2 --- Use Intel Advanced Vector Extensions 2 # avx512 = yes/no --- -mavx512bw --- Use Intel Advanced Vector Extensions 512 -# vnni = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 512 +# vnni256 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 256 +# vnni512 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 512 # neon = yes/no --- -DUSE_NEON --- Use ARM SIMD architecture # # Note that Makefile is space sensitive, so when adding new architectures @@ -102,7 +103,8 @@ ssse3 = no sse41 = no avx2 = no avx512 = no -vnni = no +vnni256 = no +vnni512 = no neon = no ARCH = x86-64-modern STRIP = strip @@ -192,7 +194,18 @@ ifeq ($(findstring -avx512,$(ARCH)),-avx512) avx512 = yes endif -ifeq ($(findstring -vnni,$(ARCH)),-vnni) +ifeq ($(findstring -vnni256,$(ARCH)),-vnni256) + popcnt = yes + sse = yes + sse2 = yes + ssse3 = yes + sse41 = yes + avx2 = yes + pext = yes + vnni256 = yes +endif + +ifeq ($(findstring -vnni512,$(ARCH)),-vnni512) popcnt = yes sse = yes sse2 = yes @@ -201,7 +214,7 @@ ifeq ($(findstring -vnni,$(ARCH)),-vnni) avx2 = yes pext = yes avx512 = yes - vnni = yes + vnni512 = yes endif ifeq ($(sse),yes) @@ -500,7 +513,14 @@ ifeq ($(avx512),yes) endif endif -ifeq ($(vnni),yes) +ifeq ($(vnni256),yes) + CXXFLAGS += -DUSE_VNNI + ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + CXXFLAGS += -mavx512vnni -mavx512dq -mavx512vl -mprefer-vector-width=256 + endif +endif + +ifeq ($(vnni512),yes) CXXFLAGS += -DUSE_VNNI ifeq ($(comp),$(filter $(comp),gcc clang mingw)) CXXFLAGS += -mavx512vnni -mavx512dq -mavx512vl @@ -623,7 +643,8 @@ help: @echo "" @echo "Supported archs:" @echo "" - @echo "x86-64-vnni > x86 64-bit with vnni support" + @echo "x86-64-vnni512 > x86 64-bit with vnni support 512bit wide" + @echo "x86-64-vnni256 > x86 64-bit with vnni support 256bit wide" @echo "x86-64-avx512 > x86 64-bit with avx512 support" @echo "x86-64-bmi2 > x86 64-bit with bmi2 support" @echo "x86-64-avx2 > x86 64-bit with avx2 support" @@ -767,7 +788,8 @@ config-sanity: @echo "sse41: '$(sse41)'" @echo "avx2: '$(avx2)'" @echo "avx512: '$(avx512)'" - @echo "vnni: '$(vnni)'" + @echo "vnni256: '$(vnni256)'" + @echo "vnni512: '$(vnni512)'" @echo "neon: '$(neon)'" @echo "" @echo "Flags:" @@ -794,7 +816,8 @@ config-sanity: @test "$(sse41)" = "yes" || test "$(sse41)" = "no" @test "$(avx2)" = "yes" || test "$(avx2)" = "no" @test "$(avx512)" = "yes" || test "$(avx512)" = "no" - @test "$(vnni)" = "yes" || test "$(vnni)" = "no" + @test "$(vnni256)" = "yes" || test "$(vnni256)" = "no" + @test "$(vnni512)" = "yes" || test "$(vnni512)" = "no" @test "$(neon)" = "yes" || test "$(neon)" = "no" @test "$(comp)" = "gcc" || test "$(comp)" = "icc" || test "$(comp)" = "mingw" || test "$(comp)" = "clang" \ || test "$(comp)" = "armv7a-linux-androideabi16-clang" || test "$(comp)" = "aarch64-linux-android21-clang" diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 7ac5a1c099f..94d0b5a9494 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -85,8 +85,10 @@ namespace Eval::NNUE::Layers { #elif defined(USE_AVX2) constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; - const __m256i kOnes = _mm256_set1_epi16(1); const auto input_vector = reinterpret_cast(input); + #if !defined(USE_VNNI) + const __m256i kOnes = _mm256_set1_epi16(1); + #endif #elif defined(USE_SSE2) constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; @@ -145,9 +147,13 @@ namespace Eval::NNUE::Layers { __m256i sum = _mm256_setzero_si256(); const auto row = reinterpret_cast(&weights_[offset]); for (IndexType j = 0; j < kNumChunks; ++j) { + #if defined(USE_VNNI) + sum = _mm256_dpbusd_epi32(sum, _mm256_loadA_si256(&input_vector[j]), _mm256_load_si256(&row[j])); + #else __m256i product = _mm256_maddubs_epi16(_mm256_loadA_si256(&input_vector[j]), _mm256_load_si256(&row[j])); product = _mm256_madd_epi16(product, kOnes); sum = _mm256_add_epi32(sum, product); + #endif } __m128i sum128 = _mm_add_epi32(_mm256_castsi256_si128(sum), _mm256_extracti128_si256(sum, 1)); sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_BADC)); From f7b3f0e8426bbf7414d139ed9d1cfa7a98d7314d Mon Sep 17 00:00:00 2001 From: Sami Kiminki Date: Fri, 21 Aug 2020 12:12:39 +0300 Subject: [PATCH 0365/1766] Allow TT entries with key16==0 to be fetched Fix the issue where a TT entry with key16==0 would always be reported as a miss. Instead, we'll use depth8 to detect whether the TT entry is occupied. In order to do that, we'll change DEPTH_OFFSET to -7 (depth8==0) to distinguish between an unoccupied entry and the otherwise lowest possible depth, i.e., DEPTH_NONE (depth8==1). To prevent a performance regression, we'll reorder the TT entry fields by the access order of TranspositionTable::probe(). Memory in general works fastest when accessed in sequential order. We'll also match the store order in TTEntry::save() with the entry field order, and re-order the 'if-or' expressions in TTEntry::save() from the cheapest to the most expensive. Finally, as we now have a proper TT entry occupancy test, we'll fix a minor corner case with hashfull reporting. To reproduce: - Use a big hash - Either: a. Start 31 very quick searches (this wraparounds generation to 0); or b. Force generation of the first search to 0. - go depth infinite Before the fix, hashfull would incorrectly report nearly full hash immediately after the search start, since TranspositionTable::hashfull() used to consider only the entry generation and not whether the entry was actually occupied. STC: LLR: 2.95 (-2.94,2.94) {-0.25,1.25} Total: 36848 W: 4091 L: 3898 D: 28859 Ptnml(0-2): 158, 2996, 11972, 3091, 207 https://tests.stockfishchess.org/tests/view/5f3f98d5dc02a01a0c2881f7 LTC: LLR: 2.95 (-2.94,2.94) {0.25,1.25} Total: 32280 W: 1828 L: 1653 D: 28799 Ptnml(0-2): 34, 1428, 13051, 1583, 44 https://tests.stockfishchess.org/tests/view/5f3fe77a87a5c3c63d8f5332 closes https://github.com/official-stockfish/Stockfish/pull/3048 Bench: 3760677 --- src/tt.cpp | 21 +++++++++++---------- src/tt.h | 12 ++++++------ src/types.h | 3 ++- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/tt.cpp b/src/tt.cpp index d494c27d4ec..60a3a5f1d3e 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -37,18 +37,19 @@ void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) if (m || (uint16_t)k != key16) move16 = (uint16_t)m; - // Overwrite less valuable entries - if ((uint16_t)k != key16 - || d - DEPTH_OFFSET > depth8 - 4 - || b == BOUND_EXACT) + // Overwrite less valuable entries (cheapest checks first) + if (b == BOUND_EXACT + || (uint16_t)k != key16 + || d - DEPTH_OFFSET > depth8 - 4) { - assert(d >= DEPTH_OFFSET); + assert(d > DEPTH_OFFSET); + assert(d < 256 + DEPTH_OFFSET); key16 = (uint16_t)k; + depth8 = (uint8_t)(d - DEPTH_OFFSET); + genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b); value16 = (int16_t)v; eval16 = (int16_t)ev; - genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b); - depth8 = (uint8_t)(d - DEPTH_OFFSET); } } @@ -119,11 +120,11 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const { const uint16_t key16 = (uint16_t)key; // Use the low 16 bits as key inside the cluster for (int i = 0; i < ClusterSize; ++i) - if (!tte[i].key16 || tte[i].key16 == key16) + if (tte[i].key16 == key16 || !tte[i].depth8) { tte[i].genBound8 = uint8_t(generation8 | (tte[i].genBound8 & 0x7)); // Refresh - return found = (bool)tte[i].key16, &tte[i]; + return found = (bool)tte[i].depth8, &tte[i]; } // Find an entry to be replaced according to the replacement strategy @@ -149,7 +150,7 @@ int TranspositionTable::hashfull() const { int cnt = 0; for (int i = 0; i < 1000; ++i) for (int j = 0; j < ClusterSize; ++j) - cnt += (table[i].entry[j].genBound8 & 0xF8) == generation8; + cnt += table[i].entry[j].depth8 && (table[i].entry[j].genBound8 & 0xF8) == generation8; return cnt / ClusterSize; } diff --git a/src/tt.h b/src/tt.h index c177ca52deb..fdfd67694e9 100644 --- a/src/tt.h +++ b/src/tt.h @@ -25,13 +25,13 @@ /// TTEntry struct is the 10 bytes transposition table entry, defined as below: /// /// key 16 bit -/// move 16 bit -/// value 16 bit -/// eval value 16 bit +/// depth 8 bit /// generation 5 bit /// pv node 1 bit /// bound type 2 bit -/// depth 8 bit +/// move 16 bit +/// value 16 bit +/// eval value 16 bit struct TTEntry { @@ -47,11 +47,11 @@ struct TTEntry { friend class TranspositionTable; uint16_t key16; + uint8_t depth8; + uint8_t genBound8; uint16_t move16; int16_t value16; int16_t eval16; - uint8_t genBound8; - uint8_t depth8; }; diff --git a/src/types.h b/src/types.h index 73da41e28f0..1cd711b6f85 100644 --- a/src/types.h +++ b/src/types.h @@ -232,7 +232,8 @@ enum : int { DEPTH_QS_RECAPTURES = -5, DEPTH_NONE = -6, - DEPTH_OFFSET = DEPTH_NONE + + DEPTH_OFFSET = -7 // value used only for TT entry occupancy check }; enum Square : int { From 843a961a8c10d5949e04718b829e3b3d5adeedb4 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Mon, 24 Aug 2020 08:04:16 +0300 Subject: [PATCH 0366/1766] Introduce countermove based pruning for qsearch This patch continues work of previous patch in introducing pruning heuristics in qsearch by analogy to main search, now with countermove based pruning. Idea is that if move is late enough and is quite check (we do generate them in qsearch) and has bad enough countermove history - prune it. passed STC https://tests.stockfishchess.org/tests/view/5f41220287a5c3c63d8f53c5 LLR: 2.93 (-2.94,2.94) {-0.25,1.25} Total: 35944 W: 4127 L: 3929 D: 27888 Ptnml(0-2): 196, 2970, 11459, 3134, 213 passed LTC https://tests.stockfishchess.org/tests/view/5f41862f87a5c3c63d8f53e8 LLR: 2.95 (-2.94,2.94) {0.25,1.25} Total: 138448 W: 7655 L: 7252 D: 123541 Ptnml(0-2): 145, 6247, 56043, 6638, 151 closes https://github.com/official-stockfish/Stockfish/pull/3058 Bench: 3610676 --- src/search.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 266e2db30d0..2ca64a0167e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1570,6 +1570,12 @@ namespace { [pos.moved_piece(move)] [to_sq(move)]; + if ( !captureOrPromotion + && moveCount >= abs(depth) + 1 + && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < CounterMovePruneThreshold + && (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < CounterMovePruneThreshold) + continue; + // Make and search the move pos.do_move(move, st, givesCheck); value = -qsearch(pos, ss+1, -beta, -alpha, depth - 1); From 530fccbf272ffe424ae53a464b91db148cc968ae Mon Sep 17 00:00:00 2001 From: mstembera Date: Mon, 24 Aug 2020 03:38:01 -0700 Subject: [PATCH 0367/1766] Allow for VNNI256 compilation with g++-8 explicitly pass needed -mavx512f -mavx512bw flags closes https://github.com/official-stockfish/Stockfish/pull/3061 No functional change --- .travis.yml | 3 +-- src/Makefile | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index c1e6d6df41f..092c7f53d31 100644 --- a/.travis.yml +++ b/.travis.yml @@ -79,8 +79,7 @@ script: - make clean && make -j2 ARCH=x86-64-bmi2 build - make clean && make -j2 ARCH=x86-64-avx512 build - make clean && make -j2 ARCH=x86-64-vnni512 build - # requires gcc 9 or higher - - if [[ "$COMPILER" != "g++-8" ]]; make clean && make -j2 ARCH=x86-64-vnni256 build; fi + - make clean && make -j2 ARCH=x86-64-vnni256 build # # Check perft and reproducible search diff --git a/src/Makefile b/src/Makefile index 228ea851f20..2e85a144733 100644 --- a/src/Makefile +++ b/src/Makefile @@ -516,7 +516,7 @@ endif ifeq ($(vnni256),yes) CXXFLAGS += -DUSE_VNNI ifeq ($(comp),$(filter $(comp),gcc clang mingw)) - CXXFLAGS += -mavx512vnni -mavx512dq -mavx512vl -mprefer-vector-width=256 + CXXFLAGS += -mavx512f -mavx512bw -mavx512vnni -mavx512dq -mavx512vl -mprefer-vector-width=256 endif endif From b0b4ca17db49ed03057b5fa4ee4a12dab0e9c9e6 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 24 Aug 2020 21:32:04 +0200 Subject: [PATCH 0368/1766] Check ARCH=.... variable to prevent user errors or generating untested code, check explicitly that the ARCH variable is equivalent to a supported architecture as listed in `make help`. To nevertheless compile for an untested target the user can override the internal variable, passing the undocumented `SUPPORTED_ARCH=true` to make. closes https://github.com/official-stockfish/Stockfish/pull/3062 No functional change. --- src/Makefile | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/Makefile b/src/Makefile index 2e85a144733..703aa23020f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -85,8 +85,15 @@ endif ### 2.1. General and architecture defaults -ifeq ($(ARCH),) - empty_arch = yes +# explicitly check for the list of supported architectures (as listed with make help), +# the user can override with `make ARCH=x86-32-vnni256 SUPPORTED_ARCH=true` +ifeq ($(ARCH),$(filter $(ARCH),x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-bmi2 x86-64-avx2 \ + x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \ + x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-32 \ + armv7 armv7-neon armv8 apple-silicon general-64 general-32)) + SUPPORTED_ARCH=true +else + SUPPORTED_ARCH=false endif optimize = yes @@ -625,6 +632,7 @@ endif ### Section 4. Public Targets ### ========================================================================== + help: @echo "" @echo "To compile stockfish, type: " @@ -684,10 +692,12 @@ help: @echo "make -j profile-build ARCH=x86-64-bmi2 COMP=gcc COMPCXX=g++-9.0" @echo "make -j build ARCH=x86-64-ssse3 COMP=clang" @echo "" -ifneq ($(empty_arch), yes) @echo "-------------------------------" +ifeq ($(SUPPORTED_ARCH), true) @echo "The selected architecture $(ARCH) will enable the following configuration: " @$(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity +else + @echo "Specify a supported architecture with the ARCH option for more details" endif @@ -802,6 +812,7 @@ config-sanity: @test "$(debug)" = "yes" || test "$(debug)" = "no" @test "$(sanitize)" = "undefined" || test "$(sanitize)" = "thread" || test "$(sanitize)" = "address" || test "$(sanitize)" = "no" @test "$(optimize)" = "yes" || test "$(optimize)" = "no" + @test "$(SUPPORTED_ARCH)" = "true" @test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \ test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || \ test "$(arch)" = "armv7" || test "$(arch)" = "armv8" || test "$(arch)" = "arm64" From 9b4967071e2fb116673820127522bc43d01d2257 Mon Sep 17 00:00:00 2001 From: syzygy1 <3028851+syzygy1@users.noreply.github.com> Date: Mon, 24 Aug 2020 02:29:38 +0200 Subject: [PATCH 0369/1766] Remove EvalList This patch removes the EvalList structure from the Position object and generally simplifies the interface between do_move() and the NNUE code. The NNUE evaluation function first calculates the "accumulator". The accumulator consists of two halves: one for white's perspective, one for black's perspective. If the "friendly king" has moved or the accumulator for the parent position is not available, the accumulator for this half has to be calculated from scratch. To do this, the NNUE node needs to know the positions and types of all non-king pieces and the position of the friendly king. This information can easily be obtained from the Position object. If the "friendly king" has not moved, its half of the accumulator can be calculated by incrementally updating the accumulator for the previous position. For this, the NNUE code needs to know which pieces have been added to which squares and which pieces have been removed from which squares. In principle this information can be derived from the Position object and StateInfo struct (in the same way as undo_move() does this). However, it is probably a bit faster to prepare this information in do_move(), so I have kept the DirtyPiece struct. Since the DirtyPiece struct now stores the squares rather than "PieceSquare" indices, there are now at most three "dirty pieces" (previously two). A promotion move that captures a piece removes the capturing pawn and the captured piece from the board (to SQ_NONE) and moves the promoted piece to the promotion square (from SQ_NONE). An STC test has confirmed a small speedup: https://tests.stockfishchess.org/tests/view/5f43f06b5089a564a10d850a LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 87704 W: 9763 L: 9500 D: 68441 Ptnml(0-2): 426, 6950, 28845, 7197, 434 closes https://github.com/official-stockfish/Stockfish/pull/3068 No functional change --- src/nnue/evaluate_nnue.cpp | 43 ++++++------ src/nnue/features/feature_set.h | 3 +- src/nnue/features/half_kp.cpp | 58 ++++++---------- src/nnue/features/half_kp.h | 10 +-- src/nnue/nnue_common.h | 21 ++++++ src/position.cpp | 95 +++++++------------------- src/position.h | 23 ------- src/types.h | 116 +++----------------------------- 8 files changed, 96 insertions(+), 273 deletions(-) diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index dfbb1ac2562..e66190892ad 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -29,30 +29,29 @@ #include "evaluate_nnue.h" -ExtPieceSquare kpp_board_index[PIECE_NB] = { - // convention: W - us, B - them - // viewed from other side, W and B are reversed - { PS_NONE, PS_NONE }, - { PS_W_PAWN, PS_B_PAWN }, - { PS_W_KNIGHT, PS_B_KNIGHT }, - { PS_W_BISHOP, PS_B_BISHOP }, - { PS_W_ROOK, PS_B_ROOK }, - { PS_W_QUEEN, PS_B_QUEEN }, - { PS_W_KING, PS_B_KING }, - { PS_NONE, PS_NONE }, - { PS_NONE, PS_NONE }, - { PS_B_PAWN, PS_W_PAWN }, - { PS_B_KNIGHT, PS_W_KNIGHT }, - { PS_B_BISHOP, PS_W_BISHOP }, - { PS_B_ROOK, PS_W_ROOK }, - { PS_B_QUEEN, PS_W_QUEEN }, - { PS_B_KING, PS_W_KING }, - { PS_NONE, PS_NONE } -}; - - namespace Eval::NNUE { + uint32_t kpp_board_index[PIECE_NB][COLOR_NB] = { + // convention: W - us, B - them + // viewed from other side, W and B are reversed + { PS_NONE, PS_NONE }, + { PS_W_PAWN, PS_B_PAWN }, + { PS_W_KNIGHT, PS_B_KNIGHT }, + { PS_W_BISHOP, PS_B_BISHOP }, + { PS_W_ROOK, PS_B_ROOK }, + { PS_W_QUEEN, PS_B_QUEEN }, + { PS_W_KING, PS_B_KING }, + { PS_NONE, PS_NONE }, + { PS_NONE, PS_NONE }, + { PS_B_PAWN, PS_W_PAWN }, + { PS_B_KNIGHT, PS_W_KNIGHT }, + { PS_B_BISHOP, PS_W_BISHOP }, + { PS_B_ROOK, PS_W_ROOK }, + { PS_B_QUEEN, PS_W_QUEEN }, + { PS_B_KING, PS_W_KING }, + { PS_NONE, PS_NONE } + }; + // Input feature converter AlignedPtr feature_transformer; diff --git a/src/nnue/features/feature_set.h b/src/nnue/features/feature_set.h index 79ca83aed0b..558a6b22877 100644 --- a/src/nnue/features/feature_set.h +++ b/src/nnue/features/feature_set.h @@ -68,8 +68,7 @@ namespace Eval::NNUE::Features { reset[perspective] = false; switch (trigger) { case TriggerEvent::kFriendKingMoved: - reset[perspective] = - dp.pieceId[0] == PIECE_ID_KING + perspective; + reset[perspective] = dp.piece[0] == make_piece(perspective, KING); break; default: assert(false); diff --git a/src/nnue/features/half_kp.cpp b/src/nnue/features/half_kp.cpp index 628add6ed12..88e384a3578 100644 --- a/src/nnue/features/half_kp.cpp +++ b/src/nnue/features/half_kp.cpp @@ -23,25 +23,17 @@ namespace Eval::NNUE::Features { - // Find the index of the feature quantity from the king position and PieceSquare - template - inline IndexType HalfKP::MakeIndex(Square sq_k, PieceSquare p) { - return static_cast(PS_END) * static_cast(sq_k) + p; + // Orient a square according to perspective (rotates by 180 for black) + inline Square orient(Color perspective, Square s) { + return Square(int(s) ^ (bool(perspective) * 63)); } - // Get pieces information + // Find the index of the feature quantity from the king position and PieceSquare template - inline void HalfKP::GetPieces( - const Position& pos, Color perspective, - PieceSquare** pieces, Square* sq_target_k) { + inline IndexType HalfKP::MakeIndex( + Color perspective, Square s, Piece pc, Square ksq) { - *pieces = (perspective == BLACK) ? - pos.eval_list()->piece_list_fb() : - pos.eval_list()->piece_list_fw(); - const PieceId target = (AssociatedKing == Side::kFriend) ? - static_cast(PIECE_ID_KING + perspective) : - static_cast(PIECE_ID_KING + ~perspective); - *sq_target_k = static_cast(((*pieces)[target] - PS_W_KING) % SQUARE_NB); + return IndexType(orient(perspective, s) + kpp_board_index[pc][perspective] + PS_END * ksq); } // Get a list of indices for active features @@ -49,16 +41,11 @@ namespace Eval::NNUE::Features { void HalfKP::AppendActiveIndices( const Position& pos, Color perspective, IndexList* active) { - // Do nothing if array size is small to avoid compiler warning - if (RawFeatures::kMaxActiveDimensions < kMaxActiveDimensions) return; - - PieceSquare* pieces; - Square sq_target_k; - GetPieces(pos, perspective, &pieces, &sq_target_k); - for (PieceId i = PIECE_ID_ZERO; i < PIECE_ID_KING; ++i) { - if (pieces[i] != PS_NONE) { - active->push_back(MakeIndex(sq_target_k, pieces[i])); - } + Square ksq = orient(perspective, pos.square(perspective)); + Bitboard bb = pos.pieces() & ~pos.pieces(KING); + while (bb) { + Square s = pop_lsb(&bb); + active->push_back(MakeIndex(perspective, s, pos.piece_on(s), ksq)); } } @@ -68,22 +55,15 @@ namespace Eval::NNUE::Features { const Position& pos, Color perspective, IndexList* removed, IndexList* added) { - PieceSquare* pieces; - Square sq_target_k; - GetPieces(pos, perspective, &pieces, &sq_target_k); + Square ksq = orient(perspective, pos.square(perspective)); const auto& dp = pos.state()->dirtyPiece; for (int i = 0; i < dp.dirty_num; ++i) { - if (dp.pieceId[i] >= PIECE_ID_KING) continue; - const auto old_p = static_cast( - dp.old_piece[i].from[perspective]); - if (old_p != PS_NONE) { - removed->push_back(MakeIndex(sq_target_k, old_p)); - } - const auto new_p = static_cast( - dp.new_piece[i].from[perspective]); - if (new_p != PS_NONE) { - added->push_back(MakeIndex(sq_target_k, new_p)); - } + Piece pc = dp.piece[i]; + if (type_of(pc) == KING) continue; + if (dp.from[i] != SQ_NONE) + removed->push_back(MakeIndex(perspective, dp.from[i], pc, ksq)); + if (dp.to[i] != SQ_NONE) + added->push_back(MakeIndex(perspective, dp.to[i], pc, ksq)); } } diff --git a/src/nnue/features/half_kp.h b/src/nnue/features/half_kp.h index 99842eea777..ee6a8df39ae 100644 --- a/src/nnue/features/half_kp.h +++ b/src/nnue/features/half_kp.h @@ -41,7 +41,7 @@ namespace Eval::NNUE::Features { static constexpr IndexType kDimensions = static_cast(SQUARE_NB) * static_cast(PS_END); // Maximum number of simultaneously active features - static constexpr IndexType kMaxActiveDimensions = PIECE_ID_KING; + static constexpr IndexType kMaxActiveDimensions = 30; // Kings don't count // Trigger for full calculation instead of difference calculation static constexpr TriggerEvent kRefreshTrigger = TriggerEvent::kFriendKingMoved; @@ -53,13 +53,9 @@ namespace Eval::NNUE::Features { static void AppendChangedIndices(const Position& pos, Color perspective, IndexList* removed, IndexList* added); - // Index of a feature for a given king position and another piece on some square - static IndexType MakeIndex(Square sq_k, PieceSquare p); - private: - // Get pieces information - static void GetPieces(const Position& pos, Color perspective, - PieceSquare** pieces, Square* sq_target_k); + // Index of a feature for a given king position and another piece on some square + static IndexType MakeIndex(Color perspective, Square s, Piece pc, Square sq_k); }; } // namespace Eval::NNUE::Features diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index a9d8e5af98d..7bc905dc751 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -94,6 +94,27 @@ namespace Eval::NNUE { constexpr std::size_t kMaxSimdWidth = 32; + // unique number for each piece type on each square + enum { + PS_NONE = 0, + PS_W_PAWN = 1, + PS_B_PAWN = 1 * SQUARE_NB + 1, + PS_W_KNIGHT = 2 * SQUARE_NB + 1, + PS_B_KNIGHT = 3 * SQUARE_NB + 1, + PS_W_BISHOP = 4 * SQUARE_NB + 1, + PS_B_BISHOP = 5 * SQUARE_NB + 1, + PS_W_ROOK = 6 * SQUARE_NB + 1, + PS_B_ROOK = 7 * SQUARE_NB + 1, + PS_W_QUEEN = 8 * SQUARE_NB + 1, + PS_B_QUEEN = 9 * SQUARE_NB + 1, + PS_W_KING = 10 * SQUARE_NB + 1, + PS_END = PS_W_KING, // pieces without kings (pawns included) + PS_B_KING = 11 * SQUARE_NB + 1, + PS_END2 = 12 * SQUARE_NB + 1 + }; + + extern uint32_t kpp_board_index[PIECE_NB][COLOR_NB]; + // Type of input feature after conversion using TransformedFeatureType = std::uint8_t; using IndexType = std::uint32_t; diff --git a/src/position.cpp b/src/position.cpp index 0289854792f..fe89b75317f 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -198,9 +198,6 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th std::fill_n(&pieceList[0][0], sizeof(pieceList) / sizeof(Square), SQ_NONE); st = si; - // Each piece on board gets a unique ID used to track the piece later - PieceId piece_id, next_piece_id = PIECE_ID_ZERO; - ss >> std::noskipws; // 1. Piece placement @@ -212,21 +209,8 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th else if (token == '/') sq += 2 * SOUTH; - else if ((idx = PieceToChar.find(token)) != string::npos) - { - auto pc = Piece(idx); - put_piece(pc, sq); - - if (Eval::useNNUE) - { - // Kings get a fixed ID, other pieces get ID in order of placement - piece_id = - (idx == W_KING) ? PIECE_ID_WKING : - (idx == B_KING) ? PIECE_ID_BKING : - next_piece_id++; - evalList.put_piece(piece_id, sq, pc); - } - + else if ((idx = PieceToChar.find(token)) != string::npos) { + put_piece(Piece(idx), sq); ++sq; } } @@ -721,8 +705,6 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // Used by NNUE st->accumulator.computed_accumulation = false; st->accumulator.computed_score = false; - PieceId dp0 = PIECE_ID_NONE; - PieceId dp1 = PIECE_ID_NONE; auto& dp = st->dirtyPiece; dp.dirty_num = 1; @@ -775,12 +757,10 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { if (Eval::useNNUE) { - dp.dirty_num = 2; // 2 pieces moved - dp1 = piece_id_on(capsq); - dp.pieceId[1] = dp1; - dp.old_piece[1] = evalList.piece_with_id(dp1); - evalList.put_piece(dp1, capsq, NO_PIECE); - dp.new_piece[1] = evalList.piece_with_id(dp1); + dp.dirty_num = 2; // 1 piece moved, 1 piece captured + dp.piece[1] = captured; + dp.from[1] = capsq; + dp.to[1] = SQ_NONE; } // Update board and piece lists @@ -821,11 +801,9 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { { if (Eval::useNNUE) { - dp0 = piece_id_on(from); - dp.pieceId[0] = dp0; - dp.old_piece[0] = evalList.piece_with_id(dp0); - evalList.put_piece(dp0, to, pc); - dp.new_piece[0] = evalList.piece_with_id(dp0); + dp.piece[0] = pc; + dp.from[0] = from; + dp.to[0] = to; } move_piece(from, to); @@ -854,9 +832,12 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { if (Eval::useNNUE) { - dp0 = piece_id_on(to); - evalList.put_piece(dp0, to, promotion); - dp.new_piece[0] = evalList.piece_with_id(dp0); + // Promoting pawn to SQ_NONE, promoted piece from SQ_NONE + dp.to[0] = SQ_NONE; + dp.piece[dp.dirty_num] = promotion; + dp.from[dp.dirty_num] = SQ_NONE; + dp.to[dp.dirty_num] = to; + dp.dirty_num++; } // Update hash keys @@ -950,12 +931,6 @@ void Position::undo_move(Move m) { { move_piece(to, from); // Put the piece back at the source square - if (Eval::useNNUE) - { - PieceId dp0 = st->dirtyPiece.pieceId[0]; - evalList.put_piece(dp0, from, pc); - } - if (st->capturedPiece) { Square capsq = to; @@ -972,14 +947,6 @@ void Position::undo_move(Move m) { } put_piece(st->capturedPiece, capsq); // Restore the captured piece - - if (Eval::useNNUE) - { - PieceId dp1 = st->dirtyPiece.pieceId[1]; - assert(evalList.piece_with_id(dp1).from[WHITE] == PS_NONE); - assert(evalList.piece_with_id(dp1).from[BLACK] == PS_NONE); - evalList.put_piece(dp1, capsq, st->capturedPiece); - } } } @@ -1001,32 +968,16 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1); to = relative_square(us, kingSide ? SQ_G1 : SQ_C1); - if (Eval::useNNUE) + if (Do && Eval::useNNUE) { - PieceId dp0, dp1; auto& dp = st->dirtyPiece; - dp.dirty_num = 2; // 2 pieces moved - - if (Do) - { - dp0 = piece_id_on(from); - dp1 = piece_id_on(rfrom); - dp.pieceId[0] = dp0; - dp.old_piece[0] = evalList.piece_with_id(dp0); - evalList.put_piece(dp0, to, make_piece(us, KING)); - dp.new_piece[0] = evalList.piece_with_id(dp0); - dp.pieceId[1] = dp1; - dp.old_piece[1] = evalList.piece_with_id(dp1); - evalList.put_piece(dp1, rto, make_piece(us, ROOK)); - dp.new_piece[1] = evalList.piece_with_id(dp1); - } - else - { - dp0 = piece_id_on(to); - dp1 = piece_id_on(rto); - evalList.put_piece(dp0, from, make_piece(us, KING)); - evalList.put_piece(dp1, rfrom, make_piece(us, ROOK)); - } + dp.piece[0] = make_piece(us, KING); + dp.from[0] = from; + dp.to[0] = to; + dp.piece[1] = make_piece(us, ROOK); + dp.from[1] = rfrom; + dp.to[1] = rto; + dp.dirty_num = 2; } // Remove both pieces first since squares could overlap in Chess960 diff --git a/src/position.h b/src/position.h index 5ce172774da..d6f5c9fdb11 100644 --- a/src/position.h +++ b/src/position.h @@ -171,7 +171,6 @@ class Position { // Used by NNUE StateInfo* state() const; - const EvalList* eval_list() const; private: // Initialization helpers (used while setting up a position) @@ -186,9 +185,6 @@ class Position { template void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto); - // ID of a piece on a given square - PieceId piece_id_on(Square sq) const; - // Data members Piece board[SQUARE_NB]; Bitboard byTypeBB[PIECE_TYPE_NB]; @@ -205,9 +201,6 @@ class Position { Thread* thisThread; StateInfo* st; bool chess960; - - // List of pieces used in NNUE evaluation function - EvalList evalList; }; namespace PSQT { @@ -451,20 +444,4 @@ inline StateInfo* Position::state() const { return st; } -inline const EvalList* Position::eval_list() const { - - return &evalList; -} - -inline PieceId Position::piece_id_on(Square sq) const -{ - - assert(piece_on(sq) != NO_PIECE); - - PieceId pid = evalList.piece_id_list[sq]; - assert(is_ok(pid)); - - return pid; -} - #endif // #ifndef POSITION_H_INCLUDED diff --git a/src/types.h b/src/types.h index 1cd711b6f85..5873c698f73 100644 --- a/src/types.h +++ b/src/types.h @@ -201,22 +201,6 @@ enum Piece { PIECE_NB = 16 }; -// An ID used to track the pieces. Max. 32 pieces on board. -enum PieceId { - PIECE_ID_ZERO = 0, - PIECE_ID_KING = 30, - PIECE_ID_WKING = 30, - PIECE_ID_BKING = 31, - PIECE_ID_NONE = 32 -}; - -inline PieceId operator++(PieceId& d, int) { - - PieceId x = d; - d = PieceId(int(d) + 1); - return x; -} - constexpr Value PieceValue[PHASE_NB][PIECE_NB] = { { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, VALUE_ZERO, VALUE_ZERO, VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, VALUE_ZERO, VALUE_ZERO }, @@ -271,93 +255,20 @@ enum Rank : int { RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB }; -// unique number for each piece type on each square -enum PieceSquare : uint32_t { - PS_NONE = 0, - PS_W_PAWN = 1, - PS_B_PAWN = 1 * SQUARE_NB + 1, - PS_W_KNIGHT = 2 * SQUARE_NB + 1, - PS_B_KNIGHT = 3 * SQUARE_NB + 1, - PS_W_BISHOP = 4 * SQUARE_NB + 1, - PS_B_BISHOP = 5 * SQUARE_NB + 1, - PS_W_ROOK = 6 * SQUARE_NB + 1, - PS_B_ROOK = 7 * SQUARE_NB + 1, - PS_W_QUEEN = 8 * SQUARE_NB + 1, - PS_B_QUEEN = 9 * SQUARE_NB + 1, - PS_W_KING = 10 * SQUARE_NB + 1, - PS_END = PS_W_KING, // pieces without kings (pawns included) - PS_B_KING = 11 * SQUARE_NB + 1, - PS_END2 = 12 * SQUARE_NB + 1 -}; - -struct ExtPieceSquare { - PieceSquare from[COLOR_NB]; -}; - -// Array for finding the PieceSquare corresponding to the piece on the board -extern ExtPieceSquare kpp_board_index[PIECE_NB]; - -constexpr bool is_ok(PieceId pid); -constexpr Square rotate180(Square sq); - -// Structure holding which tracked piece (PieceId) is where (PieceSquare) -class EvalList { - -public: - // Max. number of pieces without kings is 30 but must be a multiple of 4 in AVX2 - static const int MAX_LENGTH = 32; - - // Array that holds the piece id for the pieces on the board - PieceId piece_id_list[SQUARE_NB]; - - // List of pieces, separate from White and Black POV - PieceSquare* piece_list_fw() const { return const_cast(pieceListFw); } - PieceSquare* piece_list_fb() const { return const_cast(pieceListFb); } - - // Place the piece pc with piece_id on the square sq on the board - void put_piece(PieceId piece_id, Square sq, Piece pc) - { - assert(is_ok(piece_id)); - if (pc != NO_PIECE) - { - pieceListFw[piece_id] = PieceSquare(kpp_board_index[pc].from[WHITE] + sq); - pieceListFb[piece_id] = PieceSquare(kpp_board_index[pc].from[BLACK] + rotate180(sq)); - piece_id_list[sq] = piece_id; - } - else - { - pieceListFw[piece_id] = PS_NONE; - pieceListFb[piece_id] = PS_NONE; - piece_id_list[sq] = piece_id; - } - } - - // Convert the specified piece_id piece to ExtPieceSquare type and return it - ExtPieceSquare piece_with_id(PieceId piece_id) const - { - ExtPieceSquare eps; - eps.from[WHITE] = pieceListFw[piece_id]; - eps.from[BLACK] = pieceListFb[piece_id]; - return eps; - } - -private: - PieceSquare pieceListFw[MAX_LENGTH]; - PieceSquare pieceListFb[MAX_LENGTH]; -}; - -// For differential evaluation of pieces that changed since last turn +// Keep track of what a move changes on the board (used by NNUE) struct DirtyPiece { // Number of changed pieces int dirty_num; - // The ids of changed pieces, max. 2 pieces can change in one move - PieceId pieceId[2]; + // Max 3 pieces can change in one move. A promotion with capture moves + // both the pawn and the captured piece to SQ_NONE and the piece promoted + // to from SQ_NONE to the capture square. + Piece piece[3]; - // What changed from the piece with that piece number - ExtPieceSquare old_piece[2]; - ExtPieceSquare new_piece[2]; + // From and to squares, which may be SQ_NONE + Square from[3]; + Square to[3]; }; /// Score enum stores a middlegame and an endgame value in a single integer (enum). @@ -407,8 +318,6 @@ ENABLE_FULL_OPERATORS_ON(Value) ENABLE_FULL_OPERATORS_ON(Direction) ENABLE_INCR_OPERATORS_ON(Piece) -ENABLE_INCR_OPERATORS_ON(PieceSquare) -ENABLE_INCR_OPERATORS_ON(PieceId) ENABLE_INCR_OPERATORS_ON(PieceType) ENABLE_INCR_OPERATORS_ON(Square) ENABLE_INCR_OPERATORS_ON(File) @@ -497,10 +406,6 @@ inline Color color_of(Piece pc) { return Color(pc >> 3); } -constexpr bool is_ok(PieceId pid) { - return pid < PIECE_ID_NONE; -} - constexpr bool is_ok(Square s) { return s >= SQ_A1 && s <= SQ_H8; } @@ -537,11 +442,6 @@ constexpr Square to_sq(Move m) { return Square(m & 0x3F); } -// Return relative square when turning the board 180 degrees -constexpr Square rotate180(Square sq) { - return (Square)(sq ^ 0x3F); -} - constexpr int from_to(Move m) { return m & 0xFFF; } From 95b8f3f8005598fc3ad07a7a8f6440d828cabc29 Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Sun, 23 Aug 2020 12:04:50 -0400 Subject: [PATCH 0370/1766] Remove Reduce Depth Remove Reduce Depth at PV nodes. STC: LLR: 2.94 (-2.94,2.94) {-1.25,0.25} Total: 56760 W: 6299 L: 6236 D: 44225 Ptnml(0-2): 286, 4843, 18076, 4872, 303 https://tests.stockfishchess.org/tests/view/5f41356087a5c3c63d8f53c9 LTC: LLR: 2.95 (-2.94,2.94) {-0.75,0.25} Total: 17496 W: 954 L: 865 D: 15677 Ptnml(0-2): 13, 768, 7098, 855, 14 https://tests.stockfishchess.org/tests/view/5f41bb7687a5c3c63d8f53f9 closes https://github.com/official-stockfish/Stockfish/pull/3055 Bench: 3555051 --- src/search.cpp | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 2ca64a0167e..d6b611a31bb 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -940,12 +940,6 @@ namespace { } } - // Step 11. If the position is not in TT, decrease depth by 2 - if ( PvNode - && depth >= 6 - && !ttMove) - depth -= 2; - moves_loop: // When in check, search starts from here const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, @@ -969,7 +963,7 @@ namespace { // Mark this node as being searched ThreadHolding th(thisThread, posKey, ss->ply); - // Step 12. Loop through all pseudo-legal moves until no moves remain + // Step 11. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. while ((move = mp.next_move(moveCountPruning)) != MOVE_NONE) { @@ -1007,7 +1001,7 @@ namespace { // Calculate new depth for this move newDepth = depth - 1; - // Step 13. Pruning at shallow depth (~200 Elo) + // Step 12. Pruning at shallow depth (~200 Elo) if ( !rootNode && pos.non_pawn_material(us) && bestValue > VALUE_TB_LOSS_IN_MAX_PLY) @@ -1065,7 +1059,7 @@ namespace { } } - // Step 14. Extensions (~75 Elo) + // Step 13. Extensions (~75 Elo) // Singular extension search (~70 Elo). If all moves but one fail low on a // search of (alpha-s, beta-s), and just one fails high on (alpha, beta), @@ -1148,10 +1142,10 @@ namespace { [movedPiece] [to_sq(move)]; - // Step 15. Make the move + // Step 14. Make the move pos.do_move(move, st, givesCheck); - // Step 16. Reduced depth search (LMR, ~200 Elo). If the move fails high it will be + // Step 15. Reduced depth search (LMR, ~200 Elo). If the move fails high it will be // re-searched at full depth. if ( depth >= 3 && moveCount > 1 + 2 * rootNode + 2 * (PvNode && abs(bestValue) < 2) @@ -1254,7 +1248,7 @@ namespace { didLMR = false; } - // Step 17. Full depth search when LMR is skipped or fails high + // Step 16. Full depth search when LMR is skipped or fails high if (doFullDepthSearch) { value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode); @@ -1282,12 +1276,12 @@ namespace { value = -search(pos, ss+1, -beta, -alpha, newDepth, false); } - // Step 18. Undo move + // Step 17. Undo move pos.undo_move(move); assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); - // Step 19. Check for a new best move + // Step 18. Check for a new best move // Finished searching the move. If a stop occurred, the return value of // the search cannot be trusted, and we return immediately without // updating best move, PV and TT. @@ -1364,7 +1358,7 @@ namespace { return VALUE_DRAW; */ - // Step 20. Check for mate and stalemate + // Step 19. Check for mate and stalemate // All legal moves have been searched and if there are no legal moves, it // must be a mate or a stalemate. If we are in a singular extension search then // return a fail low score. From 242a7d9fead561488ca176a4687deef8859918f2 Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Tue, 25 Aug 2020 09:10:47 -0400 Subject: [PATCH 0371/1766] Simplify MCP in QS Simplify moveCount pruning in QS by removing depth dependency. STC LLR: 2.94 (-2.94,2.94) {-1.25,0.25} Total: 42960 W: 4741 L: 4661 D: 33558 Ptnml(0-2): 218, 3574, 13804, 3678, 206 https://tests.stockfishchess.org/tests/view/5f42e3f75089a564a10d8493 LTC LLR: 2.94 (-2.94,2.94) {-0.75,0.25} Total: 66672 W: 3563 L: 3508 D: 59601 Ptnml(0-2): 71, 3064, 26996, 3149, 56 https://tests.stockfishchess.org/tests/view/5f4353285089a564a10d84d0 closes https://github.com/official-stockfish/Stockfish/pull/3067 Bench: 4074430 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index d6b611a31bb..cae8a684562 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1526,7 +1526,7 @@ namespace { assert(type_of(move) != ENPASSANT); // Due to !pos.advanced_pawn_push // moveCount pruning - if (moveCount > abs(depth) + 2) + if (moveCount > 2) continue; futilityValue = futilityBase + PieceValue[EG][pos.piece_on(to_sq(move))]; From 406979ea12ee7828e079871b0f9f3dc8f127a741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sun, 23 Aug 2020 13:43:38 +0200 Subject: [PATCH 0372/1766] Embed default net, and simplify using non-default nets covers the most important cases from the user perspective: It embeds the default net in the binary, so a download of that binary will result in a working engine with the default net. The engine will be functional in the default mode without any additional user action. It allows non-default nets to be used, which will be looked for in up to three directories (working directory, location of the binary, and optionally a specific default directory). This mechanism is also kept for those developers that use MSVC, the one compiler that doesn't have an easy mechanism for embedding data. It is possible to disable embedding, and instead specify a specific directory, e.g. linux distros might want to use CXXFLAGS="-DNNUE_EMBEDDING_OFF -DDEFAULT_NNUE_DIRECTORY=/usr/share/games/stockfish/" make -j ARCH=x86-64 profile-build passed STC non-regression: https://tests.stockfishchess.org/tests/view/5f4a581c150f0aef5f8ae03a LLR: 2.95 (-2.94,2.94) {-1.25,-0.25} Total: 66928 W: 7202 L: 7147 D: 52579 Ptnml(0-2): 291, 5309, 22211, 5360, 293 closes https://github.com/official-stockfish/Stockfish/pull/3070 fixes https://github.com/official-stockfish/Stockfish/issues/3030 No functional change. --- AUTHORS | 3 +- README.md | 18 +- appveyor.yml | 2 +- src/Makefile | 10 +- src/evaluate.cpp | 103 +++++++++-- src/evaluate.h | 7 +- src/incbin/UNLICENCE | 26 +++ src/incbin/incbin.h | 368 +++++++++++++++++++++++++++++++++++++ src/main.cpp | 1 + src/misc.cpp | 59 ++++++ src/misc.h | 9 +- src/nnue/evaluate_nnue.cpp | 14 +- src/ucioption.cpp | 5 +- 13 files changed, 582 insertions(+), 43 deletions(-) create mode 100644 src/incbin/UNLICENCE create mode 100755 src/incbin/incbin.h diff --git a/AUTHORS b/AUTHORS index c96f870a6a6..c00ab657ff7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -36,10 +36,11 @@ Bryan Cross (crossbr) candirufish Chess13234 Chris Cain (ceebo) +Dale Weiler (graphitemaster) Dan Schmidt (dfannius) Daniel Axtens (daxtens) Daniel Dugovic (ddugovic) -Dariusz Orzechowski +Dariusz Orzechowski (dorzechowski) David Zar Daylen Yang (daylen) DiscanX diff --git a/README.md b/README.md index 2cc88bf4279..96a495ae54b 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,9 @@ about how to use Stockfish with it. The Stockfish engine features two evaluation functions for chess, the classical evaluation based on handcrafted terms, and the NNUE evaluation based on efficiently -updateable neural networks. The classical evaluation runs efficiently on most 64bit -CPU architectures, while the NNUE evaluation benefits strongly from the vector -intrinsics available on modern CPUs (avx2 or similar). +updateable neural networks. The classical evaluation runs efficiently on almost all +CPU architectures, while the NNUE evaluation benefits from the vector +intrinsics available on most CPUs (sse2, avx2, neon, or similar). ## Files @@ -29,10 +29,11 @@ This distribution of Stockfish consists of the following files: that can be used to compile Stockfish on Unix-like systems. * a file with the .nnue extension, storing the neural network for the NNUE - evaluation. + evaluation. Binary distributions will have this file embedded. Note: to use the NNUE evaluation, the additional data file with neural network parameters -needs to be downloaded. The filename for the default net can be found as the default +needs to be available. Normally, this file is already embedded in the binary or it can be downloaded. +The filename for the default (recommended) net can be found as the default value of the `EvalFile` UCI option, with the format `nn-[SHA256 first 12 digits].nnue` (for instance, `nn-c157e0a5755b.nnue`). This file can be downloaded from ``` @@ -61,11 +62,14 @@ Currently, Stockfish has the following UCI options: * #### Use NNUE Toggle between the NNUE and classical evaluation functions. If set to "true", - the network parameters must be available to load from file (see also EvalFile). + the network parameters must be available to load from file (see also EvalFile), + if they are not embedded in the binary. * #### EvalFile The name of the file of the NNUE evaluation parameters. Depending on the GUI the - filename should include the full path to the folder/directory that contains the file. + filename might have to include the full path to the folder/directory that contains the file. + Other locations, such as the directory that contains the binary and the working directory, + are also searched. * #### UCI_AnalyseMode An option handled by your GUI. diff --git a/appveyor.yml b/appveyor.yml index a3732a23f4a..ab6084090a4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -63,7 +63,7 @@ build_script: - cmake --build . --config %CONFIGURATION% -- /verbosity:minimal - ps: | # Download default NNUE net from fishtest - $nnuenet = Get-Content -Path src\ucioption.cpp | Select-String -CaseSensitive -Pattern "Option" | Select-String -CaseSensitive -Pattern "nn-[a-z0-9]{12}.nnue" + $nnuenet = Get-Content -Path src\evaluate.h | Select-String -CaseSensitive -Pattern "EvalFileDefaultName" | Select-String -CaseSensitive -Pattern "nn-[a-z0-9]{12}.nnue" $dummy = $nnuenet -match "(?nn-[a-z0-9]{12}.nnue)" $nnuenet = $Matches.nnuenet Write-Host "Default net:" $nnuenet diff --git a/src/Makefile b/src/Makefile index 703aa23020f..5f363f02412 100644 --- a/src/Makefile +++ b/src/Makefile @@ -614,10 +614,12 @@ ifeq ($(debug), no) # So, only enable it for a cross from Linux by default. else ifeq ($(comp),mingw) ifeq ($(KERNEL),Linux) + ifneq ($(arch),i386) CXXFLAGS += -flto LDFLAGS += $(CXXFLAGS) -flto=jobserver endif endif + endif endif endif @@ -705,7 +707,7 @@ endif config-sanity icc-profile-use icc-profile-make gcc-profile-use gcc-profile-make \ clang-profile-use clang-profile-make -build: config-sanity +build: config-sanity net $(MAKE) ARCH=$(ARCH) COMP=$(COMP) all profile-build: net config-sanity objclean profileclean @@ -731,12 +733,13 @@ install: -cp $(EXE) $(BINDIR) -strip $(BINDIR)/$(EXE) -#clean all +# clean all clean: objclean profileclean @rm -f .depend *~ core +# evaluation network (nnue) net: - $(eval nnuenet := $(shell grep EvalFile ucioption.cpp | grep Option | sed 's/.*\(nn-[a-z0-9]\{12\}.nnue\).*/\1/')) + $(eval nnuenet := $(shell grep EvalFileDefaultName evaluate.h | grep define | sed 's/.*\(nn-[a-z0-9]\{12\}.nnue\).*/\1/')) @echo "Default net: $(nnuenet)" $(eval nnuedownloadurl := https://tests.stockfishchess.org/api/nn/$(nnuenet)) $(eval curl_or_wget := $(shell if hash curl 2>/dev/null; then echo "curl -skL"; elif hash wget 2>/dev/null; then echo "wget -qO-"; fi)) @@ -758,7 +761,6 @@ net: echo "shasum / sha256sum not found, skipping net validation"; \ fi - # clean binaries and objects objclean: @rm -f $(EXE) *.o ./syzygy/*.o ./nnue/*.o ./nnue/features/*.o diff --git a/src/evaluate.cpp b/src/evaluate.cpp index ce92db9a733..67154751f2a 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -20,51 +20,126 @@ #include #include #include // For std::memset +#include #include #include #include +#include +#include #include "bitboard.h" #include "evaluate.h" #include "material.h" +#include "misc.h" #include "pawns.h" #include "thread.h" #include "uci.h" +#include "incbin/incbin.h" + + +// Macro to embed the default NNUE file data in the engine binary (using incbin.h, by Dale Weiler). +// This macro invocation will declare the following three variables +// const unsigned char gEmbeddedNNUEData[]; // a pointer to the embedded data +// const unsigned char *const gEmbeddedNNUEEnd; // a marker to the end +// const unsigned int gEmbeddedNNUESize; // the size of the embedded file +// Note that this does not work in Microsof Visual Studio. +#if !defined(_MSC_VER) && !defined(NNUE_EMBEDDING_OFF) + INCBIN(EmbeddedNNUE, EvalFileDefaultName); +#else + const unsigned char gEmbeddedNNUEData[1] = {0x0}; + const unsigned char *const gEmbeddedNNUEEnd = &gEmbeddedNNUEData[1]; + const unsigned int gEmbeddedNNUESize = 1; +#endif + + +using namespace std; +using namespace Eval::NNUE; namespace Eval { bool useNNUE; - std::string eval_file_loaded="None"; + string eval_file_loaded = "None"; + + /// init_NNUE() tries to load a nnue network at startup time, or when the engine + /// receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue" + /// The name of the nnue network is always retrieved from the EvalFile option. + /// We search the given network in three locations: internally (the default + /// network may be embedded in the binary), in the active working directory and + /// in the engine directory. Distro packagers may define the DEFAULT_NNUE_DIRECTORY + /// variable to have the engine search in a special directory in their distro. void init_NNUE() { useNNUE = Options["Use NNUE"]; - std::string eval_file = std::string(Options["EvalFile"]); - if (useNNUE && eval_file_loaded != eval_file) - if (Eval::NNUE::load_eval_file(eval_file)) - eval_file_loaded = eval_file; + if (!useNNUE) + return; + + string eval_file = string(Options["EvalFile"]); + + #if defined(DEFAULT_NNUE_DIRECTORY) + #define stringify2(x) #x + #define stringify(x) stringify2(x) + vector dirs = { "" , "" , CommandLine::binaryDirectory , stringify(DEFAULT_NNUE_DIRECTORY) }; + #else + vector dirs = { "" , "" , CommandLine::binaryDirectory }; + #endif + + for (string directory : dirs) + if (eval_file_loaded != eval_file) + { + if (directory != "") + { + ifstream stream(directory + eval_file, ios::binary); + if (load_eval(eval_file, stream)) + eval_file_loaded = eval_file; + } + + if (directory == "" && eval_file == EvalFileDefaultName) + { + // C++ way to prepare a buffer for a memory stream + class MemoryBuffer : public basic_streambuf { + public: MemoryBuffer(char* p, size_t n) { setg(p, p, p + n); setp(p, p + n); } + }; + + MemoryBuffer buffer(const_cast(reinterpret_cast(gEmbeddedNNUEData)), + size_t(gEmbeddedNNUESize)); + + istream stream(&buffer); + if (load_eval(eval_file, stream)) + eval_file_loaded = eval_file; + } + } } + /// verify_NNUE() verifies that the last net used was loaded successfully void verify_NNUE() { - std::string eval_file = std::string(Options["EvalFile"]); + string eval_file = string(Options["EvalFile"]); + if (useNNUE && eval_file_loaded != eval_file) { UCI::OptionsMap defaults; UCI::init(defaults); - sync_cout << "info string ERROR: NNUE evaluation used, but the network file " << eval_file << " was not loaded successfully." << sync_endl; - sync_cout << "info string ERROR: The UCI option EvalFile might need to specify the full path, including the directory/folder name, to the file." << sync_endl; - sync_cout << "info string ERROR: The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/"+std::string(defaults["EvalFile"]) << sync_endl; - sync_cout << "info string ERROR: If the UCI option Use NNUE is set to true, network evaluation parameters compatible with the program must be available." << sync_endl; - sync_cout << "info string ERROR: The engine will be terminated now." << sync_endl; - std::exit(EXIT_FAILURE); + string msg1 = "If the UCI option \"Use NNUE\" is set to true, network evaluation parameters compatible with the engine must be available."; + string msg2 = "The option is set to true, but the network file " + eval_file + " was not loaded successfully."; + string msg3 = "The UCI option EvalFile might need to specify the full path, including the directory name, to the network file."; + string msg4 = "The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/" + string(defaults["EvalFile"]); + string msg5 = "The engine will be terminated now."; + + sync_cout << "info string ERROR: " << msg1 << sync_endl; + sync_cout << "info string ERROR: " << msg2 << sync_endl; + sync_cout << "info string ERROR: " << msg3 << sync_endl; + sync_cout << "info string ERROR: " << msg4 << sync_endl; + sync_cout << "info string ERROR: " << msg5 << sync_endl; + + exit(EXIT_FAILURE); } if (useNNUE) - sync_cout << "info string NNUE evaluation using " << eval_file << " enabled." << sync_endl; + sync_cout << "info string NNUE evaluation using " << eval_file << " enabled" << sync_endl; else - sync_cout << "info string classical evaluation enabled." << sync_endl; + sync_cout << "info string classical evaluation enabled" << sync_endl; } } diff --git a/src/evaluate.h b/src/evaluate.h index e808068da2f..d701f5a7fde 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -35,12 +35,17 @@ namespace Eval { void init_NNUE(); void verify_NNUE(); + // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue + // for the build process (profile-build and fishtest) to work. Do not change the + // name of the macro, as it is used in the Makefile. + #define EvalFileDefaultName "nn-82215d0fd0df.nnue" + namespace NNUE { Value evaluate(const Position& pos); Value compute_eval(const Position& pos); void update_eval(const Position& pos); - bool load_eval_file(const std::string& evalFile); + bool load_eval(std::string streamName, std::istream& stream); } // namespace NNUE diff --git a/src/incbin/UNLICENCE b/src/incbin/UNLICENCE new file mode 100644 index 00000000000..32484ab5e70 --- /dev/null +++ b/src/incbin/UNLICENCE @@ -0,0 +1,26 @@ +The file "incbin.h" is free and unencumbered software released into +the public domain by Dale Weiler, see: + + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/src/incbin/incbin.h b/src/incbin/incbin.h new file mode 100755 index 00000000000..c19684d7242 --- /dev/null +++ b/src/incbin/incbin.h @@ -0,0 +1,368 @@ +/** + * @file incbin.h + * @author Dale Weiler + * @brief Utility for including binary files + * + * Facilities for including binary files into the current translation unit and + * making use from them externally in other translation units. + */ +#ifndef INCBIN_HDR +#define INCBIN_HDR +#include +#if defined(__AVX512BW__) || \ + defined(__AVX512CD__) || \ + defined(__AVX512DQ__) || \ + defined(__AVX512ER__) || \ + defined(__AVX512PF__) || \ + defined(__AVX512VL__) || \ + defined(__AVX512F__) +# define INCBIN_ALIGNMENT_INDEX 6 +#elif defined(__AVX__) || \ + defined(__AVX2__) +# define INCBIN_ALIGNMENT_INDEX 5 +#elif defined(__SSE__) || \ + defined(__SSE2__) || \ + defined(__SSE3__) || \ + defined(__SSSE3__) || \ + defined(__SSE4_1__) || \ + defined(__SSE4_2__) || \ + defined(__neon__) +# define INCBIN_ALIGNMENT_INDEX 4 +#elif ULONG_MAX != 0xffffffffu +# define INCBIN_ALIGNMENT_INDEX 3 +# else +# define INCBIN_ALIGNMENT_INDEX 2 +#endif + +/* Lookup table of (1 << n) where `n' is `INCBIN_ALIGNMENT_INDEX' */ +#define INCBIN_ALIGN_SHIFT_0 1 +#define INCBIN_ALIGN_SHIFT_1 2 +#define INCBIN_ALIGN_SHIFT_2 4 +#define INCBIN_ALIGN_SHIFT_3 8 +#define INCBIN_ALIGN_SHIFT_4 16 +#define INCBIN_ALIGN_SHIFT_5 32 +#define INCBIN_ALIGN_SHIFT_6 64 + +/* Actual alignment value */ +#define INCBIN_ALIGNMENT \ + INCBIN_CONCATENATE( \ + INCBIN_CONCATENATE(INCBIN_ALIGN_SHIFT, _), \ + INCBIN_ALIGNMENT_INDEX) + +/* Stringize */ +#define INCBIN_STR(X) \ + #X +#define INCBIN_STRINGIZE(X) \ + INCBIN_STR(X) +/* Concatenate */ +#define INCBIN_CAT(X, Y) \ + X ## Y +#define INCBIN_CONCATENATE(X, Y) \ + INCBIN_CAT(X, Y) +/* Deferred macro expansion */ +#define INCBIN_EVAL(X) \ + X +#define INCBIN_INVOKE(N, ...) \ + INCBIN_EVAL(N(__VA_ARGS__)) + +/* Green Hills uses a different directive for including binary data */ +#if defined(__ghs__) +# if (__ghs_asm == 2) +# define INCBIN_MACRO ".file" +/* Or consider the ".myrawdata" entry in the ld file */ +# else +# define INCBIN_MACRO "\tINCBIN" +# endif +#else +# define INCBIN_MACRO ".incbin" +#endif + +#ifndef _MSC_VER +# define INCBIN_ALIGN \ + __attribute__((aligned(INCBIN_ALIGNMENT))) +#else +# define INCBIN_ALIGN __declspec(align(INCBIN_ALIGNMENT)) +#endif + +#if defined(__arm__) || /* GNU C and RealView */ \ + defined(__arm) || /* Diab */ \ + defined(_ARM) /* ImageCraft */ +# define INCBIN_ARM +#endif + +#ifdef __GNUC__ +/* Utilize .balign where supported */ +# define INCBIN_ALIGN_HOST ".balign " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n" +# define INCBIN_ALIGN_BYTE ".balign 1\n" +#elif defined(INCBIN_ARM) +/* + * On arm assemblers, the alignment value is calculated as (1 << n) where `n' is + * the shift count. This is the value passed to `.align' + */ +# define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT_INDEX) "\n" +# define INCBIN_ALIGN_BYTE ".align 0\n" +#else +/* We assume other inline assembler's treat `.align' as `.balign' */ +# define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n" +# define INCBIN_ALIGN_BYTE ".align 1\n" +#endif + +/* INCBIN_CONST is used by incbin.c generated files */ +#if defined(__cplusplus) +# define INCBIN_EXTERNAL extern "C" +# define INCBIN_CONST extern const +#else +# define INCBIN_EXTERNAL extern +# define INCBIN_CONST const +#endif + +/** + * @brief Optionally override the linker section into which data is emitted. + * + * @warning If you use this facility, you'll have to deal with platform-specific linker output + * section naming on your own + * + * Overriding the default linker output section, e.g for esp8266/Arduino: + * @code + * #define INCBIN_OUTPUT_SECTION ".irom.text" + * #include "incbin.h" + * INCBIN(Foo, "foo.txt"); + * // Data is emitted into program memory that never gets copied to RAM + * @endcode + */ +#if !defined(INCBIN_OUTPUT_SECTION) +# if defined(__APPLE__) +# define INCBIN_OUTPUT_SECTION ".const_data" +# else +# define INCBIN_OUTPUT_SECTION ".rodata" +# endif +#endif + +#if defined(__APPLE__) +/* The directives are different for Apple branded compilers */ +# define INCBIN_SECTION INCBIN_OUTPUT_SECTION "\n" +# define INCBIN_GLOBAL(NAME) ".globl " INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n" +# define INCBIN_INT ".long " +# define INCBIN_MANGLE "_" +# define INCBIN_BYTE ".byte " +# define INCBIN_TYPE(...) +#else +# define INCBIN_SECTION ".section " INCBIN_OUTPUT_SECTION "\n" +# define INCBIN_GLOBAL(NAME) ".global " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n" +# if defined(__ghs__) +# define INCBIN_INT ".word " +# else +# define INCBIN_INT ".int " +# endif +# if defined(__USER_LABEL_PREFIX__) +# define INCBIN_MANGLE INCBIN_STRINGIZE(__USER_LABEL_PREFIX__) +# else +# define INCBIN_MANGLE "" +# endif +# if defined(INCBIN_ARM) +/* On arm assemblers, `@' is used as a line comment token */ +# define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", %object\n" +# elif defined(__MINGW32__) || defined(__MINGW64__) +/* Mingw doesn't support this directive either */ +# define INCBIN_TYPE(NAME) +# else +/* It's safe to use `@' on other architectures */ +# define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", @object\n" +# endif +# define INCBIN_BYTE ".byte " +#endif + +/* List of style types used for symbol names */ +#define INCBIN_STYLE_CAMEL 0 +#define INCBIN_STYLE_SNAKE 1 + +/** + * @brief Specify the prefix to use for symbol names. + * + * By default this is `g', producing symbols of the form: + * @code + * #include "incbin.h" + * INCBIN(Foo, "foo.txt"); + * + * // Now you have the following symbols: + * // const unsigned char gFooData[]; + * // const unsigned char *const gFooEnd; + * // const unsigned int gFooSize; + * @endcode + * + * If however you specify a prefix before including: e.g: + * @code + * #define INCBIN_PREFIX incbin + * #include "incbin.h" + * INCBIN(Foo, "foo.txt"); + * + * // Now you have the following symbols instead: + * // const unsigned char incbinFooData[]; + * // const unsigned char *const incbinFooEnd; + * // const unsigned int incbinFooSize; + * @endcode + */ +#if !defined(INCBIN_PREFIX) +# define INCBIN_PREFIX g +#endif + +/** + * @brief Specify the style used for symbol names. + * + * Possible options are + * - INCBIN_STYLE_CAMEL "CamelCase" + * - INCBIN_STYLE_SNAKE "snake_case" + * + * Default option is *INCBIN_STYLE_CAMEL* producing symbols of the form: + * @code + * #include "incbin.h" + * INCBIN(Foo, "foo.txt"); + * + * // Now you have the following symbols: + * // const unsigned char FooData[]; + * // const unsigned char *const FooEnd; + * // const unsigned int FooSize; + * @endcode + * + * If however you specify a style before including: e.g: + * @code + * #define INCBIN_STYLE INCBIN_STYLE_SNAKE + * #include "incbin.h" + * INCBIN(foo, "foo.txt"); + * + * // Now you have the following symbols: + * // const unsigned char foo_data[]; + * // const unsigned char *const foo_end; + * // const unsigned int foo_size; + * @endcode + */ +#if !defined(INCBIN_STYLE) +# define INCBIN_STYLE INCBIN_STYLE_CAMEL +#endif + +/* Style lookup tables */ +#define INCBIN_STYLE_0_DATA Data +#define INCBIN_STYLE_0_END End +#define INCBIN_STYLE_0_SIZE Size +#define INCBIN_STYLE_1_DATA _data +#define INCBIN_STYLE_1_END _end +#define INCBIN_STYLE_1_SIZE _size + +/* Style lookup: returning identifier */ +#define INCBIN_STYLE_IDENT(TYPE) \ + INCBIN_CONCATENATE( \ + INCBIN_STYLE_, \ + INCBIN_CONCATENATE( \ + INCBIN_EVAL(INCBIN_STYLE), \ + INCBIN_CONCATENATE(_, TYPE))) + +/* Style lookup: returning string literal */ +#define INCBIN_STYLE_STRING(TYPE) \ + INCBIN_STRINGIZE( \ + INCBIN_STYLE_IDENT(TYPE)) \ + +/* Generate the global labels by indirectly invoking the macro with our style + * type and concatenating the name against them. */ +#define INCBIN_GLOBAL_LABELS(NAME, TYPE) \ + INCBIN_INVOKE( \ + INCBIN_GLOBAL, \ + INCBIN_CONCATENATE( \ + NAME, \ + INCBIN_INVOKE( \ + INCBIN_STYLE_IDENT, \ + TYPE))) \ + INCBIN_INVOKE( \ + INCBIN_TYPE, \ + INCBIN_CONCATENATE( \ + NAME, \ + INCBIN_INVOKE( \ + INCBIN_STYLE_IDENT, \ + TYPE))) + +/** + * @brief Externally reference binary data included in another translation unit. + * + * Produces three external symbols that reference the binary data included in + * another translation unit. + * + * The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with + * "Data", as well as "End" and "Size" after. An example is provided below. + * + * @param NAME The name given for the binary data + * + * @code + * INCBIN_EXTERN(Foo); + * + * // Now you have the following symbols: + * // extern const unsigned char FooData[]; + * // extern const unsigned char *const FooEnd; + * // extern const unsigned int FooSize; + * @endcode + */ +#define INCBIN_EXTERN(NAME) \ + INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char \ + INCBIN_CONCATENATE( \ + INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \ + INCBIN_STYLE_IDENT(DATA))[]; \ + INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char *const \ + INCBIN_CONCATENATE( \ + INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \ + INCBIN_STYLE_IDENT(END)); \ + INCBIN_EXTERNAL const unsigned int \ + INCBIN_CONCATENATE( \ + INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \ + INCBIN_STYLE_IDENT(SIZE)) + +/** + * @brief Include a binary file into the current translation unit. + * + * Includes a binary file into the current translation unit, producing three symbols + * for objects that encode the data and size respectively. + * + * The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with + * "Data", as well as "End" and "Size" after. An example is provided below. + * + * @param NAME The name to associate with this binary data (as an identifier.) + * @param FILENAME The file to include (as a string literal.) + * + * @code + * INCBIN(Icon, "icon.png"); + * + * // Now you have the following symbols: + * // const unsigned char IconData[]; + * // const unsigned char *const IconEnd; + * // const unsigned int IconSize; + * @endcode + * + * @warning This must be used in global scope + * @warning The identifiers may be different if INCBIN_STYLE is not default + * + * To externally reference the data included by this in another translation unit + * please @see INCBIN_EXTERN. + */ +#ifdef _MSC_VER +#define INCBIN(NAME, FILENAME) \ + INCBIN_EXTERN(NAME) +#else +#define INCBIN(NAME, FILENAME) \ + __asm__(INCBIN_SECTION \ + INCBIN_GLOBAL_LABELS(NAME, DATA) \ + INCBIN_ALIGN_HOST \ + INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA) ":\n" \ + INCBIN_MACRO " \"" FILENAME "\"\n" \ + INCBIN_GLOBAL_LABELS(NAME, END) \ + INCBIN_ALIGN_BYTE \ + INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) ":\n" \ + INCBIN_BYTE "1\n" \ + INCBIN_GLOBAL_LABELS(NAME, SIZE) \ + INCBIN_ALIGN_HOST \ + INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(SIZE) ":\n" \ + INCBIN_INT INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) " - " \ + INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA) "\n" \ + INCBIN_ALIGN_HOST \ + ".text\n" \ + ); \ + INCBIN_EXTERN(NAME) + +#endif +#endif diff --git a/src/main.cpp b/src/main.cpp index fbad6622974..f95db1c2f09 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -35,6 +35,7 @@ int main(int argc, char* argv[]) { std::cout << engine_info() << std::endl; + CommandLine::init(argc, argv); UCI::init(Options); Tune::init(); PSQT::init(); diff --git a/src/misc.cpp b/src/misc.cpp index 80c436ac0f2..3fbdea35d94 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -132,6 +132,7 @@ class Logger { } // namespace + /// engine_info() returns the full name of the current Stockfish version. This /// will be either "Stockfish DD-MM-YY" (where DD-MM-YY is the date when /// the program was compiled) or "Stockfish ", depending on whether @@ -589,3 +590,61 @@ void bindThisThread(size_t idx) { #endif } // namespace WinProcGroup + +#ifdef _WIN32 +#include +#define GETCWD _getcwd +#else +#include +#define GETCWD getcwd +#endif + +namespace CommandLine { + +string argv0; // path+name of the executable binary, as given by argv[0] +string binaryDirectory; // path of the executable directory +string workingDirectory; // path of the working directory +string pathSeparator; // Separator for our current OS + +void init(int argc, char* argv[]) { + (void)argc; + string separator; + + // extract the path+name of the executable binary + argv0 = argv[0]; + +#ifdef _WIN32 + pathSeparator = "\\"; + #ifdef _MSC_VER + // Under windows argv[0] may not have the extension. Also _get_pgmptr() had + // issues in some windows 10 versions, so check returned values carefully. + char* pgmptr = nullptr; + if (!_get_pgmptr(&pgmptr) && pgmptr != nullptr && *pgmptr) + argv0 = pgmptr; + #endif +#else + pathSeparator = "/"; +#endif + + // extract the working directory + workingDirectory = ""; + char buff[40000]; + char* cwd = GETCWD(buff, 40000); + if (cwd) + workingDirectory = cwd; + + // extract the binary directory path from argv0 + binaryDirectory = argv0; + size_t pos = binaryDirectory.find_last_of("\\/"); + if (pos == std::string::npos) + binaryDirectory = "." + pathSeparator; + else + binaryDirectory.resize(pos + 1); + + // pattern replacement: "./" at the start of path is replaced by the working directory + if (binaryDirectory.find("." + pathSeparator) == 0) + binaryDirectory.replace(0, 1, workingDirectory); +} + + +} // namespace CommandLine diff --git a/src/misc.h b/src/misc.h index 8ad17b502f4..68b9c8842b9 100644 --- a/src/misc.h +++ b/src/misc.h @@ -42,9 +42,7 @@ void dbg_mean_of(int v); void dbg_print(); typedef std::chrono::milliseconds::rep TimePoint; // A value in milliseconds - static_assert(sizeof(TimePoint) == sizeof(int64_t), "TimePoint should be 64 bits"); - inline TimePoint now() { return std::chrono::duration_cast (std::chrono::steady_clock::now().time_since_epoch()).count(); @@ -126,4 +124,11 @@ namespace WinProcGroup { void bindThisThread(size_t idx); } +namespace CommandLine { + void init(int argc, char* argv[]); + + extern std::string binaryDirectory; // path of the executable directory + extern std::string workingDirectory; // path of the working directory +} + #endif // #ifndef MISC_H_INCLUDED diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index e66190892ad..d6ac9894cbb 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -18,7 +18,6 @@ // Code for calculating NNUE evaluation function -#include #include #include @@ -143,17 +142,12 @@ namespace Eval::NNUE { return accumulator.score; } - // Load the evaluation function file - bool load_eval_file(const std::string& evalFile) { + // Load eval, from a file stream or a memory stream + bool load_eval(std::string streamName, std::istream& stream) { Initialize(); - fileName = evalFile; - - std::ifstream stream(evalFile, std::ios::binary); - - const bool result = ReadParameters(stream); - - return result; + fileName = streamName; + return ReadParameters(stream); } // Evaluation function. Perform differential calculation. diff --git a/src/ucioption.cpp b/src/ucioption.cpp index ec83c7c88dc..5e747a7f13f 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -21,6 +21,7 @@ #include #include +#include "evaluate.h" #include "misc.h" #include "search.h" #include "thread.h" @@ -79,9 +80,7 @@ void init(OptionsMap& o) { o["Syzygy50MoveRule"] << Option(true); o["SyzygyProbeLimit"] << Option(7, 0, 7); o["Use NNUE"] << Option(true, on_use_NNUE); - // The default must follow the format nn-[SHA256 first 12 digits].nnue - // for the build process (profile-build and fishtest) to work. - o["EvalFile"] << Option("nn-82215d0fd0df.nnue", on_eval_file); + o["EvalFile"] << Option(EvalFileDefaultName, on_eval_file); } From e4ed7d3dd7b8895ce523180cb3da3ec2714050fc Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 26 Aug 2020 18:00:54 +0200 Subject: [PATCH 0373/1766] Cleaner make help do not print details if ARCH is an empty string. Follow up for b0b4ca17db49ed03057b5fa4ee4a12dab0e9c9e6 https://github.com/official-stockfish/Stockfish/pull/3071 No functional change --- src/Makefile | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/Makefile b/src/Makefile index 5f363f02412..9ae5a51c5df 100644 --- a/src/Makefile +++ b/src/Makefile @@ -85,12 +85,17 @@ endif ### 2.1. General and architecture defaults +ifeq ($(ARCH),) + ARCH = x86-64-modern + help_skip_sanity = yes +endif # explicitly check for the list of supported architectures (as listed with make help), # the user can override with `make ARCH=x86-32-vnni256 SUPPORTED_ARCH=true` -ifeq ($(ARCH),$(filter $(ARCH),x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-bmi2 x86-64-avx2 \ - x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \ - x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-32 \ - armv7 armv7-neon armv8 apple-silicon general-64 general-32)) +ifeq ($(ARCH), $(filter $(ARCH), \ + x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-bmi2 x86-64-avx2 \ + x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \ + x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-32 \ + armv7 armv7-neon armv8 apple-silicon general-64 general-32)) SUPPORTED_ARCH=true else SUPPORTED_ARCH=false @@ -113,7 +118,6 @@ avx512 = no vnni256 = no vnni512 = no neon = no -ARCH = x86-64-modern STRIP = strip ### 2.2 Architecture specific @@ -695,11 +699,12 @@ help: @echo "make -j build ARCH=x86-64-ssse3 COMP=clang" @echo "" @echo "-------------------------------" -ifeq ($(SUPPORTED_ARCH), true) +ifeq ($(SUPPORTED_ARCH)$(help_skip_sanity), true) @echo "The selected architecture $(ARCH) will enable the following configuration: " @$(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity else @echo "Specify a supported architecture with the ARCH option for more details" + @echo "" endif From d90d893b5eeea020b2d59d1372f5aa0a20b45412 Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Fri, 28 Aug 2020 09:27:15 +0200 Subject: [PATCH 0374/1766] Reintroduce depth reduction Reintroduce depth reduction if the position is not in TT. STC https://tests.stockfishchess.org/tests/view/5f4652e85089a564a10d868c LLR: 2.97 (-2.94,2.94) {-0.25,1.25} Total: 40240 W: 4535 L: 4331 D: 31374 Ptnml(0-2): 215, 3276, 12969, 3410, 250 LTC https://tests.stockfishchess.org/tests/view/5f46ca5e5089a564a10d86f3 LLR: 2.93 (-2.94,2.94) {0.25,1.25} Total: 63096 W: 3426 L: 3188 D: 56482 Ptnml(0-2): 51, 2798, 25645, 2970, 84 closes https://github.com/official-stockfish/Stockfish/pull/3072 bench: 3611906 --- src/search.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index cae8a684562..77447043761 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -939,6 +939,12 @@ namespace { } } } + + // Step 11. If the position is not in TT, decrease depth by 2 + if ( PvNode + && depth >= 6 + && !ttMove) + depth -= 2; moves_loop: // When in check, search starts from here @@ -963,7 +969,7 @@ namespace { // Mark this node as being searched ThreadHolding th(thisThread, posKey, ss->ply); - // Step 11. Loop through all pseudo-legal moves until no moves remain + // Step 12. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. while ((move = mp.next_move(moveCountPruning)) != MOVE_NONE) { @@ -1001,7 +1007,7 @@ namespace { // Calculate new depth for this move newDepth = depth - 1; - // Step 12. Pruning at shallow depth (~200 Elo) + // Step 13. Pruning at shallow depth (~200 Elo) if ( !rootNode && pos.non_pawn_material(us) && bestValue > VALUE_TB_LOSS_IN_MAX_PLY) @@ -1059,7 +1065,7 @@ namespace { } } - // Step 13. Extensions (~75 Elo) + // Step 14. Extensions (~75 Elo) // Singular extension search (~70 Elo). If all moves but one fail low on a // search of (alpha-s, beta-s), and just one fails high on (alpha, beta), @@ -1142,10 +1148,10 @@ namespace { [movedPiece] [to_sq(move)]; - // Step 14. Make the move + // Step 15. Make the move pos.do_move(move, st, givesCheck); - // Step 15. Reduced depth search (LMR, ~200 Elo). If the move fails high it will be + // Step 16. Reduced depth search (LMR, ~200 Elo). If the move fails high it will be // re-searched at full depth. if ( depth >= 3 && moveCount > 1 + 2 * rootNode + 2 * (PvNode && abs(bestValue) < 2) @@ -1248,7 +1254,7 @@ namespace { didLMR = false; } - // Step 16. Full depth search when LMR is skipped or fails high + // Step 17. Full depth search when LMR is skipped or fails high if (doFullDepthSearch) { value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode); @@ -1276,12 +1282,12 @@ namespace { value = -search(pos, ss+1, -beta, -alpha, newDepth, false); } - // Step 17. Undo move + // Step 18. Undo move pos.undo_move(move); assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); - // Step 18. Check for a new best move + // Step 19. Check for a new best move // Finished searching the move. If a stop occurred, the return value of // the search cannot be trusted, and we return immediately without // updating best move, PV and TT. @@ -1358,7 +1364,7 @@ namespace { return VALUE_DRAW; */ - // Step 19. Check for mate and stalemate + // Step 20. Check for mate and stalemate // All legal moves have been searched and if there are no legal moves, it // must be a mate or a stalemate. If we are in a singular extension search then // return a fail low score. From c02b3a4c7a339d212d5c6f75b3b89c926d33a800 Mon Sep 17 00:00:00 2001 From: MJZ1977 <37274752+MJZ1977@users.noreply.github.com> Date: Fri, 28 Aug 2020 12:06:36 +0200 Subject: [PATCH 0375/1766] Add / remove leaves from search tree ttPv add if previous leaf is in search tree and we didn't find a counter move else remove the position if the leaf is the last one in search tree. STC : https://tests.stockfishchess.org/tests/view/5f49203c3def640786115314 LLR: 2.95 (-2.94,2.94) {-0.25,1.25} Total: 29968 W: 3381 L: 3195 D: 23392 Ptnml(0-2): 146, 2432, 9671, 2560, 175 LTC : https://tests.stockfishchess.org/tests/view/5f494bea3def640786115336 LLR: 2.96 (-2.94,2.94) {0.25,1.25} Total: 84952 W: 4619 L: 4333 D: 76000 Ptnml(0-2): 86, 3765, 34481, 4065, 79 closes https://github.com/official-stockfish/Stockfish/pull/3075 Bench 3527337 --- src/search.cpp | 34 ++++++++++++++++++++++++---------- src/search.h | 1 + 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 77447043761..a2342a3cf4f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -597,7 +597,7 @@ namespace { Move ttMove, move, excludedMove, bestMove; Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue, probCutBeta; - bool ttHit, ttPv, formerPv, givesCheck, improving, didLMR, priorCapture; + bool ttHit, formerPv, givesCheck, improving, didLMR, priorCapture; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture, singularQuietLMR; Piece movedPiece; @@ -644,6 +644,7 @@ namespace { assert(0 <= ss->ply && ss->ply < MAX_PLY); (ss+1)->ply = ss->ply + 1; + (ss+1)->ttPv = false; (ss+1)->excludedMove = bestMove = MOVE_NONE; (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; Square prevSq = to_sq((ss-1)->currentMove); @@ -667,10 +668,11 @@ namespace { ttValue = ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] : ttHit ? tte->move() : MOVE_NONE; - ttPv = PvNode || (ttHit && tte->is_pv()); - formerPv = ttPv && !PvNode; + if (!excludedMove) + ss->ttPv = PvNode || (ttHit && tte->is_pv()); + formerPv = ss->ttPv && !PvNode; - if ( ttPv + if ( ss->ttPv && depth > 12 && ss->ply - 1 < MAX_LPH && !priorCapture @@ -748,7 +750,7 @@ namespace { if ( b == BOUND_EXACT || (b == BOUND_LOWER ? value >= beta : value <= alpha)) { - tte->save(posKey, value_to_tt(value, ss->ply), ttPv, b, + tte->save(posKey, value_to_tt(value, ss->ply), ss->ttPv, b, std::min(MAX_PLY - 1, depth + 6), MOVE_NONE, VALUE_NONE); @@ -798,7 +800,7 @@ namespace { else ss->staticEval = eval = -(ss-1)->staticEval + 2 * Tempo; - tte->save(posKey, VALUE_NONE, ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); + tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); } // Step 7. Razoring (~1 Elo) @@ -824,7 +826,7 @@ namespace { && (ss-1)->statScore < 22977 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 30 * depth - 28 * improving + 84 * ttPv + 182 + && ss->staticEval >= beta - 30 * depth - 28 * improving + 84 * ss->ttPv + 182 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -898,6 +900,8 @@ namespace { assert(probCutBeta < VALUE_INFINITE); MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, &captureHistory); int probCutCount = 0; + bool ttPv = ss->ttPv; + ss->ttPv = false; while ( (move = mp.next_move()) != MOVE_NONE && probCutCount < 2 + 2 * cutNode) @@ -938,6 +942,7 @@ namespace { return value; } } + ss->ttPv = ttPv; } // Step 11. If the position is not in TT, decrease depth by 2 @@ -1180,7 +1185,7 @@ namespace { r++; // Decrease reduction if position is or has been on the PV (~10 Elo) - if (ttPv) + if (ss->ttPv) r -= 2; if (moveCountPruning && !formerPv) @@ -1209,7 +1214,7 @@ namespace { // hence break make_move(). (~2 Elo) else if ( type_of(move) == NORMAL && !pos.see_ge(reverse_move(move))) - r -= 2 + ttPv - (type_of(movedPiece) == PAWN); + r -= 2 + ss->ttPv - (type_of(movedPiece) == PAWN); ss->statScore = thisThread->mainHistory[us][from_to(move)] + (*contHist[0])[movedPiece][to_sq(move)] @@ -1387,8 +1392,17 @@ namespace { if (PvNode) bestValue = std::min(bestValue, maxValue); + // If no good move is found and the previous position was ttPv, then the previous + // opponent move is probably good and the new position is added to the search tree. + if (bestValue <= alpha) + ss->ttPv = ss->ttPv || ((ss-1)->ttPv && depth > 3); + // Otherwise, a counter move has been found and if the position is the last leaf + // in the search tree, remove the position from the search tree. + else if (depth > 3) + ss->ttPv = ss->ttPv && (ss+1)->ttPv; + if (!excludedMove && !(rootNode && thisThread->pvIdx)) - tte->save(posKey, value_to_tt(bestValue, ss->ply), ttPv, + tte->save(posKey, value_to_tt(bestValue, ss->ply), ss->ttPv, bestValue >= beta ? BOUND_LOWER : PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER, depth, bestMove, ss->staticEval); diff --git a/src/search.h b/src/search.h index 2554f3fbd8a..79085189592 100644 --- a/src/search.h +++ b/src/search.h @@ -48,6 +48,7 @@ struct Stack { int statScore; int moveCount; bool inCheck; + bool ttPv; }; From 9b5b9ec9a6a3a0e46ac00f58976887560948a7e2 Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Sat, 29 Aug 2020 21:13:05 -0400 Subject: [PATCH 0376/1766] QS Pruning Simplification Remove depth dependence in QS pruning STC: LLR: 2.95 (-2.94,2.94) {-1.25,0.25} Total: 40536 W: 4442 L: 4358 D: 31736 Ptnml(0-2): 209, 3330, 13118, 3390, 221 https://tests.stockfishchess.org/tests/view/5f49035b3def6407861152f9 LTC: LLR: 2.95 (-2.94,2.94) {-0.75,0.25} Total: 97104 W: 5164 L: 5130 D: 86810 Ptnml(0-2): 103, 4478, 39377, 4470, 124 https://tests.stockfishchess.org/tests/view/5f4939d53def640786115322 closes https://github.com/official-stockfish/Stockfish/pull/3077 Bench: 3865238 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index a2342a3cf4f..b319dff5fe2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1585,7 +1585,7 @@ namespace { [to_sq(move)]; if ( !captureOrPromotion - && moveCount >= abs(depth) + 1 + && moveCount && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < CounterMovePruneThreshold && (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < CounterMovePruneThreshold) continue; From e0bafa1911ede61b9268e0b461a5d8856d6cd6be Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sun, 30 Aug 2020 13:58:05 +0300 Subject: [PATCH 0377/1766] Update parametes in classical evaluation. Passed STC (NNUE=False): https://tests.stockfishchess.org/tests/view/5f42edfe5089a564a10d84a0 LLR: 2.96 (-2.94,2.94) {-0.25,1.25} Total: 13840 W: 2591 L: 2336 D: 8913 Ptnml(0-2): 194, 1453, 3387, 1676, 210 Passed LTC (NNUE=False): https://tests.stockfishchess.org/tests/view/5f4369795089a564a10d84d8 LLR: 2.95 (-2.94,2.94) {0.25,1.25} Total: 159744 W: 19430 L: 18850 D: 121464 Ptnml(0-2): 960, 14185, 49030, 14709, 988 closes https://github.com/official-stockfish/Stockfish/pull/3080 bench: 3736029 --- src/evaluate.cpp | 10 +++++----- src/search.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 67154751f2a..09f36513047 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -227,26 +227,26 @@ namespace { // Outpost[knight/bishop] contains bonuses for each knight or bishop occupying a // pawn protected square on rank 4 to 6 which is also safe from a pawn attack. - constexpr Score Outpost[] = { S(56, 36), S(30, 23) }; + constexpr Score Outpost[] = { S(56, 34), S(31, 23) }; // PassedRank[Rank] contains a bonus according to the rank of a passed pawn constexpr Score PassedRank[RANK_NB] = { - S(0, 0), S(10, 28), S(17, 33), S(15, 41), S(62, 72), S(168, 177), S(276, 260) + S(0, 0), S(9, 28), S(15, 31), S(17, 39), S(64, 70), S(171, 177), S(277, 260) }; // RookOnFile[semiopen/open] contains bonuses for each rook when there is // no (friendly) pawn on the rook file. - constexpr Score RookOnFile[] = { S(19, 7), S(48, 29) }; + constexpr Score RookOnFile[] = { S(19, 7), S(48, 27) }; // 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(5, 32), S(57, 41), S(77, 56), S(88, 119), S(79, 161) + S(0, 0), S(5, 32), S(55, 41), S(77, 56), S(89, 119), S(79, 162) }; constexpr Score ThreatByRook[PIECE_TYPE_NB] = { - S(0, 0), S(3, 46), S(37, 68), S(42, 60), S(0, 38), S(58, 41) + S(0, 0), S(3, 44), S(37, 68), S(42, 60), S(0, 39), S(58, 43) }; // Assorted bonuses and penalties diff --git a/src/search.cpp b/src/search.cpp index b319dff5fe2..e6e53e7c59d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -944,7 +944,7 @@ namespace { } ss->ttPv = ttPv; } - + // Step 11. If the position is not in TT, decrease depth by 2 if ( PvNode && depth >= 6 From a0afe32d16554ff3b5c74f34ae56400f35759edf Mon Sep 17 00:00:00 2001 From: mstembera Date: Sun, 30 Aug 2020 18:40:49 -0700 Subject: [PATCH 0378/1766] Use stable sort to make sure bench with TB yields same results everywhere. std::sort() is not stable so different implementations can produce different results: use the stable version instead. Observed for '8/6k1/5r2/8/8/8/1K6/Q7 w - - 0 1' yielding different bench results for gcc and MSVC and 3-4-5 syzygy TB prior to this patch. closes https://github.com/official-stockfish/Stockfish/pull/3083 No functional change. --- src/search.cpp | 2 +- src/syzygy/tbprobe.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index e6e53e7c59d..c676bd6d62c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1964,7 +1964,7 @@ void Tablebases::rank_root_moves(Position& pos, Search::RootMoves& rootMoves) { if (RootInTB) { // Sort moves according to TB rank - std::sort(rootMoves.begin(), rootMoves.end(), + std::stable_sort(rootMoves.begin(), rootMoves.end(), [](const RootMove &a, const RootMove &b) { return a.tbRank > b.tbRank; } ); // Probe during search only if DTZ is not available and we are winning diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 20215b96d1f..3dfe3e3e481 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -758,7 +758,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu if (entry->hasPawns) { idx = LeadPawnIdx[leadPawnsCnt][squares[0]]; - std::sort(squares + 1, squares + leadPawnsCnt, pawns_comp); + std::stable_sort(squares + 1, squares + leadPawnsCnt, pawns_comp); for (int i = 1; i < leadPawnsCnt; ++i) idx += Binomial[i][MapPawns[squares[i]]]; @@ -859,7 +859,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu while (d->groupLen[++next]) { - std::sort(groupSq, groupSq + d->groupLen[next]); + std::stable_sort(groupSq, groupSq + d->groupLen[next]); uint64_t n = 0; // Map down a square if "comes later" than a square in the previous From a057f170c6920fc4d1abdae619c5259e9d80703c Mon Sep 17 00:00:00 2001 From: mstembera Date: Sun, 30 Aug 2020 20:48:10 -0700 Subject: [PATCH 0379/1766] Use llvm linker with clang on windows for LTO. other linkers might fail to link during the LTO phase. The linker might have to be installed using `pacman -Syu mingw-w64-x86_64-lld` closes https://github.com/official-stockfish/Stockfish/pull/3084 No functional change. --- src/Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Makefile b/src/Makefile index 9ae5a51c5df..340b3008381 100644 --- a/src/Makefile +++ b/src/Makefile @@ -595,6 +595,11 @@ ifeq ($(debug), no) LDFLAGS += $(CXXFLAGS) else ifeq ($(comp),clang) CXXFLAGS += -flto=thin + ifneq ($(findstring MINGW,$(KERNEL)),) + CXXFLAGS += -fuse-ld=lld + else ifneq ($(findstring MSYS,$(KERNEL)),) + CXXFLAGS += -fuse-ld=lld + endif LDFLAGS += $(CXXFLAGS) # GCC and CLANG use different methods for parallelizing LTO and CLANG pretends to be From 61381372ec896ae6b0f139555e6e3f816d8aa570 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 31 Aug 2020 22:53:20 +0200 Subject: [PATCH 0380/1766] Always print an info line before a bestmove if very few nodes are being searched before a bestmove is reported, an info line might be missing. fixes https://github.com/official-stockfish/Stockfish/issues/2757 closes https://github.com/official-stockfish/Stockfish/pull/3088 no functional change --- src/search.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c676bd6d62c..c15cd7537e6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1861,12 +1861,15 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) { { bool updated = rootMoves[i].score != -VALUE_INFINITE; - if (depth == 1 && !updated) + if (depth == 1 && !updated && i > 0) continue; - Depth d = updated ? depth : depth - 1; + Depth d = updated ? depth : std::max(1, depth - 1); Value v = updated ? rootMoves[i].score : rootMoves[i].previousScore; + if (v == -VALUE_INFINITE) + v = VALUE_ZERO; + bool tb = TB::RootInTB && abs(v) < VALUE_MATE_IN_MAX_PLY; v = tb ? rootMoves[i].tbScore : v; From a8bbaa17954471cf3fd8d168f1cafe3f2034730e Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Sun, 30 Aug 2020 13:57:57 -0400 Subject: [PATCH 0381/1766] LMR Root Node Simplification Simplify LMR at Root node STC: LLR: 2.94 (-2.94,2.94) {-1.25,0.25} Total: 71520 W: 7649 L: 7614 D: 56257 Ptnml(0-2): 346, 5845, 23349, 5868, 352 https://tests.stockfishchess.org/tests/view/5f4be8c0ba100690c5cc5cbb LTC: LLR: 2.95 (-2.94,2.94) {-0.75,0.25} Total: 74832 W: 3997 L: 3948 D: 66887 Ptnml(0-2): 77, 3422, 30362, 3485, 70 https://tests.stockfishchess.org/tests/view/5f4c603eba100690c5cc5d0e closes https://github.com/official-stockfish/Stockfish/pull/3091 Bench: 3624569 --- src/search.cpp | 1 - src/thread.cpp | 11 ----------- src/thread.h | 1 - 3 files changed, 13 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c15cd7537e6..b79fa6beaf8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1160,7 +1160,6 @@ namespace { // re-searched at full depth. if ( depth >= 3 && moveCount > 1 + 2 * rootNode + 2 * (PvNode && abs(bestValue) < 2) - && (!rootNode || thisThread->best_move_count(move) == 0) && ( !captureOrPromotion || moveCountPruning || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha diff --git a/src/thread.cpp b/src/thread.cpp index 1aa66a81661..b46fce5e873 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -51,17 +51,6 @@ Thread::~Thread() { } -/// Thread::bestMoveCount(Move move) return best move counter for the given root move - -int Thread::best_move_count(Move move) const { - - 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 void Thread::clear() { diff --git a/src/thread.h b/src/thread.h index 042bc2e9231..34b99015bac 100644 --- a/src/thread.h +++ b/src/thread.h @@ -54,7 +54,6 @@ class Thread { void idle_loop(); void start_searching(); void wait_for_search_finished(); - int best_move_count(Move move) const; Pawns::Table pawnsTable; Material::Table materialTable; From be87517734e2a1b222d1a35e98764382b4176732 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Tue, 1 Sep 2020 12:22:47 +0200 Subject: [PATCH 0382/1766] Only use MADV_RANDOM if defined needed to compile on Haiku. fixes https://github.com/official-stockfish/Stockfish/issues/3093 closes https://github.com/official-stockfish/Stockfish/pull/3094 No functional change --- src/syzygy/tbprobe.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 3dfe3e3e481..4d682f1a90b 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -223,7 +223,9 @@ class TBFile : public std::ifstream { *mapping = statbuf.st_size; *baseAddress = mmap(nullptr, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0); +#if defined(MADV_RANDOM) madvise(*baseAddress, statbuf.st_size, MADV_RANDOM); +#endif ::close(fd); if (*baseAddress == MAP_FAILED) From aa2de712302a2379d8aa26127d86455ad276f512 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 2 Sep 2020 08:05:08 +0200 Subject: [PATCH 0383/1766] Update CPU contributors list with fishtest data of Sept. 2 2020 closes https://github.com/official-stockfish/Stockfish/pull/3095 No functional change --- Top CPU Contributors.txt | 319 +++++++++++++++++++++------------------ 1 file changed, 169 insertions(+), 150 deletions(-) diff --git a/Top CPU Contributors.txt b/Top CPU Contributors.txt index 0ea5ac72e20..482e9000074 100644 --- a/Top CPU Contributors.txt +++ b/Top CPU Contributors.txt @@ -1,154 +1,173 @@ -Contributors with >10,000 CPU hours as of January 7, 2020 +Contributors with >10,000 CPU hours as of Sept 2, 2020 Thank you! Username CPU Hours Games played -------------------------------------------------- -noobpwnftw 9305707 695548021 -mlang 780050 61648867 -dew 621626 43921547 -mibere 524702 42238645 -crunchy 354587 27344275 -cw 354495 27274181 -fastgm 332801 22804359 -JojoM 295750 20437451 -CSU_Dynasty 262015 21828122 -Fisherman 232181 18939229 -ctoks 218866 17622052 -glinscott 201989 13780820 -tvijlbrief 201204 15337115 -velislav 188630 14348485 -gvreuls 187164 15149976 -bking_US 180289 11876016 -nordlandia 172076 13467830 -leszek 157152 11443978 -Thanar 148021 12365359 -spams 141975 10319326 -drabel 138073 11121749 -vdv 137850 9394330 -mgrabiak 133578 10454324 -TueRens 132485 10878471 -bcross 129683 11557084 -marrco 126078 9356740 -sqrt2 125830 9724586 -robal 122873 9593418 -vdbergh 120766 8926915 -malala 115926 8002293 -CoffeeOne 114241 5004100 -dsmith 113189 7570238 -BrunoBanani 104644 7436849 -Data 92328 8220352 -mhoram 89333 6695109 -davar 87924 7009424 -xoto 81094 6869316 -ElbertoOne 80899 7023771 -grandphish2 78067 6160199 -brabos 77212 6186135 -psk 75733 5984901 -BRAVONE 73875 5054681 -sunu 70771 5597972 -sterni1971 70605 5590573 -MaZePallas 66886 5188978 -Vizvezdenec 63708 4967313 -nssy 63462 5259388 -jromang 61634 4940891 -teddybaer 61231 5407666 -Pking_cda 60099 5293873 -solarlight 57469 5028306 -dv8silencer 56913 3883992 -tinker 54936 4086118 -renouve 49732 3501516 -Freja 49543 3733019 -robnjr 46972 4053117 -rap 46563 3219146 -Bobo1239 46036 3817196 -ttruscott 45304 3649765 -racerschmacer 44881 3975413 -finfish 44764 3370515 -eva42 41783 3599691 -biffhero 40263 3111352 -bigpen0r 39817 3291647 -mhunt 38871 2691355 -ronaldjerum 38820 3240695 -Antihistamine 38785 2761312 -pb00067 38038 3086320 -speedycpu 37591 3003273 -rkl 37207 3289580 -VoyagerOne 37050 3441673 -jbwiebe 35320 2805433 -cuistot 34191 2146279 -homyur 33927 2850481 -manap 32873 2327384 -gri 32538 2515779 -oryx 31267 2899051 -EthanOConnor 30959 2090311 -SC 30832 2730764 -csnodgrass 29505 2688994 -jmdana 29458 2205261 -strelock 28219 2067805 -jkiiski 27832 1904470 -Pyafue 27533 1902349 -Garf 27515 2747562 -eastorwest 27421 2317535 -slakovv 26903 2021889 -Prcuvu 24835 2170122 -anst 24714 2190091 -hyperbolic.tom 24319 2017394 -Patrick_G 23687 1801617 -Sharaf_DG 22896 1786697 -nabildanial 22195 1519409 -chriswk 21931 1868317 -achambord 21665 1767323 -Zirie 20887 1472937 -team-oh 20217 1636708 -Isidor 20096 1680691 -ncfish1 19931 1520927 -nesoneg 19875 1463031 -Spprtr 19853 1548165 -JanErik 19849 1703875 -agg177 19478 1395014 -SFTUser 19231 1567999 -xor12 19017 1680165 -sg4032 18431 1641865 -rstoesser 18118 1293588 -MazeOfGalious 17917 1629593 -j3corre 17743 941444 -cisco2015 17725 1690126 -ianh2105 17706 1632562 -dex 17678 1467203 -jundery 17194 1115855 -iisiraider 17019 1101015 -horst.prack 17012 1465656 -Adrian.Schmidt123 16563 1281436 -purplefishies 16342 1092533 -wei 16274 1745989 -ville 16144 1384026 -eudhan 15712 1283717 -OuaisBla 15581 972000 -DragonLord 15559 1162790 -dju 14716 875569 -chris 14479 1487385 -0xB00B1ES 14079 1001120 -OssumOpossum 13776 1007129 -enedene 13460 905279 -bpfliegel 13346 884523 -Ente 13198 1156722 -IgorLeMasson 13087 1147232 -jpulman 13000 870599 -ako027ako 12775 1173203 -Nikolay.IT 12352 1068349 -Andrew Grant 12327 895539 -joster 12008 950160 -AdrianSA 11996 804972 -Nesa92 11455 1111993 -fatmurphy 11345 853210 -Dark_wizzie 11108 1007152 -modolief 10869 896470 -mschmidt 10757 803401 -infinity 10594 727027 -mabichito 10524 749391 -Thomas A. Anderson 10474 732094 -thijsk 10431 719357 -Flopzee 10339 894821 -crocogoat 10104 1013854 -SapphireBrand 10104 969604 -stocky 10017 699440 +noobpwnftw 19352969 1231459677 +mlang 957168 61657446 +dew 949885 56893432 +mibere 703817 46865007 +crunchy 427035 27344275 +cw 416006 27521077 +JojoM 415904 24479564 +fastgm 404873 23953472 +CSU_Dynasty 335774 22850550 +tvijlbrief 335199 21871270 +Fisherman 325053 21786603 +gvreuls 311480 20751516 +ctoks 275877 18710423 +velislav 241267 15596372 +glinscott 217799 13780820 +nordlandia 211692 13484886 +bcross 206213 14934233 +bking_US 198894 11876016 +leszek 189170 11446821 +mgrabiak 183896 11778092 +drabel 181408 12489478 +TueRens 181349 12192000 +Thanar 179852 12365359 +vdv 175171 9881246 +robal 166948 10702862 +spams 157128 10319326 +marrco 149947 9376421 +sqrt2 147963 9724586 +vdbergh 137041 8926915 +CoffeeOne 136294 5004100 +malala 136182 8002293 +mhoram 128934 8177193 +davar 122092 7960001 +dsmith 122059 7570238 +xoto 119696 8222144 +grandphish2 116481 7582197 +Data 113305 8220352 +BrunoBanani 112960 7436849 +ElbertoOne 99028 7023771 +MaZePallas 98571 6362619 +brabos 92118 6186135 +psk 89957 5984901 +sunu 88463 6007033 +sterni1971 86948 5613788 +Vizvezdenec 83752 5343724 +BRAVONE 81239 5054681 +nssy 76497 5259388 +teddybaer 75125 5407666 +Pking_cda 73776 5293873 +jromang 70695 4940891 +solarlight 70517 5028306 +dv8silencer 70287 3883992 +Bobo1239 68515 4652287 +racerschmacer 67468 4935996 +manap 66273 4121774 +tinker 63458 4213726 +linrock 59082 4516053 +robnjr 57262 4053117 +Freja 56938 3733019 +ttruscott 56005 3679485 +renouve 53811 3501516 +cuistot 52532 3014920 +finfish 51360 3370515 +eva42 51272 3599691 +rkl 50759 3840947 +rap 49985 3219146 +pb00067 49727 3298270 +ronaldjerum 47654 3240695 +bigpen0r 47278 3291647 +biffhero 46564 3111352 +VoyagerOne 45386 3445881 +speedycpu 43842 3003273 +jbwiebe 43305 2805433 +Antihistamine 41788 2761312 +mhunt 41735 2691355 +eastorwest 40387 2812173 +homyur 39893 2850481 +gri 39871 2515779 +oryx 38228 2941656 +0x3C33 37773 2529097 +SC 37290 2731014 +csnodgrass 36207 2688994 +jmdana 36108 2205261 +strelock 34716 2074055 +Garf 33800 2747562 +EthanOConnor 33370 2090311 +slakovv 32915 2021889 +Spprtr 32591 2139601 +Prcuvu 30377 2170122 +anst 30301 2190091 +jkiiski 30136 1904470 +hyperbolic.tom 29840 2017394 +Pyafue 29650 1902349 +OuaisBla 27629 1578000 +chriswk 26902 1868317 +achambord 26582 1767323 +Patrick_G 26276 1801617 +yorkman 26193 1992080 +SFTUser 25182 1675689 +nabildanial 24942 1519409 +Sharaf_DG 24765 1786697 +ncfish1 24411 1520927 +agg177 23890 1395014 +JanErik 23408 1703875 +Isidor 23388 1680691 +Norabor 22976 1587862 +cisco2015 22880 1759669 +Zirie 22542 1472937 +team-oh 22272 1636708 +MazeOfGalious 21978 1629593 +sg4032 21945 1643065 +ianh2105 21725 1632562 +xor12 21628 1680365 +dex 21612 1467203 +nesoneg 21494 1463031 +horst.prack 20878 1465656 +0xB00B1ES 20590 1208666 +j3corre 20405 941444 +Adrian.Schmidt123 20316 1281436 +wei 19973 1745989 +rstoesser 19569 1293588 +eudhan 19274 1283717 +Ente 19070 1373058 +jundery 18445 1115855 +iisiraider 18247 1101015 +ville 17883 1384026 +chris 17698 1487385 +purplefishies 17595 1092533 +DragonLord 17014 1162790 +dju 16515 929427 +IgorLeMasson 16064 1147232 +ako027ako 15671 1173203 +Nikolay.IT 15154 1068349 +Andrew Grant 15114 895539 +yurikvelo 15027 1165616 +OssumOpossum 14857 1007129 +enedene 14476 905279 +bpfliegel 14298 884523 +jpulman 13982 870599 +joster 13794 950160 +Nesa92 13786 1114691 +Dark_wizzie 13422 1007152 +Hjax 13350 900887 +Fifis 13313 965473 +mabichito 12903 749391 +thijsk 12886 722107 +crocogoat 12876 1048802 +AdrianSA 12860 804972 +Flopzee 12698 894821 +fatmurphy 12547 853210 +SapphireBrand 12416 969604 +modolief 12386 896470 +scuzzi 12362 833465 +pgontarz 12151 848794 +stocky 11954 699440 +mschmidt 11941 803401 +infinity 11470 727027 +torbjo 11387 728873 +Thomas A. Anderson 11372 732094 +snicolet 11106 869170 +amicic 10779 733593 +rpngn 10712 688203 +d64 10680 771144 +basepi 10637 744851 +jjoshua2 10559 670905 +dzjp 10343 732529 +ols 10259 570669 +lbraesch 10252 647825 From c306d838697011da0a960758dde3f7ede6849060 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 2 Sep 2020 09:12:04 +0200 Subject: [PATCH 0384/1766] Stockfish 12 Official release version of Stockfish 12 Bench: 3624569 ----------------------- It is our pleasure to release Stockfish 12 to users world-wide Downloads will be freely available at https://stockfishchess.org/download/ This version 12 of Stockfish plays significantly stronger than any of its predecessors. In a match against Stockfish 11, Stockfish 12 will typically win at least ten times more game pairs than it loses. This jump in strength, visible in regular progression tests during development[1], results from the introduction of an efficiently updatable neural network (NNUE) for the evaluation in Stockfish[2], and associated tuning of the engine as a whole. The concept of the NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward. Stockfish remains a CPU-only engine, since the NNUE networks can be very efficiently evaluated on CPUs. The recommended parameters of the NNUE network are embedded in distributed binaries, and Stockfish will use NNUE by default. Both the NNUE and the classical evaluations are available, and can be used to assign values to positions that are later used in alpha-beta (PVS) search to find the best move. The classical evaluation computes this value as a function of various chess concepts, handcrafted by experts, tested and tuned using fishtest. The NNUE evaluation computes this value with a neural network based on basic inputs. The network is optimized and trained on the evaluations of millions of positions. The Stockfish project builds on a thriving community of enthusiasts that contribute their expertise, time, and resources to build a free and open source chess engine that is robust, widely available, and very strong. We invite chess fans to join the fishtest testing framework and programmers to contribute on github[3]. Stay safe and enjoy chess! The Stockfish team [1] https://github.com/glinscott/fishtest/wiki/Regression-Tests [2] https://github.com/official-stockfish/Stockfish/commit/84f3e867903f62480c33243dd0ecbffd342796fc [3] https://stockfishchess.org/get-involved/ --- src/misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index 3fbdea35d94..22070f0e996 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -65,7 +65,7 @@ namespace { /// Version number. If Version is left empty, then compile date in the format /// DD-MM-YY and show in engine_info. -const string Version = ""; +const string Version = "12"; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We From 571c2d6d8daf70de884c493b40cf0279e9b48c61 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 4 Sep 2020 07:46:06 +0200 Subject: [PATCH 0385/1766] Restore development version have fun! No functional change --- src/misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index 22070f0e996..3fbdea35d94 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -65,7 +65,7 @@ namespace { /// Version number. If Version is left empty, then compile date in the format /// DD-MM-YY and show in engine_info. -const string Version = "12"; +const string Version = ""; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We From 0e1f734b05ee5c67e9a17ae0e2045a64209dee05 Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Wed, 2 Sep 2020 16:45:49 +0200 Subject: [PATCH 0386/1766] Less pruning in qsearch do not prune moves that give discovery checks, even if with negative SSE. STC https://tests.stockfishchess.org/tests/view/5f4cb5e8ba100690c5cc5d25 LLR: 2.96 (-2.94,2.94) {-0.25,1.25} Total: 91328 W: 9940 L: 9667 D: 71721 Ptnml(0-2): 491, 7345, 29693, 7670, 465 LTC https://tests.stockfishchess.org/tests/view/5f4dbc2eba100690c5cc5dac LLR: 2.97 (-2.94,2.94) {0.25,1.25} Total: 52448 W: 2799 L: 2586 D: 47063 Ptnml(0-2): 53, 2220, 21459, 2445, 47 closes https://github.com/official-stockfish/Stockfish/pull/3098 bench: 4031192 --- src/search.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index b79fa6beaf8..0d823c8e024 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1564,7 +1564,9 @@ namespace { } // Do not search moves with negative SEE values - if (!ss->inCheck && !pos.see_ge(move)) + if ( !ss->inCheck + && !(givesCheck && pos.is_discovery_check_on_king(~pos.side_to_move(), move)) + && !pos.see_ge(move)) continue; // Speculative prefetch as early as possible From d6530f7d49ef45e38dacafd8a3a838130113265c Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Thu, 3 Sep 2020 12:18:42 +0200 Subject: [PATCH 0387/1766] Simplify singularQuietLMR remove formerPV dependence STC https://tests.stockfishchess.org/tests/view/5f4cb922ba100690c5cc5d35 LLR: 2.96 (-2.94,2.94) {-1.25,0.25} Total: 113672 W: 12347 L: 12368 D: 88957 Ptnml(0-2): 566, 9537, 36699, 9420, 614 LTC https://tests.stockfishchess.org/tests/view/5f4e8474ba100690c5cc5e12 LLR: 2.93 (-2.94,2.94) {-0.75,0.25} Total: 43032 W: 2298 L: 2227 D: 38507 Ptnml(0-2): 45, 1940, 17475, 2011, 45 closes https://github.com/official-stockfish/Stockfish/pull/3102 bench: 3290084 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 0d823c8e024..b5e190c8c81 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1196,7 +1196,7 @@ namespace { // Decrease reduction if ttMove has been singularly extended (~3 Elo) if (singularQuietLMR) - r -= 1 + formerPv; + r--; if (!captureOrPromotion) { From 2a696115094882b7dc5c024a97ed7dc2bdc98642 Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Wed, 2 Sep 2020 16:58:44 -0400 Subject: [PATCH 0388/1766] LMR Simplification remove reduction at non-check cut nodes for second move at low depths STC: LLR: 2.95 (-2.94,2.94) {-1.25,0.25} Total: 61712 W: 6594 L: 6543 D: 48575 Ptnml(0-2): 293, 5085, 20082, 5070, 326 https://tests.stockfishchess.org/tests/view/5f5007d6ba100690c5cc5ea9 LTC: LLR: 2.94 (-2.94,2.94) {-0.75,0.25} Total: 57544 W: 2983 L: 2925 D: 51636 Ptnml(0-2): 47, 2568, 23495, 2604, 58 https://tests.stockfishchess.org/tests/view/5f50c597ba100690c5cc5ef7 closes https://github.com/official-stockfish/Stockfish/pull/3103 Bench: 3952302 --- src/search.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index b5e190c8c81..a769284193a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1168,13 +1168,6 @@ namespace { { Depth r = reduction(improving, depth, moveCount); - // Decrease reduction at non-check cut nodes for second move at low depths - if ( cutNode - && depth <= 10 - && moveCount <= 2 - && !ss->inCheck) - r--; - // Decrease reduction if the ttHit running average is large if (thisThread->ttHitAverage > 509 * TtHitAverageResolution * TtHitAverageWindow / 1024) r--; From 9cc482c7889cbc5f6d92e1b69ccd28d422a44a32 Mon Sep 17 00:00:00 2001 From: Sergio Vieri Date: Thu, 3 Sep 2020 20:22:51 +0800 Subject: [PATCH 0389/1766] Update default net to nn-308d71810dff.nnue equivalent to 20200903-1739 Net trained from scratch, so it has quite different features extracted compared to the previous net (82215d0fd0df). STC: LLR: 2.98 (-2.94,2.94) {-0.25,1.25} Total: 108328 W: 14048 L: 13719 D: 80561 Ptnml(0-2): 842, 10039, 32062, 10390, 831 https://tests.stockfishchess.org/tests/view/5f50e053ba100690c5cc5f00 LTC: LLR: 2.96 (-2.94,2.94) {0.25,1.25} Total: 13872 W: 1059 L: 890 D: 11923 Ptnml(0-2): 30, 724, 5270, 871, 41 https://tests.stockfishchess.org/tests/view/5f51821fba100690c5cc5f36 closes https://github.com/official-stockfish/Stockfish/pull/3104 Bench: 3832716 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index d701f5a7fde..3da6a9fea6e 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -38,7 +38,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-82215d0fd0df.nnue" + #define EvalFileDefaultName "nn-308d71810dff.nnue" namespace NNUE { From 9a063fc3cbc8f522215392db232eeb0e04e71b2c Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Fri, 4 Sep 2020 15:53:59 +0300 Subject: [PATCH 0390/1766] Adjust penalty on refuted early quiet moves This patch changes how previous early moves are penalized in case search finds a best move. Here, the first quiet move that was not a transposition table move is penalized. passed STC https://tests.stockfishchess.org/tests/view/5f51d839ba100690c5cc5f69 LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 10088 W: 1150 L: 997 D: 7941 Ptnml(0-2): 41, 772, 3278, 899, 54 passed LTC https://tests.stockfishchess.org/tests/view/5f51e435ba100690c5cc5f76 LLR: 2.93 (-2.94,2.94) {0.25,1.25} Total: 30808 W: 1564 L: 1405 D: 27839 Ptnml(0-2): 19, 1245, 12717, 1404, 19 closes https://github.com/official-stockfish/Stockfish/pull/3106 bench 3983758 --- src/search.cpp | 42 +++++++++++++++++++++--------------------- src/search.h | 1 + 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index a769284193a..4aeadc28a78 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -597,7 +597,7 @@ namespace { Move ttMove, move, excludedMove, bestMove; Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue, probCutBeta; - bool ttHit, formerPv, givesCheck, improving, didLMR, priorCapture; + bool formerPv, givesCheck, improving, didLMR, priorCapture; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture, singularQuietLMR; Piece movedPiece; @@ -664,12 +664,12 @@ namespace { // position key in case of an excluded move. excludedMove = ss->excludedMove; posKey = excludedMove == MOVE_NONE ? pos.key() : pos.key() ^ make_key(excludedMove); - tte = TT.probe(posKey, ttHit); - ttValue = ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; + tte = TT.probe(posKey, ss->ttHit); + ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] - : ttHit ? tte->move() : MOVE_NONE; + : ss->ttHit ? tte->move() : MOVE_NONE; if (!excludedMove) - ss->ttPv = PvNode || (ttHit && tte->is_pv()); + ss->ttPv = PvNode || (ss->ttHit && tte->is_pv()); formerPv = ss->ttPv && !PvNode; if ( ss->ttPv @@ -681,11 +681,11 @@ namespace { // thisThread->ttHitAverage can be used to approximate the running average of ttHit thisThread->ttHitAverage = (TtHitAverageWindow - 1) * thisThread->ttHitAverage / TtHitAverageWindow - + TtHitAverageResolution * ttHit; + + TtHitAverageResolution * ss->ttHit; // At non-PV nodes we check for an early TT cutoff if ( !PvNode - && ttHit + && ss->ttHit && tte->depth() >= depth && ttValue != VALUE_NONE // Possible in case of TT access race && (ttValue >= beta ? (tte->bound() & BOUND_LOWER) @@ -778,7 +778,7 @@ namespace { improving = false; goto moves_loop; } - else if (ttHit) + else if (ss->ttHit) { // Never assume anything about values stored in TT ss->staticEval = eval = tte->eval(); @@ -882,14 +882,14 @@ namespace { // there and in further interactions with transposition table cutoff depth is set to depth - 3 // because probCut search has depth set to depth - 4 but we also do a move before it // so effective depth is equal to depth - 3 - && !( ttHit + && !( ss->ttHit && tte->depth() >= depth - 3 && ttValue != VALUE_NONE && ttValue < probCutBeta)) { // if ttMove is a capture and value from transposition table is good enough produce probCut // cutoff without digging into actual probCut search - if ( ttHit + if ( ss->ttHit && tte->depth() >= depth - 3 && ttValue != VALUE_NONE && ttValue >= probCutBeta @@ -933,7 +933,7 @@ namespace { if (value >= probCutBeta) { // if transposition table doesn't have equal or more deep info write probCut data into it - if ( !(ttHit + if ( !(ss->ttHit && tte->depth() >= depth - 3 && ttValue != VALUE_NONE)) tte->save(posKey, value_to_tt(value, ss->ply), ttPv, @@ -1423,7 +1423,7 @@ namespace { Move ttMove, move, bestMove; Depth ttDepth; Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha; - bool ttHit, pvHit, givesCheck, captureOrPromotion; + bool pvHit, givesCheck, captureOrPromotion; int moveCount; if (PvNode) @@ -1453,13 +1453,13 @@ namespace { : DEPTH_QS_NO_CHECKS; // Transposition table lookup posKey = pos.key(); - tte = TT.probe(posKey, ttHit); - ttValue = ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; - ttMove = ttHit ? tte->move() : MOVE_NONE; - pvHit = ttHit && tte->is_pv(); + tte = TT.probe(posKey, ss->ttHit); + ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; + ttMove = ss->ttHit ? tte->move() : MOVE_NONE; + pvHit = ss->ttHit && tte->is_pv(); if ( !PvNode - && ttHit + && ss->ttHit && tte->depth() >= ttDepth && ttValue != VALUE_NONE // Only in case of TT access race && (ttValue >= beta ? (tte->bound() & BOUND_LOWER) @@ -1474,7 +1474,7 @@ namespace { } else { - if (ttHit) + if (ss->ttHit) { // Never assume anything about values stored in TT if ((ss->staticEval = bestValue = tte->eval()) == VALUE_NONE) @@ -1493,7 +1493,7 @@ namespace { // Stand pat. Return immediately if static value is at least beta if (bestValue >= beta) { - if (!ttHit) + if (!ss->ttHit) tte->save(posKey, value_to_tt(bestValue, ss->ply), false, BOUND_LOWER, DEPTH_NONE, MOVE_NONE, ss->staticEval); @@ -1711,8 +1711,8 @@ namespace { else captureHistory[moved_piece][to_sq(bestMove)][captured] << bonus1; - // 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])) + // Extra penalty for a quiet early move that was not a TT move or main killer move in previous ply when it gets refuted + if ( ((ss-1)->moveCount == 1 + (ss-1)->ttHit || ((ss-1)->currentMove == (ss-1)->killers[0])) && !pos.captured_piece()) update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -bonus1); diff --git a/src/search.h b/src/search.h index 79085189592..f60da4a514d 100644 --- a/src/search.h +++ b/src/search.h @@ -49,6 +49,7 @@ struct Stack { int moveCount; bool inCheck; bool ttPv; + bool ttHit; }; From d539da19d2b13d70a81ab863f54046add0bc3b38 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Fri, 4 Sep 2020 17:14:50 +0800 Subject: [PATCH 0391/1766] Use classical eval more often If there is a moderate imbalance, use classical eval with small probability (1/16), as derived from the node counter. STC: LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 32320 W: 3562 L: 3377 D: 25381 Ptnml(0-2): 144, 2609, 10478, 2776, 153 https://tests.stockfishchess.org/tests/view/5f520615ba100690c5cc5f80 LTC: LLR: 2.95 (-2.94,2.94) {0.25,1.25} Total: 21032 W: 1116 L: 974 D: 18942 Ptnml(0-2): 20, 837, 8664, 971, 24 https://tests.stockfishchess.org/tests/view/5f522eaaba100690c5cc5f8c closes https://github.com/official-stockfish/Stockfish/pull/3107 Bench: 4109324 --- src/evaluate.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 09f36513047..db8379da34f 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1015,12 +1015,16 @@ namespace { Value Eval::evaluate(const Position& pos) { + bool useClassical = abs(eg_value(pos.psq_score())) * 16 > NNUEThreshold1 * (16 + pos.rule50_count()); bool classical = !Eval::useNNUE - || abs(eg_value(pos.psq_score())) * 16 > NNUEThreshold1 * (16 + pos.rule50_count()); + || useClassical + || (abs(eg_value(pos.psq_score())) > PawnValueMg / 8 && !(pos.this_thread()->nodes & 0xF)); Value v = classical ? Evaluation(pos).value() : NNUE::evaluate(pos) * 5 / 4 + Tempo; - if (classical && Eval::useNNUE && abs(v) * 16 < NNUEThreshold2 * (16 + pos.rule50_count())) + if ( useClassical + && Eval::useNNUE + && abs(v) * 16 < NNUEThreshold2 * (16 + pos.rule50_count())) v = NNUE::evaluate(pos) * 5 / 4 + Tempo; // Damp down the evaluation linearly when shuffling From fc27d158c012341593518a05abf51903ecbcb495 Mon Sep 17 00:00:00 2001 From: syzygy1 <3028851+syzygy1@users.noreply.github.com> Date: Sun, 6 Sep 2020 17:29:12 +0200 Subject: [PATCH 0392/1766] Bug fix in do_null_move() and NNUE simplification. This fixes #3108 and removes some NNUE code that is currently not used. At the moment, do_null_move() copies the accumulator from the previous state into the new state, which is correct. It then clears the "computed_score" flag because the side to move has changed, and with the other side to move NNUE will return a completely different evaluation (normally with changed sign but also with different NNUE-internal tempo bonus). The problem is that do_null_move() clears the wrong flag. It clears the computed_score flag of the old state, not of the new state. It turns out that this almost never affects the search. For example, fixing it does not change the current bench (but it does change the previous bench). This is because the search code usually avoids calling evaluate() after a null move. This PR corrects do_null_move() by removing the computed_score flag altogether. The flag is not needed because nnue_evaluate() is never called twice on a position. This PR also removes some unnecessary {}s and inserts a few blank lines in the modified NNUE files in line with SF coding style. Resulf ot STC non-regression test: LLR: 2.95 (-2.94,2.94) {-1.25,0.25} Total: 26328 W: 3118 L: 3012 D: 20198 Ptnml(0-2): 126, 2208, 8397, 2300, 133 https://tests.stockfishchess.org/tests/view/5f553ccc2d02727c56b36db1 closes https://github.com/official-stockfish/Stockfish/pull/3109 bench: 4109324 --- src/nnue/evaluate_nnue.cpp | 38 ++----------------- src/nnue/nnue_accumulator.h | 2 - src/nnue/nnue_feature_transformer.h | 58 +++++++++++++---------------- src/position.cpp | 2 - 4 files changed, 29 insertions(+), 71 deletions(-) diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index d6ac9894cbb..ed1388812e0 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -115,31 +115,16 @@ namespace Eval::NNUE { return stream && stream.peek() == std::ios::traits_type::eof(); } - // Proceed with the difference calculation if possible - static void UpdateAccumulatorIfPossible(const Position& pos) { - - feature_transformer->UpdateAccumulatorIfPossible(pos); - } - - // Calculate the evaluation value - static Value ComputeScore(const Position& pos, bool refresh) { - - auto& accumulator = pos.state()->accumulator; - if (!refresh && accumulator.computed_score) { - return accumulator.score; - } + // Evaluation function. Perform differential calculation. + Value evaluate(const Position& pos) { alignas(kCacheLineSize) TransformedFeatureType transformed_features[FeatureTransformer::kBufferSize]; - feature_transformer->Transform(pos, transformed_features, refresh); + feature_transformer->Transform(pos, transformed_features); alignas(kCacheLineSize) char buffer[Network::kBufferSize]; const auto output = network->Propagate(transformed_features, buffer); - auto score = static_cast(output[0] / FV_SCALE); - - accumulator.score = score; - accumulator.computed_score = true; - return accumulator.score; + return static_cast(output[0] / FV_SCALE); } // Load eval, from a file stream or a memory stream @@ -150,19 +135,4 @@ namespace Eval::NNUE { return ReadParameters(stream); } - // Evaluation function. Perform differential calculation. - Value evaluate(const Position& pos) { - return ComputeScore(pos, false); - } - - // Evaluation function. Perform full calculation. - Value compute_eval(const Position& pos) { - return ComputeScore(pos, true); - } - - // Proceed with the difference calculation if possible - void update_eval(const Position& pos) { - UpdateAccumulatorIfPossible(pos); - } - } // namespace Eval::NNUE diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index 69dfaad2147..26370710201 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -29,9 +29,7 @@ namespace Eval::NNUE { struct alignas(kCacheLineSize) Accumulator { std::int16_t accumulation[2][kRefreshTriggers.size()][kTransformedFeatureDimensions]; - Value score; bool computed_accumulation; - bool computed_score; }; } // namespace Eval::NNUE diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 43707610231..2b6259c3281 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -50,11 +50,13 @@ namespace Eval::NNUE { // Hash value embedded in the evaluation file static constexpr std::uint32_t GetHashValue() { + return RawFeatures::kHashValue ^ kOutputDimensions; } // Read network parameters bool ReadParameters(std::istream& stream) { + for (std::size_t i = 0; i < kHalfDimensions; ++i) biases_[i] = read_little_endian(stream); for (std::size_t i = 0; i < kHalfDimensions * kInputDimensions; ++i) @@ -64,23 +66,26 @@ namespace Eval::NNUE { // Proceed with the difference calculation if possible bool UpdateAccumulatorIfPossible(const Position& pos) const { + const auto now = pos.state(); - if (now->accumulator.computed_accumulation) { + if (now->accumulator.computed_accumulation) return true; - } + const auto prev = now->previous; if (prev && prev->accumulator.computed_accumulation) { UpdateAccumulator(pos); return true; } + return false; } // Convert input features - void Transform(const Position& pos, OutputType* output, bool refresh) const { - if (refresh || !UpdateAccumulatorIfPossible(pos)) { + void Transform(const Position& pos, OutputType* output) const { + + if (!UpdateAccumulatorIfPossible(pos)) RefreshAccumulator(pos); - } + const auto& accumulation = pos.state()->accumulator.accumulation; #if defined(USE_AVX2) @@ -177,6 +182,7 @@ namespace Eval::NNUE { private: // Calculate cumulative value without using difference calculation void RefreshAccumulator(const Position& pos) const { + auto& accumulator = pos.state()->accumulator; IndexType i = 0; Features::IndexList active_indices[2]; @@ -216,9 +222,8 @@ namespace Eval::NNUE { &accumulator.accumulation[perspective][i][0]); auto column = reinterpret_cast(&weights_[offset]); constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); - for (IndexType j = 0; j < kNumChunks; ++j) { + for (IndexType j = 0; j < kNumChunks; ++j) accumulation[j] = _mm_add_pi16(accumulation[j], column[j]); - } #elif defined(USE_NEON) auto accumulation = reinterpret_cast( @@ -240,11 +245,11 @@ namespace Eval::NNUE { #endif accumulator.computed_accumulation = true; - accumulator.computed_score = false; } // Calculate cumulative value using difference calculation void UpdateAccumulator(const Position& pos) const { + const auto prev_accumulator = pos.state()->previous->accumulator; auto& accumulator = pos.state()->accumulator; IndexType i = 0; @@ -288,33 +293,27 @@ namespace Eval::NNUE { #if defined(USE_AVX2) auto column = reinterpret_cast(&weights_[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { + for (IndexType j = 0; j < kNumChunks; ++j) accumulation[j] = _mm256_sub_epi16(accumulation[j], column[j]); - } #elif defined(USE_SSE2) auto column = reinterpret_cast(&weights_[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { + for (IndexType j = 0; j < kNumChunks; ++j) accumulation[j] = _mm_sub_epi16(accumulation[j], column[j]); - } #elif defined(USE_MMX) auto column = reinterpret_cast(&weights_[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { + for (IndexType j = 0; j < kNumChunks; ++j) accumulation[j] = _mm_sub_pi16(accumulation[j], column[j]); - } #elif defined(USE_NEON) auto column = reinterpret_cast(&weights_[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { + for (IndexType j = 0; j < kNumChunks; ++j) accumulation[j] = vsubq_s16(accumulation[j], column[j]); - } #else - for (IndexType j = 0; j < kHalfDimensions; ++j) { - accumulator.accumulation[perspective][i][j] -= - weights_[offset + j]; - } + for (IndexType j = 0; j < kHalfDimensions; ++j) + accumulator.accumulation[perspective][i][j] -= weights_[offset + j]; #endif } @@ -325,33 +324,27 @@ namespace Eval::NNUE { #if defined(USE_AVX2) auto column = reinterpret_cast(&weights_[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { + for (IndexType j = 0; j < kNumChunks; ++j) accumulation[j] = _mm256_add_epi16(accumulation[j], column[j]); - } #elif defined(USE_SSE2) auto column = reinterpret_cast(&weights_[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { + for (IndexType j = 0; j < kNumChunks; ++j) accumulation[j] = _mm_add_epi16(accumulation[j], column[j]); - } #elif defined(USE_MMX) auto column = reinterpret_cast(&weights_[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { + for (IndexType j = 0; j < kNumChunks; ++j) accumulation[j] = _mm_add_pi16(accumulation[j], column[j]); - } #elif defined(USE_NEON) auto column = reinterpret_cast(&weights_[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { + for (IndexType j = 0; j < kNumChunks; ++j) accumulation[j] = vaddq_s16(accumulation[j], column[j]); - } #else - for (IndexType j = 0; j < kHalfDimensions; ++j) { - accumulator.accumulation[perspective][i][j] += - weights_[offset + j]; - } + for (IndexType j = 0; j < kHalfDimensions; ++j) + accumulator.accumulation[perspective][i][j] += weights_[offset + j]; #endif } @@ -362,7 +355,6 @@ namespace Eval::NNUE { #endif accumulator.computed_accumulation = true; - accumulator.computed_score = false; } using BiasType = std::int16_t; diff --git a/src/position.cpp b/src/position.cpp index fe89b75317f..e6a760d2c7a 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -704,7 +704,6 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // Used by NNUE st->accumulator.computed_accumulation = false; - st->accumulator.computed_score = false; auto& dp = st->dirtyPiece; dp.dirty_num = 1; @@ -1000,7 +999,6 @@ void Position::do_null_move(StateInfo& newSt) { if (Eval::useNNUE) { std::memcpy(&newSt, st, sizeof(StateInfo)); - st->accumulator.computed_score = false; } else std::memcpy(&newSt, st, offsetof(StateInfo, accumulator)); From d2562cde12cdcc3df654279d6d632ae74c5f71af Mon Sep 17 00:00:00 2001 From: Gian-Carlo Pascutto Date: Tue, 8 Sep 2020 15:37:53 +0200 Subject: [PATCH 0393/1766] Always re-enable NNUE after "bench". Restore the default NNUE setting (enabled) after a bench command. This also makes the resulting program settings independent of the number of FENs that are being benched. Fixes issue #3112. closes https://github.com/official-stockfish/Stockfish/pull/3113 No functional change. --- src/benchmark.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 806e98401a4..ffb631a2855 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -164,5 +164,7 @@ vector setup_bench(const Position& current, istream& is) { ++posCounter; } + list.emplace_back("setoption name Use NNUE value true"); + return list; } From 0405f3540366cc16245d51531881c55d3726c8b5 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Mon, 7 Sep 2020 04:54:26 +0800 Subject: [PATCH 0394/1766] Double probability of using classical eval This patch doubles the moderate imbalance threshold and probability of using classical eval. So now if imbalance is greater than PawnValueMg / 4 then there is a 1/8 chance of using classical eval. STC: LLR: 2.93 (-2.94,2.94) {-0.25,1.25} Total: 10984 W: 1303 L: 1140 D: 8541 Ptnml(0-2): 58, 867, 3489, 1010, 68 https://tests.stockfishchess.org/tests/view/5f554c9f97da2d5437d3813e LTC: LLR: 2.95 (-2.94,2.94) {0.25,1.25} Total: 43064 W: 2476 L: 2276 D: 38312 Ptnml(0-2): 37, 1985, 17308, 2145, 57 https://tests.stockfishchess.org/tests/view/5f55690a00a0aa2ca79f0a43 closes https://github.com/official-stockfish/Stockfish/pull/3114 Bench: 4161067 --- src/evaluate.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index db8379da34f..faf71d2701d 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1015,10 +1015,13 @@ namespace { Value Eval::evaluate(const Position& pos) { + // Use classical eval if there is a large imbalance + // If there is a moderate imbalance, use classical eval with probability (1/8), + // as derived from the node counter. bool useClassical = abs(eg_value(pos.psq_score())) * 16 > NNUEThreshold1 * (16 + pos.rule50_count()); bool classical = !Eval::useNNUE || useClassical - || (abs(eg_value(pos.psq_score())) > PawnValueMg / 8 && !(pos.this_thread()->nodes & 0xF)); + || (abs(eg_value(pos.psq_score())) > PawnValueMg / 4 && !(pos.this_thread()->nodes & 0xB)); Value v = classical ? Evaluation(pos).value() : NNUE::evaluate(pos) * 5 / 4 + Tempo; From 35ab8254b70f62a4e0138c475fad0c77dcc0af2d Mon Sep 17 00:00:00 2001 From: mckx00 Date: Sun, 13 Sep 2020 19:28:32 -0700 Subject: [PATCH 0395/1766] Simplify StatSCore Initialization No need to initialize StatScore at rootNode. Current Logic is redundant because at subsequent levels the grandchildren statScore is initialized to zero. closes https://github.com/official-stockfish/Stockfish/pull/3122 Non functional change. --- src/search.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 4aeadc28a78..07c491b69f4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -654,9 +654,7 @@ namespace { // starts with statScore = 0. Later grandchildren start with the last calculated // statScore of the previous grandchild. This influences the reduction rules in // LMR which are based on the statScore of parent position. - if (rootNode) - (ss+4)->statScore = 0; - else + if (!rootNode) (ss+2)->statScore = 0; // Step 4. Transposition table lookup. We don't want the score of a partial From 7135678f71b7f6ee32e92b8dbef2b16b403d8ea9 Mon Sep 17 00:00:00 2001 From: Sergio Vieri Date: Mon, 14 Sep 2020 17:24:05 +0800 Subject: [PATCH 0396/1766] Update default net to nn-03744f8d56d8.nnue Equivalent to 20200914-1520 closes https://github.com/official-stockfish/Stockfish/pull/3123 Bench: 4222126 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 3da6a9fea6e..c723bd8fa1d 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -38,7 +38,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-308d71810dff.nnue" + #define EvalFileDefaultName "nn-03744f8d56d8.nnue" namespace NNUE { From 5f426d8667feda65eaf1eca699f629d31e170d43 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Thu, 10 Sep 2020 21:10:57 +0100 Subject: [PATCH 0397/1766] Use 2 * bestMoveChanges. NNUE appears to provide a more stable eval than the classic eval, so the time use dependencies on bestMoveChanges, fallingEval, etc may need to change to make the best use of available time. This change doubles the effect of totBestMoveChanges when giving more time because the choice of best move is unstable. STC: LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 101928 W: 11995 L: 11698 D: 78235 Elo +0.78 Ptnml(0-2): 592, 8707, 32103, 8936, 626 https://tests.stockfishchess.org/tests/view/5f538a462d02727c56b36cec LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.25} Total: 186392 W: 10383 L: 9877 D: 166132 Elo +0.81 Ptnml(0-2): 207, 8370, 75539, 8870, 210 https://tests.stockfishchess.org/tests/view/5f54a9712d02727c56b36d5a closes https://github.com/official-stockfish/Stockfish/pull/3119 Bench 4222126 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 07c491b69f4..c7d2efd40da 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -520,7 +520,7 @@ void Thread::search() { totBestMoveChanges += th->bestMoveChanges; th->bestMoveChanges = 0; } - double bestMoveInstability = 1 + totBestMoveChanges / Threads.size(); + double bestMoveInstability = 1 + 2 * totBestMoveChanges / Threads.size(); double totalTime = rootMoves.size() == 1 ? 0 : Time.optimum() * fallingEval * reduction * bestMoveInstability; From d86663af141f1256bfc32ab95891e944d84e8755 Mon Sep 17 00:00:00 2001 From: syzygy1 <3028851+syzygy1@users.noreply.github.com> Date: Sun, 13 Sep 2020 20:16:52 +0200 Subject: [PATCH 0398/1766] Improve NDK section in Makefile This PR sets the "comp" variable simply to "clang", which seems to be more consistent and allows a small simplification. The PR also moves the section that sets "profile_make" and "profile_use" to after the NDK section, which ensures that these variables are now set correctly for NDK/clang. closes https://github.com/official-stockfish/Stockfish/pull/3121 No functional change --- src/Makefile | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/src/Makefile b/src/Makefile index 340b3008381..54868b39b46 100644 --- a/src/Makefile +++ b/src/Makefile @@ -381,19 +381,6 @@ ifeq ($(COMP),clang) endif endif -ifeq ($(comp),icc) - profile_make = icc-profile-make - profile_use = icc-profile-use -else -ifeq ($(comp),clang) - profile_make = clang-profile-make - profile_use = clang-profile-use -else - profile_make = gcc-profile-make - profile_use = gcc-profile-use -endif -endif - ifeq ($(KERNEL),Darwin) CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.14 LDFLAGS += -arch $(arch) -mmacosx-version-min=10.14 @@ -405,20 +392,30 @@ endif # Currently we don't know how to make PGO builds with the NDK yet. ifeq ($(COMP),ndk) CXXFLAGS += -stdlib=libc++ -fPIE + comp=clang ifeq ($(arch),armv7) - comp=armv7a-linux-androideabi16-clang CXX=armv7a-linux-androideabi16-clang++ CXXFLAGS += -mthumb -march=armv7-a -mfloat-abi=softfp -mfpu=neon STRIP=arm-linux-androideabi-strip endif ifeq ($(arch),armv8) - comp=aarch64-linux-android21-clang CXX=aarch64-linux-android21-clang++ STRIP=aarch64-linux-android-strip endif LDFLAGS += -static-libstdc++ -pie -lm -latomic endif +ifeq ($(comp),icc) + profile_make = icc-profile-make + profile_use = icc-profile-use +else ifeq ($(comp),clang) + profile_make = clang-profile-make + profile_use = clang-profile-use +else + profile_make = gcc-profile-make + profile_use = gcc-profile-use +endif + ### Travis CI script uses COMPILER to overwrite CXX ifdef COMPILER COMPCXX=$(COMPILER) @@ -590,10 +587,7 @@ endif ### needs access to the optimization flags. ifeq ($(optimize),yes) ifeq ($(debug), no) - ifeq ($(COMP),ndk) - CXXFLAGS += -flto=thin - LDFLAGS += $(CXXFLAGS) - else ifeq ($(comp),clang) + ifeq ($(comp),clang) CXXFLAGS += -flto=thin ifneq ($(findstring MINGW,$(KERNEL)),) CXXFLAGS += -fuse-ld=lld From df43805953b241f95c246ff3e96aece76b518590 Mon Sep 17 00:00:00 2001 From: GoldenRare Date: Thu, 10 Sep 2020 00:24:40 -0400 Subject: [PATCH 0399/1766] Added FEN string to bench output fixes https://github.com/official-stockfish/Stockfish/pull/3117 closes https://github.com/official-stockfish/Stockfish/pull/3118 No functional change --- AUTHORS | 1 + src/uci.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index c00ab657ff7..198dfa5a908 100644 --- a/AUTHORS +++ b/AUTHORS @@ -63,6 +63,7 @@ Gary Heckman (gheckman) George Sobala (gsobala) gguliash Gian-Carlo Pascutto (gcp) +Deshawn Mohan-Smith (GoldenRare) Gontran Lemaire (gonlem) Goodkov Vasiliy Aleksandrovich (goodkov) Gregor Cramer diff --git a/src/uci.cpp b/src/uci.cpp index bc0ee0a0ce8..3f3cc45874f 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -170,7 +170,7 @@ namespace { if (token == "go" || token == "eval") { - cerr << "\nPosition: " << cnt++ << '/' << num << endl; + cerr << "\nPosition: " << cnt++ << '/' << num << " (" << pos.fen() << ")" << endl; if (token == "go") { go(pos, is, states); From 0ca93c5b94b820a41e2850ede084096120128a28 Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Wed, 16 Sep 2020 19:14:32 +0200 Subject: [PATCH 0400/1766] Remove castling extension STC https://tests.stockfishchess.org/tests/view/5f5fa5348fbc1c8a3f476eca LLR: 2.94 (-2.94,2.94) {-1.25,0.25} Total: 38520 W: 4713 L: 4610 D: 29197 Ptnml(0-2): 233, 3486, 11734, 3559, 248 LTC https://tests.stockfishchess.org/tests/view/5f62166a912c15f19854b806 LLR: 2.93 (-2.94,2.94) {-0.75,0.25} Total: 48024 W: 2673 L: 2600 D: 42751 Ptnml(0-2): 64, 2247, 19316, 2322, 63 closes https://github.com/official-stockfish/Stockfish/pull/3128 bench: 3818400 --- src/search.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c7d2efd40da..17cd0a73b61 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1127,11 +1127,6 @@ namespace { && pos.non_pawn_material() <= 2 * RookValueMg) extension = 1; - // Castling extension - if ( type_of(move) == CASTLING - && popcount(pos.pieces(us) & ~pos.pieces(PAWN) & (to_sq(move) & KingSide ? KingSide : QueenSide)) <= 2) - extension = 1; - // Late irreversible move extension if ( move == ttMove && pos.rule50_count() > 80 From 64a63464d7bc72a3aac33aa680cd2b2b240ff903 Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Wed, 16 Sep 2020 20:42:38 +0200 Subject: [PATCH 0401/1766] Simplify futility pruning for captures STC https://tests.stockfishchess.org/tests/view/5f61f0e4b91f2ec371e429c2 LLR: 2.94 (-2.94,2.94) {-1.25,0.25} Total: 75512 W: 8747 L: 8704 D: 58061 Ptnml(0-2): 440, 6589, 23683, 6576, 468 LTC https://tests.stockfishchess.org/tests/view/5f6215d3912c15f19854b801 LLR: 2.95 (-2.94,2.94) {-0.75,0.25} Total: 92912 W: 5030 L: 4992 D: 82890 Ptnml(0-2): 88, 4363, 37532, 4369, 104 closes https://github.com/official-stockfish/Stockfish/pull/3129 bench: 3856086 --- src/search.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 17cd0a73b61..9c5fb58bd8b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1056,7 +1056,6 @@ namespace { if ( !givesCheck && lmrDepth < 6 && !(PvNode && abs(bestValue) < 2) - && PieceValue[MG][type_of(movedPiece)] >= PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] && !ss->inCheck && ss->staticEval + 169 + 244 * lmrDepth + PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha) From 8b8a510fd6a1a17b39b2d4b166f60ac7be0dab23 Mon Sep 17 00:00:00 2001 From: syzygy1 <3028851+syzygy1@users.noreply.github.com> Date: Wed, 16 Sep 2020 17:39:11 +0200 Subject: [PATCH 0402/1766] Use tiling to speed up accumulator refreshes and updates Perform the update and refresh operations tile by tile in a local array of vectors. By selecting the array size carefully, we achieve that the compiler keeps the whole array in vector registers. Idea and original implementation by @sf-x. STC: https://tests.stockfishchess.org/tests/view/5f623eec912c15f19854b855 LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 4872 W: 623 L: 477 D: 3772 Ptnml(0-2): 14, 350, 1585, 450, 37 LTC: https://tests.stockfishchess.org/tests/view/5f62434e912c15f19854b860 LLR: 2.94 (-2.94,2.94) {0.25,1.25} Total: 25808 W: 1565 L: 1401 D: 22842 Ptnml(0-2): 23, 1186, 10332, 1330, 33 closes https://github.com/official-stockfish/Stockfish/pull/3130 No functional change --- src/nnue/nnue_feature_transformer.h | 237 +++++++++++++++------------- 1 file changed, 127 insertions(+), 110 deletions(-) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 2b6259c3281..e71ee60d45a 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -29,6 +29,56 @@ namespace Eval::NNUE { + // If vector instructions are enabled, we update and refresh the + // accumulator tile by tile such that each tile fits in the CPU's + // vector registers. + #define TILING + + #ifdef USE_AVX512 + typedef __m512i vec_t; + #define vec_load(a) _mm512_loadA_si512(a) + #define vec_store(a,b) _mm512_storeA_si512(a,b) + #define vec_add_16(a,b) _mm512_add_epi16(a,b) + #define vec_sub_16(a,b) _mm512_sub_epi16(a,b) + static constexpr IndexType kNumRegs = 8; // only 8 are needed + + #elif USE_AVX2 + typedef __m256i vec_t; + #define vec_load(a) _mm256_loadA_si256(a) + #define vec_store(a,b) _mm256_storeA_si256(a,b) + #define vec_add_16(a,b) _mm256_add_epi16(a,b) + #define vec_sub_16(a,b) _mm256_sub_epi16(a,b) + static constexpr IndexType kNumRegs = 16; + + #elif USE_SSE2 + typedef __m128i vec_t; + #define vec_load(a) (*(a)) + #define vec_store(a,b) *(a)=(b) + #define vec_add_16(a,b) _mm_add_epi16(a,b) + #define vec_sub_16(a,b) _mm_sub_epi16(a,b) + static constexpr IndexType kNumRegs = Is64Bit ? 16 : 8; + + #elif USE_MMX + typedef __m64 vec_t; + #define vec_load(a) (*(a)) + #define vec_store(a,b) *(a)=(b) + #define vec_add_16(a,b) _mm_add_pi16(a,b) + #define vec_sub_16(a,b) _mm_sub_pi16(a,b) + static constexpr IndexType kNumRegs = 8; + + #elif USE_NEON + typedef int16x8_t vec_t; + #define vec_load(a) (*(a)) + #define vec_store(a,b) *(a)=(b) + #define vec_add_16(a,b) vaddq_s16(a,b) + #define vec_sub_16(a,b) vsubq_s16(a,b) + static constexpr IndexType kNumRegs = 16; + + #else + #undef TILING + + #endif + // Input feature converter class FeatureTransformer { @@ -36,6 +86,11 @@ namespace Eval::NNUE { // Number of output dimensions for one side static constexpr IndexType kHalfDimensions = kTransformedFeatureDimensions; + #ifdef TILING + static constexpr IndexType kTileHeight = kNumRegs * sizeof(vec_t) / 2; + static_assert(kHalfDimensions % kTileHeight == 0, "kTileHeight must divide kHalfDimensions"); + #endif + public: // Output type using OutputType = TransformedFeatureType; @@ -189,57 +244,41 @@ namespace Eval::NNUE { RawFeatures::AppendActiveIndices(pos, kRefreshTriggers[i], active_indices); for (Color perspective : { WHITE, BLACK }) { + #ifdef TILING + for (unsigned j = 0; j < kHalfDimensions / kTileHeight; ++j) { + auto biasesTile = reinterpret_cast( + &biases_[j * kTileHeight]); + auto accTile = reinterpret_cast( + &accumulator.accumulation[perspective][i][j * kTileHeight]); + vec_t acc[kNumRegs]; + + for (unsigned k = 0; k < kNumRegs; ++k) + acc[k] = biasesTile[k]; + + for (const auto index : active_indices[perspective]) { + const IndexType offset = kHalfDimensions * index + j * kTileHeight; + auto column = reinterpret_cast(&weights_[offset]); + + for (unsigned k = 0; k < kNumRegs; ++k) + acc[k] = vec_add_16(acc[k], column[k]); + } + + for (unsigned k = 0; k < kNumRegs; k++) + vec_store(&accTile[k], acc[k]); + } + #else std::memcpy(accumulator.accumulation[perspective][i], biases_, - kHalfDimensions * sizeof(BiasType)); + kHalfDimensions * sizeof(BiasType)); + for (const auto index : active_indices[perspective]) { const IndexType offset = kHalfDimensions * index; - #if defined(USE_AVX512) - auto accumulation = reinterpret_cast<__m512i*>( - &accumulator.accumulation[perspective][i][0]); - auto column = reinterpret_cast(&weights_[offset]); - constexpr IndexType kNumChunks = kHalfDimensions / kSimdWidth; - for (IndexType j = 0; j < kNumChunks; ++j) - _mm512_storeA_si512(&accumulation[j], _mm512_add_epi16(_mm512_loadA_si512(&accumulation[j]), column[j])); - - #elif defined(USE_AVX2) - auto accumulation = reinterpret_cast<__m256i*>( - &accumulator.accumulation[perspective][i][0]); - auto column = reinterpret_cast(&weights_[offset]); - constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); - for (IndexType j = 0; j < kNumChunks; ++j) - _mm256_storeA_si256(&accumulation[j], _mm256_add_epi16(_mm256_loadA_si256(&accumulation[j]), column[j])); - - #elif defined(USE_SSE2) - auto accumulation = reinterpret_cast<__m128i*>( - &accumulator.accumulation[perspective][i][0]); - auto column = reinterpret_cast(&weights_[offset]); - constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); - for (IndexType j = 0; j < kNumChunks; ++j) - accumulation[j] = _mm_add_epi16(accumulation[j], column[j]); - - #elif defined(USE_MMX) - auto accumulation = reinterpret_cast<__m64*>( - &accumulator.accumulation[perspective][i][0]); - auto column = reinterpret_cast(&weights_[offset]); - constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); - for (IndexType j = 0; j < kNumChunks; ++j) - accumulation[j] = _mm_add_pi16(accumulation[j], column[j]); - #elif defined(USE_NEON) - auto accumulation = reinterpret_cast( - &accumulator.accumulation[perspective][i][0]); - auto column = reinterpret_cast(&weights_[offset]); - constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); - for (IndexType j = 0; j < kNumChunks; ++j) - accumulation[j] = vaddq_s16(accumulation[j], column[j]); - - #else for (IndexType j = 0; j < kHalfDimensions; ++j) accumulator.accumulation[perspective][i][j] += weights_[offset + j]; - #endif - } + #endif } + #if defined(USE_MMX) _mm_empty(); #endif @@ -257,29 +296,55 @@ namespace Eval::NNUE { bool reset[2]; RawFeatures::AppendChangedIndices(pos, kRefreshTriggers[i], removed_indices, added_indices, reset); - for (Color perspective : { WHITE, BLACK }) { - #if defined(USE_AVX2) - constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); - auto accumulation = reinterpret_cast<__m256i*>( - &accumulator.accumulation[perspective][i][0]); - - #elif defined(USE_SSE2) - constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); - auto accumulation = reinterpret_cast<__m128i*>( - &accumulator.accumulation[perspective][i][0]); - - #elif defined(USE_MMX) - constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); - auto accumulation = reinterpret_cast<__m64*>( - &accumulator.accumulation[perspective][i][0]); + #ifdef TILING + for (IndexType j = 0; j < kHalfDimensions / kTileHeight; ++j) { + for (Color perspective : { WHITE, BLACK }) { + auto accTile = reinterpret_cast( + &accumulator.accumulation[perspective][i][j * kTileHeight]); + vec_t acc[kNumRegs]; + + if (reset[perspective]) { + auto biasesTile = reinterpret_cast( + &biases_[j * kTileHeight]); + for (unsigned k = 0; k < kNumRegs; ++k) + acc[k] = biasesTile[k]; + } else { + auto prevAccTile = reinterpret_cast( + &prev_accumulator.accumulation[perspective][i][j * kTileHeight]); + for (IndexType k = 0; k < kNumRegs; ++k) + acc[k] = vec_load(&prevAccTile[k]); + + // Difference calculation for the deactivated features + for (const auto index : removed_indices[perspective]) { + const IndexType offset = kHalfDimensions * index + j * kTileHeight; + auto column = reinterpret_cast(&weights_[offset]); + + for (IndexType k = 0; k < kNumRegs; ++k) + acc[k] = vec_sub_16(acc[k], column[k]); + } + } + { // Difference calculation for the activated features + for (const auto index : added_indices[perspective]) { + const IndexType offset = kHalfDimensions * index + j * kTileHeight; + auto column = reinterpret_cast(&weights_[offset]); + + for (IndexType k = 0; k < kNumRegs; ++k) + acc[k] = vec_add_16(acc[k], column[k]); + } + } - #elif defined(USE_NEON) - constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); - auto accumulation = reinterpret_cast( - &accumulator.accumulation[perspective][i][0]); + for (IndexType k = 0; k < kNumRegs; ++k) + vec_store(&accTile[k], acc[k]); + } + } + #if defined(USE_MMX) + _mm_empty(); #endif + #else + for (Color perspective : { WHITE, BLACK }) { + if (reset[perspective]) { std::memcpy(accumulator.accumulation[perspective][i], biases_, kHalfDimensions * sizeof(BiasType)); @@ -291,67 +356,19 @@ namespace Eval::NNUE { for (const auto index : removed_indices[perspective]) { const IndexType offset = kHalfDimensions * index; - #if defined(USE_AVX2) - auto column = reinterpret_cast(&weights_[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) - accumulation[j] = _mm256_sub_epi16(accumulation[j], column[j]); - - #elif defined(USE_SSE2) - auto column = reinterpret_cast(&weights_[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) - accumulation[j] = _mm_sub_epi16(accumulation[j], column[j]); - - #elif defined(USE_MMX) - auto column = reinterpret_cast(&weights_[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) - accumulation[j] = _mm_sub_pi16(accumulation[j], column[j]); - - #elif defined(USE_NEON) - auto column = reinterpret_cast(&weights_[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) - accumulation[j] = vsubq_s16(accumulation[j], column[j]); - - #else for (IndexType j = 0; j < kHalfDimensions; ++j) accumulator.accumulation[perspective][i][j] -= weights_[offset + j]; - #endif - } } { // Difference calculation for the activated features for (const auto index : added_indices[perspective]) { const IndexType offset = kHalfDimensions * index; - #if defined(USE_AVX2) - auto column = reinterpret_cast(&weights_[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) - accumulation[j] = _mm256_add_epi16(accumulation[j], column[j]); - - #elif defined(USE_SSE2) - auto column = reinterpret_cast(&weights_[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) - accumulation[j] = _mm_add_epi16(accumulation[j], column[j]); - - #elif defined(USE_MMX) - auto column = reinterpret_cast(&weights_[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) - accumulation[j] = _mm_add_pi16(accumulation[j], column[j]); - - #elif defined(USE_NEON) - auto column = reinterpret_cast(&weights_[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) - accumulation[j] = vaddq_s16(accumulation[j], column[j]); - - #else for (IndexType j = 0; j < kHalfDimensions; ++j) accumulator.accumulation[perspective][i][j] += weights_[offset + j]; - #endif - } } } - #if defined(USE_MMX) - _mm_empty(); #endif accumulator.computed_accumulation = true; From 8559c439148d0f183a5d67375c12abe92d63975e Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Sun, 20 Sep 2020 09:03:37 +0200 Subject: [PATCH 0403/1766] Simplify reduced depth search Simplification in reduced depth search. STC https://tests.stockfishchess.org/tests/view/5f64c72fbb0cae038ca8f531 LLR: 2.94 (-2.94,2.94) {-1.25,0.25} Total: 28320 W: 3475 L: 3359 D: 21486 Ptnml(0-2): 170, 2485, 8773, 2523, 209 LTC https://tests.stockfishchess.org/tests/view/5f650cfabb0cae038ca8f585 LLR: 2.95 (-2.94,2.94) {-0.75,0.25} Total: 58392 W: 3354 L: 3285 D: 51753 Ptnml(0-2): 74, 2826, 23336, 2877, 83 closes https://github.com/official-stockfish/Stockfish/pull/3139 bench: 4201295 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 9c5fb58bd8b..22cb8577cf7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1151,7 +1151,7 @@ namespace { // Step 16. Reduced depth search (LMR, ~200 Elo). If the move fails high it will be // re-searched at full depth. if ( depth >= 3 - && moveCount > 1 + 2 * rootNode + 2 * (PvNode && abs(bestValue) < 2) + && moveCount > 1 + 2 * rootNode && ( !captureOrPromotion || moveCountPruning || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha From 16b4578cc1bb0cc0dead19e7d9248553c977f8ca Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Sun, 20 Sep 2020 22:25:19 +0200 Subject: [PATCH 0404/1766] Tweak hybrid treshold. Increase the first hybrid threshold with more material. Rewrite the hybrid rules for clarity. STC: LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 24416 W: 3039 L: 2848 D: 18529 Ptnml(0-2): 135, 2136, 7503, 2271, 163 https://tests.stockfishchess.org/tests/view/5f6451efbb0cae038ca8f4dc LTC; LLR: 2.95 (-2.94,2.94) {0.25,1.25} Total: 65016 W: 3702 L: 3455 D: 57859 Ptnml(0-2): 66, 2991, 26157, 3218, 76 https://tests.stockfishchess.org/tests/view/5f64b143bb0cae038ca8f51f closes https://github.com/official-stockfish/Stockfish/pull/3140 Bench: 3973739 --- src/evaluate.cpp | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index faf71d2701d..a9159477a07 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1015,20 +1015,28 @@ namespace { Value Eval::evaluate(const Position& pos) { - // Use classical eval if there is a large imbalance - // If there is a moderate imbalance, use classical eval with probability (1/8), - // as derived from the node counter. - bool useClassical = abs(eg_value(pos.psq_score())) * 16 > NNUEThreshold1 * (16 + pos.rule50_count()); - bool classical = !Eval::useNNUE - || useClassical - || (abs(eg_value(pos.psq_score())) > PawnValueMg / 4 && !(pos.this_thread()->nodes & 0xB)); - Value v = classical ? Evaluation(pos).value() - : NNUE::evaluate(pos) * 5 / 4 + Tempo; - - if ( useClassical - && Eval::useNNUE - && abs(v) * 16 < NNUEThreshold2 * (16 + pos.rule50_count())) - v = NNUE::evaluate(pos) * 5 / 4 + Tempo; + Value v; + + if (!Eval::useNNUE) + v = Evaluation(pos).value(); + else + { + // scale and shift NNUE for compatibility with search and classical evaluation + auto adjusted_NNUE = [&](){ return NNUE::evaluate(pos) * 5 / 4 + Tempo; }; + + // if there is PSQ imbalance use classical eval, with small probability if it is small + Value psq = Value(abs(eg_value(pos.psq_score()))); + int r50 = 16 + pos.rule50_count(); + bool largePsq = psq * 16 > (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50; + bool classical = largePsq || (psq > PawnValueMg / 4 && !(pos.this_thread()->nodes & 0xB)); + + v = classical ? Evaluation(pos).value() : adjusted_NNUE(); + + // if the classical eval is small and imbalance large, use NNUE nevertheless. + if ( largePsq + && abs(v) * 16 < NNUEThreshold2 * r50) + v = adjusted_NNUE(); + } // Damp down the evaluation linearly when shuffling v = v * (100 - pos.rule50_count()) / 100; From 485d517c687a2d3cb0b88cc8c198483759eaf2c7 Mon Sep 17 00:00:00 2001 From: Sami Kiminki Date: Sun, 30 Aug 2020 19:41:30 +0300 Subject: [PATCH 0405/1766] Add large page support for NNUE weights and simplify TT mem management Use TT memory functions to allocate memory for the NNUE weights. This should provide a small speed-up on systems where large pages are not automatically used, including Windows and some Linux distributions. Further, since we now have a wrapper for std::aligned_alloc(), we can simplify the TT memory management a bit: - We no longer need to store separate pointers to the hash table and its underlying memory allocation. - We also get to merge the Linux-specific and default implementations of aligned_ttmem_alloc(). Finally, we'll enable the VirtualAlloc code path with large page support also for Win32. STC: https://tests.stockfishchess.org/tests/view/5f66595823a84a47b9036fba LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 14896 W: 1854 L: 1686 D: 11356 Ptnml(0-2): 65, 1224, 4742, 1312, 105 closes https://github.com/official-stockfish/Stockfish/pull/3081 No functional change. --- README.md | 2 +- src/misc.cpp | 57 +++++++++++++++++--------------------- src/misc.h | 4 +-- src/nnue/evaluate_nnue.cpp | 18 ++++++++---- src/nnue/evaluate_nnue.h | 11 ++++++++ src/tt.cpp | 7 +++-- src/tt.h | 3 +- 7 files changed, 57 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 96a495ae54b..255ebce2afc 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,7 @@ to find the best move. The classical evaluation computes this value as a functio of various chess concepts, handcrafted by experts, tested and tuned using fishtest. The NNUE evaluation computes this value with a neural network based on basic inputs (e.g. piece positions only). The network is optimized and trained -on the evalutions of millions of positions at moderate search depth. +on the evaluations of millions of positions at moderate search depth. The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward. It can be evaluated efficiently on CPUs, and exploits the fact that only parts diff --git a/src/misc.cpp b/src/misc.cpp index 3fbdea35d94..d9bc47e3c88 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -357,27 +357,11 @@ void std_aligned_free(void* ptr) { #endif } -/// aligned_ttmem_alloc() will return suitably aligned memory, if possible using large pages. -/// The returned pointer is the aligned one, while the mem argument is the one that needs -/// to be passed to free. With c++17 some of this functionality could be simplified. +/// aligned_large_pages_alloc() will return suitably aligned memory, if possible using large pages. -#if defined(__linux__) && !defined(__ANDROID__) - -void* aligned_ttmem_alloc(size_t allocSize, void*& mem) { - - constexpr size_t alignment = 2 * 1024 * 1024; // assumed 2MB page sizes - size_t size = ((allocSize + alignment - 1) / alignment) * alignment; // multiple of alignment - if (posix_memalign(&mem, alignment, size)) - mem = nullptr; -#if defined(MADV_HUGEPAGE) - madvise(mem, allocSize, MADV_HUGEPAGE); -#endif - return mem; -} +#if defined(_WIN32) -#elif defined(_WIN64) - -static void* aligned_ttmem_alloc_large_pages(size_t allocSize) { +static void* aligned_large_pages_alloc_win(size_t allocSize) { HANDLE hProcessToken { }; LUID luid { }; @@ -422,12 +406,13 @@ static void* aligned_ttmem_alloc_large_pages(size_t allocSize) { return mem; } -void* aligned_ttmem_alloc(size_t allocSize, void*& mem) { +void* aligned_large_pages_alloc(size_t allocSize) { static bool firstCall = true; + void* mem; // Try to allocate large pages - mem = aligned_ttmem_alloc_large_pages(allocSize); + mem = aligned_large_pages_alloc_win(allocSize); // Suppress info strings on the first call. The first call occurs before 'uci' // is received and in that case this output confuses some GUIs. @@ -449,23 +434,31 @@ void* aligned_ttmem_alloc(size_t allocSize, void*& mem) { #else -void* aligned_ttmem_alloc(size_t allocSize, void*& mem) { +void* aligned_large_pages_alloc(size_t allocSize) { + +#if defined(__linux__) + constexpr size_t alignment = 2 * 1024 * 1024; // assumed 2MB page size +#else + constexpr size_t alignment = 4096; // assumed small page size +#endif - constexpr size_t alignment = 64; // assumed cache line size - size_t size = allocSize + alignment - 1; // allocate some extra space - mem = malloc(size); - void* ret = reinterpret_cast((uintptr_t(mem) + alignment - 1) & ~uintptr_t(alignment - 1)); - return ret; + // round up to multiples of alignment + size_t size = ((allocSize + alignment - 1) / alignment) * alignment; + void *mem = std_aligned_alloc(alignment, size); +#if defined(MADV_HUGEPAGE) + madvise(mem, size, MADV_HUGEPAGE); +#endif + return mem; } #endif -/// aligned_ttmem_free() will free the previously allocated ttmem +/// aligned_large_pages_free() will free the previously allocated ttmem -#if defined(_WIN64) +#if defined(_WIN32) -void aligned_ttmem_free(void* mem) { +void aligned_large_pages_free(void* mem) { if (mem && !VirtualFree(mem, 0, MEM_RELEASE)) { @@ -478,8 +471,8 @@ void aligned_ttmem_free(void* mem) { #else -void aligned_ttmem_free(void *mem) { - free(mem); +void aligned_large_pages_free(void *mem) { + std_aligned_free(mem); } #endif diff --git a/src/misc.h b/src/misc.h index 68b9c8842b9..bc48f303a88 100644 --- a/src/misc.h +++ b/src/misc.h @@ -33,8 +33,8 @@ void prefetch(void* addr); void start_logger(const std::string& fname); void* std_aligned_alloc(size_t alignment, size_t size); void std_aligned_free(void* ptr); -void* aligned_ttmem_alloc(size_t size, void*& mem); -void aligned_ttmem_free(void* mem); // nop if mem == nullptr +void* aligned_large_pages_alloc(size_t size); // memory aligned by page size, min alignment: 4096 bytes +void aligned_large_pages_free(void* mem); // nop if mem == nullptr void dbg_hit_on(bool b); void dbg_hit_on(bool c, bool b); diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index ed1388812e0..72d182003a2 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -52,7 +52,7 @@ namespace Eval::NNUE { }; // Input feature converter - AlignedPtr feature_transformer; + LargePagePtr feature_transformer; // Evaluation function AlignedPtr network; @@ -70,14 +70,22 @@ namespace Eval::NNUE { std::memset(pointer.get(), 0, sizeof(T)); } + template + void Initialize(LargePagePtr& pointer) { + + static_assert(alignof(T) <= 4096, "aligned_large_pages_alloc() may fail for such a big alignment requirement of T"); + pointer.reset(reinterpret_cast(aligned_large_pages_alloc(sizeof(T)))); + std::memset(pointer.get(), 0, sizeof(T)); + } + // Read evaluation function parameters template - bool ReadParameters(std::istream& stream, const AlignedPtr& pointer) { + bool ReadParameters(std::istream& stream, T& reference) { std::uint32_t header; header = read_little_endian(stream); if (!stream || header != T::GetHashValue()) return false; - return pointer->ReadParameters(stream); + return reference.ReadParameters(stream); } } // namespace Detail @@ -110,8 +118,8 @@ namespace Eval::NNUE { std::string architecture; if (!ReadHeader(stream, &hash_value, &architecture)) return false; if (hash_value != kHashValue) return false; - if (!Detail::ReadParameters(stream, feature_transformer)) return false; - if (!Detail::ReadParameters(stream, network)) return false; + if (!Detail::ReadParameters(stream, *feature_transformer)) return false; + if (!Detail::ReadParameters(stream, *network)) return false; return stream && stream.peek() == std::ios::traits_type::eof(); } diff --git a/src/nnue/evaluate_nnue.h b/src/nnue/evaluate_nnue.h index 5f0d185589e..459a93de7e9 100644 --- a/src/nnue/evaluate_nnue.h +++ b/src/nnue/evaluate_nnue.h @@ -40,9 +40,20 @@ namespace Eval::NNUE { } }; + template + struct TtmemDeleter { + void operator()(T* ptr) const { + ptr->~T(); + aligned_large_pages_free(ptr); + } + }; + template using AlignedPtr = std::unique_ptr>; + template + using LargePagePtr = std::unique_ptr>; + } // namespace Eval::NNUE #endif // #ifndef NNUE_EVALUATE_NNUE_H_INCLUDED diff --git a/src/tt.cpp b/src/tt.cpp index 60a3a5f1d3e..dea7c712c44 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -62,11 +62,12 @@ void TranspositionTable::resize(size_t mbSize) { Threads.main()->wait_for_search_finished(); - aligned_ttmem_free(mem); + aligned_large_pages_free(table); clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster); - table = static_cast(aligned_ttmem_alloc(clusterCount * sizeof(Cluster), mem)); - if (!mem) + + table = static_cast(aligned_large_pages_alloc(clusterCount * sizeof(Cluster))); + if (!table) { std::cerr << "Failed to allocate " << mbSize << "MB for transposition table." << std::endl; diff --git a/src/tt.h b/src/tt.h index fdfd67694e9..6aa066c5650 100644 --- a/src/tt.h +++ b/src/tt.h @@ -73,7 +73,7 @@ class TranspositionTable { static_assert(sizeof(Cluster) == 32, "Unexpected Cluster size"); public: - ~TranspositionTable() { aligned_ttmem_free(mem); } + ~TranspositionTable() { aligned_large_pages_free(table); } void new_search() { generation8 += 8; } // Lower 3 bits are used by PV flag and Bound TTEntry* probe(const Key key, bool& found) const; int hashfull() const; @@ -89,7 +89,6 @@ class TranspositionTable { size_t clusterCount; Cluster* table; - void* mem; uint8_t generation8; // Size must be not bigger than TTEntry::genBound8 }; From 9a64e737cfef639f202787161498ba94466ad730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Wed, 9 Sep 2020 10:49:31 +0200 Subject: [PATCH 0406/1766] Small cleanups 12 - Clean signature of functions in namespace NNUE - Add comment for countermove based pruning - Remove bestMoveCount variable - Add const qualifier to kpp_board_index array - Fix spaces in get_best_thread() - Fix indention in capture LMR code in search.cpp - Rename TtmemDeleter to LargePageDeleter Closes https://github.com/official-stockfish/Stockfish/pull/3063 No functional change --- src/evaluate.cpp | 8 ++++---- src/evaluate.h | 8 +++----- src/main.cpp | 2 +- src/nnue/evaluate_nnue.cpp | 6 +++--- src/nnue/evaluate_nnue.h | 4 ++-- src/nnue/nnue_common.h | 2 +- src/search.cpp | 22 ++++++++++------------ src/search.h | 1 - src/thread.cpp | 20 ++++++++++---------- src/uci.cpp | 2 +- src/ucioption.cpp | 4 ++-- 11 files changed, 37 insertions(+), 42 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index a9159477a07..d3937823fe5 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -60,7 +60,7 @@ namespace Eval { bool useNNUE; string eval_file_loaded = "None"; - /// init_NNUE() tries to load a nnue network at startup time, or when the engine + /// NNUE::init() tries to load a nnue network at startup time, or when the engine /// receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue" /// The name of the nnue network is always retrieved from the EvalFile option. /// We search the given network in three locations: internally (the default @@ -68,7 +68,7 @@ namespace Eval { /// in the engine directory. Distro packagers may define the DEFAULT_NNUE_DIRECTORY /// variable to have the engine search in a special directory in their distro. - void init_NNUE() { + void NNUE::init() { useNNUE = Options["Use NNUE"]; if (!useNNUE) @@ -111,8 +111,8 @@ namespace Eval { } } - /// verify_NNUE() verifies that the last net used was loaded successfully - void verify_NNUE() { + /// NNUE::verify() verifies that the last net used was loaded successfully + void NNUE::verify() { string eval_file = string(Options["EvalFile"]); diff --git a/src/evaluate.h b/src/evaluate.h index c723bd8fa1d..56354cf52bc 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -32,8 +32,6 @@ namespace Eval { extern bool useNNUE; extern std::string eval_file_loaded; - void init_NNUE(); - void verify_NNUE(); // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the @@ -43,9 +41,9 @@ namespace Eval { namespace NNUE { Value evaluate(const Position& pos); - Value compute_eval(const Position& pos); - void update_eval(const Position& pos); - bool load_eval(std::string streamName, std::istream& stream); + bool load_eval(std::string name, std::istream& stream); + void init(); + void verify(); } // namespace NNUE diff --git a/src/main.cpp b/src/main.cpp index f95db1c2f09..e6dff918bd9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -45,7 +45,7 @@ int main(int argc, char* argv[]) { Endgames::init(); Threads.set(size_t(Options["Threads"])); Search::clear(); // After threads are up - Eval::init_NNUE(); + Eval::NNUE::init(); UCI::loop(argc, argv); diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 72d182003a2..b5dcd992fa3 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -30,7 +30,7 @@ namespace Eval::NNUE { - uint32_t kpp_board_index[PIECE_NB][COLOR_NB] = { + const uint32_t kpp_board_index[PIECE_NB][COLOR_NB] = { // convention: W - us, B - them // viewed from other side, W and B are reversed { PS_NONE, PS_NONE }, @@ -136,10 +136,10 @@ namespace Eval::NNUE { } // Load eval, from a file stream or a memory stream - bool load_eval(std::string streamName, std::istream& stream) { + bool load_eval(std::string name, std::istream& stream) { Initialize(); - fileName = streamName; + fileName = name; return ReadParameters(stream); } diff --git a/src/nnue/evaluate_nnue.h b/src/nnue/evaluate_nnue.h index 459a93de7e9..6cacf37e385 100644 --- a/src/nnue/evaluate_nnue.h +++ b/src/nnue/evaluate_nnue.h @@ -41,7 +41,7 @@ namespace Eval::NNUE { }; template - struct TtmemDeleter { + struct LargePageDeleter { void operator()(T* ptr) const { ptr->~T(); aligned_large_pages_free(ptr); @@ -52,7 +52,7 @@ namespace Eval::NNUE { using AlignedPtr = std::unique_ptr>; template - using LargePagePtr = std::unique_ptr>; + using LargePagePtr = std::unique_ptr>; } // namespace Eval::NNUE diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index 7bc905dc751..8afea1866ab 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -113,7 +113,7 @@ namespace Eval::NNUE { PS_END2 = 12 * SQUARE_NB + 1 }; - extern uint32_t kpp_board_index[PIECE_NB][COLOR_NB]; + extern const uint32_t kpp_board_index[PIECE_NB][COLOR_NB]; // Type of input feature after conversion using TransformedFeatureType = std::uint8_t; diff --git a/src/search.cpp b/src/search.cpp index 22cb8577cf7..edc020fd690 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -225,7 +225,7 @@ void MainThread::search() { Time.init(Limits, us, rootPos.game_ply()); TT.new_search(); - Eval::verify_NNUE(); + Eval::NNUE::verify(); if (rootMoves.empty()) { @@ -462,10 +462,7 @@ void Thread::search() { ++failedHighCnt; } else - { - ++rootMoves[pvIdx].bestMoveCount; break; - } delta += delta / 4 + 5; @@ -1218,14 +1215,14 @@ namespace { } else { - // Increase reduction for captures/promotions if late move and at low depth - if (depth < 8 && moveCount > 2) - r++; - - // Unless giving check, this capture is likely bad - if ( !givesCheck - && ss->staticEval + PieceValue[EG][pos.captured_piece()] + 213 * depth <= alpha) - r++; + // Increase reduction for captures/promotions if late move and at low depth + if (depth < 8 && moveCount > 2) + r++; + + // Unless giving check, this capture is likely bad + if ( !givesCheck + && ss->staticEval + PieceValue[EG][pos.captured_piece()] + 213 * depth <= alpha) + r++; } Depth d = std::clamp(newDepth - r, 1, newDepth); @@ -1570,6 +1567,7 @@ namespace { [pos.moved_piece(move)] [to_sq(move)]; + // CounterMove based pruning if ( !captureOrPromotion && moveCount && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < CounterMovePruneThreshold diff --git a/src/search.h b/src/search.h index f60da4a514d..72d43c310dc 100644 --- a/src/search.h +++ b/src/search.h @@ -71,7 +71,6 @@ 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 b46fce5e873..2fbf745d072 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -224,16 +224,16 @@ Thread* ThreadPool::get_best_thread() const { votes[th->rootMoves[0].pv[0]] += (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth); - if (abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY) - { - // Make sure we pick the shortest mate / TB conversion or stave off mate the longest - if (th->rootMoves[0].score > bestThread->rootMoves[0].score) - bestThread = th; - } - else if ( th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY - || ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY - && votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]])) - bestThread = th; + if (abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY) + { + // Make sure we pick the shortest mate / TB conversion or stave off mate the longest + if (th->rootMoves[0].score > bestThread->rootMoves[0].score) + bestThread = th; + } + else if ( th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY + || ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY + && votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]])) + bestThread = th; } return bestThread; diff --git a/src/uci.cpp b/src/uci.cpp index 3f3cc45874f..b63e55adc6d 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -85,7 +85,7 @@ namespace { Position p; p.set(pos.fen(), Options["UCI_Chess960"], &states->back(), Threads.main()); - Eval::verify_NNUE(); + Eval::NNUE::verify(); sync_cout << "\n" << Eval::trace(p) << sync_endl; } diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 5e747a7f13f..bb0b8311475 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -41,8 +41,8 @@ void on_hash_size(const Option& o) { TT.resize(size_t(o)); } void on_logger(const Option& o) { start_logger(o); } void on_threads(const Option& o) { Threads.set(size_t(o)); } void on_tb_path(const Option& o) { Tablebases::init(o); } -void on_use_NNUE(const Option& ) { Eval::init_NNUE(); } -void on_eval_file(const Option& ) { Eval::init_NNUE(); } +void on_use_NNUE(const Option& ) { Eval::NNUE::init(); } +void on_eval_file(const Option& ) { Eval::NNUE::init(); } /// Our case insensitive less() function as required by UCI protocol bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const { From 3d5b2c8a5104888ec4d1ec44c171e29809e836a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Tue, 22 Sep 2020 22:43:41 +0200 Subject: [PATCH 0407/1766] Increase reductions with the number of threads Passed STC with 8 threads: LLR: 2.92 (-2.94,2.94) {-0.25,1.25} Total: 13520 W: 1135 L: 1012 D: 11373 Ptnml(0-2): 39, 815, 4929, 938, 39 https://tests.stockfishchess.org/tests/view/5f68e274ded68c240be73f41 Passed LTC with 8 threads: LLR: 2.96 (-2.94,2.94) {0.25,1.25} Total: 48384 W: 2183 L: 1994 D: 44207 Ptnml(0-2): 28, 1777, 20402, 1948, 37 https://tests.stockfishchess.org/tests/view/5f68f068ded68c240be747e9 closes https://github.com/official-stockfish/Stockfish/pull/3142 No functional change (for one thread) --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index edc020fd690..4650b1579bf 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -192,7 +192,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int((22.0 + std::log(Threads.size())) * std::log(i)); + Reductions[i] = int((22.0 + 2 * std::log(Threads.size())) * std::log(i)); } From 5e6a5e48e636babe1c2ba1fc63422e84c0eee942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Thu, 24 Sep 2020 11:38:35 +0200 Subject: [PATCH 0408/1766] Suppress info strings before 'uci' On Windows, Stockfish wouldn't launch in some GUI because we output some info strings (about the use of large pages) before sending the 'uci' command. It seems more robust to suppress these info strings, and instead to add a proper section section in the Readme about large pages use. fixes https://github.com/official-stockfish/Stockfish/issues/3052 closes https://github.com/official-stockfish/Stockfish/pull/3147 No functional change --- README.md | 16 ++++++++-------- src/misc.cpp | 16 +--------------- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 255ebce2afc..409d0a1036c 100644 --- a/README.md +++ b/README.md @@ -198,8 +198,8 @@ the 50-move rule. Stockfish supports large pages on Linux and Windows. Large pages make the hash access more efficient, improving the engine speed, especially -on large hash sizes. Typical increases are 5..10% in terms of nps, but -speed increases up to 30% have been measured. The support is +on large hash sizes. Typical increases are 5..10% in terms of nodes per +second, but speed increases up to 30% have been measured. The support is automatic. Stockfish attempts to use large pages when available and will fall back to regular memory allocation when this is not the case. @@ -213,11 +213,11 @@ are already enabled and no configuration is needed. The use of large pages requires "Lock Pages in Memory" privilege. See [Enable the Lock Pages in Memory Option (Windows)](https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/enable-the-lock-pages-in-memory-option-windows) -on how to enable this privilege. Logout/login may be needed -afterwards. Due to memory fragmentation, it may not always be -possible to allocate large pages even when enabled. A reboot -might alleviate this problem. To determine whether large pages -are in use, see the engine log. +on how to enable this privilege, then run [RAMMap](https://docs.microsoft.com/en-us/sysinternals/downloads/rammap) +to double-check that large pages are used. We suggest that you reboot +your computer after you have enabled large pages, because long Windows +sessions suffer from memory fragmentation which may prevent Stockfish +from getting large pages: a fresh session is better in this regard. ## Compiling Stockfish yourself from the sources @@ -232,8 +232,8 @@ targets with corresponding descriptions. ``` cd src make help - make build ARCH=x86-64-modern make net + make build ARCH=x86-64-modern ``` When not using the Makefile to compile (for instance with Microsoft MSVC) you diff --git a/src/misc.cpp b/src/misc.cpp index d9bc47e3c88..a16a6e90a3d 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -408,22 +408,8 @@ static void* aligned_large_pages_alloc_win(size_t allocSize) { void* aligned_large_pages_alloc(size_t allocSize) { - static bool firstCall = true; - void* mem; - // Try to allocate large pages - mem = aligned_large_pages_alloc_win(allocSize); - - // Suppress info strings on the first call. The first call occurs before 'uci' - // is received and in that case this output confuses some GUIs. - if (!firstCall) - { - if (mem) - sync_cout << "info string Hash table allocation: Windows large pages used." << sync_endl; - else - sync_cout << "info string Hash table allocation: Windows large pages not used." << sync_endl; - } - firstCall = false; + void* mem = aligned_large_pages_alloc_win(allocSize); // Fall back to regular, page aligned, allocation if necessary if (!mem) From f66c381f11b8603e2449b200227c8cfd7382b3ba Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Wed, 23 Sep 2020 14:00:42 +0800 Subject: [PATCH 0409/1766] Switch to NNUE eval probabilistically for OCB Introduce a small chance of switching to NNUE if PSQ imbalance is large but we have opposite colored bishops and the classical eval is struggling to win. STC: LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 25304 W: 3179 L: 2983 D: 19142 Ptnml(0-2): 172, 2171, 7781, 2345, 183 https://tests.stockfishchess.org/tests/view/5f6b14dec7759d4ee307cfe3 LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.25} Total: 84680 W: 4846 L: 4556 D: 75278 Ptnml(0-2): 89, 3933, 34011, 4213, 94 https://tests.stockfishchess.org/tests/view/5f6b3fb6c7759d4ee307cff9 closes https://github.com/official-stockfish/Stockfish/pull/3146 Bench: 3865413 --- src/evaluate.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d3937823fe5..1503be2d20b 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1021,10 +1021,10 @@ Value Eval::evaluate(const Position& pos) { v = Evaluation(pos).value(); else { - // scale and shift NNUE for compatibility with search and classical evaluation + // Scale and shift NNUE for compatibility with search and classical evaluation auto adjusted_NNUE = [&](){ return NNUE::evaluate(pos) * 5 / 4 + Tempo; }; - // if there is PSQ imbalance use classical eval, with small probability if it is small + // If there is PSQ imbalance use classical eval, with small probability if it is small Value psq = Value(abs(eg_value(pos.psq_score()))); int r50 = 16 + pos.rule50_count(); bool largePsq = psq * 16 > (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50; @@ -1032,9 +1032,14 @@ Value Eval::evaluate(const Position& pos) { v = classical ? Evaluation(pos).value() : adjusted_NNUE(); - // if the classical eval is small and imbalance large, use NNUE nevertheless. + // If the classical eval is small and imbalance large, use NNUE nevertheless. + // For the case of opposite colored bishops, switch to NNUE eval with + // small probability if the classical eval is less than the threshold. if ( largePsq - && abs(v) * 16 < NNUEThreshold2 * r50) + && (abs(v) * 16 < NNUEThreshold2 * r50 + || ( pos.opposite_bishops() + && abs(v) * 16 < (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50 + && !(pos.this_thread()->nodes & 0xB)))) v = adjusted_NNUE(); } From 1dbd2a1ad548b3ca676f7da949e1a998c64b836b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sat, 26 Sep 2020 23:19:53 +0200 Subject: [PATCH 0410/1766] Tweak nnue scaling to keep more material Current master uses a constant scale factor of 5/4 = 1.25 for the output of the NNUE network, for compatibility with search and classical evaluation. We modify this scale factor to make it dependent on the phase of the game, going from about 1.5 in the opening to 1.0 for pure pawn endgames. This helps Stockfish to avoid exchanges of pieces (heavy pieces in particular) when she has the advantage, keeping more material on the board when attacking. Passed STC: LLR: 2.95 (-2.94,2.94) {-0.25,1.25} Total: 14744 W: 1771 L: 1599 D: 11374 Ptnml(0-2): 87, 1184, 4664, 1344, 93 https://tests.stockfishchess.org/tests/view/5f6fb0a63b22d6afa506904f Passed LTC: LLR: 2.95 (-2.94,2.94) {0.25,1.25} Total: 8912 W: 512 L: 393 D: 8007 Ptnml(0-2): 7, 344, 3637, 459, 9 https://tests.stockfishchess.org/tests/view/5f6fcf533b22d6afa5069066 closes https://github.com/official-stockfish/Stockfish/pull/3154 Bench: 3943952 --- src/evaluate.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 1503be2d20b..710898bc15d 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1022,7 +1022,10 @@ Value Eval::evaluate(const Position& pos) { else { // Scale and shift NNUE for compatibility with search and classical evaluation - auto adjusted_NNUE = [&](){ return NNUE::evaluate(pos) * 5 / 4 + Tempo; }; + auto adjusted_NNUE = [&](){ + int mat = pos.non_pawn_material(); + return NNUE::evaluate(pos) * (1024 + mat / 32) / 1024 + Tempo; + }; // If there is PSQ imbalance use classical eval, with small probability if it is small Value psq = Value(abs(eg_value(pos.psq_score()))); @@ -1037,7 +1040,7 @@ Value Eval::evaluate(const Position& pos) { // small probability if the classical eval is less than the threshold. if ( largePsq && (abs(v) * 16 < NNUEThreshold2 * r50 - || ( pos.opposite_bishops() + || ( pos.opposite_bishops() && abs(v) * 16 < (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50 && !(pos.this_thread()->nodes & 0xB)))) v = adjusted_NNUE(); From c065abdcafe0486cb5cfa7de12a4ac6a905a54c5 Mon Sep 17 00:00:00 2001 From: noobpwnftw Date: Mon, 28 Sep 2020 02:29:21 +0800 Subject: [PATCH 0411/1766] Use incremental updates more often Use incremental updates for accumulators for up to 2 plies. Do not copy accumulator. About 2% speedup. Passed STC: LLR: 2.95 (-2.94,2.94) {-0.25,1.25} Total: 21752 W: 2583 L: 2403 D: 16766 Ptnml(0-2): 128, 1761, 6923, 1931, 133 https://tests.stockfishchess.org/tests/view/5f7150cf3b22d6afa5069412 closes https://github.com/official-stockfish/Stockfish/pull/3157 No functional change --- src/nnue/features/feature_set.h | 83 ++++++++++++++++++++++------- src/nnue/features/half_kp.cpp | 3 +- src/nnue/features/half_kp.h | 2 +- src/nnue/nnue_feature_transformer.h | 29 +++++++--- 4 files changed, 87 insertions(+), 30 deletions(-) diff --git a/src/nnue/features/feature_set.h b/src/nnue/features/feature_set.h index 558a6b22877..26198114a30 100644 --- a/src/nnue/features/feature_set.h +++ b/src/nnue/features/feature_set.h @@ -61,26 +61,69 @@ namespace Eval::NNUE::Features { const PositionType& pos, TriggerEvent trigger, IndexListType removed[2], IndexListType added[2], bool reset[2]) { - const auto& dp = pos.state()->dirtyPiece; - if (dp.dirty_num == 0) return; - - for (Color perspective : { WHITE, BLACK }) { - reset[perspective] = false; - switch (trigger) { - case TriggerEvent::kFriendKingMoved: - reset[perspective] = dp.piece[0] == make_piece(perspective, KING); - break; - default: - assert(false); - break; + auto collect_for_one = [&](const DirtyPiece& dp) { + for (Color perspective : { WHITE, BLACK }) { + switch (trigger) { + case TriggerEvent::kFriendKingMoved: + reset[perspective] = dp.piece[0] == make_piece(perspective, KING); + break; + default: + assert(false); + break; + } + if (reset[perspective]) { + Derived::CollectActiveIndices( + pos, trigger, perspective, &added[perspective]); + } else { + Derived::CollectChangedIndices( + pos, dp, trigger, perspective, + &removed[perspective], &added[perspective]); + } } - if (reset[perspective]) { - Derived::CollectActiveIndices( - pos, trigger, perspective, &added[perspective]); + }; + + auto collect_for_two = [&](const DirtyPiece& dp1, const DirtyPiece& dp2) { + for (Color perspective : { WHITE, BLACK }) { + switch (trigger) { + case TriggerEvent::kFriendKingMoved: + reset[perspective] = dp1.piece[0] == make_piece(perspective, KING) + || dp2.piece[0] == make_piece(perspective, KING); + break; + default: + assert(false); + break; + } + if (reset[perspective]) { + Derived::CollectActiveIndices( + pos, trigger, perspective, &added[perspective]); + } else { + Derived::CollectChangedIndices( + pos, dp1, trigger, perspective, + &removed[perspective], &added[perspective]); + Derived::CollectChangedIndices( + pos, dp2, trigger, perspective, + &removed[perspective], &added[perspective]); + } + } + }; + + if (pos.state()->previous->accumulator.computed_accumulation) { + const auto& prev_dp = pos.state()->dirtyPiece; + if (prev_dp.dirty_num == 0) return; + collect_for_one(prev_dp); + } else { + const auto& prev_dp = pos.state()->previous->dirtyPiece; + if (prev_dp.dirty_num == 0) { + const auto& prev2_dp = pos.state()->dirtyPiece; + if (prev2_dp.dirty_num == 0) return; + collect_for_one(prev2_dp); } else { - Derived::CollectChangedIndices( - pos, trigger, perspective, - &removed[perspective], &added[perspective]); + const auto& prev2_dp = pos.state()->dirtyPiece; + if (prev2_dp.dirty_num == 0) { + collect_for_one(prev_dp); + } else { + collect_for_two(prev_dp, prev2_dp); + } } } } @@ -115,11 +158,11 @@ namespace Eval::NNUE::Features { // Get a list of indices for recently changed features static void CollectChangedIndices( - const Position& pos, const TriggerEvent trigger, const Color perspective, + const Position& pos, const DirtyPiece& dp, const TriggerEvent trigger, const Color perspective, IndexList* const removed, IndexList* const added) { if (FeatureType::kRefreshTrigger == trigger) { - FeatureType::AppendChangedIndices(pos, perspective, removed, added); + FeatureType::AppendChangedIndices(pos, dp, perspective, removed, added); } } diff --git a/src/nnue/features/half_kp.cpp b/src/nnue/features/half_kp.cpp index 88e384a3578..116157cc19d 100644 --- a/src/nnue/features/half_kp.cpp +++ b/src/nnue/features/half_kp.cpp @@ -52,11 +52,10 @@ namespace Eval::NNUE::Features { // Get a list of indices for recently changed features template void HalfKP::AppendChangedIndices( - const Position& pos, Color perspective, + const Position& pos, const DirtyPiece& dp, Color perspective, IndexList* removed, IndexList* added) { Square ksq = orient(perspective, pos.square(perspective)); - const auto& dp = pos.state()->dirtyPiece; for (int i = 0; i < dp.dirty_num; ++i) { Piece pc = dp.piece[i]; if (type_of(pc) == KING) continue; diff --git a/src/nnue/features/half_kp.h b/src/nnue/features/half_kp.h index ee6a8df39ae..52a83eecf3b 100644 --- a/src/nnue/features/half_kp.h +++ b/src/nnue/features/half_kp.h @@ -50,7 +50,7 @@ namespace Eval::NNUE::Features { IndexList* active); // Get a list of indices for recently changed features - static void AppendChangedIndices(const Position& pos, Color perspective, + static void AppendChangedIndices(const Position& pos, const DirtyPiece& dp, Color perspective, IndexList* removed, IndexList* added); private: diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index e71ee60d45a..2f86d20a639 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -127,9 +127,14 @@ namespace Eval::NNUE { return true; const auto prev = now->previous; - if (prev && prev->accumulator.computed_accumulation) { - UpdateAccumulator(pos); - return true; + if (prev) { + if (prev->accumulator.computed_accumulation) { + UpdateAccumulator(pos); + return true; + } else if (prev->previous && prev->previous->accumulator.computed_accumulation) { + UpdateAccumulator(pos); + return true; + } } return false; @@ -289,11 +294,21 @@ namespace Eval::NNUE { // Calculate cumulative value using difference calculation void UpdateAccumulator(const Position& pos) const { - const auto prev_accumulator = pos.state()->previous->accumulator; + Accumulator* prev_accumulator; + assert(pos.state()->previous); + if (pos.state()->previous->accumulator.computed_accumulation) { + prev_accumulator = &pos.state()->previous->accumulator; + } + else { + assert(pos.state()->previous->previous); + assert(pos.state()->previous->previous->accumulator.computed_accumulation); + prev_accumulator = &pos.state()->previous->previous->accumulator; + } + auto& accumulator = pos.state()->accumulator; IndexType i = 0; Features::IndexList removed_indices[2], added_indices[2]; - bool reset[2]; + bool reset[2] = { false, false }; RawFeatures::AppendChangedIndices(pos, kRefreshTriggers[i], removed_indices, added_indices, reset); @@ -311,7 +326,7 @@ namespace Eval::NNUE { acc[k] = biasesTile[k]; } else { auto prevAccTile = reinterpret_cast( - &prev_accumulator.accumulation[perspective][i][j * kTileHeight]); + &prev_accumulator->accumulation[perspective][i][j * kTileHeight]); for (IndexType k = 0; k < kNumRegs; ++k) acc[k] = vec_load(&prevAccTile[k]); @@ -350,7 +365,7 @@ namespace Eval::NNUE { kHalfDimensions * sizeof(BiasType)); } else { std::memcpy(accumulator.accumulation[perspective][i], - prev_accumulator.accumulation[perspective][i], + prev_accumulator->accumulation[perspective][i], kHalfDimensions * sizeof(BiasType)); // Difference calculation for the deactivated features for (const auto index : removed_indices[perspective]) { From 36c2886302ff3f6b730fc5f69d738a5d61be8c46 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 26 Sep 2020 17:47:52 +0200 Subject: [PATCH 0412/1766] Update default net to nn-04a843f8932e.nnue an optimization of Sergio's nn-03744f8d56d8.nnue tuning the output layer (33 parameters) on game play. WIP code to make layer parameters tunable is https://github.com/vondele/Stockfish/tree/optionOutput Optimization itself is using https://github.com/vondele/nevergrad4sf Writing of the modified net using WIP code based on the learner code https://github.com/vondele/Stockfish/tree/evalWrite Most parameters in the output layer are changed only little (~5 for int8_t). passed STC: https://tests.stockfishchess.org/tests/view/5f716f6b3b22d6afa506941a LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 15488 W: 1859 L: 1689 D: 11940 Ptnml(0-2): 79, 1260, 4917, 1388, 100 passed LTC: https://tests.stockfishchess.org/tests/view/5f71908e3b22d6afa506942e LLR: 2.93 (-2.94,2.94) {0.25,1.25} Total: 8728 W: 518 L: 400 D: 7810 Ptnml(0-2): 7, 338, 3556, 456, 7 closes https://github.com/official-stockfish/Stockfish/pull/3158 Bench: 3789924 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 56354cf52bc..503aa975b84 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -36,7 +36,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-03744f8d56d8.nnue" + #define EvalFileDefaultName "nn-04a843f8932e.nnue" namespace NNUE { From a5e68d9b25539b86304a9fb26afc616dc8126a1c Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Mon, 28 Sep 2020 20:20:06 +0300 Subject: [PATCH 0413/1766] Adjust null move pruning constants Idea is that division by fraction of 2 is slightly faster than by other numbers so parameters are adjusted in a way that division in null move pruning depth reduction features dividing by 256 instead of dividing by 213. Other than this patch is almost non-functional - difference starts to exist by depth 133. passed STC https://tests.stockfishchess.org/tests/view/5f70dd943b22d6afa50693c5 LLR: 2.95 (-2.94,2.94) {-0.25,1.25} Total: 57048 W: 6616 L: 6392 D: 44040 Ptnml(0-2): 304, 4583, 18531, 4797, 309 passed LTC https://tests.stockfishchess.org/tests/view/5f7180db3b22d6afa506941f LLR: 2.95 (-2.94,2.94) {0.25,1.25} Total: 45960 W: 2419 L: 2229 D: 41312 Ptnml(0-2): 43, 1779, 19137, 1987, 34 closes https://github.com/official-stockfish/Stockfish/pull/3159 bench 3789924 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 4650b1579bf..e5f286e4597 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -829,7 +829,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and value - Depth R = (817 + 71 * depth) / 213 + std::min(int(eval - beta) / 192, 3); + Depth R = (982 + 85 * depth) / 256 + std::min(int(eval - beta) / 192, 3); ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; From ba46599aa2224a78106346fb0615b0be174374f5 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Mon, 28 Sep 2020 22:09:43 +0300 Subject: [PATCH 0414/1766] Tweaking Mobility and Safe Check Passed STC: https://tests.stockfishchess.org/tests/view/5f70d86d3b22d6afa50693b9 LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 100368 W: 20323 L: 19914 D: 60131 Ptnml(0-2): 1927, 11641, 22605, 12118, 1893 Passed LTC: https://tests.stockfishchess.org/tests/view/5f71bb553b22d6afa5069457 LLR: 2.94 (-2.94,2.94) {0.25,1.25} Total: 77648 W: 10613 L: 10181 D: 56854 Ptnml(0-2): 634, 7280, 22594, 7652, 664 closes https://github.com/official-stockfish/Stockfish/pull/3160 Bench: 3861984 --- src/evaluate.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 710898bc15d..fe92f7d7bcd 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -199,7 +199,7 @@ namespace { // SafeCheck[PieceType][single/multiple] contains safe check bonus by piece type, // higher if multiple safe checks are possible for that piece type. constexpr int SafeCheck[][2] = { - {}, {}, {792, 1283}, {645, 967}, {1084, 1897}, {772, 1119} + {}, {}, {803, 1292}, {639, 974}, {1087, 1878}, {759, 1132} }; #define S(mg, eg) make_score(mg, eg) @@ -207,19 +207,19 @@ namespace { // MobilityBonus[PieceType-2][attacked] contains bonuses for middle and end game, // indexed by piece type and number of attacked squares in the mobility area. constexpr Score MobilityBonus[][32] = { - { S(-62,-81), S(-53,-56), S(-12,-31), S( -4,-16), S( 3, 5), S( 13, 11), // Knight - S( 22, 17), S( 28, 20), S( 33, 25) }, - { S(-48,-59), S(-20,-23), S( 16, -3), S( 26, 13), S( 38, 24), S( 51, 42), // Bishop - S( 55, 54), S( 63, 57), S( 63, 65), S( 68, 73), S( 81, 78), S( 81, 86), - S( 91, 88), S( 98, 97) }, - { S(-60,-78), S(-20,-17), S( 2, 23), S( 3, 39), S( 3, 70), S( 11, 99), // Rook - S( 22,103), S( 31,121), S( 40,134), S( 40,139), S( 41,158), S( 48,164), - S( 57,168), S( 57,169), S( 62,172) }, - { S(-30,-48), S(-12,-30), S( -8, -7), S( -9, 19), S( 20, 40), S( 23, 55), // Queen - S( 23, 59), S( 35, 75), S( 38, 78), S( 53, 96), S( 64, 96), S( 65,100), - S( 65,121), S( 66,127), S( 67,131), S( 67,133), S( 72,136), S( 72,141), - S( 77,147), S( 79,150), S( 93,151), S(108,168), S(108,168), S(108,171), - S(110,182), S(114,182), S(114,192), S(116,219) } + { S(-62,-79), S(-53,-57), S(-12,-31), S( -3,-17), S( 3, 7), S( 12, 13), // Knight + S( 21, 16), S( 28, 21), S( 37, 26) }, + { S(-47,-59), S(-20,-25), S( 14, -8), S( 29, 12), S( 39, 21), S( 53, 40), // Bishop + S( 53, 56), S( 60, 58), S( 62, 65), S( 69, 72), S( 78, 78), S( 83, 87), + S( 91, 88), S( 96, 98) }, + { S(-61,-82), S(-20,-17), S( 2, 23) ,S( 3, 40), S( 4, 72), S( 11,100), // Rook + S( 22,104), S( 31,120), S( 39,134), S(40 ,138), S( 41,158), S( 47,163), + S( 59,168), S( 60,169), S( 64,173) }, + { S(-29,-49), S(-16,-29), S( -8, -8), S( -8, 17), S( 18, 39), S( 25, 54), // Queen + S( 23, 59), S( 37, 73), S( 41, 76), S( 54, 95), S( 65, 95) ,S( 68,101), + S( 69,124), S( 70,128), S( 70,132), S( 70,133) ,S( 71,136), S( 72,140), + S( 74,147), S( 76,149), S( 90,153), S(104,169), S(105,171), S(106,171), + S(112,178), S(114,185), S(114,187), S(119,221) } }; // KingProtector[knight/bishop] contains penalty for each distance unit to own king From 5efbaaba77b338dae7121c41f6590f6abc96912c Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Tue, 29 Sep 2020 02:24:26 +0800 Subject: [PATCH 0415/1766] Update default net to nn-baeb9ef2d183.nnue Further optimization of Sergio's nn-03744f8d56d8.nnue This patch is the result of collaboration with Joost VandeVondele. STC: LLR: 2.96 (-2.94,2.94) {-0.25,1.25} Total: 37000 W: 4145 L: 3947 D: 28908 Ptnml(0-2): 191, 3016, 11912, 3166, 215 https://tests.stockfishchess.org/tests/view/5f71e7983b22d6afa5069475 LTC: LLR: 2.96 (-2.94,2.94) {0.25,1.25} Total: 60224 W: 2992 L: 2769 D: 54463 Ptnml(0-2): 48, 2420, 24956, 2637, 51 https://tests.stockfishchess.org/tests/view/5f722bb83b22d6afa506998f closes https://github.com/official-stockfish/Stockfish/pull/3161 Bench: 3720073 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 503aa975b84..4b57a050110 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -36,7 +36,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-04a843f8932e.nnue" + #define EvalFileDefaultName "nn-baeb9ef2d183.nnue" namespace NNUE { From 6f0aa186d8c9ead30a107634c438c6339b9cba09 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Mon, 28 Sep 2020 22:21:14 +0200 Subject: [PATCH 0416/1766] Tweak reduction formula. Replace log(i) with log(i + 0.25 * log(i)). This increases especially for low values the reductions. But for bigger values there are nearly no changes. STC: LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 49640 W: 5505 L: 5289 D: 38846 Ptnml(0-2): 270, 4074, 15924, 4274, 278 https://tests.stockfishchess.org/tests/view/5f71f04d3b22d6afa5069478 LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.25} Total: 43856 W: 2209 L: 2021 D: 39626 Ptnml(0-2): 32, 1776, 18128, 1956, 36 https://tests.stockfishchess.org/tests/view/5f7232ee3b22d6afa50699a2 closes https://github.com/official-stockfish/Stockfish/pull/3163 Bench: 3555769 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index e5f286e4597..c7343ce8002 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -192,7 +192,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int((22.0 + 2 * std::log(Threads.size())) * std::log(i)); + Reductions[i] = int((22.0 + 2 * std::log(Threads.size())) * std::log(i + 0.25 * std::log(i))); } From 5af09cfda5b71f9470ef233298e0f4233651337d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Mon, 28 Sep 2020 22:32:55 +0200 Subject: [PATCH 0417/1766] Include pawns in NNUE scaling We now include the total pawn count in the scaling factor for the output of the NNUE evaluation network. This should have the effect of trying to keep more pawns when SF has the advantage, but exchange them when she is defending. Thanks to Alexander Pagel (Lolligerhans) for the idea of using the value of pawns to ease the comparison with the rest of the material estimation. Passed STC: LLR: 2.93 (-2.94,2.94) {-0.25,1.25} Total: 15072 W: 1700 L: 1539 D: 11833 Ptnml(0-2): 65, 1202, 4845, 1355, 69 https://tests.stockfishchess.org/tests/view/5f7235a63b22d6afa50699b3 Passed LTC: LLR: 2.93 (-2.94,2.94) {0.25,1.25} Total: 25880 W: 1270 L: 1124 D: 23486 Ptnml(0-2): 23, 980, 10788, 1126, 23 https://tests.stockfishchess.org/tests/view/5f723b483b22d6afa5069a99 closes https://github.com/official-stockfish/Stockfish/pull/3164 Bench: 3776081 --- src/evaluate.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index fe92f7d7bcd..25e3bdc1e25 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1023,8 +1023,8 @@ Value Eval::evaluate(const Position& pos) { { // Scale and shift NNUE for compatibility with search and classical evaluation auto adjusted_NNUE = [&](){ - int mat = pos.non_pawn_material(); - return NNUE::evaluate(pos) * (1024 + mat / 32) / 1024 + Tempo; + int mat = pos.non_pawn_material() + PieceValue[MG][PAWN] * pos.count(); + return NNUE::evaluate(pos) * (720 + mat / 32) / 1024 + Tempo; }; // If there is PSQ imbalance use classical eval, with small probability if it is small From 9382f854b3a67c5a970ad3342a3c12454974eccd Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 30 Sep 2020 21:22:36 +0200 Subject: [PATCH 0418/1766] Schedule threads fairly under valgrind fixes a rare case that can cause CI to fail when running multithreaded under valgrind. closes https://github.com/official-stockfish/Stockfish/pull/3165 No functional change. --- tests/instrumented.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/instrumented.sh b/tests/instrumented.sh index 03ded74abfd..03e9c9de093 100755 --- a/tests/instrumented.sh +++ b/tests/instrumented.sh @@ -20,7 +20,7 @@ case $1 in --valgrind-thread) echo "valgrind-thread testing started" prefix='' - exeprefix='valgrind --error-exitcode=42' + exeprefix='valgrind --fair-sched=try --error-exitcode=42' postfix='1>/dev/null' threads="2" ;; From 17fb3a8ce0ccd2532f667fe685c4189d0bfe3b5b Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Fri, 2 Oct 2020 22:00:55 +0200 Subject: [PATCH 0419/1766] Simplify away futility pruning for captures Remove futility pruning for captures. STC https://tests.stockfishchess.org/tests/view/5f749bfed930428c36d34c56 LLR: 2.94 (-2.94,2.94) {-1.25,0.25} Total: 38064 W: 4011 L: 3929 D: 30124 Ptnml(0-2): 192, 3004, 12567, 3068, 201 LTC https://tests.stockfishchess.org/tests/view/5f74d99bf18675b1ce2f7412 LLR: 2.94 (-2.94,2.94) {-0.75,0.25} Total: 184984 W: 8567 L: 8610 D: 167807 Ptnml(0-2): 146, 7593, 77058, 7548, 147 closes https://github.com/official-stockfish/Stockfish/pull/3166 bench: 3890648 --- src/search.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c7343ce8002..eaa79fb92bf 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1049,15 +1049,6 @@ namespace { && captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] < 0) continue; - // Futility pruning for captures - if ( !givesCheck - && lmrDepth < 6 - && !(PvNode && abs(bestValue) < 2) - && !ss->inCheck - && ss->staticEval + 169 + 244 * lmrDepth - + PieceValue[MG][type_of(pos.piece_on(to_sq(move)))] <= alpha) - continue; - // See based pruning if (!pos.see_ge(move, Value(-221) * depth)) // (~25 Elo) continue; From 767b4f4fbe5ab2e63aceabd9005f4e1eb7cbcb51 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Fri, 2 Oct 2020 15:32:19 +0300 Subject: [PATCH 0420/1766] Pawn Tuning Tuning of pawns, for classical evaluation: Passed STC: https://tests.stockfishchess.org/tests/view/5f771f0e52560f5fc78559ec LLR: 2.96 (-2.94,2.94) {-0.25,1.25} Total: 252696 W: 50321 L: 49692 D: 152683 Ptnml(0-2): 4614, 29845, 57049, 29978, 4862 Passed LTC: https://tests.stockfishchess.org/tests/view/5f77cfef090dcf9aaa16d38b LLR: 2.94 (-2.94,2.94) {0.25,1.25} Total: 48184 W: 6556 L: 6193 D: 35435 Ptnml(0-2): 335, 4516, 14100, 4733, 408 closes https://github.com/official-stockfish/Stockfish/pull/3169 bench: 4016121 --- src/pawns.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index af0f6618103..a5102db8aeb 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -30,21 +30,21 @@ namespace { #define S(mg, eg) make_score(mg, eg) // Pawn penalties - constexpr Score Backward = S( 8, 27); - constexpr Score Doubled = S(11, 55); - constexpr Score Isolated = S( 5, 17); - constexpr Score WeakLever = S( 2, 54); - constexpr Score WeakUnopposed = S(15, 25); + constexpr Score Backward = S( 8, 25); + constexpr Score Doubled = S(10, 55); + constexpr Score Isolated = S( 3, 15); + constexpr Score WeakLever = S( 3, 55); + constexpr Score WeakUnopposed = S(13, 25); // Bonus for blocked pawns at 5th or 6th rank - constexpr Score BlockedPawn[2] = { S(-13, -4), S(-4, 3) }; + constexpr Score BlockedPawn[2] = { S(-13, -4), S(-5, 2) }; constexpr Score BlockedStorm[RANK_NB] = { S(0, 0), S(0, 0), S(76, 78), S(-10, 15), S(-7, 10), S(-4, 6), S(-1, 2) }; // Connected pawn bonus - constexpr int Connected[RANK_NB] = { 0, 7, 8, 11, 24, 45, 85 }; + constexpr int Connected[RANK_NB] = { 0, 5, 7, 11, 24, 48, 86 }; // Strength of pawn shelter for our king by [distance from edge][rank]. // RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king. @@ -147,7 +147,7 @@ namespace { if (support | phalanx) { int v = Connected[r] * (2 + bool(phalanx) - bool(opposed)) - + 21 * popcount(support); + + 22 * popcount(support); score += make_score(v, v * (r - 2) / 4); } From ba73f8ce0d545a0f627b5bc8ba274ae9c85918f3 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 14 Oct 2020 10:23:30 +0200 Subject: [PATCH 0421/1766] Update default net to nn-04cf2b4ed1da.nnue Further tune the net parameters, now the last but one layer (32x32). To limit the number of parameters optimized, the network layer was decomposed using SVD, and the singular values were treated as parameters and tuned. Tuning branch: https://github.com/vondele/Stockfish/tree/svdTune Tuner: https://github.com/vondele/nevergrad4sf passed STC: https://tests.stockfishchess.org/tests/view/5f83e82f8ea73fb8ddf83e4e LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 8488 W: 944 L: 795 D: 6749 Ptnml(0-2): 39, 609, 2811, 734, 51 passed LTC: https://tests.stockfishchess.org/tests/view/5f83f4118ea73fb8ddf83e66 LLR: 2.94 (-2.94,2.94) {0.25,1.25} Total: 169016 W: 8043 L: 7589 D: 153384 Ptnml(0-2): 133, 6623, 70538, 7085, 129 closes https://github.com/official-stockfish/Stockfish/pull/3181 Bench: 3945198 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 4b57a050110..6a17f284b4c 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -36,7 +36,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-baeb9ef2d183.nnue" + #define EvalFileDefaultName "nn-04cf2b4ed1da.nnue" namespace NNUE { From 4a5cc1365f48f7fff08d3184cadac7a0a75dda6d Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Tue, 6 Oct 2020 22:43:48 +0300 Subject: [PATCH 0422/1766] RookOnQueenFile Removal Removing Rook On Queen File looks beneficial, and it might even bring some ELO. I will try to reintroduce it with a different method later on. Passed STC: https://tests.stockfishchess.org/tests/view/5f7cea204389873867eb10cb LLR: 2.94 (-2.94,2.94) {-1.25,0.25} Total: 18624 W: 3800 L: 3568 D: 11256 Ptnml(0-2): 308, 2131, 4257, 2253, 363 Passed LTC: https://tests.stockfishchess.org/tests/view/5f7d76a4e936c6892bf50598 LLR: 2.95 (-2.94,2.94) {-0.75,0.25} Total: 117864 W: 15515 L: 15340 D: 87009 Ptnml(0-2): 926, 11127, 34671, 11262, 946 closes https://github.com/official-stockfish/Stockfish/pull/3176 Bench: 3756191 --- src/evaluate.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 25e3bdc1e25..c68577a3e0e 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -265,7 +265,6 @@ namespace { constexpr Score ReachableOutpost = S( 31, 22); constexpr Score RestrictedPiece = S( 7, 7); constexpr Score RookOnKingRing = S( 16, 0); - constexpr Score RookOnQueenFile = S( 6, 11); constexpr Score SliderOnQueen = S( 60, 18); constexpr Score ThreatByKing = S( 24, 89); constexpr Score ThreatByPawnPush = S( 48, 39); @@ -481,10 +480,6 @@ namespace { if (Pt == ROOK) { - // 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 if (pos.is_on_semiopen_file(Us, s)) score += RookOnFile[pos.is_on_semiopen_file(Them, s)]; From 288a604411fa72b06b30f16194cd03592b28f6f2 Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Mon, 12 Oct 2020 09:03:49 +0200 Subject: [PATCH 0423/1766] Scale factor tweak Add !pawnsOnBothFlanks heuristic to scale factor. STC https://tests.stockfishchess.org/tests/view/5f8080575b3847b5d41f9134 LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 250960 W: 49779 L: 49168 D: 152013 Ptnml(0-2): 4224, 28822, 58802, 29383, 4249 LTC https://tests.stockfishchess.org/tests/view/5f832f498ea73fb8ddf83ddb LLR: 2.95 (-2.94,2.94) {0.25,1.25} Total: 88584 W: 11827 L: 11388 D: 65369 Ptnml(0-2): 585, 8079, 26578, 8412, 638 closes https://github.com/official-stockfish/Stockfish/pull/3179 bench: 3834252 --- src/evaluate.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index c68577a3e0e..425ba6f8fae 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -905,7 +905,9 @@ namespace { sf = 37 + 3 * (pos.count(WHITE) == 1 ? pos.count(BLACK) + pos.count(BLACK) : pos.count(WHITE) + pos.count(WHITE)); else - sf = std::min(sf, 36 + 7 * pos.count(strongSide)); + sf = std::min(sf, 36 + 7 * pos.count(strongSide)) - 4 * !pawnsOnBothFlanks; + + sf -= 4 * !pawnsOnBothFlanks; } // Interpolate between the middlegame and (scaled by 'sf') endgame score From 281d520cc2bb0123efd230fce45119b57f0bae0d Mon Sep 17 00:00:00 2001 From: mstembera Date: Sun, 18 Oct 2020 04:23:28 -0700 Subject: [PATCH 0424/1766] Update default net to nn-eba324f53044.nnue The new net is based on the previous net 04cf2b4ed1da but with the biases for the 1st hidden layer tuned SPSA, see the SPSA session on fishtest there: https://tests.stockfishchess.org/tests/view/5f875213dcdad978fe8c5211 Thanks to @vondele for writing out the net, see discussion in this thread: https://github.com/mstembera/Stockfish/commit/432da86721647dff1d9426a7cdcfd2dbada8155e Passed STC: LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 15000 W: 1640 L: 1483 D: 11877 Ptnml(0-2): 50, 1183, 4908, 1278, 81 https://tests.stockfishchess.org/tests/view/5f8955e20fea1a44ec4f0a5d Passed LTC: LLR: 2.96 (-2.94,2.94) {0.25,1.25} Total: 81272 W: 3948 L: 3682 D: 73642 Ptnml(0-2): 64, 3194, 33856, 3456, 66 https://tests.stockfishchess.org/tests/view/5f89e8efeae8a6e60644d6e7 closes https://github.com/official-stockfish/Stockfish/pull/3187 Bench: 3762411 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 6a17f284b4c..6a8603ad089 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -36,7 +36,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-04cf2b4ed1da.nnue" + #define EvalFileDefaultName "nn-eba324f53044.nnue" namespace NNUE { From 560c776397483feaaa0deb5b666f46ff3f5b655f Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sat, 17 Oct 2020 13:40:10 +0200 Subject: [PATCH 0425/1766] Do more reductions for late quiet moves in case of consecutive fail highs. Idea of this patch can be described as following - in case we have consecutive fail highs and we reach late enough moves at root node probability of remaining quiet moves being able to produce even bigger value than moves that produced previous cutoff (so ones that should be high in move ordering but now they fail to produce beta cutoff because we actually reached high move count) should be quiet small so we can reduce them more. passed STC LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 53392 W: 5681 L: 5474 D: 42237 Ptnml(0-2): 214, 4104, 17894, 4229, 255 https://tests.stockfishchess.org/tests/view/5f88501adcdad978fe8c527e passed LTC LLR: 2.94 (-2.94,2.94) {0.25,1.25} Total: 59136 W: 2773 L: 2564 D: 53799 Ptnml(0-2): 30, 2117, 25078, 2300, 43 https://tests.stockfishchess.org/tests/view/5f884dbfdcdad978fe8c527a closes https://github.com/official-stockfish/Stockfish/pull/3184 Bench: 4066972 --- src/search.cpp | 5 ++++- src/thread.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index eaa79fb92bf..ab58ca648cf 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -417,7 +417,7 @@ void Thread::search() { // Start with a small aspiration window and, in the case of a fail // high/low, re-search with a bigger window until we don't fail // high/low anymore. - int failedHighCnt = 0; + failedHighCnt = 0; while (true) { Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt - searchAgainCounter); @@ -1177,6 +1177,9 @@ namespace { if (ttCapture) r++; + // Increase reduction at root if failing high + r += rootNode ? thisThread->failedHighCnt * thisThread->failedHighCnt * moveCount / 512 : 0; + // Increase reduction for cut nodes (~10 Elo) if (cutNode) r += 2; diff --git a/src/thread.h b/src/thread.h index 34b99015bac..6a73423b2c3 100644 --- a/src/thread.h +++ b/src/thread.h @@ -73,6 +73,7 @@ class Thread { CapturePieceToHistory captureHistory; ContinuationHistory continuationHistory[2][2]; Score contempt; + int failedHighCnt; }; From f5dfad5d72e164b57b787c0224046d641b3ade84 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Wed, 21 Oct 2020 14:52:13 +0100 Subject: [PATCH 0426/1766] Reduce big time spikes by reducing PV re-searches. Save time by reducing PV re-searches above original depth. Instead use 5% extra time on every move. STC 10+0.1 th 1 : LLR: 2.93 (-2.94,2.94) {-0.25,1.25} Total: 90688 W: 9702 L: 9436 D: 71550 Ptnml(0-2): 408, 7252, 29792, 7450, 442 https://tests.stockfishchess.org/tests/view/5f8df807bacb75a4f9a47223 LTC 60+0.6 th 1 : LLR: 2.97 (-2.94,2.94) {0.25,1.25} Total: 97856 W: 4602 L: 4303 D: 88951 Ptnml(0-2): 53, 3757, 41057, 3960, 101 https://tests.stockfishchess.org/tests/view/5f8ec4872c92c7fe3a8c602d closes https://github.com/official-stockfish/Stockfish/pull/3192 Bench 3943959 --- src/search.cpp | 4 +++- src/timeman.cpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ab58ca648cf..65ed9b73ffb 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -565,6 +565,7 @@ namespace { constexpr bool PvNode = NT == PV; const bool rootNode = PvNode && ss->ply == 0; + const Depth maxNextDepth = rootNode ? depth : depth + 1; // Check if we have an upcoming move which draws by repetition, or // if the opponent had an alternative move earlier to this position. @@ -1259,7 +1260,8 @@ namespace { (ss+1)->pv = pv; (ss+1)->pv[0] = MOVE_NONE; - value = -search(pos, ss+1, -beta, -alpha, newDepth, false); + value = -search(pos, ss+1, -beta, -alpha, + std::min(maxNextDepth, newDepth), false); } // Step 18. Undo move diff --git a/src/timeman.cpp b/src/timeman.cpp index 6d9c95ef691..da08f12d969 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -75,7 +75,7 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { // game time for the current move, so also cap to 20% of available game time. if (limits.movestogo == 0) { - optScale = std::min(0.008 + std::pow(ply + 3.0, 0.5) / 250.0, + optScale = std::min(0.0084 + std::pow(ply + 3.0, 0.5) * 0.0042, 0.2 * limits.time[us] / double(timeLeft)); maxScale = std::min(7.0, 4.0 + ply / 12.0); } From 258af8ae44fc15407996e0a21a80ee8b9cfa12cb Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 18 Oct 2020 15:01:19 +0200 Subject: [PATCH 0427/1766] Add net as dependency of config cleaner output and error message if the server is down and the net is not available. closes https://github.com/official-stockfish/Stockfish/pull/3188 No functional change --- src/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index 54868b39b46..87203547f28 100644 --- a/src/Makefile +++ b/src/Makefile @@ -711,7 +711,7 @@ endif config-sanity icc-profile-use icc-profile-make gcc-profile-use gcc-profile-make \ clang-profile-use clang-profile-make -build: config-sanity net +build: net config-sanity $(MAKE) ARCH=$(ARCH) COMP=$(COMP) all profile-build: net config-sanity objclean profileclean @@ -784,7 +784,7 @@ default: all: $(EXE) .depend -config-sanity: +config-sanity: net @echo "" @echo "Config:" @echo "debug: '$(debug)'" From 2046d5da30b2cd505b69bddb40062b0d37b43bc7 Mon Sep 17 00:00:00 2001 From: syzygy1 <3028851+syzygy1@users.noreply.github.com> Date: Tue, 20 Oct 2020 21:06:06 +0200 Subject: [PATCH 0428/1766] More incremental accumulator updates This patch was inspired by c065abd which updates the accumulator, if possible, based on the accumulator of two plies back if the accumulator of the preceding ply is not available. With this patch we look back even further in the position history in an attempt to reduce the number of complete recomputations. When we find a usable accumulator for the position N plies back, we also update the accumulator of the position N-1 plies back because that accumulator is most likely to be helpful later when evaluating positions in sibling branches. By not updating all intermediate accumulators immediately, we avoid doing too much work that is not certain to be useful. Overall, roughly 2-3% speedup. This patch makes the code more specific to the net architecture, changing input features of the net will require additional changes to the incremental update code as discussed in the PR #3193 and #3191. Passed STC: https://tests.stockfishchess.org/tests/view/5f9056712c92c7fe3a8c60d0 LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 10040 W: 1116 L: 968 D: 7956 Ptnml(0-2): 42, 722, 3365, 828, 63 closes https://github.com/official-stockfish/Stockfish/pull/3193 No functional change. --- src/nnue/features/feature_set.h | 108 ----------- src/nnue/nnue_accumulator.h | 5 +- src/nnue/nnue_feature_transformer.h | 274 ++++++++++++++-------------- src/position.cpp | 17 +- 4 files changed, 150 insertions(+), 254 deletions(-) diff --git a/src/nnue/features/feature_set.h b/src/nnue/features/feature_set.h index 26198114a30..975824b658c 100644 --- a/src/nnue/features/feature_set.h +++ b/src/nnue/features/feature_set.h @@ -43,90 +43,6 @@ namespace Eval::NNUE::Features { template class FeatureSetBase { - public: - // Get a list of indices for active features - template - static void AppendActiveIndices( - const Position& pos, TriggerEvent trigger, IndexListType active[2]) { - - for (Color perspective : { WHITE, BLACK }) { - Derived::CollectActiveIndices( - pos, trigger, perspective, &active[perspective]); - } - } - - // Get a list of indices for recently changed features - template - static void AppendChangedIndices( - const PositionType& pos, TriggerEvent trigger, - IndexListType removed[2], IndexListType added[2], bool reset[2]) { - - auto collect_for_one = [&](const DirtyPiece& dp) { - for (Color perspective : { WHITE, BLACK }) { - switch (trigger) { - case TriggerEvent::kFriendKingMoved: - reset[perspective] = dp.piece[0] == make_piece(perspective, KING); - break; - default: - assert(false); - break; - } - if (reset[perspective]) { - Derived::CollectActiveIndices( - pos, trigger, perspective, &added[perspective]); - } else { - Derived::CollectChangedIndices( - pos, dp, trigger, perspective, - &removed[perspective], &added[perspective]); - } - } - }; - - auto collect_for_two = [&](const DirtyPiece& dp1, const DirtyPiece& dp2) { - for (Color perspective : { WHITE, BLACK }) { - switch (trigger) { - case TriggerEvent::kFriendKingMoved: - reset[perspective] = dp1.piece[0] == make_piece(perspective, KING) - || dp2.piece[0] == make_piece(perspective, KING); - break; - default: - assert(false); - break; - } - if (reset[perspective]) { - Derived::CollectActiveIndices( - pos, trigger, perspective, &added[perspective]); - } else { - Derived::CollectChangedIndices( - pos, dp1, trigger, perspective, - &removed[perspective], &added[perspective]); - Derived::CollectChangedIndices( - pos, dp2, trigger, perspective, - &removed[perspective], &added[perspective]); - } - } - }; - - if (pos.state()->previous->accumulator.computed_accumulation) { - const auto& prev_dp = pos.state()->dirtyPiece; - if (prev_dp.dirty_num == 0) return; - collect_for_one(prev_dp); - } else { - const auto& prev_dp = pos.state()->previous->dirtyPiece; - if (prev_dp.dirty_num == 0) { - const auto& prev2_dp = pos.state()->dirtyPiece; - if (prev2_dp.dirty_num == 0) return; - collect_for_one(prev2_dp); - } else { - const auto& prev2_dp = pos.state()->dirtyPiece; - if (prev2_dp.dirty_num == 0) { - collect_for_one(prev_dp); - } else { - collect_for_two(prev_dp, prev2_dp); - } - } - } - } }; // Class template that represents the feature set @@ -146,30 +62,6 @@ namespace Eval::NNUE::Features { CompileTimeList; static constexpr auto kRefreshTriggers = SortedTriggerSet::kValues; - private: - // Get a list of indices for active features - static void CollectActiveIndices( - const Position& pos, const TriggerEvent trigger, const Color perspective, - IndexList* const active) { - if (FeatureType::kRefreshTrigger == trigger) { - FeatureType::AppendActiveIndices(pos, perspective, active); - } - } - - // Get a list of indices for recently changed features - static void CollectChangedIndices( - const Position& pos, const DirtyPiece& dp, const TriggerEvent trigger, const Color perspective, - IndexList* const removed, IndexList* const added) { - - if (FeatureType::kRefreshTrigger == trigger) { - FeatureType::AppendChangedIndices(pos, dp, perspective, removed, added); - } - } - - // Make the base class and the class template that recursively uses itself a friend - friend class FeatureSetBase; - template - friend class FeatureSet; }; } // namespace Eval::NNUE::Features diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index 26370710201..a357d83526c 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -25,11 +25,14 @@ namespace Eval::NNUE { + // The accumulator of a StateInfo without parent is set to the INIT state + enum AccumulatorState { EMPTY, COMPUTED, INIT }; + // Class that holds the result of affine transformation of input features struct alignas(kCacheLineSize) Accumulator { std::int16_t accumulation[2][kRefreshTriggers.size()][kTransformedFeatureDimensions]; - bool computed_accumulation; + AccumulatorState state[2]; }; } // namespace Eval::NNUE diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 2f86d20a639..f145c848f96 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -32,7 +32,7 @@ namespace Eval::NNUE { // If vector instructions are enabled, we update and refresh the // accumulator tile by tile such that each tile fits in the CPU's // vector registers. - #define TILING + #define VECTOR #ifdef USE_AVX512 typedef __m512i vec_t; @@ -75,7 +75,7 @@ namespace Eval::NNUE { static constexpr IndexType kNumRegs = 16; #else - #undef TILING + #undef VECTOR #endif @@ -86,7 +86,7 @@ namespace Eval::NNUE { // Number of output dimensions for one side static constexpr IndexType kHalfDimensions = kTransformedFeatureDimensions; - #ifdef TILING + #ifdef VECTOR static constexpr IndexType kTileHeight = kNumRegs * sizeof(vec_t) / 2; static_assert(kHalfDimensions % kTileHeight == 0, "kTileHeight must divide kHalfDimensions"); #endif @@ -119,32 +119,11 @@ namespace Eval::NNUE { return !stream.fail(); } - // Proceed with the difference calculation if possible - bool UpdateAccumulatorIfPossible(const Position& pos) const { - - const auto now = pos.state(); - if (now->accumulator.computed_accumulation) - return true; - - const auto prev = now->previous; - if (prev) { - if (prev->accumulator.computed_accumulation) { - UpdateAccumulator(pos); - return true; - } else if (prev->previous && prev->previous->accumulator.computed_accumulation) { - UpdateAccumulator(pos); - return true; - } - } - - return false; - } - // Convert input features void Transform(const Position& pos, OutputType* output) const { - if (!UpdateAccumulatorIfPossible(pos)) - RefreshAccumulator(pos); + UpdateAccumulator(pos, WHITE); + UpdateAccumulator(pos, BLACK); const auto& accumulation = pos.state()->accumulator.accumulation; @@ -240,153 +219,172 @@ namespace Eval::NNUE { } private: - // Calculate cumulative value without using difference calculation - void RefreshAccumulator(const Position& pos) const { - - auto& accumulator = pos.state()->accumulator; - IndexType i = 0; - Features::IndexList active_indices[2]; - RawFeatures::AppendActiveIndices(pos, kRefreshTriggers[i], - active_indices); - for (Color perspective : { WHITE, BLACK }) { - #ifdef TILING - for (unsigned j = 0; j < kHalfDimensions / kTileHeight; ++j) { - auto biasesTile = reinterpret_cast( - &biases_[j * kTileHeight]); - auto accTile = reinterpret_cast( - &accumulator.accumulation[perspective][i][j * kTileHeight]); - vec_t acc[kNumRegs]; - - for (unsigned k = 0; k < kNumRegs; ++k) - acc[k] = biasesTile[k]; - - for (const auto index : active_indices[perspective]) { - const IndexType offset = kHalfDimensions * index + j * kTileHeight; - auto column = reinterpret_cast(&weights_[offset]); - - for (unsigned k = 0; k < kNumRegs; ++k) - acc[k] = vec_add_16(acc[k], column[k]); - } + void UpdateAccumulator(const Position& pos, const Color c) const { - for (unsigned k = 0; k < kNumRegs; k++) - vec_store(&accTile[k], acc[k]); - } - #else - std::memcpy(accumulator.accumulation[perspective][i], biases_, - kHalfDimensions * sizeof(BiasType)); - - for (const auto index : active_indices[perspective]) { - const IndexType offset = kHalfDimensions * index; - - for (IndexType j = 0; j < kHalfDimensions; ++j) - accumulator.accumulation[perspective][i][j] += weights_[offset + j]; - } + #ifdef VECTOR + // Gcc-10.2 unnecessarily spills AVX2 registers if this array + // is defined in the VECTOR code below, once in each branch + vec_t acc[kNumRegs]; #endif - } - #if defined(USE_MMX) - _mm_empty(); - #endif - - accumulator.computed_accumulation = true; - } - - // Calculate cumulative value using difference calculation - void UpdateAccumulator(const Position& pos) const { - - Accumulator* prev_accumulator; - assert(pos.state()->previous); - if (pos.state()->previous->accumulator.computed_accumulation) { - prev_accumulator = &pos.state()->previous->accumulator; - } - else { - assert(pos.state()->previous->previous); - assert(pos.state()->previous->previous->accumulator.computed_accumulation); - prev_accumulator = &pos.state()->previous->previous->accumulator; + // Look for a usable accumulator of an earlier position. We keep track + // of the estimated gain in terms of features to be added/subtracted. + StateInfo *st = pos.state(), *next = nullptr; + int gain = popcount(pos.pieces()) - 2; + while (st->accumulator.state[c] == EMPTY) + { + auto& dp = st->dirtyPiece; + // The first condition tests whether an incremental update is + // possible at all: if this side's king has moved, it is not possible. + static_assert(std::is_same_v>, + "Current code assumes that only kFriendlyKingMoved refresh trigger is being used."); + if ( dp.piece[0] == make_piece(c, KING) + || (gain -= dp.dirty_num + 1) < 0) + break; + next = st; + st = st->previous; } - auto& accumulator = pos.state()->accumulator; - IndexType i = 0; - Features::IndexList removed_indices[2], added_indices[2]; - bool reset[2] = { false, false }; - RawFeatures::AppendChangedIndices(pos, kRefreshTriggers[i], - removed_indices, added_indices, reset); - - #ifdef TILING - for (IndexType j = 0; j < kHalfDimensions / kTileHeight; ++j) { - for (Color perspective : { WHITE, BLACK }) { + if (st->accumulator.state[c] == COMPUTED) + { + if (next == nullptr) + return; + + // Update incrementally in two steps. First, we update the "next" + // accumulator. Then, we update the current accumulator (pos.state()). + + // Gather all features to be updated. This code assumes HalfKP features + // only and doesn't support refresh triggers. + static_assert(std::is_same_v>, + RawFeatures>); + Features::IndexList removed[2], added[2]; + Features::HalfKP::AppendChangedIndices(pos, + next->dirtyPiece, c, &removed[0], &added[0]); + for (StateInfo *st2 = pos.state(); st2 != next; st2 = st2->previous) + Features::HalfKP::AppendChangedIndices(pos, + st2->dirtyPiece, c, &removed[1], &added[1]); + + // Mark the accumulators as computed. + next->accumulator.state[c] = COMPUTED; + pos.state()->accumulator.state[c] = COMPUTED; + + // Now update the accumulators listed in info[], where the last element is a sentinel. + StateInfo *info[3] = + { next, next == pos.state() ? nullptr : pos.state(), nullptr }; + #ifdef VECTOR + for (IndexType j = 0; j < kHalfDimensions / kTileHeight; ++j) + { + // Load accumulator auto accTile = reinterpret_cast( - &accumulator.accumulation[perspective][i][j * kTileHeight]); - vec_t acc[kNumRegs]; - - if (reset[perspective]) { - auto biasesTile = reinterpret_cast( - &biases_[j * kTileHeight]); - for (unsigned k = 0; k < kNumRegs; ++k) - acc[k] = biasesTile[k]; - } else { - auto prevAccTile = reinterpret_cast( - &prev_accumulator->accumulation[perspective][i][j * kTileHeight]); - for (IndexType k = 0; k < kNumRegs; ++k) - acc[k] = vec_load(&prevAccTile[k]); + &st->accumulator.accumulation[c][0][j * kTileHeight]); + for (IndexType k = 0; k < kNumRegs; ++k) + acc[k] = vec_load(&accTile[k]); + for (IndexType i = 0; info[i]; ++i) + { // Difference calculation for the deactivated features - for (const auto index : removed_indices[perspective]) { + for (const auto index : removed[i]) + { const IndexType offset = kHalfDimensions * index + j * kTileHeight; auto column = reinterpret_cast(&weights_[offset]); - for (IndexType k = 0; k < kNumRegs; ++k) acc[k] = vec_sub_16(acc[k], column[k]); } - } - { // Difference calculation for the activated features - for (const auto index : added_indices[perspective]) { + + // Difference calculation for the activated features + for (const auto index : added[i]) + { const IndexType offset = kHalfDimensions * index + j * kTileHeight; auto column = reinterpret_cast(&weights_[offset]); - for (IndexType k = 0; k < kNumRegs; ++k) acc[k] = vec_add_16(acc[k], column[k]); } - } - for (IndexType k = 0; k < kNumRegs; ++k) - vec_store(&accTile[k], acc[k]); + // Store accumulator + accTile = reinterpret_cast( + &info[i]->accumulator.accumulation[c][0][j * kTileHeight]); + for (IndexType k = 0; k < kNumRegs; ++k) + vec_store(&accTile[k], acc[k]); + } } - } - #if defined(USE_MMX) - _mm_empty(); - #endif #else - for (Color perspective : { WHITE, BLACK }) { - - if (reset[perspective]) { - std::memcpy(accumulator.accumulation[perspective][i], biases_, - kHalfDimensions * sizeof(BiasType)); - } else { - std::memcpy(accumulator.accumulation[perspective][i], - prev_accumulator->accumulation[perspective][i], - kHalfDimensions * sizeof(BiasType)); + for (IndexType i = 0; info[i]; ++i) + { + std::memcpy(info[i]->accumulator.accumulation[c][0], + st->accumulator.accumulation[c][0], + kHalfDimensions * sizeof(BiasType)); + st = info[i]; + // Difference calculation for the deactivated features - for (const auto index : removed_indices[perspective]) { + for (const auto index : removed[i]) + { const IndexType offset = kHalfDimensions * index; for (IndexType j = 0; j < kHalfDimensions; ++j) - accumulator.accumulation[perspective][i][j] -= weights_[offset + j]; + st->accumulator.accumulation[c][0][j] -= weights_[offset + j]; } - } - { // Difference calculation for the activated features - for (const auto index : added_indices[perspective]) { + + // Difference calculation for the activated features + for (const auto index : added[i]) + { const IndexType offset = kHalfDimensions * index; for (IndexType j = 0; j < kHalfDimensions; ++j) - accumulator.accumulation[perspective][i][j] += weights_[offset + j]; + st->accumulator.accumulation[c][0][j] += weights_[offset + j]; } } + #endif } + else + { + // Refresh the accumulator + auto& accumulator = pos.state()->accumulator; + accumulator.state[c] = COMPUTED; + Features::IndexList active; + Features::HalfKP::AppendActiveIndices(pos, c, &active); + + #ifdef VECTOR + for (IndexType j = 0; j < kHalfDimensions / kTileHeight; ++j) + { + auto biasesTile = reinterpret_cast( + &biases_[j * kTileHeight]); + for (IndexType k = 0; k < kNumRegs; ++k) + acc[k] = biasesTile[k]; + + for (const auto index : active) + { + const IndexType offset = kHalfDimensions * index + j * kTileHeight; + auto column = reinterpret_cast(&weights_[offset]); + + for (unsigned k = 0; k < kNumRegs; ++k) + acc[k] = vec_add_16(acc[k], column[k]); + } + + auto accTile = reinterpret_cast( + &accumulator.accumulation[c][0][j * kTileHeight]); + for (unsigned k = 0; k < kNumRegs; k++) + vec_store(&accTile[k], acc[k]); + } + + #else + std::memcpy(accumulator.accumulation[c][0], biases_, + kHalfDimensions * sizeof(BiasType)); + + for (const auto index : active) + { + const IndexType offset = kHalfDimensions * index; + + for (IndexType j = 0; j < kHalfDimensions; ++j) + accumulator.accumulation[c][0][j] += weights_[offset + j]; + } #endif + } - accumulator.computed_accumulation = true; + #if defined(USE_MMX) + _mm_empty(); + #endif } using BiasType = std::int16_t; diff --git a/src/position.cpp b/src/position.cpp index e6a760d2c7a..b707293dd73 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -279,6 +279,8 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th chess960 = isChess960; thisThread = th; set_state(st); + st->accumulator.state[WHITE] = Eval::NNUE::INIT; + st->accumulator.state[BLACK] = Eval::NNUE::INIT; assert(pos_is_ok()); @@ -703,7 +705,8 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { ++st->pliesFromNull; // Used by NNUE - st->accumulator.computed_accumulation = false; + st->accumulator.state[WHITE] = Eval::NNUE::EMPTY; + st->accumulator.state[BLACK] = Eval::NNUE::EMPTY; auto& dp = st->dirtyPiece; dp.dirty_num = 1; @@ -996,16 +999,16 @@ void Position::do_null_move(StateInfo& newSt) { assert(!checkers()); assert(&newSt != st); - if (Eval::useNNUE) - { - std::memcpy(&newSt, st, sizeof(StateInfo)); - } - else - std::memcpy(&newSt, st, offsetof(StateInfo, accumulator)); + std::memcpy(&newSt, st, offsetof(StateInfo, accumulator)); newSt.previous = st; st = &newSt; + st->dirtyPiece.dirty_num = 0; + st->dirtyPiece.piece[0] = NO_PIECE; // Avoid checks in UpdateAccumulator() + st->accumulator.state[WHITE] = Eval::NNUE::EMPTY; + st->accumulator.state[BLACK] = Eval::NNUE::EMPTY; + if (st->epSquare != SQ_NONE) { st->key ^= Zobrist::enpassant[file_of(st->epSquare)]; From bde3505758417c6cd77f2e09edac5bbd5f58b570 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sat, 24 Oct 2020 02:01:04 +0300 Subject: [PATCH 0429/1766] Bishop Pawns based on Files Passed STC: https://tests.stockfishchess.org/tests/view/5f8cc8145a4eacb45305da3c LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 132544 W: 27795 L: 27328 D: 77421 Ptnml(0-2): 2756, 15558, 29272, 15835, 2851 Passed LTC: https://tests.stockfishchess.org/tests/view/5f8df614bacb75a4f9a4721e LLR: 2.94 (-2.94,2.94) {0.25,1.25} Total: 169608 W: 23257 L: 22622 D: 123729 Ptnml(0-2): 1408, 16316, 48758, 16877, 1445 closes https://github.com/official-stockfish/Stockfish/pull/3194 Bench: 4067106 --- src/evaluate.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 425ba6f8fae..030d1017579 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -222,6 +222,12 @@ namespace { S(112,178), S(114,185), S(114,187), S(119,221) } }; + // BishopPawns[distance from edge] contains a file-dependent penalty for pawns on + // squares of the same color as our bishop. + constexpr Score BishopPawns[int(FILE_NB) / 2] = { + S(3, 8), S(3, 9), S(1, 8), S(3, 7) + }; + // KingProtector[knight/bishop] contains penalty for each distance unit to own king constexpr Score KingProtector[] = { S(8, 9), S(6, 9) }; @@ -252,7 +258,6 @@ namespace { // Assorted bonuses and penalties constexpr Score BadOutpost = S( -7, 36); constexpr Score BishopOnKingRing = S( 24, 0); - constexpr Score BishopPawns = S( 3, 7); constexpr Score BishopXRayPawns = S( 4, 5); constexpr Score CorneredBishop = S( 50, 50); constexpr Score FlankAttacks = S( 8, 0); @@ -453,7 +458,7 @@ namespace { // when the bishop is outside the pawn chain. Bitboard blocked = pos.pieces(Us, PAWN) & shift(pos.pieces()); - score -= BishopPawns * pos.pawns_on_same_color_squares(Us, s) + score -= BishopPawns[edge_distance(file_of(s))] * pos.pawns_on_same_color_squares(Us, s) * (!(attackedBy[Us][PAWN] & s) + popcount(blocked & CenterFiles)); // Penalty for all enemy pawns x-rayed @@ -906,7 +911,7 @@ namespace { : pos.count(WHITE) + pos.count(WHITE)); else sf = std::min(sf, 36 + 7 * pos.count(strongSide)) - 4 * !pawnsOnBothFlanks; - + sf -= 4 * !pawnsOnBothFlanks; } From 6328135264d3b13a2cef3f0c835a27192cae0f40 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Wed, 28 Oct 2020 04:24:55 +0800 Subject: [PATCH 0430/1766] Update default net to nn-2eb2e0707c2b.nnue Optimization of the net weights of the 32 x 32 layer (1024 parameters) and net biases of the 512 x 32 layer (32 parameters) using SPSA. Tuning of 32 x 32 Layer (800,000 games, 5 seconds time control): https://tests.stockfishchess.org/tests/view/5f942040d3978d7e86f1aa05 Tuning of 512 x 32 Layer (80,000 games, 20 seconds time control): https://tests.stockfishchess.org/tests/view/5f8f926d2c92c7fe3a8c608b STC: LLR: 2.96 (-2.94,2.94) {-0.25,1.25} Total: 17336 W: 1918 L: 1754 D: 13664 Ptnml(0-2): 79, 1344, 5672, 1480, 93 https://tests.stockfishchess.org/tests/view/5f9882346a2c112b60691b34 LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.25} Total: 37304 W: 1822 L: 1651 D: 33831 Ptnml(0-2): 27, 1461, 15501, 1640, 23 https://tests.stockfishchess.org/tests/view/5f98a4b36a2c112b60691b40 closes https://github.com/official-stockfish/Stockfish/pull/3201 Bench: 3403528 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 6a8603ad089..6e5db6a302c 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -36,7 +36,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-eba324f53044.nnue" + #define EvalFileDefaultName "nn-2eb2e0707c2b.nnue" namespace NNUE { From 0f6c08c73f516873b312cb8fce0d824a2167b075 Mon Sep 17 00:00:00 2001 From: syzygy1 <3028851+syzygy1@users.noreply.github.com> Date: Tue, 27 Oct 2020 19:22:41 +0100 Subject: [PATCH 0431/1766] Do not skip non-recapture ttMove when in check The qsearch() MovePicker incorrectly skips a non-recapture ttMove when in check (if depth <= DEPTH_QS_RECAPTURES). This is clearly not intended and can cause qsearch() to return a mate score when there is no mate. Introduced in cad300c and 6596f0e, as observed by joergoster in #3171 and #3198. This PR fixes the bug by not skipping the non-recapture ttMove when in check. Passed non-regression STC: https://tests.stockfishchess.org/tests/view/5f9867ea6a2c112b60691b10 LLR: 2.98 (-2.94,2.94) {-1.25,0.25} Total: 27112 W: 2943 L: 2842 D: 21327 Ptnml(0-2): 127, 2170, 8878, 2237, 144 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/5f9967326a2c112b60691bb0 LLR: 2.99 (-2.94,2.94) {-0.75,0.25} Total: 18392 W: 807 L: 738 D: 16847 Ptnml(0-2): 9, 655, 7802, 718, 12 closes https://github.com/official-stockfish/Stockfish/pull/3199 closes https://github.com/official-stockfish/Stockfish/pull/3198 Bench: 3870606 --- src/movepick.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 153d323e8be..f5e02385556 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -73,8 +73,9 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist assert(d <= 0); stage = (pos.checkers() ? EVASION_TT : QSEARCH_TT) + - !(ttm && (depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare) - && pos.pseudo_legal(ttm)); + !( ttm + && (pos.checkers() || depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare) + && pos.pseudo_legal(ttm)); } /// MovePicker constructor for ProbCut: we generate captures with SEE greater From dfc7f88650bf8bda4a381d36e209209cf63a9bcc Mon Sep 17 00:00:00 2001 From: mstembera Date: Fri, 30 Oct 2020 13:45:40 -0700 Subject: [PATCH 0432/1766] Update default net to nn-cb26f10b1fd9.nnue Result of https://tests.stockfishchess.org/tests/view/5f9a06796a2c112b60691c0f tuning. STC LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 53712 W: 5776 L: 5561 D: 42375 Ptnml(0-2): 253, 4282, 17604, 4431, 286 https://tests.stockfishchess.org/tests/view/5f9c7bbc6a2c112b60691d4d LTC LLR: 2.97 (-2.94,2.94) {0.25,1.25} Total: 80184 W: 4007 L: 3739 D: 72438 Ptnml(0-2): 58, 3302, 33130, 3518, 84 https://tests.stockfishchess.org/tests/view/5f9d01f06a2c112b60691d87 closes https://github.com/official-stockfish/Stockfish/pull/3209 bench: 3517795 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 6e5db6a302c..6bec27db765 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -36,7 +36,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-2eb2e0707c2b.nnue" + #define EvalFileDefaultName "nn-cb26f10b1fd9.nnue" namespace NNUE { From 75e06a1c89ebac9c9ec4247bc82ec728a2bffe1e Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Thu, 29 Oct 2020 00:14:53 +0100 Subject: [PATCH 0433/1766] Optimize affine transform for SSSE3 and higher targets. A non-functional speedup. Unroll the loops going over the output dimensions in the affine transform layers by a factor of 4 and perform 4 horizontal additions at a time. Instead of doing naive horizontal additions on each vector separately use hadd and shuffling between vectors to reduce the number of instructions by using all lanes for all stages of the horizontal adds. passed STC of the initial version: LLR: 2.95 (-2.94,2.94) {-0.25,1.25} Total: 17808 W: 1914 L: 1756 D: 14138 Ptnml(0-2): 76, 1330, 5948, 1460, 90 https://tests.stockfishchess.org/tests/view/5f9d516f6a2c112b60691da3 passed STC of the final version after cleanup: LLR: 2.95 (-2.94,2.94) {-0.25,1.25} Total: 16296 W: 1750 L: 1595 D: 12951 Ptnml(0-2): 72, 1192, 5479, 1319, 86 https://tests.stockfishchess.org/tests/view/5f9df5776a2c112b60691de3 closes https://github.com/official-stockfish/Stockfish/pull/3203 No functional change --- src/nnue/layers/affine_transform.h | 474 +++++++++++++++++++++++------ 1 file changed, 382 insertions(+), 92 deletions(-) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 94d0b5a9494..f0292e453c1 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -74,113 +74,400 @@ namespace Eval::NNUE::Layers { const TransformedFeatureType* transformed_features, char* buffer) const { const auto input = previous_layer_.Propagate( transformed_features, buffer + kSelfBufferSize); + +#if defined (USE_AVX512) + + [[maybe_unused]] const __m512i kOnes512 = _mm512_set1_epi16(1); + + [[maybe_unused]] auto m512_hadd = [](__m512i sum, int bias) -> int { + return _mm512_reduce_add_epi32(sum) + bias; + }; + + [[maybe_unused]] auto m512_haddx4 = [](__m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3, __m128i bias) -> __m128i { + __m512i sum01a = _mm512_unpacklo_epi32(sum0, sum1); + __m512i sum01b = _mm512_unpackhi_epi32(sum0, sum1); + + __m512i sum23a = _mm512_unpacklo_epi32(sum2, sum3); + __m512i sum23b = _mm512_unpackhi_epi32(sum2, sum3); + + __m512i sum01 = _mm512_add_epi32(sum01a, sum01b); + __m512i sum23 = _mm512_add_epi32(sum23a, sum23b); + + __m512i sum0123a = _mm512_unpacklo_epi64(sum01, sum23); + __m512i sum0123b = _mm512_unpackhi_epi64(sum01, sum23); + + __m512i sum = _mm512_add_epi32(sum0123a, sum0123b); + + __m256i sum256lo = _mm512_castsi512_si256(sum); + __m256i sum256hi = _mm512_extracti64x4_epi64(sum, 1); + + sum256lo = _mm256_add_epi32(sum256lo, sum256hi); + + __m128i sum128lo = _mm256_castsi256_si128(sum256lo); + __m128i sum128hi = _mm256_extracti128_si256(sum256lo, 1); + + return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias); + }; + + [[maybe_unused]] auto m512_add_dpbusd_epi32 = [=](__m512i& acc, __m512i a, __m512i b) { +#if defined (USE_VNNI) + acc = _mm512_dpbusd_epi32(acc, a, b); +#else + __m512i product0 = _mm512_maddubs_epi16(a, b); + product0 = _mm512_madd_epi16(product0, kOnes512); + acc = _mm512_add_epi32(acc, product0); +#endif + }; + +#endif +#if defined (USE_AVX2) + + [[maybe_unused]] const __m256i kOnes256 = _mm256_set1_epi16(1); + + [[maybe_unused]] auto m256_hadd = [](__m256i sum, int bias) -> int { + __m128i sum128 = _mm_add_epi32(_mm256_castsi256_si128(sum), _mm256_extracti128_si256(sum, 1)); + sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_BADC)); + sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_CDAB)); + return _mm_cvtsi128_si32(sum128) + bias; + }; + + [[maybe_unused]] auto m256_haddx4 = [](__m256i sum0, __m256i sum1, __m256i sum2, __m256i sum3, __m128i bias) -> __m128i { + sum0 = _mm256_hadd_epi32(sum0, sum1); + sum2 = _mm256_hadd_epi32(sum2, sum3); + + sum0 = _mm256_hadd_epi32(sum0, sum2); + + __m128i sum128lo = _mm256_castsi256_si128(sum0); + __m128i sum128hi = _mm256_extracti128_si256(sum0, 1); + + return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias); + }; + + [[maybe_unused]] auto m256_add_dpbusd_epi32 = [=](__m256i& acc, __m256i a, __m256i b) { +#if defined (USE_VNNI) + acc = _mm256_dpbusd_epi32(acc, a, b); +#else + __m256i product0 = _mm256_maddubs_epi16(a, b); + product0 = _mm256_madd_epi16(product0, kOnes256); + acc = _mm256_add_epi32(acc, product0); +#endif + }; + +#endif + +#if defined (USE_SSSE3) + + [[maybe_unused]] const __m128i kOnes128 = _mm_set1_epi16(1); + + [[maybe_unused]] auto m128_hadd = [](__m128i sum, int bias) -> int { + sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0x4E)); //_MM_PERM_BADC + sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0xB1)); //_MM_PERM_CDAB + return _mm_cvtsi128_si32(sum) + bias; + }; + + [[maybe_unused]] auto m128_haddx4 = [](__m128i sum0, __m128i sum1, __m128i sum2, __m128i sum3, __m128i bias) -> __m128i { + sum0 = _mm_hadd_epi32(sum0, sum1); + sum2 = _mm_hadd_epi32(sum2, sum3); + + sum0 = _mm_hadd_epi32(sum0, sum2); + + return _mm_add_epi32(sum0, bias); + }; + + [[maybe_unused]] auto m128_add_dpbusd_epi32 = [=](__m128i& acc, __m128i a, __m128i b) { + __m128i product0 = _mm_maddubs_epi16(a, b); + product0 = _mm_madd_epi16(product0, kOnes128); + acc = _mm_add_epi32(acc, product0); + }; + +#endif + +#if defined (USE_AVX512) + + constexpr IndexType kNumChunks512 = kPaddedInputDimensions / (kSimdWidth * 2); + constexpr IndexType kNumChunks256 = kPaddedInputDimensions / kSimdWidth; + const auto output = reinterpret_cast(buffer); - #if defined(USE_AVX512) - constexpr IndexType kNumChunks = kPaddedInputDimensions / (kSimdWidth * 2); - const auto input_vector = reinterpret_cast(input); - #if !defined(USE_VNNI) - const __m512i kOnes = _mm512_set1_epi16(1); - #endif + // Since to saturate a zmm register it takes 64 bytes we + // cannot use AVX512 for the smaller affine transforms. + // Instead we fallback to a AVX2 implementation if the + // kInputDimensions isn't a multiple of 64. + // Note that this means that for example for + // kInputDimensions of 96 we fallback to AVX2 even though + // the first 64 elements could be processed with AVX512. + // This is caused by mixing the __m256 and __m512 variables + // required to better handle that case and it would + // require handling more cases statically not to lose performance. + // This should be revisited if such input dimensions are to be considered. + [[maybe_unused]] const auto input_vector512 = reinterpret_cast(input); + [[maybe_unused]] const auto input_vector256 = reinterpret_cast(input); + + // kOutputDimensions is either 1 or a multiple of kSimdWidth + // because then it is also an input dimension. + if constexpr (kOutputDimensions % 4 == 0) + { + for (IndexType i = 0; i < kOutputDimensions; i += 4) + { + const IndexType offset0 = (i + 0) * kPaddedInputDimensions; + const IndexType offset1 = (i + 1) * kPaddedInputDimensions; + const IndexType offset2 = (i + 2) * kPaddedInputDimensions; + const IndexType offset3 = (i + 3) * kPaddedInputDimensions; + + const __m128i bias = *reinterpret_cast(&biases_[i]); + __m128i* outptr = reinterpret_cast<__m128i*>(&output[i]); + + if constexpr (kPaddedInputDimensions % (kSimdWidth * 2) == 0) + { + __m512i sum0 = _mm512_setzero_si512(); + __m512i sum1 = _mm512_setzero_si512(); + __m512i sum2 = _mm512_setzero_si512(); + __m512i sum3 = _mm512_setzero_si512(); + + const auto row0 = reinterpret_cast(&weights_[offset0]); + const auto row1 = reinterpret_cast(&weights_[offset1]); + const auto row2 = reinterpret_cast(&weights_[offset2]); + const auto row3 = reinterpret_cast(&weights_[offset3]); + + for (IndexType j = 0; j < kNumChunks512; ++j) + { + const __m512i in = input_vector512[j]; + + m512_add_dpbusd_epi32(sum0, in, row0[j]); + m512_add_dpbusd_epi32(sum1, in, row1[j]); + m512_add_dpbusd_epi32(sum2, in, row2[j]); + m512_add_dpbusd_epi32(sum3, in, row3[j]); + } + + *outptr = m512_haddx4(sum0, sum1, sum2, sum3, bias); + } + else + { + __m256i sum0 = _mm256_setzero_si256(); + __m256i sum1 = _mm256_setzero_si256(); + __m256i sum2 = _mm256_setzero_si256(); + __m256i sum3 = _mm256_setzero_si256(); + + const auto row0 = reinterpret_cast(&weights_[offset0]); + const auto row1 = reinterpret_cast(&weights_[offset1]); + const auto row2 = reinterpret_cast(&weights_[offset2]); + const auto row3 = reinterpret_cast(&weights_[offset3]); + + for (IndexType j = 0; j < kNumChunks256; ++j) + { + const __m256i in = input_vector256[j]; + + m256_add_dpbusd_epi32(sum0, in, row0[j]); + m256_add_dpbusd_epi32(sum1, in, row1[j]); + m256_add_dpbusd_epi32(sum2, in, row2[j]); + m256_add_dpbusd_epi32(sum3, in, row3[j]); + } + + *outptr = m256_haddx4(sum0, sum1, sum2, sum3, bias); + } + } + } + else if constexpr (kOutputDimensions == 1) + { + if constexpr (kPaddedInputDimensions % (kSimdWidth * 2) == 0) + { + __m512i sum0 = _mm512_setzero_si512(); + + const auto row0 = reinterpret_cast(&weights_[0]); + + for (IndexType j = 0; j < kNumChunks512; ++j) + { + const __m512i in = input_vector512[j]; + + m512_add_dpbusd_epi32(sum0, in, row0[j]); + } + + output[0] = m512_hadd(sum0, biases_[0]); + } + else + { + __m256i sum0 = _mm256_setzero_si256(); + + const auto row0 = reinterpret_cast(&weights_[0]); + + for (IndexType j = 0; j < kNumChunks256; ++j) + { + const __m256i in = input_vector256[j]; + + m256_add_dpbusd_epi32(sum0, in, row0[j]); + } + + output[0] = m256_hadd(sum0, biases_[0]); + } + } + else + { + // This case can never happen because kOutputDimensions + // is always 1 or a multiple of kSimdWidth. + assert(false); + } + +#elif defined (USE_AVX2) - #elif defined(USE_AVX2) constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; + + const auto output = reinterpret_cast(buffer); const auto input_vector = reinterpret_cast(input); - #if !defined(USE_VNNI) - const __m256i kOnes = _mm256_set1_epi16(1); - #endif - #elif defined(USE_SSE2) + // kOutputDimensions is either 1 or a multiple of kSimdWidth + // because then it is also an input dimension. + if constexpr (kOutputDimensions % 4 == 0) + { + for (IndexType i = 0; i < kOutputDimensions; i += 4) + { + const IndexType offset0 = (i + 0) * kPaddedInputDimensions; + const IndexType offset1 = (i + 1) * kPaddedInputDimensions; + const IndexType offset2 = (i + 2) * kPaddedInputDimensions; + const IndexType offset3 = (i + 3) * kPaddedInputDimensions; + + const __m128i bias = *reinterpret_cast(&biases_[i]); + __m128i* outptr = reinterpret_cast<__m128i*>(&output[i]); + + __m256i sum0 = _mm256_setzero_si256(); + __m256i sum1 = _mm256_setzero_si256(); + __m256i sum2 = _mm256_setzero_si256(); + __m256i sum3 = _mm256_setzero_si256(); + + const auto row0 = reinterpret_cast(&weights_[offset0]); + const auto row1 = reinterpret_cast(&weights_[offset1]); + const auto row2 = reinterpret_cast(&weights_[offset2]); + const auto row3 = reinterpret_cast(&weights_[offset3]); + + for (IndexType j = 0; j < kNumChunks; ++j) + { + const __m256i in = input_vector[j]; + + m256_add_dpbusd_epi32(sum0, in, row0[j]); + m256_add_dpbusd_epi32(sum1, in, row1[j]); + m256_add_dpbusd_epi32(sum2, in, row2[j]); + m256_add_dpbusd_epi32(sum3, in, row3[j]); + } + + *outptr = m256_haddx4(sum0, sum1, sum2, sum3, bias); + } + } + else if constexpr (kOutputDimensions == 1) + { + __m256i sum0 = _mm256_setzero_si256(); + + const auto row0 = reinterpret_cast(&weights_[0]); + + for (IndexType j = 0; j < kNumChunks; ++j) + { + const __m256i in = input_vector[j]; + + m256_add_dpbusd_epi32(sum0, in, row0[j]); + } + + output[0] = m256_hadd(sum0, biases_[0]); + } + else + { + // This case can never happen because kOutputDimensions + // is always 1 or a multiple of kSimdWidth. + assert(false); + } + +#elif defined (USE_SSSE3) + constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; - #ifndef USE_SSSE3 + + auto output = reinterpret_cast(buffer); + const auto input_vector = reinterpret_cast(input); + + // kOutputDimensions is either 1 or a multiple of kSimdWidth + // because then it is also an input dimension. + if constexpr (kOutputDimensions % 4 == 0) + { + for (IndexType i = 0; i < kOutputDimensions; i += 4) + { + const IndexType offset0 = (i + 0) * kPaddedInputDimensions; + const IndexType offset1 = (i + 1) * kPaddedInputDimensions; + const IndexType offset2 = (i + 2) * kPaddedInputDimensions; + const IndexType offset3 = (i + 3) * kPaddedInputDimensions; + + const __m128i bias = *reinterpret_cast(&biases_[i]); + __m128i* outptr = reinterpret_cast<__m128i*>(&output[i]); + + __m128i sum0 = _mm_setzero_si128(); + __m128i sum1 = _mm_setzero_si128(); + __m128i sum2 = _mm_setzero_si128(); + __m128i sum3 = _mm_setzero_si128(); + + const auto row0 = reinterpret_cast(&weights_[offset0]); + const auto row1 = reinterpret_cast(&weights_[offset1]); + const auto row2 = reinterpret_cast(&weights_[offset2]); + const auto row3 = reinterpret_cast(&weights_[offset3]); + + for (int j = 0; j < (int)kNumChunks; j += 1) + { + const __m128i in = input_vector[j]; + + m128_add_dpbusd_epi32(sum0, in, row0[j]); + m128_add_dpbusd_epi32(sum1, in, row1[j]); + m128_add_dpbusd_epi32(sum2, in, row2[j]); + m128_add_dpbusd_epi32(sum3, in, row3[j]); + } + + *outptr = m128_haddx4(sum0, sum1, sum2, sum3, bias); + } + } + else if constexpr (kOutputDimensions == 1) + { + __m128i sum0 = _mm_setzero_si128(); + + const auto row0 = reinterpret_cast(&weights_[0]); + + for (int j = 0; j < (int)kNumChunks; j += 1) + { + const __m128i in = input_vector[j]; + + m128_add_dpbusd_epi32(sum0, in, row0[j]); + } + + output[0] = m128_hadd(sum0, biases_[0]); + } + else + { + // This case can never happen because kOutputDimensions + // is always 1 or a multiple of kSimdWidth. + assert(false); + } + +#else + +// Use old implementation for the other architectures. + + auto output = reinterpret_cast(buffer); + +#if defined(USE_SSE2) + constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; +#ifndef USE_SSSE3 const __m128i kZeros = _mm_setzero_si128(); - #else +#else const __m128i kOnes = _mm_set1_epi16(1); - #endif +#endif const auto input_vector = reinterpret_cast(input); - #elif defined(USE_MMX) +#elif defined(USE_MMX) constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; const __m64 kZeros = _mm_setzero_si64(); const auto input_vector = reinterpret_cast(input); - #elif defined(USE_NEON) +#elif defined(USE_NEON) constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; const auto input_vector = reinterpret_cast(input); - #endif +#endif for (IndexType i = 0; i < kOutputDimensions; ++i) { const IndexType offset = i * kPaddedInputDimensions; - #if defined(USE_AVX512) - __m512i sum = _mm512_setzero_si512(); - const auto row = reinterpret_cast(&weights_[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { - #if defined(USE_VNNI) - sum = _mm512_dpbusd_epi32(sum, _mm512_loadA_si512(&input_vector[j]), _mm512_load_si512(&row[j])); - #else - __m512i product = _mm512_maddubs_epi16(_mm512_loadA_si512(&input_vector[j]), _mm512_load_si512(&row[j])); - product = _mm512_madd_epi16(product, kOnes); - sum = _mm512_add_epi32(sum, product); - #endif - } - - // Note: Changing kMaxSimdWidth from 32 to 64 breaks loading existing networks. - // As a result kPaddedInputDimensions may not be an even multiple of 64(512bit) - // and we have to do one more 256bit chunk. - if (kPaddedInputDimensions != kNumChunks * kSimdWidth * 2) - { - const auto iv256 = reinterpret_cast(&input_vector[kNumChunks]); - const auto row256 = reinterpret_cast(&row[kNumChunks]); - #if defined(USE_VNNI) - __m256i product256 = _mm256_dpbusd_epi32( - _mm512_castsi512_si256(sum), _mm256_loadA_si256(&iv256[0]), _mm256_load_si256(&row256[0])); - sum = _mm512_inserti32x8(sum, product256, 0); - #else - __m256i product256 = _mm256_maddubs_epi16(_mm256_loadA_si256(&iv256[0]), _mm256_load_si256(&row256[0])); - sum = _mm512_add_epi32(sum, _mm512_cvtepi16_epi32(product256)); - #endif - } - output[i] = _mm512_reduce_add_epi32(sum) + biases_[i]; - - #elif defined(USE_AVX2) - __m256i sum = _mm256_setzero_si256(); - const auto row = reinterpret_cast(&weights_[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { - #if defined(USE_VNNI) - sum = _mm256_dpbusd_epi32(sum, _mm256_loadA_si256(&input_vector[j]), _mm256_load_si256(&row[j])); - #else - __m256i product = _mm256_maddubs_epi16(_mm256_loadA_si256(&input_vector[j]), _mm256_load_si256(&row[j])); - product = _mm256_madd_epi16(product, kOnes); - sum = _mm256_add_epi32(sum, product); - #endif - } - __m128i sum128 = _mm_add_epi32(_mm256_castsi256_si128(sum), _mm256_extracti128_si256(sum, 1)); - sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_BADC)); - sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_CDAB)); - output[i] = _mm_cvtsi128_si32(sum128) + biases_[i]; - - #elif defined(USE_SSSE3) - __m128i sum = _mm_setzero_si128(); - const auto row = reinterpret_cast(&weights_[offset]); - for (int j = 0; j < (int)kNumChunks - 1; j += 2) { - __m128i product0 = _mm_maddubs_epi16(_mm_load_si128(&input_vector[j]), _mm_load_si128(&row[j])); - product0 = _mm_madd_epi16(product0, kOnes); - sum = _mm_add_epi32(sum, product0); - __m128i product1 = _mm_maddubs_epi16(_mm_load_si128(&input_vector[j+1]), _mm_load_si128(&row[j+1])); - product1 = _mm_madd_epi16(product1, kOnes); - sum = _mm_add_epi32(sum, product1); - } - if (kNumChunks & 0x1) { - __m128i product = _mm_maddubs_epi16(_mm_load_si128(&input_vector[kNumChunks-1]), _mm_load_si128(&row[kNumChunks-1])); - product = _mm_madd_epi16(product, kOnes); - sum = _mm_add_epi32(sum, product); - } - sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0x4E)); //_MM_PERM_BADC - sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0xB1)); //_MM_PERM_CDAB - output[i] = _mm_cvtsi128_si32(sum) + biases_[i]; - - #elif defined(USE_SSE2) +#if defined(USE_SSE2) __m128i sum_lo = _mm_cvtsi32_si128(biases_[i]); __m128i sum_hi = kZeros; const auto row = reinterpret_cast(&weights_[offset]); @@ -204,7 +491,7 @@ namespace Eval::NNUE::Layers { sum = _mm_add_epi32(sum, sum_second_32); output[i] = _mm_cvtsi128_si32(sum); - #elif defined(USE_MMX) +#elif defined(USE_MMX) __m64 sum_lo = _mm_cvtsi32_si64(biases_[i]); __m64 sum_hi = kZeros; const auto row = reinterpret_cast(&weights_[offset]); @@ -225,7 +512,7 @@ namespace Eval::NNUE::Layers { sum = _mm_add_pi32(sum, _mm_unpackhi_pi32(sum, sum)); output[i] = _mm_cvtsi64_si32(sum); - #elif defined(USE_NEON) +#elif defined(USE_NEON) int32x4_t sum = {biases_[i]}; const auto row = reinterpret_cast(&weights_[offset]); for (IndexType j = 0; j < kNumChunks; ++j) { @@ -235,18 +522,21 @@ namespace Eval::NNUE::Layers { } output[i] = sum[0] + sum[1] + sum[2] + sum[3]; - #else +#else OutputType sum = biases_[i]; for (IndexType j = 0; j < kInputDimensions; ++j) { sum += weights_[offset + j] * input[j]; } output[i] = sum; - #endif +#endif } - #if defined(USE_MMX) +#if defined(USE_MMX) _mm_empty(); - #endif +#endif + +#endif + return output; } From 931070b65ac0332469a24765a60eb27e038f73bc Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Thu, 29 Oct 2020 17:33:18 +0300 Subject: [PATCH 0434/1766] Elo Worth in King Danger Adding the EloWorth for each term in King Danger. Should be useful for simplifications, tuning patches, and new ideas. closes https://github.com/official-stockfish/Stockfish/pull/3204 non-functional change --- src/evaluate.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 030d1017579..4ade46fa4a1 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -582,18 +582,18 @@ namespace { int kingFlankAttack = popcount(b1) + popcount(b2); int kingFlankDefense = popcount(b3); - kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them] - + 185 * popcount(kingRing[Us] & weak) - + 148 * popcount(unsafeChecks) - + 98 * popcount(pos.blockers_for_king(Us)) - + 69 * kingAttacksCount[Them] - + 3 * kingFlankAttack * kingFlankAttack / 8 - + mg_value(mobility[Them] - mobility[Us]) - - 873 * !pos.count(Them) - - 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING]) - - 6 * mg_value(score) / 8 - - 4 * kingFlankDefense - + 37; + kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them] // (~10 Elo) + + 185 * popcount(kingRing[Us] & weak) // (~15 Elo) + + 148 * popcount(unsafeChecks) // (~4 Elo) + + 98 * popcount(pos.blockers_for_king(Us)) // (~2 Elo) + + 69 * kingAttacksCount[Them] // (~0.5 Elo) + + 3 * kingFlankAttack * kingFlankAttack / 8 // (~0.5 Elo) + + mg_value(mobility[Them] - mobility[Us]) // (~0.5 Elo) + - 873 * !pos.count(Them) // (~24 Elo) + - 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING]) // (~5 Elo) + - 6 * mg_value(score) / 8 // (~8 Elo) + - 4 * kingFlankDefense // (~5 Elo) + + 37; // (~0.5 Elo) // Transform the kingDanger units into a Score, and subtract it from the evaluation if (kingDanger > 100) From a260c9a8a24a2630a900efc3821000c3481b0c5d Mon Sep 17 00:00:00 2001 From: "J. Oster" Date: Sun, 1 Nov 2020 18:33:17 +0100 Subject: [PATCH 0435/1766] Fix incorrect pruning in qsearch Only do countermove based pruning in qsearch if we already have a move with a better score than a TB loss. This patch fixes a bug (started as 843a961) that incorrectly prunes moves if in check, and adds an assert to make sure no wrong mate scores are given in the future. It replaces a no-op moveCount check with a check for bestValue. Initially discussed in #3171 and later in #3199, #3198 and #3210. This PR effectively closes #3171 It also likely fixes #3196 where this causes user visible incorrect TB scores, which probably result from these incorrect mate scores. Passed STC and LTC non-regression tests. https://tests.stockfishchess.org/tests/view/5f9ef8dabca9bf35bae7f648 LLR: 2.93 (-2.94,2.94) {-1.25,0.25} Total: 21672 W: 2339 L: 2230 D: 17103 Ptnml(0-2): 126, 1689, 7083, 1826, 112 https://tests.stockfishchess.org/tests/view/5f9f0caebca9bf35bae7f666 LLR: 2.97 (-2.94,2.94) {-0.75,0.25} Total: 33152 W: 1551 L: 1485 D: 30116 Ptnml(0-2): 27, 1308, 13832, 1390, 19 closes https://github.com/official-stockfish/Stockfish/pull/3214 Bench: 3625915 --- src/search.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 65ed9b73ffb..743449fa39c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1565,7 +1565,7 @@ namespace { // CounterMove based pruning if ( !captureOrPromotion - && moveCount + && bestValue > VALUE_TB_LOSS_IN_MAX_PLY && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < CounterMovePruneThreshold && (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < CounterMovePruneThreshold) continue; @@ -1600,7 +1600,11 @@ namespace { // All legal moves have been searched. A special case: if we're in check // and no legal moves were found, it is checkmate. if (ss->inCheck && bestValue == -VALUE_INFINITE) + { + assert(!MoveList(pos).size()); + return mated_in(ss->ply); // Plies to mate from the root + } tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit, bestValue >= beta ? BOUND_LOWER : From 3f6451eff7c62e8d4a33c5b11f055a81b3da8387 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Tue, 3 Nov 2020 11:23:35 +0100 Subject: [PATCH 0436/1766] Manually align arrays on the stack as a workaround to issues with overaligned alignas() on stack variables in gcc < 9.3 on windows. closes https://github.com/official-stockfish/Stockfish/pull/3217 fixes #3216 No functional change --- src/misc.h | 12 ++++++++++++ src/nnue/evaluate_nnue.cpp | 25 ++++++++++++++++++++++--- src/nnue/layers/clipped_relu.h | 10 +++++----- src/nnue/nnue_common.h | 23 ----------------------- src/nnue/nnue_feature_transformer.h | 14 +++++++------- src/position.cpp | 4 ++++ src/search.cpp | 8 ++++++++ src/types.h | 6 ++++++ 8 files changed, 64 insertions(+), 38 deletions(-) diff --git a/src/misc.h b/src/misc.h index bc48f303a88..682ef81641c 100644 --- a/src/misc.h +++ b/src/misc.h @@ -24,6 +24,7 @@ #include #include #include +#include #include "types.h" @@ -63,6 +64,17 @@ std::ostream& operator<<(std::ostream&, SyncCout); #define sync_cout std::cout << IO_LOCK #define sync_endl std::endl << IO_UNLOCK +// `ptr` must point to an array of size at least +// `sizeof(T) * N + alignment` bytes, where `N` is the +// number of elements in the array. +template +T* align_ptr_up(T* ptr) +{ + static_assert(alignof(T) < Alignment); + + const uintptr_t ptrint = reinterpret_cast(reinterpret_cast(ptr)); + return reinterpret_cast(reinterpret_cast((ptrint + (Alignment - 1)) / Alignment * Alignment)); +} /// xorshift64star Pseudo-Random Number Generator /// This class is based on original code written and dedicated diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index b5dcd992fa3..b0ed7d2f5a4 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -25,6 +25,7 @@ #include "../position.h" #include "../misc.h" #include "../uci.h" +#include "../types.h" #include "evaluate_nnue.h" @@ -126,10 +127,28 @@ namespace Eval::NNUE { // Evaluation function. Perform differential calculation. Value evaluate(const Position& pos) { - alignas(kCacheLineSize) TransformedFeatureType - transformed_features[FeatureTransformer::kBufferSize]; + // We manually align the arrays on the stack because with gcc < 9.3 + // overaligning stack variables with alignas() doesn't work correctly. + + constexpr uint64_t alignment = kCacheLineSize; + +#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) + TransformedFeatureType transformed_features_unaligned[ + FeatureTransformer::kBufferSize + alignment / sizeof(TransformedFeatureType)]; + char buffer_unaligned[Network::kBufferSize + alignment]; + + auto* transformed_features = align_ptr_up(&transformed_features_unaligned[0]); + auto* buffer = align_ptr_up(&buffer_unaligned[0]); +#else + alignas(alignment) + TransformedFeatureType transformed_features[FeatureTransformer::kBufferSize]; + alignas(alignment) char buffer[Network::kBufferSize]; +#endif + + ASSERT_ALIGNED(transformed_features, alignment); + ASSERT_ALIGNED(buffer, alignment); + feature_transformer->Transform(pos, transformed_features); - alignas(kCacheLineSize) char buffer[Network::kBufferSize]; const auto output = network->Propagate(transformed_features, buffer); return static_cast(output[0] / FV_SCALE); diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index 44d8a7def4c..7f6d67bfe3b 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -74,12 +74,12 @@ namespace Eval::NNUE::Layers { const auto out = reinterpret_cast<__m256i*>(output); for (IndexType i = 0; i < kNumChunks; ++i) { const __m256i words0 = _mm256_srai_epi16(_mm256_packs_epi32( - _mm256_loadA_si256(&in[i * 4 + 0]), - _mm256_loadA_si256(&in[i * 4 + 1])), kWeightScaleBits); + _mm256_load_si256(&in[i * 4 + 0]), + _mm256_load_si256(&in[i * 4 + 1])), kWeightScaleBits); const __m256i words1 = _mm256_srai_epi16(_mm256_packs_epi32( - _mm256_loadA_si256(&in[i * 4 + 2]), - _mm256_loadA_si256(&in[i * 4 + 3])), kWeightScaleBits); - _mm256_storeA_si256(&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8( + _mm256_load_si256(&in[i * 4 + 2]), + _mm256_load_si256(&in[i * 4 + 3])), kWeightScaleBits); + _mm256_store_si256(&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8( _mm256_packs_epi16(words0, words1), kZero), kOffsets)); } constexpr IndexType kStart = kNumChunks * kSimdWidth; diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index 8afea1866ab..a9664262a47 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -43,29 +43,6 @@ #include #endif -// HACK: Use _mm256_loadu_si256() instead of _mm256_load_si256. Otherwise a binary -// compiled with older g++ crashes because the output memory is not aligned -// even though alignas is specified. -#if defined(USE_AVX2) -#if defined(__GNUC__ ) && (__GNUC__ < 9) && defined(_WIN32) && !defined(__clang__) -#define _mm256_loadA_si256 _mm256_loadu_si256 -#define _mm256_storeA_si256 _mm256_storeu_si256 -#else -#define _mm256_loadA_si256 _mm256_load_si256 -#define _mm256_storeA_si256 _mm256_store_si256 -#endif -#endif - -#if defined(USE_AVX512) -#if defined(__GNUC__ ) && (__GNUC__ < 9) && defined(_WIN32) && !defined(__clang__) -#define _mm512_loadA_si512 _mm512_loadu_si512 -#define _mm512_storeA_si512 _mm512_storeu_si512 -#else -#define _mm512_loadA_si512 _mm512_load_si512 -#define _mm512_storeA_si512 _mm512_store_si512 -#endif -#endif - namespace Eval::NNUE { // Version of the evaluation file diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index f145c848f96..c3f012e412e 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -36,16 +36,16 @@ namespace Eval::NNUE { #ifdef USE_AVX512 typedef __m512i vec_t; - #define vec_load(a) _mm512_loadA_si512(a) - #define vec_store(a,b) _mm512_storeA_si512(a,b) + #define vec_load(a) _mm512_load_si512(a) + #define vec_store(a,b) _mm512_store_si512(a,b) #define vec_add_16(a,b) _mm512_add_epi16(a,b) #define vec_sub_16(a,b) _mm512_sub_epi16(a,b) static constexpr IndexType kNumRegs = 8; // only 8 are needed #elif USE_AVX2 typedef __m256i vec_t; - #define vec_load(a) _mm256_loadA_si256(a) - #define vec_store(a,b) _mm256_storeA_si256(a,b) + #define vec_load(a) _mm256_load_si256(a) + #define vec_store(a,b) _mm256_store_si256(a,b) #define vec_add_16(a,b) _mm256_add_epi16(a,b) #define vec_sub_16(a,b) _mm256_sub_epi16(a,b) static constexpr IndexType kNumRegs = 16; @@ -157,11 +157,11 @@ namespace Eval::NNUE { #if defined(USE_AVX2) auto out = reinterpret_cast<__m256i*>(&output[offset]); for (IndexType j = 0; j < kNumChunks; ++j) { - __m256i sum0 = _mm256_loadA_si256( + __m256i sum0 = _mm256_load_si256( &reinterpret_cast(accumulation[perspectives[p]][0])[j * 2 + 0]); - __m256i sum1 = _mm256_loadA_si256( + __m256i sum1 = _mm256_load_si256( &reinterpret_cast(accumulation[perspectives[p]][0])[j * 2 + 1]); - _mm256_storeA_si256(&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8( + _mm256_store_si256(&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8( _mm256_packs_epi16(sum0, sum1), kZero), kControl)); } diff --git a/src/position.cpp b/src/position.cpp index b707293dd73..5ce7da22894 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -77,6 +77,8 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) { && !pos.can_castle(ANY_CASTLING)) { StateInfo st; + ASSERT_ALIGNED(&st, Eval::NNUE::kCacheLineSize); + Position p; p.set(pos.fen(), pos.is_chess960(), &st, pos.this_thread()); Tablebases::ProbeState s1, s2; @@ -1318,6 +1320,8 @@ bool Position::pos_is_ok() const { assert(0 && "pos_is_ok: Bitboards"); StateInfo si = *st; + ASSERT_ALIGNED(&si, Eval::NNUE::kCacheLineSize); + set_state(&si); if (std::memcmp(&si, st, sizeof(StateInfo))) assert(0 && "pos_is_ok: State"); diff --git a/src/search.cpp b/src/search.cpp index 743449fa39c..12c32194eb9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -164,6 +164,8 @@ namespace { uint64_t perft(Position& pos, Depth depth) { StateInfo st; + ASSERT_ALIGNED(&st, Eval::NNUE::kCacheLineSize); + uint64_t cnt, nodes = 0; const bool leaf = (depth == 2); @@ -590,6 +592,8 @@ namespace { Move pv[MAX_PLY+1], capturesSearched[32], quietsSearched[64]; StateInfo st; + ASSERT_ALIGNED(&st, Eval::NNUE::kCacheLineSize); + TTEntry* tte; Key posKey; Move ttMove, move, excludedMove, bestMove; @@ -1403,6 +1407,8 @@ namespace { Move pv[MAX_PLY+1]; StateInfo st; + ASSERT_ALIGNED(&st, Eval::NNUE::kCacheLineSize); + TTEntry* tte; Key posKey; Move ttMove, move, bestMove; @@ -1898,6 +1904,8 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) { bool RootMove::extract_ponder_from_tt(Position& pos) { StateInfo st; + ASSERT_ALIGNED(&st, Eval::NNUE::kCacheLineSize); + bool ttHit; assert(pv.size() == 1); diff --git a/src/types.h b/src/types.h index 5873c698f73..bf692f7e645 100644 --- a/src/types.h +++ b/src/types.h @@ -57,6 +57,12 @@ /// _WIN32 Building on Windows (any) /// _WIN64 Building on Windows 64 bit +#if defined(__GNUC__ ) && (__GNUC__ < 9 || (__GNUC__ == 9 && __GNUC_MINOR__ <= 2)) && defined(_WIN32) && !defined(__clang__) +#define ALIGNAS_ON_STACK_VARIABLES_BROKEN +#endif + +#define ASSERT_ALIGNED(ptr, alignment) assert(reinterpret_cast(ptr) % alignment == 0) + #if defined(_WIN64) && defined(_MSC_VER) // No Makefile used # include // Microsoft header for _BitScanForward64() # define IS_64BIT From 04a320666efce725ef66d1a84aaef493a880153d Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 23 Oct 2020 07:39:35 +0200 Subject: [PATCH 0437/1766] Change handling the special case of a single legal move. Using no searching time in case of a single legal move is not beneficial from a strength point of view, and this special case can be easily removed: STC: LLR: 2.93 (-2.94,2.94) {-1.25,0.25} Total: 22472 W: 2458 L: 2357 D: 17657 Ptnml(0-2): 106, 1733, 7453, 1842, 102 https://tests.stockfishchess.org/tests/view/5f926cbc81eda81bd78cb6df LTC: LLR: 2.94 (-2.94,2.94) {-0.75,0.25} Total: 37880 W: 1736 L: 1682 D: 34462 Ptnml(0-2): 22, 1392, 16057, 1448, 21 https://tests.stockfishchess.org/tests/view/5f92a26081eda81bd78cb6fe The advantage of using the normal time management for a single legal move is that scores reported for that move are reasonable, not searching leads to artifacts during games (see e.g. https://tcec-chess.com/#div=sf&game=96&season=19) The disadvantage of using normal time management of a single legal move is that thinking times can be unnaturally long, making it 'painful to watch' in online tournaments. This patch uses normal time management, but caps the used time to 500ms. This should lead to reasonable scores, and be hardly perceptible. closes https://github.com/official-stockfish/Stockfish/pull/3195 closes https://github.com/official-stockfish/Stockfish/pull/3183 variant of a patch suggested by SFisGOD No functional change. --- src/search.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 12c32194eb9..6e37fba1e52 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -521,10 +521,14 @@ void Thread::search() { } double bestMoveInstability = 1 + 2 * totBestMoveChanges / Threads.size(); - double totalTime = rootMoves.size() == 1 ? 0 : - Time.optimum() * fallingEval * reduction * bestMoveInstability; + double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability; - // Stop the search if we have exceeded the totalTime, at least 1ms search + // Cap used time in case of a single legal move for a better viewer experience in tournaments + // yielding correct scores and sufficiently fast moves. + if (rootMoves.size() == 1) + totalTime = std::min(500.0, totalTime); + + // Stop the search if we have exceeded the totalTime if (Time.elapsed() > totalTime) { // If we are allowed to ponder do not stop the search now but From 7fc47eeb6f6b5f3c5ff697e974093ff14413e42c Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Thu, 5 Nov 2020 01:54:53 +0200 Subject: [PATCH 0438/1766] Introducing King On File this new concept calculates bonuses/penalties for the king when the king is in a semiopen or open file. Passed STC: LLR: 2.95 (-2.94,2.94) {-0.25,1.25} Total: 44904 W: 9365 L: 9028 D: 26511 Ptnml(0-2): 857, 5309, 9841, 5530, 915 https://tests.stockfishchess.org/tests/view/5fa343625d72639a7acef72b Passed LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.25} Total: 60552 W: 8449 L: 8051 D: 44052 Ptnml(0-2): 466, 5772, 17481, 6012, 545 https://tests.stockfishchess.org/tests/view/5fa40e365d72639a7acef79e closes https://github.com/official-stockfish/Stockfish/pull/3219 Bench: 3689484 --- src/pawns.cpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index a5102db8aeb..fde70ba5306 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -49,10 +49,10 @@ namespace { // Strength of pawn shelter for our king by [distance from edge][rank]. // RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king. constexpr Value ShelterStrength[int(FILE_NB) / 2][RANK_NB] = { - { V( -6), V( 81), V( 93), V( 58), V( 39), V( 18), V( 25) }, - { V(-43), V( 61), V( 35), V(-49), V(-29), V(-11), V( -63) }, - { V(-10), V( 75), V( 23), V( -2), V( 32), V( 3), V( -45) }, - { V(-39), V(-13), V(-29), V(-52), V(-48), V(-67), V(-166) } + { V( -5), V( 82), V( 92), V( 54), V( 36), V( 22), V( 28) }, + { V(-44), V( 63), V( 33), V(-50), V(-30), V(-12), V( -62) }, + { V(-11), V( 77), V( 22), V( -6), V( 31), V( 8), V( -45) }, + { V(-39), V(-12), V(-29), V(-50), V(-43), V(-68), V(-164) } }; // Danger of enemy pawns moving toward our king by [distance from edge][rank]. @@ -60,12 +60,17 @@ namespace { // 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( 85), V(-289), V(-166), V(97), V(50), V( 45), V( 50) }, - { V( 46), V( -25), V( 122), V(45), V(37), V(-10), V( 20) }, - { V( -6), V( 51), V( 168), V(34), V(-2), V(-22), V(-14) }, - { V(-15), V( -11), V( 101), V( 4), V(11), V(-15), V(-29) } + { V( 87), V(-288), V(-168), V( 96), V( 47), V( 44), V( 46) }, + { V( 42), V( -25), V( 120), V( 45), V( 34), V( -9), V( 24) }, + { V( -8), V( 51), V( 167), V( 35), V( -4), V(-16), V(-12) }, + { V(-17), V( -13), V( 100), V( 4), V( 9), V(-16), V(-31) } }; + // KingOnFile[semi-open Us][semi-open Them] contains bonuses/penalties + // for king when the king is on a semi-open or open file. + constexpr Score KingOnFile[2][2] = {{ S(-19,12), S(-6, 7) }, + { S( 0, 2), S( 6,-5) }}; + #undef S #undef V @@ -237,6 +242,9 @@ Score Entry::evaluate_shelter(const Position& pos, Square ksq) const { bonus -= make_score(UnblockedStorm[d][theirRank], 0); } + // King On File + bonus -= KingOnFile[pos.is_on_semiopen_file(Us, ksq)][pos.is_on_semiopen_file(Them, ksq)]; + return bonus; } From ba35c88ab84b959d41a67b3d8fcb40adc6537ec8 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Tue, 3 Nov 2020 22:49:10 +0100 Subject: [PATCH 0439/1766] AVX-512 for smaller affine and feature transforms. For the feature transformer the code is analogical to AVX2 since there was room for easy adaptation of wider simd registers. For the smaller affine transforms that have 32 byte stride we keep 2 columns in one zmm register. We also unroll more aggressively so that in the end we have to do 16 parallel horizontal additions on ymm slices each consisting of 4 32-bit integers. The slices are embedded in 8 zmm registers. These changes provide about 1.5% speedup for AVX-512 builds. Closes https://github.com/official-stockfish/Stockfish/pull/3218 No functional change. --- src/nnue/layers/affine_transform.h | 129 +++++++++++++++++++++++++++- src/nnue/nnue_feature_transformer.h | 27 ++++-- 2 files changed, 148 insertions(+), 8 deletions(-) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index f0292e453c1..47c9c488b0c 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -83,7 +83,21 @@ namespace Eval::NNUE::Layers { return _mm512_reduce_add_epi32(sum) + bias; }; - [[maybe_unused]] auto m512_haddx4 = [](__m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3, __m128i bias) -> __m128i { + // This function takes + // sum0 = [xmm0a, xmm0b, xmm0c, xmm0d] + // sum1 = [xmm1a, xmm1b, xmm1c, xmm1d] + // sum2 = [xmm2a, xmm2b, xmm2c, xmm2d] + // sum3 = [xmm3a, xmm3b, xmm3c, xmm3d] + // and returns + // ret = [ + // reduce_add_epi32(xmm0a), reduce_add_epi32(xmm1a), reduce_add_epi32(xmm2a), reduce_add_epi32(xmm3a), + // reduce_add_epi32(xmm0b), reduce_add_epi32(xmm1b), reduce_add_epi32(xmm2b), reduce_add_epi32(xmm3b), + // reduce_add_epi32(xmm0c), reduce_add_epi32(xmm1c), reduce_add_epi32(xmm2c), reduce_add_epi32(xmm3c), + // reduce_add_epi32(xmm0d), reduce_add_epi32(xmm1d), reduce_add_epi32(xmm2d), reduce_add_epi32(xmm3d) + // ] + [[maybe_unused]] auto m512_hadd128x16_interleave = []( + __m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3) -> __m512i { + __m512i sum01a = _mm512_unpacklo_epi32(sum0, sum1); __m512i sum01b = _mm512_unpackhi_epi32(sum0, sum1); @@ -96,7 +110,13 @@ namespace Eval::NNUE::Layers { __m512i sum0123a = _mm512_unpacklo_epi64(sum01, sum23); __m512i sum0123b = _mm512_unpackhi_epi64(sum01, sum23); - __m512i sum = _mm512_add_epi32(sum0123a, sum0123b); + return _mm512_add_epi32(sum0123a, sum0123b); + }; + + [[maybe_unused]] auto m512_haddx4 = [m512_hadd128x16_interleave]( + __m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3, __m128i bias) -> __m128i { + + __m512i sum = m512_hadd128x16_interleave(sum0, sum1, sum2, sum3); __m256i sum256lo = _mm512_castsi512_si256(sum); __m256i sum256hi = _mm512_extracti64x4_epi64(sum, 1); @@ -109,6 +129,58 @@ namespace Eval::NNUE::Layers { return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias); }; + [[maybe_unused]] auto m512_haddx8 = [m512_hadd128x16_interleave]( + __m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3, + __m512i sum4, __m512i sum5, __m512i sum6, __m512i sum7, __m256i bias) -> __m256i { + + __m512i suma = m512_hadd128x16_interleave(sum0, sum1, sum2, sum3); + __m512i sumb = m512_hadd128x16_interleave(sum4, sum5, sum6, sum7); + + __m512i indices0 = _mm512_setr_epi64(0, 1, 8, 9, 4, 5, 12, 13); + __m512i indices1 = _mm512_setr_epi64(2, 3, 10, 11, 6, 7, 14, 15); + __m512i x = _mm512_add_epi32( + _mm512_permutex2var_epi64(suma, indices0, sumb), + _mm512_permutex2var_epi64(suma, indices1, sumb)); + + __m256i sum256lo = _mm512_castsi512_si256(x); + __m256i sum256hi = _mm512_extracti64x4_epi64(x, 1); + + return _mm256_add_epi32(_mm256_add_epi32(sum256lo, sum256hi), bias); + }; + + [[maybe_unused]] auto m512_hadd256x8 =[m512_hadd128x16_interleave]( + __m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3, __m256i bias) -> __m256i { + + __m512i sum = m512_hadd128x16_interleave(sum0, sum1, sum2, sum3); + + __m512i indices = _mm512_setr_epi32( + 0, 4, 8, 12, 2, 6, 10, 14, + 1, 5, 9, 13, 3, 7, 11, 15); + sum = _mm512_permutexvar_epi32(indices, sum); + + __m256i sum256lo = _mm512_castsi512_si256(sum); + __m256i sum256hi = _mm512_extracti64x4_epi64(sum, 1); + + return _mm256_add_epi32(_mm256_hadd_epi32(sum256lo, sum256hi), bias); + }; + + [[maybe_unused]] auto m512_hadd256x16 = [m512_hadd128x16_interleave]( + __m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3, + __m512i sum4, __m512i sum5, __m512i sum6, __m512i sum7, __m512i bias) -> __m512i { + + __m512i suma = m512_hadd128x16_interleave(sum0, sum1, sum2, sum3); + __m512i sumb = m512_hadd128x16_interleave(sum4, sum5, sum6, sum7); + + __m512i indices0 = _mm512_setr_epi64(0, 1, 8, 9, 4, 5, 12, 13); + __m512i indices1 = _mm512_setr_epi64(2, 3, 10, 11, 6, 7, 14, 15); + __m512i x = _mm512_add_epi32( + _mm512_permutex2var_epi64(suma, indices0, sumb), + _mm512_permutex2var_epi64(suma, indices1, sumb)); + + __m512i indices = _mm512_setr_epi32(0, 8, 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15); + return _mm512_add_epi32(_mm512_permutexvar_epi32(indices, x), bias); + }; + [[maybe_unused]] auto m512_add_dpbusd_epi32 = [=](__m512i& acc, __m512i a, __m512i b) { #if defined (USE_VNNI) acc = _mm512_dpbusd_epi32(acc, a, b); @@ -205,7 +277,58 @@ namespace Eval::NNUE::Layers { // kOutputDimensions is either 1 or a multiple of kSimdWidth // because then it is also an input dimension. - if constexpr (kOutputDimensions % 4 == 0) + if constexpr (kOutputDimensions % 16 == 0 && kNumChunks256 == 1) + { + for (IndexType i = 0; i < kOutputDimensions; i += 16) + { + const IndexType offset01a = (i + 0) * kPaddedInputDimensions; + const IndexType offset23a = (i + 2) * kPaddedInputDimensions; + const IndexType offset45a = (i + 4) * kPaddedInputDimensions; + const IndexType offset67a = (i + 6) * kPaddedInputDimensions; + const IndexType offset01b = (i + 8) * kPaddedInputDimensions; + const IndexType offset23b = (i + 10) * kPaddedInputDimensions; + const IndexType offset45b = (i + 12) * kPaddedInputDimensions; + const IndexType offset67b = (i + 14) * kPaddedInputDimensions; + + const __m512i bias = *reinterpret_cast(&biases_[i]); + __m512i* outptr = reinterpret_cast<__m512i*>(&output[i]); + + __m512i sum01a = _mm512_setzero_si512(); + __m512i sum23a = _mm512_setzero_si512(); + __m512i sum45a = _mm512_setzero_si512(); + __m512i sum67a = _mm512_setzero_si512(); + __m512i sum01b = _mm512_setzero_si512(); + __m512i sum23b = _mm512_setzero_si512(); + __m512i sum45b = _mm512_setzero_si512(); + __m512i sum67b = _mm512_setzero_si512(); + + const auto row01a = *reinterpret_cast(&weights_[offset01a]); + const auto row23a = *reinterpret_cast(&weights_[offset23a]); + const auto row45a = *reinterpret_cast(&weights_[offset45a]); + const auto row67a = *reinterpret_cast(&weights_[offset67a]); + const auto row01b = *reinterpret_cast(&weights_[offset01b]); + const auto row23b = *reinterpret_cast(&weights_[offset23b]); + const auto row45b = *reinterpret_cast(&weights_[offset45b]); + const auto row67b = *reinterpret_cast(&weights_[offset67b]); + + const __m256i in256 = input_vector256[0]; + const __m512i in = _mm512_inserti64x4(_mm512_castsi256_si512(in256), in256, 1); + + m512_add_dpbusd_epi32(sum01a, in, row01a); + m512_add_dpbusd_epi32(sum23a, in, row23a); + m512_add_dpbusd_epi32(sum45a, in, row45a); + m512_add_dpbusd_epi32(sum67a, in, row67a); + m512_add_dpbusd_epi32(sum01b, in, row01b); + m512_add_dpbusd_epi32(sum23b, in, row23b); + m512_add_dpbusd_epi32(sum45b, in, row45b); + m512_add_dpbusd_epi32(sum67b, in, row67b); + + *outptr = m512_hadd256x16( + sum01a, sum23a, sum45a, sum67a, + sum01b, sum23b, sum45b, sum67b, bias); + } + } + else if constexpr (kOutputDimensions % 4 == 0) { for (IndexType i = 0; i < kOutputDimensions; i += 4) { diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index c3f012e412e..f49777b50bb 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -127,7 +127,13 @@ namespace Eval::NNUE { const auto& accumulation = pos.state()->accumulator.accumulation; - #if defined(USE_AVX2) + #if defined(USE_AVX512) + constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth * 2); + static_assert(kHalfDimensions % (kSimdWidth * 2) == 0); + const __m512i kControl = _mm512_setr_epi64(0, 2, 4, 6, 1, 3, 5, 7); + const __m512i kZero = _mm512_setzero_si512(); + + #elif defined(USE_AVX2) constexpr IndexType kNumChunks = kHalfDimensions / kSimdWidth; constexpr int kControl = 0b11011000; const __m256i kZero = _mm256_setzero_si256(); @@ -154,13 +160,24 @@ namespace Eval::NNUE { for (IndexType p = 0; p < 2; ++p) { const IndexType offset = kHalfDimensions * p; - #if defined(USE_AVX2) + #if defined(USE_AVX512) + auto out = reinterpret_cast<__m512i*>(&output[offset]); + for (IndexType j = 0; j < kNumChunks; ++j) { + __m512i sum0 = _mm512_load_si512( + &reinterpret_cast(accumulation[perspectives[p]][0])[j * 2 + 0]); + __m512i sum1 = _mm512_load_si512( + &reinterpret_cast(accumulation[perspectives[p]][0])[j * 2 + 1]); + _mm512_store_si512(&out[j], _mm512_permutexvar_epi64(kControl, + _mm512_max_epi8(_mm512_packs_epi16(sum0, sum1), kZero))); + } + + #elif defined(USE_AVX2) auto out = reinterpret_cast<__m256i*>(&output[offset]); for (IndexType j = 0; j < kNumChunks; ++j) { __m256i sum0 = _mm256_load_si256( &reinterpret_cast(accumulation[perspectives[p]][0])[j * 2 + 0]); __m256i sum1 = _mm256_load_si256( - &reinterpret_cast(accumulation[perspectives[p]][0])[j * 2 + 1]); + &reinterpret_cast(accumulation[perspectives[p]][0])[j * 2 + 1]); _mm256_store_si256(&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8( _mm256_packs_epi16(sum0, sum1), kZero), kControl)); } @@ -177,9 +194,9 @@ namespace Eval::NNUE { _mm_store_si128(&out[j], #ifdef USE_SSE41 - _mm_max_epi8(packedbytes, kZero) + _mm_max_epi8(packedbytes, kZero) #else - _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s) + _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s) #endif ); From 32edb1d009e09a9442cb7393920e072ffd08005d Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Sat, 7 Nov 2020 08:50:02 +0800 Subject: [PATCH 0440/1766] Update default net to nn-c3ca321c51c9.nnue Optimization of the net biases of the 32 x 32 layer and the output layer. Tuning of 32 x 32 layer (200k games, 5 seconds TC) https://tests.stockfishchess.org/tests/view/5f9aaf266a2c112b60691c68 STC: LLR: 2.95 (-2.94,2.94) {-0.25,1.25} Total: 41848 W: 4665 L: 4461 D: 32722 Ptnml(0-2): 239, 3308, 13659, 3446, 272 https://tests.stockfishchess.org/tests/view/5fa5ef5a936c54e11ec9954f LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.25} Total: 88008 W: 4045 L: 3768 D: 80195 Ptnml(0-2): 69, 3339, 36908, 3622, 66 https://tests.stockfishchess.org/tests/view/5fa62a78936c54e11ec99577 closes https://github.com/official-stockfish/Stockfish/pull/3220 Bench: 3649288 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 6bec27db765..06c66f716f1 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -36,7 +36,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-cb26f10b1fd9.nnue" + #define EvalFileDefaultName "nn-c3ca321c51c9.nnue" namespace NNUE { From 392b529c3f52103ad47ad096b86103c17758cb4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Fri, 6 Nov 2020 19:20:27 +0100 Subject: [PATCH 0441/1766] Qsearch pruning: follow-up This is a follow-up of the recent qsearch pruning patch in https://github.com/official-stockfish/Stockfish/commit/a260c9a8a24a2630a900efc3821000c3481b0c5d We now use the same guard condition (testing that we already have a defense with a score better score than a TB loss) for all pruning heuristics in qsearch(). This allows some pruning when in check, but in a controlled way to ensure that no wrong mate scores appear. Tested with Elo-gaining bounds: STC: LLR: 2.97 (-2.94,2.94) {-0.25,1.25} Total: 22632 W: 2433 L: 2264 D: 17935 Ptnml(0-2): 98, 1744, 7487, 1865, 122 https://tests.stockfishchess.org/tests/view/5fa59405936c54e11ec99515 LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.25} Total: 105432 W: 4965 L: 4648 D: 95819 Ptnml(0-2): 85, 4110, 44011, 4423, 87 https://tests.stockfishchess.org/tests/view/5fa5b609936c54e11ec9952a closes https://github.com/official-stockfish/Stockfish/pull/3221 Bench: 3578092 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 6e37fba1e52..b5b93bf01f6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1525,7 +1525,7 @@ namespace { moveCount++; // Futility pruning - if ( !ss->inCheck + if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY && !givesCheck && futilityBase > -VALUE_KNOWN_WIN && !pos.advanced_pawn_push(move)) @@ -1552,7 +1552,7 @@ namespace { } // Do not search moves with negative SEE values - if ( !ss->inCheck + if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY && !(givesCheck && pos.is_discovery_check_on_king(~pos.side_to_move(), move)) && !pos.see_ge(move)) continue; From b5781150ea8557e2030f8bc8b4eadede0ecec6bd Mon Sep 17 00:00:00 2001 From: lonfom169 <50217346+lonfom169@users.noreply.github.com> Date: Sun, 8 Nov 2020 23:43:32 -0300 Subject: [PATCH 0442/1766] Increase reduction based on the number of best move changes. Thanks to Vizvezdenec for the PvNode idea and also to vondele the !PvNode idea. Passed STC: LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 19120 W: 1998 L: 1839 D: 15283 Ptnml(0-2): 76, 1445, 6375, 1572, 92 https://tests.stockfishchess.org/tests/view/5fa8af3e67cbf42301d6a6c9 Passed LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.25} Total: 75584 W: 3454 L: 3205 D: 68925 Ptnml(0-2): 54, 2832, 31771, 3081, 54 closes https://github.com/official-stockfish/Stockfish/pull/3224 Bench: 3595418 --- AUTHORS | 1 + src/search.cpp | 3 +++ 2 files changed, 4 insertions(+) diff --git a/AUTHORS b/AUTHORS index 198dfa5a908..f0356090efd 100644 --- a/AUTHORS +++ b/AUTHORS @@ -19,6 +19,7 @@ Alain Savard (Rocky640) Alayan Feh (Alayan-stk-2) Alexander Kure Alexander Pagel (Lolligerhans) +Alfredo Menezes (lonfom169) Ali AlZhrani (Cooffe) Andrew Grant (AndyGrant) Andrey Neporada (nepal) diff --git a/src/search.cpp b/src/search.cpp index b5b93bf01f6..56b56733fb2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1169,6 +1169,9 @@ namespace { if (ss->ttPv) r -= 2; + if (!PvNode && depth > 10 && thisThread->bestMoveChanges <= 2) + r++; + if (moveCountPruning && !formerPv) r++; From 285bf7041ad214156188823eb9118e6af7f4b2e4 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Tue, 10 Nov 2020 18:28:43 +0100 Subject: [PATCH 0443/1766] Increase reduction at root when the best move does not change frequently STC: LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 51320 W: 5159 L: 4956 D: 41205 Ptnml(0-2): 215, 3897, 17242, 4082, 224 https://tests.stockfishchess.org/tests/view/5faa072367cbf42301d6a767 LTC: LLR: 2.98 (-2.94,2.94) {0.25,1.25} Total: 15952 W: 762 L: 642 D: 14548 Ptnml(0-2): 8, 561, 6725, 667, 15 https://tests.stockfishchess.org/tests/view/5faa4c3567cbf42301d6a794 closes https://github.com/official-stockfish/Stockfish/pull/3225 Bench: 3954692 --- AUTHORS | 2 +- src/search.cpp | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index f0356090efd..f30be4de107 100644 --- a/AUTHORS +++ b/AUTHORS @@ -86,7 +86,7 @@ Jekaa Jerry Donald Watson (jerrydonaldwatson) jjoshua2 Jonathan Calovski (Mysseno) -Jonathan Dumale (SFisGOD) +Jonathan Buladas Dumale (SFisGOD) Joost VandeVondele (vondele) Jörg Oster (joergoster) Joseph Ellis (jhellis3) diff --git a/src/search.cpp b/src/search.cpp index 56b56733fb2..66ef5043d28 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1161,7 +1161,7 @@ namespace { if (thisThread->ttHitAverage > 509 * TtHitAverageResolution * TtHitAverageWindow / 1024) r--; - // Reduction if other threads are searching this position + // Increase reduction if other threads are searching this position if (th.marked()) r++; @@ -1169,7 +1169,8 @@ namespace { if (ss->ttPv) r -= 2; - if (!PvNode && depth > 10 && thisThread->bestMoveChanges <= 2) + // Increase reduction at root and non-PV nodes when the best move does not change frequently + if ((rootNode || !PvNode) && depth > 10 && thisThread->bestMoveChanges <= 2) r++; if (moveCountPruning && !formerPv) From f9595828eb7e5e970b0be3ee5f84ddd726845523 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Wed, 11 Nov 2020 20:56:29 +0200 Subject: [PATCH 0444/1766] Rook Mobility Tweak Passed STC: LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 171152 W: 34715 L: 34202 D: 102235 Ptnml(0-2): 3278, 20155, 38228, 20606, 3309 https://tests.stockfishchess.org/tests/view/5fa861f467cbf42301d6a68e Passed LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.25} Total: 149616 W: 20471 L: 19882 D: 109263 Ptnml(0-2): 1172, 14434, 43102, 14833, 1267 https://tests.stockfishchess.org/tests/view/5fa9c8ff67cbf42301d6a74f closes https://github.com/official-stockfish/Stockfish/pull/3226 Bench: 3597730 --- src/evaluate.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 4ade46fa4a1..34ebe6c3feb 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -212,9 +212,9 @@ namespace { { S(-47,-59), S(-20,-25), S( 14, -8), S( 29, 12), S( 39, 21), S( 53, 40), // Bishop S( 53, 56), S( 60, 58), S( 62, 65), S( 69, 72), S( 78, 78), S( 83, 87), S( 91, 88), S( 96, 98) }, - { S(-61,-82), S(-20,-17), S( 2, 23) ,S( 3, 40), S( 4, 72), S( 11,100), // Rook - S( 22,104), S( 31,120), S( 39,134), S(40 ,138), S( 41,158), S( 47,163), - S( 59,168), S( 60,169), S( 64,173) }, + { S(-60,-82), S(-24,-15), S( 0, 17) ,S( 3, 43), S( 4, 72), S( 14,100), // Rook + S( 20,102), S( 30,122), S( 41,133), S(41 ,139), S( 41,153), S( 45,160), + S( 57,165), S( 58,170), S( 67,175) }, { S(-29,-49), S(-16,-29), S( -8, -8), S( -8, 17), S( 18, 39), S( 25, 54), // Queen S( 23, 59), S( 37, 73), S( 41, 76), S( 54, 95), S( 65, 95) ,S( 68,101), S( 69,124), S( 70,128), S( 70,132), S( 70,133) ,S( 71,136), S( 72,140), From 027626db1e449597ba2211a0819f251beda37b88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Thu, 12 Nov 2020 14:05:28 +0100 Subject: [PATCH 0445/1766] Small cleanups 13 No functional change --- AUTHORS | 2 +- src/evaluate.cpp | 10 +++++----- src/misc.cpp | 3 +-- src/nnue/nnue_feature_transformer.h | 2 +- src/pawns.cpp | 4 ++-- src/search.cpp | 2 +- src/types.h | 4 ++-- 7 files changed, 13 insertions(+), 14 deletions(-) diff --git a/AUTHORS b/AUTHORS index f30be4de107..71b718b80de 100644 --- a/AUTHORS +++ b/AUTHORS @@ -44,6 +44,7 @@ Daniel Dugovic (ddugovic) Dariusz Orzechowski (dorzechowski) David Zar Daylen Yang (daylen) +Deshawn Mohan-Smith (GoldenRare) DiscanX Dominik Schlösser (domschl) double-beep @@ -64,7 +65,6 @@ Gary Heckman (gheckman) George Sobala (gsobala) gguliash Gian-Carlo Pascutto (gcp) -Deshawn Mohan-Smith (GoldenRare) Gontran Lemaire (gonlem) Goodkov Vasiliy Aleksandrovich (goodkov) Gregor Cramer diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 34ebe6c3feb..1a8cf662c6f 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1025,7 +1025,7 @@ Value Eval::evaluate(const Position& pos) { { // Scale and shift NNUE for compatibility with search and classical evaluation auto adjusted_NNUE = [&](){ - int mat = pos.non_pawn_material() + PieceValue[MG][PAWN] * pos.count(); + int mat = pos.non_pawn_material() + PawnValueMg * pos.count(); return NNUE::evaluate(pos) * (720 + mat / 32) / 1024 + Tempo; }; @@ -1041,10 +1041,10 @@ Value Eval::evaluate(const Position& pos) { // For the case of opposite colored bishops, switch to NNUE eval with // small probability if the classical eval is less than the threshold. if ( largePsq - && (abs(v) * 16 < NNUEThreshold2 * r50 - || ( pos.opposite_bishops() - && abs(v) * 16 < (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50 - && !(pos.this_thread()->nodes & 0xB)))) + && ( abs(v) * 16 < NNUEThreshold2 * r50 + || ( pos.opposite_bishops() + && abs(v) * 16 < (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50 + && !(pos.this_thread()->nodes & 0xB)))) v = adjusted_NNUE(); } diff --git a/src/misc.cpp b/src/misc.cpp index a16a6e90a3d..f2bce6b04f9 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -583,11 +583,10 @@ namespace CommandLine { string argv0; // path+name of the executable binary, as given by argv[0] string binaryDirectory; // path of the executable directory string workingDirectory; // path of the working directory -string pathSeparator; // Separator for our current OS void init(int argc, char* argv[]) { (void)argc; - string separator; + string pathSeparator; // extract the path+name of the executable binary argv0 = argv[0]; diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index f49777b50bb..85bc2bc8e66 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -247,7 +247,7 @@ namespace Eval::NNUE { // Look for a usable accumulator of an earlier position. We keep track // of the estimated gain in terms of features to be added/subtracted. StateInfo *st = pos.state(), *next = nullptr; - int gain = popcount(pos.pieces()) - 2; + int gain = pos.count() - 2; while (st->accumulator.state[c] == EMPTY) { auto& dp = st->dirtyPiece; diff --git a/src/pawns.cpp b/src/pawns.cpp index fde70ba5306..68aaf331a8b 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -176,8 +176,8 @@ namespace { score -= Doubled * doubled + WeakLever * more_than_one(lever); - if (blocked && r > RANK_4) - score += BlockedPawn[r-4]; + if (blocked && r >= RANK_5) + score += BlockedPawn[r - RANK_5]; } return score; diff --git a/src/search.cpp b/src/search.cpp index 66ef5043d28..78a1f7b6eb9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1058,7 +1058,7 @@ namespace { && captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] < 0) continue; - // See based pruning + // SEE based pruning if (!pos.see_ge(move, Value(-221) * depth)) // (~25 Elo) continue; } diff --git a/src/types.h b/src/types.h index bf692f7e645..8506b06eab0 100644 --- a/src/types.h +++ b/src/types.h @@ -202,8 +202,8 @@ enum PieceType { enum Piece { NO_PIECE, - W_PAWN = 1, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, - B_PAWN = 9, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING, + W_PAWN = PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, + B_PAWN = PAWN + 8, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING, PIECE_NB = 16 }; From 9fb6383ed804d0bc86d52b07def14352f44eb5b4 Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Tue, 24 Nov 2020 17:06:30 +0100 Subject: [PATCH 0446/1766] Assorted search and eval parameter tune Search and eval parameter tune. STC https://tests.stockfishchess.org/tests/view/5fba850a67cbf42301d6b07d LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 24312 W: 2388 L: 2228 D: 19696 Ptnml(0-2): 85, 1800, 8241, 1930, 100 LTC https://tests.stockfishchess.org/tests/view/5fbad5ea67cbf42301d6b0fa LLR: 2.95 (-2.94,2.94) {0.25,1.25} Total: 88376 W: 3619 L: 3351 D: 81406 Ptnml(0-2): 56, 2977, 37849, 3255, 51 closes https://github.com/official-stockfish/Stockfish/pull/3232 bench: 3600361 --- src/evaluate.cpp | 10 +++++----- src/search.cpp | 34 +++++++++++++++++----------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 1a8cf662c6f..3d8871191f9 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -187,11 +187,11 @@ using namespace Trace; namespace { // Threshold for lazy and space evaluation - constexpr Value LazyThreshold1 = Value(1400); - constexpr Value LazyThreshold2 = Value(1300); - constexpr Value SpaceThreshold = Value(12222); - constexpr Value NNUEThreshold1 = Value(550); - constexpr Value NNUEThreshold2 = Value(150); + constexpr Value LazyThreshold1 = Value(1565); + constexpr Value LazyThreshold2 = Value(1102); + constexpr Value SpaceThreshold = Value(11551); + constexpr Value NNUEThreshold1 = Value(682); + constexpr Value NNUEThreshold2 = Value(176); // KingAttackWeights[PieceType] contains king attack weights by piece type constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 }; diff --git a/src/search.cpp b/src/search.cpp index 78a1f7b6eb9..7c797bef408 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -65,7 +65,7 @@ namespace { // Razor and futility margins constexpr int RazorMargin = 510; Value futility_margin(Depth d, bool improving) { - return Value(223 * (d - improving)); + return Value(234 * (d - improving)); } // Reductions lookup table, initialized at startup @@ -73,7 +73,7 @@ namespace { Depth reduction(bool i, Depth d, int mn) { int r = Reductions[d] * Reductions[mn]; - return (r + 509) / 1024 + (!i && r > 894); + return (r + 503) / 1024 + (!i && r > 915); } constexpr int futility_move_count(bool improving, Depth depth) { @@ -194,7 +194,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int((22.0 + 2 * std::log(Threads.size())) * std::log(i + 0.25 * std::log(i))); + Reductions[i] = int((21.3 + 2 * std::log(Threads.size())) * std::log(i + 0.25 * std::log(i))); } @@ -410,7 +410,7 @@ void Thread::search() { beta = std::min(prev + delta, VALUE_INFINITE); // Adjust contempt based on root move's previousScore (dynamic contempt) - int dct = ct + (105 - ct / 2) * prev / (abs(prev) + 149); + int dct = ct + (113 - ct / 2) * prev / (abs(prev) + 147); contempt = (us == WHITE ? make_score(dct, dct / 2) : -make_score(dct, dct / 2)); @@ -830,7 +830,7 @@ namespace { && (ss-1)->statScore < 22977 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 30 * depth - 28 * improving + 84 * ss->ttPv + 182 + && ss->staticEval >= beta - 30 * depth - 28 * improving + 84 * ss->ttPv + 168 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -838,7 +838,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and value - Depth R = (982 + 85 * depth) / 256 + std::min(int(eval - beta) / 192, 3); + Depth R = (1015 + 85 * depth) / 256 + std::min(int(eval - beta) / 191, 3); ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -855,7 +855,7 @@ namespace { if (nullValue >= VALUE_TB_WIN_IN_MAX_PLY) nullValue = beta; - if (thisThread->nmpMinPly || (abs(beta) < VALUE_KNOWN_WIN && depth < 13)) + if (thisThread->nmpMinPly || (abs(beta) < VALUE_KNOWN_WIN && depth < 14)) return nullValue; assert(!thisThread->nmpMinPly); // Recursive verification is not allowed @@ -874,7 +874,7 @@ namespace { } } - probCutBeta = beta + 176 - 49 * improving; + probCutBeta = beta + 183 - 49 * improving; // Step 10. ProbCut (~10 Elo) // If we have a good enough capture and a reduced search returns a value @@ -1039,7 +1039,7 @@ namespace { // Futility pruning: parent node (~5 Elo) if ( lmrDepth < 7 && !ss->inCheck - && ss->staticEval + 283 + 170 * lmrDepth <= alpha + && ss->staticEval + 266 + 170 * lmrDepth <= alpha && (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] @@ -1047,7 +1047,7 @@ namespace { continue; // Prune moves with negative SEE (~20 Elo) - if (!pos.see_ge(move, Value(-(29 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth))) + if (!pos.see_ge(move, Value(-(30 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth))) continue; } else @@ -1059,7 +1059,7 @@ namespace { continue; // SEE based pruning - if (!pos.see_ge(move, Value(-221) * depth)) // (~25 Elo) + if (!pos.see_ge(move, Value(-213) * depth)) // (~25 Elo) continue; } } @@ -1153,12 +1153,12 @@ namespace { || moveCountPruning || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha || cutNode - || thisThread->ttHitAverage < 427 * TtHitAverageResolution * TtHitAverageWindow / 1024)) + || thisThread->ttHitAverage < 432 * TtHitAverageResolution * TtHitAverageWindow / 1024)) { Depth r = reduction(improving, depth, moveCount); // Decrease reduction if the ttHit running average is large - if (thisThread->ttHitAverage > 509 * TtHitAverageResolution * TtHitAverageWindow / 1024) + if (thisThread->ttHitAverage > 537 * TtHitAverageResolution * TtHitAverageWindow / 1024) r--; // Increase reduction if other threads are searching this position @@ -1211,10 +1211,10 @@ namespace { - 5287; // Decrease/increase reduction by comparing opponent's stat score (~10 Elo) - if (ss->statScore >= -106 && (ss-1)->statScore < -104) + if (ss->statScore >= -105 && (ss-1)->statScore < -103) r--; - else if ((ss-1)->statScore >= -119 && ss->statScore < -140) + else if ((ss-1)->statScore >= -122 && ss->statScore < -129) r++; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) @@ -1228,7 +1228,7 @@ namespace { // Unless giving check, this capture is likely bad if ( !givesCheck - && ss->staticEval + PieceValue[EG][pos.captured_piece()] + 213 * depth <= alpha) + && ss->staticEval + PieceValue[EG][pos.captured_piece()] + 210 * depth <= alpha) r++; } @@ -1502,7 +1502,7 @@ namespace { if (PvNode && bestValue > alpha) alpha = bestValue; - futilityBase = bestValue + 145; + futilityBase = bestValue + 155; } const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, From 7615e3485e75c2f1715d372f7bb1f546738a5c76 Mon Sep 17 00:00:00 2001 From: MaximMolchanov Date: Sat, 14 Nov 2020 02:55:29 +0200 Subject: [PATCH 0447/1766] Calculate sum from first elements in affine transform for AVX512/AVX2/SSSE3 The idea is to initialize sum with the first element instead of zero. Reduce one add_epi32 and one set_zero SIMD instructions for each output dimension. sum = 0; for i = 1 to n sum += a[i] -> sum = a[1]; for i = 2 to n sum += a[i] STC: LLR: 2.95 (-2.94,2.94) {-0.25,1.25} Total: 69048 W: 7024 L: 6799 D: 55225 Ptnml(0-2): 260, 5175, 23458, 5342, 289 https://tests.stockfishchess.org/tests/view/5faf2cf467cbf42301d6aa06 closes https://github.com/official-stockfish/Stockfish/pull/3227 No functional change. --- AUTHORS | 1 + src/nnue/layers/affine_transform.h | 211 ++++++++++++++++++++--------- 2 files changed, 148 insertions(+), 64 deletions(-) diff --git a/AUTHORS b/AUTHORS index 71b718b80de..b31a36e9d62 100644 --- a/AUTHORS +++ b/AUTHORS @@ -112,6 +112,7 @@ Mark Tenzer (31m059) marotear Matthew Lai (matthewlai) Matthew Sullivan (Matt14916) +Maxim Molchanov (Maxim) Michael An (man) Michael Byrne (MichaelB7) Michael Chaly (Vizvezdenec) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 47c9c488b0c..caf315b2792 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -181,13 +181,13 @@ namespace Eval::NNUE::Layers { return _mm512_add_epi32(_mm512_permutexvar_epi32(indices, x), bias); }; - [[maybe_unused]] auto m512_add_dpbusd_epi32 = [=](__m512i& acc, __m512i a, __m512i b) { #if defined (USE_VNNI) + [[maybe_unused]] auto m512_add_dpbusd_epi32 = [=](__m512i& acc, __m512i a, __m512i b) { acc = _mm512_dpbusd_epi32(acc, a, b); #else + [[maybe_unused]] auto m512_dpbusd_epi32 = [=](__m512i a, __m512i b) -> __m512i { __m512i product0 = _mm512_maddubs_epi16(a, b); - product0 = _mm512_madd_epi16(product0, kOnes512); - acc = _mm512_add_epi32(acc, product0); + return _mm512_madd_epi16(product0, kOnes512); #endif }; @@ -214,14 +214,13 @@ namespace Eval::NNUE::Layers { return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias); }; - - [[maybe_unused]] auto m256_add_dpbusd_epi32 = [=](__m256i& acc, __m256i a, __m256i b) { #if defined (USE_VNNI) + [[maybe_unused]] auto m256_add_dpbusd_epi32 = [=](__m256i& acc, __m256i a, __m256i b) { acc = _mm256_dpbusd_epi32(acc, a, b); #else + [[maybe_unused]] auto m256_dpbusd_epi32 = [=](__m256i a, __m256i b) -> __m256i { __m256i product0 = _mm256_maddubs_epi16(a, b); - product0 = _mm256_madd_epi16(product0, kOnes256); - acc = _mm256_add_epi32(acc, product0); + return _mm256_madd_epi16(product0, kOnes256); #endif }; @@ -246,10 +245,9 @@ namespace Eval::NNUE::Layers { return _mm_add_epi32(sum0, bias); }; - [[maybe_unused]] auto m128_add_dpbusd_epi32 = [=](__m128i& acc, __m128i a, __m128i b) { + [[maybe_unused]] auto m128_dpbusd_epi32 = [=](__m128i a, __m128i b) -> __m128i { __m128i product0 = _mm_maddubs_epi16(a, b); - product0 = _mm_madd_epi16(product0, kOnes128); - acc = _mm_add_epi32(acc, product0); + return _mm_madd_epi16(product0, kOnes128); }; #endif @@ -293,15 +291,6 @@ namespace Eval::NNUE::Layers { const __m512i bias = *reinterpret_cast(&biases_[i]); __m512i* outptr = reinterpret_cast<__m512i*>(&output[i]); - __m512i sum01a = _mm512_setzero_si512(); - __m512i sum23a = _mm512_setzero_si512(); - __m512i sum45a = _mm512_setzero_si512(); - __m512i sum67a = _mm512_setzero_si512(); - __m512i sum01b = _mm512_setzero_si512(); - __m512i sum23b = _mm512_setzero_si512(); - __m512i sum45b = _mm512_setzero_si512(); - __m512i sum67b = _mm512_setzero_si512(); - const auto row01a = *reinterpret_cast(&weights_[offset01a]); const auto row23a = *reinterpret_cast(&weights_[offset23a]); const auto row45a = *reinterpret_cast(&weights_[offset45a]); @@ -314,6 +303,16 @@ namespace Eval::NNUE::Layers { const __m256i in256 = input_vector256[0]; const __m512i in = _mm512_inserti64x4(_mm512_castsi256_si512(in256), in256, 1); +#if defined (USE_VNNI) + __m512i sum01a = _mm512_setzero_si512(); + __m512i sum23a = _mm512_setzero_si512(); + __m512i sum45a = _mm512_setzero_si512(); + __m512i sum67a = _mm512_setzero_si512(); + __m512i sum01b = _mm512_setzero_si512(); + __m512i sum23b = _mm512_setzero_si512(); + __m512i sum45b = _mm512_setzero_si512(); + __m512i sum67b = _mm512_setzero_si512(); + m512_add_dpbusd_epi32(sum01a, in, row01a); m512_add_dpbusd_epi32(sum23a, in, row23a); m512_add_dpbusd_epi32(sum45a, in, row45a); @@ -322,6 +321,16 @@ namespace Eval::NNUE::Layers { m512_add_dpbusd_epi32(sum23b, in, row23b); m512_add_dpbusd_epi32(sum45b, in, row45b); m512_add_dpbusd_epi32(sum67b, in, row67b); +#else + __m512i sum01a = m512_dpbusd_epi32(in, row01a); + __m512i sum23a = m512_dpbusd_epi32(in, row23a); + __m512i sum45a = m512_dpbusd_epi32(in, row45a); + __m512i sum67a = m512_dpbusd_epi32(in, row67a); + __m512i sum01b = m512_dpbusd_epi32(in, row01b); + __m512i sum23b = m512_dpbusd_epi32(in, row23b); + __m512i sum45b = m512_dpbusd_epi32(in, row45b); + __m512i sum67b = m512_dpbusd_epi32(in, row67b); +#endif *outptr = m512_hadd256x16( sum01a, sum23a, sum45a, sum67a, @@ -342,48 +351,80 @@ namespace Eval::NNUE::Layers { if constexpr (kPaddedInputDimensions % (kSimdWidth * 2) == 0) { - __m512i sum0 = _mm512_setzero_si512(); - __m512i sum1 = _mm512_setzero_si512(); - __m512i sum2 = _mm512_setzero_si512(); - __m512i sum3 = _mm512_setzero_si512(); - const auto row0 = reinterpret_cast(&weights_[offset0]); const auto row1 = reinterpret_cast(&weights_[offset1]); const auto row2 = reinterpret_cast(&weights_[offset2]); const auto row3 = reinterpret_cast(&weights_[offset3]); - for (IndexType j = 0; j < kNumChunks512; ++j) +#if defined (USE_VNNI) + __m512i sum0 = _mm512_setzero_si512(); + __m512i sum1 = _mm512_setzero_si512(); + __m512i sum2 = _mm512_setzero_si512(); + __m512i sum3 = _mm512_setzero_si512(); + const IndexType kStart = 0; +#else + __m512i sum0 = m512_dpbusd_epi32(input_vector512[0], row0[0]); + __m512i sum1 = m512_dpbusd_epi32(input_vector512[0], row1[0]); + __m512i sum2 = m512_dpbusd_epi32(input_vector512[0], row2[0]); + __m512i sum3 = m512_dpbusd_epi32(input_vector512[0], row3[0]); + const IndexType kStart = 1; +#endif + + for (IndexType j = kStart; j < kNumChunks512; ++j) { const __m512i in = input_vector512[j]; +#if defined (USE_VNNI) m512_add_dpbusd_epi32(sum0, in, row0[j]); m512_add_dpbusd_epi32(sum1, in, row1[j]); m512_add_dpbusd_epi32(sum2, in, row2[j]); m512_add_dpbusd_epi32(sum3, in, row3[j]); +#else + sum0 = _mm512_add_epi32(sum0, m512_dpbusd_epi32(in, row0[j])); + sum1 = _mm512_add_epi32(sum1, m512_dpbusd_epi32(in, row1[j])); + sum2 = _mm512_add_epi32(sum2, m512_dpbusd_epi32(in, row2[j])); + sum3 = _mm512_add_epi32(sum3, m512_dpbusd_epi32(in, row3[j])); +#endif } *outptr = m512_haddx4(sum0, sum1, sum2, sum3, bias); } else { - __m256i sum0 = _mm256_setzero_si256(); - __m256i sum1 = _mm256_setzero_si256(); - __m256i sum2 = _mm256_setzero_si256(); - __m256i sum3 = _mm256_setzero_si256(); - const auto row0 = reinterpret_cast(&weights_[offset0]); const auto row1 = reinterpret_cast(&weights_[offset1]); const auto row2 = reinterpret_cast(&weights_[offset2]); const auto row3 = reinterpret_cast(&weights_[offset3]); - for (IndexType j = 0; j < kNumChunks256; ++j) +#if defined (USE_VNNI) + __m256i sum0 = _mm256_setzero_si256(); + __m256i sum1 = _mm256_setzero_si256(); + __m256i sum2 = _mm256_setzero_si256(); + __m256i sum3 = _mm256_setzero_si256(); + const IndexType kStart = 0; +#else + __m256i sum0 = m256_dpbusd_epi32(input_vector256[0], row0[0]); + __m256i sum1 = m256_dpbusd_epi32(input_vector256[0], row1[0]); + __m256i sum2 = m256_dpbusd_epi32(input_vector256[0], row2[0]); + __m256i sum3 = m256_dpbusd_epi32(input_vector256[0], row3[0]); + const IndexType kStart = 1; +#endif + + for (IndexType j = kStart; j < kNumChunks256; ++j) { const __m256i in = input_vector256[j]; +#if defined (USE_VNNI) m256_add_dpbusd_epi32(sum0, in, row0[j]); m256_add_dpbusd_epi32(sum1, in, row1[j]); m256_add_dpbusd_epi32(sum2, in, row2[j]); m256_add_dpbusd_epi32(sum3, in, row3[j]); +#else + sum0 = _mm256_add_epi32(sum0, m256_dpbusd_epi32(in, row0[j])); + sum1 = _mm256_add_epi32(sum1, m256_dpbusd_epi32(in, row1[j])); + sum2 = _mm256_add_epi32(sum2, m256_dpbusd_epi32(in, row2[j])); + sum3 = _mm256_add_epi32(sum3, m256_dpbusd_epi32(in, row3[j])); +#endif } *outptr = m256_haddx4(sum0, sum1, sum2, sum3, bias); @@ -394,30 +435,50 @@ namespace Eval::NNUE::Layers { { if constexpr (kPaddedInputDimensions % (kSimdWidth * 2) == 0) { - __m512i sum0 = _mm512_setzero_si512(); - const auto row0 = reinterpret_cast(&weights_[0]); - for (IndexType j = 0; j < kNumChunks512; ++j) +#if defined (USE_VNNI) + __m512i sum0 = _mm512_setzero_si512(); + const IndexType kStart = 0; +#else + __m512i sum0 = m512_dpbusd_epi32(input_vector512[0], row0[0]); + const IndexType kStart = 1; +#endif + + for (IndexType j = kStart; j < kNumChunks512; ++j) { const __m512i in = input_vector512[j]; +#if defined (USE_VNNI) m512_add_dpbusd_epi32(sum0, in, row0[j]); +#else + sum0 = _mm512_add_epi32(sum0, m512_dpbusd_epi32(in, row0[j])); +#endif } output[0] = m512_hadd(sum0, biases_[0]); } else { - __m256i sum0 = _mm256_setzero_si256(); - const auto row0 = reinterpret_cast(&weights_[0]); - for (IndexType j = 0; j < kNumChunks256; ++j) +#if defined (USE_VNNI) + __m256i sum0 = _mm256_setzero_si256(); + const IndexType kStart = 0; +#else + __m256i sum0 = m256_dpbusd_epi32(input_vector256[0], row0[0]); + const IndexType kStart = 1; +#endif + + for (IndexType j = kStart; j < kNumChunks256; ++j) { const __m256i in = input_vector256[j]; +#if defined (USE_VNNI) m256_add_dpbusd_epi32(sum0, in, row0[j]); +#else + sum0 = _mm256_add_epi32(sum0, m256_dpbusd_epi32(in, row0[j])); +#endif } output[0] = m256_hadd(sum0, biases_[0]); @@ -451,24 +512,40 @@ namespace Eval::NNUE::Layers { const __m128i bias = *reinterpret_cast(&biases_[i]); __m128i* outptr = reinterpret_cast<__m128i*>(&output[i]); - __m256i sum0 = _mm256_setzero_si256(); - __m256i sum1 = _mm256_setzero_si256(); - __m256i sum2 = _mm256_setzero_si256(); - __m256i sum3 = _mm256_setzero_si256(); - const auto row0 = reinterpret_cast(&weights_[offset0]); const auto row1 = reinterpret_cast(&weights_[offset1]); const auto row2 = reinterpret_cast(&weights_[offset2]); const auto row3 = reinterpret_cast(&weights_[offset3]); - for (IndexType j = 0; j < kNumChunks; ++j) +#if defined (USE_VNNI) + __m256i sum0 = _mm256_setzero_si256(); + __m256i sum1 = _mm256_setzero_si256(); + __m256i sum2 = _mm256_setzero_si256(); + __m256i sum3 = _mm256_setzero_si256(); + const IndexType kStart = 0; +#else + __m256i sum0 = m256_dpbusd_epi32(input_vector[0], row0[0]); + __m256i sum1 = m256_dpbusd_epi32(input_vector[0], row1[0]); + __m256i sum2 = m256_dpbusd_epi32(input_vector[0], row2[0]); + __m256i sum3 = m256_dpbusd_epi32(input_vector[0], row3[0]); + const IndexType kStart = 1; +#endif + + for (IndexType j = kStart; j < kNumChunks; ++j) { const __m256i in = input_vector[j]; +#if defined (USE_VNNI) m256_add_dpbusd_epi32(sum0, in, row0[j]); m256_add_dpbusd_epi32(sum1, in, row1[j]); m256_add_dpbusd_epi32(sum2, in, row2[j]); m256_add_dpbusd_epi32(sum3, in, row3[j]); +#else + sum0 = _mm256_add_epi32(sum0, m256_dpbusd_epi32(in, row0[j])); + sum1 = _mm256_add_epi32(sum1, m256_dpbusd_epi32(in, row1[j])); + sum2 = _mm256_add_epi32(sum2, m256_dpbusd_epi32(in, row2[j])); + sum3 = _mm256_add_epi32(sum3, m256_dpbusd_epi32(in, row3[j])); +#endif } *outptr = m256_haddx4(sum0, sum1, sum2, sum3, bias); @@ -476,15 +553,25 @@ namespace Eval::NNUE::Layers { } else if constexpr (kOutputDimensions == 1) { - __m256i sum0 = _mm256_setzero_si256(); - const auto row0 = reinterpret_cast(&weights_[0]); - for (IndexType j = 0; j < kNumChunks; ++j) +#if defined (USE_VNNI) + __m256i sum0 = _mm256_setzero_si256(); + const IndexType kStart = 0; +#else + __m256i sum0 = m256_dpbusd_epi32(input_vector[0], row0[0]); + const IndexType kStart = 1; +#endif + + for (IndexType j = kStart; j < kNumChunks; ++j) { const __m256i in = input_vector[j]; - m256_add_dpbusd_epi32(sum0, in, row0[j]); +#if defined (USE_VNNI) + m256_add_dpbusd_epi32(sum0, in, row0[j]); +#else + sum0 = _mm256_add_epi32(sum0, m256_dpbusd_epi32(in, row0[j])); +#endif } output[0] = m256_hadd(sum0, biases_[0]); @@ -517,24 +604,24 @@ namespace Eval::NNUE::Layers { const __m128i bias = *reinterpret_cast(&biases_[i]); __m128i* outptr = reinterpret_cast<__m128i*>(&output[i]); - __m128i sum0 = _mm_setzero_si128(); - __m128i sum1 = _mm_setzero_si128(); - __m128i sum2 = _mm_setzero_si128(); - __m128i sum3 = _mm_setzero_si128(); - const auto row0 = reinterpret_cast(&weights_[offset0]); const auto row1 = reinterpret_cast(&weights_[offset1]); const auto row2 = reinterpret_cast(&weights_[offset2]); const auto row3 = reinterpret_cast(&weights_[offset3]); - for (int j = 0; j < (int)kNumChunks; j += 1) + __m128i sum0 = m128_dpbusd_epi32(input_vector[0], row0[0]); + __m128i sum1 = m128_dpbusd_epi32(input_vector[0], row1[0]); + __m128i sum2 = m128_dpbusd_epi32(input_vector[0], row2[0]); + __m128i sum3 = m128_dpbusd_epi32(input_vector[0], row3[0]); + + for (int j = 1; j < (int)kNumChunks; ++j) { const __m128i in = input_vector[j]; - m128_add_dpbusd_epi32(sum0, in, row0[j]); - m128_add_dpbusd_epi32(sum1, in, row1[j]); - m128_add_dpbusd_epi32(sum2, in, row2[j]); - m128_add_dpbusd_epi32(sum3, in, row3[j]); + sum0 = _mm_add_epi32(sum0, m128_dpbusd_epi32(in, row0[j])); + sum1 = _mm_add_epi32(sum1, m128_dpbusd_epi32(in, row1[j])); + sum2 = _mm_add_epi32(sum2, m128_dpbusd_epi32(in, row2[j])); + sum3 = _mm_add_epi32(sum3, m128_dpbusd_epi32(in, row3[j])); } *outptr = m128_haddx4(sum0, sum1, sum2, sum3, bias); @@ -542,16 +629,12 @@ namespace Eval::NNUE::Layers { } else if constexpr (kOutputDimensions == 1) { - __m128i sum0 = _mm_setzero_si128(); - const auto row0 = reinterpret_cast(&weights_[0]); - for (int j = 0; j < (int)kNumChunks; j += 1) - { - const __m128i in = input_vector[j]; + __m128i sum0 = m128_dpbusd_epi32(input_vector[0], row0[0]); - m128_add_dpbusd_epi32(sum0, in, row0[j]); - } + for (int j = 1; j < (int)kNumChunks; ++j) + sum0 = _mm_add_epi32(sum0, m128_dpbusd_epi32(input_vector[j], row0[j])); output[0] = m128_hadd(sum0, biases_[0]); } From 190dd26b9f1bc6442acf7b2ae4750eb4ab8b90bd Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Thu, 26 Nov 2020 06:38:09 +0100 Subject: [PATCH 0448/1766] use classical for certain endgames. STC https://tests.stockfishchess.org/tests/view/5fbc64c067cbf42301d6b1d6 LLR: 2.97 (-2.94,2.94) {-0.25,1.25} Total: 53360 W: 5223 L: 5024 D: 43113 Ptnml(0-2): 184, 3877, 18390, 4014, 215 LTC https://tests.stockfishchess.org/tests/view/5fbc97f267cbf42301d6b1ee LLR: 2.96 (-2.94,2.94) {0.25,1.25} Total: 126472 W: 5111 L: 4766 D: 116595 Ptnml(0-2): 50, 4032, 54749, 4333, 72 closes https://github.com/official-stockfish/Stockfish/pull/3240 bench: 3820648 --- src/evaluate.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 3d8871191f9..90d11a00fd8 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1035,12 +1035,14 @@ Value Eval::evaluate(const Position& pos) { bool largePsq = psq * 16 > (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50; bool classical = largePsq || (psq > PawnValueMg / 4 && !(pos.this_thread()->nodes & 0xB)); - v = classical ? Evaluation(pos).value() : adjusted_NNUE(); + bool strongClassical = pos.non_pawn_material() < 2 * RookValueMg && pos.count() < 2; + + v = classical || strongClassical ? Evaluation(pos).value() : adjusted_NNUE(); // If the classical eval is small and imbalance large, use NNUE nevertheless. // For the case of opposite colored bishops, switch to NNUE eval with // small probability if the classical eval is less than the threshold. - if ( largePsq + if ( largePsq && !strongClassical && ( abs(v) * 16 < NNUEThreshold2 * r50 || ( pos.opposite_bishops() && abs(v) * 16 < (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50 From 9b7983a4521b66bf8d3c37ee58963d39deb2695c Mon Sep 17 00:00:00 2001 From: mstembera Date: Tue, 17 Nov 2020 15:58:25 -0800 Subject: [PATCH 0449/1766] Cleaned up MakeIndex() The index order in kpp_board_index[][] is reversed to be more optimal for the access pattern STC https://tests.stockfishchess.org/tests/view/5fbd74f967cbf42301d6b24f LLR: 2.93 (-2.94,2.94) {-1.25,0.25} Total: 27504 W: 2686 L: 2607 D: 22211 Ptnml(0-2): 84, 2001, 9526, 2034, 107 closes https://github.com/official-stockfish/Stockfish/pull/3233 No functional change --- src/nnue/evaluate_nnue.cpp | 21 --------------------- src/nnue/features/half_kp.cpp | 15 ++++++--------- src/nnue/features/half_kp.h | 4 ---- src/nnue/nnue_common.h | 9 ++++++++- 4 files changed, 14 insertions(+), 35 deletions(-) diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index b0ed7d2f5a4..382d8ff9d06 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -31,27 +31,6 @@ namespace Eval::NNUE { - const uint32_t kpp_board_index[PIECE_NB][COLOR_NB] = { - // convention: W - us, B - them - // viewed from other side, W and B are reversed - { PS_NONE, PS_NONE }, - { PS_W_PAWN, PS_B_PAWN }, - { PS_W_KNIGHT, PS_B_KNIGHT }, - { PS_W_BISHOP, PS_B_BISHOP }, - { PS_W_ROOK, PS_B_ROOK }, - { PS_W_QUEEN, PS_B_QUEEN }, - { PS_W_KING, PS_B_KING }, - { PS_NONE, PS_NONE }, - { PS_NONE, PS_NONE }, - { PS_B_PAWN, PS_W_PAWN }, - { PS_B_KNIGHT, PS_W_KNIGHT }, - { PS_B_BISHOP, PS_W_BISHOP }, - { PS_B_ROOK, PS_W_ROOK }, - { PS_B_QUEEN, PS_W_QUEEN }, - { PS_B_KING, PS_W_KING }, - { PS_NONE, PS_NONE } - }; - // Input feature converter LargePagePtr feature_transformer; diff --git a/src/nnue/features/half_kp.cpp b/src/nnue/features/half_kp.cpp index 116157cc19d..29322f04089 100644 --- a/src/nnue/features/half_kp.cpp +++ b/src/nnue/features/half_kp.cpp @@ -28,12 +28,9 @@ namespace Eval::NNUE::Features { return Square(int(s) ^ (bool(perspective) * 63)); } - // Find the index of the feature quantity from the king position and PieceSquare - template - inline IndexType HalfKP::MakeIndex( - Color perspective, Square s, Piece pc, Square ksq) { - - return IndexType(orient(perspective, s) + kpp_board_index[pc][perspective] + PS_END * ksq); + // Index of a feature for a given king position and another piece on some square + inline IndexType make_index(Color perspective, Square s, Piece pc, Square ksq) { + return IndexType(orient(perspective, s) + kpp_board_index[perspective][pc] + PS_END * ksq); } // Get a list of indices for active features @@ -45,7 +42,7 @@ namespace Eval::NNUE::Features { Bitboard bb = pos.pieces() & ~pos.pieces(KING); while (bb) { Square s = pop_lsb(&bb); - active->push_back(MakeIndex(perspective, s, pos.piece_on(s), ksq)); + active->push_back(make_index(perspective, s, pos.piece_on(s), ksq)); } } @@ -60,9 +57,9 @@ namespace Eval::NNUE::Features { Piece pc = dp.piece[i]; if (type_of(pc) == KING) continue; if (dp.from[i] != SQ_NONE) - removed->push_back(MakeIndex(perspective, dp.from[i], pc, ksq)); + removed->push_back(make_index(perspective, dp.from[i], pc, ksq)); if (dp.to[i] != SQ_NONE) - added->push_back(MakeIndex(perspective, dp.to[i], pc, ksq)); + added->push_back(make_index(perspective, dp.to[i], pc, ksq)); } } diff --git a/src/nnue/features/half_kp.h b/src/nnue/features/half_kp.h index 52a83eecf3b..708fd7ea0ba 100644 --- a/src/nnue/features/half_kp.h +++ b/src/nnue/features/half_kp.h @@ -52,10 +52,6 @@ namespace Eval::NNUE::Features { // Get a list of indices for recently changed features static void AppendChangedIndices(const Position& pos, const DirtyPiece& dp, Color perspective, IndexList* removed, IndexList* added); - - private: - // Index of a feature for a given king position and another piece on some square - static IndexType MakeIndex(Color perspective, Square s, Piece pc, Square sq_k); }; } // namespace Eval::NNUE::Features diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index a9664262a47..f9ff2bc81ee 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -90,7 +90,14 @@ namespace Eval::NNUE { PS_END2 = 12 * SQUARE_NB + 1 }; - extern const uint32_t kpp_board_index[PIECE_NB][COLOR_NB]; + constexpr uint32_t kpp_board_index[COLOR_NB][PIECE_NB] = { + // convention: W - us, B - them + // viewed from other side, W and B are reversed + { PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_W_KING, PS_NONE, + PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_B_KING, PS_NONE }, + { PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_B_KING, PS_NONE, + PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_W_KING, PS_NONE } + }; // Type of input feature after conversion using TransformedFeatureType = std::uint8_t; From d6d6972a66cb2fb599748bae1f14d32e6c42fc1e Mon Sep 17 00:00:00 2001 From: Lolligerhans Date: Fri, 20 Nov 2020 18:09:41 +0100 Subject: [PATCH 0450/1766] Refine rook penalty on closed files +-----------------+ | . . . . . . . . | All files are closed. Some files are | . . . . . o o . | more valuable for rooks, because | . . . . o . . o | they might open in the future. | . . . o x . . x | | o . o x . x x . | | x o x . . . . . | x our pawns | . x . . . . . . | o their pawns | . . . . . . . . | ^ rooks are scored higher on these files +-----------------+ ^ ^ Files containing none of our own pawns are open or half-open (otherwise they are closed). Rooks on (half-)open files recieve a bonus for the future potential to act along all ranks. This commit refines the (relative) penalty of rooks on closed files. Files that contain one of our blocked pawns are considered less likely to open in the future; rooks on these files are now penalized stronger. This bonus does not generally correlate with mobility. If the condition is sufficiently refined in the future, it may be beneficial to adjust or override mobility scores in some cases. LTC LLR: 2.94 (-2.94,2.94) {0.25,1.25} Total: 494384 W: 71565 L: 70231 D: 352588 Ptnml(0-2): 3907, 48050, 142118, 49036, 4081 https://tests.stockfishchess.org/tests/view/5fb9312e67cbf42301d6afb9 LTC (non-regression w/ book noob_3moves.epd) LLR: 2.95 (-2.94,2.94) {-0.75,0.25} Total: 208520 W: 27044 L: 26937 D: 154539 Ptnml(0-2): 1557, 19850, 61391, 19853, 1609 https://tests.stockfishchess.org/tests/view/5fc01ced67cbf42301d6b3df STC LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 98392 W: 20269 L: 19868 D: 58255 Ptnml(0-2): 1804, 11297, 22589, 11706, 1800 https://tests.stockfishchess.org/tests/view/5fb7f88a67cbf42301d6af10 closes https://github.com/official-stockfish/Stockfish/pull/3242 Bench: 3682630 --- src/evaluate.cpp | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 90d11a00fd8..fd51ad5347a 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -240,9 +240,8 @@ namespace { S(0, 0), S(9, 28), S(15, 31), S(17, 39), S(64, 70), S(171, 177), S(277, 260) }; - // RookOnFile[semiopen/open] contains bonuses for each rook when there is - // no (friendly) pawn on the rook file. - constexpr Score RookOnFile[] = { S(19, 7), S(48, 27) }; + constexpr Score RookOnClosedFile = S(10, 5); + constexpr Score RookOnOpenFile[] = { S(19, 7), S(48, 27) }; // ThreatByMinor/ByRook[attacked PieceType] contains bonuses according to // which piece type attacks which one. Attacks on lesser pieces which are @@ -485,16 +484,28 @@ namespace { if (Pt == ROOK) { - // Bonus for rook on an open or semi-open file + // Bonuses for rook on a (semi-)open or closed file if (pos.is_on_semiopen_file(Us, 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) { - File kf = file_of(pos.square(Us)); - if ((kf < FILE_E) == (file_of(s) < kf)) - score -= TrappedRook * (1 + !pos.castling_rights(Us)); + score += RookOnOpenFile[pos.is_on_semiopen_file(Them, s)]; + } + else + { + // If our pawn on this file is blocked, increase penalty + if ( pos.pieces(Us, PAWN) + & shift(pos.pieces()) + & file_bb(s)) + { + score -= RookOnClosedFile; + } + + // Penalty when trapped by the king, even more if the king cannot castle + if (mob <= 3) + { + File kf = file_of(pos.square(Us)); + if ((kf < FILE_E) == (file_of(s) < kf)) + score -= TrappedRook * (1 + !pos.castling_rights(Us)); + } } } From 66da1e802c22b1952c2a07af65c6224d0e01b3df Mon Sep 17 00:00:00 2001 From: lonfom169 Date: Thu, 26 Nov 2020 09:40:54 -0300 Subject: [PATCH 0451/1766] Remove bonus for killers. Passed non-regression STC: LLR: 2.94 (-2.94,2.94) {-1.25,0.25} Total: 14712 W: 1416 L: 1315 D: 11981 Ptnml(0-2): 59, 1029, 5082, 1124, 62 https://tests.stockfishchess.org/tests/view/5fbfa31f67cbf42301d6b36e Passed non-regression LTC: LLR: 2.95 (-2.94,2.94) {-0.75,0.25} Total: 27536 W: 1099 L: 1044 D: 25393 Ptnml(0-2): 11, 929, 11838, 974, 16 https://tests.stockfishchess.org/tests/view/5fbfac9167cbf42301d6b371 closes https://github.com/official-stockfish/Stockfish/pull/3241 Bench: 3887644 --- src/search.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 7c797bef408..d854cd95d1d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1257,9 +1257,6 @@ namespace { 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 6c429c4d6527a8e88d2ad1e7bfc8f4bd0ec05729 Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Sat, 28 Nov 2020 16:32:52 +0100 Subject: [PATCH 0452/1766] Search simplification STC https://tests.stockfishchess.org/tests/view/5fc2083942a050a89f02c8bb LLR: 2.93 (-2.94,2.94) {-1.25,0.25} Total: 23200 W: 2251 L: 2160 D: 18789 Ptnml(0-2): 86, 1726, 7895, 1797, 96 LTC https://tests.stockfishchess.org/tests/view/5fc22d7b42a050a89f02c8d0 LLR: 2.92 (-2.94,2.94) {-0.75,0.25} Total: 15832 W: 653 L: 590 D: 14589 Ptnml(0-2): 7, 521, 6795, 588, 5 closes https://github.com/official-stockfish/Stockfish/pull/3244/files bench: 3827317 Simplify search. --- src/search.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index d854cd95d1d..99d1cb0ebcc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1554,7 +1554,6 @@ namespace { // Do not search moves with negative SEE values if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY - && !(givesCheck && pos.is_discovery_check_on_king(~pos.side_to_move(), move)) && !pos.see_ge(move)) continue; From 2bc4ae172a7897106946ab9ede5db98db331c600 Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Sun, 29 Nov 2020 09:07:31 +0100 Subject: [PATCH 0453/1766] Update README.md fix a few typos closes https://github.com/official-stockfish/Stockfish/pull/3245 No functional change --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 409d0a1036c..eb7aa5a749a 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ about how to use Stockfish with it. The Stockfish engine features two evaluation functions for chess, the classical evaluation based on handcrafted terms, and the NNUE evaluation based on efficiently -updateable neural networks. The classical evaluation runs efficiently on almost all +updatable neural networks. The classical evaluation runs efficiently on almost all CPU architectures, while the NNUE evaluation benefits from the vector intrinsics available on most CPUs (sse2, avx2, neon, or similar). @@ -108,7 +108,7 @@ Currently, Stockfish has the following UCI options: * #### SyzygyProbeDepth Minimum remaining search depth for which a position is probed. Set this option - to a higher value to probe less agressively if you experience too much slowdown + to a higher value to probe less aggressively if you experience too much slowdown (in terms of nps) due to TB probing. * #### Syzygy50MoveRule @@ -173,8 +173,8 @@ to be compatible with that binary. If the engine is searching a position that is not in the tablebases (e.g. a position with 8 pieces), it will access the tablebases during the search. -If the engine reports a very large score (typically 153.xx), this means -that it has found a winning line into a tablebase position. +If the engine reports a very large score (typically 153.xx), this means +it has found a winning line into a tablebase position. If the engine is given a position to search that is in the tablebases, it will use the tablebases at the beginning of the search to preselect all @@ -182,7 +182,7 @@ good moves, i.e. all moves that preserve the win or preserve the draw while taking into account the 50-move rule. It will then perform a search only on those moves. **The engine will not move immediately**, unless there is only a single good move. **The engine likely -will not report a mate score even if the position is known to be won.** +will not report a mate score, even if the position is known to be won.** It is therefore clear that this behaviour is not identical to what one might be used to with Nalimov tablebases. There are technical reasons for this @@ -207,7 +207,7 @@ will fall back to regular memory allocation when this is not the case. Large page support on Linux is obtained by the Linux kernel transparent huge pages functionality. Typically, transparent huge pages -are already enabled and no configuration is needed. +are already enabled, and no configuration is needed. ### Support on Windows @@ -216,7 +216,7 @@ The use of large pages requires "Lock Pages in Memory" privilege. See on how to enable this privilege, then run [RAMMap](https://docs.microsoft.com/en-us/sysinternals/downloads/rammap) to double-check that large pages are used. We suggest that you reboot your computer after you have enabled large pages, because long Windows -sessions suffer from memory fragmentation which may prevent Stockfish +sessions suffer from memory fragmentation, which may prevent Stockfish from getting large pages: a fresh session is better in this regard. ## Compiling Stockfish yourself from the sources @@ -236,7 +236,7 @@ targets with corresponding descriptions. make build ARCH=x86-64-modern ``` -When not using the Makefile to compile (for instance with Microsoft MSVC) you +When not using the Makefile to compile (for instance, with Microsoft MSVC) you need to manually set/unset some switches in the compiler command line; see file *types.h* for a quick reference. @@ -281,9 +281,9 @@ first, where the basics of Stockfish development are explained. ## Terms of use Stockfish is free, and distributed under the **GNU General Public License version 3** -(GPL v3). Essentially, this means that you are free to do almost exactly +(GPL v3). Essentially, this means you are free to do almost exactly what you want with the program, including distributing it among your -friends, making it available for download from your web site, selling +friends, making it available for download from your website, selling it (either by itself or as part of some bigger software package), or using it as the starting point for a software project of your own. From 045728a7da9dfee1746da0c5b4632a62f68c0d97 Mon Sep 17 00:00:00 2001 From: syzygy1 <3028851+syzygy1@users.noreply.github.com> Date: Sun, 29 Nov 2020 12:05:26 +0100 Subject: [PATCH 0454/1766] Remove piece lists This patch removes the incrementally updated piece lists from the Position object. This has been tried before but always failed. My reasons for trying again are: * 32-bit systems (including phones) are now much less important than they were some years ago (and are absent from fishtest); * NNUE may have made SF less finely tuned to the order in which moves were generated. STC: LLR: 2.94 (-2.94,2.94) {-1.25,0.25} Total: 55272 W: 5260 L: 5216 D: 44796 Ptnml(0-2): 208, 4147, 18898, 4159, 224 https://tests.stockfishchess.org/tests/view/5fc2986a42a050a89f02c926 LTC: LLR: 2.96 (-2.94,2.94) {-0.75,0.25} Total: 16600 W: 673 L: 608 D: 15319 Ptnml(0-2): 14, 533, 7138, 604, 11 https://tests.stockfishchess.org/tests/view/5fc2f98342a050a89f02c95c closes https://github.com/official-stockfish/Stockfish/pull/3247 Bench: 3940967 --- src/endgame.cpp | 8 ++++---- src/evaluate.cpp | 8 ++++---- src/movegen.cpp | 7 ++++--- src/pawns.cpp | 7 ++++--- src/position.cpp | 7 ------- src/position.h | 27 ++++----------------------- 6 files changed, 20 insertions(+), 44 deletions(-) diff --git a/src/endgame.cpp b/src/endgame.cpp index c8be21983dd..7e005a28277 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -553,8 +553,8 @@ ScaleFactor Endgame::operator()(const Position& pos) const { assert(verify_material(pos, strongSide, RookValueMg, 2)); assert(verify_material(pos, weakSide, RookValueMg, 1)); - Square strongPawn1 = pos.squares(strongSide)[0]; - Square strongPawn2 = pos.squares(strongSide)[1]; + Square strongPawn1 = lsb(pos.pieces(strongSide, PAWN)); + Square strongPawn2 = msb(pos.pieces(strongSide, PAWN)); Square weakKing = pos.square(weakSide); // Does the stronger side have a passed pawn? @@ -638,8 +638,8 @@ ScaleFactor Endgame::operator()(const Position& pos) const { return SCALE_FACTOR_NONE; Square weakKing = pos.square(weakSide); - Square strongPawn1 = pos.squares(strongSide)[0]; - Square strongPawn2 = pos.squares(strongSide)[1]; + Square strongPawn1 = lsb(pos.pieces(strongSide, PAWN)); + Square strongPawn2 = msb(pos.pieces(strongSide, PAWN)); Square blockSq1, blockSq2; if (relative_rank(strongSide, strongPawn1) > relative_rank(strongSide, strongPawn2)) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index fd51ad5347a..6ee536216d0 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -387,15 +387,15 @@ namespace { constexpr Direction Down = -pawn_push(Us); constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB : Rank5BB | Rank4BB | Rank3BB); - const Square* pl = pos.squares(Us); - + Bitboard b1 = pos.pieces(Us, Pt); Bitboard b, bb; Score score = SCORE_ZERO; attackedBy[Us][Pt] = 0; - for (Square s = *pl; s != SQ_NONE; s = *++pl) - { + while (b1) { + Square s = pop_lsb(&b1); + // Find attacked squares, including x-ray attacks for bishops and rooks b = Pt == BISHOP ? attacks_bb(s, pos.pieces() ^ pos.pieces(QUEEN)) : Pt == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(QUEEN) ^ pos.pieces(Us, ROOK)) diff --git a/src/movegen.cpp b/src/movegen.cpp index 3340f65cf10..cc1518a078d 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -180,10 +180,11 @@ namespace { static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()"); - const Square* pl = pos.squares(Us); + Bitboard bb = pos.pieces(Us, Pt); + + while (bb) { + Square from = pop_lsb(&bb); - for (Square from = *pl; from != SQ_NONE; from = *++pl) - { if (Checks) { if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN) diff --git a/src/pawns.cpp b/src/pawns.cpp index 68aaf331a8b..b6d2900305a 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -91,7 +91,7 @@ namespace { Square s; bool backward, passed, doubled; Score score = SCORE_ZERO; - const Square* pl = pos.squares(Us); + Bitboard b = pos.pieces(Us, PAWN); Bitboard ourPawns = pos.pieces( Us, PAWN); Bitboard theirPawns = pos.pieces(Them, PAWN); @@ -104,8 +104,9 @@ namespace { e->blockedCount += popcount(shift(ourPawns) & (theirPawns | doubleAttackThem)); // Loop through all pawns of the current color and score each pawn - while ((s = *pl++) != SQ_NONE) - { + while (b) { + s = pop_lsb(&b); + assert(pos.piece_on(s) == make_piece(Us, PAWN)); Rank r = relative_rank(Us, s); diff --git a/src/position.cpp b/src/position.cpp index 5ce7da22894..07ce0a7cdbc 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -197,7 +197,6 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th std::memset(this, 0, sizeof(Position)); std::memset(si, 0, sizeof(StateInfo)); - std::fill_n(&pieceList[0][0], sizeof(pieceList) / sizeof(Square), SQ_NONE); st = si; ss >> std::noskipws; @@ -1327,16 +1326,10 @@ bool Position::pos_is_ok() const { assert(0 && "pos_is_ok: State"); for (Piece pc : Pieces) - { if ( pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc))) || pieceCount[pc] != std::count(board, board + SQUARE_NB, pc)) assert(0 && "pos_is_ok: Pieces"); - for (int i = 0; i < pieceCount[pc]; ++i) - if (board[pieceList[pc][i]] != pc || index[pieceList[pc][i]] != i) - assert(0 && "pos_is_ok: Index"); - } - for (Color c : { WHITE, BLACK }) for (CastlingRights cr : {c & KING_SIDE, c & QUEEN_SIDE}) { diff --git a/src/position.h b/src/position.h index d6f5c9fdb11..02156448ea7 100644 --- a/src/position.h +++ b/src/position.h @@ -99,7 +99,6 @@ class Position { bool empty(Square s) const; template int count(Color c) const; template int count() const; - template const Square* squares(Color c) const; template Square square(Color c) const; bool is_on_semiopen_file(Color c, Square s) const; @@ -190,8 +189,6 @@ class Position { Bitboard byTypeBB[PIECE_TYPE_NB]; Bitboard byColorBB[COLOR_NB]; int pieceCount[PIECE_NB]; - Square pieceList[PIECE_NB][16]; - int index[SQUARE_NB]; int castlingRightsMask[SQUARE_NB]; Square castlingRookSquare[CASTLING_RIGHT_NB]; Bitboard castlingPath[CASTLING_RIGHT_NB]; @@ -254,13 +251,9 @@ template inline int Position::count() const { return count(WHITE) + count(BLACK); } -template inline const Square* Position::squares(Color c) const { - return pieceList[make_piece(c, Pt)]; -} - template inline Square Position::square(Color c) const { - assert(pieceCount[make_piece(c, Pt)] == 1); - return squares(c)[0]; + assert(count(c) == 1); + return lsb(pieces(c, Pt)); } inline Square Position::ep_square() const { @@ -394,35 +387,25 @@ inline void Position::put_piece(Piece pc, Square s) { board[s] = pc; byTypeBB[ALL_PIECES] |= byTypeBB[type_of(pc)] |= s; byColorBB[color_of(pc)] |= s; - index[s] = pieceCount[pc]++; - pieceList[pc][index[s]] = s; + pieceCount[pc]++; pieceCount[make_piece(color_of(pc), ALL_PIECES)]++; psq += PSQT::psq[pc][s]; } inline void Position::remove_piece(Square s) { - // WARNING: This is not a reversible operation. If we remove a piece in - // do_move() and then replace it in undo_move() we will put it at the end of - // the list and not in its original place, it means index[] and pieceList[] - // are not invariant to a do_move() + undo_move() sequence. Piece pc = board[s]; byTypeBB[ALL_PIECES] ^= s; byTypeBB[type_of(pc)] ^= s; byColorBB[color_of(pc)] ^= s; /* board[s] = NO_PIECE; Not needed, overwritten by the capturing one */ - Square lastSquare = pieceList[pc][--pieceCount[pc]]; - index[lastSquare] = index[s]; - pieceList[pc][index[lastSquare]] = lastSquare; - pieceList[pc][pieceCount[pc]] = SQ_NONE; + pieceCount[pc]--; pieceCount[make_piece(color_of(pc), ALL_PIECES)]--; psq -= PSQT::psq[pc][s]; } inline void Position::move_piece(Square from, Square to) { - // index[from] is not updated and becomes stale. This works as long as index[] - // is accessed just by known occupied squares. Piece pc = board[from]; Bitboard fromTo = from | to; byTypeBB[ALL_PIECES] ^= fromTo; @@ -430,8 +413,6 @@ inline void Position::move_piece(Square from, Square to) { byColorBB[color_of(pc)] ^= fromTo; board[from] = NO_PIECE; board[to] = pc; - index[to] = index[from]; - pieceList[pc][index[to]] = to; psq += PSQT::psq[pc][to] - PSQT::psq[pc][from]; } From 2442ba2b0e8a399b0dbfe9d23a8a2819cb0af987 Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Sun, 29 Nov 2020 13:52:36 +0100 Subject: [PATCH 0455/1766] Reductions simplification Simplify increase reduction for captures/promotions if late move and at low depth. STC https://tests.stockfishchess.org/tests/view/5fbff65067cbf42301d6b3ae LLR: 2.97 (-2.94,2.94) {-1.25,0.25} Total: 49088 W: 4607 L: 4555 D: 39926 Ptnml(0-2): 177, 3615, 16932, 3619, 201 LTC https://tests.stockfishchess.org/tests/view/5fc0902967cbf42301d6b3fc LLR: 2.99 (-2.94,2.94) {-0.75,0.25} Total: 160944 W: 6153 L: 6193 D: 148598 Ptnml(0-2): 90, 5525, 69294, 5461, 102 closes https://github.com/official-stockfish/Stockfish/pull/3248 bench: 3834568 --- src/search.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 99d1cb0ebcc..52541868b87 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1222,10 +1222,6 @@ namespace { } else { - // Increase reduction for captures/promotions if late move and at low depth - if (depth < 8 && moveCount > 2) - r++; - // Unless giving check, this capture is likely bad if ( !givesCheck && ss->staticEval + PieceValue[EG][pos.captured_piece()] + 210 * depth <= alpha) From 736400675746c6b84a0bdf131babce1b07ade0df Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Sat, 28 Nov 2020 23:14:34 +0800 Subject: [PATCH 0456/1766] Update default net to nn-62ef826d1a6d.nnue Include scaling change as suggested by Dietrich Kappe, the one who trained net for Komodo. According to him, some nets may require different scaling in order to utilize its full strength. STC: LLR: 2.93 (-2.94,2.94) {-0.25,1.25} Total: 99856 W: 9669 L: 9401 D: 80786 Ptnml(0-2): 374, 7468, 34037, 7614, 435 https://tests.stockfishchess.org/tests/view/5fc2697642a050a89f02c8ec LTC: LLR: 2.96 (-2.94,2.94) {0.25,1.25} Total: 29840 W: 1220 L: 1081 D: 27539 Ptnml(0-2): 10, 969, 12827, 1100, 14 https://tests.stockfishchess.org/tests/view/5fc2ea5142a050a89f02c957 Bench: 3561701 --- src/evaluate.cpp | 2 +- src/evaluate.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 6ee536216d0..a1b04316e79 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1037,7 +1037,7 @@ Value Eval::evaluate(const Position& pos) { // Scale and shift NNUE for compatibility with search and classical evaluation auto adjusted_NNUE = [&](){ int mat = pos.non_pawn_material() + PawnValueMg * pos.count(); - return NNUE::evaluate(pos) * (720 + mat / 32) / 1024 + Tempo; + return NNUE::evaluate(pos) * (679 + mat / 32) / 1024 + Tempo; }; // If there is PSQ imbalance use classical eval, with small probability if it is small diff --git a/src/evaluate.h b/src/evaluate.h index 06c66f716f1..7dbc35deabd 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -36,7 +36,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-c3ca321c51c9.nnue" + #define EvalFileDefaultName "nn-62ef826d1a6d.nnue" namespace NNUE { From be7a03a957d5c2590a329f8f47acea8af2305adf Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sat, 5 Dec 2020 04:00:41 +0300 Subject: [PATCH 0457/1766] Introduce static history The idea of this patch can be described as following: we update static history stats based on comparison of the static evaluations of the position before and after the move. If the move increases static evaluation it's assigned positive bonus, if it decreases static evaluation it's assigned negative bonus. These stats are used in movepicker to sort quiet moves. passed STC https://tests.stockfishchess.org/tests/view/5fca4c0842a050a89f02cd66 LLR: 3.00 (-2.94,2.94) {-0.25,1.25} Total: 78152 W: 7409 L: 7171 D: 63572 Ptnml(0-2): 303, 5695, 26873, 5871, 334 passed LTC https://tests.stockfishchess.org/tests/view/5fca6be442a050a89f02cd75 LLR: 2.94 (-2.94,2.94) {0.25,1.25} Total: 40240 W: 1602 L: 1441 D: 37197 Ptnml(0-2): 19, 1306, 17305, 1475, 15 closes https://github.com/official-stockfish/Stockfish/pull/3253 bench 3845156 --- src/movepick.cpp | 9 +++++---- src/movepick.h | 3 +++ src/search.cpp | 11 +++++++++++ src/thread.cpp | 1 + src/thread.h | 1 + 5 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index f5e02385556..9ca1a21f6d0 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -54,9 +54,9 @@ namespace { /// ordering is at the current node. /// MovePicker constructor for the main search -MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const LowPlyHistory* lp, +MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const ButterflyHistory* sh, const LowPlyHistory* lp, const CapturePieceToHistory* cph, const PieceToHistory** ch, Move cm, const Move* killers, int pl) - : pos(p), mainHistory(mh), lowPlyHistory(lp), captureHistory(cph), continuationHistory(ch), + : pos(p), mainHistory(mh), staticHistory(sh), lowPlyHistory(lp), captureHistory(cph), continuationHistory(ch), ttMove(ttm), refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d), ply(pl) { assert(d > 0); @@ -66,9 +66,9 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist } /// MovePicker constructor for quiescence search -MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, +MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const ButterflyHistory* sh, const CapturePieceToHistory* cph, const PieceToHistory** ch, Square rs) - : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), ttMove(ttm), recaptureSquare(rs), depth(d) { + : pos(p), mainHistory(mh), staticHistory(sh), captureHistory(cph), continuationHistory(ch), ttMove(ttm), recaptureSquare(rs), depth(d) { assert(d <= 0); @@ -105,6 +105,7 @@ void MovePicker::score() { else if (Type == QUIETS) m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] + + (*staticHistory)[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)] diff --git a/src/movepick.h b/src/movepick.h index 4c0ad55172d..7fe98e49b9a 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -123,10 +123,12 @@ class MovePicker { MovePicker& operator=(const MovePicker&) = delete; MovePicker(const Position&, Move, Value, const CapturePieceToHistory*); MovePicker(const Position&, Move, Depth, const ButterflyHistory*, + const ButterflyHistory*, const CapturePieceToHistory*, const PieceToHistory**, Square); MovePicker(const Position&, Move, Depth, const ButterflyHistory*, + const ButterflyHistory*, const LowPlyHistory*, const CapturePieceToHistory*, const PieceToHistory**, @@ -143,6 +145,7 @@ class MovePicker { const Position& pos; const ButterflyHistory* mainHistory; + const ButterflyHistory* staticHistory; const LowPlyHistory* lowPlyHistory; const CapturePieceToHistory* captureHistory; const PieceToHistory** continuationHistory; diff --git a/src/search.cpp b/src/search.cpp index 52541868b87..ab559696ebe 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -807,6 +807,15 @@ namespace { tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); } + // Update static history for previous move + if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture) + { + int bonus = ss->staticEval > -(ss-1)->staticEval + 2 * Tempo ? -stat_bonus(depth) : + ss->staticEval < -(ss-1)->staticEval + 2 * Tempo ? stat_bonus(depth) : + 0; + thisThread->staticHistory[~us][from_to((ss-1)->currentMove)] << bonus; + } + // Step 7. Razoring (~1 Elo) if ( !rootNode // The required rootNode PV handling is not available in qsearch && depth == 1 @@ -964,6 +973,7 @@ namespace { Move countermove = thisThread->counterMoves[pos.piece_on(prevSq)][prevSq]; MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, + &thisThread->staticHistory, &thisThread->lowPlyHistory, &captureHistory, contHist, @@ -1507,6 +1517,7 @@ namespace { // queen and checking knight promotions, and other checks(only if depth >= DEPTH_QS_CHECKS) // will be generated. MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, + &thisThread->staticHistory, &thisThread->captureHistory, contHist, to_sq((ss-1)->currentMove)); diff --git a/src/thread.cpp b/src/thread.cpp index 2fbf745d072..bfcdb65ebad 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -57,6 +57,7 @@ void Thread::clear() { counterMoves.fill(MOVE_NONE); mainHistory.fill(0); + staticHistory.fill(0); lowPlyHistory.fill(0); captureHistory.fill(0); diff --git a/src/thread.h b/src/thread.h index 6a73423b2c3..c05fa941a12 100644 --- a/src/thread.h +++ b/src/thread.h @@ -69,6 +69,7 @@ class Thread { Depth rootDepth, completedDepth; CounterMoveHistory counterMoves; ButterflyHistory mainHistory; + ButterflyHistory staticHistory; LowPlyHistory lowPlyHistory; CapturePieceToHistory captureHistory; ContinuationHistory continuationHistory[2][2]; From 8630d03dd4e1ec7e492ddf6c40d9d9e4cdba2a58 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Tue, 1 Dec 2020 15:02:35 +0300 Subject: [PATCH 0458/1766] Add comments to uncommented parts of code https://github.com/official-stockfish/Stockfish/pull/3250 No functional change --- src/evaluate.cpp | 16 ++++++++++++++++ src/search.cpp | 38 +++++++++++++++++++++++++++++++++----- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index a1b04316e79..ef33adf5d77 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -844,6 +844,8 @@ namespace { behind |= shift(behind); behind |= shift(behind); + // Compute space score based on the number of safe squares and number of our pieces + // increased with number of total blocked pawns in position. int bonus = popcount(safe) + popcount(behind & safe & ~attackedBy[Them][ALL_PIECES]); int weight = pos.count(Us) - 3 + std::min(pe->blocked_count(), 9); Score score = make_score(bonus * weight * weight / 16, 0); @@ -905,24 +907,36 @@ namespace { { if (pos.opposite_bishops()) { + // For pure opposite colored bishops endgames use scale factor + // based on the number of passed pawns of the strong side. if ( pos.non_pawn_material(WHITE) == BishopValueMg && pos.non_pawn_material(BLACK) == BishopValueMg) sf = 18 + 4 * popcount(pe->passed_pawns(strongSide)); + // For every other opposite colored bishops endgames use scale factor + // based on the number of all pieces of the strong side. else sf = 22 + 3 * pos.count(strongSide); } + // For rook endgames with strong side not having overwhelming pawn number advantage + // and its pawns being on one flank and weak side protecting its pieces with a king + // use lower scale factor. else if ( pos.non_pawn_material(WHITE) == RookValueMg && pos.non_pawn_material(BLACK) == RookValueMg && pos.count(strongSide) - pos.count(~strongSide) <= 1 && bool(KingSide & pos.pieces(strongSide, PAWN)) != bool(QueenSide & pos.pieces(strongSide, PAWN)) && (attacks_bb(pos.square(~strongSide)) & pos.pieces(~strongSide, PAWN))) sf = 36; + // For queen vs no queen endgames use scale factor + // based on number of minors of side that doesn't have queen. else if (pos.count() == 1) sf = 37 + 3 * (pos.count(WHITE) == 1 ? pos.count(BLACK) + pos.count(BLACK) : pos.count(WHITE) + pos.count(WHITE)); + // In every other case use scale factor based on + // the number of pawns of the strong side reduced if pawns are on a single flank. else sf = std::min(sf, 36 + 7 * pos.count(strongSide)) - 4 * !pawnsOnBothFlanks; + // Reduce scale factor in case of pawns being on a single flank sf -= 4 * !pawnsOnBothFlanks; } @@ -1046,6 +1060,8 @@ Value Eval::evaluate(const Position& pos) { bool largePsq = psq * 16 > (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50; bool classical = largePsq || (psq > PawnValueMg / 4 && !(pos.this_thread()->nodes & 0xB)); + // Use classical evaluation for really low piece endgames. + // The most critical case is a bishop + A/H file pawn vs naked king draw. bool strongClassical = pos.non_pawn_material() < 2 * RookValueMg && pos.count() < 2; v = classical || strongClassical ? Evaluation(pos).value() : adjusted_NNUE(); diff --git a/src/search.cpp b/src/search.cpp index ab559696ebe..ba9265ddb38 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -676,6 +676,7 @@ namespace { ss->ttPv = PvNode || (ss->ttHit && tte->is_pv()); formerPv = ss->ttPv && !PvNode; + // Update low ply history for previous move if we are near root and position is or has been in PV if ( ss->ttPv && depth > 12 && ss->ply - 1 < MAX_LPH @@ -700,6 +701,7 @@ namespace { { if (ttValue >= beta) { + // Bonus for a quiet ttMove that fails high if (!pos.capture_or_promotion(ttMove)) update_quiet_stats(pos, ss, ttMove, stat_bonus(depth), depth); @@ -716,6 +718,8 @@ namespace { } } + // Partial workaround for the graph history interaction problem + // For high rule50 counts don't produce transposition table cutoffs. if (pos.rule50_count() < 90) return ttValue; } @@ -789,6 +793,7 @@ namespace { if (eval == VALUE_NONE) ss->staticEval = eval = evaluate(pos); + // Randomize draw evaluation if (eval == VALUE_DRAW) eval = value_draw(thisThread); @@ -799,11 +804,14 @@ namespace { } else { + // In case of null move search use previous static eval with a different sign + // and addition of two tempos if ((ss-1)->currentMove != MOVE_NULL) ss->staticEval = eval = evaluate(pos); else ss->staticEval = eval = -(ss-1)->staticEval + 2 * Tempo; + // Save static evaluation into transposition table tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); } @@ -822,6 +830,10 @@ namespace { && eval <= alpha - RazorMargin) return qsearch(pos, ss, alpha, beta); + // Set up improving flag that is used in various pruning heuristics + // We define position as improving if static evaluation of position is better + // Than the previous static evaluation at our turn + // In case of us being in check at our previous move we look at move prior to it improving = (ss-2)->staticEval == VALUE_NONE ? ss->staticEval > (ss-4)->staticEval || (ss-4)->staticEval == VALUE_NONE : ss->staticEval > (ss-2)->staticEval; @@ -1183,6 +1195,7 @@ namespace { if ((rootNode || !PvNode) && depth > 10 && thisThread->bestMoveChanges <= 2) r++; + // More reductions for late moves if position was not in previous PV if (moveCountPruning && !formerPv) r++; @@ -1258,6 +1271,7 @@ namespace { { value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode); + // If the move passed LMR update its stats if (didLMR && !captureOrPromotion) { int bonus = value > alpha ? stat_bonus(newDepth) @@ -1309,8 +1323,7 @@ namespace { rm.pv.push_back(*m); // We record how often the best move has been changed in each - // iteration. This information is used for time management: when - // the best move changes frequently, we allocate some more time. + // iteration. This information is used for time management and LMR if (moveCount > 1) ++thisThread->bestMoveChanges; } @@ -1343,6 +1356,7 @@ namespace { } } + // If the move is worse than some previously searched move, remember it to update its stats later if (move != bestMove) { if (captureOrPromotion && captureCount < 32) @@ -1372,6 +1386,7 @@ namespace { bestValue = excludedMove ? alpha : ss->inCheck ? mated_in(ss->ply) : VALUE_DRAW; + // If there is a move which produces search value greater than alpha we update stats of searched moves else if (bestMove) update_all_stats(pos, ss, bestMove, bestValue, beta, prevSq, quietsSearched, quietCount, capturesSearched, captureCount, depth); @@ -1393,6 +1408,7 @@ namespace { else if (depth > 3) ss->ttPv = ss->ttPv && (ss+1)->ttPv; + // Write gathered information in transposition table if (!excludedMove && !(rootNode && thisThread->pvIdx)) tte->save(posKey, value_to_tt(bestValue, ss->ply), ss->ttPv, bestValue >= beta ? BOUND_LOWER : @@ -1488,6 +1504,8 @@ namespace { bestValue = ttValue; } else + // In case of null move search use previous static eval with a different sign + // and addition of two tempos ss->staticEval = bestValue = (ss-1)->currentMove != MOVE_NULL ? evaluate(pos) : -(ss-1)->staticEval + 2 * Tempo; @@ -1495,6 +1513,7 @@ namespace { // Stand pat. Return immediately if static value is at least beta if (bestValue >= beta) { + // Save gathered info in transposition table if (!ss->ttHit) tte->save(posKey, value_to_tt(bestValue, ss->ply), false, BOUND_LOWER, DEPTH_NONE, MOVE_NONE, ss->staticEval); @@ -1623,6 +1642,7 @@ namespace { return mated_in(ss->ply); // Plies to mate from the root } + // Save gathered info in transposition table tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit, bestValue >= beta ? BOUND_LOWER : PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER, @@ -1706,9 +1726,10 @@ namespace { if (!pos.capture_or_promotion(bestMove)) { + // Increase stats for the best move in case it was a quiet move update_quiet_stats(pos, ss, bestMove, bonus2, depth); - // Decrease all the non-best quiet moves + // Decrease stats for all non-best quiet moves for (int i = 0; i < quietCount; ++i) { thisThread->mainHistory[us][from_to(quietsSearched[i])] << -bonus2; @@ -1716,14 +1737,16 @@ namespace { } } else + // Increase stats for the best move in case it was a capture move captureHistory[moved_piece][to_sq(bestMove)][captured] << bonus1; - // Extra penalty for a quiet early move that was not a TT move or main killer move in previous ply when it gets refuted + // Extra penalty for a quiet early move that was not a TT move or + // main killer move in previous ply when it gets refuted. if ( ((ss-1)->moveCount == 1 + (ss-1)->ttHit || ((ss-1)->currentMove == (ss-1)->killers[0])) && !pos.captured_piece()) update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -bonus1); - // Decrease all the non-best capture moves + // Decrease stats for all non-best capture moves for (int i = 0; i < captureCount; ++i) { moved_piece = pos.moved_piece(capturesSearched[i]); @@ -1740,6 +1763,7 @@ namespace { for (int i : {1, 2, 4, 6}) { + // Only update first 2 continuation histories if we are in check if (ss->inCheck && i > 2) break; if (is_ok((ss-i)->currentMove)) @@ -1752,6 +1776,7 @@ namespace { void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus, int depth) { + // Update killers if (ss->killers[0] != move) { ss->killers[1] = ss->killers[0]; @@ -1763,15 +1788,18 @@ namespace { thisThread->mainHistory[us][from_to(move)] << bonus; update_continuation_histories(ss, pos.moved_piece(move), to_sq(move), bonus); + // Penalty for reversed move in case of moved piece not being a pawn if (type_of(pos.moved_piece(move)) != PAWN) thisThread->mainHistory[us][from_to(reverse_move(move))] << -bonus; + // Update countermove history if (is_ok((ss-1)->currentMove)) { Square prevSq = to_sq((ss-1)->currentMove); thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] = move; } + // Update low ply history if (depth > 11 && ss->ply < MAX_LPH) thisThread->lowPlyHistory[ss->ply][from_to(move)] << stat_bonus(depth - 7); } From c7f0a768cb9d5972861baae0f215d69f9e86a626 Mon Sep 17 00:00:00 2001 From: Fanael Linithien Date: Mon, 7 Dec 2020 14:46:29 +0100 Subject: [PATCH 0459/1766] Use arithmetic right shift for sign extension in MMX and SSE2 paths This appears to be slightly faster than using a comparison against zero to compute the high bits, on both old (like Pentium III) and new (like Zen 2) hardware. closes https://github.com/official-stockfish/Stockfish/pull/3254 No functional change. --- src/nnue/layers/affine_transform.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index caf315b2792..0e0515f932a 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -680,9 +680,8 @@ namespace Eval::NNUE::Layers { for (IndexType j = 0; j < kNumChunks; ++j) { __m128i row_j = _mm_load_si128(&row[j]); __m128i input_j = _mm_load_si128(&input_vector[j]); - __m128i row_signs = _mm_cmpgt_epi8(kZeros, row_j); - __m128i extended_row_lo = _mm_unpacklo_epi8(row_j, row_signs); - __m128i extended_row_hi = _mm_unpackhi_epi8(row_j, row_signs); + __m128i extended_row_lo = _mm_srai_epi16(_mm_unpacklo_epi8(row_j, row_j), 8); + __m128i extended_row_hi = _mm_srai_epi16(_mm_unpackhi_epi8(row_j, row_j), 8); __m128i extended_input_lo = _mm_unpacklo_epi8(input_j, kZeros); __m128i extended_input_hi = _mm_unpackhi_epi8(input_j, kZeros); __m128i product_lo = _mm_madd_epi16(extended_row_lo, extended_input_lo); @@ -704,9 +703,8 @@ namespace Eval::NNUE::Layers { for (IndexType j = 0; j < kNumChunks; ++j) { __m64 row_j = row[j]; __m64 input_j = input_vector[j]; - __m64 row_signs = _mm_cmpgt_pi8(kZeros, row_j); - __m64 extended_row_lo = _mm_unpacklo_pi8(row_j, row_signs); - __m64 extended_row_hi = _mm_unpackhi_pi8(row_j, row_signs); + __m64 extended_row_lo = _mm_srai_pi16(_mm_unpacklo_pi8(row_j, row_j), 8); + __m64 extended_row_hi = _mm_srai_pi16(_mm_unpackhi_pi8(row_j, row_j), 8); __m64 extended_input_lo = _mm_unpacklo_pi8(input_j, kZeros); __m64 extended_input_hi = _mm_unpackhi_pi8(input_j, kZeros); __m64 product_lo = _mm_madd_pi16(extended_row_lo, extended_input_lo); From d706ae62d73d90c0f80cdccd58384a347295d549 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Mon, 7 Dec 2020 19:28:47 +0200 Subject: [PATCH 0460/1766] New Imbalance Tables Tweak Imbalance tables tweaked to contain MiddleGame and Endgame values, instead of a single value. The idea started from Fisherman, which requested my help to tune the values back in June/July, so I tuned the values back then, and we were able to accomplish good results, but not enough to pass both STC and LTC tests. So after the recent changes, I decided to give it another shot, and I am glad that it was a successful attempt. A special thanks goes also to mstembera, which notified me a simple way to let the patch perform a little better. Passed STC: LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 115976 W: 23124 L: 22695 D: 70157 Ptnml(0-2): 2074, 13652, 26285, 13725, 2252 https://tests.stockfishchess.org/tests/view/5fc92d2d42a050a89f02ccc8 Passed LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.25} Total: 156304 W: 20617 L: 20024 D: 115663 Ptnml(0-2): 1138, 14647, 46084, 15050, 1233 https://tests.stockfishchess.org/tests/view/5fc9fee142a050a89f02cd3e closes https://github.com/official-stockfish/Stockfish/pull/3255 Bench: 4278746 --- src/evaluate.cpp | 2 +- src/material.cpp | 37 ++++++++++++++++++++----------------- src/material.h | 8 ++++---- src/pawns.cpp | 1 + 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index ef33adf5d77..c507aa06a85 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -594,7 +594,7 @@ namespace { int kingFlankDefense = popcount(b3); kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them] // (~10 Elo) - + 185 * popcount(kingRing[Us] & weak) // (~15 Elo) + + 183 * popcount(kingRing[Us] & weak) // (~15 Elo) + 148 * popcount(unsafeChecks) // (~4 Elo) + 98 * popcount(pos.blockers_for_king(Us)) // (~2 Elo) + 69 * kingAttacksCount[Them] // (~0.5 Elo) diff --git a/src/material.cpp b/src/material.cpp index 870a5e112cb..f77972e352c 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -25,31 +25,34 @@ using namespace std; namespace { + #define S(mg, eg) make_score(mg, eg) // Polynomial material imbalance parameters - constexpr int QuadraticOurs[][PIECE_TYPE_NB] = { + constexpr Score QuadraticOurs[][PIECE_TYPE_NB] = { // OUR PIECES // pair pawn knight bishop rook queen - {1438 }, // Bishop pair - { 40, 38 }, // Pawn - { 32, 255, -62 }, // Knight OUR PIECES - { 0, 104, 4, 0 }, // Bishop - { -26, -2, 47, 105, -208 }, // Rook - {-189, 24, 117, 133, -134, -6 } // Queen + {S(1419, 1455) }, // Bishop pair + {S( 101, 28), S( 37, 39) }, // Pawn + {S( 57, 64), S(249, 187), S(-49, -62) }, // Knight OUR PIECES + {S( 0, 0), S(118, 137), S( 10, 27), S( 0, 0) }, // Bishop + {S( -63, -68), S( -5, 3), S(100, 81), S(132, 118), S(-246, -244) }, // Rook + {S(-210, -211), S( 37, 14), S(147, 141), S(161, 105), S(-158, -174), S(-9,-31) } // Queen }; - constexpr int QuadraticTheirs[][PIECE_TYPE_NB] = { + constexpr Score QuadraticTheirs[][PIECE_TYPE_NB] = { // THEIR PIECES // pair pawn knight bishop rook queen - { }, // Bishop pair - { 36, }, // Pawn - { 9, 63, }, // Knight OUR PIECES - { 59, 65, 42, }, // Bishop - { 46, 39, 24, -24, }, // Rook - { 97, 100, -42, 137, 268, } // Queen + { }, // Bishop pair + {S( 33, 30) }, // Pawn + {S( 46, 18), S(106, 84) }, // Knight OUR PIECES + {S( 75, 35), S( 59, 44), S( 60, 15) }, // Bishop + {S( 26, 35), S( 6, 22), S( 38, 39), S(-12, -2) }, // Rook + {S( 97, 93), S(100, 163), S(-58, -91), S(112, 192), S(276, 225) } // Queen }; + #undef S + // Endgame evaluation and scaling functions are accessed directly and not through // the function maps because they correspond to more than one material hash key. Endgame EvaluateKXK[] = { Endgame(WHITE), Endgame(BLACK) }; @@ -82,11 +85,11 @@ namespace { /// piece type for both colors. template - int imbalance(const int pieceCount[][PIECE_TYPE_NB]) { + Score imbalance(const int pieceCount[][PIECE_TYPE_NB]) { constexpr Color Them = ~Us; - int bonus = 0; + Score bonus = SCORE_ZERO; // Second-degree polynomial material imbalance, by Tord Romstad for (int pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1) @@ -213,7 +216,7 @@ Entry* probe(const Position& pos) { { pos.count(BLACK) > 1, pos.count(BLACK), pos.count(BLACK), pos.count(BLACK) , pos.count(BLACK), pos.count(BLACK) } }; - e->value = int16_t((imbalance(pieceCount) - imbalance(pieceCount)) / 16); + e->score = (imbalance(pieceCount) - imbalance(pieceCount)) / 16; return e; } diff --git a/src/material.h b/src/material.h index 80d01655111..28da59dbf33 100644 --- a/src/material.h +++ b/src/material.h @@ -37,8 +37,8 @@ namespace Material { struct Entry { - Score imbalance() const { return make_score(value, value); } - Phase game_phase() const { return gamePhase; } + Score imbalance() const { return score; } + Phase game_phase() const { return (Phase)gamePhase; } bool specialized_eval_exists() const { return evaluationFunction != nullptr; } Value evaluate(const Position& pos) const { return (*evaluationFunction)(pos); } @@ -57,9 +57,9 @@ struct Entry { const EndgameBase* evaluationFunction; const EndgameBase* scalingFunction[COLOR_NB]; // Could be one for each // side (e.g. KPKP, KBPsK) - int16_t value; + Score score; + int16_t gamePhase; uint8_t factor[COLOR_NB]; - Phase gamePhase; }; typedef HashTable Table; diff --git a/src/pawns.cpp b/src/pawns.cpp index b6d2900305a..16dbf27a5b1 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -66,6 +66,7 @@ namespace { { V(-17), V( -13), V( 100), V( 4), V( 9), V(-16), V(-31) } }; + // KingOnFile[semi-open Us][semi-open Them] contains bonuses/penalties // for king when the king is on a semi-open or open file. constexpr Score KingOnFile[2][2] = {{ S(-19,12), S(-6, 7) }, From d862ba40692797031ec5b0d95e46bcfc5a80f06c Mon Sep 17 00:00:00 2001 From: mstembera Date: Sat, 12 Dec 2020 14:18:38 -0800 Subject: [PATCH 0461/1766] AVX512, AVX2 and SSSE3 speedups Improves throughput by summing 2 intermediate dot products using 16 bit addition before upconverting to 32 bit. Potential saturation is detected and the code-path is avoided in this case. The saturation can't happen with the current nets, but nets can be constructed that trigger this check. STC https://tests.stockfishchess.org/tests/view/5fd40a861ac1691201888479 LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 25544 W: 2451 L: 2296 D: 20797 Ptnml(0-2): 92, 1761, 8925, 1888, 106 about 5% speedup closes https://github.com/official-stockfish/Stockfish/pull/3261 No functional change --- src/nnue/layers/affine_transform.h | 355 ++++++++++++++++------------- 1 file changed, 199 insertions(+), 156 deletions(-) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 0e0515f932a..a715ca85090 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -66,6 +66,53 @@ namespace Eval::NNUE::Layers { biases_[i] = read_little_endian(stream); for (std::size_t i = 0; i < kOutputDimensions * kPaddedInputDimensions; ++i) weights_[i] = read_little_endian(stream); + +#if defined (USE_SSSE3) + // Determine if quadruplets of weight and input products can be summed using 16bits + // without saturation. We assume worst case combinations of 0 and 127 for all inputs. + if (!stream.fail()) + { + auto can_saturate = [](const WeightType* w, int idx[4]) { + int pSum = 0, nSum = 0; + for (int p = 0; p < 4; ++p) + if (w[idx[p]] > 0) + pSum += w[idx[p]]; + else + nSum += w[idx[p]]; + + return pSum > 258 || nSum < -258; + }; + + for (IndexType i = 0; i < kOutputDimensions; ++i) + { + canSaturate16[i] = false; + const WeightType* w = &weights_[i * kPaddedInputDimensions]; +#if defined (USE_AVX512) + for (IndexType j = 0; j < (kPaddedInputDimensions & ~127) && !canSaturate16[i]; j += 128) + for (int k = 0; k < 64 && !canSaturate16[i]; k += 2) + { + int spacing[4] = { 0, 1, 64, 65 }; + canSaturate16[i] = can_saturate(&w[j + k], spacing); + } +#elif defined (USE_AVX2) + for (IndexType j = 0; j < (kPaddedInputDimensions & ~63) && !canSaturate16[i]; j += 64) + for (int k = 0; k < 32 && !canSaturate16[i]; k += 2) + { + int spacing[4] = { 0, 1, 32, 33 }; + canSaturate16[i] = can_saturate(&w[j + k], spacing); + } +#elif defined (USE_SSSE3) + for (IndexType j = 0; j < (kPaddedInputDimensions & ~31) && !canSaturate16[i]; j += 32) + for (int k = 0; k < 16 && !canSaturate16[i]; k += 2) + { + int spacing[4] = { 0, 1, 16, 17 }; + canSaturate16[i] = can_saturate(&w[j + k], spacing); + } +#endif + } + } +#endif + return !stream.fail(); } @@ -181,13 +228,26 @@ namespace Eval::NNUE::Layers { return _mm512_add_epi32(_mm512_permutexvar_epi32(indices, x), bias); }; -#if defined (USE_VNNI) [[maybe_unused]] auto m512_add_dpbusd_epi32 = [=](__m512i& acc, __m512i a, __m512i b) { +#if defined (USE_VNNI) acc = _mm512_dpbusd_epi32(acc, a, b); #else - [[maybe_unused]] auto m512_dpbusd_epi32 = [=](__m512i a, __m512i b) -> __m512i { __m512i product0 = _mm512_maddubs_epi16(a, b); - return _mm512_madd_epi16(product0, kOnes512); + product0 = _mm512_madd_epi16(product0, kOnes512); + acc = _mm512_add_epi32(acc, product0); +#endif + }; + + [[maybe_unused]] auto m512_add_dpbusd_epi32x2 = [=](__m512i& acc, __m512i a0, __m512i b0, __m512i a1, __m512i b1) { +#if defined (USE_VNNI) + acc = _mm512_dpbusd_epi32(acc, a0, b0); + acc = _mm512_dpbusd_epi32(acc, a1, b1); +#else + __m512i product0 = _mm512_maddubs_epi16(a0, b0); + __m512i product1 = _mm512_maddubs_epi16(a1, b1); + product0 = _mm512_adds_epi16(product0, product1); + product0 = _mm512_madd_epi16(product0, kOnes512); + acc = _mm512_add_epi32(acc, product0); #endif }; @@ -214,13 +274,27 @@ namespace Eval::NNUE::Layers { return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias); }; -#if defined (USE_VNNI) + [[maybe_unused]] auto m256_add_dpbusd_epi32 = [=](__m256i& acc, __m256i a, __m256i b) { +#if defined (USE_VNNI) acc = _mm256_dpbusd_epi32(acc, a, b); #else - [[maybe_unused]] auto m256_dpbusd_epi32 = [=](__m256i a, __m256i b) -> __m256i { __m256i product0 = _mm256_maddubs_epi16(a, b); - return _mm256_madd_epi16(product0, kOnes256); + product0 = _mm256_madd_epi16(product0, kOnes256); + acc = _mm256_add_epi32(acc, product0); +#endif + }; + + [[maybe_unused]] auto m256_add_dpbusd_epi32x2 = [=](__m256i& acc, __m256i a0, __m256i b0, __m256i a1, __m256i b1) { +#if defined (USE_VNNI) + acc = _mm256_dpbusd_epi32(acc, a0, b0); + acc = _mm256_dpbusd_epi32(acc, a1, b1); +#else + __m256i product0 = _mm256_maddubs_epi16(a0, b0); + __m256i product1 = _mm256_maddubs_epi16(a1, b1); + product0 = _mm256_adds_epi16(product0, product1); + product0 = _mm256_madd_epi16(product0, kOnes256); + acc = _mm256_add_epi32(acc, product0); #endif }; @@ -245,9 +319,18 @@ namespace Eval::NNUE::Layers { return _mm_add_epi32(sum0, bias); }; - [[maybe_unused]] auto m128_dpbusd_epi32 = [=](__m128i a, __m128i b) -> __m128i { + [[maybe_unused]] auto m128_add_dpbusd_epi32 = [=](__m128i& acc, __m128i a, __m128i b) { __m128i product0 = _mm_maddubs_epi16(a, b); - return _mm_madd_epi16(product0, kOnes128); + product0 = _mm_madd_epi16(product0, kOnes128); + acc = _mm_add_epi32(acc, product0); + }; + + [[maybe_unused]] auto m128_add_dpbusd_epi32x2 = [=](__m128i& acc, __m128i a0, __m128i b0, __m128i a1, __m128i b1) { + __m128i product0 = _mm_maddubs_epi16(a0, b0); + __m128i product1 = _mm_maddubs_epi16(a1, b1); + product0 = _mm_adds_epi16(product0, product1); + product0 = _mm_madd_epi16(product0, kOnes128); + acc = _mm_add_epi32(acc, product0); }; #endif @@ -291,6 +374,15 @@ namespace Eval::NNUE::Layers { const __m512i bias = *reinterpret_cast(&biases_[i]); __m512i* outptr = reinterpret_cast<__m512i*>(&output[i]); + __m512i sum01a = _mm512_setzero_si512(); + __m512i sum23a = _mm512_setzero_si512(); + __m512i sum45a = _mm512_setzero_si512(); + __m512i sum67a = _mm512_setzero_si512(); + __m512i sum01b = _mm512_setzero_si512(); + __m512i sum23b = _mm512_setzero_si512(); + __m512i sum45b = _mm512_setzero_si512(); + __m512i sum67b = _mm512_setzero_si512(); + const auto row01a = *reinterpret_cast(&weights_[offset01a]); const auto row23a = *reinterpret_cast(&weights_[offset23a]); const auto row45a = *reinterpret_cast(&weights_[offset45a]); @@ -303,16 +395,6 @@ namespace Eval::NNUE::Layers { const __m256i in256 = input_vector256[0]; const __m512i in = _mm512_inserti64x4(_mm512_castsi256_si512(in256), in256, 1); -#if defined (USE_VNNI) - __m512i sum01a = _mm512_setzero_si512(); - __m512i sum23a = _mm512_setzero_si512(); - __m512i sum45a = _mm512_setzero_si512(); - __m512i sum67a = _mm512_setzero_si512(); - __m512i sum01b = _mm512_setzero_si512(); - __m512i sum23b = _mm512_setzero_si512(); - __m512i sum45b = _mm512_setzero_si512(); - __m512i sum67b = _mm512_setzero_si512(); - m512_add_dpbusd_epi32(sum01a, in, row01a); m512_add_dpbusd_epi32(sum23a, in, row23a); m512_add_dpbusd_epi32(sum45a, in, row45a); @@ -321,16 +403,6 @@ namespace Eval::NNUE::Layers { m512_add_dpbusd_epi32(sum23b, in, row23b); m512_add_dpbusd_epi32(sum45b, in, row45b); m512_add_dpbusd_epi32(sum67b, in, row67b); -#else - __m512i sum01a = m512_dpbusd_epi32(in, row01a); - __m512i sum23a = m512_dpbusd_epi32(in, row23a); - __m512i sum45a = m512_dpbusd_epi32(in, row45a); - __m512i sum67a = m512_dpbusd_epi32(in, row67a); - __m512i sum01b = m512_dpbusd_epi32(in, row01b); - __m512i sum23b = m512_dpbusd_epi32(in, row23b); - __m512i sum45b = m512_dpbusd_epi32(in, row45b); - __m512i sum67b = m512_dpbusd_epi32(in, row67b); -#endif *outptr = m512_hadd256x16( sum01a, sum23a, sum45a, sum67a, @@ -351,80 +423,62 @@ namespace Eval::NNUE::Layers { if constexpr (kPaddedInputDimensions % (kSimdWidth * 2) == 0) { - const auto row0 = reinterpret_cast(&weights_[offset0]); - const auto row1 = reinterpret_cast(&weights_[offset1]); - const auto row2 = reinterpret_cast(&weights_[offset2]); - const auto row3 = reinterpret_cast(&weights_[offset3]); - -#if defined (USE_VNNI) __m512i sum0 = _mm512_setzero_si512(); __m512i sum1 = _mm512_setzero_si512(); __m512i sum2 = _mm512_setzero_si512(); __m512i sum3 = _mm512_setzero_si512(); - const IndexType kStart = 0; -#else - __m512i sum0 = m512_dpbusd_epi32(input_vector512[0], row0[0]); - __m512i sum1 = m512_dpbusd_epi32(input_vector512[0], row1[0]); - __m512i sum2 = m512_dpbusd_epi32(input_vector512[0], row2[0]); - __m512i sum3 = m512_dpbusd_epi32(input_vector512[0], row3[0]); - const IndexType kStart = 1; -#endif - for (IndexType j = kStart; j < kNumChunks512; ++j) + const auto row0 = reinterpret_cast(&weights_[offset0]); + const auto row1 = reinterpret_cast(&weights_[offset1]); + const auto row2 = reinterpret_cast(&weights_[offset2]); + const auto row3 = reinterpret_cast(&weights_[offset3]); + + int j = 0; + if (!canSaturate16x4[i / 4]) + { + for (; j < (int)kNumChunks512 - 1; j += 2) + { + const __m512i in0 = input_vector512[j]; + const __m512i in1 = input_vector512[j + 1]; + + m512_add_dpbusd_epi32x2(sum0, in0, row0[j], in1, row0[j + 1]); + m512_add_dpbusd_epi32x2(sum1, in0, row1[j], in1, row1[j + 1]); + m512_add_dpbusd_epi32x2(sum2, in0, row2[j], in1, row2[j + 1]); + m512_add_dpbusd_epi32x2(sum3, in0, row3[j], in1, row3[j + 1]); + } + } + for (; j < (int)kNumChunks512; ++j) { const __m512i in = input_vector512[j]; -#if defined (USE_VNNI) m512_add_dpbusd_epi32(sum0, in, row0[j]); m512_add_dpbusd_epi32(sum1, in, row1[j]); m512_add_dpbusd_epi32(sum2, in, row2[j]); m512_add_dpbusd_epi32(sum3, in, row3[j]); -#else - sum0 = _mm512_add_epi32(sum0, m512_dpbusd_epi32(in, row0[j])); - sum1 = _mm512_add_epi32(sum1, m512_dpbusd_epi32(in, row1[j])); - sum2 = _mm512_add_epi32(sum2, m512_dpbusd_epi32(in, row2[j])); - sum3 = _mm512_add_epi32(sum3, m512_dpbusd_epi32(in, row3[j])); -#endif } *outptr = m512_haddx4(sum0, sum1, sum2, sum3, bias); } else { - const auto row0 = reinterpret_cast(&weights_[offset0]); - const auto row1 = reinterpret_cast(&weights_[offset1]); - const auto row2 = reinterpret_cast(&weights_[offset2]); - const auto row3 = reinterpret_cast(&weights_[offset3]); - -#if defined (USE_VNNI) __m256i sum0 = _mm256_setzero_si256(); __m256i sum1 = _mm256_setzero_si256(); __m256i sum2 = _mm256_setzero_si256(); __m256i sum3 = _mm256_setzero_si256(); - const IndexType kStart = 0; -#else - __m256i sum0 = m256_dpbusd_epi32(input_vector256[0], row0[0]); - __m256i sum1 = m256_dpbusd_epi32(input_vector256[0], row1[0]); - __m256i sum2 = m256_dpbusd_epi32(input_vector256[0], row2[0]); - __m256i sum3 = m256_dpbusd_epi32(input_vector256[0], row3[0]); - const IndexType kStart = 1; -#endif - for (IndexType j = kStart; j < kNumChunks256; ++j) + const auto row0 = reinterpret_cast(&weights_[offset0]); + const auto row1 = reinterpret_cast(&weights_[offset1]); + const auto row2 = reinterpret_cast(&weights_[offset2]); + const auto row3 = reinterpret_cast(&weights_[offset3]); + + for (IndexType j = 0; j < kNumChunks256; ++j) { const __m256i in = input_vector256[j]; -#if defined (USE_VNNI) m256_add_dpbusd_epi32(sum0, in, row0[j]); m256_add_dpbusd_epi32(sum1, in, row1[j]); m256_add_dpbusd_epi32(sum2, in, row2[j]); m256_add_dpbusd_epi32(sum3, in, row3[j]); -#else - sum0 = _mm256_add_epi32(sum0, m256_dpbusd_epi32(in, row0[j])); - sum1 = _mm256_add_epi32(sum1, m256_dpbusd_epi32(in, row1[j])); - sum2 = _mm256_add_epi32(sum2, m256_dpbusd_epi32(in, row2[j])); - sum3 = _mm256_add_epi32(sum3, m256_dpbusd_epi32(in, row3[j])); -#endif } *outptr = m256_haddx4(sum0, sum1, sum2, sum3, bias); @@ -435,50 +489,30 @@ namespace Eval::NNUE::Layers { { if constexpr (kPaddedInputDimensions % (kSimdWidth * 2) == 0) { - const auto row0 = reinterpret_cast(&weights_[0]); - -#if defined (USE_VNNI) __m512i sum0 = _mm512_setzero_si512(); - const IndexType kStart = 0; -#else - __m512i sum0 = m512_dpbusd_epi32(input_vector512[0], row0[0]); - const IndexType kStart = 1; -#endif - for (IndexType j = kStart; j < kNumChunks512; ++j) + const auto row0 = reinterpret_cast(&weights_[0]); + + for (IndexType j = 0; j < kNumChunks512; ++j) { const __m512i in = input_vector512[j]; -#if defined (USE_VNNI) m512_add_dpbusd_epi32(sum0, in, row0[j]); -#else - sum0 = _mm512_add_epi32(sum0, m512_dpbusd_epi32(in, row0[j])); -#endif } output[0] = m512_hadd(sum0, biases_[0]); } else { - const auto row0 = reinterpret_cast(&weights_[0]); - -#if defined (USE_VNNI) __m256i sum0 = _mm256_setzero_si256(); - const IndexType kStart = 0; -#else - __m256i sum0 = m256_dpbusd_epi32(input_vector256[0], row0[0]); - const IndexType kStart = 1; -#endif - for (IndexType j = kStart; j < kNumChunks256; ++j) + const auto row0 = reinterpret_cast(&weights_[0]); + + for (IndexType j = 0; j < kNumChunks256; ++j) { const __m256i in = input_vector256[j]; -#if defined (USE_VNNI) m256_add_dpbusd_epi32(sum0, in, row0[j]); -#else - sum0 = _mm256_add_epi32(sum0, m256_dpbusd_epi32(in, row0[j])); -#endif } output[0] = m256_hadd(sum0, biases_[0]); @@ -512,40 +546,38 @@ namespace Eval::NNUE::Layers { const __m128i bias = *reinterpret_cast(&biases_[i]); __m128i* outptr = reinterpret_cast<__m128i*>(&output[i]); - const auto row0 = reinterpret_cast(&weights_[offset0]); - const auto row1 = reinterpret_cast(&weights_[offset1]); - const auto row2 = reinterpret_cast(&weights_[offset2]); - const auto row3 = reinterpret_cast(&weights_[offset3]); - -#if defined (USE_VNNI) __m256i sum0 = _mm256_setzero_si256(); __m256i sum1 = _mm256_setzero_si256(); __m256i sum2 = _mm256_setzero_si256(); __m256i sum3 = _mm256_setzero_si256(); - const IndexType kStart = 0; -#else - __m256i sum0 = m256_dpbusd_epi32(input_vector[0], row0[0]); - __m256i sum1 = m256_dpbusd_epi32(input_vector[0], row1[0]); - __m256i sum2 = m256_dpbusd_epi32(input_vector[0], row2[0]); - __m256i sum3 = m256_dpbusd_epi32(input_vector[0], row3[0]); - const IndexType kStart = 1; -#endif - for (IndexType j = kStart; j < kNumChunks; ++j) + const auto row0 = reinterpret_cast(&weights_[offset0]); + const auto row1 = reinterpret_cast(&weights_[offset1]); + const auto row2 = reinterpret_cast(&weights_[offset2]); + const auto row3 = reinterpret_cast(&weights_[offset3]); + + int j = 0; + if (!canSaturate16x4[i / 4]) { - const __m256i in = input_vector[j]; + for (; j < (int)kNumChunks - 1; j += 2) + { + const __m256i in0 = input_vector[j]; + const __m256i in1 = input_vector[j + 1]; + + m256_add_dpbusd_epi32x2(sum0, in0, row0[j], in1, row0[j + 1]); + m256_add_dpbusd_epi32x2(sum1, in0, row1[j], in1, row1[j + 1]); + m256_add_dpbusd_epi32x2(sum2, in0, row2[j], in1, row2[j + 1]); + m256_add_dpbusd_epi32x2(sum3, in0, row3[j], in1, row3[j + 1]); + } + } + for (; j < (int)kNumChunks; ++j) + { + const __m256i in = input_vector[j]; -#if defined (USE_VNNI) - m256_add_dpbusd_epi32(sum0, in, row0[j]); - m256_add_dpbusd_epi32(sum1, in, row1[j]); - m256_add_dpbusd_epi32(sum2, in, row2[j]); - m256_add_dpbusd_epi32(sum3, in, row3[j]); -#else - sum0 = _mm256_add_epi32(sum0, m256_dpbusd_epi32(in, row0[j])); - sum1 = _mm256_add_epi32(sum1, m256_dpbusd_epi32(in, row1[j])); - sum2 = _mm256_add_epi32(sum2, m256_dpbusd_epi32(in, row2[j])); - sum3 = _mm256_add_epi32(sum3, m256_dpbusd_epi32(in, row3[j])); -#endif + m256_add_dpbusd_epi32(sum0, in, row0[j]); + m256_add_dpbusd_epi32(sum1, in, row1[j]); + m256_add_dpbusd_epi32(sum2, in, row2[j]); + m256_add_dpbusd_epi32(sum3, in, row3[j]); } *outptr = m256_haddx4(sum0, sum1, sum2, sum3, bias); @@ -553,25 +585,15 @@ namespace Eval::NNUE::Layers { } else if constexpr (kOutputDimensions == 1) { - const auto row0 = reinterpret_cast(&weights_[0]); - -#if defined (USE_VNNI) __m256i sum0 = _mm256_setzero_si256(); - const IndexType kStart = 0; -#else - __m256i sum0 = m256_dpbusd_epi32(input_vector[0], row0[0]); - const IndexType kStart = 1; -#endif - for (IndexType j = kStart; j < kNumChunks; ++j) + const auto row0 = reinterpret_cast(&weights_[0]); + + for (IndexType j = 0; j < kNumChunks; ++j) { - const __m256i in = input_vector[j]; + const __m256i in = input_vector[j]; -#if defined (USE_VNNI) - m256_add_dpbusd_epi32(sum0, in, row0[j]); -#else - sum0 = _mm256_add_epi32(sum0, m256_dpbusd_epi32(in, row0[j])); -#endif + m256_add_dpbusd_epi32(sum0, in, row0[j]); } output[0] = m256_hadd(sum0, biases_[0]); @@ -604,24 +626,38 @@ namespace Eval::NNUE::Layers { const __m128i bias = *reinterpret_cast(&biases_[i]); __m128i* outptr = reinterpret_cast<__m128i*>(&output[i]); + __m128i sum0 = _mm_setzero_si128(); + __m128i sum1 = _mm_setzero_si128(); + __m128i sum2 = _mm_setzero_si128(); + __m128i sum3 = _mm_setzero_si128(); + const auto row0 = reinterpret_cast(&weights_[offset0]); const auto row1 = reinterpret_cast(&weights_[offset1]); const auto row2 = reinterpret_cast(&weights_[offset2]); const auto row3 = reinterpret_cast(&weights_[offset3]); - __m128i sum0 = m128_dpbusd_epi32(input_vector[0], row0[0]); - __m128i sum1 = m128_dpbusd_epi32(input_vector[0], row1[0]); - __m128i sum2 = m128_dpbusd_epi32(input_vector[0], row2[0]); - __m128i sum3 = m128_dpbusd_epi32(input_vector[0], row3[0]); - - for (int j = 1; j < (int)kNumChunks; ++j) + int j = 0; + if (!canSaturate16x4[i / 4]) + { + for (; j < (int)kNumChunks - 1; j += 2) + { + const __m128i in0 = input_vector[j]; + const __m128i in1 = input_vector[j + 1]; + + m128_add_dpbusd_epi32x2(sum0, in0, row0[j], in1, row0[j + 1]); + m128_add_dpbusd_epi32x2(sum1, in0, row1[j], in1, row1[j + 1]); + m128_add_dpbusd_epi32x2(sum2, in0, row2[j], in1, row2[j + 1]); + m128_add_dpbusd_epi32x2(sum3, in0, row3[j], in1, row3[j + 1]); + } + } + for (; j < (int)kNumChunks; ++j) { - const __m128i in = input_vector[j]; + const __m128i in = input_vector[j]; - sum0 = _mm_add_epi32(sum0, m128_dpbusd_epi32(in, row0[j])); - sum1 = _mm_add_epi32(sum1, m128_dpbusd_epi32(in, row1[j])); - sum2 = _mm_add_epi32(sum2, m128_dpbusd_epi32(in, row2[j])); - sum3 = _mm_add_epi32(sum3, m128_dpbusd_epi32(in, row3[j])); + m128_add_dpbusd_epi32(sum0, in, row0[j]); + m128_add_dpbusd_epi32(sum1, in, row1[j]); + m128_add_dpbusd_epi32(sum2, in, row2[j]); + m128_add_dpbusd_epi32(sum3, in, row3[j]); } *outptr = m128_haddx4(sum0, sum1, sum2, sum3, bias); @@ -629,12 +665,16 @@ namespace Eval::NNUE::Layers { } else if constexpr (kOutputDimensions == 1) { + __m128i sum0 = _mm_setzero_si128(); + const auto row0 = reinterpret_cast(&weights_[0]); - __m128i sum0 = m128_dpbusd_epi32(input_vector[0], row0[0]); + for (int j = 0; j < (int)kNumChunks; ++j) + { + const __m128i in = input_vector[j]; - for (int j = 1; j < (int)kNumChunks; ++j) - sum0 = _mm_add_epi32(sum0, m128_dpbusd_epi32(input_vector[j], row0[j])); + m128_add_dpbusd_epi32(sum0, in, row0[j]); + } output[0] = m128_hadd(sum0, biases_[0]); } @@ -751,8 +791,11 @@ namespace Eval::NNUE::Layers { PreviousLayer previous_layer_; alignas(kCacheLineSize) BiasType biases_[kOutputDimensions]; - alignas(kCacheLineSize) - WeightType weights_[kOutputDimensions * kPaddedInputDimensions]; + alignas(kCacheLineSize) WeightType weights_[kOutputDimensions * kPaddedInputDimensions]; + union { + uint32_t canSaturate16x4[(kOutputDimensions + 3) / 4]; + bool canSaturate16[kOutputDimensions]; + }; }; } // namespace Eval::NNUE::Layers From 16adcb5374a6847b52dd901e183d78c0c8c6cf8a Mon Sep 17 00:00:00 2001 From: pb00067 Date: Sun, 13 Dec 2020 21:23:30 +0100 Subject: [PATCH 0462/1766] Merge static history into main history, thus simplifying and reducing the memory footprint. I believe using static diff for better move ordering is more suited for low depths, so restrict writing to low depths. Todo: probably the condition for writing can be simplified LTC: LLR: 2.95 (-2.94,2.94) {-0.75,0.25} Total: 18752 W: 768 L: 705 D: 17279 Ptnml(0-2): 7, 635, 8034, 688, 12 https://tests.stockfishchess.org/tests/view/5fd631791ac169120188859e STC: LLR: 2.95 (-2.94,2.94) {-1.25,0.25} Total: 36504 W: 3380 L: 3313 D: 29811 Ptnml(0-2): 116, 2667, 12645, 2682, 142 https://tests.stockfishchess.org/tests/view/5fd5ed861ac1691201888569 closes https://github.com/official-stockfish/Stockfish/pull/3262 bench: 4018036 --- src/movepick.cpp | 9 ++++----- src/movepick.h | 3 --- src/search.cpp | 11 +++-------- src/thread.cpp | 1 - src/thread.h | 1 - 5 files changed, 7 insertions(+), 18 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 9ca1a21f6d0..f5e02385556 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -54,9 +54,9 @@ namespace { /// ordering is at the current node. /// MovePicker constructor for the main search -MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const ButterflyHistory* sh, const LowPlyHistory* lp, +MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const LowPlyHistory* lp, const CapturePieceToHistory* cph, const PieceToHistory** ch, Move cm, const Move* killers, int pl) - : pos(p), mainHistory(mh), staticHistory(sh), lowPlyHistory(lp), captureHistory(cph), continuationHistory(ch), + : pos(p), mainHistory(mh), lowPlyHistory(lp), captureHistory(cph), continuationHistory(ch), ttMove(ttm), refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d), ply(pl) { assert(d > 0); @@ -66,9 +66,9 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist } /// MovePicker constructor for quiescence search -MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const ButterflyHistory* sh, +MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const CapturePieceToHistory* cph, const PieceToHistory** ch, Square rs) - : pos(p), mainHistory(mh), staticHistory(sh), captureHistory(cph), continuationHistory(ch), ttMove(ttm), recaptureSquare(rs), depth(d) { + : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), ttMove(ttm), recaptureSquare(rs), depth(d) { assert(d <= 0); @@ -105,7 +105,6 @@ void MovePicker::score() { else if (Type == QUIETS) m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] - + (*staticHistory)[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)] diff --git a/src/movepick.h b/src/movepick.h index 7fe98e49b9a..4c0ad55172d 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -123,12 +123,10 @@ class MovePicker { MovePicker& operator=(const MovePicker&) = delete; MovePicker(const Position&, Move, Value, const CapturePieceToHistory*); MovePicker(const Position&, Move, Depth, const ButterflyHistory*, - const ButterflyHistory*, const CapturePieceToHistory*, const PieceToHistory**, Square); MovePicker(const Position&, Move, Depth, const ButterflyHistory*, - const ButterflyHistory*, const LowPlyHistory*, const CapturePieceToHistory*, const PieceToHistory**, @@ -145,7 +143,6 @@ class MovePicker { const Position& pos; const ButterflyHistory* mainHistory; - const ButterflyHistory* staticHistory; const LowPlyHistory* lowPlyHistory; const CapturePieceToHistory* captureHistory; const PieceToHistory** continuationHistory; diff --git a/src/search.cpp b/src/search.cpp index ba9265ddb38..e825fce7522 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -815,13 +815,10 @@ namespace { tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); } - // Update static history for previous move - if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture) + if ((ss-1)->moveCount > 1 && is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture && depth < 7) { - int bonus = ss->staticEval > -(ss-1)->staticEval + 2 * Tempo ? -stat_bonus(depth) : - ss->staticEval < -(ss-1)->staticEval + 2 * Tempo ? stat_bonus(depth) : - 0; - thisThread->staticHistory[~us][from_to((ss-1)->currentMove)] << bonus; + int bonus = std::clamp(- (depth+1) * 2 * int((ss-1)->staticEval + ss->staticEval - 2 * Tempo), -1000, 1000); + thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus; } // Step 7. Razoring (~1 Elo) @@ -985,7 +982,6 @@ namespace { Move countermove = thisThread->counterMoves[pos.piece_on(prevSq)][prevSq]; MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, - &thisThread->staticHistory, &thisThread->lowPlyHistory, &captureHistory, contHist, @@ -1536,7 +1532,6 @@ namespace { // queen and checking knight promotions, and other checks(only if depth >= DEPTH_QS_CHECKS) // will be generated. MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, - &thisThread->staticHistory, &thisThread->captureHistory, contHist, to_sq((ss-1)->currentMove)); diff --git a/src/thread.cpp b/src/thread.cpp index bfcdb65ebad..2fbf745d072 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -57,7 +57,6 @@ void Thread::clear() { counterMoves.fill(MOVE_NONE); mainHistory.fill(0); - staticHistory.fill(0); lowPlyHistory.fill(0); captureHistory.fill(0); diff --git a/src/thread.h b/src/thread.h index c05fa941a12..6a73423b2c3 100644 --- a/src/thread.h +++ b/src/thread.h @@ -69,7 +69,6 @@ class Thread { Depth rootDepth, completedDepth; CounterMoveHistory counterMoves; ButterflyHistory mainHistory; - ButterflyHistory staticHistory; LowPlyHistory lowPlyHistory; CapturePieceToHistory captureHistory; ContinuationHistory continuationHistory[2][2]; From a88a38c3a91749181ffa5d6dc0af7314a70a1c41 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Mon, 14 Dec 2020 03:49:04 +0300 Subject: [PATCH 0463/1766] Increase reduction in case of stable best move The idea of this patch is pretty simple - we already do more reductions for non-PV and root nodes in case of stable best move for depth > 10. This patch makes us do so if root depth if > 10 instead, which is logical since best move changes (thus instability of it) is counted at root, so it makes a lot of sense to use depth of the root. passed STC https://tests.stockfishchess.org/tests/view/5fd643271ac16912018885c5 LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 13232 W: 1308 L: 1169 D: 10755 Ptnml(0-2): 39, 935, 4535, 1062, 45 passed LTC https://tests.stockfishchess.org/tests/view/5fd68db11ac16912018885f0 LLR: 2.96 (-2.94,2.94) {0.25,1.25} Total: 14024 W: 565 L: 463 D: 12996 Ptnml(0-2): 3, 423, 6062, 517, 7 closes https://github.com/official-stockfish/Stockfish/pull/3263 Bench: 4050630 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index e825fce7522..cdbccb4c0ab 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1188,7 +1188,7 @@ namespace { r -= 2; // Increase reduction at root and non-PV nodes when the best move does not change frequently - if ((rootNode || !PvNode) && depth > 10 && thisThread->bestMoveChanges <= 2) + if ((rootNode || !PvNode) && thisThread->rootDepth > 10 && thisThread->bestMoveChanges <= 2) r++; // More reductions for late moves if position was not in previous PV From 66a7a8a0cc4a5958c91c46d32197dc04523cdb43 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Wed, 16 Dec 2020 15:35:39 +0200 Subject: [PATCH 0464/1766] Adjust definition of unsafeSquares and adjust related bonus values. The bonus is now not given whenever there is an enemy piece in front of the pawn. Passed STC: LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 109472 W: 22097 L: 21673 D: 65702 Ptnml(0-2): 2111, 12800, 24482, 13240, 2103 https://tests.stockfishchess.org/tests/view/5fd8d3740c5870924361ffad Passed LTC: LLR: 2.95 (-2.94,2.94) {0.25,1.25} Total: 39384 W: 5334 L: 4990 D: 29060 Ptnml(0-2): 279, 3648, 11535, 3910, 320 https://tests.stockfishchess.org/tests/view/5fd971ab0c5870924361fff0 closes https://github.com/official-stockfish/Stockfish/pull/3266 Bench: 4488955 --- src/evaluate.cpp | 8 ++++---- src/pawns.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index c507aa06a85..dd5d23b253d 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -237,11 +237,11 @@ namespace { // PassedRank[Rank] contains a bonus according to the rank of a passed pawn constexpr Score PassedRank[RANK_NB] = { - S(0, 0), S(9, 28), S(15, 31), S(17, 39), S(64, 70), S(171, 177), S(277, 260) + S(0, 0), S(7, 27), S(16, 32), S(17, 40), S(64, 71), S(170, 174), S(278, 262) }; constexpr Score RookOnClosedFile = S(10, 5); - constexpr Score RookOnOpenFile[] = { S(19, 7), S(48, 27) }; + constexpr Score RookOnOpenFile[] = { S(19, 6), S(47, 26) }; // ThreatByMinor/ByRook[attacked PieceType] contains bonuses according to // which piece type attacks which one. Attacks on lesser pieces which are @@ -788,9 +788,9 @@ namespace { bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN); if (!(pos.pieces(Them) & bb)) - unsafeSquares &= attackedBy[Them][ALL_PIECES]; + unsafeSquares &= attackedBy[Them][ALL_PIECES] | pos.pieces(Them); - // If there are no enemy attacks on passed pawn span, assign a big bonus. + // If there are no enemy pieces or attacks on passed pawn span, assign a big bonus. // Otherwise assign a smaller bonus if the path to queen is not attacked // and even smaller bonus if it is attacked but block square is not. int k = !unsafeSquares ? 35 : diff --git a/src/pawns.cpp b/src/pawns.cpp index 16dbf27a5b1..ed83fde72e5 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -37,10 +37,10 @@ namespace { constexpr Score WeakUnopposed = S(13, 25); // Bonus for blocked pawns at 5th or 6th rank - constexpr Score BlockedPawn[2] = { S(-13, -4), S(-5, 2) }; + constexpr Score BlockedPawn[2] = { S(-15, -3), S(-6, 3) }; constexpr Score BlockedStorm[RANK_NB] = { - S(0, 0), S(0, 0), S(76, 78), S(-10, 15), S(-7, 10), S(-4, 6), S(-1, 2) + S(0, 0), S(0, 0), S(75, 78), S(-8, 16), S(-6, 10), S(-6, 6), S(0, 2) }; // Connected pawn bonus From 1f3b5b8b54fbf9d3b74bcba831466eeb1104b421 Mon Sep 17 00:00:00 2001 From: pb00067 Date: Mon, 14 Dec 2020 16:30:56 +0100 Subject: [PATCH 0465/1766] Simplify condition for assigning static-eval based bonus for quiet move ordering and simplify bonus formula. Due to clamping the bonus to relative low values the impact on high depths is minimal, thus the restriction to low depths seems not necessary. Also the condition of movecount in previous node seems to be not determinant. Passed STC: LLR: 2.95 (-2.94,2.94) {-1.25,0.25} Total: 14600 W: 1424 L: 1323 D: 11853 Ptnml(0-2): 55, 1033, 5020, 1140, 52 https://tests.stockfishchess.org/tests/view/5fd67b381ac16912018885ec Passed LTC: LLR: 2.95 (-2.94,2.94) {-0.75,0.25} Total: 85008 W: 3218 L: 3206 D: 78584 Ptnml(0-2): 49, 2840, 36700, 2880, 35 https://tests.stockfishchess.org/tests/view/5fd6af041ac16912018885f8 closes https://github.com/official-stockfish/Stockfish/pull/3265 bench: 4524994 --- src/search.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index cdbccb4c0ab..e272c10b740 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -815,9 +815,10 @@ namespace { tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); } - if ((ss-1)->moveCount > 1 && is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture && depth < 7) + // Use static evaluation difference to improve quiet move ordering + if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture) { - int bonus = std::clamp(- (depth+1) * 2 * int((ss-1)->staticEval + ss->staticEval - 2 * Tempo), -1000, 1000); + int bonus = std::clamp(-depth * 4 * int((ss-1)->staticEval + ss->staticEval - 2 * Tempo), -1000, 1000); thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus; } From 45b05328b6d10b1f72786a59d78ae95c17ec40dd Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sun, 20 Dec 2020 17:50:34 +0200 Subject: [PATCH 0466/1766] Tweak the formulas for unsafeSquares We give more bonus for a special case: If there are some enemy squares occupied or attacked by the enemy on the passed pawn span, but if they are all attacked by our pawn, use new intermediate factor 30. The main credit goes to Rocky for the idea, with additional tuning and tests. Passed STC: LLR: 2.95 (-2.94,2.94) {-0.25,1.25} Total: 96464 W: 19233 L: 18834 D: 58397 Ptnml(0-2): 1683, 11327, 21950, 11452, 1820 https://tests.stockfishchess.org/tests/view/5fdd21ab3932f79192d39357 Passed LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.25} Total: 81320 W: 10784 L: 10352 D: 60184 Ptnml(0-2): 602, 7524, 24044, 7820, 670 https://tests.stockfishchess.org/tests/view/5fddec983932f79192d393a4 closes https://github.com/official-stockfish/Stockfish/pull/3268 Bench: 4338972 --- src/evaluate.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index dd5d23b253d..c945cf53345 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -791,11 +791,13 @@ namespace { unsafeSquares &= attackedBy[Them][ALL_PIECES] | pos.pieces(Them); // If there are no enemy pieces or attacks on passed pawn span, assign a big bonus. + // Or if there is some, but they are all attacked by our pawns, assign a bit smaller bonus. // Otherwise assign a smaller bonus if the path to queen is not attacked // and even smaller bonus if it is attacked but block square is not. - int k = !unsafeSquares ? 35 : - !(unsafeSquares & squaresToQueen) ? 20 : - !(unsafeSquares & blockSq) ? 9 : + int k = !unsafeSquares ? 36 : + !(unsafeSquares & ~attackedBy[Us][PAWN]) ? 30 : + !(unsafeSquares & squaresToQueen) ? 17 : + !(unsafeSquares & blockSq) ? 7 : 0 ; // Assign a larger bonus if the block square is defended From b06ef36ae5f23fa2d4188c9fe6d95c4f551ab035 Mon Sep 17 00:00:00 2001 From: Moez Jellouli <37274752+MJZ1977@users.noreply.github.com> Date: Sun, 20 Dec 2020 22:28:23 +0100 Subject: [PATCH 0467/1766] Correct Outflanking calculations in classical eval Take signed value of rank difference between kings squares instead absolute value in outflanking calculation. This change correct evaluation of endgames with one king invading opponent last ranks. Passed STC: LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 122240 W: 24326 L: 23896 D: 74018 Ptnml(0-2): 2101, 14139, 28236, 14517, 2127 https://tests.stockfishchess.org/tests/view/5fdfc33a3932f79192d394b8 Passed LTC: LLR: 2.97 (-2.94,2.94) {0.25,1.25} Total: 157416 W: 20870 L: 20292 D: 116254 Ptnml(0-2): 973, 13954, 48333, 14418, 1030 https://tests.stockfishchess.org/tests/view/5fe07a453932f79192d39502 closes https://github.com/official-stockfish/Stockfish/pull/3271 Bench: 4162769 --- src/evaluate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index c945cf53345..7671f6052df 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -867,7 +867,7 @@ namespace { Value Evaluation::winnable(Score score) const { int outflanking = distance(pos.square(WHITE), pos.square(BLACK)) - - distance(pos.square(WHITE), pos.square(BLACK)); + + int(rank_of(pos.square(WHITE)) - rank_of(pos.square(BLACK))); bool pawnsOnBothFlanks = (pos.pieces(PAWN) & QueenSide) && (pos.pieces(PAWN) & KingSide); From 51deae899814bbbfd9db5686b824f23105ca8a39 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Fri, 25 Dec 2020 01:11:09 +0300 Subject: [PATCH 0468/1766] Do more LMR for captures This patch enables LMR for all captures at allNodes that were not in PV. Currently we do LMR for all captures at cutNodes so this is an expansion of this logic: now we do LMR for all captures almost at all non-pv nodes, excluding only allNodes that were in PV. passed STC https://tests.stockfishchess.org/tests/view/5fe50b9d3932f79192d3973c LLR: 2.95 (-2.94,2.94) {-0.25,1.25} Total: 83128 W: 7606 L: 7368 D: 68154 Ptnml(0-2): 292, 5905, 28939, 6129, 299 passed LTC https://tests.stockfishchess.org/tests/view/5fe552e43932f79192d39744 LLR: 2.92 (-2.94,2.94) {0.25,1.25} Total: 13968 W: 568 L: 466 D: 12934 Ptnml(0-2): 5, 418, 6043, 506, 12 closes https://github.com/official-stockfish/Stockfish/pull/3273 Bench: 4194835 --- src/search.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/search.cpp b/src/search.cpp index e272c10b740..60a002a9088 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1172,6 +1172,7 @@ namespace { || moveCountPruning || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha || cutNode + || (!PvNode && !formerPv) || thisThread->ttHitAverage < 432 * TtHitAverageResolution * TtHitAverageWindow / 1024)) { Depth r = reduction(improving, depth, moveCount); From 4262461457d2227fa788ee9b238651d7ae498339 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Thu, 31 Dec 2020 01:45:37 +0100 Subject: [PATCH 0469/1766] Tweak capture LMR. Apply the recently added LMR condition for captures at nodes which are not PV or former PV nodes only if capture history is not too good. STC: LLR: 2.96 (-2.94,2.94) {-0.25,1.25} Total: 95296 W: 8917 L: 8660 D: 77719 Ptnml(0-2): 323, 6871, 33045, 7044, 365 https://tests.stockfishchess.org/tests/view/5feca7f46019e097de3ee9ae LTC: LLR: 2.95 (-2.94,2.94) {0.25,1.25} Total: 29216 W: 1172 L: 1034 D: 27010 Ptnml(0-2): 11, 946, 12568, 1060, 23 https://tests.stockfishchess.org/tests/view/5fecf1786019e097de3ee9d5 closes https://github.com/official-stockfish/Stockfish/pull/3283 Bench: 4006138 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 60a002a9088..c6db82ba180 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1172,7 +1172,7 @@ namespace { || moveCountPruning || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha || cutNode - || (!PvNode && !formerPv) + || (!PvNode && !formerPv && thisThread->captureHistory[movedPiece][to_sq(move)][type_of(pos.captured_piece())] < 4506) || thisThread->ttHitAverage < 432 * TtHitAverageResolution * TtHitAverageWindow / 1024)) { Depth r = reduction(improving, depth, moveCount); From c57c71bf5c56665b0339fa983665bdef4bf8a099 Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Wed, 30 Dec 2020 15:38:25 +0100 Subject: [PATCH 0470/1766] Assorted parameter tweak Parameter tweak from various tunes and patches. STC https://tests.stockfishchess.org/tests/view/5fec2ae36019e097de3ee94a LLR: 2.97 (-2.94,2.94) {-0.25,1.25} Total: 41976 W: 4032 L: 3848 D: 34096 Ptnml(0-2): 147, 3086, 14341, 3264, 150 LTC https://tests.stockfishchess.org/tests/view/5fec5c3c6019e097de3ee973 LLR: 2.94 (-2.94,2.94) {0.25,1.25} Total: 23936 W: 970 L: 844 D: 22122 Ptnml(0-2): 14, 749, 10319, 869, 17 closes https://github.com/official-stockfish/Stockfish/pull/3281 bench: 4354546 --- src/movepick.h | 2 +- src/search.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/movepick.h b/src/movepick.h index 4c0ad55172d..f8472c6e67f 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -84,7 +84,7 @@ enum StatsType { NoCaptures, Captures }; /// unsuccessful during the current search, and is used for reduction and move /// ordering decisions. It uses 2 tables (one for each color) indexed by /// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards -typedef Stats ButterflyHistory; +typedef Stats ButterflyHistory; /// At higher depths LowPlyHistory records successful quiet moves near the root /// and quiet moves which are/were in the PV (ttPv). It is cleared with each new diff --git a/src/search.cpp b/src/search.cpp index c6db82ba180..5f545ef62fc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -82,7 +82,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return d > 13 ? 29 : 17 * d * d + 134 * d - 134; + return d > 14 ? 29 : 8 * d * d + 224 * d - 215; } // Add a small random component to draw evaluations to avoid 3fold-blindness @@ -838,7 +838,7 @@ namespace { // Step 8. Futility pruning: child node (~50 Elo) if ( !PvNode - && depth < 8 + && depth < 9 && eval - futility_margin(depth, improving) >= beta && eval < VALUE_KNOWN_WIN) // Do not return unproven wins return eval; @@ -893,7 +893,7 @@ namespace { } } - probCutBeta = beta + 183 - 49 * improving; + probCutBeta = beta + 194 - 49 * improving; // Step 10. ProbCut (~10 Elo) // If we have a good enough capture and a reduced search returns a value @@ -1058,11 +1058,11 @@ namespace { // Futility pruning: parent node (~5 Elo) if ( lmrDepth < 7 && !ss->inCheck - && ss->staticEval + 266 + 170 * lmrDepth <= alpha + && ss->staticEval + 254 + 159 * lmrDepth <= alpha && (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - + (*contHist[5])[movedPiece][to_sq(move)] / 2 < 27376) + + (*contHist[5])[movedPiece][to_sq(move)] / 2 < 26394) continue; // Prune moves with negative SEE (~20 Elo) @@ -1078,7 +1078,7 @@ namespace { continue; // SEE based pruning - if (!pos.see_ge(move, Value(-213) * depth)) // (~25 Elo) + if (!pos.see_ge(move, Value(-218) * depth)) // (~25 Elo) continue; } } From 8985c210a1c40770fb81085bd908f757c486963c Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Sun, 27 Dec 2020 21:19:19 +0100 Subject: [PATCH 0471/1766] Simplify away late irreversible move extension Late irreversible move extension seems to be useless now. STC https://tests.stockfishchess.org/tests/view/5fe75c5c3932f79192d398ca LLR: 2.93 (-2.94,2.94) {-1.25,0.25} Total: 196192 W: 18111 L: 18278 D: 159803 Ptnml(0-2): 681, 14097, 68652, 14040, 626 LTC https://tests.stockfishchess.org/tests/view/5fe875e23932f79192d39952 LLR: 2.96 (-2.94,2.94) {-0.75,0.25} Total: 28080 W: 1105 L: 1053 D: 25922 Ptnml(0-2): 13, 904, 12158, 948, 17 closes https://github.com/official-stockfish/Stockfish/pull/3279 bench: 4144640 --- src/search.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5f545ef62fc..b96efad09aa 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1142,12 +1142,6 @@ namespace { && pos.non_pawn_material() <= 2 * RookValueMg) extension = 1; - // Late irreversible move extension - if ( move == ttMove - && pos.rule50_count() > 80 - && (captureOrPromotion || type_of(movedPiece) == PAWN)) - extension = 2; - // Add extension to new depth newDepth += extension; From 8ec97d161ed82b7c1d8f1f05c305e9a55d8ccff3 Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Sat, 26 Dec 2020 13:48:04 +0100 Subject: [PATCH 0472/1766] Remove razoring has become ineffective now. STC https://tests.stockfishchess.org/tests/view/5fe653403932f79192d3981a LLR: 2.95 (-2.94,2.94) {-1.25,0.25} Total: 63448 W: 5965 L: 5934 D: 51549 Ptnml(0-2): 230, 4738, 21769, 4745, 242 LTC https://tests.stockfishchess.org/tests/view/5fe6f0f03932f79192d39856 LLR: 2.93 (-2.94,2.94) {-0.75,0.25} Total: 65368 W: 2485 L: 2459 D: 60424 Ptnml(0-2): 33, 2186, 28230, 2192, 43 closes https://github.com/official-stockfish/Stockfish/pull/3278 bench: 4493379 --- src/search.cpp | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index b96efad09aa..e2c3f584eed 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -62,8 +62,7 @@ namespace { constexpr uint64_t TtHitAverageWindow = 4096; constexpr uint64_t TtHitAverageResolution = 1024; - // Razor and futility margins - constexpr int RazorMargin = 510; + // Futility margin Value futility_margin(Depth d, bool improving) { return Value(234 * (d - improving)); } @@ -822,12 +821,6 @@ namespace { thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus; } - // Step 7. Razoring (~1 Elo) - if ( !rootNode // The required rootNode PV handling is not available in qsearch - && depth == 1 - && eval <= alpha - RazorMargin) - return qsearch(pos, ss, alpha, beta); - // Set up improving flag that is used in various pruning heuristics // We define position as improving if static evaluation of position is better // Than the previous static evaluation at our turn @@ -836,14 +829,14 @@ namespace { ? ss->staticEval > (ss-4)->staticEval || (ss-4)->staticEval == VALUE_NONE : ss->staticEval > (ss-2)->staticEval; - // Step 8. Futility pruning: child node (~50 Elo) + // Step 7. Futility pruning: child node (~50 Elo) if ( !PvNode && depth < 9 && eval - futility_margin(depth, improving) >= beta && eval < VALUE_KNOWN_WIN) // Do not return unproven wins return eval; - // Step 9. Null move search with verification search (~40 Elo) + // Step 8. Null move search with verification search (~40 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL && (ss-1)->statScore < 22977 @@ -895,7 +888,7 @@ namespace { probCutBeta = beta + 194 - 49 * improving; - // Step 10. ProbCut (~10 Elo) + // Step 9. ProbCut (~10 Elo) // 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 @@ -968,7 +961,7 @@ namespace { ss->ttPv = ttPv; } - // Step 11. If the position is not in TT, decrease depth by 2 + // Step 10. If the position is not in TT, decrease depth by 2 if ( PvNode && depth >= 6 && !ttMove) @@ -997,7 +990,7 @@ namespace { // Mark this node as being searched ThreadHolding th(thisThread, posKey, ss->ply); - // Step 12. Loop through all pseudo-legal moves until no moves remain + // Step 11. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. while ((move = mp.next_move(moveCountPruning)) != MOVE_NONE) { @@ -1035,7 +1028,7 @@ namespace { // Calculate new depth for this move newDepth = depth - 1; - // Step 13. Pruning at shallow depth (~200 Elo) + // Step 12. Pruning at shallow depth (~200 Elo) if ( !rootNode && pos.non_pawn_material(us) && bestValue > VALUE_TB_LOSS_IN_MAX_PLY) @@ -1083,7 +1076,7 @@ namespace { } } - // Step 14. Extensions (~75 Elo) + // Step 13. Extensions (~75 Elo) // Singular extension search (~70 Elo). If all moves but one fail low on a // search of (alpha-s, beta-s), and just one fails high on (alpha, beta), @@ -1155,10 +1148,10 @@ namespace { [movedPiece] [to_sq(move)]; - // Step 15. Make the move + // Step 14. Make the move pos.do_move(move, st, givesCheck); - // Step 16. Reduced depth search (LMR, ~200 Elo). If the move fails high it will be + // Step 15. Reduced depth search (LMR, ~200 Elo). If the move fails high it will be // re-searched at full depth. if ( depth >= 3 && moveCount > 1 + 2 * rootNode @@ -1258,7 +1251,7 @@ namespace { didLMR = false; } - // Step 17. Full depth search when LMR is skipped or fails high + // Step 16. Full depth search when LMR is skipped or fails high if (doFullDepthSearch) { value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode); @@ -1285,12 +1278,12 @@ namespace { std::min(maxNextDepth, newDepth), false); } - // Step 18. Undo move + // Step 17. Undo move pos.undo_move(move); assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); - // Step 19. Check for a new best move + // Step 18. Check for a new best move // Finished searching the move. If a stop occurred, the return value of // the search cannot be trusted, and we return immediately without // updating best move, PV and TT. @@ -1367,7 +1360,7 @@ namespace { return VALUE_DRAW; */ - // Step 20. Check for mate and stalemate + // Step 19. Check for mate and stalemate // All legal moves have been searched and if there are no legal moves, it // must be a mate or a stalemate. If we are in a singular extension search then // return a fail low score. From d21e421ad74cff3b157d156d6ea8fdee3634e75b Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Fri, 25 Dec 2020 16:19:04 +0200 Subject: [PATCH 0473/1766] WeakUnopposed penalty for backwards on file A or H Do not give the WeakUnopposed penalty for backwards on file A or H The original idea comes from Lolligerhans, and a series of tunings and tests done by Fauzi. Passed STC: LLR: 2.96 (-2.94,2.94) {-0.25,1.25} Total: 140864 W: 28127 L: 27660 D: 85077 Ptnml(0-2): 2529, 16660, 31735, 16831, 2677 https://tests.stockfishchess.org/tests/view/5fe39dec3932f79192d39673 Passed LTC: LLR: 2.95 (-2.94,2.94) {0.25,1.25} Total: 67568 W: 8993 L: 8590 D: 49985 Ptnml(0-2): 523, 6176, 19983, 6579, 523 https://tests.stockfishchess.org/tests/view/5fe3dd1b3932f79192d39693 closes https://github.com/official-stockfish/Stockfish/pull/3275 Bench: 4109336 --- src/pawns.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index ed83fde72e5..d3d2ea0f744 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -30,11 +30,11 @@ namespace { #define S(mg, eg) make_score(mg, eg) // Pawn penalties - constexpr Score Backward = S( 8, 25); - constexpr Score Doubled = S(10, 55); - constexpr Score Isolated = S( 3, 15); - constexpr Score WeakLever = S( 3, 55); - constexpr Score WeakUnopposed = S(13, 25); + constexpr Score Backward = S( 6, 23); + constexpr Score Doubled = S(13, 53); + constexpr Score Isolated = S( 2, 15); + constexpr Score WeakLever = S( 5, 57); + constexpr Score WeakUnopposed = S(16, 22); // Bonus for blocked pawns at 5th or 6th rank constexpr Score BlockedPawn[2] = { S(-15, -3), S(-6, 3) }; @@ -69,8 +69,8 @@ namespace { // KingOnFile[semi-open Us][semi-open Them] contains bonuses/penalties // for king when the king is on a semi-open or open file. - constexpr Score KingOnFile[2][2] = {{ S(-19,12), S(-6, 7) }, - { S( 0, 2), S( 6,-5) }}; + constexpr Score KingOnFile[2][2] = {{ S(-21,10), S(-7, 1) }, + { S( 0,-3), S( 9,-4) }}; #undef S #undef V @@ -172,7 +172,7 @@ namespace { else if (backward) score -= Backward - + WeakUnopposed * !opposed; + + WeakUnopposed * !opposed * bool(~(FileABB | FileHBB) & s); if (!support) score -= Doubled * doubled From 23c385ec36f9d5a9514ec5b0811ec99d08b45e90 Mon Sep 17 00:00:00 2001 From: MaximMolchanov Date: Wed, 6 Jan 2021 05:29:32 +0200 Subject: [PATCH 0474/1766] Affine transform refactoring. Reordered weights in such a way that accumulated sum fits to output. Weights are grouped in blocks of four elements because four int8 (weight type) corresponds to one int32 (output type). No horizontal additions. Grouped AVX512, AVX2 and SSSE3 implementations. Repeated code was removed. An earlier version passed STC: LLR: 2.97 (-2.94,2.94) {-0.25,1.25} Total: 15336 W: 1495 L: 1355 D: 12486 Ptnml(0-2): 44, 1054, 5350, 1158, 62 https://tests.stockfishchess.org/tests/view/5ff60e106019e097de3eefd5 Speedup depends on the architecture, up to 4% measured on a NNUE only bench. closes https://github.com/official-stockfish/Stockfish/pull/3287 No functional change --- src/nnue/layers/affine_transform.h | 633 +++++++---------------------- 1 file changed, 137 insertions(+), 496 deletions(-) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index a715ca85090..ab2beab7168 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -41,6 +41,11 @@ namespace Eval::NNUE::Layers { static constexpr IndexType kOutputDimensions = OutputDimensions; static constexpr IndexType kPaddedInputDimensions = CeilToMultiple(kInputDimensions, kMaxSimdWidth); +#if defined (USE_AVX512) + static constexpr const IndexType kOutputSimdWidth = kSimdWidth / 2; +#elif defined (USE_SSSE3) + static constexpr const IndexType kOutputSimdWidth = kSimdWidth / 4; +#endif // Size of forward propagation buffer used in this layer static constexpr std::size_t kSelfBufferSize = @@ -65,51 +70,55 @@ namespace Eval::NNUE::Layers { for (std::size_t i = 0; i < kOutputDimensions; ++i) biases_[i] = read_little_endian(stream); for (std::size_t i = 0; i < kOutputDimensions * kPaddedInputDimensions; ++i) +#if !defined (USE_SSSE3) weights_[i] = read_little_endian(stream); +#else + weights_[ + (i / 4) % (kPaddedInputDimensions / 4) * kOutputDimensions * 4 + + i / kPaddedInputDimensions * 4 + + i % 4 + ] = read_little_endian(stream); -#if defined (USE_SSSE3) - // Determine if quadruplets of weight and input products can be summed using 16bits + // Determine if eights of weight and input products can be summed using 16bits // without saturation. We assume worst case combinations of 0 and 127 for all inputs. - if (!stream.fail()) + if (kOutputDimensions > 1 && !stream.fail()) { - auto can_saturate = [](const WeightType* w, int idx[4]) { - int pSum = 0, nSum = 0; - for (int p = 0; p < 4; ++p) - if (w[idx[p]] > 0) - pSum += w[idx[p]]; - else - nSum += w[idx[p]]; - - return pSum > 258 || nSum < -258; - }; - - for (IndexType i = 0; i < kOutputDimensions; ++i) - { - canSaturate16[i] = false; - const WeightType* w = &weights_[i * kPaddedInputDimensions]; -#if defined (USE_AVX512) - for (IndexType j = 0; j < (kPaddedInputDimensions & ~127) && !canSaturate16[i]; j += 128) - for (int k = 0; k < 64 && !canSaturate16[i]; k += 2) + canSaturate16.count = 0; +#if !defined(USE_VNNI) + for (IndexType i = 0; i < kPaddedInputDimensions; i += 16) + for (IndexType j = 0; j < kOutputDimensions; ++j) + for (int x = 0; x < 2; ++x) { - int spacing[4] = { 0, 1, 64, 65 }; - canSaturate16[i] = can_saturate(&w[j + k], spacing); - } -#elif defined (USE_AVX2) - for (IndexType j = 0; j < (kPaddedInputDimensions & ~63) && !canSaturate16[i]; j += 64) - for (int k = 0; k < 32 && !canSaturate16[i]; k += 2) - { - int spacing[4] = { 0, 1, 32, 33 }; - canSaturate16[i] = can_saturate(&w[j + k], spacing); - } -#elif defined (USE_SSSE3) - for (IndexType j = 0; j < (kPaddedInputDimensions & ~31) && !canSaturate16[i]; j += 32) - for (int k = 0; k < 16 && !canSaturate16[i]; k += 2) - { - int spacing[4] = { 0, 1, 16, 17 }; - canSaturate16[i] = can_saturate(&w[j + k], spacing); + WeightType* w = &weights_[i * kOutputDimensions + j * 4 + x * 2]; + int sum[2] = {0, 0}; + for (int k = 0; k < 8; ++k) + { + IndexType idx = k / 2 * kOutputDimensions * 4 + k % 2; + sum[w[idx] < 0] += w[idx]; + } + for (int sign : {-1, 1}) + while (sign * sum[sign == -1] > 258) + { + int maxK = 0, maxW = 0; + for (int k = 0; k < 8; ++k) + { + IndexType idx = k / 2 * kOutputDimensions * 4 + k % 2; + if (maxW < sign * w[idx]) + maxK = k, maxW = sign * w[idx]; + } + + IndexType idx = maxK / 2 * kOutputDimensions * 4 + maxK % 2; + sum[sign == -1] -= w[idx]; + canSaturate16.add(j, i + maxK / 2 * 4 + maxK % 2 + x * 2, w[idx]); + w[idx] = 0; + } } + + // Non functional optimization for faster more linear access + std::sort(canSaturate16.ids, canSaturate16.ids + canSaturate16.count, + [](const typename CanSaturate::Entry& e1, const typename CanSaturate::Entry& e2) + { return e1.in == e2.in ? e1.out < e2.out : e1.in < e2.in; }); #endif - } } #endif @@ -130,104 +139,6 @@ namespace Eval::NNUE::Layers { return _mm512_reduce_add_epi32(sum) + bias; }; - // This function takes - // sum0 = [xmm0a, xmm0b, xmm0c, xmm0d] - // sum1 = [xmm1a, xmm1b, xmm1c, xmm1d] - // sum2 = [xmm2a, xmm2b, xmm2c, xmm2d] - // sum3 = [xmm3a, xmm3b, xmm3c, xmm3d] - // and returns - // ret = [ - // reduce_add_epi32(xmm0a), reduce_add_epi32(xmm1a), reduce_add_epi32(xmm2a), reduce_add_epi32(xmm3a), - // reduce_add_epi32(xmm0b), reduce_add_epi32(xmm1b), reduce_add_epi32(xmm2b), reduce_add_epi32(xmm3b), - // reduce_add_epi32(xmm0c), reduce_add_epi32(xmm1c), reduce_add_epi32(xmm2c), reduce_add_epi32(xmm3c), - // reduce_add_epi32(xmm0d), reduce_add_epi32(xmm1d), reduce_add_epi32(xmm2d), reduce_add_epi32(xmm3d) - // ] - [[maybe_unused]] auto m512_hadd128x16_interleave = []( - __m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3) -> __m512i { - - __m512i sum01a = _mm512_unpacklo_epi32(sum0, sum1); - __m512i sum01b = _mm512_unpackhi_epi32(sum0, sum1); - - __m512i sum23a = _mm512_unpacklo_epi32(sum2, sum3); - __m512i sum23b = _mm512_unpackhi_epi32(sum2, sum3); - - __m512i sum01 = _mm512_add_epi32(sum01a, sum01b); - __m512i sum23 = _mm512_add_epi32(sum23a, sum23b); - - __m512i sum0123a = _mm512_unpacklo_epi64(sum01, sum23); - __m512i sum0123b = _mm512_unpackhi_epi64(sum01, sum23); - - return _mm512_add_epi32(sum0123a, sum0123b); - }; - - [[maybe_unused]] auto m512_haddx4 = [m512_hadd128x16_interleave]( - __m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3, __m128i bias) -> __m128i { - - __m512i sum = m512_hadd128x16_interleave(sum0, sum1, sum2, sum3); - - __m256i sum256lo = _mm512_castsi512_si256(sum); - __m256i sum256hi = _mm512_extracti64x4_epi64(sum, 1); - - sum256lo = _mm256_add_epi32(sum256lo, sum256hi); - - __m128i sum128lo = _mm256_castsi256_si128(sum256lo); - __m128i sum128hi = _mm256_extracti128_si256(sum256lo, 1); - - return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias); - }; - - [[maybe_unused]] auto m512_haddx8 = [m512_hadd128x16_interleave]( - __m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3, - __m512i sum4, __m512i sum5, __m512i sum6, __m512i sum7, __m256i bias) -> __m256i { - - __m512i suma = m512_hadd128x16_interleave(sum0, sum1, sum2, sum3); - __m512i sumb = m512_hadd128x16_interleave(sum4, sum5, sum6, sum7); - - __m512i indices0 = _mm512_setr_epi64(0, 1, 8, 9, 4, 5, 12, 13); - __m512i indices1 = _mm512_setr_epi64(2, 3, 10, 11, 6, 7, 14, 15); - __m512i x = _mm512_add_epi32( - _mm512_permutex2var_epi64(suma, indices0, sumb), - _mm512_permutex2var_epi64(suma, indices1, sumb)); - - __m256i sum256lo = _mm512_castsi512_si256(x); - __m256i sum256hi = _mm512_extracti64x4_epi64(x, 1); - - return _mm256_add_epi32(_mm256_add_epi32(sum256lo, sum256hi), bias); - }; - - [[maybe_unused]] auto m512_hadd256x8 =[m512_hadd128x16_interleave]( - __m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3, __m256i bias) -> __m256i { - - __m512i sum = m512_hadd128x16_interleave(sum0, sum1, sum2, sum3); - - __m512i indices = _mm512_setr_epi32( - 0, 4, 8, 12, 2, 6, 10, 14, - 1, 5, 9, 13, 3, 7, 11, 15); - sum = _mm512_permutexvar_epi32(indices, sum); - - __m256i sum256lo = _mm512_castsi512_si256(sum); - __m256i sum256hi = _mm512_extracti64x4_epi64(sum, 1); - - return _mm256_add_epi32(_mm256_hadd_epi32(sum256lo, sum256hi), bias); - }; - - [[maybe_unused]] auto m512_hadd256x16 = [m512_hadd128x16_interleave]( - __m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3, - __m512i sum4, __m512i sum5, __m512i sum6, __m512i sum7, __m512i bias) -> __m512i { - - __m512i suma = m512_hadd128x16_interleave(sum0, sum1, sum2, sum3); - __m512i sumb = m512_hadd128x16_interleave(sum4, sum5, sum6, sum7); - - __m512i indices0 = _mm512_setr_epi64(0, 1, 8, 9, 4, 5, 12, 13); - __m512i indices1 = _mm512_setr_epi64(2, 3, 10, 11, 6, 7, 14, 15); - __m512i x = _mm512_add_epi32( - _mm512_permutex2var_epi64(suma, indices0, sumb), - _mm512_permutex2var_epi64(suma, indices1, sumb)); - - __m512i indices = _mm512_setr_epi32(0, 8, 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15); - return _mm512_add_epi32(_mm512_permutexvar_epi32(indices, x), bias); - }; - [[maybe_unused]] auto m512_add_dpbusd_epi32 = [=](__m512i& acc, __m512i a, __m512i b) { #if defined (USE_VNNI) acc = _mm512_dpbusd_epi32(acc, a, b); @@ -238,14 +149,21 @@ namespace Eval::NNUE::Layers { #endif }; - [[maybe_unused]] auto m512_add_dpbusd_epi32x2 = [=](__m512i& acc, __m512i a0, __m512i b0, __m512i a1, __m512i b1) { + [[maybe_unused]] auto m512_add_dpbusd_epi32x4 = [=](__m512i& acc, __m512i a0, __m512i b0, __m512i a1, __m512i b1, + __m512i a2, __m512i b2, __m512i a3, __m512i b3) { #if defined (USE_VNNI) acc = _mm512_dpbusd_epi32(acc, a0, b0); acc = _mm512_dpbusd_epi32(acc, a1, b1); + acc = _mm512_dpbusd_epi32(acc, a2, b2); + acc = _mm512_dpbusd_epi32(acc, a3, b3); #else __m512i product0 = _mm512_maddubs_epi16(a0, b0); __m512i product1 = _mm512_maddubs_epi16(a1, b1); - product0 = _mm512_adds_epi16(product0, product1); + __m512i product2 = _mm512_maddubs_epi16(a2, b2); + __m512i product3 = _mm512_maddubs_epi16(a3, b3); + product0 = _mm512_add_epi16(product0, product1); + product2 = _mm512_add_epi16(product2, product3); + product0 = _mm512_add_epi16(product0, product2); product0 = _mm512_madd_epi16(product0, kOnes512); acc = _mm512_add_epi32(acc, product0); #endif @@ -263,18 +181,6 @@ namespace Eval::NNUE::Layers { return _mm_cvtsi128_si32(sum128) + bias; }; - [[maybe_unused]] auto m256_haddx4 = [](__m256i sum0, __m256i sum1, __m256i sum2, __m256i sum3, __m128i bias) -> __m128i { - sum0 = _mm256_hadd_epi32(sum0, sum1); - sum2 = _mm256_hadd_epi32(sum2, sum3); - - sum0 = _mm256_hadd_epi32(sum0, sum2); - - __m128i sum128lo = _mm256_castsi256_si128(sum0); - __m128i sum128hi = _mm256_extracti128_si256(sum0, 1); - - return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias); - }; - [[maybe_unused]] auto m256_add_dpbusd_epi32 = [=](__m256i& acc, __m256i a, __m256i b) { #if defined (USE_VNNI) acc = _mm256_dpbusd_epi32(acc, a, b); @@ -285,21 +191,27 @@ namespace Eval::NNUE::Layers { #endif }; - [[maybe_unused]] auto m256_add_dpbusd_epi32x2 = [=](__m256i& acc, __m256i a0, __m256i b0, __m256i a1, __m256i b1) { + [[maybe_unused]] auto m256_add_dpbusd_epi32x4 = [=](__m256i& acc, __m256i a0, __m256i b0, __m256i a1, __m256i b1, + __m256i a2, __m256i b2, __m256i a3, __m256i b3) { #if defined (USE_VNNI) acc = _mm256_dpbusd_epi32(acc, a0, b0); acc = _mm256_dpbusd_epi32(acc, a1, b1); + acc = _mm256_dpbusd_epi32(acc, a2, b2); + acc = _mm256_dpbusd_epi32(acc, a3, b3); #else __m256i product0 = _mm256_maddubs_epi16(a0, b0); __m256i product1 = _mm256_maddubs_epi16(a1, b1); - product0 = _mm256_adds_epi16(product0, product1); + __m256i product2 = _mm256_maddubs_epi16(a2, b2); + __m256i product3 = _mm256_maddubs_epi16(a3, b3); + product0 = _mm256_add_epi16(product0, product1); + product2 = _mm256_add_epi16(product2, product3); + product0 = _mm256_add_epi16(product0, product2); product0 = _mm256_madd_epi16(product0, kOnes256); acc = _mm256_add_epi32(acc, product0); #endif }; #endif - #if defined (USE_SSSE3) [[maybe_unused]] const __m128i kOnes128 = _mm_set1_epi16(1); @@ -310,25 +222,21 @@ namespace Eval::NNUE::Layers { return _mm_cvtsi128_si32(sum) + bias; }; - [[maybe_unused]] auto m128_haddx4 = [](__m128i sum0, __m128i sum1, __m128i sum2, __m128i sum3, __m128i bias) -> __m128i { - sum0 = _mm_hadd_epi32(sum0, sum1); - sum2 = _mm_hadd_epi32(sum2, sum3); - - sum0 = _mm_hadd_epi32(sum0, sum2); - - return _mm_add_epi32(sum0, bias); - }; - [[maybe_unused]] auto m128_add_dpbusd_epi32 = [=](__m128i& acc, __m128i a, __m128i b) { __m128i product0 = _mm_maddubs_epi16(a, b); product0 = _mm_madd_epi16(product0, kOnes128); acc = _mm_add_epi32(acc, product0); }; - [[maybe_unused]] auto m128_add_dpbusd_epi32x2 = [=](__m128i& acc, __m128i a0, __m128i b0, __m128i a1, __m128i b1) { + [[maybe_unused]] auto m128_add_dpbusd_epi32x4 = [=](__m128i& acc, __m128i a0, __m128i b0, __m128i a1, __m128i b1, + __m128i a2, __m128i b2, __m128i a3, __m128i b3) { __m128i product0 = _mm_maddubs_epi16(a0, b0); __m128i product1 = _mm_maddubs_epi16(a1, b1); + __m128i product2 = _mm_maddubs_epi16(a2, b2); + __m128i product3 = _mm_maddubs_epi16(a3, b3); product0 = _mm_adds_epi16(product0, product1); + product2 = _mm_adds_epi16(product2, product3); + product0 = _mm_adds_epi16(product0, product2); product0 = _mm_madd_epi16(product0, kOnes128); acc = _mm_add_epi32(acc, product0); }; @@ -336,353 +244,77 @@ namespace Eval::NNUE::Layers { #endif #if defined (USE_AVX512) + using vec_t = __m512i; + #define vec_setzero _mm512_setzero_si512 + #define vec_set_32 _mm512_set1_epi32 + auto& vec_add_dpbusd_32 = m512_add_dpbusd_epi32; + auto& vec_add_dpbusd_32x4 = m512_add_dpbusd_epi32x4; + auto& vec_hadd = m512_hadd; +#elif defined (USE_AVX2) + using vec_t = __m256i; + #define vec_setzero _mm256_setzero_si256 + #define vec_set_32 _mm256_set1_epi32 + auto& vec_add_dpbusd_32 = m256_add_dpbusd_epi32; + auto& vec_add_dpbusd_32x4 = m256_add_dpbusd_epi32x4; + auto& vec_hadd = m256_hadd; +#elif defined (USE_SSSE3) + using vec_t = __m128i; + #define vec_setzero _mm_setzero_si128 + #define vec_set_32 _mm_set1_epi32 + auto& vec_add_dpbusd_32 = m128_add_dpbusd_epi32; + auto& vec_add_dpbusd_32x4 = m128_add_dpbusd_epi32x4; + auto& vec_hadd = m128_hadd; +#endif - constexpr IndexType kNumChunks512 = kPaddedInputDimensions / (kSimdWidth * 2); - constexpr IndexType kNumChunks256 = kPaddedInputDimensions / kSimdWidth; +#if defined (USE_SSSE3) const auto output = reinterpret_cast(buffer); + const auto input_vector = reinterpret_cast(input); - // Since to saturate a zmm register it takes 64 bytes we - // cannot use AVX512 for the smaller affine transforms. - // Instead we fallback to a AVX2 implementation if the - // kInputDimensions isn't a multiple of 64. - // Note that this means that for example for - // kInputDimensions of 96 we fallback to AVX2 even though - // the first 64 elements could be processed with AVX512. - // This is caused by mixing the __m256 and __m512 variables - // required to better handle that case and it would - // require handling more cases statically not to lose performance. - // This should be revisited if such input dimensions are to be considered. - [[maybe_unused]] const auto input_vector512 = reinterpret_cast(input); - [[maybe_unused]] const auto input_vector256 = reinterpret_cast(input); + static_assert(kOutputDimensions % kOutputSimdWidth == 0 || kOutputDimensions == 1); // kOutputDimensions is either 1 or a multiple of kSimdWidth // because then it is also an input dimension. - if constexpr (kOutputDimensions % 16 == 0 && kNumChunks256 == 1) - { - for (IndexType i = 0; i < kOutputDimensions; i += 16) - { - const IndexType offset01a = (i + 0) * kPaddedInputDimensions; - const IndexType offset23a = (i + 2) * kPaddedInputDimensions; - const IndexType offset45a = (i + 4) * kPaddedInputDimensions; - const IndexType offset67a = (i + 6) * kPaddedInputDimensions; - const IndexType offset01b = (i + 8) * kPaddedInputDimensions; - const IndexType offset23b = (i + 10) * kPaddedInputDimensions; - const IndexType offset45b = (i + 12) * kPaddedInputDimensions; - const IndexType offset67b = (i + 14) * kPaddedInputDimensions; - - const __m512i bias = *reinterpret_cast(&biases_[i]); - __m512i* outptr = reinterpret_cast<__m512i*>(&output[i]); - - __m512i sum01a = _mm512_setzero_si512(); - __m512i sum23a = _mm512_setzero_si512(); - __m512i sum45a = _mm512_setzero_si512(); - __m512i sum67a = _mm512_setzero_si512(); - __m512i sum01b = _mm512_setzero_si512(); - __m512i sum23b = _mm512_setzero_si512(); - __m512i sum45b = _mm512_setzero_si512(); - __m512i sum67b = _mm512_setzero_si512(); - - const auto row01a = *reinterpret_cast(&weights_[offset01a]); - const auto row23a = *reinterpret_cast(&weights_[offset23a]); - const auto row45a = *reinterpret_cast(&weights_[offset45a]); - const auto row67a = *reinterpret_cast(&weights_[offset67a]); - const auto row01b = *reinterpret_cast(&weights_[offset01b]); - const auto row23b = *reinterpret_cast(&weights_[offset23b]); - const auto row45b = *reinterpret_cast(&weights_[offset45b]); - const auto row67b = *reinterpret_cast(&weights_[offset67b]); - - const __m256i in256 = input_vector256[0]; - const __m512i in = _mm512_inserti64x4(_mm512_castsi256_si512(in256), in256, 1); - - m512_add_dpbusd_epi32(sum01a, in, row01a); - m512_add_dpbusd_epi32(sum23a, in, row23a); - m512_add_dpbusd_epi32(sum45a, in, row45a); - m512_add_dpbusd_epi32(sum67a, in, row67a); - m512_add_dpbusd_epi32(sum01b, in, row01b); - m512_add_dpbusd_epi32(sum23b, in, row23b); - m512_add_dpbusd_epi32(sum45b, in, row45b); - m512_add_dpbusd_epi32(sum67b, in, row67b); - - *outptr = m512_hadd256x16( - sum01a, sum23a, sum45a, sum67a, - sum01b, sum23b, sum45b, sum67b, bias); - } - } - else if constexpr (kOutputDimensions % 4 == 0) - { - for (IndexType i = 0; i < kOutputDimensions; i += 4) - { - const IndexType offset0 = (i + 0) * kPaddedInputDimensions; - const IndexType offset1 = (i + 1) * kPaddedInputDimensions; - const IndexType offset2 = (i + 2) * kPaddedInputDimensions; - const IndexType offset3 = (i + 3) * kPaddedInputDimensions; - - const __m128i bias = *reinterpret_cast(&biases_[i]); - __m128i* outptr = reinterpret_cast<__m128i*>(&output[i]); - - if constexpr (kPaddedInputDimensions % (kSimdWidth * 2) == 0) - { - __m512i sum0 = _mm512_setzero_si512(); - __m512i sum1 = _mm512_setzero_si512(); - __m512i sum2 = _mm512_setzero_si512(); - __m512i sum3 = _mm512_setzero_si512(); - - const auto row0 = reinterpret_cast(&weights_[offset0]); - const auto row1 = reinterpret_cast(&weights_[offset1]); - const auto row2 = reinterpret_cast(&weights_[offset2]); - const auto row3 = reinterpret_cast(&weights_[offset3]); - - int j = 0; - if (!canSaturate16x4[i / 4]) - { - for (; j < (int)kNumChunks512 - 1; j += 2) - { - const __m512i in0 = input_vector512[j]; - const __m512i in1 = input_vector512[j + 1]; - - m512_add_dpbusd_epi32x2(sum0, in0, row0[j], in1, row0[j + 1]); - m512_add_dpbusd_epi32x2(sum1, in0, row1[j], in1, row1[j + 1]); - m512_add_dpbusd_epi32x2(sum2, in0, row2[j], in1, row2[j + 1]); - m512_add_dpbusd_epi32x2(sum3, in0, row3[j], in1, row3[j + 1]); - } - } - for (; j < (int)kNumChunks512; ++j) - { - const __m512i in = input_vector512[j]; - - m512_add_dpbusd_epi32(sum0, in, row0[j]); - m512_add_dpbusd_epi32(sum1, in, row1[j]); - m512_add_dpbusd_epi32(sum2, in, row2[j]); - m512_add_dpbusd_epi32(sum3, in, row3[j]); - } - - *outptr = m512_haddx4(sum0, sum1, sum2, sum3, bias); - } - else - { - __m256i sum0 = _mm256_setzero_si256(); - __m256i sum1 = _mm256_setzero_si256(); - __m256i sum2 = _mm256_setzero_si256(); - __m256i sum3 = _mm256_setzero_si256(); - - const auto row0 = reinterpret_cast(&weights_[offset0]); - const auto row1 = reinterpret_cast(&weights_[offset1]); - const auto row2 = reinterpret_cast(&weights_[offset2]); - const auto row3 = reinterpret_cast(&weights_[offset3]); - - for (IndexType j = 0; j < kNumChunks256; ++j) - { - const __m256i in = input_vector256[j]; - - m256_add_dpbusd_epi32(sum0, in, row0[j]); - m256_add_dpbusd_epi32(sum1, in, row1[j]); - m256_add_dpbusd_epi32(sum2, in, row2[j]); - m256_add_dpbusd_epi32(sum3, in, row3[j]); - } - - *outptr = m256_haddx4(sum0, sum1, sum2, sum3, bias); - } - } - } - else if constexpr (kOutputDimensions == 1) + if constexpr (kOutputDimensions % kOutputSimdWidth == 0) { - if constexpr (kPaddedInputDimensions % (kSimdWidth * 2) == 0) - { - __m512i sum0 = _mm512_setzero_si512(); + constexpr IndexType kNumChunks = kPaddedInputDimensions / 4; - const auto row0 = reinterpret_cast(&weights_[0]); + const auto input32 = reinterpret_cast(input); + vec_t* outptr = reinterpret_cast(output); + std::memcpy(output, biases_, kOutputDimensions * sizeof(OutputType)); - for (IndexType j = 0; j < kNumChunks512; ++j) + for (int i = 0; i < (int)kNumChunks - 3; i += 4) { - const __m512i in = input_vector512[j]; - - m512_add_dpbusd_epi32(sum0, in, row0[j]); + const vec_t in0 = vec_set_32(input32[i + 0]); + const vec_t in1 = vec_set_32(input32[i + 1]); + const vec_t in2 = vec_set_32(input32[i + 2]); + const vec_t in3 = vec_set_32(input32[i + 3]); + const auto col0 = reinterpret_cast(&weights_[(i + 0) * kOutputDimensions * 4]); + const auto col1 = reinterpret_cast(&weights_[(i + 1) * kOutputDimensions * 4]); + const auto col2 = reinterpret_cast(&weights_[(i + 2) * kOutputDimensions * 4]); + const auto col3 = reinterpret_cast(&weights_[(i + 3) * kOutputDimensions * 4]); + for (int j = 0; j * kOutputSimdWidth < kOutputDimensions; ++j) + vec_add_dpbusd_32x4(outptr[j], in0, col0[j], in1, col1[j], in2, col2[j], in3, col3[j]); } - - output[0] = m512_hadd(sum0, biases_[0]); - } - else - { - __m256i sum0 = _mm256_setzero_si256(); - - const auto row0 = reinterpret_cast(&weights_[0]); - - for (IndexType j = 0; j < kNumChunks256; ++j) - { - const __m256i in = input_vector256[j]; - - m256_add_dpbusd_epi32(sum0, in, row0[j]); - } - - output[0] = m256_hadd(sum0, biases_[0]); - } - } - else - { - // This case can never happen because kOutputDimensions - // is always 1 or a multiple of kSimdWidth. - assert(false); - } - -#elif defined (USE_AVX2) - - constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; - - const auto output = reinterpret_cast(buffer); - const auto input_vector = reinterpret_cast(input); - - // kOutputDimensions is either 1 or a multiple of kSimdWidth - // because then it is also an input dimension. - if constexpr (kOutputDimensions % 4 == 0) - { - for (IndexType i = 0; i < kOutputDimensions; i += 4) - { - const IndexType offset0 = (i + 0) * kPaddedInputDimensions; - const IndexType offset1 = (i + 1) * kPaddedInputDimensions; - const IndexType offset2 = (i + 2) * kPaddedInputDimensions; - const IndexType offset3 = (i + 3) * kPaddedInputDimensions; - - const __m128i bias = *reinterpret_cast(&biases_[i]); - __m128i* outptr = reinterpret_cast<__m128i*>(&output[i]); - - __m256i sum0 = _mm256_setzero_si256(); - __m256i sum1 = _mm256_setzero_si256(); - __m256i sum2 = _mm256_setzero_si256(); - __m256i sum3 = _mm256_setzero_si256(); - - const auto row0 = reinterpret_cast(&weights_[offset0]); - const auto row1 = reinterpret_cast(&weights_[offset1]); - const auto row2 = reinterpret_cast(&weights_[offset2]); - const auto row3 = reinterpret_cast(&weights_[offset3]); - - int j = 0; - if (!canSaturate16x4[i / 4]) - { - for (; j < (int)kNumChunks - 1; j += 2) - { - const __m256i in0 = input_vector[j]; - const __m256i in1 = input_vector[j + 1]; - - m256_add_dpbusd_epi32x2(sum0, in0, row0[j], in1, row0[j + 1]); - m256_add_dpbusd_epi32x2(sum1, in0, row1[j], in1, row1[j + 1]); - m256_add_dpbusd_epi32x2(sum2, in0, row2[j], in1, row2[j + 1]); - m256_add_dpbusd_epi32x2(sum3, in0, row3[j], in1, row3[j + 1]); - } - } - for (; j < (int)kNumChunks; ++j) - { - const __m256i in = input_vector[j]; - - m256_add_dpbusd_epi32(sum0, in, row0[j]); - m256_add_dpbusd_epi32(sum1, in, row1[j]); - m256_add_dpbusd_epi32(sum2, in, row2[j]); - m256_add_dpbusd_epi32(sum3, in, row3[j]); - } - - *outptr = m256_haddx4(sum0, sum1, sum2, sum3, bias); - } + for (int i = 0; i < canSaturate16.count; ++i) + output[canSaturate16.ids[i].out] += input[canSaturate16.ids[i].in] * canSaturate16.ids[i].w; } else if constexpr (kOutputDimensions == 1) { - __m256i sum0 = _mm256_setzero_si256(); - - const auto row0 = reinterpret_cast(&weights_[0]); - - for (IndexType j = 0; j < kNumChunks; ++j) - { - const __m256i in = input_vector[j]; - - m256_add_dpbusd_epi32(sum0, in, row0[j]); - } - - output[0] = m256_hadd(sum0, biases_[0]); - } - else - { - // This case can never happen because kOutputDimensions - // is always 1 or a multiple of kSimdWidth. - assert(false); - } + constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; -#elif defined (USE_SSSE3) + vec_t sum0 = vec_setzero(); - constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; + const auto row0 = reinterpret_cast(&weights_[0]); - auto output = reinterpret_cast(buffer); - const auto input_vector = reinterpret_cast(input); - - // kOutputDimensions is either 1 or a multiple of kSimdWidth - // because then it is also an input dimension. - if constexpr (kOutputDimensions % 4 == 0) - { - for (IndexType i = 0; i < kOutputDimensions; i += 4) - { - const IndexType offset0 = (i + 0) * kPaddedInputDimensions; - const IndexType offset1 = (i + 1) * kPaddedInputDimensions; - const IndexType offset2 = (i + 2) * kPaddedInputDimensions; - const IndexType offset3 = (i + 3) * kPaddedInputDimensions; - - const __m128i bias = *reinterpret_cast(&biases_[i]); - __m128i* outptr = reinterpret_cast<__m128i*>(&output[i]); - - __m128i sum0 = _mm_setzero_si128(); - __m128i sum1 = _mm_setzero_si128(); - __m128i sum2 = _mm_setzero_si128(); - __m128i sum3 = _mm_setzero_si128(); - - const auto row0 = reinterpret_cast(&weights_[offset0]); - const auto row1 = reinterpret_cast(&weights_[offset1]); - const auto row2 = reinterpret_cast(&weights_[offset2]); - const auto row3 = reinterpret_cast(&weights_[offset3]); - - int j = 0; - if (!canSaturate16x4[i / 4]) - { - for (; j < (int)kNumChunks - 1; j += 2) - { - const __m128i in0 = input_vector[j]; - const __m128i in1 = input_vector[j + 1]; - - m128_add_dpbusd_epi32x2(sum0, in0, row0[j], in1, row0[j + 1]); - m128_add_dpbusd_epi32x2(sum1, in0, row1[j], in1, row1[j + 1]); - m128_add_dpbusd_epi32x2(sum2, in0, row2[j], in1, row2[j + 1]); - m128_add_dpbusd_epi32x2(sum3, in0, row3[j], in1, row3[j + 1]); - } - } - for (; j < (int)kNumChunks; ++j) + for (int j = 0; j < (int)kNumChunks; ++j) { - const __m128i in = input_vector[j]; + const vec_t in = input_vector[j]; - m128_add_dpbusd_epi32(sum0, in, row0[j]); - m128_add_dpbusd_epi32(sum1, in, row1[j]); - m128_add_dpbusd_epi32(sum2, in, row2[j]); - m128_add_dpbusd_epi32(sum3, in, row3[j]); + vec_add_dpbusd_32(sum0, in, row0[j]); } - *outptr = m128_haddx4(sum0, sum1, sum2, sum3, bias); - } - } - else if constexpr (kOutputDimensions == 1) - { - __m128i sum0 = _mm_setzero_si128(); - - const auto row0 = reinterpret_cast(&weights_[0]); - - for (int j = 0; j < (int)kNumChunks; ++j) - { - const __m128i in = input_vector[j]; - - m128_add_dpbusd_epi32(sum0, in, row0[j]); - } - - output[0] = m128_hadd(sum0, biases_[0]); - } - else - { - // This case can never happen because kOutputDimensions - // is always 1 or a multiple of kSimdWidth. - assert(false); + output[0] = vec_hadd(sum0, biases_[0]); } #else @@ -693,11 +325,7 @@ namespace Eval::NNUE::Layers { #if defined(USE_SSE2) constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; -#ifndef USE_SSSE3 const __m128i kZeros = _mm_setzero_si128(); -#else - const __m128i kOnes = _mm_set1_epi16(1); -#endif const auto input_vector = reinterpret_cast(input); #elif defined(USE_MMX) @@ -792,10 +420,23 @@ namespace Eval::NNUE::Layers { alignas(kCacheLineSize) BiasType biases_[kOutputDimensions]; alignas(kCacheLineSize) WeightType weights_[kOutputDimensions * kPaddedInputDimensions]; - union { - uint32_t canSaturate16x4[(kOutputDimensions + 3) / 4]; - bool canSaturate16[kOutputDimensions]; - }; +#if defined (USE_SSSE3) + struct CanSaturate { + int count; + struct Entry { + uint16_t out; + uint16_t in; + int8_t w; + } ids[kPaddedInputDimensions * kOutputDimensions * 3 / 4]; + + void add(int i, int j, int8_t w) { + ids[count].out = i; + ids[count].in = j; + ids[count].w = w; + ++count; + } + } canSaturate16; +#endif }; } // namespace Eval::NNUE::Layers From 2c1be0be8ec51005befcd4fc422e88b6e32cef45 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Fri, 1 Jan 2021 12:59:35 +0300 Subject: [PATCH 0475/1766] Reorder conditions in LMR and pruning Make code logic somewhat easier to follow. closes https://github.com/official-stockfish/Stockfish/pull/3285 No functional change. --- src/search.cpp | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index e2c3f584eed..9ea77b3a435 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1039,8 +1039,20 @@ namespace { // Reduced depth of the next LMR search int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), 0); - if ( !captureOrPromotion - && !givesCheck) + if ( captureOrPromotion + || givesCheck) + { + // Capture history based pruning when the move doesn't give check + if ( !givesCheck + && lmrDepth < 1 + && captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] < 0) + continue; + + // SEE based pruning + if (!pos.see_ge(move, Value(-218) * depth)) // (~25 Elo) + continue; + } + else { // Countermoves based pruning (~20 Elo) if ( lmrDepth < 4 + ((ss-1)->statScore > 0 || (ss-1)->moveCount == 1) @@ -1062,18 +1074,6 @@ namespace { if (!pos.see_ge(move, Value(-(30 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth))) continue; } - else - { - // Capture history based pruning when the move doesn't give check - if ( !givesCheck - && lmrDepth < 1 - && captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] < 0) - continue; - - // SEE based pruning - if (!pos.see_ge(move, Value(-218) * depth)) // (~25 Elo) - continue; - } } // Step 13. Extensions (~75 Elo) @@ -1192,7 +1192,14 @@ namespace { if (singularQuietLMR) r--; - if (!captureOrPromotion) + if (captureOrPromotion) + { + // Unless giving check, this capture is likely bad + if ( !givesCheck + && ss->staticEval + PieceValue[EG][pos.captured_piece()] + 210 * depth <= alpha) + r++; + } + else { // Increase reduction if ttMove is a capture (~5 Elo) if (ttCapture) @@ -1228,13 +1235,6 @@ namespace { // Decrease/increase reduction for moves with a good/bad history (~30 Elo) r -= ss->statScore / 14884; } - else - { - // Unless giving check, this capture is likely bad - if ( !givesCheck - && ss->staticEval + PieceValue[EG][pos.captured_piece()] + 210 * depth <= alpha) - r++; - } Depth d = std::clamp(newDepth - r, 1, newDepth); From c4d67d77c99b99c9ac387ab622773a320f8d5cc3 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 8 Jan 2021 17:04:23 +0100 Subject: [PATCH 0476/1766] Update copyright years No functional change --- src/benchmark.cpp | 2 +- src/bitbase.cpp | 2 +- src/bitboard.cpp | 2 +- src/bitboard.h | 2 +- src/endgame.cpp | 2 +- src/endgame.h | 2 +- src/evaluate.cpp | 2 +- src/evaluate.h | 2 +- src/main.cpp | 2 +- src/material.cpp | 2 +- src/material.h | 2 +- src/misc.cpp | 2 +- src/misc.h | 2 +- src/movegen.cpp | 2 +- src/movegen.h | 2 +- src/movepick.cpp | 2 +- src/movepick.h | 2 +- src/nnue/architectures/halfkp_256x2-32-32.h | 2 +- src/nnue/evaluate_nnue.cpp | 2 +- src/nnue/evaluate_nnue.h | 2 +- src/nnue/features/feature_set.h | 2 +- src/nnue/features/features_common.h | 2 +- src/nnue/features/half_kp.cpp | 2 +- src/nnue/features/half_kp.h | 2 +- src/nnue/features/index_list.h | 2 +- src/nnue/layers/affine_transform.h | 2 +- src/nnue/layers/clipped_relu.h | 2 +- src/nnue/layers/input_slice.h | 2 +- src/nnue/nnue_accumulator.h | 2 +- src/nnue/nnue_architecture.h | 2 +- src/nnue/nnue_common.h | 2 +- src/nnue/nnue_feature_transformer.h | 2 +- src/pawns.cpp | 2 +- src/pawns.h | 2 +- src/position.cpp | 2 +- src/position.h | 2 +- src/psqt.cpp | 2 +- src/search.cpp | 2 +- src/search.h | 2 +- src/syzygy/tbprobe.cpp | 2 +- src/syzygy/tbprobe.h | 2 +- src/thread.cpp | 2 +- src/thread.h | 2 +- src/thread_win32_osx.h | 2 +- src/timeman.cpp | 2 +- src/timeman.h | 2 +- src/tt.cpp | 2 +- src/tt.h | 2 +- src/tune.cpp | 2 +- src/tune.h | 2 +- src/types.h | 2 +- src/uci.cpp | 2 +- src/uci.h | 2 +- src/ucioption.cpp | 2 +- 54 files changed, 54 insertions(+), 54 deletions(-) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index ffb631a2855..7cb04382e12 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/bitbase.cpp b/src/bitbase.cpp index bbe8e9a7e33..b640eabb6d1 100644 --- a/src/bitbase.cpp +++ b/src/bitbase.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 80206b58af9..841aa0b6c1f 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/bitboard.h b/src/bitboard.h index 29d8f66d415..95591fc4533 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/endgame.cpp b/src/endgame.cpp index 7e005a28277..1489a36bd4f 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/endgame.h b/src/endgame.h index 1351d88ab1c..860cc8634b4 100644 --- a/src/endgame.h +++ b/src/endgame.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 7671f6052df..c137fab5ad5 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/evaluate.h b/src/evaluate.h index 7dbc35deabd..8beca2d0a2b 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/main.cpp b/src/main.cpp index e6dff918bd9..ef46d0b50e8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/material.cpp b/src/material.cpp index f77972e352c..36b6132c650 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/material.h b/src/material.h index 28da59dbf33..be26425f32b 100644 --- a/src/material.h +++ b/src/material.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/misc.cpp b/src/misc.cpp index f2bce6b04f9..48e20a396c4 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/misc.h b/src/misc.h index 682ef81641c..7b551adee6a 100644 --- a/src/misc.h +++ b/src/misc.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/movegen.cpp b/src/movegen.cpp index cc1518a078d..e017d8fe2c5 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/movegen.h b/src/movegen.h index fb616d000e1..85887a64bb4 100644 --- a/src/movegen.h +++ b/src/movegen.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/movepick.cpp b/src/movepick.cpp index f5e02385556..8bface8a087 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/movepick.h b/src/movepick.h index f8472c6e67f..9862978351d 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/architectures/halfkp_256x2-32-32.h b/src/nnue/architectures/halfkp_256x2-32-32.h index 9216bd41746..a0fe2e0ad83 100644 --- a/src/nnue/architectures/halfkp_256x2-32-32.h +++ b/src/nnue/architectures/halfkp_256x2-32-32.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 382d8ff9d06..fb4a502197a 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/evaluate_nnue.h b/src/nnue/evaluate_nnue.h index 6cacf37e385..c30d7c01fbb 100644 --- a/src/nnue/evaluate_nnue.h +++ b/src/nnue/evaluate_nnue.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/features/feature_set.h b/src/nnue/features/feature_set.h index 975824b658c..77d2220fcf8 100644 --- a/src/nnue/features/feature_set.h +++ b/src/nnue/features/feature_set.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/features/features_common.h b/src/nnue/features/features_common.h index d00a35df918..b0073b8b6ed 100644 --- a/src/nnue/features/features_common.h +++ b/src/nnue/features/features_common.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/features/half_kp.cpp b/src/nnue/features/half_kp.cpp index 29322f04089..b52a45f2523 100644 --- a/src/nnue/features/half_kp.cpp +++ b/src/nnue/features/half_kp.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/features/half_kp.h b/src/nnue/features/half_kp.h index 708fd7ea0ba..d203dc22abb 100644 --- a/src/nnue/features/half_kp.h +++ b/src/nnue/features/half_kp.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/features/index_list.h b/src/nnue/features/index_list.h index d9ad680adf7..ca3ebee5617 100644 --- a/src/nnue/features/index_list.h +++ b/src/nnue/features/index_list.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index ab2beab7168..34777ef66e7 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index 7f6d67bfe3b..3ed41ee5633 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/layers/input_slice.h b/src/nnue/layers/input_slice.h index afca14c82c8..efdf07250a0 100644 --- a/src/nnue/layers/input_slice.h +++ b/src/nnue/layers/input_slice.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index a357d83526c..6b4390f9216 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index 91cdc4bda23..ad5be0064db 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index f9ff2bc81ee..33e58745e7f 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 85bc2bc8e66..2641321e6cb 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/pawns.cpp b/src/pawns.cpp index d3d2ea0f744..de47570efaf 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/pawns.h b/src/pawns.h index 5499826e8d9..888bf9900f4 100644 --- a/src/pawns.h +++ b/src/pawns.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/position.cpp b/src/position.cpp index 07ce0a7cdbc..837847b48c7 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/position.h b/src/position.h index 02156448ea7..8509029d577 100644 --- a/src/position.h +++ b/src/position.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/psqt.cpp b/src/psqt.cpp index eb36e75e903..bf87237af32 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/search.cpp b/src/search.cpp index 9ea77b3a435..d2a641768fe 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/search.h b/src/search.h index 72d43c310dc..3bf3e9ae605 100644 --- a/src/search.h +++ b/src/search.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 4d682f1a90b..97d848a22af 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/syzygy/tbprobe.h b/src/syzygy/tbprobe.h index b998989b3de..cefd39ce8de 100644 --- a/src/syzygy/tbprobe.h +++ b/src/syzygy/tbprobe.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/thread.cpp b/src/thread.cpp index 2fbf745d072..a12c0bccc11 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/thread.h b/src/thread.h index 6a73423b2c3..585f208813a 100644 --- a/src/thread.h +++ b/src/thread.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/thread_win32_osx.h b/src/thread_win32_osx.h index 75ef5d9a328..a0e4d1994f8 100644 --- a/src/thread_win32_osx.h +++ b/src/thread_win32_osx.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/timeman.cpp b/src/timeman.cpp index da08f12d969..fc4fbaacd46 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/timeman.h b/src/timeman.h index 5ad72b32ff8..55a68de4771 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/tt.cpp b/src/tt.cpp index dea7c712c44..d1ba9ebbaba 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/tt.h b/src/tt.h index 6aa066c5650..5f9525a6fbd 100644 --- a/src/tt.h +++ b/src/tt.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/tune.cpp b/src/tune.cpp index e94f67f816f..424bdac85dc 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/tune.h b/src/tune.h index 1489fa328c0..ef4189688fb 100644 --- a/src/tune.h +++ b/src/tune.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/types.h b/src/types.h index 8506b06eab0..1832b3023bc 100644 --- a/src/types.h +++ b/src/types.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/uci.cpp b/src/uci.cpp index b63e55adc6d..b3017e91069 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/uci.h b/src/uci.h index eb0b390ba92..edcfcadece5 100644 --- a/src/uci.h +++ b/src/uci.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/ucioption.cpp b/src/ucioption.cpp index bb0b8311475..03f377b3994 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2020 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by From 4d30438400932d18c095a8b85c3e51789d5f0feb Mon Sep 17 00:00:00 2001 From: bmc4 Date: Sun, 10 Jan 2021 03:30:40 -0300 Subject: [PATCH 0477/1766] Remove Condition from Generate_Move Loop it seems it's faster to handle blockers_for_king(~Us) outside loops Passed STC: LLR: 2.96 (-2.94,2.94) {-0.25,1.25} Total: 22184 W: 2063 L: 1919 D: 18202 Ptnml(0-2): 63, 1485, 7855, 1623, 66 https://tests.stockfishchess.org/tests/view/5ffbee2f6019e097de3ef18d closes https://github.com/official-stockfish/Stockfish/pull/3299 No functional change --- AUTHORS | 1 + src/movegen.cpp | 31 ++++++++++++++----------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/AUTHORS b/AUTHORS index b31a36e9d62..d170364e016 100644 --- a/AUTHORS +++ b/AUTHORS @@ -33,6 +33,7 @@ Bill Henry (VoyagerOne) Bojun Guo (noobpwnftw, Nooby) braich Brian Sheppard (SapphireBrand, briansheppard-toast) +Bruno de Melo Costa (BM123499) Bryan Cross (crossbr) candirufish Chess13234 diff --git a/src/movegen.cpp b/src/movegen.cpp index e017d8fe2c5..855a203ec9d 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -175,25 +175,19 @@ namespace { } - template - ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) { + template + ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard piecesToMove, Bitboard target) { static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()"); - Bitboard bb = pos.pieces(Us, Pt); + Bitboard bb = piecesToMove & pos.pieces(Pt); while (bb) { Square from = pop_lsb(&bb); - if (Checks) - { - if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN) - && !(attacks_bb(from) & target & pos.check_squares(Pt))) - continue; - - if (pos.blockers_for_king(~Us) & from) - continue; - } + if (Checks && (Pt == BISHOP || Pt == ROOK || Pt == QUEEN) + && !(attacks_bb(from) & target & pos.check_squares(Pt))) + continue; Bitboard b = attacks_bb(from, pos.pieces()) & target; @@ -211,7 +205,10 @@ namespace { template ExtMove* generate_all(const Position& pos, ExtMove* moveList) { constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantations - Bitboard target; + Bitboard target, piecesToMove = pos.pieces(Us); + + if(Type == QUIET_CHECKS) + piecesToMove &= ~pos.blockers_for_king(~Us); switch (Type) { @@ -236,10 +233,10 @@ namespace { } moveList = generate_pawn_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, piecesToMove, target); + moveList = generate_moves(pos, moveList, piecesToMove, target); + moveList = generate_moves< ROOK, Checks>(pos, moveList, piecesToMove, target); + moveList = generate_moves< QUEEN, Checks>(pos, moveList, piecesToMove, target); if (Type != QUIET_CHECKS && Type != EVASIONS) { From 303713b560e356a902c1830bce205716cef54a44 Mon Sep 17 00:00:00 2001 From: MaximMolchanov Date: Mon, 11 Jan 2021 07:49:41 +0200 Subject: [PATCH 0478/1766] Affine transform robust implementation Size of the weights in the last layer is less than 512 bits. It leads to wrong data access for AVX512. There is no error because in current implementation it is guaranteed that there is an array of zeros after weights so zero multiplied by something is returned and sum is correct. It is a mistake that can lead to unexpected bugs in the future. Used AVX2 instructions for smaller input size. No measurable slowdown on avx512. closes https://github.com/official-stockfish/Stockfish/pull/3298 No functional change. --- src/nnue/layers/affine_transform.h | 44 ++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 34777ef66e7..adf152eea5b 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -301,20 +301,40 @@ namespace Eval::NNUE::Layers { } else if constexpr (kOutputDimensions == 1) { - constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; - - vec_t sum0 = vec_setzero(); - - const auto row0 = reinterpret_cast(&weights_[0]); - - for (int j = 0; j < (int)kNumChunks; ++j) +#if defined (USE_AVX512) + if constexpr (kPaddedInputDimensions % (kSimdWidth * 2) != 0) { - const vec_t in = input_vector[j]; - - vec_add_dpbusd_32(sum0, in, row0[j]); + constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; + const auto input_vector256 = reinterpret_cast(input); + + __m256i sum0 = _mm256_setzero_si256(); + const auto row0 = reinterpret_cast(&weights_[0]); + + for (int j = 0; j < (int)kNumChunks; ++j) + { + const __m256i in = input_vector256[j]; + m256_add_dpbusd_epi32(sum0, in, row0[j]); + } + output[0] = m256_hadd(sum0, biases_[0]); + } + else +#endif + { +#if defined (USE_AVX512) + constexpr IndexType kNumChunks = kPaddedInputDimensions / (kSimdWidth * 2); +#else + constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; +#endif + vec_t sum0 = vec_setzero(); + const auto row0 = reinterpret_cast(&weights_[0]); + + for (int j = 0; j < (int)kNumChunks; ++j) + { + const vec_t in = input_vector[j]; + vec_add_dpbusd_32(sum0, in, row0[j]); + } + output[0] = vec_hadd(sum0, biases_[0]); } - - output[0] = vec_hadd(sum0, biases_[0]); } #else From b1bb376c3cc9a2e197759b3e6f0d365c8aae7e72 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sun, 10 Jan 2021 20:24:37 +0300 Subject: [PATCH 0479/1766] Small code cleanup in LMR In a recent patch we added comparing capture history to a number for LMR of captures. Calling it via thisThread-> is not needed since capture history was already declared by this time - so removing makes code slightly shorter and easier to follow. closes https://github.com/official-stockfish/Stockfish/pull/3297 No functional change. --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index d2a641768fe..e405a3732d7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1159,7 +1159,7 @@ namespace { || moveCountPruning || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha || cutNode - || (!PvNode && !formerPv && thisThread->captureHistory[movedPiece][to_sq(move)][type_of(pos.captured_piece())] < 4506) + || (!PvNode && !formerPv && captureHistory[movedPiece][to_sq(move)][type_of(pos.captured_piece())] < 4506) || thisThread->ttHitAverage < 432 * TtHitAverageResolution * TtHitAverageWindow / 1024)) { Depth r = reduction(improving, depth, moveCount); From 87586b3d0c8961c2fc9330e2f8ac2f8c3fe79018 Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Sat, 9 Jan 2021 16:46:06 +0100 Subject: [PATCH 0480/1766] Use correct chess terms + fix spelling. - "discovered check" (instead of "discovery check") - "en passant" (instead of "en-passant") - "pseudo-legal" before a noun (instead of "pseudo legal") - "3-fold" (instead of "3fold") closes https://github.com/official-stockfish/Stockfish/pull/3294 No functional change. --- AUTHORS | 1 + src/movegen.cpp | 8 ++++---- src/movepick.cpp | 2 +- src/movepick.h | 4 ++-- src/position.cpp | 16 ++++++++-------- src/position.h | 6 +++--- src/search.cpp | 6 +++--- src/types.h | 4 ++-- 8 files changed, 24 insertions(+), 23 deletions(-) diff --git a/AUTHORS b/AUTHORS index d170364e016..f3ae5f09024 100644 --- a/AUTHORS +++ b/AUTHORS @@ -46,6 +46,7 @@ Dariusz Orzechowski (dorzechowski) David Zar Daylen Yang (daylen) Deshawn Mohan-Smith (GoldenRare) +Dieter Dobbelaere (ddobbelaere) DiscanX Dominik Schlösser (domschl) double-beep diff --git a/src/movegen.cpp b/src/movegen.cpp index 855a203ec9d..5ce2de7c6ba 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -85,7 +85,7 @@ namespace { // Add pawn pushes which give discovered check. This is possible only // if the pawn is not on the same file as the enemy king, because we - // don't generate captures. Note that a possible discovery check + // don't generate captures. Note that a possible discovered check // promotion has been already generated amongst the captures. Bitboard dcCandidateQuiets = pos.blockers_for_king(Them) & pawnsNotOn7; if (dcCandidateQuiets) @@ -134,7 +134,7 @@ namespace { moveList = make_promotions(moveList, pop_lsb(&b3), ksq); } - // Standard and en-passant captures + // Standard and en passant captures if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) { Bitboard b1 = shift(pawnsNotOn7) & enemies; @@ -167,7 +167,7 @@ namespace { assert(b1); while (b1) - *moveList++ = make(pop_lsb(&b1), pos.ep_square()); + *moveList++ = make(pop_lsb(&b1), pos.ep_square()); } } @@ -355,7 +355,7 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { moveList = pos.checkers() ? generate(pos, moveList) : generate(pos, moveList); while (cur != moveList) - if ( (pinned || from_sq(*cur) == ksq || type_of(*cur) == ENPASSANT) + if ( (pinned || from_sq(*cur) == ksq || type_of(*cur) == EN_PASSANT) && !pos.legal(*cur)) *cur = (--moveList)->move; else diff --git a/src/movepick.cpp b/src/movepick.cpp index 8bface8a087..9cb8447f778 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -142,7 +142,7 @@ Move MovePicker::select(Pred filter) { } /// MovePicker::next_move() is the most important method of the MovePicker class. It -/// returns a new pseudo legal move every time it is called until there are no more +/// returns a new pseudo-legal move every time it is called until there are no more /// moves left, picking the move with the highest score from a list of generated moves. Move MovePicker::next_move(bool skipQuiets) { diff --git a/src/movepick.h b/src/movepick.h index 9862978351d..5232ee4d486 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -108,9 +108,9 @@ typedef Stats PieceToHistory; typedef Stats ContinuationHistory; -/// MovePicker class is used to pick one pseudo legal move at a time from the +/// MovePicker class is used to pick one pseudo-legal move at a time from the /// current position. The most important method is next_move(), which returns a -/// new pseudo legal move each time it is called, until there are no moves left, +/// new pseudo-legal move each time it is called, until there are no moves left, /// when MOVE_NONE is returned. In order to improve the efficiency of the alpha /// beta algorithm, MovePicker attempts to return the moves which are most likely /// to get a cut-off first. diff --git a/src/position.cpp b/src/position.cpp index 837847b48c7..12b1bd9a8c5 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -505,7 +505,7 @@ bool Position::legal(Move m) const { // En passant captures are a tricky special case. Because they are rather // uncommon, we do it simply by testing whether the king is attacked after // the move is made. - if (type_of(m) == ENPASSANT) + if (type_of(m) == EN_PASSANT) { Square ksq = square(us); Square capsq = to - pawn_push(us); @@ -655,7 +655,7 @@ bool Position::gives_check(Move m) const { // of direct checks and ordinary discovered check, so the only case we // need to handle is the unusual case of a discovered check through // the captured pawn. - case ENPASSANT: + case EN_PASSANT: { Square capsq = make_square(file_of(to), rank_of(from)); Bitboard b = (pieces() ^ from ^ capsq) | to; @@ -716,7 +716,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { Square from = from_sq(m); Square to = to_sq(m); Piece pc = piece_on(from); - Piece captured = type_of(m) == ENPASSANT ? make_piece(them, PAWN) : piece_on(to); + Piece captured = type_of(m) == EN_PASSANT ? make_piece(them, PAWN) : piece_on(to); assert(color_of(pc) == us); assert(captured == NO_PIECE || color_of(captured) == (type_of(m) != CASTLING ? them : us)); @@ -742,7 +742,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // update non-pawn material. if (type_of(captured) == PAWN) { - if (type_of(m) == ENPASSANT) + if (type_of(m) == EN_PASSANT) { capsq -= pawn_push(us); @@ -769,7 +769,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // Update board and piece lists remove_piece(capsq); - if (type_of(m) == ENPASSANT) + if (type_of(m) == EN_PASSANT) board[capsq] = NO_PIECE; // Update material hash key and prefetch access to materialTable @@ -815,7 +815,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // If the moving piece is a pawn do some special extra work if (type_of(pc) == PAWN) { - // Set en-passant square if the moved pawn can be captured + // Set en passant square if the moved pawn can be captured if ( (int(to) ^ int(from)) == 16 && (pawn_attacks_bb(us, to - pawn_push(us)) & pieces(them, PAWN))) { @@ -938,7 +938,7 @@ void Position::undo_move(Move m) { { Square capsq = to; - if (type_of(m) == ENPASSANT) + if (type_of(m) == EN_PASSANT) { capsq -= pawn_push(us); @@ -1042,7 +1042,7 @@ void Position::undo_null_move() { /// Position::key_after() computes the new hash key after the given move. Needed /// for speculative prefetch. It doesn't recognize special moves like castling, -/// en-passant and promotions. +/// en passant and promotions. Key Position::key_after(Move m) const { diff --git a/src/position.h b/src/position.h index 8509029d577..93e0f91d675 100644 --- a/src/position.h +++ b/src/position.h @@ -113,7 +113,7 @@ class Position { Bitboard blockers_for_king(Color c) const; Bitboard check_squares(PieceType pt) const; Bitboard pinners(Color c) const; - bool is_discovery_check_on_king(Color c, Move m) const; + bool is_discovered_check_on_king(Color c, Move m) const; // Attacks to/from a given square Bitboard attackers_to(Square s) const; @@ -304,7 +304,7 @@ inline Bitboard Position::check_squares(PieceType pt) const { return st->checkSquares[pt]; } -inline bool Position::is_discovery_check_on_king(Color c, Move m) const { +inline bool Position::is_discovered_check_on_king(Color c, Move m) const { return st->blockersForKing[c] & from_sq(m); } @@ -371,7 +371,7 @@ inline bool Position::capture_or_promotion(Move m) const { inline bool Position::capture(Move m) const { assert(is_ok(m)); // Castling is encoded as "king captures rook" - return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == ENPASSANT; + return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == EN_PASSANT; } inline Piece Position::captured_piece() const { diff --git a/src/search.cpp b/src/search.cpp index e405a3732d7..3da3d04ea96 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -84,7 +84,7 @@ namespace { return d > 14 ? 29 : 8 * d * d + 224 * d - 215; } - // Add a small random component to draw evaluations to avoid 3fold-blindness + // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(Thread* thisThread) { return VALUE_DRAW + Value(2 * (thisThread->nodes & 1) - 1); } @@ -1127,7 +1127,7 @@ namespace { // Check extension (~2 Elo) else if ( givesCheck - && (pos.is_discovery_check_on_king(~us, move) || pos.see_ge(move))) + && (pos.is_discovered_check_on_king(~us, move) || pos.see_ge(move))) extension = 1; // Last captures extension @@ -1541,7 +1541,7 @@ namespace { && futilityBase > -VALUE_KNOWN_WIN && !pos.advanced_pawn_push(move)) { - assert(type_of(move) != ENPASSANT); // Due to !pos.advanced_pawn_push + assert(type_of(move) != EN_PASSANT); // Due to !pos.advanced_pawn_push // moveCount pruning if (moveCount > 2) diff --git a/src/types.h b/src/types.h index 1832b3023bc..d270384eb99 100644 --- a/src/types.h +++ b/src/types.h @@ -113,7 +113,7 @@ constexpr int MAX_PLY = 246; /// bit 6-11: origin square (from 0 to 63) /// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2) /// bit 14-15: special move flag: promotion (1), en passant (2), castling (3) -/// NOTE: EN-PASSANT bit is set only when a pawn can be captured +/// NOTE: en passant bit is set only when a pawn can be captured /// /// Special cases are MOVE_NONE and MOVE_NULL. We can sneak these in because in /// any normal move destination square is always different from origin square @@ -127,7 +127,7 @@ enum Move : int { enum MoveType { NORMAL, PROMOTION = 1 << 14, - ENPASSANT = 2 << 14, + EN_PASSANT = 2 << 14, CASTLING = 3 << 14 }; From 0266e702970640df693a8e572dd3cb9d227cdfc6 Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Sun, 10 Jan 2021 12:22:52 +0100 Subject: [PATCH 0481/1766] Fix static_assert. With a hard-coded true, this declaration has no effect. closes https://github.com/official-stockfish/Stockfish/pull/3295 No functional change. --- src/movegen.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index 5ce2de7c6ba..aaa1ff88bdb 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -204,6 +204,9 @@ namespace { template ExtMove* generate_all(const Position& pos, ExtMove* moveList) { + + static_assert(Type != LEGAL, "Unsupported type in generate_all()"); + constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantations Bitboard target, piecesToMove = pos.pieces(Us); @@ -228,8 +231,6 @@ namespace { case NON_EVASIONS: target = ~pos.pieces(Us); break; - default: - static_assert(true, "Unsupported type in generate_all()"); } moveList = generate_pawn_moves(pos, moveList, target); From 5f222f1d98c9b4cb07aa2303f085c2064e7ea77a Mon Sep 17 00:00:00 2001 From: BM123499 <48841672+BM123499@users.noreply.github.com> Date: Fri, 8 Jan 2021 14:03:26 -0300 Subject: [PATCH 0482/1766] Rethink En Passant Evasion Capture It now checks if it were a discovery attack instead of the attacking piece is the double-moved pawn. As a side effect, certain illegal fens have different, and slightly more logical move generation. There is no intend to maintain particular behavior for such non-reachable fens. Passed STC: LLR: 2.93 (-2.94,2.94) {-1.25,0.25} Total: 47912 W: 4327 L: 4285 D: 39300 Ptnml(0-2): 144, 3312, 17012, 3334, 154 https://tests.stockfishchess.org/tests/view/5ff890946019e097de3ef0a5 closes https://github.com/official-stockfish/Stockfish/pull/3292 closes / fixes https://github.com/official-stockfish/Stockfish/issues/3270 No functional change --- src/movegen.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index aaa1ff88bdb..fafa65ee53f 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -156,10 +156,8 @@ namespace { { assert(rank_of(pos.ep_square()) == relative_rank(Us, RANK_6)); - // An en passant capture can be an evasion only if the checking piece - // is the double pushed pawn and so is in the target. Otherwise this - // is a discovery check and we are forced to do otherwise. - if (Type == EVASIONS && !(target & (pos.ep_square() - Up))) + // An en passant capture cannot resolve a discovered check. + if (Type == EVASIONS && (target & (pos.ep_square() + Up))) return moveList; b1 = pawnsNotOn7 & pawn_attacks_bb(Them, pos.ep_square()); From 37c2b5685efa8a0c3de04604c73e19f6e82dd6e8 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sat, 9 Jan 2021 17:42:58 +0300 Subject: [PATCH 0483/1766] Refine stat based reductions This patch separates stat based reductions for quiet moves in case of being in check and in case of not being in check. We will be using sum of first continuation history and main history (similar to movepicker) instead of statScore for the first case. passed STC https://tests.stockfishchess.org/tests/view/5ff87b2f6019e097de3ef09b LLR: 2.93 (-2.94,2.94) {-0.25,1.25} Total: 63992 W: 5887 L: 5678 D: 52427 Ptnml(0-2): 201, 4561, 22305, 4686, 243 passed LTC https://tests.stockfishchess.org/tests/view/5ff8b6206019e097de3ef0b2 LLR: 2.94 (-2.94,2.94) {0.25,1.25} Total: 81216 W: 3127 L: 2880 D: 75209 Ptnml(0-2): 46, 2544, 35176, 2801, 41 closes https://github.com/official-stockfish/Stockfish/pull/3293 bench 4395984 --- src/search.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 3da3d04ea96..7abffb8779d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1233,7 +1233,13 @@ namespace { r++; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - r -= ss->statScore / 14884; + // If we are not in check use statScore, if we are in check + // use sum of main history and first continuation history with an offset + if (ss->inCheck) + r -= (thisThread->mainHistory[us][from_to(move)] + + (*contHist[0])[movedPiece][to_sq(move)] - 4333) / 16384; + else + r -= ss->statScore / 14884; } Depth d = std::clamp(newDepth - r, 1, newDepth); From ee3f7b6b6e1a1051b32cedb38ac89b3458ff4ab2 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sun, 10 Jan 2021 01:31:09 +0200 Subject: [PATCH 0484/1766] Bad Outpost Pawn Scale Changed name from Bad Outpost to Uncontested Outpost Scale Uncontested Outpost with number of pawns + Decrease Bishop PSQT values and general tuning Credits for the decrease of the Bishop PSQT values: Fauzi Credits for scaling Uncontested Outpost with number of pawns: Lolligerhans Credits for the tunings: Fauzi Passed STC: LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 32040 W: 6593 L: 6281 D: 19166 Ptnml(0-2): 596, 3713, 7095, 4015, 601 https://tests.stockfishchess.org/tests/view/5ffa43026019e097de3ef0f2 Passed LTC: LLR: 2.95 (-2.94,2.94) {0.25,1.25} Total: 84376 W: 11395 L: 10950 D: 62031 Ptnml(0-2): 652, 7930, 24623, 8287, 696 https://tests.stockfishchess.org/tests/view/5ffa6e7b6019e097de3ef0fd closes https://github.com/official-stockfish/Stockfish/pull/3296 Bench: 4287509 --- src/evaluate.cpp | 10 +++++----- src/psqt.cpp | 16 ++++++++-------- src/search.cpp | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index c137fab5ad5..76266937d56 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -225,7 +225,7 @@ namespace { // BishopPawns[distance from edge] contains a file-dependent penalty for pawns on // squares of the same color as our bishop. constexpr Score BishopPawns[int(FILE_NB) / 2] = { - S(3, 8), S(3, 9), S(1, 8), S(3, 7) + S(3, 8), S(3, 9), S(2, 8), S(3, 8) }; // KingProtector[knight/bishop] contains penalty for each distance unit to own king @@ -233,7 +233,7 @@ namespace { // Outpost[knight/bishop] contains bonuses for each knight or bishop occupying a // pawn protected square on rank 4 to 6 which is also safe from a pawn attack. - constexpr Score Outpost[] = { S(56, 34), S(31, 23) }; + constexpr Score Outpost[] = { S(57, 38), S(31, 24) }; // PassedRank[Rank] contains a bonus according to the rank of a passed pawn constexpr Score PassedRank[RANK_NB] = { @@ -255,7 +255,7 @@ namespace { }; // Assorted bonuses and penalties - constexpr Score BadOutpost = S( -7, 36); + constexpr Score UncontestedOutpost = S( 1, 10); constexpr Score BishopOnKingRing = S( 24, 0); constexpr Score BishopXRayPawns = S( 4, 5); constexpr Score CorneredBishop = S( 50, 50); @@ -428,7 +428,7 @@ namespace { if (Pt == BISHOP || Pt == KNIGHT) { // Bonus if the piece is on an outpost square or can reach one - // Reduced bonus for knights (BadOutpost) if few relevant targets + // Bonus for knights (UncontestedOutpost) if few relevant targets bb = OutpostRanks & (attackedBy[Us][PAWN] | shift(pos.pieces(PAWN))) & ~pe->pawn_attacks_span(Them); Bitboard targets = pos.pieces(Them) & ~pos.pieces(PAWN); @@ -437,7 +437,7 @@ namespace { && bb & s & ~CenterFiles // on a side outpost && !(b & targets) // no relevant attacks && (!more_than_one(targets & (s & QueenSide ? QueenSide : KingSide)))) - score += BadOutpost; + score += UncontestedOutpost * popcount(pos.pieces(PAWN) & (s & QueenSide ? QueenSide : KingSide)); else if (bb & s) score += Outpost[Pt == BISHOP]; else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us)) diff --git a/src/psqt.cpp b/src/psqt.cpp index bf87237af32..e2107240770 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -43,14 +43,14 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { { S(-201,-100), S(-83,-88), S(-56,-56), S(-26,-17) } }, { // Bishop - { S(-53,-57), S( -5,-30), S( -8,-37), S(-23,-12) }, - { S(-15,-37), S( 8,-13), S( 19,-17), S( 4, 1) }, - { S( -7,-16), S( 21, -1), S( -5, -2), S( 17, 10) }, - { S( -5,-20), S( 11, -6), S( 25, 0), S( 39, 17) }, - { S(-12,-17), S( 29, -1), S( 22,-14), S( 31, 15) }, - { S(-16,-30), S( 6, 6), S( 1, 4), S( 11, 6) }, - { S(-17,-31), S(-14,-20), S( 5, -1), S( 0, 1) }, - { S(-48,-46), S( 1,-42), S(-14,-37), S(-23,-24) } + { S(-37,-40), S(-4 ,-21), S( -6,-26), S(-16, -8) }, + { S(-11,-26), S( 6, -9), S( 13,-12), S( 3, 1) }, + { S(-5 ,-11), S( 15, -1), S( -4, -1), S( 12, 7) }, + { S(-4 ,-14), S( 8, -4), S( 18, 0), S( 27, 12) }, + { S(-8 ,-12), S( 20, -1), S( 15,-10), S( 22, 11) }, + { S(-11,-21), S( 4, 4), S( 1, 3), S( 8, 4) }, + { S(-12,-22), S(-10,-14), S( 4, -1), S( 0, 1) }, + { S(-34,-32), S( 1,-29), S(-10,-26), S(-16,-17) } }, { // Rook { S(-31, -9), S(-20,-13), S(-14,-10), S(-5, -9) }, diff --git a/src/search.cpp b/src/search.cpp index 7abffb8779d..6cc115fcf94 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1233,10 +1233,10 @@ namespace { r++; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - // If we are not in check use statScore, if we are in check + // If we are not in check use statScore, if we are in check // use sum of main history and first continuation history with an offset if (ss->inCheck) - r -= (thisThread->mainHistory[us][from_to(move)] + r -= (thisThread->mainHistory[us][from_to(move)] + (*contHist[0])[movedPiece][to_sq(move)] - 4333) / 16384; else r -= ss->statScore / 14884; From 6dddcecb09df268d93810a1a38deb116f97672af Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Sun, 10 Jan 2021 03:30:40 -0300 Subject: [PATCH 0485/1766] Optimize generate_moves This change simplifies control flow in the generate_moves function which ensures the compiler doesn't duplicate work due to possibly not resolving pureness of the function calls. Also the biggest change is the removal of the unnecessary condition checking for empty b in a convoluted way. The rationale for removal of this condition is that computing attacks_bb with occupancy is not much more costly than computing pseudo attacks and overall the condition (also being likely unpredictable) is a pessimisation. This is inspired by previous changes by @BM123499. Passed STC: LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 88040 W: 8172 L: 7931 D: 71937 Ptnml(0-2): 285, 6128, 30957, 6361, 289 https://tests.stockfishchess.org/tests/view/5ffc28386019e097de3ef1c7 closes https://github.com/official-stockfish/Stockfish/pull/3300 No functional change. --- src/movegen.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index fafa65ee53f..88c31cfa485 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -180,17 +180,17 @@ namespace { Bitboard bb = piecesToMove & pos.pieces(Pt); + if (!bb) + return moveList; + + [[maybe_unused]] const Bitboard checkSquares = pos.check_squares(Pt); + while (bb) { Square from = pop_lsb(&bb); - if (Checks && (Pt == BISHOP || Pt == ROOK || Pt == QUEEN) - && !(attacks_bb(from) & target & pos.check_squares(Pt))) - continue; - Bitboard b = attacks_bb(from, pos.pieces()) & target; - - if (Checks) - b &= pos.check_squares(Pt); + if constexpr (Checks) + b &= checkSquares; while (b) *moveList++ = make_move(from, pop_lsb(&b)); From 77eeea407c9d10cf6dbca6f4e07efbbe836c4fb0 Mon Sep 17 00:00:00 2001 From: Lolligerhans Date: Tue, 12 Jan 2021 14:30:25 +0100 Subject: [PATCH 0486/1766] Add penalty for doubled pawns in agile structure Give an additional penalty of S(20, 10) for any doubled pawn if none of the opponent's pawns is facing any of our - pawns or - pawn attacks; that means, each of their pawns can push at least one square without being captured. This ignores their non-pawns pieces and attacks. One possible justification: Their pawns' ability to push freely provides options to react to our threats by changing their pawn structure. Our doubled pawns however will likely lead to an exploitable weakness, even if the pawn structure is not yet fixed. Note that the notion of "their pawns not being fixed" is symmetric for both players: If all of their pawns can push freely so can ours. All pawns being freely pushable might just be an early-game-indicator. However, it can trigger during endgame pawns races, where doubled pawns are especially hindering, too. LTC LLR: 2.94 (-2.94,2.94) {0.25,1.25} Total: 134976 W: 17964 L: 17415 D: 99597 Ptnml(0-2): 998, 12702, 39619, 13091, 1078 https://tests.stockfishchess.org/tests/view/5ffdd5316019e097de3ef281 STC LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 35640 W: 7219 L: 6904 D: 21517 Ptnml(0-2): 645, 4096, 8084, 4289, 706 https://tests.stockfishchess.org/tests/view/5ffda4a16019e097de3ef265 closes https://github.com/official-stockfish/Stockfish/pull/3302 Bench: 4363873 --- src/pawns.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/pawns.cpp b/src/pawns.cpp index de47570efaf..5d6770ed76e 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -32,6 +32,7 @@ namespace { // Pawn penalties constexpr Score Backward = S( 6, 23); constexpr Score Doubled = S(13, 53); + constexpr Score DoubledEarly = S(20, 10); constexpr Score Isolated = S( 2, 15); constexpr Score WeakLever = S( 5, 57); constexpr Score WeakUnopposed = S(16, 22); @@ -86,6 +87,7 @@ namespace { constexpr Color Them = ~Us; constexpr Direction Up = pawn_push(Us); + constexpr Direction Down = -Up; Bitboard neighbours, stoppers, support, phalanx, opposed; Bitboard lever, leverPush, blocked; @@ -123,6 +125,13 @@ namespace { phalanx = neighbours & rank_bb(s); support = neighbours & rank_bb(s - Up); + if (doubled) + { + // Additional doubled penalty if none of their pawns is fixed + if (!(ourPawns & shift(theirPawns | pawn_attacks_bb(theirPawns)))) + score -= DoubledEarly; + } + // A pawn is backward when it is behind all pawns of the same color on // the adjacent files and cannot safely advance. backward = !(neighbours & forward_ranks_bb(Them, s + Up)) From 329ef2a6aa362f4d831bdde4b31da40f3547985d Mon Sep 17 00:00:00 2001 From: Krystian Kuzniarek Date: Thu, 31 Dec 2020 17:00:39 +0100 Subject: [PATCH 0487/1766] Change lock type No additional features of std::unique_lock has been previously used so it's better to use a lighter lock. closes https://github.com/official-stockfish/Stockfish/pull/3284 No functional change. --- AUTHORS | 1 + src/syzygy/tbprobe.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index f3ae5f09024..d2b26469ea5 100644 --- a/AUTHORS +++ b/AUTHORS @@ -100,6 +100,7 @@ Ken Takusagawa kinderchocolate Kiran Panditrao (Krgp) Kojirion +Krystian Kuzniarek (kuzkry) Leonardo Ljubičić (ICCF World Champion) Leonid Pechenik (lp--) Linus Arver (listx) diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 97d848a22af..36234786f12 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1141,7 +1141,7 @@ void* mapped(TBTable& e, const Position& pos) { 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::scoped_lock lk(mutex); if (e.ready.load(std::memory_order_relaxed)) // Recheck under lock return e.baseAddress; From b7f643fe391d00fd9088587e8ef0ca520fa1480f Mon Sep 17 00:00:00 2001 From: Rod Johnson Date: Mon, 4 Jan 2021 22:59:55 +1100 Subject: [PATCH 0488/1766] Add .gitignore add files produced during the build to a newly added .gitignore closes https://github.com/official-stockfish/Stockfish/pull/3286 No functional change --- .gitignore | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..8981efcaf13 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# Files from build +**/*.o +**/*.s +src/.depend + +# Built binary +src/stockfish* +src/-lstdc++.res + +# Neural network for the NNUE evaluation +**/*.nnue + From 1188141aa78d01c361582daaa73de5154b6d09b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Thu, 21 Jan 2021 18:53:29 +0100 Subject: [PATCH 0489/1766] Improve play for closed positions This patch give a small bonus to incite the attacking side to keep more pawns on the board. A consequence of this bonus is that Stockfish will tend to play positions slightly more closed on average than master, especially when it believes that it has an advantage. To lower the risk of blockades where Stockfish start shuffling without progress, we also implement a progressive decrease of the evaluation value with the 50 moves counter (along with the necessary aging of the transposition table to reduce the impact of the Graph History Interaction problem): since the evaluation decreases during shuffling phases, the engine will tend to examine the consequences of pawn breaks faster during the search. Passed STC: LLR: 2.96 (-2.94,2.94) {-0.25,1.25} Total: 26184 W: 2406 L: 2252 D: 21526 Ptnml(0-2): 85, 1784, 9223, 1892, 108 https://tests.stockfishchess.org/tests/view/600cc08b735dd7f0f0352c06 Passed LCT: LLR: 2.95 (-2.94,2.94) {0.25,1.25} Total: 199768 W: 7695 L: 7191 D: 184882 Ptnml(0-2): 85, 6478, 86269, 6952, 100 https://tests.stockfishchess.org/tests/view/600ccd28735dd7f0f0352c10 Closes https://github.com/official-stockfish/Stockfish/pull/3321 Bench: 3988915 --- src/evaluate.cpp | 4 ++-- src/position.cpp | 2 +- src/position.h | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 76266937d56..6bd3c08b513 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1052,8 +1052,8 @@ Value Eval::evaluate(const Position& pos) { { // Scale and shift NNUE for compatibility with search and classical evaluation auto adjusted_NNUE = [&](){ - int mat = pos.non_pawn_material() + PawnValueMg * pos.count(); - return NNUE::evaluate(pos) * (679 + mat / 32) / 1024 + Tempo; + int mat = pos.non_pawn_material() + 2 * PawnValueMg * pos.count(); + return NNUE::evaluate(pos) * (641 + mat / 32 - 4 * pos.rule50_count()) / 1024 + Tempo; }; // If there is PSQ imbalance use classical eval, with small probability if it is small diff --git a/src/position.cpp b/src/position.cpp index 12b1bd9a8c5..8c97160b8e9 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1017,7 +1017,7 @@ void Position::do_null_move(StateInfo& newSt) { } st->key ^= Zobrist::side; - prefetch(TT.first_entry(st->key)); + prefetch(TT.first_entry(key())); ++st->rule50; st->pliesFromNull = 0; diff --git a/src/position.h b/src/position.h index 93e0f91d675..928366bcf43 100644 --- a/src/position.h +++ b/src/position.h @@ -322,7 +322,8 @@ inline int Position::pawns_on_same_color_squares(Color c, Square s) const { } inline Key Position::key() const { - return st->key; + return st->rule50 < 14 ? st->key + : st->key ^ make_key((st->rule50 - 14) / 8); } inline Key Position::pawn_key() const { From 70a818cbd6784ccfa8503d94bc31dd1dc16dd1ff Mon Sep 17 00:00:00 2001 From: Lolligerhans Date: Tue, 12 Jan 2021 14:59:51 +0100 Subject: [PATCH 0490/1766] Small cleanups closes https://github.com/official-stockfish/Stockfish/pull/3301 No functional change --- src/evaluate.cpp | 29 +++++++++++++++-------------- src/main.cpp | 7 ++----- src/material.cpp | 14 ++++++++------ src/movegen.cpp | 8 ++++---- src/movepick.h | 6 +++--- src/position.cpp | 2 +- src/position.h | 5 +---- src/psqt.cpp | 42 ++++++++++++++++++++++++------------------ src/psqt.h | 38 ++++++++++++++++++++++++++++++++++++++ src/syzygy/tbprobe.cpp | 4 ++-- src/tune.h | 6 +++--- 11 files changed, 101 insertions(+), 60 deletions(-) create mode 100644 src/psqt.h diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 6bd3c08b513..0b2112612a7 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -37,12 +37,13 @@ #include "incbin/incbin.h" -// Macro to embed the default NNUE file data in the engine binary (using incbin.h, by Dale Weiler). +// Macro to embed the default efficiently updatable neural network (NNUE) file +// data in the engine binary (using incbin.h, by Dale Weiler). // This macro invocation will declare the following three variables // const unsigned char gEmbeddedNNUEData[]; // a pointer to the embedded data // const unsigned char *const gEmbeddedNNUEEnd; // a marker to the end // const unsigned int gEmbeddedNNUESize; // the size of the embedded file -// Note that this does not work in Microsof Visual Studio. +// Note that this does not work in Microsoft Visual Studio. #if !defined(_MSC_VER) && !defined(NNUE_EMBEDDING_OFF) INCBIN(EmbeddedNNUE, EvalFileDefaultName); #else @@ -60,9 +61,9 @@ namespace Eval { bool useNNUE; string eval_file_loaded = "None"; - /// NNUE::init() tries to load a nnue network at startup time, or when the engine + /// NNUE::init() tries to load a NNUE network at startup time, or when the engine /// receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue" - /// The name of the nnue network is always retrieved from the EvalFile option. + /// The name of the NNUE network is always retrieved from the EvalFile option. /// We search the given network in three locations: internally (the default /// network may be embedded in the binary), in the active working directory and /// in the engine directory. Distro packagers may define the DEFAULT_NNUE_DIRECTORY @@ -450,7 +451,7 @@ namespace { // Penalty if the piece is far from the king score -= KingProtector[Pt == BISHOP] * distance(pos.square(Us), s); - if (Pt == BISHOP) + if constexpr (Pt == BISHOP) { // Penalty according to the number of our pawns on the same color square as the // bishop, bigger when the center files are blocked with pawns and smaller @@ -482,7 +483,7 @@ namespace { } } - if (Pt == ROOK) + if constexpr (Pt == ROOK) { // Bonuses for rook on a (semi-)open or closed file if (pos.is_on_semiopen_file(Us, s)) @@ -509,7 +510,7 @@ namespace { } } - if (Pt == QUEEN) + if constexpr (Pt == QUEEN) { // Penalty if any relative pin or discovered attack against the queen Bitboard queenPinners; @@ -517,7 +518,7 @@ namespace { score -= WeakQueen; } } - if (T) + if constexpr (T) Trace::add(Pt, Us, score); return score; @@ -617,7 +618,7 @@ namespace { // Penalty if king flank is under attack, potentially moving toward the king score -= FlankAttacks * kingFlankAttack; - if (T) + if constexpr (T) Trace::add(KING, Us, score); return score; @@ -718,7 +719,7 @@ namespace { score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]) * (1 + queenImbalance); } - if (T) + if constexpr (T) Trace::add(THREAT, Us, score); return score; @@ -811,7 +812,7 @@ namespace { score += bonus - PassedFile * edge_distance(file_of(s)); } - if (T) + if constexpr (T) Trace::add(PASSED, Us, score); return score; @@ -852,7 +853,7 @@ namespace { int weight = pos.count(Us) - 3 + std::min(pe->blocked_count(), 9); Score score = make_score(bonus * weight * weight / 16, 0); - if (T) + if constexpr (T) Trace::add(SPACE, Us, score); return score; @@ -947,7 +948,7 @@ namespace { + eg * int(PHASE_MIDGAME - me->game_phase()) * ScaleFactor(sf) / SCALE_FACTOR_NORMAL; v /= PHASE_MIDGAME; - if (T) + if constexpr (T) { Trace::add(WINNABLE, make_score(u, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL - eg_value(score))); Trace::add(TOTAL, make_score(mg, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL)); @@ -1019,7 +1020,7 @@ namespace { Value v = winnable(score); // In case of tracing add all remaining individual evaluation terms - if (T) + if constexpr (T) { Trace::add(MATERIAL, pos.psq_score()); Trace::add(IMBALANCE, me->imbalance()); diff --git a/src/main.cpp b/src/main.cpp index ef46d0b50e8..ef662468ff4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,15 +21,12 @@ #include "bitboard.h" #include "endgame.h" #include "position.h" +#include "psqt.h" #include "search.h" +#include "syzygy/tbprobe.h" #include "thread.h" #include "tt.h" #include "uci.h" -#include "syzygy/tbprobe.h" - -namespace PSQT { - void init(); -} int main(int argc, char* argv[]) { diff --git a/src/material.cpp b/src/material.cpp index 36b6132c650..e76641d1a0a 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -29,23 +29,25 @@ namespace { // Polynomial material imbalance parameters + // One Score parameter for each pair (our piece, another of our pieces) constexpr Score QuadraticOurs[][PIECE_TYPE_NB] = { - // OUR PIECES - // pair pawn knight bishop rook queen + // OUR PIECE 2 + // bishop pair pawn knight bishop rook queen {S(1419, 1455) }, // Bishop pair {S( 101, 28), S( 37, 39) }, // Pawn - {S( 57, 64), S(249, 187), S(-49, -62) }, // Knight OUR PIECES + {S( 57, 64), S(249, 187), S(-49, -62) }, // Knight OUR PIECE 1 {S( 0, 0), S(118, 137), S( 10, 27), S( 0, 0) }, // Bishop {S( -63, -68), S( -5, 3), S(100, 81), S(132, 118), S(-246, -244) }, // Rook {S(-210, -211), S( 37, 14), S(147, 141), S(161, 105), S(-158, -174), S(-9,-31) } // Queen }; + // One Score parameter for each pair (our piece, their piece) constexpr Score QuadraticTheirs[][PIECE_TYPE_NB] = { - // THEIR PIECES - // pair pawn knight bishop rook queen + // THEIR PIECE + // bishop pair pawn knight bishop rook queen { }, // Bishop pair {S( 33, 30) }, // Pawn - {S( 46, 18), S(106, 84) }, // Knight OUR PIECES + {S( 46, 18), S(106, 84) }, // Knight OUR PIECE {S( 75, 35), S( 59, 44), S( 60, 15) }, // Bishop {S( 26, 35), S( 6, 22), S( 38, 39), S(-12, -2) }, // Rook {S( 97, 93), S(100, 163), S(-58, -91), S(112, 192), S(276, 225) } // Queen diff --git a/src/movegen.cpp b/src/movegen.cpp index 88c31cfa485..14df1f0504d 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -205,7 +205,7 @@ namespace { static_assert(Type != LEGAL, "Unsupported type in generate_all()"); - constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantations + constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantiations Bitboard target, piecesToMove = pos.pieces(Us); if(Type == QUIET_CHECKS) @@ -257,7 +257,7 @@ namespace { /// Generates all pseudo-legal captures plus queen and checking knight promotions -/// Generates all pseudo-legal non-captures and underpromotions(except checking knight) +/// Generates all pseudo-legal non-captures and underpromotions (except checking knight) /// Generates all pseudo-legal captures and non-captures /// /// Returns a pointer to the end of the move list. @@ -280,8 +280,8 @@ template ExtMove* generate(const Position&, ExtMove*); template ExtMove* generate(const Position&, ExtMove*); -/// generate generates all pseudo-legal non-captures. -/// Returns a pointer to the end of the move list. +/// generate generates all pseudo-legal non-captures giving check, +/// except castling. Returns a pointer to the end of the move list. template<> ExtMove* generate(const Position& pos, ExtMove* moveList) { diff --git a/src/movepick.h b/src/movepick.h index 5232ee4d486..ea599cda1af 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -111,9 +111,9 @@ typedef Stats ContinuationHistory /// MovePicker class is used to pick one pseudo-legal move at a time from the /// current position. The most important method is next_move(), which returns a /// new pseudo-legal move each time it is called, until there are no moves left, -/// when MOVE_NONE is returned. In order to improve the efficiency of the alpha -/// beta algorithm, MovePicker attempts to return the moves which are most likely -/// to get a cut-off first. +/// when MOVE_NONE is returned. In order to improve the efficiency of the +/// alpha-beta algorithm, MovePicker attempts to return the moves which are most +/// likely to get a cut-off first. class MovePicker { enum PickType { Next, Best }; diff --git a/src/position.cpp b/src/position.cpp index 8c97160b8e9..6a5d09ee385 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1067,7 +1067,7 @@ bool Position::see_ge(Move m, Value threshold) const { assert(is_ok(m)); - // Only deal with normal moves, assume others pass a simple see + // Only deal with normal moves, assume others pass a simple SEE if (type_of(m) != NORMAL) return VALUE_ZERO >= threshold; diff --git a/src/position.h b/src/position.h index 928366bcf43..3624e29e723 100644 --- a/src/position.h +++ b/src/position.h @@ -26,6 +26,7 @@ #include "bitboard.h" #include "evaluate.h" +#include "psqt.h" #include "types.h" #include "nnue/nnue_accumulator.h" @@ -200,10 +201,6 @@ class Position { bool chess960; }; -namespace PSQT { - extern Score psq[PIECE_NB][SQUARE_NB]; -} - extern std::ostream& operator<<(std::ostream& os, const Position& pos); inline Color Position::side_to_move() const { diff --git a/src/psqt.cpp b/src/psqt.cpp index e2107240770..46605d52d47 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -16,19 +16,22 @@ along with this program. If not, see . */ + +#include "psqt.h" + #include -#include "types.h" #include "bitboard.h" +#include "types.h" -namespace PSQT { -#define S(mg, eg) make_score(mg, eg) +namespace +{ -// Bonus[PieceType][Square / 2] contains Piece-Square scores. For each piece -// type on a given square a (middlegame, endgame) score pair is assigned. Table -// is defined for files A..D and white side: it is symmetric for black side and -// second half of the files. +auto constexpr S = make_score; + +// 'Bonus' contains Piece-Square parameters. +// Scores are explicit for files A to D, implicitly mirrored for E to H. constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { { }, { }, @@ -95,10 +98,13 @@ constexpr Score PBonus[RANK_NB][FILE_NB] = { S( -7, 0), S( 7,-11), S( -3, 12), S(-13, 21), S( 5, 25), S(-16, 19), S( 10, 4), S( -8, 7) } }; -#undef S +} // namespace -Score psq[PIECE_NB][SQUARE_NB]; +namespace PSQT +{ + +Score psq[PIECE_NB][SQUARE_NB]; // PSQT::init() initializes piece-square tables: the white halves of the tables are // copied from Bonus[] and PBonus[], adding the piece value, then the black halves of @@ -107,15 +113,15 @@ void init() { for (Piece pc : {W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING}) { - Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]); - - for (Square s = SQ_A1; s <= SQ_H8; ++s) - { - File f = File(edge_distance(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][flip_rank(s)] = -psq[pc][s]; - } + Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]); + + for (Square s = SQ_A1; s <= SQ_H8; ++s) + { + File f = File(edge_distance(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][flip_rank(s)] = -psq[pc][s]; + } } } diff --git a/src/psqt.h b/src/psqt.h new file mode 100644 index 00000000000..8b4fd6eb8c2 --- /dev/null +++ b/src/psqt.h @@ -0,0 +1,38 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#ifndef PSQT_H_INCLUDED +#define PSQT_H_INCLUDED + + +#include "types.h" + + +namespace PSQT +{ + +extern Score psq[PIECE_NB][SQUARE_NB]; + +// Fill psqt array from a set of internally linked parameters +extern void init(); + +} // namespace PSQT + + +#endif // PSQT_H_INCLUDED diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 36234786f12..115815e121a 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1000,7 +1000,7 @@ uint8_t* set_sizes(PairsData* d, uint8_t* data) { // so that d->lowestSym[i] >= d->lowestSym[i+1] (when read as LittleEndian). // Starting from this we compute a base64[] table indexed by symbol length // and containing 64 bit values so that d->base64[i] >= d->base64[i+1]. - // See http://www.eecs.harvard.edu/~michaelm/E210/huffman.pdf + // See https://en.wikipedia.org/wiki/Huffman_coding for (int i = d->base64.size() - 2; i >= 0; --i) { d->base64[i] = (d->base64[i + 1] + number(&d->lowestSym[i]) - number(&d->lowestSym[i + 1])) / 2; @@ -1440,7 +1440,7 @@ WDLScore Tablebases::probe_wdl(Position& pos, ProbeState* result) { // If n = 100 immediately after a capture or pawn move, then the position // is also certainly a win, and during the whole phase until the next // capture or pawn move, the inequality to be preserved is -// dtz + 50-movecounter <= 100. +// dtz + 50-move-counter <= 100. // // In short, if a move is available resulting in dtz + 50-move-counter <= 99, // then do not accept moves leading to dtz + 50-move-counter == 100. diff --git a/src/tune.h b/src/tune.h index ef4189688fb..c2cd0c97e39 100644 --- a/src/tune.h +++ b/src/tune.h @@ -130,9 +130,9 @@ class Tune { SetRange range; }; - // Our facilty to fill the container, each Entry corresponds to a parameter to tune. - // We use variadic templates to deal with an unspecified number of entries, each one - // of a possible different type. + // Our facility to fill the container, each Entry corresponds to a parameter + // to tune. We use variadic templates to deal with an unspecified number of + // entries, each one of a possible different type. static std::string next(std::string& names, bool pop = true); int add(const SetRange&, std::string&&) { return 0; } From 7d0a16e06d968c81f17140b0123db9768ce02a82 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Sat, 16 Jan 2021 01:44:11 -0300 Subject: [PATCH 0491/1766] Avoid more expensive legality check speedup of the code, enough to pass STC, failed LTC. Passed STC: LLR: 2.93 (-2.94,2.94) {-0.25,1.25} Total: 68928 W: 6334 L: 6122 D: 56472 Ptnml(0-2): 233, 4701, 24369, 4943, 218 https://tests.stockfishchess.org/tests/view/6002747f6019e097de3ef8dc Failed LTC: LLR: -2.96 (-2.94,2.94) {0.25,1.25} Total: 44560 W: 1702 L: 1675 D: 41183 Ptnml(0-2): 25, 1383, 19438, 1408, 26 https://tests.stockfishchess.org/tests/view/6002a4836019e097de3ef8e3 About 1% speedup: Result of 50 runs ================== base (...kfish.master) = 2237500 +/- 7428 test (...ckfish.patch) = 2267003 +/- 7017 diff = +29503 +/- 4774 speedup = +0.0132 P(speedup > 0) = 1.0000 closes https://github.com/official-stockfish/Stockfish/pull/3304 No functional change. --- src/position.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/position.cpp b/src/position.cpp index 6a5d09ee385..ad1865f037f 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -564,8 +564,10 @@ bool Position::pseudo_legal(const Move m) const { Piece pc = moved_piece(m); // Use a slower but simpler function for uncommon cases + // yet we skip the legality check of MoveList(). if (type_of(m) != NORMAL) - return MoveList(*this).contains(m); + return checkers() ? MoveList< EVASIONS>(*this).contains(m) + : MoveList(*this).contains(m); // Is not a promotion, so promotion piece must be empty if (promotion_type(m) - KNIGHT != NO_PIECE_TYPE) From befbcffb4e4c5d2ebfb9504b059a337ae4a60ece Mon Sep 17 00:00:00 2001 From: bmc4 Date: Mon, 18 Jan 2021 17:03:43 -0300 Subject: [PATCH 0492/1766] Clean Up Castling in gives_check There is no need to add rto or kto on the Bitboard which represents the pieces. STC: LLR: 2.93 (-2.94,2.94) {-1.25,0.25} Total: 57064 W: 5096 L: 5067 D: 46901 Ptnml(0-2): 202, 3862, 20355, 3931, 182 https://tests.stockfishchess.org/tests/view/6005ea2c6019e097de3efa55 LTC: LLR: 2.92 (-2.94,2.94) {-0.75,0.25} Total: 30088 W: 1094 L: 1052 D: 27942 Ptnml(0-2): 10, 882, 13217, 926, 9 https://tests.stockfishchess.org/tests/view/6006115a6019e097de3efa6e closes https://github.com/official-stockfish/Stockfish/pull/3311 No functional change. --- src/position.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index ad1865f037f..954e5ffdf75 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -665,19 +665,15 @@ bool Position::gives_check(Move m) const { return (attacks_bb< ROOK>(square(~sideToMove), b) & pieces(sideToMove, QUEEN, ROOK)) | (attacks_bb(square(~sideToMove), b) & pieces(sideToMove, QUEEN, BISHOP)); } - case CASTLING: + default: //CASTLING { - Square kfrom = from; - Square rfrom = to; // Castling is encoded as 'king captures the rook' - Square kto = relative_square(sideToMove, rfrom > kfrom ? SQ_G1 : SQ_C1); - Square rto = relative_square(sideToMove, rfrom > kfrom ? SQ_F1 : SQ_D1); + // Castling is encoded as 'king captures the rook' + Square ksq = square(~sideToMove); + Square rto = relative_square(sideToMove, to > from ? SQ_F1 : SQ_D1); - return (attacks_bb(rto) & square(~sideToMove)) - && (attacks_bb(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & square(~sideToMove)); + return (attacks_bb(rto) & ksq) + && (attacks_bb(rto, pieces() ^ from ^ to) & ksq); } - default: - assert(false); - return false; } } From 0db374777e793f60bb897d078b3ab641613112ad Mon Sep 17 00:00:00 2001 From: bmc4 Date: Tue, 19 Jan 2021 09:21:59 -0300 Subject: [PATCH 0493/1766] Speed Up Perft Search It speeds up generate, and thus perft, roughly by 2-3%. closes https://github.com/official-stockfish/Stockfish/pull/3312 No functional change --- src/movegen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index 14df1f0504d..c9d6a90d7d0 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -354,7 +354,7 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { moveList = pos.checkers() ? generate(pos, moveList) : generate(pos, moveList); while (cur != moveList) - if ( (pinned || from_sq(*cur) == ksq || type_of(*cur) == EN_PASSANT) + if ( ((pinned && pinned & from_sq(*cur)) || from_sq(*cur) == ksq || type_of(*cur) == EN_PASSANT) && !pos.legal(*cur)) *cur = (--moveList)->move; else From dd9609521437dc839236c36d35bdb8cb633ba19f Mon Sep 17 00:00:00 2001 From: bmc4 Date: Sun, 17 Jan 2021 15:21:45 -0300 Subject: [PATCH 0494/1766] Simplify Chess 960 castling a little cleanup, and small speedup (about 0.3%) for Chess 960. Verified with perft on a large set of chess960 positions. Closes https://github.com/official-stockfish/Stockfish/pull/3317 No functional change --- src/position.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index 954e5ffdf75..826e847f09e 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -533,11 +533,9 @@ bool Position::legal(Move m) const { if (attackers_to(s) & pieces(~us)) return false; - // In case of Chess960, verify that when moving the castling rook we do - // not discover some hidden checker. + // In case of Chess960, verify if the Rook blocks some checks // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1. - return !chess960 - || !(attacks_bb(to, pieces() ^ to_sq(m)) & pieces(~us, ROOK, QUEEN)); + return !chess960 || !(blockers_for_king(us) & to_sq(m)); } // If the moving piece is a king, check whether the destination square is From 6617ad6e033fc636e82453e121469f10e4f31a1a Mon Sep 17 00:00:00 2001 From: bmc4 Date: Sun, 31 Jan 2021 00:05:39 -0300 Subject: [PATCH 0495/1766] Tune ordering of moves at internal nodes We change the relative weights of the function used to order quiet moves in our MovePicker class. Passed STC: LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 32184 W: 2936 L: 2773 D: 26475 Ptnml(0-2): 115, 2196, 11328, 2317, 136 https://tests.stockfishchess.org/tests/view/60161ee1735dd7f0f03530f8 Passed LTC: LLR: 2.93 (-2.94,2.94) {0.25,1.25} Total: 33088 W: 1292 L: 1149 D: 30647 Ptnml(0-2): 14, 1030, 14318, 1163, 19 https://tests.stockfishchess.org/tests/view/60163146735dd7f0f03530ff The new weight were chosen after the following SPSA session: https://tests.stockfishchess.org/tests/view/60136857735dd7f0f0352f6c Closes https://github.com/official-stockfish/Stockfish/pull/3331 Bench: 4398803 --- src/movepick.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 9cb8447f778..f5b274966da 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -106,8 +106,8 @@ void MovePicker::score() { else if (Type == QUIETS) 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[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)] + (ply < MAX_LPH ? std::min(4, depth / 3) * (*lowPlyHistory)[ply][from_to(m)] : 0); @@ -117,8 +117,8 @@ void MovePicker::score() { m.value = PieceValue[MG][pos.piece_on(to_sq(m))] - Value(type_of(pos.moved_piece(m))); else - m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] - + (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] + m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] + + 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] - (1 << 28); } } From 9f8058bd26df1c3ca37b85f811026f1eb82e6524 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Sun, 31 Jan 2021 10:12:32 -0300 Subject: [PATCH 0496/1766] Simplify En Passant simplifies the handling of en passant during search, needs a little more care in initialization. Passed STC: LLR: 2.95 (-2.94,2.94) {-1.25,0.25} Total: 72608 W: 6569 L: 6559 D: 59480 Ptnml(0-2): 233, 5117, 25629, 5057, 268 https://tests.stockfishchess.org/tests/view/600f1363735dd7f0f0352ce7 Passed LTC: LLR: 2.92 (-2.94,2.94) {-0.75,0.25} Total: 24328 W: 913 L: 864 D: 22551 Ptnml(0-2): 10, 731, 10633, 780, 10 https://tests.stockfishchess.org/tests/view/600f2e93735dd7f0f0352cf6 closes https://github.com/official-stockfish/Stockfish/pull/3330 No functional change. --- src/position.cpp | 55 +++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index 826e847f09e..35e307efcbc 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -249,6 +249,8 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th set_castling_right(c, rsq); } + set_state(st); + // 4. En passant square. // Ignore if square is invalid or not on side to move relative rank 6. bool enpassant = false; @@ -262,12 +264,24 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th // a) side to move have a pawn threatening epSquare // b) there is an enemy pawn in front of epSquare // c) there is no piece on epSquare or behind epSquare + // d) enemy pawn didn't block a check of its own color by moving forward enpassant = pawn_attacks_bb(~sideToMove, st->epSquare) & pieces(sideToMove, PAWN) && (pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove))) - && !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove)))); + && !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove)))) + && (file_of(square(sideToMove)) == file_of(st->epSquare) + || !(blockers_for_king(sideToMove) & (st->epSquare + pawn_push(~sideToMove)))); } - if (!enpassant) + // It's necessary for st->previous to be intialized in this way because legality check relies on its existence + if (enpassant) { + st->previous = new StateInfo(); + remove_piece(st->epSquare - pawn_push(sideToMove)); + st->previous->checkersBB = attackers_to(square(~sideToMove)) & pieces(sideToMove); + st->previous->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), square(WHITE), st->previous->pinners[BLACK]); + st->previous->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square(BLACK), st->previous->pinners[WHITE]); + put_piece(make_piece(~sideToMove, PAWN), st->epSquare - pawn_push(sideToMove)); + } + else st->epSquare = SQ_NONE; // 5-6. Halfmove clock and fullmove number @@ -279,7 +293,6 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th chess960 = isChess960; thisThread = th; - set_state(st); st->accumulator.state[WHITE] = Eval::NNUE::INIT; st->accumulator.state[BLACK] = Eval::NNUE::INIT; @@ -502,23 +515,11 @@ bool Position::legal(Move m) const { assert(color_of(moved_piece(m)) == us); assert(piece_on(square(us)) == make_piece(us, KING)); - // En passant captures are a tricky special case. Because they are rather - // uncommon, we do it simply by testing whether the king is attacked after - // the move is made. + // st->previous->blockersForKing consider capsq as empty. + // If pinned, it has to move along the king ray. if (type_of(m) == EN_PASSANT) - { - Square ksq = square(us); - Square capsq = to - pawn_push(us); - Bitboard occupied = (pieces() ^ from ^ capsq) | to; - - assert(to == ep_square()); - assert(moved_piece(m) == make_piece(us, PAWN)); - assert(piece_on(capsq) == make_piece(~us, PAWN)); - assert(piece_on(to) == NO_PIECE); - - return !(attacks_bb< ROOK>(ksq, occupied) & pieces(~us, QUEEN, ROOK)) - && !(attacks_bb(ksq, occupied) & pieces(~us, QUEEN, BISHOP)); - } + return !(st->previous->blockersForKing[sideToMove] & from) || + aligned(from, to, square(us)); // Castling moves generation does not check if the castling path is clear of // enemy attacks, it is delayed at a later time: now! @@ -651,18 +652,14 @@ bool Position::gives_check(Move m) const { case PROMOTION: return attacks_bb(promotion_type(m), to, pieces() ^ from) & square(~sideToMove); - // En passant capture with check? We have already handled the case - // of direct checks and ordinary discovered check, so the only case we - // need to handle is the unusual case of a discovered check through - // the captured pawn. + // The double-pushed pawn blocked a check? En Passant will remove the blocker. + // The only discovery check that wasn't handle is through capsq and fromsq + // So the King must be in the same rank as fromsq to consider this possibility. + // st->previous->blockersForKing consider capsq as empty. case EN_PASSANT: - { - Square capsq = make_square(file_of(to), rank_of(from)); - Bitboard b = (pieces() ^ from ^ capsq) | to; + return st->previous->checkersBB || (rank_of(square(~sideToMove)) == rank_of(from) + && st->previous->blockersForKing[~sideToMove] & from); - return (attacks_bb< ROOK>(square(~sideToMove), b) & pieces(sideToMove, QUEEN, ROOK)) - | (attacks_bb(square(~sideToMove), b) & pieces(sideToMove, QUEEN, BISHOP)); - } default: //CASTLING { // Castling is encoded as 'king captures the rook' From 5ebdc40f8301745b4f8023e595ca2ddde74aa647 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sun, 7 Feb 2021 13:44:11 +0200 Subject: [PATCH 0497/1766] Pawns Tuning A simple tuning of Pawns parameters, and some PSQT changes. Passed STC: LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 219424 W: 43681 L: 43103 D: 132640 Ptnml(0-2): 4014, 25760, 49669, 26172, 4097 https://tests.stockfishchess.org/tests/view/601bce167f517a561bc491eb Passed LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.25} Total: 317312 W: 42525 L: 41579 D: 233208 Ptnml(0-2): 2447, 30157, 92636, 30835, 2581 https://tests.stockfishchess.org/tests/view/601c21557f517a561bc49227 closes https://github.com/official-stockfish/Stockfish/pull/3337 Bench: 4154473 --- src/pawns.cpp | 16 ++++++++-------- src/psqt.cpp | 18 +++++++++--------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/pawns.cpp b/src/pawns.cpp index 5d6770ed76e..cd4d4e45212 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -30,22 +30,22 @@ namespace { #define S(mg, eg) make_score(mg, eg) // Pawn penalties - constexpr Score Backward = S( 6, 23); - constexpr Score Doubled = S(13, 53); - constexpr Score DoubledEarly = S(20, 10); - constexpr Score Isolated = S( 2, 15); - constexpr Score WeakLever = S( 5, 57); - constexpr Score WeakUnopposed = S(16, 22); + constexpr Score Backward = S( 9, 22); + constexpr Score Doubled = S(13, 51); + constexpr Score DoubledEarly = S(20, 7); + constexpr Score Isolated = S( 3, 15); + constexpr Score WeakLever = S( 4, 58); + constexpr Score WeakUnopposed = S(13, 24); // Bonus for blocked pawns at 5th or 6th rank - constexpr Score BlockedPawn[2] = { S(-15, -3), S(-6, 3) }; + constexpr Score BlockedPawn[2] = { S(-17, -6), S(-9, 2) }; constexpr Score BlockedStorm[RANK_NB] = { S(0, 0), S(0, 0), S(75, 78), S(-8, 16), S(-6, 10), S(-6, 6), S(0, 2) }; // Connected pawn bonus - constexpr int Connected[RANK_NB] = { 0, 5, 7, 11, 24, 48, 86 }; + constexpr int Connected[RANK_NB] = { 0, 5, 7, 11, 23, 48, 87 }; // Strength of pawn shelter for our king by [distance from edge][rank]. // RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king. diff --git a/src/psqt.cpp b/src/psqt.cpp index 46605d52d47..cfade2951ff 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -67,13 +67,13 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { }, { // Queen { S( 3,-69), S(-5,-57), S(-5,-47), S( 4,-26) }, - { S(-3,-55), S( 5,-31), S( 8,-22), S(12, -4) }, + { S(-3,-54), S( 5,-31), S( 8,-22), S(12, -4) }, { S(-3,-39), S( 6,-18), S(13, -9), S( 7, 3) }, { S( 4,-23), S( 5, -3), S( 9, 13), S( 8, 24) }, { S( 0,-29), S(14, -6), S(12, 9), S( 5, 21) }, - { S(-4,-38), S(10,-18), S( 6,-12), S( 8, 1) }, + { S(-4,-38), S(10,-18), S( 6,-11), S( 8, 1) }, { S(-5,-50), S( 6,-27), S(10,-24), S( 8, -8) }, - { S(-2,-75), S(-2,-52), S( 1,-43), S(-2,-36) } + { S(-2,-74), S(-2,-52), S( 1,-43), S(-2,-34) } }, { // King { S(271, 1), S(327, 45), S(271, 85), S(198, 76) }, @@ -90,12 +90,12 @@ constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { constexpr Score PBonus[RANK_NB][FILE_NB] = { // Pawn (asymmetric distribution) { }, - { S( 3,-10), S( 3, -6), S( 10, 10), S( 19, 0), S( 16, 14), S( 19, 7), S( 7, -5), S( -5,-19) }, - { S( -9,-10), S(-15,-10), S( 11,-10), S( 15, 4), S( 32, 4), S( 22, 3), S( 5, -6), S(-22, -4) }, - { S( -4, 6), S(-23, -2), S( 6, -8), S( 20, -4), S( 40,-13), S( 17,-12), S( 4,-10), S( -8, -9) }, - { S( 13, 10), S( 0, 5), S(-13, 4), S( 1, -5), S( 11, -5), S( -2, -5), S(-13, 14), S( 5, 9) }, - { S( 5, 28), S(-12, 20), S( -7, 21), S( 22, 28), S( -8, 30), S( -5, 7), S(-15, 6), S( -8, 13) }, - { S( -7, 0), S( 7,-11), S( -3, 12), S(-13, 21), S( 5, 25), S(-16, 19), S( 10, 4), S( -8, 7) } + { S( 2, -8), S( 4, -6), S( 11, 9), S( 18, 5), S( 16, 16), S( 21, 6), S( 9, -6), S( -3,-18) }, + { S( -9, -9), S(-15, -7), S( 11,-10), S( 15, 5), S( 31, 2), S( 23, 3), S( 6, -8), S(-20, -5) }, + { S( -3, 7), S(-20, 1), S( 8, -8), S( 19, -2), S( 39,-14), S( 17,-13), S( 2,-11), S( -5, -6) }, + { S( 11, 12), S( -4, 6), S(-11, 2), S( 2, -6), S( 11, -5), S( 0, -4), S(-12, 14), S( 5, 9) }, + { S( 3, 27), S(-11, 18), S( -6, 19), S( 22, 29), S( -8, 30), S( -5, 9), S(-14, 8), S(-11, 14) }, + { S( -7, -1), S( 6,-14), S( -2, 13), S(-11, 22), S( 4, 24), S(-14, 17), S( 10, 7), S( -9, 7) } }; } // namespace From 29ed22de8cd347cacb0ba826ee43fa587985a98d Mon Sep 17 00:00:00 2001 From: bmc4 Date: Thu, 4 Feb 2021 21:50:22 -0300 Subject: [PATCH 0498/1766] Search Parameters Tuning A simple tuning on search.cpp. based SPSA test: https://tests.stockfishchess.org/tests/view/601f2a787f517a561bc493cd passed STC: LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 117840 W: 10796 L: 10508 D: 96536 Ptnml(0-2): 422, 8381, 41041, 8639, 437 https://tests.stockfishchess.org/tests/view/602144c37f517a561bc494ae passed LTC: LLR: 2.96 (-2.94,2.94) {0.25,1.25} Total: 25024 W: 972 L: 847 D: 23205 Ptnml(0-2): 7, 767, 10847, 876, 15 https://tests.stockfishchess.org/tests/view/602156877f517a561bc494be closes https://github.com/official-stockfish/Stockfish/pull/3340 Bench: 3974098 --- src/search.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 6cc115fcf94..80c3e4c245f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -81,7 +81,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return d > 14 ? 29 : 8 * d * d + 224 * d - 215; + return d > 14 ? 66 : 6 * d * d + 231 * d - 206; } // Add a small random component to draw evaluations to avoid 3-fold blindness @@ -839,10 +839,10 @@ namespace { // Step 8. Null move search with verification search (~40 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 22977 + && (ss-1)->statScore < 22661 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 30 * depth - 28 * improving + 84 * ss->ttPv + 168 + && ss->staticEval >= beta - 24 * depth - 34 * improving + 162 * ss->ttPv + 159 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -850,7 +850,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and value - Depth R = (1015 + 85 * depth) / 256 + std::min(int(eval - beta) / 191, 3); + Depth R = (1062 + 68 * depth) / 256 + std::min(int(eval - beta) / 190, 3); ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -886,7 +886,7 @@ namespace { } } - probCutBeta = beta + 194 - 49 * improving; + probCutBeta = beta + 209 - 44 * improving; // Step 9. ProbCut (~10 Elo) // If we have a good enough capture and a reduced search returns a value @@ -1063,11 +1063,11 @@ namespace { // Futility pruning: parent node (~5 Elo) if ( lmrDepth < 7 && !ss->inCheck - && ss->staticEval + 254 + 159 * lmrDepth <= alpha + && ss->staticEval + 174 + 157 * lmrDepth <= alpha && (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - + (*contHist[5])[movedPiece][to_sq(move)] / 2 < 26394) + + (*contHist[5])[movedPiece][to_sq(move)] / 3 < 26237) continue; // Prune moves with negative SEE (~20 Elo) @@ -1223,13 +1223,13 @@ namespace { + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 5287; + - 5337; // Decrease/increase reduction by comparing opponent's stat score (~10 Elo) - if (ss->statScore >= -105 && (ss-1)->statScore < -103) + if (ss->statScore >= -89 && (ss-1)->statScore < -116) r--; - else if ((ss-1)->statScore >= -122 && ss->statScore < -129) + else if ((ss-1)->statScore >= -112 && ss->statScore < -100) r++; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) @@ -1237,9 +1237,9 @@ namespace { // use sum of main history and first continuation history with an offset if (ss->inCheck) r -= (thisThread->mainHistory[us][from_to(move)] - + (*contHist[0])[movedPiece][to_sq(move)] - 4333) / 16384; + + (*contHist[0])[movedPiece][to_sq(move)] - 4341) / 16384; else - r -= ss->statScore / 14884; + r -= ss->statScore / 14382; } Depth d = std::clamp(newDepth - r, 1, newDepth); @@ -1711,8 +1711,8 @@ namespace { PieceType captured = type_of(pos.piece_on(to_sq(bestMove))); bonus1 = stat_bonus(depth + 1); - bonus2 = bestValue > beta + PawnValueMg ? bonus1 // larger bonus - : stat_bonus(depth); // smaller bonus + bonus2 = bestValue > beta + PawnValueMg ? bonus1 // larger bonus + : std::min(bonus1, stat_bonus(depth)); // smaller bonus if (!pos.capture_or_promotion(bestMove)) { From 1f87a9eb6c2cd51be5a2f28d8b5694d931408a66 Mon Sep 17 00:00:00 2001 From: Andy Pilate Date: Mon, 8 Feb 2021 22:02:42 +0100 Subject: [PATCH 0499/1766] Fixes FreeBSD compilation when using Clang closes https://github.com/official-stockfish/Stockfish/pull/3342 No functional change --- src/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Makefile b/src/Makefile index 87203547f28..55139a1f75e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -366,9 +366,11 @@ ifeq ($(COMP),clang) ifneq ($(KERNEL),Darwin) ifneq ($(KERNEL),OpenBSD) + ifneq ($(KERNEL),FreeBSD) LDFLAGS += -latomic endif endif + endif ifeq ($(arch),$(filter $(arch),armv7 armv8)) ifeq ($(OS),Android) From b15e3b3fa9a4fb5da6e30685c0813a62cee5dd3f Mon Sep 17 00:00:00 2001 From: Gian-Carlo Pascutto Date: Tue, 9 Feb 2021 14:05:35 +0100 Subject: [PATCH 0500/1766] Disable ThinLTO when using Clang. Benchmarking with current Clang 12 shows that and ThinLTO is a pessimization, see issue #3341. closes https://github.com/official-stockfish/Stockfish/pull/3345 No functional change. --- src/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index 55139a1f75e..827ce6bb9d8 100644 --- a/src/Makefile +++ b/src/Makefile @@ -590,7 +590,7 @@ endif ifeq ($(optimize),yes) ifeq ($(debug), no) ifeq ($(comp),clang) - CXXFLAGS += -flto=thin + CXXFLAGS += -flto ifneq ($(findstring MINGW,$(KERNEL)),) CXXFLAGS += -fuse-ld=lld else ifneq ($(findstring MSYS,$(KERNEL)),) @@ -610,7 +610,7 @@ ifeq ($(debug), no) LDFLAGS += -save-temps endif else - CXXFLAGS += -flto=thin + CXXFLAGS += -flto LDFLAGS += $(CXXFLAGS) endif From 550fed3343089357dc89ecf78ce8eb4b35bcab88 Mon Sep 17 00:00:00 2001 From: Gian-Carlo Pascutto Date: Tue, 9 Feb 2021 17:38:58 +0100 Subject: [PATCH 0501/1766] Enable New Pass Manager for Clang. It's about 1% speedup for Stockfish. Result of 100 runs ================== base (...fish_clang12) = 1946851 +/- 3717 test (./stockfish ) = 1967276 +/- 3408 diff = +20425 +/- 2438 speedup = +0.0105 P(speedup > 0) = 1.0000 Thanks to David Major for making me aware of this part of LLVM development. closes https://github.com/official-stockfish/Stockfish/pull/3346 No functional change --- src/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Makefile b/src/Makefile index 827ce6bb9d8..1ff03f8391a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -481,6 +481,10 @@ ifeq ($(optimize),yes) CXXFLAGS += -mdynamic-no-pic endif endif + + ifeq ($(comp),clang) + CXXFLAGS += -fexperimental-new-pass-manager + endif endif ### 3.4 Bits From 573f0e364ff4c1e5928be2ca947f65c5d4e177d5 Mon Sep 17 00:00:00 2001 From: mattginsberg Date: Thu, 11 Feb 2021 22:29:28 +0100 Subject: [PATCH 0502/1766] Better code for hash table generation This patch removes some magic numbers in TT bit management and introduce proper constants in the code, to improve documentation and ease further modifications. No function change --- AUTHORS | 1 + src/tt.cpp | 15 ++++++++------- src/tt.h | 8 +++++++- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/AUTHORS b/AUTHORS index d2b26469ea5..5f21c0485b3 100644 --- a/AUTHORS +++ b/AUTHORS @@ -113,6 +113,7 @@ Maciej Żenczykowski (zenczykowski) Malcolm Campbell (xoto10) Mark Tenzer (31m059) marotear +Matt Ginsberg (mattginsberg) Matthew Lai (matthewlai) Matthew Sullivan (Matt14916) Maxim Molchanov (Maxim) diff --git a/src/tt.cpp b/src/tt.cpp index d1ba9ebbaba..cb5af5c8590 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -123,7 +123,7 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const { for (int i = 0; i < ClusterSize; ++i) if (tte[i].key16 == key16 || !tte[i].depth8) { - tte[i].genBound8 = uint8_t(generation8 | (tte[i].genBound8 & 0x7)); // Refresh + tte[i].genBound8 = uint8_t(generation8 | (tte[i].genBound8 & (GENERATION_DELTA - 1))); // Refresh return found = (bool)tte[i].depth8, &tte[i]; } @@ -132,11 +132,12 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const { TTEntry* replace = tte; for (int i = 1; i < ClusterSize; ++i) // Due to our packed storage format for generation and its cyclic - // nature we add 263 (256 is the modulus plus 7 to keep the unrelated - // lowest three bits from affecting the result) to calculate the entry - // age correctly even after generation8 overflows into the next cycle. - if ( replace->depth8 - ((263 + generation8 - replace->genBound8) & 0xF8) - > tte[i].depth8 - ((263 + generation8 - tte[i].genBound8) & 0xF8)) + // nature we add GENERATION_CYCLE (256 is the modulus, plus what + // is needed to keep the unrelated lowest n bits from affecting + // the result) to calculate the entry age correctly even after + // generation8 overflows into the next cycle. + if ( replace->depth8 - ((GENERATION_CYCLE + generation8 - replace->genBound8) & GENERATION_MASK) + > tte[i].depth8 - ((GENERATION_CYCLE + generation8 - tte[i].genBound8) & GENERATION_MASK)) replace = &tte[i]; return found = false, replace; @@ -151,7 +152,7 @@ int TranspositionTable::hashfull() const { int cnt = 0; for (int i = 0; i < 1000; ++i) for (int j = 0; j < ClusterSize; ++j) - cnt += table[i].entry[j].depth8 && (table[i].entry[j].genBound8 & 0xF8) == generation8; + cnt += table[i].entry[j].depth8 && (table[i].entry[j].genBound8 & GENERATION_MASK) == generation8; return cnt / ClusterSize; } diff --git a/src/tt.h b/src/tt.h index 5f9525a6fbd..a750b6c4657 100644 --- a/src/tt.h +++ b/src/tt.h @@ -72,9 +72,15 @@ class TranspositionTable { static_assert(sizeof(Cluster) == 32, "Unexpected Cluster size"); + // Constants used to refresh the hash table periodically + static constexpr unsigned GENERATION_BITS = 3; // nb of bits reserved for other things + static constexpr int GENERATION_DELTA = (1 << GENERATION_BITS); // increment for generation field + static constexpr int GENERATION_CYCLE = 255 + (1 << GENERATION_BITS); // cycle length + static constexpr int GENERATION_MASK = (0xFF << GENERATION_BITS) & 0xFF; // mask to pull out generation number + public: ~TranspositionTable() { aligned_large_pages_free(table); } - void new_search() { generation8 += 8; } // Lower 3 bits are used by PV flag and Bound + void new_search() { generation8 += GENERATION_DELTA; } // Lower bits are used for other things TTEntry* probe(const Key key, bool& found) const; int hashfull() const; void resize(size_t mbSize); From 76daa88cf878b12a03755dc0550b3fa8e4d19cb1 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Thu, 11 Feb 2021 10:45:16 +0300 Subject: [PATCH 0503/1766] PV-Nodes likely to fail low Do not decrease reduction at pv-nodes which are likely to fail low. The idea of this patch can be described as following: during the search, if a node on the principal variation was re-searched in non-pv search and this re-search got a value which was much lower than alpha, then we can assume that this pv-node is likely to fail low again, and we can reduce more aggressively at this node. Passed STC https://tests.stockfishchess.org/tests/view/6023a5fa7f517a561bc49638 LLR: 2.95 (-2.94,2.94) {-0.25,1.25} Total: 70288 W: 6443 L: 6223 D: 57622 Ptnml(0-2): 239, 5022, 24436, 5174, 273 Passed LTC https://tests.stockfishchess.org/tests/view/6023f2617f517a561bc49661 LLR: 2.94 (-2.94,2.94) {0.25,1.25} Total: 105656 W: 4048 L: 3748 D: 97860 Ptnml(0-2): 67, 3312, 45761, 3630, 58 Closes https://github.com/official-stockfish/Stockfish/pull/3349 Bench: 3766422 --- src/search.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 80c3e4c245f..d77ab691d62 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1025,6 +1025,14 @@ namespace { movedPiece = pos.moved_piece(move); givesCheck = pos.gives_check(move); + // Indicate PvNodes that will probably fail low if node was searched with non-PV search + // at depth equal or greater to current depth and result of this search was far below alpha + bool likelyFailLow = PvNode + && ttMove + && (tte->bound() & BOUND_UPPER) + && ttValue < alpha + 200 + 100 * depth + && tte->depth() >= depth; + // Calculate new depth for this move newDepth = depth - 1; @@ -1172,8 +1180,9 @@ namespace { if (th.marked()) r++; - // Decrease reduction if position is or has been on the PV (~10 Elo) - if (ss->ttPv) + // Decrease reduction if position is or has been on the PV + // and node is not likely to fail low (~10 Elo) + if (ss->ttPv && !likelyFailLow) r -= 2; // Increase reduction at root and non-PV nodes when the best move does not change frequently From b46813f9b74c9edcb830b32d877469ed428072ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Mon, 15 Feb 2021 23:58:03 +0100 Subject: [PATCH 0504/1766] Update Top CPU Contributors No functional change --- Top CPU Contributors.txt | 358 ++++++++++++++++++++------------------- 1 file changed, 187 insertions(+), 171 deletions(-) diff --git a/Top CPU Contributors.txt b/Top CPU Contributors.txt index 482e9000074..f5347ea1541 100644 --- a/Top CPU Contributors.txt +++ b/Top CPU Contributors.txt @@ -1,173 +1,189 @@ -Contributors with >10,000 CPU hours as of Sept 2, 2020 +Contributors to Fishtest with >10,000 CPU hours, as of Feb 15, 2021. Thank you! -Username CPU Hours Games played --------------------------------------------------- -noobpwnftw 19352969 1231459677 -mlang 957168 61657446 -dew 949885 56893432 -mibere 703817 46865007 -crunchy 427035 27344275 -cw 416006 27521077 -JojoM 415904 24479564 -fastgm 404873 23953472 -CSU_Dynasty 335774 22850550 -tvijlbrief 335199 21871270 -Fisherman 325053 21786603 -gvreuls 311480 20751516 -ctoks 275877 18710423 -velislav 241267 15596372 -glinscott 217799 13780820 -nordlandia 211692 13484886 -bcross 206213 14934233 -bking_US 198894 11876016 -leszek 189170 11446821 -mgrabiak 183896 11778092 -drabel 181408 12489478 -TueRens 181349 12192000 -Thanar 179852 12365359 -vdv 175171 9881246 -robal 166948 10702862 -spams 157128 10319326 -marrco 149947 9376421 -sqrt2 147963 9724586 -vdbergh 137041 8926915 -CoffeeOne 136294 5004100 -malala 136182 8002293 -mhoram 128934 8177193 -davar 122092 7960001 -dsmith 122059 7570238 -xoto 119696 8222144 -grandphish2 116481 7582197 -Data 113305 8220352 -BrunoBanani 112960 7436849 -ElbertoOne 99028 7023771 -MaZePallas 98571 6362619 -brabos 92118 6186135 -psk 89957 5984901 -sunu 88463 6007033 -sterni1971 86948 5613788 -Vizvezdenec 83752 5343724 -BRAVONE 81239 5054681 -nssy 76497 5259388 -teddybaer 75125 5407666 -Pking_cda 73776 5293873 -jromang 70695 4940891 -solarlight 70517 5028306 -dv8silencer 70287 3883992 -Bobo1239 68515 4652287 -racerschmacer 67468 4935996 -manap 66273 4121774 -tinker 63458 4213726 -linrock 59082 4516053 -robnjr 57262 4053117 -Freja 56938 3733019 -ttruscott 56005 3679485 -renouve 53811 3501516 -cuistot 52532 3014920 -finfish 51360 3370515 -eva42 51272 3599691 -rkl 50759 3840947 -rap 49985 3219146 -pb00067 49727 3298270 -ronaldjerum 47654 3240695 -bigpen0r 47278 3291647 -biffhero 46564 3111352 -VoyagerOne 45386 3445881 -speedycpu 43842 3003273 -jbwiebe 43305 2805433 -Antihistamine 41788 2761312 -mhunt 41735 2691355 -eastorwest 40387 2812173 -homyur 39893 2850481 -gri 39871 2515779 -oryx 38228 2941656 -0x3C33 37773 2529097 -SC 37290 2731014 -csnodgrass 36207 2688994 -jmdana 36108 2205261 -strelock 34716 2074055 -Garf 33800 2747562 -EthanOConnor 33370 2090311 -slakovv 32915 2021889 -Spprtr 32591 2139601 -Prcuvu 30377 2170122 -anst 30301 2190091 -jkiiski 30136 1904470 -hyperbolic.tom 29840 2017394 -Pyafue 29650 1902349 -OuaisBla 27629 1578000 -chriswk 26902 1868317 -achambord 26582 1767323 -Patrick_G 26276 1801617 -yorkman 26193 1992080 -SFTUser 25182 1675689 -nabildanial 24942 1519409 -Sharaf_DG 24765 1786697 -ncfish1 24411 1520927 -agg177 23890 1395014 -JanErik 23408 1703875 -Isidor 23388 1680691 -Norabor 22976 1587862 -cisco2015 22880 1759669 -Zirie 22542 1472937 -team-oh 22272 1636708 -MazeOfGalious 21978 1629593 -sg4032 21945 1643065 -ianh2105 21725 1632562 -xor12 21628 1680365 -dex 21612 1467203 -nesoneg 21494 1463031 -horst.prack 20878 1465656 -0xB00B1ES 20590 1208666 -j3corre 20405 941444 -Adrian.Schmidt123 20316 1281436 -wei 19973 1745989 -rstoesser 19569 1293588 -eudhan 19274 1283717 -Ente 19070 1373058 -jundery 18445 1115855 -iisiraider 18247 1101015 -ville 17883 1384026 -chris 17698 1487385 -purplefishies 17595 1092533 -DragonLord 17014 1162790 -dju 16515 929427 -IgorLeMasson 16064 1147232 -ako027ako 15671 1173203 -Nikolay.IT 15154 1068349 -Andrew Grant 15114 895539 -yurikvelo 15027 1165616 -OssumOpossum 14857 1007129 -enedene 14476 905279 -bpfliegel 14298 884523 -jpulman 13982 870599 -joster 13794 950160 -Nesa92 13786 1114691 -Dark_wizzie 13422 1007152 -Hjax 13350 900887 -Fifis 13313 965473 -mabichito 12903 749391 -thijsk 12886 722107 -crocogoat 12876 1048802 -AdrianSA 12860 804972 -Flopzee 12698 894821 -fatmurphy 12547 853210 -SapphireBrand 12416 969604 -modolief 12386 896470 -scuzzi 12362 833465 -pgontarz 12151 848794 -stocky 11954 699440 -mschmidt 11941 803401 -infinity 11470 727027 -torbjo 11387 728873 -Thomas A. Anderson 11372 732094 -snicolet 11106 869170 -amicic 10779 733593 -rpngn 10712 688203 -d64 10680 771144 -basepi 10637 744851 -jjoshua2 10559 670905 -dzjp 10343 732529 -ols 10259 570669 -lbraesch 10252 647825 +Username CPU Hours Games played +---------------------------------------------------- +noobpwnftw 23930906 1560559941 +dew 1169948 70333008 +mlang 957168 61657446 +mibere 703840 46867607 +tvijlbrief 517888 33379462 +JojoM 515404 30334272 +cw 443276 29385549 +crunchy 427035 27344275 +grandphish2 425794 26347253 +fastgm 414133 24519696 +gvreuls 377843 24708884 +CSU_Dynasty 338718 23030006 +Fisherman 326795 21820747 +TueRens 313730 19490246 +ctoks 298442 20052551 +velislav 270519 17355456 +bcross 241064 17196165 +glinscott 217799 13780820 +nordlandia 211692 13484886 +bking_US 198894 11876016 +drabel 191096 13129722 +leszek 189170 11446821 +mgrabiak 187153 12013300 +robal 181389 11539242 +Thanar 179852 12365359 +vdv 175274 9889046 +spams 157128 10319326 +marrco 150292 9401741 +sqrt2 147963 9724586 +CoffeeOne 137086 5022516 +vdbergh 137041 8926915 +malala 136182 8002293 +mhoram 132780 8398229 +xoto 124729 8652088 +davar 122092 7960001 +dsmith 122059 7570238 +Data 113305 8220352 +BrunoBanani 112960 7436849 +pemo 109598 5036441 +Dantist 106768 6431396 +MaZePallas 102741 6630419 +ElbertoOne 99028 7023771 +brabos 92118 6186135 +linrock 90903 6708639 +psk 89957 5984901 +sunu 88614 6020673 +sterni1971 86948 5613788 +Vizvezdenec 83761 5344740 +BRAVONE 81239 5054681 +nssy 76497 5259388 +cuistot 76366 4370584 +racerschmacer 75753 5442626 +teddybaer 75125 5407666 +Pking_cda 73776 5293873 +0x3C33 73133 4670293 +jromang 72117 5054915 +solarlight 70517 5028306 +dv8silencer 70287 3883992 +Bobo1239 68515 4652287 +manap 66273 4121774 +tinker 64321 4268390 +robnjr 57262 4053117 +Freja 56938 3733019 +ttruscott 56010 3680085 +rkl 54986 4150767 +renouve 53811 3501516 +finfish 51360 3370515 +eva42 51272 3599691 +rap 49985 3219146 +pb00067 49727 3298270 +amicic 49691 3042481 +ronaldjerum 47654 3240695 +bigpen0r 47278 3291647 +biffhero 46564 3111352 +VoyagerOne 45476 3452465 +eastorwest 45033 3071805 +speedycpu 43842 3003273 +jbwiebe 43305 2805433 +Antihistamine 41788 2761312 +mhunt 41735 2691355 +homyur 39893 2850481 +gri 39871 2515779 +oryx 38282 2944400 +Spprtr 38157 2470529 +SC 37290 2731014 +csnodgrass 36207 2688994 +jmdana 36157 2210661 +strelock 34716 2074055 +Garf 33800 2747562 +skiminki 33515 2055584 +EthanOConnor 33370 2090311 +slakovv 32915 2021889 +yurikvelo 32600 2255966 +Prcuvu 30377 2170122 +manapbk 30326 1770143 +anst 30301 2190091 +jkiiski 30136 1904470 +hyperbolic.tom 29840 2017394 +Pyafue 29650 1902349 +qurashee 27758 1509620 +OuaisBla 27636 1578800 +chriswk 26902 1868317 +achambord 26582 1767323 +Fifis 26376 1776853 +Patrick_G 26276 1801617 +yorkman 26193 1992080 +SFTUser 25182 1675689 +nabildanial 24942 1519409 +Sharaf_DG 24765 1786697 +ncfish1 24411 1520927 +agg177 23890 1395014 +JanErik 23408 1703875 +Isidor 23388 1680691 +Norabor 23164 1591830 +cisco2015 22895 1762069 +Zirie 22542 1472937 +team-oh 22272 1636708 +MazeOfGalious 21978 1629593 +sg4032 21945 1643065 +ianh2105 21725 1632562 +xor12 21628 1680365 +dex 21612 1467203 +nesoneg 21494 1463031 +jjoshua2 20997 1422689 +horst.prack 20878 1465656 +0xB00B1ES 20590 1208666 +sphinx 20515 1352368 +j3corre 20405 941444 +Adrian.Schmidt123 20316 1281436 +Ente 20017 1432602 +wei 19973 1745989 +rstoesser 19569 1293588 +eudhan 19274 1283717 +jundery 18445 1115855 +iisiraider 18247 1101015 +ville 17883 1384026 +chris 17698 1487385 +purplefishies 17595 1092533 +DMBK 17357 1279152 +DragonLord 17014 1162790 +dju 16515 929427 +IgorLeMasson 16064 1147232 +ako027ako 15671 1173203 +Nikolay.IT 15154 1068349 +Andrew Grant 15114 895539 +OssumOpossum 14857 1007129 +enedene 14476 905279 +bpfliegel 14298 884523 +jpulman 13982 870599 +joster 13794 950160 +Nesa92 13786 1114691 +crocogoat 13753 1114622 +Hjax 13535 915487 +Dark_wizzie 13422 1007152 +mpx86 12941 693640 +mabichito 12903 749391 +thijsk 12886 722107 +AdrianSA 12860 804972 +Flopzee 12698 894821 +fatmurphy 12547 853210 +scuzzi 12511 845761 +Karby 12429 735880 +SapphireBrand 12416 969604 +modolief 12386 896470 +pgontarz 12151 848794 +stocky 11954 699440 +mschmidt 11941 803401 +infinity 11470 727027 +torbjo 11395 729145 +Thomas A. Anderson 11372 732094 +d64 11263 789184 +Maxim 11129 804704 +snicolet 11106 869170 +MooTheCow 11008 694942 +savage84 10965 641068 +Rudolphous 10915 741268 +Wolfgang 10809 580032 +rpngn 10712 688203 +basepi 10637 744851 +michaelrpg 10409 735127 +dzjp 10343 732529 +ali-al-zhrani 10324 726502 +ols 10259 570669 +lbraesch 10252 647825 From 40cb0f076a62115af030c4524825d9ba73d61023 Mon Sep 17 00:00:00 2001 From: Lolligerhans Date: Sun, 31 Jan 2021 13:46:05 +0100 Subject: [PATCH 0505/1766] Small trivial clean-ups, February 2021 Closes https://github.com/official-stockfish/Stockfish/pull/3329 No functional change --- src/Makefile | 4 +--- src/evaluate.cpp | 1 - src/movepick.cpp | 4 ++-- src/position.cpp | 17 +++++++++-------- src/search.cpp | 14 +++++++------- 5 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/Makefile b/src/Makefile index 1ff03f8391a..eb32758f98d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,7 +1,5 @@ # Stockfish, a UCI chess playing engine derived from Glaurung 2.1 -# Copyright (C) 2004-2008 Tord Romstad (Glaurung author) -# Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad -# Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad +# Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) # # Stockfish is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 0b2112612a7..d55ef695b75 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -423,7 +423,6 @@ namespace { score += BishopOnKingRing; int mob = popcount(b & mobilityArea[Us]); - mobility[Us] += MobilityBonus[Pt - 2][mob]; if (Pt == BISHOP || Pt == KNIGHT) diff --git a/src/movepick.cpp b/src/movepick.cpp index f5b274966da..0ceeb8eaf60 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -99,11 +99,11 @@ void MovePicker::score() { static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type"); for (auto& m : *this) - if (Type == CAPTURES) + if constexpr (Type == CAPTURES) m.value = int(PieceValue[MG][pos.piece_on(to_sq(m))]) * 6 + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]; - else if (Type == QUIETS) + else if constexpr (Type == QUIETS) m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] + 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] + (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)] diff --git a/src/position.cpp b/src/position.cpp index 35e307efcbc..2eb30ca0d2a 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -268,8 +268,8 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th enpassant = pawn_attacks_bb(~sideToMove, st->epSquare) & pieces(sideToMove, PAWN) && (pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove))) && !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove)))) - && (file_of(square(sideToMove)) == file_of(st->epSquare) - || !(blockers_for_king(sideToMove) & (st->epSquare + pawn_push(~sideToMove)))); + && ( file_of(square(sideToMove)) == file_of(st->epSquare) + || !(blockers_for_king(sideToMove) & (st->epSquare + pawn_push(~sideToMove)))); } // It's necessary for st->previous to be intialized in this way because legality check relies on its existence @@ -518,8 +518,8 @@ bool Position::legal(Move m) const { // st->previous->blockersForKing consider capsq as empty. // If pinned, it has to move along the king ray. if (type_of(m) == EN_PASSANT) - return !(st->previous->blockersForKing[sideToMove] & from) || - aligned(from, to, square(us)); + return !(st->previous->blockersForKing[sideToMove] & from) + || aligned(from, to, square(us)); // Castling moves generation does not check if the castling path is clear of // enemy attacks, it is delayed at a later time: now! @@ -546,8 +546,8 @@ bool Position::legal(Move m) const { // A non-king move is legal if and only if it is not pinned or it // is moving along the ray towards or away from the king. - return !(blockers_for_king(us) & from) - || aligned(from, to, square(us)); + return !(blockers_for_king(us) & from) + || aligned(from, to, square(us)); } @@ -657,8 +657,9 @@ bool Position::gives_check(Move m) const { // So the King must be in the same rank as fromsq to consider this possibility. // st->previous->blockersForKing consider capsq as empty. case EN_PASSANT: - return st->previous->checkersBB || (rank_of(square(~sideToMove)) == rank_of(from) - && st->previous->blockersForKing[~sideToMove] & from); + return st->previous->checkersBB + || ( rank_of(square(~sideToMove)) == rank_of(from) + && st->previous->blockersForKing[~sideToMove] & from); default: //CASTLING { diff --git a/src/search.cpp b/src/search.cpp index d77ab691d62..b5d21b9d558 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1025,12 +1025,12 @@ namespace { movedPiece = pos.moved_piece(move); givesCheck = pos.gives_check(move); - // Indicate PvNodes that will probably fail low if node was searched with non-PV search + // Indicate PvNodes that will probably fail low if node was searched with non-PV search // at depth equal or greater to current depth and result of this search was far below alpha - bool likelyFailLow = PvNode - && ttMove - && (tte->bound() & BOUND_UPPER) - && ttValue < alpha + 200 + 100 * depth + bool likelyFailLow = PvNode + && ttMove + && (tte->bound() & BOUND_UPPER) + && ttValue < alpha + 200 + 100 * depth && tte->depth() >= depth; // Calculate new depth for this move @@ -1180,8 +1180,8 @@ namespace { if (th.marked()) r++; - // Decrease reduction if position is or has been on the PV - // and node is not likely to fail low (~10 Elo) + // Decrease reduction if position is or has been on the PV + // and node is not likely to fail low. (~10 Elo) if (ss->ttPv && !likelyFailLow) r -= 2; From 9f6d69c544a64e06056524ddeaa690560b3896c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Tue, 16 Feb 2021 16:19:37 +0100 Subject: [PATCH 0506/1766] Update README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • reorder some sections of the README file • add reference to the AUTHORS file • rename Syzygybases to Syzygy tablebases • add pointer to the Discord channel • more precise info about the GPLv3 licence No functional change --- README.md | 68 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index eb7aa5a749a..fdc6fd619fa 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,8 @@ This distribution of Stockfish consists of the following files: * Readme.md, the file you are currently reading. * Copying.txt, a text file containing the GNU General Public License version 3. + + * AUTHORS, a text file with the list of authors for the project * src, a subdirectory containing the full source code, including a Makefile that can be used to compile Stockfish on Unix-like systems. @@ -31,17 +33,6 @@ This distribution of Stockfish consists of the following files: * a file with the .nnue extension, storing the neural network for the NNUE evaluation. Binary distributions will have this file embedded. -Note: to use the NNUE evaluation, the additional data file with neural network parameters -needs to be available. Normally, this file is already embedded in the binary or it can be downloaded. -The filename for the default (recommended) net can be found as the default -value of the `EvalFile` UCI option, with the format `nn-[SHA256 first 12 digits].nnue` -(for instance, `nn-c157e0a5755b.nnue`). This file can be downloaded from -``` -https://tests.stockfishchess.org/api/nn/[filename] -``` -replacing `[filename]` as needed. - - ## UCI options Currently, Stockfish has the following UCI options: @@ -53,6 +44,9 @@ Currently, Stockfish has the following UCI options: * #### Hash The size of the hash table in MB. It is recommended to set Hash after setting Threads. + * #### Clear Hash + Clear the hash table. + * #### Ponder Let Stockfish ponder its next move while the opponent is thinking. @@ -109,7 +103,7 @@ Currently, Stockfish has the following UCI options: * #### SyzygyProbeDepth Minimum remaining search depth for which a position is probed. Set this option to a higher value to probe less aggressively if you experience too much slowdown - (in terms of nps) due to TB probing. + (in terms of nps) due to tablebase probing. * #### Syzygy50MoveRule Disable to let fifty-move rule draws detected by Syzygy tablebase probes count @@ -139,13 +133,10 @@ Currently, Stockfish has the following UCI options: Tells the engine to use nodes searched instead of wall time to account for elapsed time. Useful for engine testing. - * #### Clear Hash - Clear the hash table. - * #### Debug Log File Write all communication to and from the engine into a text file. -## A note on classical and NNUE evaluation +## A note on classical evaluation versus NNUE evaluation Both approaches assign a value to a position that is used in alpha-beta (PVS) search to find the best move. The classical evaluation computes this value as a function @@ -158,18 +149,29 @@ The NNUE evaluation was first introduced in shogi, and ported to Stockfish after It can be evaluated efficiently on CPUs, and exploits the fact that only parts of the neural network need to be updated after a typical chess move. [The nodchip repository](https://github.com/nodchip/Stockfish) provides additional -tools to train and develop the NNUE networks. +tools to train and develop the NNUE networks. On CPUs supporting modern vector instructions +(avx2 and similar), the NNUE evaluation results in much stronger playing strength, even +if the nodes per second computed by the engine is somewhat lower (roughly 80% of nps +is typical). -On CPUs supporting modern vector instructions (avx2 and similar), the NNUE evaluation -results in stronger playing strength, even if the nodes per second computed by the engine -is somewhat lower (roughly 60% of nps is typical). +Notes: -Note that the NNUE evaluation depends on the Stockfish binary and the network parameter -file (see EvalFile). Not every parameter file is compatible with a given Stockfish binary. -The default value of the EvalFile UCI option is the name of a network that is guaranteed -to be compatible with that binary. +1) the NNUE evaluation depends on the Stockfish binary and the network parameter +file (see the EvalFile UCI option). Not every parameter file is compatible with a given +Stockfish binary, but the default value of the EvalFile UCI option is the name of a network +that is guaranteed to be compatible with that binary. + +2) to use the NNUE evaluation, the additional data file with neural network parameters +needs to be available. Normally, this file is already embedded in the binary or it +can be downloaded. The filename for the default (recommended) net can be found as the default +value of the `EvalFile` UCI option, with the format `nn-[SHA256 first 12 digits].nnue` +(for instance, `nn-c157e0a5755b.nnue`). This file can be downloaded from +``` +https://tests.stockfishchess.org/api/nn/[filename] +``` +replacing `[filename]` as needed. -## What to expect from Syzygybases? +## What to expect from the Syzygy tablebases? If the engine is searching a position that is not in the tablebases (e.g. a position with 8 pieces), it will access the tablebases during the search. @@ -187,9 +189,9 @@ will not report a mate score, even if the position is known to be won.** It is therefore clear that this behaviour is not identical to what one might be used to with Nalimov tablebases. There are technical reasons for this difference, the main technical reason being that Nalimov tablebases use the -DTM metric (distance-to-mate), while Syzygybases use a variation of the +DTM metric (distance-to-mate), while the Syzygy tablebases use a variation of the DTZ metric (distance-to-zero, zero meaning any move that resets the 50-move -counter). This special metric is one of the reasons that Syzygybases are +counter). This special metric is one of the reasons that the Syzygy tablebases are more compact than Nalimov tablebases, while still storing all information needed for optimal play and in addition being able to take into account the 50-move rule. @@ -272,8 +274,9 @@ generic rather than being focused on Stockfish's precise implementation. Nevertheless, a helpful resource. * The latest source can always be found on [GitHub](https://github.com/official-stockfish/Stockfish). -Discussions about Stockfish take place in the [FishCooking](https://groups.google.com/forum/#!forum/fishcooking) -group and engine testing is done on [Fishtest](https://tests.stockfishchess.org/tests). +Discussions about Stockfish take place these days mainly in the [FishCooking](https://groups.google.com/forum/#!forum/fishcooking) +group and on the [Stockfish Discord channel](https://discord.gg/nv8gDtt). +The engine testing is done on [Fishtest](https://tests.stockfishchess.org/tests). If you want to help improve Stockfish, please read this [guideline](https://github.com/glinscott/fishtest/wiki/Creating-my-first-test) first, where the basics of Stockfish development are explained. @@ -288,9 +291,10 @@ it (either by itself or as part of some bigger software package), or using it as the starting point for a software project of your own. The only real limitation is that whenever you distribute Stockfish in -some way, you must always include the full source code, or a pointer -to where the source code can be found. If you make any changes to the -source code, these changes must also be made available under the GPL. +some way, you MUST always include the full source code, or a pointer +to where the source code can be found, to generate the exact binary +you are distributing. If you make any changes to the source code, +these changes must also be made available under the GPL. For full details, read the copy of the GPL v3 found in the file named *Copying.txt*. From 3597f1942ec6f2cfbd50b905683739b0900ff5dd Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 15 Feb 2021 13:58:51 +0100 Subject: [PATCH 0507/1766] Stockfish 13 Official release version of Stockfish 13 Bench: 3766422 ----- It is our pleasure to release Stockfish 13 to chess fans worldwide. As usual, downloads are freely available at https://stockfishchess.org The Stockfish project builds on a thriving community of enthusiasts who contribute their expertise, time, and resources to build a free and open-source chess engine that is robust, widely available, and very strong. We would like to thank them all! The good news first: from now on, our users can expect more frequent high-quality releases of Stockfish! Sadly, this decision has been triggered by the start of sales of the Fat Fritz 2 engine by ChessBase, which is a copy of a very recent development version of Stockfish with minor modifications. We refer to our statement on Fat Fritz 2[1] and a community blog[2] for further information. This version of Stockfish is significantly stronger than any of its predecessors. Stockfish 13 outperforms Stockfish 12 by at least 35 Elo[3]. When playing against a one-year-old Stockfish, it wins 60 times more game pairs than it loses[4]. This release features an NNUE network retrained on billions of positions, much faster network evaluation code, and significantly improved search heuristics, as well as additional evaluation tweaks. In the course of its development, this version has won the superfinals of the TCEC Season 19 and TCEC Season 20. Going forward, the Leela Chess Zero and Stockfish teams will join forces to demonstrate our commitment to open source chess engines and training tools, and open data. We are convinced that our free and open-source chess engines serve the chess community very well. Stay safe and enjoy chess! The Stockfish team [1] https://blog.stockfishchess.org/post/643239805544792064/statement-on-fat-fritz-2 [2] https://lichess.org/blog/YCvy7xMAACIA8007/fat-fritz-2-is-a-rip-off [3] https://tests.stockfishchess.org/tests/view/602bcccf7f517a561bc49b11 [4] https://tests.stockfishchess.org/tests/view/600fbb9c735dd7f0f0352d59 --- src/misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index 48e20a396c4..ebc7c028fd7 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -65,7 +65,7 @@ namespace { /// Version number. If Version is left empty, then compile date in the format /// DD-MM-YY and show in engine_info. -const string Version = ""; +const string Version = "13"; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We From a31007c9e7005d2ab4ee3f5aa479eb50cecd63e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sat, 20 Feb 2021 22:19:14 +0100 Subject: [PATCH 0508/1766] Restore development version No functional change --- src/misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index ebc7c028fd7..48e20a396c4 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -65,7 +65,7 @@ namespace { /// Version number. If Version is left empty, then compile date in the format /// DD-MM-YY and show in engine_info. -const string Version = "13"; +const string Version = ""; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We From 6294db7514798ac12536b4d6e77bc591f7e846da Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Fri, 19 Feb 2021 10:32:12 +0300 Subject: [PATCH 0509/1766] Tune search parameters (with Unai Corzo) The values used in this patch are taken from a SPSA parameter tuning session originated by Unai Corzo (@unaiic), but the final difference of his tune was multiplied x2 by hand. Most of the credits should go to him :-) STC: https://tests.stockfishchess.org/tests/view/602f03d07f517a561bc49d40 LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 67664 W: 6252 L: 6035 D: 55377 Ptnml(0-2): 256, 4799, 23527, 4972, 278 LTC: https://tests.stockfishchess.org/tests/view/602f41697f517a561bc49d5a LLR: 2.96 (-2.94,2.94) {0.25,1.25} Total: 26256 W: 1034 L: 906 D: 24316 Ptnml(0-2): 10, 804, 11377, 922, 15 Closes https://github.com/official-stockfish/Stockfish/pull/3363 Bench: 3957653 --- src/search.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index b5d21b9d558..8f01f785a9e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -839,7 +839,7 @@ namespace { // Step 8. Null move search with verification search (~40 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 22661 + && (ss-1)->statScore < 24185 && eval >= beta && eval >= ss->staticEval && ss->staticEval >= beta - 24 * depth - 34 * improving + 162 * ss->ttPv + 159 @@ -1075,7 +1075,7 @@ namespace { && (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - + (*contHist[5])[movedPiece][to_sq(move)] / 3 < 26237) + + (*contHist[5])[movedPiece][to_sq(move)] / 3 < 28255) continue; // Prune moves with negative SEE (~20 Elo) @@ -1167,7 +1167,7 @@ namespace { || moveCountPruning || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha || cutNode - || (!PvNode && !formerPv && captureHistory[movedPiece][to_sq(move)][type_of(pos.captured_piece())] < 4506) + || (!PvNode && !formerPv && captureHistory[movedPiece][to_sq(move)][type_of(pos.captured_piece())] < 3678) || thisThread->ttHitAverage < 432 * TtHitAverageResolution * TtHitAverageWindow / 1024)) { Depth r = reduction(improving, depth, moveCount); @@ -1232,7 +1232,7 @@ namespace { + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 5337; + - 4741; // Decrease/increase reduction by comparing opponent's stat score (~10 Elo) if (ss->statScore >= -89 && (ss-1)->statScore < -116) @@ -1246,9 +1246,9 @@ namespace { // use sum of main history and first continuation history with an offset if (ss->inCheck) r -= (thisThread->mainHistory[us][from_to(move)] - + (*contHist[0])[movedPiece][to_sq(move)] - 4341) / 16384; + + (*contHist[0])[movedPiece][to_sq(move)] - 3833) / 16384; else - r -= ss->statScore / 14382; + r -= ss->statScore / 14790; } Depth d = std::clamp(newDepth - r, 1, newDepth); From 7c30091a92abddb8265e53768b32751c49642040 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sat, 20 Feb 2021 22:48:08 +0100 Subject: [PATCH 0510/1766] Introduce ProbCut for check evasions The idea of this patch can be described as follows: if we are in check and the transposition table move is a capture that returns a value far above beta, we can assume that the opponent just blundered a piece by giving check, and we return the transposition table value. This is similar to the usual probCut logic for quiet moves, but with a different threshold. Passed STC LLR: 2.94 (-2.94,2.94) {-0.25,1.25} Total: 33440 W: 3056 L: 2891 D: 27493 Ptnml(0-2): 110, 2338, 11672, 2477, 123 https://tests.stockfishchess.org/tests/view/602cd1087f517a561bc49bda Passed LTC LLR: 2.98 (-2.94,2.94) {0.25,1.25} Total: 10072 W: 401 L: 309 D: 9362 Ptnml(0-2): 2, 288, 4365, 378, 3 https://tests.stockfishchess.org/tests/view/602ceea57f517a561bc49bf0 The committed version has an additional fix to never return unproven wins in the tablebase range or the mate range. This fix passed tests for non- regression at STC and LTC: STC: LLR: 2.93 (-2.94,2.94) {-1.25,0.25} Total: 26240 W: 2354 L: 2280 D: 21606 Ptnml(0-2): 85, 1763, 9372, 1793, 107 https://tests.stockfishchess.org/tests/view/602d86a87f517a561bc49c7a LTC: LLR: 2.95 (-2.94,2.94) {-0.75,0.25} Total: 35304 W: 1299 L: 1256 D: 32749 Ptnml(0-2): 14, 1095, 15395, 1130, 18 https://tests.stockfishchess.org/tests/view/602d98d17f517a561bc49c83 Closes https://github.com/official-stockfish/Stockfish/pull/3362 Bench: 3830215 --- src/search.cpp | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 8f01f785a9e..c33bc914392 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -969,6 +969,23 @@ namespace { moves_loop: // When in check, search starts from here + ttCapture = ttMove && pos.capture_or_promotion(ttMove); + + // Step 11. A small Probcut idea, when we are in check + probCutBeta = beta + 400; + if ( ss->inCheck + && !PvNode + && depth >= 4 + && ttCapture + && (tte->bound() & BOUND_LOWER) + && tte->depth() >= depth - 3 + && ttValue >= probCutBeta + && abs(ttValue) <= VALUE_KNOWN_WIN + && abs(beta) <= VALUE_KNOWN_WIN + ) + return probCutBeta; + + const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, nullptr , (ss-4)->continuationHistory, nullptr , (ss-6)->continuationHistory }; @@ -985,12 +1002,11 @@ namespace { value = bestValue; singularQuietLMR = moveCountPruning = false; - ttCapture = ttMove && pos.capture_or_promotion(ttMove); // Mark this node as being searched ThreadHolding th(thisThread, posKey, ss->ply); - // Step 11. Loop through all pseudo-legal moves until no moves remain + // Step 12. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. while ((move = mp.next_move(moveCountPruning)) != MOVE_NONE) { @@ -1036,7 +1052,7 @@ namespace { // Calculate new depth for this move newDepth = depth - 1; - // Step 12. Pruning at shallow depth (~200 Elo) + // Step 13. Pruning at shallow depth (~200 Elo) if ( !rootNode && pos.non_pawn_material(us) && bestValue > VALUE_TB_LOSS_IN_MAX_PLY) @@ -1084,7 +1100,7 @@ namespace { } } - // Step 13. Extensions (~75 Elo) + // Step 14. Extensions (~75 Elo) // Singular extension search (~70 Elo). If all moves but one fail low on a // search of (alpha-s, beta-s), and just one fails high on (alpha, beta), @@ -1156,10 +1172,10 @@ namespace { [movedPiece] [to_sq(move)]; - // Step 14. Make the move + // Step 15. Make the move pos.do_move(move, st, givesCheck); - // Step 15. Reduced depth search (LMR, ~200 Elo). If the move fails high it will be + // Step 16. Reduced depth search (LMR, ~200 Elo). If the move fails high it will be // re-searched at full depth. if ( depth >= 3 && moveCount > 1 + 2 * rootNode @@ -1266,7 +1282,7 @@ namespace { didLMR = false; } - // Step 16. Full depth search when LMR is skipped or fails high + // Step 17. Full depth search when LMR is skipped or fails high if (doFullDepthSearch) { value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode); @@ -1293,12 +1309,12 @@ namespace { std::min(maxNextDepth, newDepth), false); } - // Step 17. Undo move + // Step 18. Undo move pos.undo_move(move); assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); - // Step 18. Check for a new best move + // Step 19. Check for a new best move // Finished searching the move. If a stop occurred, the return value of // the search cannot be trusted, and we return immediately without // updating best move, PV and TT. @@ -1375,7 +1391,7 @@ namespace { return VALUE_DRAW; */ - // Step 19. Check for mate and stalemate + // Step 20. Check for mate and stalemate // All legal moves have been searched and if there are no legal moves, it // must be a mate or a stalemate. If we are in a singular extension search then // return a fail low score. From 0f3f5d85fb5c9f75199f27fbf7a725ff3e8bb4dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Fri, 26 Feb 2021 10:13:37 +0100 Subject: [PATCH 0511/1766] Introduce DistanceFromPV We introduce a metric for each internal node in search, called DistanceFromPV. This distance indicated how far the current node is from the principal variation. We then use this distance to search the nodes which are close to the PV a little deeper (up to 4 plies deeper than the PV): this improves the quality of the search at these nodes and bring better updates for the PV during search. STC: LLR: 2.96 (-2.94,2.94) {-0.25,1.25} Total: 54936 W: 5047 L: 4850 D: 45039 Ptnml(0-2): 183, 3907, 19075, 4136, 167 https://tests.stockfishchess.org/tests/view/6037b88e7f517a561bc4a392 LTC: LLR: 2.95 (-2.94,2.94) {0.25,1.25} Total: 49608 W: 1880 L: 1703 D: 46025 Ptnml(0-2): 22, 1514, 21555, 1691, 22 https://tests.stockfishchess.org/tests/view/6038271b7f517a561bc4a3cb Closes https://github.com/official-stockfish/Stockfish/pull/3369 Bench: 5037279 --- src/search.cpp | 23 +++++++++++++++-------- src/search.h | 1 + 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c33bc914392..b3bd2f0371f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -616,6 +616,7 @@ namespace { moveCount = captureCount = quietCount = ss->moveCount = 0; bestValue = -VALUE_INFINITE; maxValue = VALUE_INFINITE; + ss->distanceFromPv = (PvNode ? 0 : ss->distanceFromPv); // Check for the available remaining time if (thisThread == Threads.main()) @@ -1175,8 +1176,12 @@ namespace { // Step 15. Make the move pos.do_move(move, st, givesCheck); - // Step 16. Reduced depth search (LMR, ~200 Elo). If the move fails high it will be - // re-searched at full depth. + (ss+1)->distanceFromPv = ss->distanceFromPv + moveCount - 1; + + // Step 16. Late moves reduction / extension (LMR, ~200 Elo) + // We use various heuristics for the sons of a node after the first son has + // been searched. In general we would like to reduce them, but there are many + // cases where we extend a son if it has good chances to be "interesting". if ( depth >= 3 && moveCount > 1 + 2 * rootNode && ( !captureOrPromotion @@ -1258,8 +1263,8 @@ namespace { r++; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - // If we are not in check use statScore, if we are in check - // use sum of main history and first continuation history with an offset + // If we are not in check use statScore, but if we are in check we use + // the sum of main history and first continuation history with an offset. if (ss->inCheck) r -= (thisThread->mainHistory[us][from_to(move)] + (*contHist[0])[movedPiece][to_sq(move)] - 3833) / 16384; @@ -1267,18 +1272,20 @@ namespace { r -= ss->statScore / 14790; } - Depth d = std::clamp(newDepth - r, 1, newDepth); + // In general we want to cap the LMR depth search at newDepth. But for nodes + // close to the principal variation the cap is at (newDepth + 1), which will + // allow these nodes to be searched deeper than the pv (up to 4 plies deeper). + Depth d = std::clamp(newDepth - r, 1, newDepth + ((ss+1)->distanceFromPv <= 4)); value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); - doFullDepthSearch = value > alpha && d != newDepth; - + // If the son is reduced and fails high it will be re-searched at full depth + doFullDepthSearch = value > alpha && d < newDepth; didLMR = true; } else { doFullDepthSearch = !PvNode || moveCount > 1; - didLMR = false; } diff --git a/src/search.h b/src/search.h index 3bf3e9ae605..5e51c18e043 100644 --- a/src/search.h +++ b/src/search.h @@ -47,6 +47,7 @@ struct Stack { Value staticEval; int statScore; int moveCount; + int distanceFromPv; bool inCheck; bool ttPv; bool ttHit; From 9b1274aba3cad440b925283fe7407954743ade78 Mon Sep 17 00:00:00 2001 From: Antoine Champion Date: Sat, 30 Jan 2021 09:50:04 +0100 Subject: [PATCH 0512/1766] Clean functions returning by const values The codebase contains multiple functions returning by const-value. This patch is a small cleanup making those function returns by value instead, removing the const specifier. closes https://github.com/official-stockfish/Stockfish/pull/3328 No functional change --- AUTHORS | 1 + src/bitboard.cpp | 2 +- src/bitboard.h | 2 +- src/misc.cpp | 4 ++-- src/misc.h | 4 ++-- src/position.cpp | 2 +- src/position.h | 2 +- 7 files changed, 9 insertions(+), 8 deletions(-) diff --git a/AUTHORS b/AUTHORS index 5f21c0485b3..3ef7d1b13ec 100644 --- a/AUTHORS +++ b/AUTHORS @@ -24,6 +24,7 @@ Ali AlZhrani (Cooffe) Andrew Grant (AndyGrant) Andrey Neporada (nepal) Andy Duplain +Antoine Champion (antoinechampion) Aram Tumanian (atumanian) Arjun Temurnikar Auguste Pop diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 841aa0b6c1f..8146ce97228 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -55,7 +55,7 @@ inline Bitboard safe_destination(Square s, int step) { /// Bitboards::pretty() returns an ASCII representation of a bitboard suitable /// to be printed to standard output. Useful for debugging. -const std::string Bitboards::pretty(Bitboard b) { +std::string Bitboards::pretty(Bitboard b) { std::string s = "+---+---+---+---+---+---+---+---+\n"; diff --git a/src/bitboard.h b/src/bitboard.h index 95591fc4533..c9555b6bf72 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -33,7 +33,7 @@ bool probe(Square wksq, Square wpsq, Square bksq, Color us); namespace Bitboards { void init(); -const std::string pretty(Bitboard b); +std::string pretty(Bitboard b); } diff --git a/src/misc.cpp b/src/misc.cpp index 48e20a396c4..fe920140dba 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -138,7 +138,7 @@ class Logger { /// the program was compiled) or "Stockfish ", depending on whether /// Version is empty. -const string engine_info(bool to_uci) { +string engine_info(bool to_uci) { const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); string month, day, year; @@ -161,7 +161,7 @@ const string engine_info(bool to_uci) { /// compiler_info() returns a string trying to describe the compiler we use -const std::string compiler_info() { +std::string compiler_info() { #define stringify2(x) #x #define stringify(x) stringify2(x) diff --git a/src/misc.h b/src/misc.h index 7b551adee6a..7eb75bc70ec 100644 --- a/src/misc.h +++ b/src/misc.h @@ -28,8 +28,8 @@ #include "types.h" -const std::string engine_info(bool to_uci = false); -const std::string compiler_info(); +std::string engine_info(bool to_uci = false); +std::string compiler_info(); void prefetch(void* addr); void start_logger(const std::string& fname); void* std_aligned_alloc(size_t alignment, size_t size); diff --git a/src/position.cpp b/src/position.cpp index 2eb30ca0d2a..8f9b7eee89e 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -408,7 +408,7 @@ Position& Position::set(const string& code, Color c, StateInfo* si) { /// Position::fen() returns a FEN representation of the position. In case of /// Chess960 the Shredder-FEN notation is used. This is mainly a debugging function. -const string Position::fen() const { +string Position::fen() const { int emptyCnt; std::ostringstream ss; diff --git a/src/position.h b/src/position.h index 3624e29e723..e9803756609 100644 --- a/src/position.h +++ b/src/position.h @@ -87,7 +87,7 @@ class Position { // FEN string input/output Position& set(const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th); Position& set(const std::string& code, Color c, StateInfo* si); - const std::string fen() const; + std::string fen() const; // Position representation Bitboard pieces(PieceType pt) const; From 7ffae17f85709e49672a0e98e136b66aea067b2c Mon Sep 17 00:00:00 2001 From: Dieter Dobbelaere Date: Fri, 26 Feb 2021 10:02:13 +0100 Subject: [PATCH 0513/1766] Add Stockfish namespace. fixes #3350 and is a small cleanup that might make it easier to use SF in separate projects, like a NNUE trainer or similar. closes https://github.com/official-stockfish/Stockfish/pull/3370 No functional change. --- src/benchmark.cpp | 4 ++++ src/bitbase.cpp | 6 ++++-- src/bitboard.cpp | 6 ++++-- src/bitboard.h | 8 ++++++-- src/endgame.cpp | 4 ++++ src/endgame.h | 3 +++ src/evaluate.cpp | 6 +++++- src/evaluate.h | 4 ++++ src/main.cpp | 2 ++ src/material.cpp | 4 ++++ src/material.h | 4 ++-- src/misc.cpp | 4 ++++ src/misc.h | 4 ++++ src/movegen.cpp | 4 ++++ src/movegen.h | 4 ++++ src/movepick.cpp | 4 ++++ src/movepick.h | 4 ++++ src/nnue/architectures/halfkp_256x2-32-32.h | 4 ++-- src/nnue/evaluate_nnue.cpp | 4 ++-- src/nnue/evaluate_nnue.h | 4 ++-- src/nnue/features/feature_set.h | 4 ++-- src/nnue/features/features_common.h | 4 ++-- src/nnue/features/half_kp.cpp | 4 ++-- src/nnue/features/half_kp.h | 4 ++-- src/nnue/features/index_list.h | 4 ++-- src/nnue/layers/affine_transform.h | 4 ++-- src/nnue/layers/clipped_relu.h | 4 ++-- src/nnue/layers/input_slice.h | 4 ++-- src/nnue/nnue_accumulator.h | 4 ++-- src/nnue/nnue_architecture.h | 4 ++-- src/nnue/nnue_common.h | 4 ++-- src/nnue/nnue_feature_transformer.h | 4 ++-- src/pawns.cpp | 4 ++++ src/pawns.h | 4 ++-- src/position.cpp | 4 ++++ src/position.h | 3 +++ src/psqt.cpp | 3 +++ src/psqt.h | 4 ++-- src/search.cpp | 6 +++++- src/search.h | 4 ++++ src/syzygy/tbprobe.cpp | 8 ++++++-- src/syzygy/tbprobe.h | 4 ++-- src/thread.cpp | 4 ++++ src/thread.h | 3 +++ src/thread_win32_osx.h | 8 ++++++++ src/timeman.cpp | 4 ++++ src/timeman.h | 4 ++++ src/tt.cpp | 4 ++++ src/tt.h | 4 ++++ src/tune.cpp | 8 ++++++++ src/tune.h | 4 ++++ src/types.h | 4 ++++ src/uci.cpp | 4 ++++ src/uci.h | 4 ++++ src/ucioption.cpp | 4 ++++ tests/instrumented.sh | 20 ++++++++++---------- 56 files changed, 200 insertions(+), 58 deletions(-) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 7cb04382e12..7945a4535ca 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -92,6 +92,8 @@ const vector Defaults = { } // namespace +namespace Stockfish { + /// setup_bench() builds a list of UCI commands to be run by bench. There /// are five parameters: TT size in MB, number of search threads that /// should be used, the limit value spent for each position, a file name @@ -168,3 +170,5 @@ vector setup_bench(const Position& current, istream& is) { return list; } + +} // namespace Stockfish diff --git a/src/bitbase.cpp b/src/bitbase.cpp index b640eabb6d1..ece9ec72b23 100644 --- a/src/bitbase.cpp +++ b/src/bitbase.cpp @@ -23,6 +23,8 @@ #include "bitboard.h" #include "types.h" +namespace Stockfish { + namespace { // There are 24 possible pawn squares: files A to D and ranks from 2 to 7. @@ -66,7 +68,6 @@ namespace { } // namespace - bool Bitbases::probe(Square wksq, Square wpsq, Square bksq, Color stm) { assert(file_of(wpsq) <= FILE_D); @@ -96,7 +97,6 @@ void Bitbases::init() { KPKBitbase.set(idx); } - namespace { KPKPosition::KPKPosition(unsigned idx) { @@ -168,3 +168,5 @@ namespace { } } // namespace + +} // namespace Stockfish diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 8146ce97228..a2021449123 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -22,6 +22,8 @@ #include "bitboard.h" #include "misc.h" +namespace Stockfish { + uint8_t PopCnt16[1 << 16]; uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; @@ -42,7 +44,6 @@ namespace { } - /// safe_destination() returns the bitboard of target square for the given step /// from the given square. If the step is off the board, returns empty bitboard. @@ -111,7 +112,6 @@ void Bitboards::init() { } } - namespace { Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) { @@ -211,3 +211,5 @@ namespace { } } } + +} // namespace Stockfish diff --git a/src/bitboard.h b/src/bitboard.h index c9555b6bf72..e14fe0df7d3 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -23,19 +23,21 @@ #include "types.h" +namespace Stockfish { + namespace Bitbases { void init(); bool probe(Square wksq, Square wpsq, Square bksq, Color us); -} +} // namespace Stockfish::Bitbases namespace Bitboards { void init(); std::string pretty(Bitboard b); -} +} // namespace Stockfish::Bitboards constexpr Bitboard AllSquares = ~Bitboard(0); constexpr Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL; @@ -430,4 +432,6 @@ inline Square frontmost_sq(Color c, Bitboard b) { return c == WHITE ? msb(b) : lsb(b); } +} // namespace Stockfish + #endif // #ifndef BITBOARD_H_INCLUDED diff --git a/src/endgame.cpp b/src/endgame.cpp index 1489a36bd4f..a44d3a1c5e6 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -22,6 +22,8 @@ #include "endgame.h" #include "movegen.h" +namespace Stockfish { + namespace { // Used to drive the king towards the edge of the board @@ -741,3 +743,5 @@ ScaleFactor Endgame::operator()(const Position& pos) const { // it's probably at least a draw even with the pawn. return Bitbases::probe(strongKing, strongPawn, weakKing, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; } + +} // namespace Stockfish diff --git a/src/endgame.h b/src/endgame.h index 860cc8634b4..146111b9579 100644 --- a/src/endgame.h +++ b/src/endgame.h @@ -28,6 +28,7 @@ #include "position.h" #include "types.h" +namespace Stockfish { /// EndgameCode lists all supported endgame functions by corresponding codes @@ -120,4 +121,6 @@ namespace Endgames { } } +} // namespace Stockfish + #endif // #ifndef ENDGAME_H_INCLUDED diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d55ef695b75..d43b8fa76c5 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -54,7 +54,9 @@ using namespace std; -using namespace Eval::NNUE; +using namespace Stockfish::Eval::NNUE; + +namespace Stockfish { namespace Eval { @@ -1146,3 +1148,5 @@ std::string Eval::trace(const Position& pos) { return ss.str(); } + +} // namespace Stockfish diff --git a/src/evaluate.h b/src/evaluate.h index 8beca2d0a2b..6210bd5816b 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -23,6 +23,8 @@ #include "types.h" +namespace Stockfish { + class Position; namespace Eval { @@ -49,4 +51,6 @@ namespace Eval { } // namespace Eval +} // namespace Stockfish + #endif // #ifndef EVALUATE_H_INCLUDED diff --git a/src/main.cpp b/src/main.cpp index ef662468ff4..62e0ed525d7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -28,6 +28,8 @@ #include "tt.h" #include "uci.h" +using namespace Stockfish; + int main(int argc, char* argv[]) { std::cout << engine_info() << std::endl; diff --git a/src/material.cpp b/src/material.cpp index e76641d1a0a..84d7a4bd0a6 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -24,6 +24,8 @@ using namespace std; +namespace Stockfish { + namespace { #define S(mg, eg) make_score(mg, eg) @@ -223,3 +225,5 @@ Entry* probe(const Position& pos) { } } // namespace Material + +} // namespace Stockfish diff --git a/src/material.h b/src/material.h index be26425f32b..26535a535af 100644 --- a/src/material.h +++ b/src/material.h @@ -24,7 +24,7 @@ #include "position.h" #include "types.h" -namespace Material { +namespace Stockfish::Material { /// Material::Entry contains various information about a material configuration. /// It contains a material imbalance evaluation, a function pointer to a special @@ -66,6 +66,6 @@ typedef HashTable Table; Entry* probe(const Position& pos); -} // namespace Material +} // namespace Stockfish::Material #endif // #ifndef MATERIAL_H_INCLUDED diff --git a/src/misc.cpp b/src/misc.cpp index fe920140dba..834e909b981 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -61,6 +61,8 @@ typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY); using namespace std; +namespace Stockfish { + namespace { /// Version number. If Version is left empty, then compile date in the format @@ -626,3 +628,5 @@ void init(int argc, char* argv[]) { } // namespace CommandLine + +} // namespace Stockfish diff --git a/src/misc.h b/src/misc.h index 7eb75bc70ec..f834e470cbf 100644 --- a/src/misc.h +++ b/src/misc.h @@ -28,6 +28,8 @@ #include "types.h" +namespace Stockfish { + std::string engine_info(bool to_uci = false); std::string compiler_info(); void prefetch(void* addr); @@ -143,4 +145,6 @@ namespace CommandLine { extern std::string workingDirectory; // path of the working directory } +} // namespace Stockfish + #endif // #ifndef MISC_H_INCLUDED diff --git a/src/movegen.cpp b/src/movegen.cpp index c9d6a90d7d0..51df6d07e67 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -21,6 +21,8 @@ #include "movegen.h" #include "position.h" +namespace Stockfish { + namespace { template @@ -362,3 +364,5 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { return moveList; } + +} // namespace Stockfish diff --git a/src/movegen.h b/src/movegen.h index 85887a64bb4..3f895f05ad4 100644 --- a/src/movegen.h +++ b/src/movegen.h @@ -23,6 +23,8 @@ #include "types.h" +namespace Stockfish { + class Position; enum GenType { @@ -70,4 +72,6 @@ struct MoveList { ExtMove moveList[MAX_MOVES], *last; }; +} // namespace Stockfish + #endif // #ifndef MOVEGEN_H_INCLUDED diff --git a/src/movepick.cpp b/src/movepick.cpp index 0ceeb8eaf60..4ff4cff44b4 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -20,6 +20,8 @@ #include "movepick.h" +namespace Stockfish { + namespace { enum Stages { @@ -263,3 +265,5 @@ Move MovePicker::next_move(bool skipQuiets) { assert(false); return MOVE_NONE; // Silence warning } + +} // namespace Stockfish diff --git a/src/movepick.h b/src/movepick.h index ea599cda1af..c76d49572b8 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -27,6 +27,8 @@ #include "position.h" #include "types.h" +namespace Stockfish { + /// StatsEntry stores the stat table value. It is usually a number but could /// be a move or even a nested history. We use a class instead of naked value /// to directly call history update operator<<() on the entry so to use stats @@ -156,4 +158,6 @@ class MovePicker { ExtMove moves[MAX_MOVES]; }; +} // namespace Stockfish + #endif // #ifndef MOVEPICK_H_INCLUDED diff --git a/src/nnue/architectures/halfkp_256x2-32-32.h b/src/nnue/architectures/halfkp_256x2-32-32.h index a0fe2e0ad83..a6768204649 100644 --- a/src/nnue/architectures/halfkp_256x2-32-32.h +++ b/src/nnue/architectures/halfkp_256x2-32-32.h @@ -28,7 +28,7 @@ #include "../layers/affine_transform.h" #include "../layers/clipped_relu.h" -namespace Eval::NNUE { +namespace Stockfish::Eval::NNUE { // Input features used in evaluation function using RawFeatures = Features::FeatureSet< @@ -49,6 +49,6 @@ using OutputLayer = AffineTransform; using Network = Layers::OutputLayer; -} // namespace Eval::NNUE +} // namespace Stockfish::Eval::NNUE #endif // #ifndef NNUE_HALFKP_256X2_32_32_H_INCLUDED diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index fb4a502197a..5416f13e1f7 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -29,7 +29,7 @@ #include "evaluate_nnue.h" -namespace Eval::NNUE { +namespace Stockfish::Eval::NNUE { // Input feature converter LargePagePtr feature_transformer; @@ -141,4 +141,4 @@ namespace Eval::NNUE { return ReadParameters(stream); } -} // namespace Eval::NNUE +} // namespace Stockfish::Eval::NNUE diff --git a/src/nnue/evaluate_nnue.h b/src/nnue/evaluate_nnue.h index c30d7c01fbb..24aa6cc0051 100644 --- a/src/nnue/evaluate_nnue.h +++ b/src/nnue/evaluate_nnue.h @@ -25,7 +25,7 @@ #include -namespace Eval::NNUE { +namespace Stockfish::Eval::NNUE { // Hash value of evaluation function structure constexpr std::uint32_t kHashValue = @@ -54,6 +54,6 @@ namespace Eval::NNUE { template using LargePagePtr = std::unique_ptr>; -} // namespace Eval::NNUE +} // namespace Stockfish::Eval::NNUE #endif // #ifndef NNUE_EVALUATE_NNUE_H_INCLUDED diff --git a/src/nnue/features/feature_set.h b/src/nnue/features/feature_set.h index 77d2220fcf8..a3fea9c0b2c 100644 --- a/src/nnue/features/feature_set.h +++ b/src/nnue/features/feature_set.h @@ -24,7 +24,7 @@ #include "features_common.h" #include -namespace Eval::NNUE::Features { +namespace Stockfish::Eval::NNUE::Features { // Class template that represents a list of values template @@ -64,6 +64,6 @@ namespace Eval::NNUE::Features { }; -} // namespace Eval::NNUE::Features +} // namespace Stockfish::Eval::NNUE::Features #endif // #ifndef NNUE_FEATURE_SET_H_INCLUDED diff --git a/src/nnue/features/features_common.h b/src/nnue/features/features_common.h index b0073b8b6ed..118ec9532a1 100644 --- a/src/nnue/features/features_common.h +++ b/src/nnue/features/features_common.h @@ -24,7 +24,7 @@ #include "../../evaluate.h" #include "../nnue_common.h" -namespace Eval::NNUE::Features { +namespace Stockfish::Eval::NNUE::Features { class IndexList; @@ -40,6 +40,6 @@ namespace Eval::NNUE::Features { kFriend // side to move }; -} // namespace Eval::NNUE::Features +} // namespace Stockfish::Eval::NNUE::Features #endif // #ifndef NNUE_FEATURES_COMMON_H_INCLUDED diff --git a/src/nnue/features/half_kp.cpp b/src/nnue/features/half_kp.cpp index b52a45f2523..ac6317e7da6 100644 --- a/src/nnue/features/half_kp.cpp +++ b/src/nnue/features/half_kp.cpp @@ -21,7 +21,7 @@ #include "half_kp.h" #include "index_list.h" -namespace Eval::NNUE::Features { +namespace Stockfish::Eval::NNUE::Features { // Orient a square according to perspective (rotates by 180 for black) inline Square orient(Color perspective, Square s) { @@ -65,4 +65,4 @@ namespace Eval::NNUE::Features { template class HalfKP; -} // namespace Eval::NNUE::Features +} // namespace Stockfish::Eval::NNUE::Features diff --git a/src/nnue/features/half_kp.h b/src/nnue/features/half_kp.h index d203dc22abb..2461acb725a 100644 --- a/src/nnue/features/half_kp.h +++ b/src/nnue/features/half_kp.h @@ -24,7 +24,7 @@ #include "../../evaluate.h" #include "features_common.h" -namespace Eval::NNUE::Features { +namespace Stockfish::Eval::NNUE::Features { // Feature HalfKP: Combination of the position of own king // and the position of pieces other than kings @@ -54,6 +54,6 @@ namespace Eval::NNUE::Features { IndexList* removed, IndexList* added); }; -} // namespace Eval::NNUE::Features +} // namespace Stockfish::Eval::NNUE::Features #endif // #ifndef NNUE_FEATURES_HALF_KP_H_INCLUDED diff --git a/src/nnue/features/index_list.h b/src/nnue/features/index_list.h index ca3ebee5617..9f03993bede 100644 --- a/src/nnue/features/index_list.h +++ b/src/nnue/features/index_list.h @@ -24,7 +24,7 @@ #include "../../position.h" #include "../nnue_architecture.h" -namespace Eval::NNUE::Features { +namespace Stockfish::Eval::NNUE::Features { // Class template used for feature index list template @@ -59,6 +59,6 @@ namespace Eval::NNUE::Features { : public ValueList { }; -} // namespace Eval::NNUE::Features +} // namespace Stockfish::Eval::NNUE::Features #endif // NNUE_FEATURES_INDEX_LIST_H_INCLUDED diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index adf152eea5b..d2713c5aaf6 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -24,7 +24,7 @@ #include #include "../nnue_common.h" -namespace Eval::NNUE::Layers { +namespace Stockfish::Eval::NNUE::Layers { // Affine transformation layer template @@ -459,6 +459,6 @@ namespace Eval::NNUE::Layers { #endif }; -} // namespace Eval::NNUE::Layers +} // namespace Stockfish::Eval::NNUE::Layers #endif // #ifndef NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index 3ed41ee5633..a10e3e482b7 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -23,7 +23,7 @@ #include "../nnue_common.h" -namespace Eval::NNUE::Layers { +namespace Stockfish::Eval::NNUE::Layers { // Clipped ReLU template @@ -161,6 +161,6 @@ namespace Eval::NNUE::Layers { PreviousLayer previous_layer_; }; -} // namespace Eval::NNUE::Layers +} // namespace Stockfish::Eval::NNUE::Layers #endif // NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED diff --git a/src/nnue/layers/input_slice.h b/src/nnue/layers/input_slice.h index efdf07250a0..43b06eec5a9 100644 --- a/src/nnue/layers/input_slice.h +++ b/src/nnue/layers/input_slice.h @@ -23,7 +23,7 @@ #include "../nnue_common.h" -namespace Eval::NNUE::Layers { +namespace Stockfish::Eval::NNUE::Layers { // Input layer template @@ -63,6 +63,6 @@ class InputSlice { private: }; -} // namespace Layers +} // namespace Stockfish::Eval::NNUE::Layers #endif // #ifndef NNUE_LAYERS_INPUT_SLICE_H_INCLUDED diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index 6b4390f9216..55fafa138fc 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -23,7 +23,7 @@ #include "nnue_architecture.h" -namespace Eval::NNUE { +namespace Stockfish::Eval::NNUE { // The accumulator of a StateInfo without parent is set to the INIT state enum AccumulatorState { EMPTY, COMPUTED, INIT }; @@ -35,6 +35,6 @@ namespace Eval::NNUE { AccumulatorState state[2]; }; -} // namespace Eval::NNUE +} // namespace Stockfish::Eval::NNUE #endif // NNUE_ACCUMULATOR_H_INCLUDED diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index ad5be0064db..1680368edb5 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -24,7 +24,7 @@ // Defines the network structure #include "architectures/halfkp_256x2-32-32.h" -namespace Eval::NNUE { +namespace Stockfish::Eval::NNUE { static_assert(kTransformedFeatureDimensions % kMaxSimdWidth == 0, ""); static_assert(Network::kOutputDimensions == 1, ""); @@ -33,6 +33,6 @@ namespace Eval::NNUE { // Trigger for full calculation instead of difference calculation constexpr auto kRefreshTriggers = RawFeatures::kRefreshTriggers; -} // namespace Eval::NNUE +} // namespace Stockfish::Eval::NNUE #endif // #ifndef NNUE_ARCHITECTURE_H_INCLUDED diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index 33e58745e7f..09a152a53c9 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -43,7 +43,7 @@ #include #endif -namespace Eval::NNUE { +namespace Stockfish::Eval::NNUE { // Version of the evaluation file constexpr std::uint32_t kVersion = 0x7AF32F16u; @@ -127,6 +127,6 @@ namespace Eval::NNUE { return result; } -} // namespace Eval::NNUE +} // namespace Stockfish::Eval::NNUE #endif // #ifndef NNUE_COMMON_H_INCLUDED diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 2641321e6cb..1e0b0e6da55 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -27,7 +27,7 @@ #include // std::memset() -namespace Eval::NNUE { +namespace Stockfish::Eval::NNUE { // If vector instructions are enabled, we update and refresh the // accumulator tile by tile such that each tile fits in the CPU's @@ -412,6 +412,6 @@ namespace Eval::NNUE { WeightType weights_[kHalfDimensions * kInputDimensions]; }; -} // namespace Eval::NNUE +} // namespace Stockfish::Eval::NNUE #endif // #ifndef NNUE_FEATURE_TRANSFORMER_H_INCLUDED diff --git a/src/pawns.cpp b/src/pawns.cpp index cd4d4e45212..9a0610a0f53 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -24,6 +24,8 @@ #include "position.h" #include "thread.h" +namespace Stockfish { + namespace { #define V Value @@ -298,3 +300,5 @@ template Score Entry::do_king_safety(const Position& pos); template Score Entry::do_king_safety(const Position& pos); } // namespace Pawns + +} // namespace Stockfish diff --git a/src/pawns.h b/src/pawns.h index 888bf9900f4..124619d66a0 100644 --- a/src/pawns.h +++ b/src/pawns.h @@ -23,7 +23,7 @@ #include "position.h" #include "types.h" -namespace Pawns { +namespace Stockfish::Pawns { /// Pawns::Entry contains various information about a pawn structure. A lookup /// to the pawn hash table (performed by calling the probe function) returns a @@ -65,6 +65,6 @@ typedef HashTable Table; Entry* probe(const Position& pos); -} // namespace Pawns +} // namespace Stockfish::Pawns #endif // #ifndef PAWNS_H_INCLUDED diff --git a/src/position.cpp b/src/position.cpp index 8f9b7eee89e..17b165b9afb 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -34,6 +34,8 @@ using std::string; +namespace Stockfish { + namespace Zobrist { Key psq[PIECE_NB][SQUARE_NB]; @@ -1338,3 +1340,5 @@ bool Position::pos_is_ok() const { return true; } + +} // namespace Stockfish diff --git a/src/position.h b/src/position.h index e9803756609..4ab3761fd4a 100644 --- a/src/position.h +++ b/src/position.h @@ -31,6 +31,7 @@ #include "nnue/nnue_accumulator.h" +namespace Stockfish { /// StateInfo struct stores information needed to restore a Position object to /// its previous state when we retract a move. Whenever a move is made on the @@ -423,4 +424,6 @@ inline StateInfo* Position::state() const { return st; } +} // namespace Stockfish + #endif // #ifndef POSITION_H_INCLUDED diff --git a/src/psqt.cpp b/src/psqt.cpp index cfade2951ff..33a3e00c91d 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -24,6 +24,7 @@ #include "bitboard.h" #include "types.h" +namespace Stockfish { namespace { @@ -126,3 +127,5 @@ void init() { } } // namespace PSQT + +} // namespace Stockfish diff --git a/src/psqt.h b/src/psqt.h index 8b4fd6eb8c2..7abb14830c1 100644 --- a/src/psqt.h +++ b/src/psqt.h @@ -24,7 +24,7 @@ #include "types.h" -namespace PSQT +namespace Stockfish::PSQT { extern Score psq[PIECE_NB][SQUARE_NB]; @@ -32,7 +32,7 @@ extern Score psq[PIECE_NB][SQUARE_NB]; // Fill psqt array from a set of internally linked parameters extern void init(); -} // namespace PSQT +} // namespace Stockfish::PSQT #endif // PSQT_H_INCLUDED diff --git a/src/search.cpp b/src/search.cpp index b3bd2f0371f..4e2ec095cb3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -35,6 +35,8 @@ #include "uci.h" #include "syzygy/tbprobe.h" +namespace Stockfish { + namespace Search { LimitsType Limits; @@ -422,7 +424,7 @@ void Thread::search() { while (true) { Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt - searchAgainCounter); - bestValue = ::search(rootPos, ss, alpha, beta, adjustedDepth, false); + bestValue = Stockfish::search(rootPos, ss, alpha, beta, adjustedDepth, false); // Bring the best move to the front. It is critical that sorting // is done with a stable algorithm because all the values but the @@ -2034,3 +2036,5 @@ void Tablebases::rank_root_moves(Position& pos, Search::RootMoves& rootMoves) { m.tbRank = 0; } } + +} // namespace Stockfish diff --git a/src/search.h b/src/search.h index 5e51c18e043..6f9fbd05277 100644 --- a/src/search.h +++ b/src/search.h @@ -25,6 +25,8 @@ #include "movepick.h" #include "types.h" +namespace Stockfish { + class Position; namespace Search { @@ -107,4 +109,6 @@ void clear(); } // namespace Search +} // namespace Stockfish + #endif // #ifndef SEARCH_H_INCLUDED diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 115815e121a..5cfd38e5577 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -50,9 +50,11 @@ #include #endif -using namespace Tablebases; +using namespace Stockfish::Tablebases; -int Tablebases::MaxCardinality; +int Stockfish::Tablebases::MaxCardinality; + +namespace Stockfish { namespace { @@ -1610,3 +1612,5 @@ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves) { return true; } + +} // namespace Stockfish diff --git a/src/syzygy/tbprobe.h b/src/syzygy/tbprobe.h index cefd39ce8de..56734af9bdc 100644 --- a/src/syzygy/tbprobe.h +++ b/src/syzygy/tbprobe.h @@ -23,7 +23,7 @@ #include "../search.h" -namespace Tablebases { +namespace Stockfish::Tablebases { enum WDLScore { WDLLoss = -2, // Loss @@ -73,6 +73,6 @@ inline std::ostream& operator<<(std::ostream& os, const ProbeState v) { return os; } -} +} // namespace Stockfish::Tablebases #endif diff --git a/src/thread.cpp b/src/thread.cpp index a12c0bccc11..3ef73dfa322 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -26,6 +26,8 @@ #include "syzygy/tbprobe.h" #include "tt.h" +namespace Stockfish { + ThreadPool Threads; // Global object @@ -258,3 +260,5 @@ void ThreadPool::wait_for_search_finished() const { if (th != front()) th->wait_for_search_finished(); } + +} // namespace Stockfish diff --git a/src/thread.h b/src/thread.h index 585f208813a..2b3dea0d7cc 100644 --- a/src/thread.h +++ b/src/thread.h @@ -32,6 +32,7 @@ #include "search.h" #include "thread_win32_osx.h" +namespace Stockfish { /// Thread class keeps together all the thread-related stuff. We use /// per-thread pawn and material hash tables so that once we get a @@ -128,4 +129,6 @@ struct ThreadPool : public std::vector { extern ThreadPool Threads; +} // namespace Stockfish + #endif // #ifndef THREAD_H_INCLUDED diff --git a/src/thread_win32_osx.h b/src/thread_win32_osx.h index a0e4d1994f8..a21674cc68f 100644 --- a/src/thread_win32_osx.h +++ b/src/thread_win32_osx.h @@ -31,6 +31,8 @@ #include +namespace Stockfish { + static const size_t TH_STACK_SIZE = 8 * 1024 * 1024; template > @@ -57,10 +59,16 @@ class NativeThread { void join() { pthread_join(thread, NULL); } }; +} // namespace Stockfish + #else // Default case: use STL classes +namespace Stockfish { + typedef std::thread NativeThread; +} // namespace Stockfish + #endif #endif // #ifndef THREAD_WIN32_OSX_H_INCLUDED diff --git a/src/timeman.cpp b/src/timeman.cpp index fc4fbaacd46..f742d1e4422 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -24,6 +24,8 @@ #include "timeman.h" #include "uci.h" +namespace Stockfish { + TimeManagement Time; // Our global time management object @@ -95,3 +97,5 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { if (Options["Ponder"]) optimumTime += optimumTime / 4; } + +} // namespace Stockfish diff --git a/src/timeman.h b/src/timeman.h index 55a68de4771..b1878d65f25 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -23,6 +23,8 @@ #include "search.h" #include "thread.h" +namespace Stockfish { + /// The TimeManagement class computes the optimal time to think depending on /// the maximum available time, the game move number and other parameters. @@ -44,4 +46,6 @@ class TimeManagement { extern TimeManagement Time; +} // namespace Stockfish + #endif // #ifndef TIMEMAN_H_INCLUDED diff --git a/src/tt.cpp b/src/tt.cpp index cb5af5c8590..1f495ca9d12 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -26,6 +26,8 @@ #include "tt.h" #include "uci.h" +namespace Stockfish { + TranspositionTable TT; // Our global transposition table /// TTEntry::save() populates the TTEntry with a new node's data, possibly @@ -156,3 +158,5 @@ int TranspositionTable::hashfull() const { return cnt / ClusterSize; } + +} // namespace Stockfish diff --git a/src/tt.h b/src/tt.h index a750b6c4657..d915d92e43f 100644 --- a/src/tt.h +++ b/src/tt.h @@ -22,6 +22,8 @@ #include "misc.h" #include "types.h" +namespace Stockfish { + /// TTEntry struct is the 10 bytes transposition table entry, defined as below: /// /// key 16 bit @@ -100,4 +102,6 @@ class TranspositionTable { extern TranspositionTable TT; +} // namespace Stockfish + #endif // #ifndef TT_H_INCLUDED diff --git a/src/tune.cpp b/src/tune.cpp index 424bdac85dc..d9618efc9c7 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -26,6 +26,8 @@ using std::string; +namespace Stockfish { + bool Tune::update_on_last; const UCI::Option* LastOption = nullptr; BoolConditions Conditions; @@ -126,6 +128,8 @@ void BoolConditions::set() { sync_cout << binary[i] << sync_endl; } +} // namespace Stockfish + // Init options with tuning session results instead of default values. Useful to // get correct bench signature after a tuning session or to test tuned values. @@ -138,7 +142,11 @@ void BoolConditions::set() { #include +namespace Stockfish { + void Tune::read_results() { /* ...insert your values here... */ } + +} // namespace Stockfish diff --git a/src/tune.h b/src/tune.h index c2cd0c97e39..c904c09dc94 100644 --- a/src/tune.h +++ b/src/tune.h @@ -24,6 +24,8 @@ #include #include +namespace Stockfish { + typedef std::pair Range; // Option's min-max values typedef Range (RangeFun) (int); @@ -190,4 +192,6 @@ class Tune { #define TUNE_CONDITIONS() int UNIQUE(c, __LINE__) = (Conditions.init(__COUNTER__), 0); \ TUNE(Conditions, set_conditions) +} // namespace Stockfish + #endif // #ifndef TUNE_H_INCLUDED diff --git a/src/types.h b/src/types.h index d270384eb99..efebce1a7b6 100644 --- a/src/types.h +++ b/src/types.h @@ -83,6 +83,8 @@ # define pext(b, m) 0 #endif +namespace Stockfish { + #ifdef USE_POPCNT constexpr bool HasPopCnt = true; #else @@ -482,6 +484,8 @@ constexpr Key make_key(uint64_t seed) { return seed * 6364136223846793005ULL + 1442695040888963407ULL; } +} // namespace Stockfish + #endif // #ifndef TYPES_H_INCLUDED #include "tune.h" // Global visibility to tuning setup diff --git a/src/uci.cpp b/src/uci.cpp index b3017e91069..47a8824e803 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -34,6 +34,8 @@ using namespace std; +namespace Stockfish { + extern vector setup_bench(const Position&, istream&); namespace { @@ -369,3 +371,5 @@ Move UCI::to_move(const Position& pos, string& str) { return MOVE_NONE; } + +} // namespace Stockfish diff --git a/src/uci.h b/src/uci.h index edcfcadece5..d3160109d6e 100644 --- a/src/uci.h +++ b/src/uci.h @@ -24,6 +24,8 @@ #include "types.h" +namespace Stockfish { + class Position; namespace UCI { @@ -78,4 +80,6 @@ Move to_move(const Position& pos, std::string& str); extern UCI::OptionsMap Options; +} // namespace Stockfish + #endif // #ifndef UCI_H_INCLUDED diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 03f377b3994..d59c010017e 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -31,6 +31,8 @@ using std::string; +namespace Stockfish { + UCI::OptionsMap Options; // Global object namespace UCI { @@ -190,3 +192,5 @@ Option& Option::operator=(const string& v) { } } // namespace UCI + +} // namespace Stockfish diff --git a/tests/instrumented.sh b/tests/instrumented.sh index 03e9c9de093..bfb50e94c19 100755 --- a/tests/instrumented.sh +++ b/tests/instrumented.sh @@ -39,16 +39,16 @@ case $1 in threads="2" cat << EOF > tsan.supp -race:TTEntry::move -race:TTEntry::depth -race:TTEntry::bound -race:TTEntry::save -race:TTEntry::value -race:TTEntry::eval -race:TTEntry::is_pv - -race:TranspositionTable::probe -race:TranspositionTable::hashfull +race:Stockfish::TTEntry::move +race:Stockfish::TTEntry::depth +race:Stockfish::TTEntry::bound +race:Stockfish::TTEntry::save +race:Stockfish::TTEntry::value +race:Stockfish::TTEntry::eval +race:Stockfish::TTEntry::is_pv + +race:Stockfish::TranspositionTable::probe +race:Stockfish::TranspositionTable::hashfull EOF From d4b864ff126e4a5784c9d7f636be057c247738f1 Mon Sep 17 00:00:00 2001 From: noobpwnftw Date: Wed, 3 Mar 2021 22:30:23 +0800 Subject: [PATCH 0514/1766] Do not try to use large pages on 32 bit Windows. verified to work on windows XP. fixes #3379 closes https://github.com/official-stockfish/Stockfish/pull/3380 No functional change. --- src/misc.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index 834e909b981..7600fc114c2 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -362,7 +362,7 @@ void std_aligned_free(void* ptr) { /// aligned_large_pages_alloc() will return suitably aligned memory, if possible using large pages. #if defined(_WIN32) - +#if defined(_WIN64) static void* aligned_large_pages_alloc_win(size_t allocSize) { HANDLE hProcessToken { }; @@ -407,15 +407,20 @@ static void* aligned_large_pages_alloc_win(size_t allocSize) { return mem; } +#endif void* aligned_large_pages_alloc(size_t allocSize) { +#if defined(_WIN64) // Try to allocate large pages void* mem = aligned_large_pages_alloc_win(allocSize); // Fall back to regular, page aligned, allocation if necessary if (!mem) mem = VirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); +#else + void* mem = VirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); +#endif return mem; } From 5346f1c6c72e46d66bb4c21259f8c06096c63034 Mon Sep 17 00:00:00 2001 From: mattginsberg Date: Sun, 28 Feb 2021 07:59:07 -0800 Subject: [PATCH 0515/1766] Deal with commented lines in UCI input commands starting with '#' as the first character will be ignored closes https://github.com/official-stockfish/Stockfish/pull/3378 No functional change --- src/uci.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uci.cpp b/src/uci.cpp index 47a8824e803..051ff2e02f4 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -277,7 +277,7 @@ void UCI::loop(int argc, char* argv[]) { else if (token == "d") sync_cout << pos << sync_endl; else if (token == "eval") trace_eval(pos); else if (token == "compiler") sync_cout << compiler_info() << sync_endl; - else + else if (!token.empty() && token[0] != '#') sync_cout << "Unknown command: " << cmd << sync_endl; } while (token != "quit" && argc == 1); // Command line args are one-shot From b74274628c052cc910e36202b88bc5f81724d78c Mon Sep 17 00:00:00 2001 From: bmc4 Date: Fri, 5 Mar 2021 08:57:43 -0300 Subject: [PATCH 0516/1766] Use Bitboard over Square in movegen It uses pos.checkers() on target when movegen is the type of EVASION. It simplify the code. And it's also expected a slightly speed up, because Bitboard is more direct when doing bitwise. Passed STC: LLR: 2.93 (-2.94,2.94) {-1.25,0.25} Total: 28176 W: 2506 L: 2437 D: 23233 Ptnml(0-2): 80, 1904, 10063, 1949, 92 https://tests.stockfishchess.org/tests/view/60421d18ddcba5f0627bb6a9 Passed LTC: LLR: 2.93 (-2.94,2.94) {-0.75,0.25} Total: 9704 W: 402 L: 341 D: 8961 Ptnml(0-2): 3, 279, 4230, 334, 6 https://tests.stockfishchess.org/tests/view/60422823ddcba5f0627bb6ae closes https://github.com/official-stockfish/Stockfish/pull/3383 No functional change --- src/movegen.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index 51df6d07e67..c5d76afa43b 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -223,11 +223,8 @@ namespace { target = ~pos.pieces(); break; case EVASIONS: - { - Square checksq = lsb(pos.checkers()); - target = between_bb(pos.square(Us), checksq) | checksq; + target = between_bb(pos.square(Us), lsb(pos.checkers())) | pos.checkers(); break; - } case NON_EVASIONS: target = ~pos.pieces(Us); break; From f3b296c2e2061951d366edfbd5287f336e865553 Mon Sep 17 00:00:00 2001 From: Topologist Date: Mon, 8 Mar 2021 19:46:41 +0100 Subject: [PATCH 0517/1766] Change advanced pawn push threshold A pawn push is now considered to be "advanced" if the relative destination rank is > 6 (previously it was > 5). This affects the search heuristic. Also remove an assert concerning en passant moves in qsearch(). STC: LLR: 2.97 (-2.94,2.94) {-0.25,1.25} Total: 46744 W: 4224 L: 4040 D: 38480 Ptnml(0-2): 165, 3206, 16451, 3380, 170 https://tests.stockfishchess.org/tests/view/604746082433018de7a3872e LTC: LLR: 2.94 (-2.94,2.94) {0.25,1.25} Total: 107840 W: 4198 L: 3892 D: 99750 Ptnml(0-2): 58, 3472, 46557, 3772, 61 https://tests.stockfishchess.org/tests/view/60475eae2433018de7a38737 Closes https://github.com/official-stockfish/Stockfish/pull/3389 Bench: 4796780 --- AUTHORS | 1 + src/position.h | 2 +- src/search.cpp | 4 +--- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index 3ef7d1b13ec..662cc6ecef9 100644 --- a/AUTHORS +++ b/AUTHORS @@ -168,6 +168,7 @@ Sergio Vieri (sergiovieri) sf-x Shane Booth (shane31) Shawn Varghese (xXH4CKST3RXx) +Siad Daboul (Topologist) Stefan Geschwentner (locutus2) Stefano Cardanobile (Stefano80) Steinar Gunderson (sesse) diff --git a/src/position.h b/src/position.h index 4ab3761fd4a..a7654aa1a54 100644 --- a/src/position.h +++ b/src/position.h @@ -312,7 +312,7 @@ inline bool Position::pawn_passed(Color c, Square s) const { inline bool Position::advanced_pawn_push(Move m) const { return type_of(moved_piece(m)) == PAWN - && relative_rank(sideToMove, to_sq(m)) > RANK_5; + && relative_rank(sideToMove, to_sq(m)) > RANK_6; } inline int Position::pawns_on_same_color_squares(Color c, Square s) const { diff --git a/src/search.cpp b/src/search.cpp index 4e2ec095cb3..fa592a85994 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1575,15 +1575,13 @@ namespace { moveCount++; - // Futility pruning + // Futility pruning and moveCount pruning if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY && !givesCheck && futilityBase > -VALUE_KNOWN_WIN && !pos.advanced_pawn_push(move)) { - assert(type_of(move) != EN_PASSANT); // Due to !pos.advanced_pawn_push - // moveCount pruning if (moveCount > 2) continue; From 939395729c78dd43816826ffdb0a61f33a833e9f Mon Sep 17 00:00:00 2001 From: bmc4 Date: Tue, 16 Mar 2021 20:51:31 +0100 Subject: [PATCH 0518/1766] Introduce least_significant_square_bb() Introducing least_significant_square_bb(). It is a function that returns a value equal to square_bb(lsb(bb)), but it uses fewer instruction. It should speed up more on older processors like armv7-a Clang. Passed STC: LLR: 2.93 (-2.94,2.94) {-0.25,1.25} Total: 213200 W: 19171 L: 18753 D: 175276 Ptnml(0-2): 680, 14513, 75831, 14861, 715 https://tests.stockfishchess.org/tests/view/604bc7632433018de7a38982 Closes https://github.com/official-stockfish/Stockfish/pull/3391 No functional change --- src/bitboard.h | 7 +++++++ src/position.cpp | 10 +++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/bitboard.h b/src/bitboard.h index e14fe0df7d3..1b6af3ead1a 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -414,6 +414,13 @@ inline Square msb(Bitboard b) { #endif +/// least_significant_square_bb() returns the bitboard of the least significant +/// square of a non-zero bitboard. It is equivalent to square_bb(lsb(bb)). + +inline Bitboard least_significant_square_bb(Bitboard b) { + assert(b); + return b & -b; +} /// pop_lsb() finds and clears the least significant bit in a non-zero bitboard diff --git a/src/position.cpp b/src/position.cpp index 17b165b9afb..f4739413da4 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1109,7 +1109,7 @@ bool Position::see_ge(Move m, Value threshold) const { if ((swap = PawnValueMg - swap) < res) break; - occupied ^= lsb(bb); + occupied ^= least_significant_square_bb(bb); attackers |= attacks_bb(to, occupied) & pieces(BISHOP, QUEEN); } @@ -1118,7 +1118,7 @@ bool Position::see_ge(Move m, Value threshold) const { if ((swap = KnightValueMg - swap) < res) break; - occupied ^= lsb(bb); + occupied ^= least_significant_square_bb(bb); } else if ((bb = stmAttackers & pieces(BISHOP))) @@ -1126,7 +1126,7 @@ bool Position::see_ge(Move m, Value threshold) const { if ((swap = BishopValueMg - swap) < res) break; - occupied ^= lsb(bb); + occupied ^= least_significant_square_bb(bb); attackers |= attacks_bb(to, occupied) & pieces(BISHOP, QUEEN); } @@ -1135,7 +1135,7 @@ bool Position::see_ge(Move m, Value threshold) const { if ((swap = RookValueMg - swap) < res) break; - occupied ^= lsb(bb); + occupied ^= least_significant_square_bb(bb); attackers |= attacks_bb(to, occupied) & pieces(ROOK, QUEEN); } @@ -1144,7 +1144,7 @@ bool Position::see_ge(Move m, Value threshold) const { if ((swap = QueenValueMg - swap) < res) break; - occupied ^= lsb(bb); + occupied ^= least_significant_square_bb(bb); attackers |= (attacks_bb(to, occupied) & pieces(BISHOP, QUEEN)) | (attacks_bb(to, occupied) & pieces(ROOK , QUEEN)); } From 4b509559fbabe8a41cb8387c71d07bb1c7b78d6f Mon Sep 17 00:00:00 2001 From: bmc4 Date: Mon, 15 Mar 2021 19:52:45 -0300 Subject: [PATCH 0519/1766] Simplify move generation (1/2) STC: LLR: 2.95 (-2.94,2.94) {-1.25,0.25} Total: 29792 W: 2611 L: 2545 D: 24636 Ptnml(0-2): 94, 1982, 10659, 2086, 75 https://tests.stockfishchess.org/tests/view/604fe5b62433018de7a38ba8 LTC: LLR: 2.92 (-2.94,2.94) {-0.75,0.25} Total: 22040 W: 826 L: 777 D: 20437 Ptnml(0-2): 8, 646, 9664, 693, 9 https://tests.stockfishchess.org/tests/view/604fec892433018de7a38bac Closes https://github.com/official-stockfish/Stockfish/pull/3399 No functional change --- src/movegen.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index c5d76afa43b..d8c4370ac83 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -182,17 +182,12 @@ namespace { Bitboard bb = piecesToMove & pos.pieces(Pt); - if (!bb) - return moveList; - - [[maybe_unused]] const Bitboard checkSquares = pos.check_squares(Pt); - while (bb) { Square from = pop_lsb(&bb); Bitboard b = attacks_bb(from, pos.pieces()) & target; if constexpr (Checks) - b &= checkSquares; + b &= pos.check_squares(Pt); while (b) *moveList++ = make_move(from, pop_lsb(&b)); From 830f597134bc942554283833623f12aa970bcad6 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Tue, 16 Mar 2021 12:21:24 -0300 Subject: [PATCH 0520/1766] Simplify move generation (2/2) STC: LLR: 2.97 (-2.94,2.94) {-1.25,0.25} Total: 39352 W: 3551 L: 3493 D: 32308 Ptnml(0-2): 143, 2695, 13928, 2781, 129 https://tests.stockfishchess.org/tests/view/6050007a2433018de7a38bbb LTC: LLR: 2.96 (-2.94,2.94) {-0.75,0.25} Total: 44944 W: 1629 L: 1596 D: 41719 Ptnml(0-2): 22, 1319, 19762, 1342, 27 https://tests.stockfishchess.org/tests/view/60500e892433018de7a38bc4 Closes https://github.com/official-stockfish/Stockfish/pull/3399 No functional change --- src/movegen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index d8c4370ac83..5dbc37ced39 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -63,7 +63,7 @@ namespace { Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB; Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB; - Bitboard enemies = (Type == EVASIONS ? pos.pieces(Them) & target: + Bitboard enemies = (Type == EVASIONS ? pos.checkers(): Type == CAPTURES ? target : pos.pieces(Them)); // Single and double pawn pushes, no promotions From d58e83695f1dbe5bb75ca9e5d6775757b5fd5f7a Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Thu, 11 Mar 2021 14:51:20 +0300 Subject: [PATCH 0521/1766] Remove advanced_pawn_push() Continuation of work by @topologist: we now do futility pruning and movecount pruning in qsearch() for pawn pushes up to the 7th rank. So the condition to avoid the pruning is if the move is a promotion or not. This allows to get rid of the advanced_pawn_push() function in position.h alltogether. Passed STC https://tests.stockfishchess.org/tests/view/6048c5842433018de7a387e6 LLR: 2.93 (-2.94,2.94) {-1.25,0.25} Total: 34424 W: 3081 L: 3015 D: 28328 Ptnml(0-2): 110, 2442, 12052, 2488, 120 Passed LTC https://tests.stockfishchess.org/tests/view/6048f7d22433018de7a387f0 LLR: 2.94 (-2.94,2.94) {-0.75,0.25} Total: 142024 W: 5170 L: 5202 D: 131652 Ptnml(0-2): 50, 4678, 61613, 4596, 75 Closes https://github.com/official-stockfish/Stockfish/pull/3390 Bench: 4339126 --- src/position.h | 6 ------ src/search.cpp | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/position.h b/src/position.h index a7654aa1a54..d470ef908e1 100644 --- a/src/position.h +++ b/src/position.h @@ -128,7 +128,6 @@ class Position { bool capture(Move m) const; bool capture_or_promotion(Move m) const; bool gives_check(Move m) const; - bool advanced_pawn_push(Move m) const; Piece moved_piece(Move m) const; Piece captured_piece() const; @@ -310,11 +309,6 @@ inline bool Position::pawn_passed(Color c, Square s) const { return !(pieces(~c, PAWN) & passed_pawn_span(c, s)); } -inline bool Position::advanced_pawn_push(Move m) const { - return type_of(moved_piece(m)) == PAWN - && relative_rank(sideToMove, to_sq(m)) > RANK_6; -} - inline int Position::pawns_on_same_color_squares(Color c, Square s) const { return popcount(pieces(c, PAWN) & ((DarkSquares & s) ? DarkSquares : ~DarkSquares)); } diff --git a/src/search.cpp b/src/search.cpp index fa592a85994..af7f801ffe2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1579,7 +1579,7 @@ namespace { if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY && !givesCheck && futilityBase > -VALUE_KNOWN_WIN - && !pos.advanced_pawn_push(move)) + && type_of(move) != PROMOTION) { if (moveCount > 2) From 50890616591443ab06faa0927747bf14c8d450e3 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Mon, 15 Mar 2021 16:06:42 -0300 Subject: [PATCH 0522/1766] Change definition of between_bb() We remark that in current master, most of our use cases for between_bb() can be optimized if the second parameter of the function is added to the segment. So we change the definition of between_bb(s1, s2) such that it excludes s1 but includes s2. We also use a precomputed array for between_bb() for another small speed gain (see https://tests.stockfishchess.org/tests/view/604d09f72433018de7a389fb). Passed STC: LLR: 2.96 (-2.94,2.94) {-0.25,1.25} Total: 18736 W: 1746 L: 1607 D: 15383 Ptnml(0-2): 61, 1226, 6644, 1387, 50 https://tests.stockfishchess.org/tests/view/60428c84ddcba5f0627bb6e4 Yellow LTC: LTC: LLR: -3.00 (-2.94,2.94) {0.25,1.25} Total: 39144 W: 1431 L: 1413 D: 36300 Ptnml(0-2): 13, 1176, 17184, 1178, 21 https://tests.stockfishchess.org/tests/view/605128702433018de7a38ca1 Closes https://github.com/official-stockfish/Stockfish/pull/3397 --------- Verified for correctness by running perft on the following position: ./stockfish position fen 4rrk1/1p1nq3/p7/2p1P1pp/3P2bp/3Q1Bn1/PPPB4/1K2R1NR w - - 40 21 go perft 6 Nodes searched: 6136386434 -------- No functional change --- src/bitboard.cpp | 9 ++++++++- src/bitboard.h | 20 ++++++++++++-------- src/movegen.cpp | 6 +++--- src/position.cpp | 8 ++++---- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/bitboard.cpp b/src/bitboard.cpp index a2021449123..2da4d728ee7 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -29,6 +29,7 @@ uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; Bitboard SquareBB[SQUARE_NB]; Bitboard LineBB[SQUARE_NB][SQUARE_NB]; +Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; @@ -107,8 +108,14 @@ void Bitboards::init() { for (PieceType pt : { BISHOP, ROOK }) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) + { if (PseudoAttacks[pt][s1] & s2) - LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2; + { + LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2; + BetweenBB[s1][s2] = (attacks_bb(pt, s1, square_bb(s2)) & attacks_bb(pt, s2, square_bb(s1))); + } + BetweenBB[s1][s2] |= s2; + } } } diff --git a/src/bitboard.h b/src/bitboard.h index 1b6af3ead1a..70835e8eeff 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -75,6 +75,7 @@ extern uint8_t PopCnt16[1 << 16]; extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; extern Bitboard SquareBB[SQUARE_NB]; +extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; extern Bitboard LineBB[SQUARE_NB][SQUARE_NB]; extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; @@ -215,19 +216,22 @@ inline Bitboard line_bb(Square s1, Square s2) { } -/// between_bb() returns a bitboard representing squares that are linearly -/// between the two given squares (excluding the given squares). If the given -/// squares are not on a same file/rank/diagonal, we return 0. For instance, -/// between_bb(SQ_C4, SQ_F7) will return a bitboard with squares D5 and E6. +/// between_bb(s1, s2) returns a bitboard representing the squares in the semi-open +/// segment between the squares s1 and s2 (excluding s1 but including s2). If the +/// given squares are not on a same file/rank/diagonal, it returns s2. For instance, +/// between_bb(SQ_C4, SQ_F7) will return a bitboard with squares D5, E6 and F7, but +/// between_bb(SQ_E6, SQ_F8) will return a bitboard with the square F8. This trick +/// allows to generate non-king evasion moves faster: the defending piece must either +/// interpose itself to cover the check or capture the checking piece. inline Bitboard between_bb(Square s1, Square s2) { - Bitboard b = line_bb(s1, s2) & ((AllSquares << s1) ^ (AllSquares << s2)); - return b & (b - 1); //exclude lsb + assert(is_ok(s1) && is_ok(s2)); + return BetweenBB[s1][s2]; } -/// forward_ranks_bb() returns a bitboard representing the squares on the ranks -/// in front of the given one, from the point of view of the given color. For instance, +/// forward_ranks_bb() returns a bitboard representing the squares on the ranks in +/// front of the given one, from the point of view of the given color. For instance, /// forward_ranks_bb(BLACK, SQ_D3) will return the 16 squares on ranks 1 and 2. constexpr Bitboard forward_ranks_bb(Color c, Square s) { diff --git a/src/movegen.cpp b/src/movegen.cpp index 5dbc37ced39..742dbf40413 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -158,7 +158,7 @@ namespace { { assert(rank_of(pos.ep_square()) == relative_rank(Us, RANK_6)); - // An en passant capture cannot resolve a discovered check. + // An en passant capture cannot resolve a discovered check if (Type == EVASIONS && (target & (pos.ep_square() + Up))) return moveList; @@ -218,7 +218,7 @@ namespace { target = ~pos.pieces(); break; case EVASIONS: - target = between_bb(pos.square(Us), lsb(pos.checkers())) | pos.checkers(); + target = between_bb(pos.square(Us), lsb(pos.checkers())); break; case NON_EVASIONS: target = ~pos.pieces(Us); @@ -329,7 +329,7 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { if (more_than_one(pos.checkers())) return moveList; // Double check, only a king move can save the day - // Generate blocking evasions or captures of the checking piece + // Generate blocking interpositions or captures of the checking piece return us == WHITE ? generate_all(pos, moveList) : generate_all(pos, moveList); } diff --git a/src/position.cpp b/src/position.cpp index f4739413da4..772e45454c6 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -320,7 +320,7 @@ void Position::set_castling_right(Color c, Square rfrom) { 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) + castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto)) & ~(kfrom | rfrom); } @@ -613,8 +613,8 @@ bool Position::pseudo_legal(const Move m) const { if (more_than_one(checkers())) return false; - // Our move must be a blocking evasion or a capture of the checking piece - if (!((between_bb(lsb(checkers()), square(us)) | checkers()) & to)) + // Our move must be a blocking interposition or a capture of the checking piece + if (!(between_bb(square(us), lsb(checkers())) & to)) return false; } // In case of king moves under check we have to remove king so as to catch @@ -1218,7 +1218,7 @@ bool Position::has_game_cycle(int ply) const { Square s1 = from_sq(move); Square s2 = to_sq(move); - if (!(between_bb(s1, s2) & pieces())) + if (!((between_bb(s1, s2) ^ s2) & pieces())) { if (ply > i) return true; From ace9632c6713e346ccca73b3e2edc046c1f5527c Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Mon, 15 Mar 2021 19:05:01 +0100 Subject: [PATCH 0523/1766] Add a specific FRC correction from classical to NNUE our net currently is not trained on FRC games, and so doesn't know about the important pattern of a bishop that is cornered in FRC. This patch introduces a term we have in the classical evaluation for this case, and adds it to the NNUE eval. Since fishtest doesn't support FRC right now, the patch was tested locally at STC conditions, starting from the book of FRC starting positions. Score of master vs patch: 993 - 2226 - 6781 [0.438] 10000 Which corresponds to approximately 40 Elo The patch passes non-regression testing for traditional chess (where it adds one branch). passed STC: https://tests.stockfishchess.org/tests/view/604fa2532433018de7a38b67 LLR: 2.95 (-2.94,2.94) {-1.25,0.25} Total: 30560 W: 2701 L: 2636 D: 25223 Ptnml(0-2): 88, 2056, 10921, 2133, 82 passed STC also in an earlier version: https://tests.stockfishchess.org/tests/view/604f61282433018de7a38b4d closes https://github.com/official-stockfish/Stockfish/pull/3398 No functional change --- src/evaluate.cpp | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d43b8fa76c5..bb4f9ef7f44 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1038,6 +1038,45 @@ namespace { return v; } + // specifically correct for cornered bishops to fix FRC with NNUE. + Value fix_FRC(const Position& pos) { + + Value bAdjust = Value(0); + + constexpr Value p1=Value(209), p2=Value(136), p3=Value(148); + + Color Us = pos.side_to_move(); + if ( (pos.pieces(Us, BISHOP) & relative_square(Us, SQ_A1)) + && (pos.pieces(Us, PAWN) & relative_square(Us, SQ_B2))) + { + bAdjust -= !pos.empty(relative_square(Us,SQ_B3)) ? p1 + : pos.piece_on(relative_square(Us,SQ_C3)) == make_piece(Us, PAWN) ? p2 + : p3; + } + if ( (pos.pieces(Us, BISHOP) & relative_square(Us, SQ_H1)) + && (pos.pieces(Us, PAWN) & relative_square(Us, SQ_G2))) + { + bAdjust -= !pos.empty(relative_square(Us,SQ_G3)) ? p1 + : pos.piece_on(relative_square(Us,SQ_F3)) == make_piece(Us, PAWN) ? p2 + : p3; + } + if ( (pos.pieces(~Us, BISHOP) & relative_square(Us, SQ_A8)) + && (pos.pieces(~Us, PAWN) & relative_square(Us, SQ_B7))) + { + bAdjust += !pos.empty(relative_square(Us,SQ_B6)) ? p1 + : pos.piece_on(relative_square(Us,SQ_C6)) == make_piece(~Us, PAWN) ? p2 + : p3; + } + if ( (pos.pieces(~Us, BISHOP) & relative_square(Us, SQ_H8)) + && (pos.pieces(~Us, PAWN) & relative_square(Us, SQ_G7))) + { + bAdjust += !pos.empty(relative_square(Us,SQ_G6)) ? p1 + : pos.piece_on(relative_square(Us,SQ_F6)) == make_piece(~Us, PAWN) ? p2 + : p3; + } + return bAdjust; + } + } // namespace @@ -1055,7 +1094,12 @@ Value Eval::evaluate(const Position& pos) { // Scale and shift NNUE for compatibility with search and classical evaluation auto adjusted_NNUE = [&](){ int mat = pos.non_pawn_material() + 2 * PawnValueMg * pos.count(); - return NNUE::evaluate(pos) * (641 + mat / 32 - 4 * pos.rule50_count()) / 1024 + Tempo; + Value nnueValue = NNUE::evaluate(pos) * (641 + mat / 32 - 4 * pos.rule50_count()) / 1024 + Tempo; + + if (pos.is_chess960()) + nnueValue += fix_FRC(pos); + + return nnueValue; }; // If there is PSQ imbalance use classical eval, with small probability if it is small From ec42154ef2569a58dae2164e328d5bbffcb2aee9 Mon Sep 17 00:00:00 2001 From: Guy Vreuls Date: Sat, 13 Mar 2021 17:40:07 +0100 Subject: [PATCH 0524/1766] Use reference instead of pointer for pop_lsb() signature This patch changes the pop_lsb() signature from Square pop_lsb(Bitboard*) to Square pop_lsb(Bitboard&). This is more idomatic for C++ style signatures. Passed a non-regression STC test: LLR: 2.93 (-2.94,2.94) {-1.25,0.25} Total: 21280 W: 1928 L: 1847 D: 17505 Ptnml(0-2): 71, 1427, 7558, 1518, 66 https://tests.stockfishchess.org/tests/view/6053a1e22433018de7a38e2f We have verified that the generated binary is identical on gcc-10. Closes https://github.com/official-stockfish/Stockfish/pull/3404 No functional change. --- src/bitbase.cpp | 4 ++-- src/bitboard.h | 8 ++++---- src/evaluate.cpp | 8 ++++---- src/movegen.cpp | 30 +++++++++++++++--------------- src/nnue/features/half_kp.cpp | 2 +- src/pawns.cpp | 4 ++-- src/position.cpp | 6 +++--- src/syzygy/tbprobe.cpp | 4 ++-- 8 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/bitbase.cpp b/src/bitbase.cpp index ece9ec72b23..10aab6d9980 100644 --- a/src/bitbase.cpp +++ b/src/bitbase.cpp @@ -150,8 +150,8 @@ namespace { Bitboard b = attacks_bb(ksq[stm]); while (b) - r |= stm == WHITE ? db[index(BLACK, ksq[BLACK] , pop_lsb(&b), psq)] - : db[index(WHITE, pop_lsb(&b), ksq[WHITE], psq)]; + r |= stm == WHITE ? db[index(BLACK, ksq[BLACK] , pop_lsb(b), psq)] + : db[index(WHITE, pop_lsb(b), ksq[WHITE], psq)]; if (stm == WHITE) { diff --git a/src/bitboard.h b/src/bitboard.h index 70835e8eeff..c663f4bd1f7 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -428,10 +428,10 @@ inline Bitboard least_significant_square_bb(Bitboard b) { /// pop_lsb() finds and clears the least significant bit in a non-zero bitboard -inline Square pop_lsb(Bitboard* b) { - assert(*b); - const Square s = lsb(*b); - *b &= *b - 1; +inline Square pop_lsb(Bitboard& b) { + assert(b); + const Square s = lsb(b); + b &= b - 1; return s; } diff --git a/src/evaluate.cpp b/src/evaluate.cpp index bb4f9ef7f44..d4138cfaab5 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -397,7 +397,7 @@ namespace { attackedBy[Us][Pt] = 0; while (b1) { - Square s = pop_lsb(&b1); + Square s = pop_lsb(b1); // Find attacked squares, including x-ray attacks for bishops and rooks b = Pt == BISHOP ? attacks_bb(s, pos.pieces() ^ pos.pieces(QUEEN)) @@ -658,11 +658,11 @@ namespace { { b = (defended | weak) & (attackedBy[Us][KNIGHT] | attackedBy[Us][BISHOP]); while (b) - score += ThreatByMinor[type_of(pos.piece_on(pop_lsb(&b)))]; + score += ThreatByMinor[type_of(pos.piece_on(pop_lsb(b)))]; b = weak & attackedBy[Us][ROOK]; while (b) - score += ThreatByRook[type_of(pos.piece_on(pop_lsb(&b)))]; + score += ThreatByRook[type_of(pos.piece_on(pop_lsb(b)))]; if (weak & attackedBy[Us][KING]) score += ThreatByKing; @@ -760,7 +760,7 @@ namespace { while (b) { - Square s = pop_lsb(&b); + Square s = pop_lsb(b); assert(!(pos.pieces(Them, PAWN) & forward_file_bb(Us, s + Up))); diff --git a/src/movegen.cpp b/src/movegen.cpp index 742dbf40413..8b043f426eb 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -102,13 +102,13 @@ namespace { while (b1) { - Square to = pop_lsb(&b1); + Square to = pop_lsb(b1); *moveList++ = make_move(to - Up, to); } while (b2) { - Square to = pop_lsb(&b2); + Square to = pop_lsb(b2); *moveList++ = make_move(to - Up - Up, to); } } @@ -127,13 +127,13 @@ namespace { Bitboard b3 = shift(pawnsOn7) & emptySquares; while (b1) - moveList = make_promotions(moveList, pop_lsb(&b1), ksq); + moveList = make_promotions(moveList, pop_lsb(b1), ksq); while (b2) - moveList = make_promotions(moveList, pop_lsb(&b2), ksq); + moveList = make_promotions(moveList, pop_lsb(b2), ksq); while (b3) - moveList = make_promotions(moveList, pop_lsb(&b3), ksq); + moveList = make_promotions(moveList, pop_lsb(b3), ksq); } // Standard and en passant captures @@ -144,13 +144,13 @@ namespace { while (b1) { - Square to = pop_lsb(&b1); + Square to = pop_lsb(b1); *moveList++ = make_move(to - UpRight, to); } while (b2) { - Square to = pop_lsb(&b2); + Square to = pop_lsb(b2); *moveList++ = make_move(to - UpLeft, to); } @@ -167,7 +167,7 @@ namespace { assert(b1); while (b1) - *moveList++ = make(pop_lsb(&b1), pos.ep_square()); + *moveList++ = make(pop_lsb(b1), pos.ep_square()); } } @@ -183,14 +183,14 @@ namespace { Bitboard bb = piecesToMove & pos.pieces(Pt); while (bb) { - Square from = pop_lsb(&bb); + Square from = pop_lsb(bb); Bitboard b = attacks_bb(from, pos.pieces()) & target; if constexpr (Checks) b &= pos.check_squares(Pt); while (b) - *moveList++ = make_move(from, pop_lsb(&b)); + *moveList++ = make_move(from, pop_lsb(b)); } return moveList; @@ -236,7 +236,7 @@ namespace { Square ksq = pos.square(Us); Bitboard b = attacks_bb(ksq) & target; while (b) - *moveList++ = make_move(ksq, pop_lsb(&b)); + *moveList++ = make_move(ksq, pop_lsb(b)); if ((Type != CAPTURES) && pos.can_castle(Us & ANY_CASTLING)) for (CastlingRights cr : { Us & KING_SIDE, Us & QUEEN_SIDE } ) @@ -286,7 +286,7 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { while (dc) { - Square from = pop_lsb(&dc); + Square from = pop_lsb(dc); PieceType pt = type_of(pos.piece_on(from)); Bitboard b = attacks_bb(pt, from, pos.pieces()) & ~pos.pieces(); @@ -295,7 +295,7 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { b &= ~attacks_bb(pos.square(~us)); while (b) - *moveList++ = make_move(from, pop_lsb(&b)); + *moveList++ = make_move(from, pop_lsb(b)); } return us == WHITE ? generate_all(pos, moveList) @@ -319,12 +319,12 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { // the king evasions in order to skip known illegal moves, which avoids any // useless legality checks later on. while (sliders) - sliderAttacks |= line_bb(ksq, pop_lsb(&sliders)) & ~pos.checkers(); + sliderAttacks |= line_bb(ksq, pop_lsb(sliders)) & ~pos.checkers(); // Generate evasions for king, capture and non capture moves Bitboard b = attacks_bb(ksq) & ~pos.pieces(us) & ~sliderAttacks; while (b) - *moveList++ = make_move(ksq, pop_lsb(&b)); + *moveList++ = make_move(ksq, pop_lsb(b)); if (more_than_one(pos.checkers())) return moveList; // Double check, only a king move can save the day diff --git a/src/nnue/features/half_kp.cpp b/src/nnue/features/half_kp.cpp index ac6317e7da6..3a25a91d2c1 100644 --- a/src/nnue/features/half_kp.cpp +++ b/src/nnue/features/half_kp.cpp @@ -41,7 +41,7 @@ namespace Stockfish::Eval::NNUE::Features { Square ksq = orient(perspective, pos.square(perspective)); Bitboard bb = pos.pieces() & ~pos.pieces(KING); while (bb) { - Square s = pop_lsb(&bb); + Square s = pop_lsb(bb); active->push_back(make_index(perspective, s, pos.piece_on(s), ksq)); } } diff --git a/src/pawns.cpp b/src/pawns.cpp index 9a0610a0f53..81255813565 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -110,7 +110,7 @@ namespace { // Loop through all pawns of the current color and score each pawn while (b) { - s = pop_lsb(&b); + s = pop_lsb(b); assert(pos.piece_on(s) == make_piece(Us, PAWN)); @@ -290,7 +290,7 @@ Score Entry::do_king_safety(const Position& pos) { if (pawns & attacks_bb(ksq)) minPawnDist = 1; else while (pawns) - minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(&pawns))); + minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(pawns))); return shelter - make_score(0, 16 * minPawnDist); } diff --git a/src/position.cpp b/src/position.cpp index 772e45454c6..6c5a11b2595 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -73,7 +73,7 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) { << std::setfill(' ') << std::dec << "\nCheckers: "; for (Bitboard b = pos.checkers(); b; ) - os << UCI::square(pop_lsb(&b)) << " "; + os << UCI::square(pop_lsb(b)) << " "; if ( int(Tablebases::MaxCardinality) >= popcount(pos.pieces()) && !pos.can_castle(ANY_CASTLING)) @@ -359,7 +359,7 @@ void Position::set_state(StateInfo* si) const { for (Bitboard b = pieces(); b; ) { - Square s = pop_lsb(&b); + Square s = pop_lsb(b); Piece pc = piece_on(s); si->key ^= Zobrist::psq[pc][s]; @@ -476,7 +476,7 @@ Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners while (snipers) { - Square sniperSq = pop_lsb(&snipers); + Square sniperSq = pop_lsb(snipers); Bitboard b = between_bb(s, sniperSq) & occupancy; if (b && !more_than_one(b)) diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 5cfd38e5577..0500dd5a4f8 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -711,7 +711,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu leadPawns = b = pos.pieces(color_of(pc), PAWN); do - squares[size++] = pop_lsb(&b) ^ flipSquares; + squares[size++] = pop_lsb(b) ^ flipSquares; while (b); leadPawnsCnt = size; @@ -731,7 +731,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu // directly map them to the correct color and square. b = pos.pieces() ^ leadPawns; do { - Square s = pop_lsb(&b); + Square s = pop_lsb(b); squares[size] = s ^ flipSquares; pieces[size++] = Piece(pos.piece_on(s) ^ flipColor); } while (b); From 83eac08e7562d93787f75eccd4b7781c4bd45dd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Fri, 19 Mar 2021 19:43:25 +0100 Subject: [PATCH 0525/1766] Small cleanups (march 2021) With help of @BM123499, @mstembera, @gvreuls, @noobpwnftw and @Fanael Thanks! Closes https://github.com/official-stockfish/Stockfish/pull/3405 No functional change --- AUTHORS | 2 +- src/bitbase.cpp | 4 +- src/evaluate.cpp | 92 +++++++++++++++++------------- src/material.cpp | 2 +- src/misc.cpp | 22 +++---- src/movegen.cpp | 3 +- src/nnue/features/half_kp.cpp | 3 +- src/nnue/layers/affine_transform.h | 8 +-- src/pawns.cpp | 3 +- src/search.cpp | 33 ++++++----- src/syzygy/tbprobe.cpp | 10 ++-- src/thread.cpp | 6 +- 12 files changed, 107 insertions(+), 81 deletions(-) diff --git a/AUTHORS b/AUTHORS index 662cc6ecef9..11de282bef0 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,4 +1,4 @@ -# List of authors for Stockfish, as of August 4, 2020 +# List of authors for Stockfish, as of March 24, 2021 # Founders of the Stockfish project and fishtest infrastructure Tord Romstad (romstad) diff --git a/src/bitbase.cpp b/src/bitbase.cpp index 10aab6d9980..27bf4095478 100644 --- a/src/bitbase.cpp +++ b/src/bitbase.cpp @@ -150,8 +150,8 @@ namespace { Bitboard b = attacks_bb(ksq[stm]); while (b) - r |= stm == WHITE ? db[index(BLACK, ksq[BLACK] , pop_lsb(b), psq)] - : db[index(WHITE, pop_lsb(b), ksq[WHITE], psq)]; + r |= stm == WHITE ? db[index(BLACK, ksq[BLACK], pop_lsb(b), psq)] + : db[index(WHITE, pop_lsb(b), ksq[WHITE], psq)]; if (stm == WHITE) { diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d4138cfaab5..ca86a435fbf 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -54,7 +54,6 @@ using namespace std; -using namespace Stockfish::Eval::NNUE; namespace Stockfish { @@ -396,7 +395,8 @@ namespace { attackedBy[Us][Pt] = 0; - while (b1) { + while (b1) + { Square s = pop_lsb(b1); // Find attacked squares, including x-ray attacks for bishops and rooks @@ -1038,46 +1038,51 @@ namespace { return v; } - // specifically correct for cornered bishops to fix FRC with NNUE. + + /// Fisher Random Chess: correction for cornered bishops, to fix chess960 play with NNUE + Value fix_FRC(const Position& pos) { - Value bAdjust = Value(0); + constexpr Bitboard Corners = 1ULL << SQ_A1 | 1ULL << SQ_H1 | 1ULL << SQ_A8 | 1ULL << SQ_H8; - constexpr Value p1=Value(209), p2=Value(136), p3=Value(148); + if (!(pos.pieces(BISHOP) & Corners)) + return VALUE_ZERO; - Color Us = pos.side_to_move(); - if ( (pos.pieces(Us, BISHOP) & relative_square(Us, SQ_A1)) - && (pos.pieces(Us, PAWN) & relative_square(Us, SQ_B2))) - { - bAdjust -= !pos.empty(relative_square(Us,SQ_B3)) ? p1 - : pos.piece_on(relative_square(Us,SQ_C3)) == make_piece(Us, PAWN) ? p2 - : p3; - } - if ( (pos.pieces(Us, BISHOP) & relative_square(Us, SQ_H1)) - && (pos.pieces(Us, PAWN) & relative_square(Us, SQ_G2))) - { - bAdjust -= !pos.empty(relative_square(Us,SQ_G3)) ? p1 - : pos.piece_on(relative_square(Us,SQ_F3)) == make_piece(Us, PAWN) ? p2 - : p3; - } - if ( (pos.pieces(~Us, BISHOP) & relative_square(Us, SQ_A8)) - && (pos.pieces(~Us, PAWN) & relative_square(Us, SQ_B7))) - { - bAdjust += !pos.empty(relative_square(Us,SQ_B6)) ? p1 - : pos.piece_on(relative_square(Us,SQ_C6)) == make_piece(~Us, PAWN) ? p2 - : p3; - } - if ( (pos.pieces(~Us, BISHOP) & relative_square(Us, SQ_H8)) - && (pos.pieces(~Us, PAWN) & relative_square(Us, SQ_G7))) - { - bAdjust += !pos.empty(relative_square(Us,SQ_G6)) ? p1 - : pos.piece_on(relative_square(Us,SQ_F6)) == make_piece(~Us, PAWN) ? p2 - : p3; - } - return bAdjust; + constexpr int penalty1 = -209; + constexpr int penalty2 = -136; + constexpr int penalty3 = -148; + + int correction = 0; + + if ( pos.piece_on(SQ_A1) == W_BISHOP + && pos.piece_on(SQ_B2) == W_PAWN) + correction += !pos.empty(SQ_B3) ? penalty1 + : pos.piece_on(SQ_C3) == W_PAWN ? penalty2 + : penalty3; + + if ( pos.piece_on(SQ_H1) == W_BISHOP + && pos.piece_on(SQ_G2) == W_PAWN) + correction += !pos.empty(SQ_G3) ? penalty1 + : pos.piece_on(SQ_F3) == W_PAWN ? penalty2 + : penalty3; + + if ( pos.piece_on(SQ_A8) == B_BISHOP + && pos.piece_on(SQ_B7) == B_PAWN) + correction += !pos.empty(SQ_B6) ? -penalty1 + : pos.piece_on(SQ_C6) == B_PAWN ? -penalty2 + : -penalty3; + + if ( pos.piece_on(SQ_H8) == B_BISHOP + && pos.piece_on(SQ_G7) == B_PAWN) + correction += !pos.empty(SQ_G6) ? -penalty1 + : pos.piece_on(SQ_F6) == B_PAWN ? -penalty2 + : -penalty3; + + return pos.side_to_move() == WHITE ? Value(correction) + : -Value(correction); } -} // namespace +} // namespace Eval /// evaluate() is the evaluator for the outer world. It returns a static @@ -1092,14 +1097,19 @@ Value Eval::evaluate(const Position& pos) { else { // Scale and shift NNUE for compatibility with search and classical evaluation - auto adjusted_NNUE = [&](){ - int mat = pos.non_pawn_material() + 2 * PawnValueMg * pos.count(); - Value nnueValue = NNUE::evaluate(pos) * (641 + mat / 32 - 4 * pos.rule50_count()) / 1024 + Tempo; + auto adjusted_NNUE = [&]() + { + int material = pos.non_pawn_material() + 2 * PawnValueMg * pos.count(); + int scale = 641 + + material / 32 + - 4 * pos.rule50_count(); + + Value nnue = NNUE::evaluate(pos) * scale / 1024 + Tempo; if (pos.is_chess960()) - nnueValue += fix_FRC(pos); + nnue += fix_FRC(pos); - return nnueValue; + return nnue; }; // If there is PSQ imbalance use classical eval, with small probability if it is small diff --git a/src/material.cpp b/src/material.cpp index 84d7a4bd0a6..9d17af208c4 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -74,7 +74,7 @@ namespace { bool is_KBPsK(const Position& pos, Color us) { return pos.non_pawn_material(us) == BishopValueMg - && pos.count(us) >= 1; + && pos.count(us) >= 1; } bool is_KQKRPs(const Position& pos, Color us) { diff --git a/src/misc.cpp b/src/misc.cpp index 7600fc114c2..918dc7a9534 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -362,8 +362,12 @@ void std_aligned_free(void* ptr) { /// aligned_large_pages_alloc() will return suitably aligned memory, if possible using large pages. #if defined(_WIN32) -#if defined(_WIN64) -static void* aligned_large_pages_alloc_win(size_t allocSize) { + +static void* aligned_large_pages_alloc_windows(size_t allocSize) { + + #if !defined(_WIN64) + return nullptr; + #else HANDLE hProcessToken { }; LUID luid { }; @@ -406,21 +410,18 @@ static void* aligned_large_pages_alloc_win(size_t allocSize) { CloseHandle(hProcessToken); return mem; + + #endif } -#endif void* aligned_large_pages_alloc(size_t allocSize) { -#if defined(_WIN64) // Try to allocate large pages - void* mem = aligned_large_pages_alloc_win(allocSize); + void* mem = aligned_large_pages_alloc_windows(allocSize); // Fall back to regular, page aligned, allocation if necessary if (!mem) mem = VirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); -#else - void* mem = VirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); -#endif return mem; } @@ -456,8 +457,9 @@ void aligned_large_pages_free(void* mem) { if (mem && !VirtualFree(mem, 0, MEM_RELEASE)) { DWORD err = GetLastError(); - std::cerr << "Failed to free transposition table. Error code: 0x" << - std::hex << err << std::dec << std::endl; + std::cerr << "Failed to free large page memory. Error code: 0x" + << std::hex << err + << std::dec << std::endl; exit(EXIT_FAILURE); } } diff --git a/src/movegen.cpp b/src/movegen.cpp index 8b043f426eb..1e42f99e912 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -182,7 +182,8 @@ namespace { Bitboard bb = piecesToMove & pos.pieces(Pt); - while (bb) { + while (bb) + { Square from = pop_lsb(bb); Bitboard b = attacks_bb(from, pos.pieces()) & target; diff --git a/src/nnue/features/half_kp.cpp b/src/nnue/features/half_kp.cpp index 3a25a91d2c1..5a440b92b62 100644 --- a/src/nnue/features/half_kp.cpp +++ b/src/nnue/features/half_kp.cpp @@ -40,7 +40,8 @@ namespace Stockfish::Eval::NNUE::Features { Square ksq = orient(perspective, pos.square(perspective)); Bitboard bb = pos.pieces() & ~pos.pieces(KING); - while (bb) { + while (bb) + { Square s = pop_lsb(bb); active->push_back(make_index(perspective, s, pos.piece_on(s), ksq)); } diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index d2713c5aaf6..1faa180d4dd 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -96,7 +96,7 @@ namespace Stockfish::Eval::NNUE::Layers { IndexType idx = k / 2 * kOutputDimensions * 4 + k % 2; sum[w[idx] < 0] += w[idx]; } - for (int sign : {-1, 1}) + for (int sign : { -1, 1 }) while (sign * sum[sign == -1] > 258) { int maxK = 0, maxW = 0; @@ -234,9 +234,9 @@ namespace Stockfish::Eval::NNUE::Layers { __m128i product1 = _mm_maddubs_epi16(a1, b1); __m128i product2 = _mm_maddubs_epi16(a2, b2); __m128i product3 = _mm_maddubs_epi16(a3, b3); - product0 = _mm_adds_epi16(product0, product1); - product2 = _mm_adds_epi16(product2, product3); - product0 = _mm_adds_epi16(product0, product2); + product0 = _mm_add_epi16(product0, product1); + product2 = _mm_add_epi16(product2, product3); + product0 = _mm_add_epi16(product0, product2); product0 = _mm_madd_epi16(product0, kOnes128); acc = _mm_add_epi32(acc, product0); }; diff --git a/src/pawns.cpp b/src/pawns.cpp index 81255813565..70fb6f23782 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -109,7 +109,8 @@ namespace { e->blockedCount += popcount(shift(ourPawns) & (theirPawns | doubleAttackThem)); // Loop through all pawns of the current color and score each pawn - while (b) { + while (b) + { s = pop_lsb(b); assert(pos.piece_on(s) == make_piece(Us, PAWN)); diff --git a/src/search.cpp b/src/search.cpp index af7f801ffe2..ff8764d8a80 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -612,12 +612,12 @@ namespace { // Step 1. Initialize node Thread* thisThread = pos.this_thread(); - ss->inCheck = pos.checkers(); - priorCapture = pos.captured_piece(); - Color us = pos.side_to_move(); - moveCount = captureCount = quietCount = ss->moveCount = 0; - bestValue = -VALUE_INFINITE; - maxValue = VALUE_INFINITE; + ss->inCheck = pos.checkers(); + priorCapture = pos.captured_piece(); + Color us = pos.side_to_move(); + moveCount = captureCount = quietCount = ss->moveCount = 0; + bestValue = -VALUE_INFINITE; + maxValue = VALUE_INFINITE; ss->distanceFromPv = (PvNode ? 0 : ss->distanceFromPv); // Check for the available remaining time @@ -917,6 +917,7 @@ namespace { return probCutBeta; assert(probCutBeta < VALUE_INFINITE); + MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, &captureHistory); int probCutCount = 0; bool ttPv = ss->ttPv; @@ -1121,6 +1122,7 @@ namespace { { Value singularBeta = ttValue - ((formerPv + 4) * depth) / 2; Depth singularDepth = (depth - 1 + 3 * formerPv) / 2; + ss->excludedMove = move; value = search(pos, ss, singularBeta - 1, singularBeta, singularDepth, cutNode); ss->excludedMove = MOVE_NONE; @@ -1205,15 +1207,19 @@ namespace { // Decrease reduction if position is or has been on the PV // and node is not likely to fail low. (~10 Elo) - if (ss->ttPv && !likelyFailLow) + if ( ss->ttPv + && !likelyFailLow) r -= 2; // Increase reduction at root and non-PV nodes when the best move does not change frequently - if ((rootNode || !PvNode) && thisThread->rootDepth > 10 && thisThread->bestMoveChanges <= 2) + if ( (rootNode || !PvNode) + && thisThread->rootDepth > 10 + && thisThread->bestMoveChanges <= 2) r++; // More reductions for late moves if position was not in previous PV - if (moveCountPruning && !formerPv) + if ( moveCountPruning + && !formerPv) r++; // Decrease reduction if opponent's move count is high (~5 Elo) @@ -1226,7 +1232,7 @@ namespace { if (captureOrPromotion) { - // Unless giving check, this capture is likely bad + // Increase reduction for non-checking captures likely to be bad if ( !givesCheck && ss->staticEval + PieceValue[EG][pos.captured_piece()] + 210 * depth <= alpha) r++; @@ -1246,7 +1252,7 @@ namespace { // 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(). (~2 Elo) + // hence break reverse_move() (~2 Elo) else if ( type_of(move) == NORMAL && !pos.see_ge(reverse_move(move))) r -= 2 + ss->ttPv - (type_of(movedPiece) == PAWN); @@ -1408,8 +1414,9 @@ namespace { assert(moveCount || !ss->inCheck || excludedMove || !MoveList(pos).size()); if (!moveCount) - bestValue = excludedMove ? alpha - : ss->inCheck ? mated_in(ss->ply) : VALUE_DRAW; + bestValue = excludedMove ? alpha : + ss->inCheck ? mated_in(ss->ply) + : VALUE_DRAW; // If there is a move which produces search value greater than alpha we update stats of searched moves else if (bestMove) diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 0500dd5a4f8..831c8259c53 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -192,7 +192,8 @@ class TBFile : public std::ifstream { std::stringstream ss(Paths); std::string path; - while (std::getline(ss, path, SepChar)) { + while (std::getline(ss, path, SepChar)) + { fname = path + "/" + f; std::ifstream::open(fname); if (is_open()) @@ -567,7 +568,8 @@ int decompress_pairs(PairsData* d, uint64_t idx) { int buf64Size = 64; Sym sym; - while (true) { + while (true) + { int len = 0; // This is the symbol length - d->min_sym_len // Now get the symbol length. For any symbol s64 of length l right-padded @@ -605,8 +607,8 @@ int decompress_pairs(PairsData* d, uint64_t idx) { // We binary-search for our value recursively expanding into the left and // right child symbols until we reach a leaf node where symlen[sym] + 1 == 1 // that will store the value we need. - while (d->symlen[sym]) { - + while (d->symlen[sym]) + { Sym left = d->btree[sym].get(); // If a symbol contains 36 sub-symbols (d->symlen[sym] + 1 = 36) and diff --git a/src/thread.cpp b/src/thread.cpp index 3ef73dfa322..da8e1d05be9 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -128,14 +128,16 @@ void Thread::idle_loop() { void ThreadPool::set(size_t requested) { - if (size() > 0) { // destroy any existing thread(s) + if (size() > 0) // destroy any existing thread(s) + { main()->wait_for_search_finished(); while (size() > 0) delete back(), pop_back(); } - if (requested > 0) { // create new thread(s) + if (requested > 0) // create new thread(s) + { push_back(new MainThread(0)); while (size() < requested) From f28303d214645e37ee13b5c285d33142dd71bcb1 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Sat, 27 Feb 2021 11:52:18 +0100 Subject: [PATCH 0526/1766] Allow using Intel SDE for PGO builds. The software development emulator (SDE) allows to run binaries compiled for architectures not supported by the actual CPU. This is useful to do PGO builds for newer architectures. The SDE can currently be obtained from https://software.intel.com/content/www/us/en/develop/articles/intel-software-development-emulator.html This patch introduces a new optional makefile argument SDE_PATH. If not empty it should contain the path to the sde executable closes https://github.com/official-stockfish/Stockfish/pull/3373 No functional change. --- src/Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index eb32758f98d..cdd2007fd2b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -31,7 +31,11 @@ PREFIX = /usr/local BINDIR = $(PREFIX)/bin ### Built-in benchmark for pgo-builds -PGOBENCH = ./$(EXE) bench +ifeq ($(SDE_PATH),) + PGOBENCH = ./$(EXE) bench +else + PGOBENCH = $(SDE_PATH) -- ./$(EXE) bench +endif ### Source and object files SRCS = benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.cpp main.cpp \ From 62a0b65ff886f8f4895d854705c0c870e6a2a834 Mon Sep 17 00:00:00 2001 From: mstembera Date: Thu, 25 Mar 2021 13:33:05 -0700 Subject: [PATCH 0527/1766] Simplify and unify FRC cornered bishop. tested locally as fishtest doesn't support FRC: STC NNUE 9646 - 9647 - 20707 [0.500] 40000 -0.0 +/- 2.4, LOS: 49.7 %, DrawRatio: 51.8 % STC classical 9678 - 9609 - 20713 [0.501] 40000 0.6 +/- 2.4, LOS: 69.0 %, DrawRatio: 51.8 % and verified independently: Score of master vs patch: 6463 - 6580 - 34957 [0.499] 48000 closes https://github.com/official-stockfish/Stockfish/pull/3413 bench: 4321677 --- src/evaluate.cpp | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index ca86a435fbf..74f2e39bdf0 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -260,7 +260,8 @@ namespace { constexpr Score UncontestedOutpost = S( 1, 10); constexpr Score BishopOnKingRing = S( 24, 0); constexpr Score BishopXRayPawns = S( 4, 5); - constexpr Score CorneredBishop = S( 50, 50); + constexpr Value CorneredBishopV = Value(50); + constexpr Score CorneredBishop = S(CorneredBishopV, CorneredBishopV); constexpr Score FlankAttacks = S( 8, 0); constexpr Score Hanging = S( 69, 36); constexpr Score KnightOnQueen = S( 16, 11); @@ -477,9 +478,8 @@ namespace { { Direction d = pawn_push(Us) + (file_of(s) == FILE_A ? EAST : WEST); if (pos.piece_on(s + d) == make_piece(Us, PAWN)) - score -= !pos.empty(s + d + pawn_push(Us)) ? CorneredBishop * 4 - : pos.piece_on(s + d + d) == make_piece(Us, PAWN) ? CorneredBishop * 2 - : CorneredBishop; + score -= !pos.empty(s + d + pawn_push(Us)) ? CorneredBishop * 4 + : CorneredBishop * 3; } } } @@ -1048,35 +1048,27 @@ namespace { if (!(pos.pieces(BISHOP) & Corners)) return VALUE_ZERO; - constexpr int penalty1 = -209; - constexpr int penalty2 = -136; - constexpr int penalty3 = -148; - int correction = 0; if ( pos.piece_on(SQ_A1) == W_BISHOP && pos.piece_on(SQ_B2) == W_PAWN) - correction += !pos.empty(SQ_B3) ? penalty1 - : pos.piece_on(SQ_C3) == W_PAWN ? penalty2 - : penalty3; + correction += !pos.empty(SQ_B3) ? -CorneredBishopV * 4 + : -CorneredBishopV * 3; if ( pos.piece_on(SQ_H1) == W_BISHOP && pos.piece_on(SQ_G2) == W_PAWN) - correction += !pos.empty(SQ_G3) ? penalty1 - : pos.piece_on(SQ_F3) == W_PAWN ? penalty2 - : penalty3; + correction += !pos.empty(SQ_G3) ? -CorneredBishopV * 4 + : -CorneredBishopV * 3; if ( pos.piece_on(SQ_A8) == B_BISHOP && pos.piece_on(SQ_B7) == B_PAWN) - correction += !pos.empty(SQ_B6) ? -penalty1 - : pos.piece_on(SQ_C6) == B_PAWN ? -penalty2 - : -penalty3; + correction += !pos.empty(SQ_B6) ? CorneredBishopV * 4 + : CorneredBishopV * 3; if ( pos.piece_on(SQ_H8) == B_BISHOP && pos.piece_on(SQ_G7) == B_PAWN) - correction += !pos.empty(SQ_G6) ? -penalty1 - : pos.piece_on(SQ_F6) == B_PAWN ? -penalty2 - : -penalty3; + correction += !pos.empty(SQ_G6) ? CorneredBishopV * 4 + : CorneredBishopV * 3; return pos.side_to_move() == WHITE ? Value(correction) : -Value(correction); From c489df6f5b5629a135af3b8222fa1ef607ec1526 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Tue, 30 Mar 2021 00:42:45 -0300 Subject: [PATCH 0528/1766] Simplify King Evasion Simplify away the removal of some illegal `KING`-evasion moves during move generation. Verified for correctness by running perft on the following positions: ``` ./stockfish bench 16 1 6 default perft Nodes searched: 71608931810 ./stockfish position fen 4rrk1/1p1nq3/p7/2p1P1pp/3P2bp/3Q1Bn1/PPPB4/1K2R1NR w - - 40 21 go perft 6 Nodes searched: 6136386434 ``` Passed STC: LLR: 2.94 (-2.94,2.94) {-1.00,0.20} Total: 16072 W: 1473 L: 1349 D: 13250 Ptnml(0-2): 57, 1047, 5710, 1159, 63 https://tests.stockfishchess.org/tests/view/60629e7ef183b42957b423b1 Passed LTC: LLR: 2.94 (-2.94,2.94) {-0.70,0.20} Total: 59064 W: 2214 L: 2177 D: 54673 Ptnml(0-2): 26, 1944, 25556, 1979, 27 https://tests.stockfishchess.org/tests/view/6062dce4f183b42957b423de closes https://github.com/official-stockfish/Stockfish/pull/3415 No functional change --- src/movegen.cpp | 12 ++---------- src/position.cpp | 2 +- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index 1e42f99e912..5049613617a 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -313,17 +313,9 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { Color us = pos.side_to_move(); Square ksq = pos.square(us); - Bitboard sliderAttacks = 0; - Bitboard sliders = pos.checkers() & ~pos.pieces(KNIGHT, PAWN); - // Find all the squares attacked by slider checkers. We will remove them from - // the king evasions in order to skip known illegal moves, which avoids any - // useless legality checks later on. - while (sliders) - sliderAttacks |= line_bb(ksq, pop_lsb(sliders)) & ~pos.checkers(); - - // Generate evasions for king, capture and non capture moves - Bitboard b = attacks_bb(ksq) & ~pos.pieces(us) & ~sliderAttacks; + // Generate evasions for king + Bitboard b = attacks_bb(ksq) & ~pos.pieces(us); while (b) *moveList++ = make_move(ksq, pop_lsb(b)); diff --git a/src/position.cpp b/src/position.cpp index 6c5a11b2595..ec356ace9b0 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -544,7 +544,7 @@ bool Position::legal(Move m) const { // If the moving piece is a king, check whether the destination square is // attacked by the opponent. if (type_of(piece_on(from)) == KING) - return !(attackers_to(to) & pieces(~us)); + return !(attackers_to(to, pieces() ^ from) & pieces(~us)); // A non-king move is legal if and only if it is not pinned or it // is moving along the ray towards or away from the king. From b862c8d4be2cf6245360a8072ab910a67eb5bc00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Wed, 24 Mar 2021 21:55:49 +0100 Subject: [PATCH 0529/1766] Small clean-up Bench: 4321677 --- AUTHORS | 2 +- README.md | 10 +++++----- src/bitboard.cpp | 2 +- src/bitboard.h | 3 +++ src/evaluate.cpp | 27 ++++++++++++++------------- 5 files changed, 24 insertions(+), 20 deletions(-) diff --git a/AUTHORS b/AUTHORS index 11de282bef0..c12b98a038a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,4 +1,4 @@ -# List of authors for Stockfish, as of March 24, 2021 +# List of authors for Stockfish, as of March 31, 2021 # Founders of the Stockfish project and fishtest infrastructure Tord Romstad (romstad) diff --git a/README.md b/README.md index fdc6fd619fa..67fb5fa09ac 100644 --- a/README.md +++ b/README.md @@ -242,9 +242,9 @@ When not using the Makefile to compile (for instance, with Microsoft MSVC) you need to manually set/unset some switches in the compiler command line; see file *types.h* for a quick reference. -When reporting an issue or a bug, please tell us which version and -compiler you used to create your executable. These informations can -be found by typing the following commands in a console: +When reporting an issue or a bug, please tell us which Stockfish version +and which compiler you used to create your executable. This information +can be found by typing the following command in a console: ``` ./stockfish compiler @@ -252,8 +252,8 @@ be found by typing the following commands in a console: ## Understanding the code base and participating in the project -Stockfish's improvement over the last couple of years has been a great -community effort. There are a few ways to help contribute to its growth. +Stockfish's improvement over the last decade has been a great community +effort. There are a few ways to help contribute to its growth. ### Donating hardware diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 2da4d728ee7..6b84b51e036 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -130,7 +130,7 @@ namespace { for (Direction d : (pt == ROOK ? RookDirections : BishopDirections)) { Square s = sq; - while(safe_destination(s, d) && !(occupied & s)) + while (safe_destination(s, d) && !(occupied & s)) attacks |= (s += d); } diff --git a/src/bitboard.h b/src/bitboard.h index c663f4bd1f7..b29f3e24fa1 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -212,6 +212,7 @@ constexpr Bitboard adjacent_files_bb(Square s) { inline Bitboard line_bb(Square s1, Square s2) { assert(is_ok(s1) && is_ok(s2)); + return LineBB[s1][s2]; } @@ -225,7 +226,9 @@ inline Bitboard line_bb(Square s1, Square s2) { /// interpose itself to cover the check or capture the checking piece. inline Bitboard between_bb(Square s1, Square s2) { + assert(is_ok(s1) && is_ok(s2)); + return BetweenBB[s1][s2]; } diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 74f2e39bdf0..437e753a7ca 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -256,12 +256,12 @@ namespace { S(0, 0), S(3, 44), S(37, 68), S(42, 60), S(0, 39), S(58, 43) }; + constexpr Value CorneredBishop = Value(50); + // Assorted bonuses and penalties constexpr Score UncontestedOutpost = S( 1, 10); constexpr Score BishopOnKingRing = S( 24, 0); constexpr Score BishopXRayPawns = S( 4, 5); - constexpr Value CorneredBishopV = Value(50); - constexpr Score CorneredBishop = S(CorneredBishopV, CorneredBishopV); constexpr Score FlankAttacks = S( 8, 0); constexpr Score Hanging = S( 69, 36); constexpr Score KnightOnQueen = S( 16, 11); @@ -478,8 +478,8 @@ namespace { { Direction d = pawn_push(Us) + (file_of(s) == FILE_A ? EAST : WEST); if (pos.piece_on(s + d) == make_piece(Us, PAWN)) - score -= !pos.empty(s + d + pawn_push(Us)) ? CorneredBishop * 4 - : CorneredBishop * 3; + score -= !pos.empty(s + d + pawn_push(Us)) ? 4 * make_score(CorneredBishop, CorneredBishop) + : 3 * make_score(CorneredBishop, CorneredBishop); } } } @@ -1052,23 +1052,23 @@ namespace { if ( pos.piece_on(SQ_A1) == W_BISHOP && pos.piece_on(SQ_B2) == W_PAWN) - correction += !pos.empty(SQ_B3) ? -CorneredBishopV * 4 - : -CorneredBishopV * 3; + correction += !pos.empty(SQ_B3) ? -CorneredBishop * 4 + : -CorneredBishop * 3; if ( pos.piece_on(SQ_H1) == W_BISHOP && pos.piece_on(SQ_G2) == W_PAWN) - correction += !pos.empty(SQ_G3) ? -CorneredBishopV * 4 - : -CorneredBishopV * 3; + correction += !pos.empty(SQ_G3) ? -CorneredBishop * 4 + : -CorneredBishop * 3; if ( pos.piece_on(SQ_A8) == B_BISHOP && pos.piece_on(SQ_B7) == B_PAWN) - correction += !pos.empty(SQ_B6) ? CorneredBishopV * 4 - : CorneredBishopV * 3; + correction += !pos.empty(SQ_B6) ? CorneredBishop * 4 + : CorneredBishop * 3; if ( pos.piece_on(SQ_H8) == B_BISHOP && pos.piece_on(SQ_G7) == B_PAWN) - correction += !pos.empty(SQ_G6) ? CorneredBishopV * 4 - : CorneredBishopV * 3; + correction += !pos.empty(SQ_G6) ? CorneredBishop * 4 + : CorneredBishop * 3; return pos.side_to_move() == WHITE ? Value(correction) : -Value(correction); @@ -1119,7 +1119,8 @@ Value Eval::evaluate(const Position& pos) { // If the classical eval is small and imbalance large, use NNUE nevertheless. // For the case of opposite colored bishops, switch to NNUE eval with // small probability if the classical eval is less than the threshold. - if ( largePsq && !strongClassical + if ( largePsq + && !strongClassical && ( abs(v) * 16 < NNUEThreshold2 * r50 || ( pos.opposite_bishops() && abs(v) * 16 < (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50 From f40913f7f699d9d875e44929608b45947421ad03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sun, 4 Apr 2021 10:51:45 +0200 Subject: [PATCH 0530/1766] Keep more pawns This patch increases the weight of pawns in the scale factor applied to the output of the NNUE evaluation. This has the effect that Stockfish will try a little bit harder to keep more pawns in position where the engine has the advantage, and exchange more pawns in bad positions. STC: LLR: 2.93 (-2.94,2.94) {-0.20,1.10} Total: 42552 W: 3858 L: 3668 D: 35026 Ptnml(0-2): 152, 2956, 14876, 3134, 158 https://tests.stockfishchess.org/tests/view/606a06dd2b2df919fd5f0504 LTC: LLR: 2.95 (-2.94,2.94) {0.20,0.90} Total: 44328 W: 1703 L: 1531 D: 41094 Ptnml(0-2): 20, 1373, 19207, 1543, 21 https://tests.stockfishchess.org/tests/view/606aa4ec2b2df919fd5f053e Closes https://github.com/official-stockfish/Stockfish/pull/3420 Bench: 4310076 --- src/evaluate.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 437e753a7ca..789e28594cd 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1091,8 +1091,8 @@ Value Eval::evaluate(const Position& pos) { // Scale and shift NNUE for compatibility with search and classical evaluation auto adjusted_NNUE = [&]() { - int material = pos.non_pawn_material() + 2 * PawnValueMg * pos.count(); - int scale = 641 + int material = pos.non_pawn_material() + 4 * PawnValueMg * pos.count(); + int scale = 580 + material / 32 - 4 * pos.rule50_count(); From 3dfda1b28e14d1f3c2422fed5f203e305f58af28 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Tue, 6 Apr 2021 16:51:05 +0300 Subject: [PATCH 0531/1766] Replace distanceFromPv with a better logic This patch removes the recently introduced distanceFromPv logic, and replaces it with following logic: if reduction of moves with low movecount is really negative, we search them deeper than the first move. passed STC: LLR: 2.95 (-2.94,2.94) {-0.20,1.10} Total: 153008 W: 13913 L: 13579 D: 125516 Ptnml(0-2): 547, 10811, 53470, 11113, 563 https://tests.stockfishchess.org/tests/view/6069c9d02b2df919fd5f04d2 passed LTC: LLR: 2.94 (-2.94,2.94) {0.20,0.90} Total: 101920 W: 3964 L: 3699 D: 94257 Ptnml(0-2): 55, 3279, 44019, 3560, 47 https://tests.stockfishchess.org/tests/view/606a99fd2b2df919fd5f0532 Closes https://github.com/official-stockfish/Stockfish/pull/3421 Bench: 4191632 --- src/search.cpp | 11 ++++------- src/search.h | 1 - 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ff8764d8a80..f23db4c1919 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -618,7 +618,6 @@ namespace { moveCount = captureCount = quietCount = ss->moveCount = 0; bestValue = -VALUE_INFINITE; maxValue = VALUE_INFINITE; - ss->distanceFromPv = (PvNode ? 0 : ss->distanceFromPv); // Check for the available remaining time if (thisThread == Threads.main()) @@ -1180,8 +1179,6 @@ namespace { // Step 15. Make the move pos.do_move(move, st, givesCheck); - (ss+1)->distanceFromPv = ss->distanceFromPv + moveCount - 1; - // Step 16. Late moves reduction / extension (LMR, ~200 Elo) // We use various heuristics for the sons of a node after the first son has // been searched. In general we would like to reduce them, but there are many @@ -1280,10 +1277,10 @@ namespace { r -= ss->statScore / 14790; } - // In general we want to cap the LMR depth search at newDepth. But for nodes - // close to the principal variation the cap is at (newDepth + 1), which will - // allow these nodes to be searched deeper than the pv (up to 4 plies deeper). - Depth d = std::clamp(newDepth - r, 1, newDepth + ((ss+1)->distanceFromPv <= 4)); + // In general we want to cap the LMR depth search at newDepth. But if + // reductions are really negative and movecount is low, we allow this move + // to be searched deeper than the first move. + Depth d = std::clamp(newDepth - r, 1, newDepth + (r < -1 && moveCount <= 5)); value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); diff --git a/src/search.h b/src/search.h index 6f9fbd05277..811b2e2a946 100644 --- a/src/search.h +++ b/src/search.h @@ -49,7 +49,6 @@ struct Stack { Value staticEval; int statScore; int moveCount; - int distanceFromPv; bool inCheck; bool ttPv; bool ttHit; From 79bb28281ce5e9b40b2662915a96e9072ddad82e Mon Sep 17 00:00:00 2001 From: bmc4 Date: Fri, 2 Apr 2021 09:30:14 -0300 Subject: [PATCH 0532/1766] Merge all move generators Merging `generate` and `generate` into `generate_all()`. STC: LLR: 2.94 (-2.94,2.94) {-1.00,0.20} Total: 161800 W: 14585 L: 14624 D: 132591 Ptnml(0-2): 577, 11681, 56451, 11586, 605 https://tests.stockfishchess.org/tests/view/606532732b2df919fd5f026d LTC: LLR: 2.98 (-2.94,2.94) {-0.70,0.20} Total: 188504 W: 6906 L: 6961 D: 174637 Ptnml(0-2): 87, 6272, 81610, 6175, 108 https://tests.stockfishchess.org/tests/view/6065b0772b2df919fd5f02ae ------------ Verified for correctness of `EVASIONS` by running perft: ``` ./stockfish b3nch 16 1 6 default perft (replace 3 by e in b3nch) Nodes searched : 71608931810 ``` Also tested for correctness on Chess960 with a similar code shown here: https://github.com/official-stockfish/Stockfish/pull/3418#issuecomment-816630295 ``` ./stockfish b3nch 16 1 6 fischer.txt perft Nodes searched : 506736009395 ``` ------------ Closes https://github.com/official-stockfish/Stockfish/pull/3418 No functional change --- src/movegen.cpp | 99 ++++++++++++++----------------------------------- 1 file changed, 27 insertions(+), 72 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index 5049613617a..c3c149b700b 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -175,19 +175,19 @@ namespace { } - template - ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard piecesToMove, Bitboard target) { + template + ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) { static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()"); - Bitboard bb = piecesToMove & pos.pieces(Pt); + Bitboard bb = pos.pieces(Us, Pt); while (bb) { Square from = pop_lsb(bb); Bitboard b = attacks_bb(from, pos.pieces()) & target; - if constexpr (Checks) + if (Checks && (Pt == QUEEN || !(pos.blockers_for_king(~Us) & from))) b &= pos.check_squares(Pt); while (b) @@ -204,10 +204,11 @@ namespace { static_assert(Type != LEGAL, "Unsupported type in generate_all()"); constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantiations - Bitboard target, piecesToMove = pos.pieces(Us); - - if(Type == QUIET_CHECKS) - piecesToMove &= ~pos.blockers_for_king(~Us); + const Square ksq = pos.square(Us); + Bitboard target; + + if (Type == EVASIONS && more_than_one(pos.checkers())) + goto kingMoves; // Double check, only a king move can save the day switch (Type) { @@ -219,7 +220,7 @@ namespace { target = ~pos.pieces(); break; case EVASIONS: - target = between_bb(pos.square(Us), lsb(pos.checkers())); + target = between_bb(ksq, lsb(pos.checkers())); break; case NON_EVASIONS: target = ~pos.pieces(Us); @@ -227,19 +228,22 @@ namespace { } moveList = generate_pawn_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, piecesToMove, target); - moveList = generate_moves(pos, moveList, piecesToMove, target); - moveList = generate_moves< ROOK, Checks>(pos, moveList, piecesToMove, target); - moveList = generate_moves< QUEEN, Checks>(pos, moveList, piecesToMove, target); + moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); - if (Type != QUIET_CHECKS && Type != EVASIONS) +kingMoves: + if (!Checks || pos.blockers_for_king(~Us) & ksq) { - Square ksq = pos.square(Us); - Bitboard b = attacks_bb(ksq) & target; + Bitboard b = attacks_bb(ksq) & (Type == EVASIONS ? ~pos.pieces(Us) : target); + if (Checks) + b &= ~attacks_bb(pos.square(~Us)); + while (b) *moveList++ = make_move(ksq, pop_lsb(b)); - if ((Type != CAPTURES) && pos.can_castle(Us & ANY_CASTLING)) + if ((Type == QUIETS || Type == NON_EVASIONS) && pos.can_castle(Us & ANY_CASTLING)) for (CastlingRights cr : { Us & KING_SIDE, Us & QUEEN_SIDE } ) if (!pos.castling_impeded(cr) && pos.can_castle(cr)) *moveList++ = make(ksq, pos.castling_rook_square(cr)); @@ -253,6 +257,8 @@ namespace { /// Generates all pseudo-legal captures plus queen and checking knight promotions /// Generates all pseudo-legal non-captures and underpromotions (except checking knight) +/// Generates all pseudo-legal check evasions when the side to move is in check +/// Generates all pseudo-legal non-captures giving check, except castling /// Generates all pseudo-legal captures and non-captures /// /// Returns a pointer to the end of the move list. @@ -260,8 +266,8 @@ namespace { template ExtMove* generate(const Position& pos, ExtMove* moveList) { - static_assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS, "Unsupported type in generate()"); - assert(!pos.checkers()); + static_assert(Type != LEGAL, "Unsupported type in generate()"); + assert((Type == EVASIONS) == (bool)pos.checkers()); Color us = pos.side_to_move(); @@ -272,62 +278,11 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { // Explicit template instantiations template ExtMove* generate(const Position&, ExtMove*); template ExtMove* generate(const Position&, ExtMove*); +template ExtMove* generate(const Position&, ExtMove*); +template ExtMove* generate(const Position&, ExtMove*); template ExtMove* generate(const Position&, ExtMove*); -/// generate generates all pseudo-legal non-captures giving check, -/// except castling. Returns a pointer to the end of the move list. -template<> -ExtMove* generate(const Position& pos, ExtMove* moveList) { - - assert(!pos.checkers()); - - Color us = pos.side_to_move(); - Bitboard dc = pos.blockers_for_king(~us) & pos.pieces(us) & ~pos.pieces(PAWN); - - while (dc) - { - Square from = pop_lsb(dc); - PieceType pt = type_of(pos.piece_on(from)); - - Bitboard b = attacks_bb(pt, from, pos.pieces()) & ~pos.pieces(); - - if (pt == KING) - b &= ~attacks_bb(pos.square(~us)); - - while (b) - *moveList++ = make_move(from, pop_lsb(b)); - } - - return us == WHITE ? generate_all(pos, moveList) - : generate_all(pos, moveList); -} - - -/// generate generates all pseudo-legal check evasions when the side -/// to move is in check. Returns a pointer to the end of the move list. -template<> -ExtMove* generate(const Position& pos, ExtMove* moveList) { - - assert(pos.checkers()); - - Color us = pos.side_to_move(); - Square ksq = pos.square(us); - - // Generate evasions for king - Bitboard b = attacks_bb(ksq) & ~pos.pieces(us); - while (b) - *moveList++ = make_move(ksq, pop_lsb(b)); - - if (more_than_one(pos.checkers())) - return moveList; // Double check, only a king move can save the day - - // Generate blocking interpositions or captures of the checking piece - return us == WHITE ? generate_all(pos, moveList) - : generate_all(pos, moveList); -} - - /// generate generates all the legal moves in the given position template<> From 4889cf22bb2985d6d0babb8da93698e5d7641304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Thu, 15 Apr 2021 11:18:38 +0200 Subject: [PATCH 0533/1766] Revert previous patch Revert the previous patch about move generation, as it unexpectedly changed the bench. Better to take the time to understand the issue. Bench: 4191632 --- src/movegen.cpp | 99 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 72 insertions(+), 27 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index c3c149b700b..5049613617a 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -175,19 +175,19 @@ namespace { } - template - ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) { + template + ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard piecesToMove, Bitboard target) { static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()"); - Bitboard bb = pos.pieces(Us, Pt); + Bitboard bb = piecesToMove & pos.pieces(Pt); while (bb) { Square from = pop_lsb(bb); Bitboard b = attacks_bb(from, pos.pieces()) & target; - if (Checks && (Pt == QUEEN || !(pos.blockers_for_king(~Us) & from))) + if constexpr (Checks) b &= pos.check_squares(Pt); while (b) @@ -204,11 +204,10 @@ namespace { static_assert(Type != LEGAL, "Unsupported type in generate_all()"); constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantiations - const Square ksq = pos.square(Us); - Bitboard target; - - if (Type == EVASIONS && more_than_one(pos.checkers())) - goto kingMoves; // Double check, only a king move can save the day + Bitboard target, piecesToMove = pos.pieces(Us); + + if(Type == QUIET_CHECKS) + piecesToMove &= ~pos.blockers_for_king(~Us); switch (Type) { @@ -220,7 +219,7 @@ namespace { target = ~pos.pieces(); break; case EVASIONS: - target = between_bb(ksq, lsb(pos.checkers())); + target = between_bb(pos.square(Us), lsb(pos.checkers())); break; case NON_EVASIONS: target = ~pos.pieces(Us); @@ -228,22 +227,19 @@ namespace { } moveList = generate_pawn_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, piecesToMove, target); + moveList = generate_moves(pos, moveList, piecesToMove, target); + moveList = generate_moves< ROOK, Checks>(pos, moveList, piecesToMove, target); + moveList = generate_moves< QUEEN, Checks>(pos, moveList, piecesToMove, target); -kingMoves: - if (!Checks || pos.blockers_for_king(~Us) & ksq) + if (Type != QUIET_CHECKS && Type != EVASIONS) { - Bitboard b = attacks_bb(ksq) & (Type == EVASIONS ? ~pos.pieces(Us) : target); - if (Checks) - b &= ~attacks_bb(pos.square(~Us)); - + Square ksq = pos.square(Us); + Bitboard b = attacks_bb(ksq) & target; while (b) *moveList++ = make_move(ksq, pop_lsb(b)); - if ((Type == QUIETS || Type == NON_EVASIONS) && pos.can_castle(Us & ANY_CASTLING)) + if ((Type != CAPTURES) && pos.can_castle(Us & ANY_CASTLING)) for (CastlingRights cr : { Us & KING_SIDE, Us & QUEEN_SIDE } ) if (!pos.castling_impeded(cr) && pos.can_castle(cr)) *moveList++ = make(ksq, pos.castling_rook_square(cr)); @@ -257,8 +253,6 @@ namespace { /// Generates all pseudo-legal captures plus queen and checking knight promotions /// Generates all pseudo-legal non-captures and underpromotions (except checking knight) -/// Generates all pseudo-legal check evasions when the side to move is in check -/// Generates all pseudo-legal non-captures giving check, except castling /// Generates all pseudo-legal captures and non-captures /// /// Returns a pointer to the end of the move list. @@ -266,8 +260,8 @@ namespace { template ExtMove* generate(const Position& pos, ExtMove* moveList) { - static_assert(Type != LEGAL, "Unsupported type in generate()"); - assert((Type == EVASIONS) == (bool)pos.checkers()); + static_assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS, "Unsupported type in generate()"); + assert(!pos.checkers()); Color us = pos.side_to_move(); @@ -278,11 +272,62 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { // Explicit template instantiations template ExtMove* generate(const Position&, ExtMove*); template ExtMove* generate(const Position&, ExtMove*); -template ExtMove* generate(const Position&, ExtMove*); -template ExtMove* generate(const Position&, ExtMove*); template ExtMove* generate(const Position&, ExtMove*); +/// generate generates all pseudo-legal non-captures giving check, +/// except castling. Returns a pointer to the end of the move list. +template<> +ExtMove* generate(const Position& pos, ExtMove* moveList) { + + assert(!pos.checkers()); + + Color us = pos.side_to_move(); + Bitboard dc = pos.blockers_for_king(~us) & pos.pieces(us) & ~pos.pieces(PAWN); + + while (dc) + { + Square from = pop_lsb(dc); + PieceType pt = type_of(pos.piece_on(from)); + + Bitboard b = attacks_bb(pt, from, pos.pieces()) & ~pos.pieces(); + + if (pt == KING) + b &= ~attacks_bb(pos.square(~us)); + + while (b) + *moveList++ = make_move(from, pop_lsb(b)); + } + + return us == WHITE ? generate_all(pos, moveList) + : generate_all(pos, moveList); +} + + +/// generate generates all pseudo-legal check evasions when the side +/// to move is in check. Returns a pointer to the end of the move list. +template<> +ExtMove* generate(const Position& pos, ExtMove* moveList) { + + assert(pos.checkers()); + + Color us = pos.side_to_move(); + Square ksq = pos.square(us); + + // Generate evasions for king + Bitboard b = attacks_bb(ksq) & ~pos.pieces(us); + while (b) + *moveList++ = make_move(ksq, pop_lsb(b)); + + if (more_than_one(pos.checkers())) + return moveList; // Double check, only a king move can save the day + + // Generate blocking interpositions or captures of the checking piece + return us == WHITE ? generate_all(pos, moveList) + : generate_all(pos, moveList); +} + + /// generate generates all the legal moves in the given position template<> From 14d162d9f4f3544acf617de46a38a69878003e31 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Wed, 14 Apr 2021 21:25:18 +0300 Subject: [PATCH 0534/1766] Simplification: last capture extension The code for last capture extension can be removed in current master. Passed STC LLR: 2.95 (-2.94,2.94) {-1.00,0.20} Total: 85024 W: 7754 L: 7707 D: 69563 Ptnml(0-2): 293, 5991, 29914, 6004, 310 https://tests.stockfishchess.org/tests/view/607690f1814175337896068f Passed LTC LLR: 2.96 (-2.94,2.94) {-0.70,0.20} Total: 39880 W: 1503 L: 1453 D: 36924 Ptnml(0-2): 17, 1281, 17293, 1333, 16 https://tests.stockfishchess.org/tests/view/6076ccbe814175337896069e Closes https://github.com/official-stockfish/Stockfish/pull/3430 Bench: 4202264 --- src/search.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index f23db4c1919..c9ee47fea11 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1158,11 +1158,6 @@ namespace { && (pos.is_discovered_check_on_king(~us, move) || pos.see_ge(move))) extension = 1; - // Last captures extension - else if ( PieceValue[EG][pos.captured_piece()] > PawnValueEg - && pos.non_pawn_material() <= 2 * RookValueMg) - extension = 1; - // Add extension to new depth newDepth += extension; From 255514fb29f5a7a2c95863c9b074e36209b841c1 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Tue, 13 Apr 2021 20:02:28 +0200 Subject: [PATCH 0535/1766] Documentation patch: AppendChangedIndices Clarify the assumptions on the position passed to the AppendChangedIndices(). Closes https://github.com/official-stockfish/Stockfish/pull/3428 No functional change --- src/nnue/features/half_kp.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/nnue/features/half_kp.cpp b/src/nnue/features/half_kp.cpp index 5a440b92b62..8e6907ae002 100644 --- a/src/nnue/features/half_kp.cpp +++ b/src/nnue/features/half_kp.cpp @@ -47,7 +47,25 @@ namespace Stockfish::Eval::NNUE::Features { } } - // Get a list of indices for recently changed features + + // AppendChangedIndices() : get a list of indices for recently changed features + + // IMPORTANT: The `pos` in this function is pretty much useless as it + // is not always the position the features are updated to. The feature + // transformer code right now can update multiple accumulators per move, + // but since Stockfish only keeps the full state of the current leaf + // search position it is not possible to always pass here the position for + // which the accumulator is being updated. Therefore the only thing that + // can be reliably extracted from `pos` is the king square for the king + // of the `perspective` color (note: not even the other king's square will + // match reality in all cases, this is also the reason why `dp` is passed + // as a parameter and not extracted from pos.state()). This is of particular + // problem for future nets with other feature sets, where updating the active + // feature might require more information from the intermediate positions. In + // this case the only easy solution is to remove the multiple updates from + // the feature transformer update code and only update the accumulator for + // the current leaf position (the position after the move). + template void HalfKP::AppendChangedIndices( const Position& pos, const DirtyPiece& dp, Color perspective, From a7ab92ec25c91e8413630c52cfc2db6b4ecacf0c Mon Sep 17 00:00:00 2001 From: dsmsgms Date: Mon, 12 Apr 2021 12:06:22 -0300 Subject: [PATCH 0536/1766] Use classical eval for Bishop vs Pawns NNUE evaluation is incapable of recognizing trivially drawn bishop endgames (the wrong-colored rook pawn), which are in fact ubiquitous and stock standard in chess analysis. Switching off NNUE evaluation in KBPs vs KPs endgames is a measure that stops Stockfish from trading down to a drawn version of these endings when we presumably have advantage. The patch is able to edge over master in endgame positions. Patch tested for Elo gain with the "endgame.epd" book, and verified for non-regression with our usual book (see the pull request for details). STC: LLR: 2.93 (-2.94,2.94) {-0.20,1.10} Total: 33232 W: 6655 L: 6497 D: 20080 Ptnml(0-2): 4, 2342, 11769, 2494, 7 https://tests.stockfishchess.org/tests/view/6074a52981417533789605b8 LTC: LLR: 2.93 (-2.94,2.94) {0.20,0.90} Total: 159056 W: 29799 L: 29378 D: 99879 Ptnml(0-2): 7, 9004, 61085, 9425, 7 https://tests.stockfishchess.org/tests/view/6074c39a81417533789605ca Closes https://github.com/official-stockfish/Stockfish/pull/3427 Bench: 4503918 blah --- src/evaluate.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 789e28594cd..ba3de70bb25 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1104,23 +1104,26 @@ Value Eval::evaluate(const Position& pos) { return nnue; }; - // If there is PSQ imbalance use classical eval, with small probability if it is small + // If there is PSQ imbalance we use the classical eval. We also introduce + // a small probability of using the classical eval when PSQ imbalance is small. Value psq = Value(abs(eg_value(pos.psq_score()))); int r50 = 16 + pos.rule50_count(); bool largePsq = psq * 16 > (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50; bool classical = largePsq || (psq > PawnValueMg / 4 && !(pos.this_thread()->nodes & 0xB)); // Use classical evaluation for really low piece endgames. - // The most critical case is a bishop + A/H file pawn vs naked king draw. - bool strongClassical = pos.non_pawn_material() < 2 * RookValueMg && pos.count() < 2; + // One critical case is the draw for bishop + A/H file pawn vs naked king. + bool lowPieceEndgame = pos.non_pawn_material() == BishopValueMg + || (pos.non_pawn_material() < 2 * RookValueMg && pos.count() < 2); - v = classical || strongClassical ? Evaluation(pos).value() : adjusted_NNUE(); + v = classical || lowPieceEndgame ? Evaluation(pos).value() + : adjusted_NNUE(); // If the classical eval is small and imbalance large, use NNUE nevertheless. - // For the case of opposite colored bishops, switch to NNUE eval with - // small probability if the classical eval is less than the threshold. - if ( largePsq - && !strongClassical + // For the case of opposite colored bishops, switch to NNUE eval with small + // probability if the classical eval is less than the threshold. + if ( largePsq + && !lowPieceEndgame && ( abs(v) * 16 < NNUEThreshold2 * r50 || ( pos.opposite_bishops() && abs(v) * 16 < (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50 From fbbd4adc3c01460faa3cc8f91771ab9b0ef718ca Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Mon, 19 Apr 2021 19:50:19 +0200 Subject: [PATCH 0537/1766] Unify naming convention of the NNUE code matches the rest of the stockfish code base closes https://github.com/official-stockfish/Stockfish/pull/3437 No functional change --- src/nnue/architectures/halfkp_256x2-32-32.h | 6 +- src/nnue/evaluate_nnue.cpp | 64 ++--- src/nnue/evaluate_nnue.h | 4 +- src/nnue/features/feature_set.h | 14 +- src/nnue/features/features_common.h | 4 +- src/nnue/features/half_kp.cpp | 10 +- src/nnue/features/half_kp.h | 22 +- src/nnue/features/index_list.h | 2 +- src/nnue/layers/affine_transform.h | 252 ++++++++++---------- src/nnue/layers/clipped_relu.h | 96 ++++---- src/nnue/layers/input_slice.h | 24 +- src/nnue/nnue_accumulator.h | 4 +- src/nnue/nnue_architecture.h | 6 +- src/nnue/nnue_common.h | 35 ++- src/nnue/nnue_feature_transformer.h | 179 +++++++------- src/position.cpp | 4 +- src/search.cpp | 8 +- 17 files changed, 364 insertions(+), 370 deletions(-) diff --git a/src/nnue/architectures/halfkp_256x2-32-32.h b/src/nnue/architectures/halfkp_256x2-32-32.h index a6768204649..5f6cc7f336d 100644 --- a/src/nnue/architectures/halfkp_256x2-32-32.h +++ b/src/nnue/architectures/halfkp_256x2-32-32.h @@ -32,15 +32,15 @@ namespace Stockfish::Eval::NNUE { // Input features used in evaluation function using RawFeatures = Features::FeatureSet< - Features::HalfKP>; + Features::HalfKP>; // Number of input feature dimensions after conversion -constexpr IndexType kTransformedFeatureDimensions = 256; +constexpr IndexType TransformedFeatureDimensions = 256; namespace Layers { // Define network structure -using InputLayer = InputSlice; +using InputLayer = InputSlice; using HiddenLayer1 = ClippedReLU>; using HiddenLayer2 = ClippedReLU>; using OutputLayer = AffineTransform; diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 5416f13e1f7..0e539611671 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -32,7 +32,7 @@ namespace Stockfish::Eval::NNUE { // Input feature converter - LargePagePtr feature_transformer; + LargePagePtr featureTransformer; // Evaluation function AlignedPtr network; @@ -44,14 +44,14 @@ namespace Stockfish::Eval::NNUE { // Initialize the evaluation function parameters template - void Initialize(AlignedPtr& pointer) { + void initialize(AlignedPtr& pointer) { pointer.reset(reinterpret_cast(std_aligned_alloc(alignof(T), sizeof(T)))); std::memset(pointer.get(), 0, sizeof(T)); } template - void Initialize(LargePagePtr& pointer) { + void initialize(LargePagePtr& pointer) { static_assert(alignof(T) <= 4096, "aligned_large_pages_alloc() may fail for such a big alignment requirement of T"); pointer.reset(reinterpret_cast(aligned_large_pages_alloc(sizeof(T)))); @@ -60,46 +60,46 @@ namespace Stockfish::Eval::NNUE { // Read evaluation function parameters template - bool ReadParameters(std::istream& stream, T& reference) { + bool read_parameters(std::istream& stream, T& reference) { std::uint32_t header; header = read_little_endian(stream); - if (!stream || header != T::GetHashValue()) return false; - return reference.ReadParameters(stream); + if (!stream || header != T::get_hash_value()) return false; + return reference.read_parameters(stream); } } // namespace Detail // Initialize the evaluation function parameters - void Initialize() { + void initialize() { - Detail::Initialize(feature_transformer); - Detail::Initialize(network); + Detail::initialize(featureTransformer); + Detail::initialize(network); } // Read network header - bool ReadHeader(std::istream& stream, std::uint32_t* hash_value, std::string* architecture) + bool read_header(std::istream& stream, std::uint32_t* hashValue, std::string* architecture) { std::uint32_t version, size; version = read_little_endian(stream); - *hash_value = read_little_endian(stream); + *hashValue = read_little_endian(stream); size = read_little_endian(stream); - if (!stream || version != kVersion) return false; + if (!stream || version != Version) return false; architecture->resize(size); stream.read(&(*architecture)[0], size); return !stream.fail(); } // Read network parameters - bool ReadParameters(std::istream& stream) { + bool read_parameters(std::istream& stream) { - std::uint32_t hash_value; + std::uint32_t hashValue; std::string architecture; - if (!ReadHeader(stream, &hash_value, &architecture)) return false; - if (hash_value != kHashValue) return false; - if (!Detail::ReadParameters(stream, *feature_transformer)) return false; - if (!Detail::ReadParameters(stream, *network)) return false; + if (!read_header(stream, &hashValue, &architecture)) return false; + if (hashValue != HashValue) return false; + if (!Detail::read_parameters(stream, *featureTransformer)) return false; + if (!Detail::read_parameters(stream, *network)) return false; return stream && stream.peek() == std::ios::traits_type::eof(); } @@ -109,36 +109,36 @@ namespace Stockfish::Eval::NNUE { // We manually align the arrays on the stack because with gcc < 9.3 // overaligning stack variables with alignas() doesn't work correctly. - constexpr uint64_t alignment = kCacheLineSize; + constexpr uint64_t alignment = CacheLineSize; #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) - TransformedFeatureType transformed_features_unaligned[ - FeatureTransformer::kBufferSize + alignment / sizeof(TransformedFeatureType)]; - char buffer_unaligned[Network::kBufferSize + alignment]; + TransformedFeatureType transformedFeaturesUnaligned[ + FeatureTransformer::BufferSize + alignment / sizeof(TransformedFeatureType)]; + char bufferUnaligned[Network::BufferSize + alignment]; - auto* transformed_features = align_ptr_up(&transformed_features_unaligned[0]); - auto* buffer = align_ptr_up(&buffer_unaligned[0]); + auto* transformedFeatures = align_ptr_up(&transformedFeaturesUnaligned[0]); + auto* buffer = align_ptr_up(&bufferUnaligned[0]); #else alignas(alignment) - TransformedFeatureType transformed_features[FeatureTransformer::kBufferSize]; - alignas(alignment) char buffer[Network::kBufferSize]; + TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize]; + alignas(alignment) char buffer[Network::BufferSize]; #endif - ASSERT_ALIGNED(transformed_features, alignment); + ASSERT_ALIGNED(transformedFeatures, alignment); ASSERT_ALIGNED(buffer, alignment); - feature_transformer->Transform(pos, transformed_features); - const auto output = network->Propagate(transformed_features, buffer); + featureTransformer->transform(pos, transformedFeatures); + const auto output = network->propagate(transformedFeatures, buffer); - return static_cast(output[0] / FV_SCALE); + return static_cast(output[0] / OutputScale); } // Load eval, from a file stream or a memory stream bool load_eval(std::string name, std::istream& stream) { - Initialize(); + initialize(); fileName = name; - return ReadParameters(stream); + return read_parameters(stream); } } // namespace Stockfish::Eval::NNUE diff --git a/src/nnue/evaluate_nnue.h b/src/nnue/evaluate_nnue.h index 24aa6cc0051..c7fa4a96f55 100644 --- a/src/nnue/evaluate_nnue.h +++ b/src/nnue/evaluate_nnue.h @@ -28,8 +28,8 @@ namespace Stockfish::Eval::NNUE { // Hash value of evaluation function structure - constexpr std::uint32_t kHashValue = - FeatureTransformer::GetHashValue() ^ Network::GetHashValue(); + constexpr std::uint32_t HashValue = + FeatureTransformer::get_hash_value() ^ Network::get_hash_value(); // Deleter for automating release of memory area template diff --git a/src/nnue/features/feature_set.h b/src/nnue/features/feature_set.h index a3fea9c0b2c..d09f9b9474d 100644 --- a/src/nnue/features/feature_set.h +++ b/src/nnue/features/feature_set.h @@ -36,7 +36,7 @@ namespace Stockfish::Eval::NNUE::Features { return value == First || CompileTimeList::Contains(value); } static constexpr std::array - kValues = {{First, Remaining...}}; + Values = {{First, Remaining...}}; }; // Base class of feature set @@ -51,16 +51,16 @@ namespace Stockfish::Eval::NNUE::Features { public: // Hash value embedded in the evaluation file - static constexpr std::uint32_t kHashValue = FeatureType::kHashValue; + static constexpr std::uint32_t HashValue = FeatureType::HashValue; // Number of feature dimensions - static constexpr IndexType kDimensions = FeatureType::kDimensions; + static constexpr IndexType Dimensions = FeatureType::Dimensions; // Maximum number of simultaneously active features - static constexpr IndexType kMaxActiveDimensions = - FeatureType::kMaxActiveDimensions; + static constexpr IndexType MaxActiveDimensions = + FeatureType::MaxActiveDimensions; // Trigger for full calculation instead of difference calculation using SortedTriggerSet = - CompileTimeList; - static constexpr auto kRefreshTriggers = SortedTriggerSet::kValues; + CompileTimeList; + static constexpr auto RefreshTriggers = SortedTriggerSet::Values; }; diff --git a/src/nnue/features/features_common.h b/src/nnue/features/features_common.h index 118ec9532a1..9584cac8825 100644 --- a/src/nnue/features/features_common.h +++ b/src/nnue/features/features_common.h @@ -33,11 +33,11 @@ namespace Stockfish::Eval::NNUE::Features { // Trigger to perform full calculations instead of difference only enum class TriggerEvent { - kFriendKingMoved // calculate full evaluation when own king moves + FriendKingMoved // calculate full evaluation when own king moves }; enum class Side { - kFriend // side to move + Friend // side to move }; } // namespace Stockfish::Eval::NNUE::Features diff --git a/src/nnue/features/half_kp.cpp b/src/nnue/features/half_kp.cpp index 8e6907ae002..5c7538de4a1 100644 --- a/src/nnue/features/half_kp.cpp +++ b/src/nnue/features/half_kp.cpp @@ -30,12 +30,12 @@ namespace Stockfish::Eval::NNUE::Features { // Index of a feature for a given king position and another piece on some square inline IndexType make_index(Color perspective, Square s, Piece pc, Square ksq) { - return IndexType(orient(perspective, s) + kpp_board_index[perspective][pc] + PS_END * ksq); + return IndexType(orient(perspective, s) + PieceSquareIndex[perspective][pc] + PS_NB * ksq); } // Get a list of indices for active features template - void HalfKP::AppendActiveIndices( + void HalfKP::append_active_indices( const Position& pos, Color perspective, IndexList* active) { Square ksq = orient(perspective, pos.square(perspective)); @@ -48,7 +48,7 @@ namespace Stockfish::Eval::NNUE::Features { } - // AppendChangedIndices() : get a list of indices for recently changed features + // append_changed_indices() : get a list of indices for recently changed features // IMPORTANT: The `pos` in this function is pretty much useless as it // is not always the position the features are updated to. The feature @@ -67,7 +67,7 @@ namespace Stockfish::Eval::NNUE::Features { // the current leaf position (the position after the move). template - void HalfKP::AppendChangedIndices( + void HalfKP::append_changed_indices( const Position& pos, const DirtyPiece& dp, Color perspective, IndexList* removed, IndexList* added) { @@ -82,6 +82,6 @@ namespace Stockfish::Eval::NNUE::Features { } } - template class HalfKP; + template class HalfKP; } // namespace Stockfish::Eval::NNUE::Features diff --git a/src/nnue/features/half_kp.h b/src/nnue/features/half_kp.h index 2461acb725a..14efb0895ed 100644 --- a/src/nnue/features/half_kp.h +++ b/src/nnue/features/half_kp.h @@ -33,25 +33,25 @@ namespace Stockfish::Eval::NNUE::Features { public: // Feature name - static constexpr const char* kName = "HalfKP(Friend)"; + static constexpr const char* Name = "HalfKP(Friend)"; // Hash value embedded in the evaluation file - static constexpr std::uint32_t kHashValue = - 0x5D69D5B9u ^ (AssociatedKing == Side::kFriend); + static constexpr std::uint32_t HashValue = + 0x5D69D5B9u ^ (AssociatedKing == Side::Friend); // Number of feature dimensions - static constexpr IndexType kDimensions = - static_cast(SQUARE_NB) * static_cast(PS_END); + static constexpr IndexType Dimensions = + static_cast(SQUARE_NB) * static_cast(PS_NB); // Maximum number of simultaneously active features - static constexpr IndexType kMaxActiveDimensions = 30; // Kings don't count + static constexpr IndexType MaxActiveDimensions = 30; // Kings don't count // Trigger for full calculation instead of difference calculation - static constexpr TriggerEvent kRefreshTrigger = TriggerEvent::kFriendKingMoved; + static constexpr TriggerEvent RefreshTrigger = TriggerEvent::FriendKingMoved; // Get a list of indices for active features - static void AppendActiveIndices(const Position& pos, Color perspective, - IndexList* active); + static void append_active_indices(const Position& pos, Color perspective, + IndexList* active); // Get a list of indices for recently changed features - static void AppendChangedIndices(const Position& pos, const DirtyPiece& dp, Color perspective, - IndexList* removed, IndexList* added); + static void append_changed_indices(const Position& pos, const DirtyPiece& dp, Color perspective, + IndexList* removed, IndexList* added); }; } // namespace Stockfish::Eval::NNUE::Features diff --git a/src/nnue/features/index_list.h b/src/nnue/features/index_list.h index 9f03993bede..edf0add1b45 100644 --- a/src/nnue/features/index_list.h +++ b/src/nnue/features/index_list.h @@ -56,7 +56,7 @@ namespace Stockfish::Eval::NNUE::Features { //Type of feature index list class IndexList - : public ValueList { + : public ValueList { }; } // namespace Stockfish::Eval::NNUE::Features diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 1faa180d4dd..424fad5650f 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -27,7 +27,7 @@ namespace Stockfish::Eval::NNUE::Layers { // Affine transformation layer - template + template class AffineTransform { public: // Input/output type @@ -36,64 +36,64 @@ namespace Stockfish::Eval::NNUE::Layers { static_assert(std::is_same::value, ""); // Number of input/output dimensions - static constexpr IndexType kInputDimensions = - PreviousLayer::kOutputDimensions; - static constexpr IndexType kOutputDimensions = OutputDimensions; - static constexpr IndexType kPaddedInputDimensions = - CeilToMultiple(kInputDimensions, kMaxSimdWidth); + static constexpr IndexType InputDimensions = + PreviousLayer::OutputDimensions; + static constexpr IndexType OutputDimensions = OutDims; + static constexpr IndexType PaddedInputDimensions = + ceil_to_multiple(InputDimensions, MaxSimdWidth); #if defined (USE_AVX512) - static constexpr const IndexType kOutputSimdWidth = kSimdWidth / 2; + static constexpr const IndexType OutputSimdWidth = SimdWidth / 2; #elif defined (USE_SSSE3) - static constexpr const IndexType kOutputSimdWidth = kSimdWidth / 4; + static constexpr const IndexType OutputSimdWidth = SimdWidth / 4; #endif // Size of forward propagation buffer used in this layer - static constexpr std::size_t kSelfBufferSize = - CeilToMultiple(kOutputDimensions * sizeof(OutputType), kCacheLineSize); + static constexpr std::size_t SelfBufferSize = + ceil_to_multiple(OutputDimensions * sizeof(OutputType), CacheLineSize); // Size of the forward propagation buffer used from the input layer to this layer - static constexpr std::size_t kBufferSize = - PreviousLayer::kBufferSize + kSelfBufferSize; + static constexpr std::size_t BufferSize = + PreviousLayer::BufferSize + SelfBufferSize; // Hash value embedded in the evaluation file - static constexpr std::uint32_t GetHashValue() { - std::uint32_t hash_value = 0xCC03DAE4u; - hash_value += kOutputDimensions; - hash_value ^= PreviousLayer::GetHashValue() >> 1; - hash_value ^= PreviousLayer::GetHashValue() << 31; - return hash_value; + static constexpr std::uint32_t get_hash_value() { + std::uint32_t hashValue = 0xCC03DAE4u; + hashValue += OutputDimensions; + hashValue ^= PreviousLayer::get_hash_value() >> 1; + hashValue ^= PreviousLayer::get_hash_value() << 31; + return hashValue; } - // Read network parameters - bool ReadParameters(std::istream& stream) { - if (!previous_layer_.ReadParameters(stream)) return false; - for (std::size_t i = 0; i < kOutputDimensions; ++i) - biases_[i] = read_little_endian(stream); - for (std::size_t i = 0; i < kOutputDimensions * kPaddedInputDimensions; ++i) + // Read network parameters + bool read_parameters(std::istream& stream) { + if (!previousLayer.read_parameters(stream)) return false; + for (std::size_t i = 0; i < OutputDimensions; ++i) + biases[i] = read_little_endian(stream); + for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) #if !defined (USE_SSSE3) - weights_[i] = read_little_endian(stream); + weights[i] = read_little_endian(stream); #else - weights_[ - (i / 4) % (kPaddedInputDimensions / 4) * kOutputDimensions * 4 + - i / kPaddedInputDimensions * 4 + + weights[ + (i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 + + i / PaddedInputDimensions * 4 + i % 4 ] = read_little_endian(stream); // Determine if eights of weight and input products can be summed using 16bits // without saturation. We assume worst case combinations of 0 and 127 for all inputs. - if (kOutputDimensions > 1 && !stream.fail()) + if (OutputDimensions > 1 && !stream.fail()) { canSaturate16.count = 0; #if !defined(USE_VNNI) - for (IndexType i = 0; i < kPaddedInputDimensions; i += 16) - for (IndexType j = 0; j < kOutputDimensions; ++j) + for (IndexType i = 0; i < PaddedInputDimensions; i += 16) + for (IndexType j = 0; j < OutputDimensions; ++j) for (int x = 0; x < 2; ++x) { - WeightType* w = &weights_[i * kOutputDimensions + j * 4 + x * 2]; + WeightType* w = &weights[i * OutputDimensions + j * 4 + x * 2]; int sum[2] = {0, 0}; for (int k = 0; k < 8; ++k) { - IndexType idx = k / 2 * kOutputDimensions * 4 + k % 2; + IndexType idx = k / 2 * OutputDimensions * 4 + k % 2; sum[w[idx] < 0] += w[idx]; } for (int sign : { -1, 1 }) @@ -102,12 +102,12 @@ namespace Stockfish::Eval::NNUE::Layers { int maxK = 0, maxW = 0; for (int k = 0; k < 8; ++k) { - IndexType idx = k / 2 * kOutputDimensions * 4 + k % 2; + IndexType idx = k / 2 * OutputDimensions * 4 + k % 2; if (maxW < sign * w[idx]) maxK = k, maxW = sign * w[idx]; } - IndexType idx = maxK / 2 * kOutputDimensions * 4 + maxK % 2; + IndexType idx = maxK / 2 * OutputDimensions * 4 + maxK % 2; sum[sign == -1] -= w[idx]; canSaturate16.add(j, i + maxK / 2 * 4 + maxK % 2 + x * 2, w[idx]); w[idx] = 0; @@ -126,14 +126,14 @@ namespace Stockfish::Eval::NNUE::Layers { } // Forward propagation - const OutputType* Propagate( - const TransformedFeatureType* transformed_features, char* buffer) const { - const auto input = previous_layer_.Propagate( - transformed_features, buffer + kSelfBufferSize); + const OutputType* propagate( + const TransformedFeatureType* transformedFeatures, char* buffer) const { + const auto input = previousLayer.propagate( + transformedFeatures, buffer + SelfBufferSize); #if defined (USE_AVX512) - [[maybe_unused]] const __m512i kOnes512 = _mm512_set1_epi16(1); + [[maybe_unused]] const __m512i Ones512 = _mm512_set1_epi16(1); [[maybe_unused]] auto m512_hadd = [](__m512i sum, int bias) -> int { return _mm512_reduce_add_epi32(sum) + bias; @@ -144,7 +144,7 @@ namespace Stockfish::Eval::NNUE::Layers { acc = _mm512_dpbusd_epi32(acc, a, b); #else __m512i product0 = _mm512_maddubs_epi16(a, b); - product0 = _mm512_madd_epi16(product0, kOnes512); + product0 = _mm512_madd_epi16(product0, Ones512); acc = _mm512_add_epi32(acc, product0); #endif }; @@ -164,7 +164,7 @@ namespace Stockfish::Eval::NNUE::Layers { product0 = _mm512_add_epi16(product0, product1); product2 = _mm512_add_epi16(product2, product3); product0 = _mm512_add_epi16(product0, product2); - product0 = _mm512_madd_epi16(product0, kOnes512); + product0 = _mm512_madd_epi16(product0, Ones512); acc = _mm512_add_epi32(acc, product0); #endif }; @@ -172,7 +172,7 @@ namespace Stockfish::Eval::NNUE::Layers { #endif #if defined (USE_AVX2) - [[maybe_unused]] const __m256i kOnes256 = _mm256_set1_epi16(1); + [[maybe_unused]] const __m256i Ones256 = _mm256_set1_epi16(1); [[maybe_unused]] auto m256_hadd = [](__m256i sum, int bias) -> int { __m128i sum128 = _mm_add_epi32(_mm256_castsi256_si128(sum), _mm256_extracti128_si256(sum, 1)); @@ -186,7 +186,7 @@ namespace Stockfish::Eval::NNUE::Layers { acc = _mm256_dpbusd_epi32(acc, a, b); #else __m256i product0 = _mm256_maddubs_epi16(a, b); - product0 = _mm256_madd_epi16(product0, kOnes256); + product0 = _mm256_madd_epi16(product0, Ones256); acc = _mm256_add_epi32(acc, product0); #endif }; @@ -206,7 +206,7 @@ namespace Stockfish::Eval::NNUE::Layers { product0 = _mm256_add_epi16(product0, product1); product2 = _mm256_add_epi16(product2, product3); product0 = _mm256_add_epi16(product0, product2); - product0 = _mm256_madd_epi16(product0, kOnes256); + product0 = _mm256_madd_epi16(product0, Ones256); acc = _mm256_add_epi32(acc, product0); #endif }; @@ -214,7 +214,7 @@ namespace Stockfish::Eval::NNUE::Layers { #endif #if defined (USE_SSSE3) - [[maybe_unused]] const __m128i kOnes128 = _mm_set1_epi16(1); + [[maybe_unused]] const __m128i Ones128 = _mm_set1_epi16(1); [[maybe_unused]] auto m128_hadd = [](__m128i sum, int bias) -> int { sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0x4E)); //_MM_PERM_BADC @@ -224,7 +224,7 @@ namespace Stockfish::Eval::NNUE::Layers { [[maybe_unused]] auto m128_add_dpbusd_epi32 = [=](__m128i& acc, __m128i a, __m128i b) { __m128i product0 = _mm_maddubs_epi16(a, b); - product0 = _mm_madd_epi16(product0, kOnes128); + product0 = _mm_madd_epi16(product0, Ones128); acc = _mm_add_epi32(acc, product0); }; @@ -237,7 +237,7 @@ namespace Stockfish::Eval::NNUE::Layers { product0 = _mm_add_epi16(product0, product1); product2 = _mm_add_epi16(product2, product3); product0 = _mm_add_epi16(product0, product2); - product0 = _mm_madd_epi16(product0, kOnes128); + product0 = _mm_madd_epi16(product0, Ones128); acc = _mm_add_epi32(acc, product0); }; @@ -269,71 +269,71 @@ namespace Stockfish::Eval::NNUE::Layers { #if defined (USE_SSSE3) const auto output = reinterpret_cast(buffer); - const auto input_vector = reinterpret_cast(input); + const auto inputVector = reinterpret_cast(input); - static_assert(kOutputDimensions % kOutputSimdWidth == 0 || kOutputDimensions == 1); + static_assert(OutputDimensions % OutputSimdWidth == 0 || OutputDimensions == 1); - // kOutputDimensions is either 1 or a multiple of kSimdWidth + // OutputDimensions is either 1 or a multiple of SimdWidth // because then it is also an input dimension. - if constexpr (kOutputDimensions % kOutputSimdWidth == 0) + if constexpr (OutputDimensions % OutputSimdWidth == 0) { - constexpr IndexType kNumChunks = kPaddedInputDimensions / 4; + constexpr IndexType NumChunks = PaddedInputDimensions / 4; const auto input32 = reinterpret_cast(input); vec_t* outptr = reinterpret_cast(output); - std::memcpy(output, biases_, kOutputDimensions * sizeof(OutputType)); + std::memcpy(output, biases, OutputDimensions * sizeof(OutputType)); - for (int i = 0; i < (int)kNumChunks - 3; i += 4) + for (int i = 0; i < (int)NumChunks - 3; i += 4) { const vec_t in0 = vec_set_32(input32[i + 0]); const vec_t in1 = vec_set_32(input32[i + 1]); const vec_t in2 = vec_set_32(input32[i + 2]); const vec_t in3 = vec_set_32(input32[i + 3]); - const auto col0 = reinterpret_cast(&weights_[(i + 0) * kOutputDimensions * 4]); - const auto col1 = reinterpret_cast(&weights_[(i + 1) * kOutputDimensions * 4]); - const auto col2 = reinterpret_cast(&weights_[(i + 2) * kOutputDimensions * 4]); - const auto col3 = reinterpret_cast(&weights_[(i + 3) * kOutputDimensions * 4]); - for (int j = 0; j * kOutputSimdWidth < kOutputDimensions; ++j) + const auto col0 = reinterpret_cast(&weights[(i + 0) * OutputDimensions * 4]); + const auto col1 = reinterpret_cast(&weights[(i + 1) * OutputDimensions * 4]); + const auto col2 = reinterpret_cast(&weights[(i + 2) * OutputDimensions * 4]); + const auto col3 = reinterpret_cast(&weights[(i + 3) * OutputDimensions * 4]); + for (int j = 0; j * OutputSimdWidth < OutputDimensions; ++j) vec_add_dpbusd_32x4(outptr[j], in0, col0[j], in1, col1[j], in2, col2[j], in3, col3[j]); } for (int i = 0; i < canSaturate16.count; ++i) output[canSaturate16.ids[i].out] += input[canSaturate16.ids[i].in] * canSaturate16.ids[i].w; } - else if constexpr (kOutputDimensions == 1) + else if constexpr (OutputDimensions == 1) { #if defined (USE_AVX512) - if constexpr (kPaddedInputDimensions % (kSimdWidth * 2) != 0) + if constexpr (PaddedInputDimensions % (SimdWidth * 2) != 0) { - constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; - const auto input_vector256 = reinterpret_cast(input); + constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; + const auto inputVector256 = reinterpret_cast(input); __m256i sum0 = _mm256_setzero_si256(); - const auto row0 = reinterpret_cast(&weights_[0]); + const auto row0 = reinterpret_cast(&weights[0]); - for (int j = 0; j < (int)kNumChunks; ++j) + for (int j = 0; j < (int)NumChunks; ++j) { - const __m256i in = input_vector256[j]; + const __m256i in = inputVector256[j]; m256_add_dpbusd_epi32(sum0, in, row0[j]); } - output[0] = m256_hadd(sum0, biases_[0]); + output[0] = m256_hadd(sum0, biases[0]); } else #endif { #if defined (USE_AVX512) - constexpr IndexType kNumChunks = kPaddedInputDimensions / (kSimdWidth * 2); + constexpr IndexType NumChunks = PaddedInputDimensions / (SimdWidth * 2); #else - constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; + constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; #endif vec_t sum0 = vec_setzero(); - const auto row0 = reinterpret_cast(&weights_[0]); + const auto row0 = reinterpret_cast(&weights[0]); - for (int j = 0; j < (int)kNumChunks; ++j) + for (int j = 0; j < (int)NumChunks; ++j) { - const vec_t in = input_vector[j]; + const vec_t in = inputVector[j]; vec_add_dpbusd_32(sum0, in, row0[j]); } - output[0] = vec_hadd(sum0, biases_[0]); + output[0] = vec_hadd(sum0, biases[0]); } } @@ -344,80 +344,80 @@ namespace Stockfish::Eval::NNUE::Layers { auto output = reinterpret_cast(buffer); #if defined(USE_SSE2) - constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; - const __m128i kZeros = _mm_setzero_si128(); - const auto input_vector = reinterpret_cast(input); + constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; + const __m128i Zeros = _mm_setzero_si128(); + const auto inputVector = reinterpret_cast(input); #elif defined(USE_MMX) - constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; - const __m64 kZeros = _mm_setzero_si64(); - const auto input_vector = reinterpret_cast(input); + constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; + const __m64 Zeros = _mm_setzero_si64(); + const auto inputVector = reinterpret_cast(input); #elif defined(USE_NEON) - constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; - const auto input_vector = reinterpret_cast(input); + constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; + const auto inputVector = reinterpret_cast(input); #endif - for (IndexType i = 0; i < kOutputDimensions; ++i) { - const IndexType offset = i * kPaddedInputDimensions; + for (IndexType i = 0; i < OutputDimensions; ++i) { + const IndexType offset = i * PaddedInputDimensions; #if defined(USE_SSE2) - __m128i sum_lo = _mm_cvtsi32_si128(biases_[i]); - __m128i sum_hi = kZeros; - const auto row = reinterpret_cast(&weights_[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { + __m128i sumLo = _mm_cvtsi32_si128(biases[i]); + __m128i sumHi = Zeros; + const auto row = reinterpret_cast(&weights[offset]); + for (IndexType j = 0; j < NumChunks; ++j) { __m128i row_j = _mm_load_si128(&row[j]); - __m128i input_j = _mm_load_si128(&input_vector[j]); - __m128i extended_row_lo = _mm_srai_epi16(_mm_unpacklo_epi8(row_j, row_j), 8); - __m128i extended_row_hi = _mm_srai_epi16(_mm_unpackhi_epi8(row_j, row_j), 8); - __m128i extended_input_lo = _mm_unpacklo_epi8(input_j, kZeros); - __m128i extended_input_hi = _mm_unpackhi_epi8(input_j, kZeros); - __m128i product_lo = _mm_madd_epi16(extended_row_lo, extended_input_lo); - __m128i product_hi = _mm_madd_epi16(extended_row_hi, extended_input_hi); - sum_lo = _mm_add_epi32(sum_lo, product_lo); - sum_hi = _mm_add_epi32(sum_hi, product_hi); + __m128i input_j = _mm_load_si128(&inputVector[j]); + __m128i extendedRowLo = _mm_srai_epi16(_mm_unpacklo_epi8(row_j, row_j), 8); + __m128i extendedRowHi = _mm_srai_epi16(_mm_unpackhi_epi8(row_j, row_j), 8); + __m128i extendedInputLo = _mm_unpacklo_epi8(input_j, Zeros); + __m128i extendedInputHi = _mm_unpackhi_epi8(input_j, Zeros); + __m128i productLo = _mm_madd_epi16(extendedRowLo, extendedInputLo); + __m128i productHi = _mm_madd_epi16(extendedRowHi, extendedInputHi); + sumLo = _mm_add_epi32(sumLo, productLo); + sumHi = _mm_add_epi32(sumHi, productHi); } - __m128i sum = _mm_add_epi32(sum_lo, sum_hi); - __m128i sum_high_64 = _mm_shuffle_epi32(sum, _MM_SHUFFLE(1, 0, 3, 2)); - sum = _mm_add_epi32(sum, sum_high_64); + __m128i sum = _mm_add_epi32(sumLo, sumHi); + __m128i sumHigh_64 = _mm_shuffle_epi32(sum, _MM_SHUFFLE(1, 0, 3, 2)); + sum = _mm_add_epi32(sum, sumHigh_64); __m128i sum_second_32 = _mm_shufflelo_epi16(sum, _MM_SHUFFLE(1, 0, 3, 2)); sum = _mm_add_epi32(sum, sum_second_32); output[i] = _mm_cvtsi128_si32(sum); #elif defined(USE_MMX) - __m64 sum_lo = _mm_cvtsi32_si64(biases_[i]); - __m64 sum_hi = kZeros; - const auto row = reinterpret_cast(&weights_[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { + __m64 sumLo = _mm_cvtsi32_si64(biases[i]); + __m64 sumHi = Zeros; + const auto row = reinterpret_cast(&weights[offset]); + for (IndexType j = 0; j < NumChunks; ++j) { __m64 row_j = row[j]; - __m64 input_j = input_vector[j]; - __m64 extended_row_lo = _mm_srai_pi16(_mm_unpacklo_pi8(row_j, row_j), 8); - __m64 extended_row_hi = _mm_srai_pi16(_mm_unpackhi_pi8(row_j, row_j), 8); - __m64 extended_input_lo = _mm_unpacklo_pi8(input_j, kZeros); - __m64 extended_input_hi = _mm_unpackhi_pi8(input_j, kZeros); - __m64 product_lo = _mm_madd_pi16(extended_row_lo, extended_input_lo); - __m64 product_hi = _mm_madd_pi16(extended_row_hi, extended_input_hi); - sum_lo = _mm_add_pi32(sum_lo, product_lo); - sum_hi = _mm_add_pi32(sum_hi, product_hi); + __m64 input_j = inputVector[j]; + __m64 extendedRowLo = _mm_srai_pi16(_mm_unpacklo_pi8(row_j, row_j), 8); + __m64 extendedRowHi = _mm_srai_pi16(_mm_unpackhi_pi8(row_j, row_j), 8); + __m64 extendedInputLo = _mm_unpacklo_pi8(input_j, Zeros); + __m64 extendedInputHi = _mm_unpackhi_pi8(input_j, Zeros); + __m64 productLo = _mm_madd_pi16(extendedRowLo, extendedInputLo); + __m64 productHi = _mm_madd_pi16(extendedRowHi, extendedInputHi); + sumLo = _mm_add_pi32(sumLo, productLo); + sumHi = _mm_add_pi32(sumHi, productHi); } - __m64 sum = _mm_add_pi32(sum_lo, sum_hi); + __m64 sum = _mm_add_pi32(sumLo, sumHi); sum = _mm_add_pi32(sum, _mm_unpackhi_pi32(sum, sum)); output[i] = _mm_cvtsi64_si32(sum); #elif defined(USE_NEON) - int32x4_t sum = {biases_[i]}; - const auto row = reinterpret_cast(&weights_[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { - int16x8_t product = vmull_s8(input_vector[j * 2], row[j * 2]); - product = vmlal_s8(product, input_vector[j * 2 + 1], row[j * 2 + 1]); + int32x4_t sum = {biases[i]}; + const auto row = reinterpret_cast(&weights[offset]); + for (IndexType j = 0; j < NumChunks; ++j) { + int16x8_t product = vmull_s8(inputVector[j * 2], row[j * 2]); + product = vmlal_s8(product, inputVector[j * 2 + 1], row[j * 2 + 1]); sum = vpadalq_s16(sum, product); } output[i] = sum[0] + sum[1] + sum[2] + sum[3]; #else - OutputType sum = biases_[i]; - for (IndexType j = 0; j < kInputDimensions; ++j) { - sum += weights_[offset + j] * input[j]; + OutputType sum = biases[i]; + for (IndexType j = 0; j < InputDimensions; ++j) { + sum += weights[offset + j] * input[j]; } output[i] = sum; #endif @@ -436,10 +436,10 @@ namespace Stockfish::Eval::NNUE::Layers { using BiasType = OutputType; using WeightType = std::int8_t; - PreviousLayer previous_layer_; + PreviousLayer previousLayer; - alignas(kCacheLineSize) BiasType biases_[kOutputDimensions]; - alignas(kCacheLineSize) WeightType weights_[kOutputDimensions * kPaddedInputDimensions]; + alignas(CacheLineSize) BiasType biases[OutputDimensions]; + alignas(CacheLineSize) WeightType weights[OutputDimensions * PaddedInputDimensions]; #if defined (USE_SSSE3) struct CanSaturate { int count; @@ -447,7 +447,7 @@ namespace Stockfish::Eval::NNUE::Layers { uint16_t out; uint16_t in; int8_t w; - } ids[kPaddedInputDimensions * kOutputDimensions * 3 / 4]; + } ids[PaddedInputDimensions * OutputDimensions * 3 / 4]; void add(int i, int j, int8_t w) { ids[count].out = i; diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index a10e3e482b7..00809c507b3 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -35,130 +35,130 @@ namespace Stockfish::Eval::NNUE::Layers { static_assert(std::is_same::value, ""); // Number of input/output dimensions - static constexpr IndexType kInputDimensions = - PreviousLayer::kOutputDimensions; - static constexpr IndexType kOutputDimensions = kInputDimensions; + static constexpr IndexType InputDimensions = + PreviousLayer::OutputDimensions; + static constexpr IndexType OutputDimensions = InputDimensions; // Size of forward propagation buffer used in this layer - static constexpr std::size_t kSelfBufferSize = - CeilToMultiple(kOutputDimensions * sizeof(OutputType), kCacheLineSize); + static constexpr std::size_t SelfBufferSize = + ceil_to_multiple(OutputDimensions * sizeof(OutputType), CacheLineSize); // Size of the forward propagation buffer used from the input layer to this layer - static constexpr std::size_t kBufferSize = - PreviousLayer::kBufferSize + kSelfBufferSize; + static constexpr std::size_t BufferSize = + PreviousLayer::BufferSize + SelfBufferSize; // Hash value embedded in the evaluation file - static constexpr std::uint32_t GetHashValue() { - std::uint32_t hash_value = 0x538D24C7u; - hash_value += PreviousLayer::GetHashValue(); - return hash_value; + static constexpr std::uint32_t get_hash_value() { + std::uint32_t hashValue = 0x538D24C7u; + hashValue += PreviousLayer::get_hash_value(); + return hashValue; } // Read network parameters - bool ReadParameters(std::istream& stream) { - return previous_layer_.ReadParameters(stream); + bool read_parameters(std::istream& stream) { + return previousLayer.read_parameters(stream); } // Forward propagation - const OutputType* Propagate( - const TransformedFeatureType* transformed_features, char* buffer) const { - const auto input = previous_layer_.Propagate( - transformed_features, buffer + kSelfBufferSize); + const OutputType* propagate( + const TransformedFeatureType* transformedFeatures, char* buffer) const { + const auto input = previousLayer.propagate( + transformedFeatures, buffer + SelfBufferSize); const auto output = reinterpret_cast(buffer); #if defined(USE_AVX2) - constexpr IndexType kNumChunks = kInputDimensions / kSimdWidth; - const __m256i kZero = _mm256_setzero_si256(); - const __m256i kOffsets = _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0); + constexpr IndexType NumChunks = InputDimensions / SimdWidth; + const __m256i Zero = _mm256_setzero_si256(); + const __m256i Offsets = _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0); const auto in = reinterpret_cast(input); const auto out = reinterpret_cast<__m256i*>(output); - for (IndexType i = 0; i < kNumChunks; ++i) { + for (IndexType i = 0; i < NumChunks; ++i) { const __m256i words0 = _mm256_srai_epi16(_mm256_packs_epi32( _mm256_load_si256(&in[i * 4 + 0]), - _mm256_load_si256(&in[i * 4 + 1])), kWeightScaleBits); + _mm256_load_si256(&in[i * 4 + 1])), WeightScaleBits); const __m256i words1 = _mm256_srai_epi16(_mm256_packs_epi32( _mm256_load_si256(&in[i * 4 + 2]), - _mm256_load_si256(&in[i * 4 + 3])), kWeightScaleBits); + _mm256_load_si256(&in[i * 4 + 3])), WeightScaleBits); _mm256_store_si256(&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8( - _mm256_packs_epi16(words0, words1), kZero), kOffsets)); + _mm256_packs_epi16(words0, words1), Zero), Offsets)); } - constexpr IndexType kStart = kNumChunks * kSimdWidth; + constexpr IndexType Start = NumChunks * SimdWidth; #elif defined(USE_SSE2) - constexpr IndexType kNumChunks = kInputDimensions / kSimdWidth; + constexpr IndexType NumChunks = InputDimensions / SimdWidth; #ifdef USE_SSE41 - const __m128i kZero = _mm_setzero_si128(); + const __m128i Zero = _mm_setzero_si128(); #else const __m128i k0x80s = _mm_set1_epi8(-128); #endif const auto in = reinterpret_cast(input); const auto out = reinterpret_cast<__m128i*>(output); - for (IndexType i = 0; i < kNumChunks; ++i) { + for (IndexType i = 0; i < NumChunks; ++i) { const __m128i words0 = _mm_srai_epi16(_mm_packs_epi32( _mm_load_si128(&in[i * 4 + 0]), - _mm_load_si128(&in[i * 4 + 1])), kWeightScaleBits); + _mm_load_si128(&in[i * 4 + 1])), WeightScaleBits); const __m128i words1 = _mm_srai_epi16(_mm_packs_epi32( _mm_load_si128(&in[i * 4 + 2]), - _mm_load_si128(&in[i * 4 + 3])), kWeightScaleBits); + _mm_load_si128(&in[i * 4 + 3])), WeightScaleBits); const __m128i packedbytes = _mm_packs_epi16(words0, words1); _mm_store_si128(&out[i], #ifdef USE_SSE41 - _mm_max_epi8(packedbytes, kZero) + _mm_max_epi8(packedbytes, Zero) #else _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s) #endif ); } - constexpr IndexType kStart = kNumChunks * kSimdWidth; + constexpr IndexType Start = NumChunks * SimdWidth; #elif defined(USE_MMX) - constexpr IndexType kNumChunks = kInputDimensions / kSimdWidth; + constexpr IndexType NumChunks = InputDimensions / SimdWidth; const __m64 k0x80s = _mm_set1_pi8(-128); const auto in = reinterpret_cast(input); const auto out = reinterpret_cast<__m64*>(output); - for (IndexType i = 0; i < kNumChunks; ++i) { + for (IndexType i = 0; i < NumChunks; ++i) { const __m64 words0 = _mm_srai_pi16( _mm_packs_pi32(in[i * 4 + 0], in[i * 4 + 1]), - kWeightScaleBits); + WeightScaleBits); const __m64 words1 = _mm_srai_pi16( _mm_packs_pi32(in[i * 4 + 2], in[i * 4 + 3]), - kWeightScaleBits); + WeightScaleBits); const __m64 packedbytes = _mm_packs_pi16(words0, words1); out[i] = _mm_subs_pi8(_mm_adds_pi8(packedbytes, k0x80s), k0x80s); } _mm_empty(); - constexpr IndexType kStart = kNumChunks * kSimdWidth; + constexpr IndexType Start = NumChunks * SimdWidth; #elif defined(USE_NEON) - constexpr IndexType kNumChunks = kInputDimensions / (kSimdWidth / 2); - const int8x8_t kZero = {0}; + constexpr IndexType NumChunks = InputDimensions / (SimdWidth / 2); + const int8x8_t Zero = {0}; const auto in = reinterpret_cast(input); const auto out = reinterpret_cast(output); - for (IndexType i = 0; i < kNumChunks; ++i) { + for (IndexType i = 0; i < NumChunks; ++i) { int16x8_t shifted; const auto pack = reinterpret_cast(&shifted); - pack[0] = vqshrn_n_s32(in[i * 2 + 0], kWeightScaleBits); - pack[1] = vqshrn_n_s32(in[i * 2 + 1], kWeightScaleBits); - out[i] = vmax_s8(vqmovn_s16(shifted), kZero); + pack[0] = vqshrn_n_s32(in[i * 2 + 0], WeightScaleBits); + pack[1] = vqshrn_n_s32(in[i * 2 + 1], WeightScaleBits); + out[i] = vmax_s8(vqmovn_s16(shifted), Zero); } - constexpr IndexType kStart = kNumChunks * (kSimdWidth / 2); + constexpr IndexType Start = NumChunks * (SimdWidth / 2); #else - constexpr IndexType kStart = 0; + constexpr IndexType Start = 0; #endif - for (IndexType i = kStart; i < kInputDimensions; ++i) { + for (IndexType i = Start; i < InputDimensions; ++i) { output[i] = static_cast( - std::max(0, std::min(127, input[i] >> kWeightScaleBits))); + std::max(0, std::min(127, input[i] >> WeightScaleBits))); } return output; } private: - PreviousLayer previous_layer_; + PreviousLayer previousLayer; }; } // namespace Stockfish::Eval::NNUE::Layers diff --git a/src/nnue/layers/input_slice.h b/src/nnue/layers/input_slice.h index 43b06eec5a9..f113b911239 100644 --- a/src/nnue/layers/input_slice.h +++ b/src/nnue/layers/input_slice.h @@ -26,38 +26,38 @@ namespace Stockfish::Eval::NNUE::Layers { // Input layer -template +template class InputSlice { public: // Need to maintain alignment - static_assert(Offset % kMaxSimdWidth == 0, ""); + static_assert(Offset % MaxSimdWidth == 0, ""); // Output type using OutputType = TransformedFeatureType; // Output dimensionality - static constexpr IndexType kOutputDimensions = OutputDimensions; + static constexpr IndexType OutputDimensions = OutDims; // Size of forward propagation buffer used from the input layer to this layer - static constexpr std::size_t kBufferSize = 0; + static constexpr std::size_t BufferSize = 0; // Hash value embedded in the evaluation file - static constexpr std::uint32_t GetHashValue() { - std::uint32_t hash_value = 0xEC42E90Du; - hash_value ^= kOutputDimensions ^ (Offset << 10); - return hash_value; + static constexpr std::uint32_t get_hash_value() { + std::uint32_t hashValue = 0xEC42E90Du; + hashValue ^= OutputDimensions ^ (Offset << 10); + return hashValue; } // Read network parameters - bool ReadParameters(std::istream& /*stream*/) { + bool read_parameters(std::istream& /*stream*/) { return true; } // Forward propagation - const OutputType* Propagate( - const TransformedFeatureType* transformed_features, + const OutputType* propagate( + const TransformedFeatureType* transformedFeatures, char* /*buffer*/) const { - return transformed_features + Offset; + return transformedFeatures + Offset; } private: diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index 55fafa138fc..aeb5f2bdb08 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -29,9 +29,9 @@ namespace Stockfish::Eval::NNUE { enum AccumulatorState { EMPTY, COMPUTED, INIT }; // Class that holds the result of affine transformation of input features - struct alignas(kCacheLineSize) Accumulator { + struct alignas(CacheLineSize) Accumulator { std::int16_t - accumulation[2][kRefreshTriggers.size()][kTransformedFeatureDimensions]; + accumulation[2][RefreshTriggers.size()][TransformedFeatureDimensions]; AccumulatorState state[2]; }; diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index 1680368edb5..f59474df463 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -26,12 +26,12 @@ namespace Stockfish::Eval::NNUE { - static_assert(kTransformedFeatureDimensions % kMaxSimdWidth == 0, ""); - static_assert(Network::kOutputDimensions == 1, ""); + static_assert(TransformedFeatureDimensions % MaxSimdWidth == 0, ""); + static_assert(Network::OutputDimensions == 1, ""); static_assert(std::is_same::value, ""); // Trigger for full calculation instead of difference calculation - constexpr auto kRefreshTriggers = RawFeatures::kRefreshTriggers; + constexpr auto RefreshTriggers = RawFeatures::RefreshTriggers; } // namespace Stockfish::Eval::NNUE diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index 09a152a53c9..20eb27d402e 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -46,30 +46,30 @@ namespace Stockfish::Eval::NNUE { // Version of the evaluation file - constexpr std::uint32_t kVersion = 0x7AF32F16u; + constexpr std::uint32_t Version = 0x7AF32F16u; // Constant used in evaluation value calculation - constexpr int FV_SCALE = 16; - constexpr int kWeightScaleBits = 6; + constexpr int OutputScale = 16; + constexpr int WeightScaleBits = 6; // Size of cache line (in bytes) - constexpr std::size_t kCacheLineSize = 64; + constexpr std::size_t CacheLineSize = 64; // SIMD width (in bytes) #if defined(USE_AVX2) - constexpr std::size_t kSimdWidth = 32; + constexpr std::size_t SimdWidth = 32; #elif defined(USE_SSE2) - constexpr std::size_t kSimdWidth = 16; + constexpr std::size_t SimdWidth = 16; #elif defined(USE_MMX) - constexpr std::size_t kSimdWidth = 8; + constexpr std::size_t SimdWidth = 8; #elif defined(USE_NEON) - constexpr std::size_t kSimdWidth = 16; + constexpr std::size_t SimdWidth = 16; #endif - constexpr std::size_t kMaxSimdWidth = 32; + constexpr std::size_t MaxSimdWidth = 32; // unique number for each piece type on each square enum { @@ -84,19 +84,16 @@ namespace Stockfish::Eval::NNUE { PS_B_ROOK = 7 * SQUARE_NB + 1, PS_W_QUEEN = 8 * SQUARE_NB + 1, PS_B_QUEEN = 9 * SQUARE_NB + 1, - PS_W_KING = 10 * SQUARE_NB + 1, - PS_END = PS_W_KING, // pieces without kings (pawns included) - PS_B_KING = 11 * SQUARE_NB + 1, - PS_END2 = 12 * SQUARE_NB + 1 + PS_NB = 10 * SQUARE_NB + 1 }; - constexpr uint32_t kpp_board_index[COLOR_NB][PIECE_NB] = { + constexpr uint32_t PieceSquareIndex[COLOR_NB][PIECE_NB] = { // convention: W - us, B - them // viewed from other side, W and B are reversed - { PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_W_KING, PS_NONE, - PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_B_KING, PS_NONE }, - { PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_B_KING, PS_NONE, - PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_W_KING, PS_NONE } + { PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_NONE, PS_NONE, + PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_NONE, PS_NONE }, + { PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_NONE, PS_NONE, + PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_NONE, PS_NONE } }; // Type of input feature after conversion @@ -105,7 +102,7 @@ namespace Stockfish::Eval::NNUE { // Round n up to be a multiple of base template - constexpr IntType CeilToMultiple(IntType n, IntType base) { + constexpr IntType ceil_to_multiple(IntType n, IntType base) { return (n + base - 1) / base * base; } diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 1e0b0e6da55..de4b49374f9 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -40,7 +40,7 @@ namespace Stockfish::Eval::NNUE { #define vec_store(a,b) _mm512_store_si512(a,b) #define vec_add_16(a,b) _mm512_add_epi16(a,b) #define vec_sub_16(a,b) _mm512_sub_epi16(a,b) - static constexpr IndexType kNumRegs = 8; // only 8 are needed + static constexpr IndexType NumRegs = 8; // only 8 are needed #elif USE_AVX2 typedef __m256i vec_t; @@ -48,7 +48,7 @@ namespace Stockfish::Eval::NNUE { #define vec_store(a,b) _mm256_store_si256(a,b) #define vec_add_16(a,b) _mm256_add_epi16(a,b) #define vec_sub_16(a,b) _mm256_sub_epi16(a,b) - static constexpr IndexType kNumRegs = 16; + static constexpr IndexType NumRegs = 16; #elif USE_SSE2 typedef __m128i vec_t; @@ -56,7 +56,7 @@ namespace Stockfish::Eval::NNUE { #define vec_store(a,b) *(a)=(b) #define vec_add_16(a,b) _mm_add_epi16(a,b) #define vec_sub_16(a,b) _mm_sub_epi16(a,b) - static constexpr IndexType kNumRegs = Is64Bit ? 16 : 8; + static constexpr IndexType NumRegs = Is64Bit ? 16 : 8; #elif USE_MMX typedef __m64 vec_t; @@ -64,7 +64,7 @@ namespace Stockfish::Eval::NNUE { #define vec_store(a,b) *(a)=(b) #define vec_add_16(a,b) _mm_add_pi16(a,b) #define vec_sub_16(a,b) _mm_sub_pi16(a,b) - static constexpr IndexType kNumRegs = 8; + static constexpr IndexType NumRegs = 8; #elif USE_NEON typedef int16x8_t vec_t; @@ -72,7 +72,7 @@ namespace Stockfish::Eval::NNUE { #define vec_store(a,b) *(a)=(b) #define vec_add_16(a,b) vaddq_s16(a,b) #define vec_sub_16(a,b) vsubq_s16(a,b) - static constexpr IndexType kNumRegs = 16; + static constexpr IndexType NumRegs = 16; #else #undef VECTOR @@ -84,11 +84,11 @@ namespace Stockfish::Eval::NNUE { private: // Number of output dimensions for one side - static constexpr IndexType kHalfDimensions = kTransformedFeatureDimensions; + static constexpr IndexType HalfDimensions = TransformedFeatureDimensions; #ifdef VECTOR - static constexpr IndexType kTileHeight = kNumRegs * sizeof(vec_t) / 2; - static_assert(kHalfDimensions % kTileHeight == 0, "kTileHeight must divide kHalfDimensions"); + static constexpr IndexType TileHeight = NumRegs * sizeof(vec_t) / 2; + static_assert(HalfDimensions % TileHeight == 0, "TileHeight must divide HalfDimensions"); #endif public: @@ -96,95 +96,92 @@ namespace Stockfish::Eval::NNUE { using OutputType = TransformedFeatureType; // Number of input/output dimensions - static constexpr IndexType kInputDimensions = RawFeatures::kDimensions; - static constexpr IndexType kOutputDimensions = kHalfDimensions * 2; + static constexpr IndexType InputDimensions = RawFeatures::Dimensions; + static constexpr IndexType OutputDimensions = HalfDimensions * 2; // Size of forward propagation buffer - static constexpr std::size_t kBufferSize = - kOutputDimensions * sizeof(OutputType); + static constexpr std::size_t BufferSize = + OutputDimensions * sizeof(OutputType); // Hash value embedded in the evaluation file - static constexpr std::uint32_t GetHashValue() { - - return RawFeatures::kHashValue ^ kOutputDimensions; + static constexpr std::uint32_t get_hash_value() { + return RawFeatures::HashValue ^ OutputDimensions; } // Read network parameters - bool ReadParameters(std::istream& stream) { - - for (std::size_t i = 0; i < kHalfDimensions; ++i) - biases_[i] = read_little_endian(stream); - for (std::size_t i = 0; i < kHalfDimensions * kInputDimensions; ++i) - weights_[i] = read_little_endian(stream); + bool read_parameters(std::istream& stream) { + for (std::size_t i = 0; i < HalfDimensions; ++i) + biases[i] = read_little_endian(stream); + for (std::size_t i = 0; i < HalfDimensions * InputDimensions; ++i) + weights[i] = read_little_endian(stream); return !stream.fail(); } // Convert input features - void Transform(const Position& pos, OutputType* output) const { - - UpdateAccumulator(pos, WHITE); - UpdateAccumulator(pos, BLACK); + void transform(const Position& pos, OutputType* output) const { + update_accumulator(pos, WHITE); + update_accumulator(pos, BLACK); const auto& accumulation = pos.state()->accumulator.accumulation; #if defined(USE_AVX512) - constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth * 2); - static_assert(kHalfDimensions % (kSimdWidth * 2) == 0); - const __m512i kControl = _mm512_setr_epi64(0, 2, 4, 6, 1, 3, 5, 7); - const __m512i kZero = _mm512_setzero_si512(); + constexpr IndexType NumChunks = HalfDimensions / (SimdWidth * 2); + static_assert(HalfDimensions % (SimdWidth * 2) == 0); + const __m512i Control = _mm512_setr_epi64(0, 2, 4, 6, 1, 3, 5, 7); + const __m512i Zero = _mm512_setzero_si512(); #elif defined(USE_AVX2) - constexpr IndexType kNumChunks = kHalfDimensions / kSimdWidth; - constexpr int kControl = 0b11011000; - const __m256i kZero = _mm256_setzero_si256(); + constexpr IndexType NumChunks = HalfDimensions / SimdWidth; + constexpr int Control = 0b11011000; + const __m256i Zero = _mm256_setzero_si256(); #elif defined(USE_SSE2) - constexpr IndexType kNumChunks = kHalfDimensions / kSimdWidth; + constexpr IndexType NumChunks = HalfDimensions / SimdWidth; #ifdef USE_SSE41 - const __m128i kZero = _mm_setzero_si128(); + const __m128i Zero = _mm_setzero_si128(); #else const __m128i k0x80s = _mm_set1_epi8(-128); #endif #elif defined(USE_MMX) - constexpr IndexType kNumChunks = kHalfDimensions / kSimdWidth; + constexpr IndexType NumChunks = HalfDimensions / SimdWidth; const __m64 k0x80s = _mm_set1_pi8(-128); #elif defined(USE_NEON) - constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); - const int8x8_t kZero = {0}; + constexpr IndexType NumChunks = HalfDimensions / (SimdWidth / 2); + const int8x8_t Zero = {0}; #endif const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()}; for (IndexType p = 0; p < 2; ++p) { - const IndexType offset = kHalfDimensions * p; + const IndexType offset = HalfDimensions * p; #if defined(USE_AVX512) auto out = reinterpret_cast<__m512i*>(&output[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { + for (IndexType j = 0; j < NumChunks; ++j) { __m512i sum0 = _mm512_load_si512( &reinterpret_cast(accumulation[perspectives[p]][0])[j * 2 + 0]); __m512i sum1 = _mm512_load_si512( &reinterpret_cast(accumulation[perspectives[p]][0])[j * 2 + 1]); - _mm512_store_si512(&out[j], _mm512_permutexvar_epi64(kControl, - _mm512_max_epi8(_mm512_packs_epi16(sum0, sum1), kZero))); + _mm512_store_si512(&out[j], _mm512_permutexvar_epi64(Control, + _mm512_max_epi8(_mm512_packs_epi16(sum0, sum1), Zero))); } #elif defined(USE_AVX2) auto out = reinterpret_cast<__m256i*>(&output[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { + for (IndexType j = 0; j < NumChunks; ++j) { __m256i sum0 = _mm256_load_si256( &reinterpret_cast(accumulation[perspectives[p]][0])[j * 2 + 0]); __m256i sum1 = _mm256_load_si256( &reinterpret_cast(accumulation[perspectives[p]][0])[j * 2 + 1]); _mm256_store_si256(&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8( - _mm256_packs_epi16(sum0, sum1), kZero), kControl)); + _mm256_packs_epi16(sum0, sum1), Zero), Control)); } #elif defined(USE_SSE2) auto out = reinterpret_cast<__m128i*>(&output[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { + for (IndexType j = 0; j < NumChunks; ++j) { __m128i sum0 = _mm_load_si128(&reinterpret_cast( accumulation[perspectives[p]][0])[j * 2 + 0]); __m128i sum1 = _mm_load_si128(&reinterpret_cast( @@ -194,7 +191,7 @@ namespace Stockfish::Eval::NNUE { _mm_store_si128(&out[j], #ifdef USE_SSE41 - _mm_max_epi8(packedbytes, kZero) + _mm_max_epi8(packedbytes, Zero) #else _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s) #endif @@ -204,7 +201,7 @@ namespace Stockfish::Eval::NNUE { #elif defined(USE_MMX) auto out = reinterpret_cast<__m64*>(&output[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { + for (IndexType j = 0; j < NumChunks; ++j) { __m64 sum0 = *(&reinterpret_cast( accumulation[perspectives[p]][0])[j * 2 + 0]); __m64 sum1 = *(&reinterpret_cast( @@ -215,14 +212,14 @@ namespace Stockfish::Eval::NNUE { #elif defined(USE_NEON) const auto out = reinterpret_cast(&output[offset]); - for (IndexType j = 0; j < kNumChunks; ++j) { + for (IndexType j = 0; j < NumChunks; ++j) { int16x8_t sum = reinterpret_cast( accumulation[perspectives[p]][0])[j]; - out[j] = vmax_s8(vqmovn_s16(sum), kZero); + out[j] = vmax_s8(vqmovn_s16(sum), Zero); } #else - for (IndexType j = 0; j < kHalfDimensions; ++j) { + for (IndexType j = 0; j < HalfDimensions; ++j) { BiasType sum = accumulation[static_cast(perspectives[p])][0][j]; output[offset + j] = static_cast( std::max(0, std::min(127, sum))); @@ -236,12 +233,12 @@ namespace Stockfish::Eval::NNUE { } private: - void UpdateAccumulator(const Position& pos, const Color c) const { + void update_accumulator(const Position& pos, const Color c) const { #ifdef VECTOR // Gcc-10.2 unnecessarily spills AVX2 registers if this array // is defined in the VECTOR code below, once in each branch - vec_t acc[kNumRegs]; + vec_t acc[NumRegs]; #endif // Look for a usable accumulator of an earlier position. We keep track @@ -254,8 +251,8 @@ namespace Stockfish::Eval::NNUE { // The first condition tests whether an incremental update is // possible at all: if this side's king has moved, it is not possible. static_assert(std::is_same_v>, - "Current code assumes that only kFriendlyKingMoved refresh trigger is being used."); + Features::CompileTimeList>, + "Current code assumes that only FriendlyKingMoved refresh trigger is being used."); if ( dp.piece[0] == make_piece(c, KING) || (gain -= dp.dirty_num + 1) < 0) break; @@ -273,13 +270,13 @@ namespace Stockfish::Eval::NNUE { // Gather all features to be updated. This code assumes HalfKP features // only and doesn't support refresh triggers. - static_assert(std::is_same_v>, + static_assert(std::is_same_v>, RawFeatures>); Features::IndexList removed[2], added[2]; - Features::HalfKP::AppendChangedIndices(pos, + Features::HalfKP::append_changed_indices(pos, next->dirtyPiece, c, &removed[0], &added[0]); for (StateInfo *st2 = pos.state(); st2 != next; st2 = st2->previous) - Features::HalfKP::AppendChangedIndices(pos, + Features::HalfKP::append_changed_indices(pos, st2->dirtyPiece, c, &removed[1], &added[1]); // Mark the accumulators as computed. @@ -290,12 +287,12 @@ namespace Stockfish::Eval::NNUE { StateInfo *info[3] = { next, next == pos.state() ? nullptr : pos.state(), nullptr }; #ifdef VECTOR - for (IndexType j = 0; j < kHalfDimensions / kTileHeight; ++j) + for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) { // Load accumulator auto accTile = reinterpret_cast( - &st->accumulator.accumulation[c][0][j * kTileHeight]); - for (IndexType k = 0; k < kNumRegs; ++k) + &st->accumulator.accumulation[c][0][j * TileHeight]); + for (IndexType k = 0; k < NumRegs; ++k) acc[k] = vec_load(&accTile[k]); for (IndexType i = 0; info[i]; ++i) @@ -303,25 +300,25 @@ namespace Stockfish::Eval::NNUE { // Difference calculation for the deactivated features for (const auto index : removed[i]) { - const IndexType offset = kHalfDimensions * index + j * kTileHeight; - auto column = reinterpret_cast(&weights_[offset]); - for (IndexType k = 0; k < kNumRegs; ++k) + const IndexType offset = HalfDimensions * index + j * TileHeight; + auto column = reinterpret_cast(&weights[offset]); + for (IndexType k = 0; k < NumRegs; ++k) acc[k] = vec_sub_16(acc[k], column[k]); } // Difference calculation for the activated features for (const auto index : added[i]) { - const IndexType offset = kHalfDimensions * index + j * kTileHeight; - auto column = reinterpret_cast(&weights_[offset]); - for (IndexType k = 0; k < kNumRegs; ++k) + const IndexType offset = HalfDimensions * index + j * TileHeight; + auto column = reinterpret_cast(&weights[offset]); + for (IndexType k = 0; k < NumRegs; ++k) acc[k] = vec_add_16(acc[k], column[k]); } // Store accumulator accTile = reinterpret_cast( - &info[i]->accumulator.accumulation[c][0][j * kTileHeight]); - for (IndexType k = 0; k < kNumRegs; ++k) + &info[i]->accumulator.accumulation[c][0][j * TileHeight]); + for (IndexType k = 0; k < NumRegs; ++k) vec_store(&accTile[k], acc[k]); } } @@ -331,25 +328,25 @@ namespace Stockfish::Eval::NNUE { { std::memcpy(info[i]->accumulator.accumulation[c][0], st->accumulator.accumulation[c][0], - kHalfDimensions * sizeof(BiasType)); + HalfDimensions * sizeof(BiasType)); st = info[i]; // Difference calculation for the deactivated features for (const auto index : removed[i]) { - const IndexType offset = kHalfDimensions * index; + const IndexType offset = HalfDimensions * index; - for (IndexType j = 0; j < kHalfDimensions; ++j) - st->accumulator.accumulation[c][0][j] -= weights_[offset + j]; + for (IndexType j = 0; j < HalfDimensions; ++j) + st->accumulator.accumulation[c][0][j] -= weights[offset + j]; } // Difference calculation for the activated features for (const auto index : added[i]) { - const IndexType offset = kHalfDimensions * index; + const IndexType offset = HalfDimensions * index; - for (IndexType j = 0; j < kHalfDimensions; ++j) - st->accumulator.accumulation[c][0][j] += weights_[offset + j]; + for (IndexType j = 0; j < HalfDimensions; ++j) + st->accumulator.accumulation[c][0][j] += weights[offset + j]; } } #endif @@ -360,41 +357,41 @@ namespace Stockfish::Eval::NNUE { auto& accumulator = pos.state()->accumulator; accumulator.state[c] = COMPUTED; Features::IndexList active; - Features::HalfKP::AppendActiveIndices(pos, c, &active); + Features::HalfKP::append_active_indices(pos, c, &active); #ifdef VECTOR - for (IndexType j = 0; j < kHalfDimensions / kTileHeight; ++j) + for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) { auto biasesTile = reinterpret_cast( - &biases_[j * kTileHeight]); - for (IndexType k = 0; k < kNumRegs; ++k) + &biases[j * TileHeight]); + for (IndexType k = 0; k < NumRegs; ++k) acc[k] = biasesTile[k]; for (const auto index : active) { - const IndexType offset = kHalfDimensions * index + j * kTileHeight; - auto column = reinterpret_cast(&weights_[offset]); + const IndexType offset = HalfDimensions * index + j * TileHeight; + auto column = reinterpret_cast(&weights[offset]); - for (unsigned k = 0; k < kNumRegs; ++k) + for (unsigned k = 0; k < NumRegs; ++k) acc[k] = vec_add_16(acc[k], column[k]); } auto accTile = reinterpret_cast( - &accumulator.accumulation[c][0][j * kTileHeight]); - for (unsigned k = 0; k < kNumRegs; k++) + &accumulator.accumulation[c][0][j * TileHeight]); + for (unsigned k = 0; k < NumRegs; k++) vec_store(&accTile[k], acc[k]); } #else - std::memcpy(accumulator.accumulation[c][0], biases_, - kHalfDimensions * sizeof(BiasType)); + std::memcpy(accumulator.accumulation[c][0], biases, + HalfDimensions * sizeof(BiasType)); for (const auto index : active) { - const IndexType offset = kHalfDimensions * index; + const IndexType offset = HalfDimensions * index; - for (IndexType j = 0; j < kHalfDimensions; ++j) - accumulator.accumulation[c][0][j] += weights_[offset + j]; + for (IndexType j = 0; j < HalfDimensions; ++j) + accumulator.accumulation[c][0][j] += weights[offset + j]; } #endif } @@ -407,9 +404,9 @@ namespace Stockfish::Eval::NNUE { using BiasType = std::int16_t; using WeightType = std::int16_t; - alignas(kCacheLineSize) BiasType biases_[kHalfDimensions]; - alignas(kCacheLineSize) - WeightType weights_[kHalfDimensions * kInputDimensions]; + alignas(CacheLineSize) BiasType biases[HalfDimensions]; + alignas(CacheLineSize) + WeightType weights[HalfDimensions * InputDimensions]; }; } // namespace Stockfish::Eval::NNUE diff --git a/src/position.cpp b/src/position.cpp index ec356ace9b0..2b3be3f7767 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -79,7 +79,7 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) { && !pos.can_castle(ANY_CASTLING)) { StateInfo st; - ASSERT_ALIGNED(&st, Eval::NNUE::kCacheLineSize); + ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize); Position p; p.set(pos.fen(), pos.is_chess960(), &st, pos.this_thread()); @@ -1315,7 +1315,7 @@ bool Position::pos_is_ok() const { assert(0 && "pos_is_ok: Bitboards"); StateInfo si = *st; - ASSERT_ALIGNED(&si, Eval::NNUE::kCacheLineSize); + ASSERT_ALIGNED(&si, Eval::NNUE::CacheLineSize); set_state(&si); if (std::memcmp(&si, st, sizeof(StateInfo))) diff --git a/src/search.cpp b/src/search.cpp index c9ee47fea11..1d841023039 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -165,7 +165,7 @@ namespace { uint64_t perft(Position& pos, Depth depth) { StateInfo st; - ASSERT_ALIGNED(&st, Eval::NNUE::kCacheLineSize); + ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize); uint64_t cnt, nodes = 0; const bool leaf = (depth == 2); @@ -597,7 +597,7 @@ namespace { Move pv[MAX_PLY+1], capturesSearched[32], quietsSearched[64]; StateInfo st; - ASSERT_ALIGNED(&st, Eval::NNUE::kCacheLineSize); + ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize); TTEntry* tte; Key posKey; @@ -1458,7 +1458,7 @@ namespace { Move pv[MAX_PLY+1]; StateInfo st; - ASSERT_ALIGNED(&st, Eval::NNUE::kCacheLineSize); + ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize); TTEntry* tte; Key posKey; @@ -1964,7 +1964,7 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) { bool RootMove::extract_ponder_from_tt(Position& pos) { StateInfo st; - ASSERT_ALIGNED(&st, Eval::NNUE::kCacheLineSize); + ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize); bool ttHit; From 32d781769db9f017405fe422bfdc702069fb0422 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Thu, 22 Apr 2021 14:07:37 -0300 Subject: [PATCH 0538/1766] Merge all move generators Merging `generate` and `generate` into `generate_all()`. verified to yield correct perft results, even though bench changes due to different order of generated moves. No regresion playing games: passed STC: LLR: 2.94 (-2.94,2.94) {-1.00,0.20} Total: 161800 W: 14585 L: 14624 D: 132591 Ptnml(0-2): 577, 11681, 56451, 11586, 605 https://tests.stockfishchess.org/tests/view/606532732b2df919fd5f026d passed LTC: LLR: 2.98 (-2.94,2.94) {-0.70,0.20} Total: 188504 W: 6906 L: 6961 D: 174637 Ptnml(0-2): 87, 6272, 81610, 6175, 108 https://tests.stockfishchess.org/tests/view/6065b0772b2df919fd5f02ae closes https://github.com/official-stockfish/Stockfish/pull/3418 Bench: 4536129 --- src/movegen.cpp | 119 +++++++++++++----------------------------------- 1 file changed, 31 insertions(+), 88 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index 5049613617a..bd9d0b62409 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -63,8 +63,8 @@ namespace { Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB; Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB; - Bitboard enemies = (Type == EVASIONS ? pos.checkers(): - Type == CAPTURES ? target : pos.pieces(Them)); + Bitboard enemies = (Type == EVASIONS ? pos.checkers() + : Type == CAPTURES ? target : pos.pieces(Them)); // Single and double pawn pushes, no promotions if (Type != CAPTURES) @@ -175,19 +175,19 @@ namespace { } - template - ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard piecesToMove, Bitboard target) { + template + ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) { static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()"); - Bitboard bb = piecesToMove & pos.pieces(Pt); + Bitboard bb = pos.pieces(Us, Pt); while (bb) { Square from = pop_lsb(bb); Bitboard b = attacks_bb(from, pos.pieces()) & target; - if constexpr (Checks) + if (Checks && (Pt == QUEEN || !(pos.blockers_for_king(~Us) & from))) b &= pos.check_squares(Pt); while (b) @@ -204,42 +204,34 @@ namespace { static_assert(Type != LEGAL, "Unsupported type in generate_all()"); constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantiations - Bitboard target, piecesToMove = pos.pieces(Us); + const Square ksq = pos.square(Us); + Bitboard target; - if(Type == QUIET_CHECKS) - piecesToMove &= ~pos.blockers_for_king(~Us); + if (Type == EVASIONS && more_than_one(pos.checkers())) + goto kingMoves; // Double check, only a king move can save the day - switch (Type) - { - case CAPTURES: - target = pos.pieces(~Us); - break; - case QUIETS: - case QUIET_CHECKS: - target = ~pos.pieces(); - break; - case EVASIONS: - target = between_bb(pos.square(Us), lsb(pos.checkers())); - break; - case NON_EVASIONS: - target = ~pos.pieces(Us); - break; - } + target = Type == EVASIONS ? between_bb(ksq, lsb(pos.checkers())) + : Type == NON_EVASIONS ? ~pos.pieces( Us) + : Type == CAPTURES ? pos.pieces(~Us) + : ~pos.pieces( ); // QUIETS || QUIET_CHECKS moveList = generate_pawn_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, piecesToMove, target); - moveList = generate_moves(pos, moveList, piecesToMove, target); - moveList = generate_moves< ROOK, Checks>(pos, moveList, piecesToMove, target); - moveList = generate_moves< QUEEN, Checks>(pos, moveList, piecesToMove, target); + moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); - if (Type != QUIET_CHECKS && Type != EVASIONS) +kingMoves: + if (!Checks || pos.blockers_for_king(~Us) & ksq) { - Square ksq = pos.square(Us); - Bitboard b = attacks_bb(ksq) & target; + Bitboard b = attacks_bb(ksq) & (Type == EVASIONS ? ~pos.pieces(Us) : target); + if (Checks) + b &= ~attacks_bb(pos.square(~Us)); + while (b) *moveList++ = make_move(ksq, pop_lsb(b)); - if ((Type != CAPTURES) && pos.can_castle(Us & ANY_CASTLING)) + if ((Type == QUIETS || Type == NON_EVASIONS) && pos.can_castle(Us & ANY_CASTLING)) for (CastlingRights cr : { Us & KING_SIDE, Us & QUEEN_SIDE } ) if (!pos.castling_impeded(cr) && pos.can_castle(cr)) *moveList++ = make(ksq, pos.castling_rook_square(cr)); @@ -253,6 +245,8 @@ namespace { /// Generates all pseudo-legal captures plus queen and checking knight promotions /// Generates all pseudo-legal non-captures and underpromotions (except checking knight) +/// Generates all pseudo-legal check evasions when the side to move is in check +/// Generates all pseudo-legal non-captures giving check, except castling /// Generates all pseudo-legal captures and non-captures /// /// Returns a pointer to the end of the move list. @@ -260,8 +254,8 @@ namespace { template ExtMove* generate(const Position& pos, ExtMove* moveList) { - static_assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS, "Unsupported type in generate()"); - assert(!pos.checkers()); + static_assert(Type != LEGAL, "Unsupported type in generate()"); + assert((Type == EVASIONS) == (bool)pos.checkers()); Color us = pos.side_to_move(); @@ -272,62 +266,11 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { // Explicit template instantiations template ExtMove* generate(const Position&, ExtMove*); template ExtMove* generate(const Position&, ExtMove*); +template ExtMove* generate(const Position&, ExtMove*); +template ExtMove* generate(const Position&, ExtMove*); template ExtMove* generate(const Position&, ExtMove*); -/// generate generates all pseudo-legal non-captures giving check, -/// except castling. Returns a pointer to the end of the move list. -template<> -ExtMove* generate(const Position& pos, ExtMove* moveList) { - - assert(!pos.checkers()); - - Color us = pos.side_to_move(); - Bitboard dc = pos.blockers_for_king(~us) & pos.pieces(us) & ~pos.pieces(PAWN); - - while (dc) - { - Square from = pop_lsb(dc); - PieceType pt = type_of(pos.piece_on(from)); - - Bitboard b = attacks_bb(pt, from, pos.pieces()) & ~pos.pieces(); - - if (pt == KING) - b &= ~attacks_bb(pos.square(~us)); - - while (b) - *moveList++ = make_move(from, pop_lsb(b)); - } - - return us == WHITE ? generate_all(pos, moveList) - : generate_all(pos, moveList); -} - - -/// generate generates all pseudo-legal check evasions when the side -/// to move is in check. Returns a pointer to the end of the move list. -template<> -ExtMove* generate(const Position& pos, ExtMove* moveList) { - - assert(pos.checkers()); - - Color us = pos.side_to_move(); - Square ksq = pos.square(us); - - // Generate evasions for king - Bitboard b = attacks_bb(ksq) & ~pos.pieces(us); - while (b) - *moveList++ = make_move(ksq, pop_lsb(b)); - - if (more_than_one(pos.checkers())) - return moveList; // Double check, only a king move can save the day - - // Generate blocking interpositions or captures of the checking piece - return us == WHITE ? generate_all(pos, moveList) - : generate_all(pos, moveList); -} - - /// generate generates all the legal moves in the given position template<> From b748b46714d5f8e0acca0a042ede1fc95e4f5190 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Sat, 24 Apr 2021 15:08:11 +0200 Subject: [PATCH 0539/1766] Cleanup and simplify NNUE code. A lot of optimizations happend since the NNUE was introduced and since then some parts of the code were left unused. This got to the point where asserts were have to be made just to let people know that modifying something will not have any effects or may even break everything due to the assumptions being made. Removing these parts removes those inexisting "false dependencies". Additionally: * append_changed_indices now takes the king pos and stateinfo explicitly, no more misleading pos parameter * IndexList is removed in favor of a generic ValueList. Feature transformer just instantiates the type it needs. * The update cost and refresh requirement is deferred to the feature set once again, but now doesn't go through the whole FeatureSet machinery and just calls HalfKP directly. * accumulator no longer has a singular dimension. * The PS constants and the PieceSquareIndex array are made local to the HalfKP feature set because they are specific to it and DO differ for other feature sets. * A few names are changed to more descriptive Passed STC non-regression: https://tests.stockfishchess.org/tests/view/608421dd95e7f1852abd2790 LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 180008 W: 16186 L: 16258 D: 147564 Ptnml(0-2): 587, 12593, 63725, 12503, 596 closes https://github.com/official-stockfish/Stockfish/pull/3441 No functional change --- src/misc.h | 43 ++++++++ src/nnue/architectures/halfkp_256x2-32-32.h | 54 ---------- src/nnue/features/feature_set.h | 69 ------------ src/nnue/features/features_common.h | 45 -------- src/nnue/features/half_kp.cpp | 65 ++++++------ src/nnue/features/half_kp.h | 75 ++++++++++--- src/nnue/features/index_list.h | 64 ------------ src/nnue/nnue_accumulator.h | 2 +- src/nnue/nnue_architecture.h | 30 +++++- src/nnue/nnue_common.h | 25 ----- src/nnue/nnue_feature_transformer.h | 110 ++++++++++---------- 11 files changed, 219 insertions(+), 363 deletions(-) delete mode 100644 src/nnue/architectures/halfkp_256x2-32-32.h delete mode 100644 src/nnue/features/feature_set.h delete mode 100644 src/nnue/features/features_common.h delete mode 100644 src/nnue/features/index_list.h diff --git a/src/misc.h b/src/misc.h index f834e470cbf..59ca6e376bd 100644 --- a/src/misc.h +++ b/src/misc.h @@ -78,6 +78,49 @@ T* align_ptr_up(T* ptr) return reinterpret_cast(reinterpret_cast((ptrint + (Alignment - 1)) / Alignment * Alignment)); } +template +class ValueListInserter { +public: + ValueListInserter(T* v, std::size_t& s) : + values(v), + size(&s) + { + } + + void push_back(const T& value) { values[(*size)++] = value; } +private: + T* values; + std::size_t* size; +}; + +template +class ValueList { + +public: + std::size_t size() const { return size_; } + void resize(std::size_t newSize) { size_ = newSize; } + void push_back(const T& value) { values_[size_++] = value; } + T& operator[](std::size_t index) { return values_[index]; } + T* begin() { return values_; } + T* end() { return values_ + size_; } + const T& operator[](std::size_t index) const { return values_[index]; } + const T* begin() const { return values_; } + const T* end() const { return values_ + size_; } + operator ValueListInserter() { return ValueListInserter(values_, size_); } + + void swap(ValueList& other) { + const std::size_t maxSize = std::max(size_, other.size_); + for (std::size_t i = 0; i < maxSize; ++i) { + std::swap(values_[i], other.values_[i]); + } + std::swap(size_, other.size_); + } + +private: + T values_[MaxSize]; + std::size_t size_ = 0; +}; + /// xorshift64star Pseudo-Random Number Generator /// This class is based on original code written and dedicated /// to the public domain by Sebastiano Vigna (2014). diff --git a/src/nnue/architectures/halfkp_256x2-32-32.h b/src/nnue/architectures/halfkp_256x2-32-32.h deleted file mode 100644 index 5f6cc7f336d..00000000000 --- a/src/nnue/architectures/halfkp_256x2-32-32.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -// Definition of input features and network structure used in NNUE evaluation function - -#ifndef NNUE_HALFKP_256X2_32_32_H_INCLUDED -#define NNUE_HALFKP_256X2_32_32_H_INCLUDED - -#include "../features/feature_set.h" -#include "../features/half_kp.h" - -#include "../layers/input_slice.h" -#include "../layers/affine_transform.h" -#include "../layers/clipped_relu.h" - -namespace Stockfish::Eval::NNUE { - -// Input features used in evaluation function -using RawFeatures = Features::FeatureSet< - Features::HalfKP>; - -// Number of input feature dimensions after conversion -constexpr IndexType TransformedFeatureDimensions = 256; - -namespace Layers { - -// Define network structure -using InputLayer = InputSlice; -using HiddenLayer1 = ClippedReLU>; -using HiddenLayer2 = ClippedReLU>; -using OutputLayer = AffineTransform; - -} // namespace Layers - -using Network = Layers::OutputLayer; - -} // namespace Stockfish::Eval::NNUE - -#endif // #ifndef NNUE_HALFKP_256X2_32_32_H_INCLUDED diff --git a/src/nnue/features/feature_set.h b/src/nnue/features/feature_set.h deleted file mode 100644 index d09f9b9474d..00000000000 --- a/src/nnue/features/feature_set.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -// A class template that represents the input feature set of the NNUE evaluation function - -#ifndef NNUE_FEATURE_SET_H_INCLUDED -#define NNUE_FEATURE_SET_H_INCLUDED - -#include "features_common.h" -#include - -namespace Stockfish::Eval::NNUE::Features { - - // Class template that represents a list of values - template - struct CompileTimeList; - - template - struct CompileTimeList { - static constexpr bool Contains(T value) { - return value == First || CompileTimeList::Contains(value); - } - static constexpr std::array - Values = {{First, Remaining...}}; - }; - - // Base class of feature set - template - class FeatureSetBase { - - }; - - // Class template that represents the feature set - template - class FeatureSet : public FeatureSetBase> { - - public: - // Hash value embedded in the evaluation file - static constexpr std::uint32_t HashValue = FeatureType::HashValue; - // Number of feature dimensions - static constexpr IndexType Dimensions = FeatureType::Dimensions; - // Maximum number of simultaneously active features - static constexpr IndexType MaxActiveDimensions = - FeatureType::MaxActiveDimensions; - // Trigger for full calculation instead of difference calculation - using SortedTriggerSet = - CompileTimeList; - static constexpr auto RefreshTriggers = SortedTriggerSet::Values; - - }; - -} // namespace Stockfish::Eval::NNUE::Features - -#endif // #ifndef NNUE_FEATURE_SET_H_INCLUDED diff --git a/src/nnue/features/features_common.h b/src/nnue/features/features_common.h deleted file mode 100644 index 9584cac8825..00000000000 --- a/src/nnue/features/features_common.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -//Common header of input features of NNUE evaluation function - -#ifndef NNUE_FEATURES_COMMON_H_INCLUDED -#define NNUE_FEATURES_COMMON_H_INCLUDED - -#include "../../evaluate.h" -#include "../nnue_common.h" - -namespace Stockfish::Eval::NNUE::Features { - - class IndexList; - - template - class FeatureSet; - - // Trigger to perform full calculations instead of difference only - enum class TriggerEvent { - FriendKingMoved // calculate full evaluation when own king moves - }; - - enum class Side { - Friend // side to move - }; - -} // namespace Stockfish::Eval::NNUE::Features - -#endif // #ifndef NNUE_FEATURES_COMMON_H_INCLUDED diff --git a/src/nnue/features/half_kp.cpp b/src/nnue/features/half_kp.cpp index 5c7538de4a1..aa1deceece2 100644 --- a/src/nnue/features/half_kp.cpp +++ b/src/nnue/features/half_kp.cpp @@ -19,69 +19,68 @@ //Definition of input features HalfKP of NNUE evaluation function #include "half_kp.h" -#include "index_list.h" + +#include "../../position.h" namespace Stockfish::Eval::NNUE::Features { // Orient a square according to perspective (rotates by 180 for black) - inline Square orient(Color perspective, Square s) { + inline Square HalfKP::orient(Color perspective, Square s) { return Square(int(s) ^ (bool(perspective) * 63)); } // Index of a feature for a given king position and another piece on some square - inline IndexType make_index(Color perspective, Square s, Piece pc, Square ksq) { + inline IndexType HalfKP::make_index(Color perspective, Square s, Piece pc, Square ksq) { return IndexType(orient(perspective, s) + PieceSquareIndex[perspective][pc] + PS_NB * ksq); } // Get a list of indices for active features - template - void HalfKP::append_active_indices( - const Position& pos, Color perspective, IndexList* active) { - + void HalfKP::append_active_indices( + const Position& pos, + Color perspective, + ValueListInserter active + ) { Square ksq = orient(perspective, pos.square(perspective)); Bitboard bb = pos.pieces() & ~pos.pieces(KING); while (bb) { Square s = pop_lsb(bb); - active->push_back(make_index(perspective, s, pos.piece_on(s), ksq)); + active.push_back(make_index(perspective, s, pos.piece_on(s), ksq)); } } // append_changed_indices() : get a list of indices for recently changed features - // IMPORTANT: The `pos` in this function is pretty much useless as it - // is not always the position the features are updated to. The feature - // transformer code right now can update multiple accumulators per move, - // but since Stockfish only keeps the full state of the current leaf - // search position it is not possible to always pass here the position for - // which the accumulator is being updated. Therefore the only thing that - // can be reliably extracted from `pos` is the king square for the king - // of the `perspective` color (note: not even the other king's square will - // match reality in all cases, this is also the reason why `dp` is passed - // as a parameter and not extracted from pos.state()). This is of particular - // problem for future nets with other feature sets, where updating the active - // feature might require more information from the intermediate positions. In - // this case the only easy solution is to remove the multiple updates from - // the feature transformer update code and only update the accumulator for - // the current leaf position (the position after the move). - - template - void HalfKP::append_changed_indices( - const Position& pos, const DirtyPiece& dp, Color perspective, - IndexList* removed, IndexList* added) { - - Square ksq = orient(perspective, pos.square(perspective)); + void HalfKP::append_changed_indices( + Square ksq, + StateInfo* st, + Color perspective, + ValueListInserter removed, + ValueListInserter added + ) { + const auto& dp = st->dirtyPiece; + Square oriented_ksq = orient(perspective, ksq); for (int i = 0; i < dp.dirty_num; ++i) { Piece pc = dp.piece[i]; if (type_of(pc) == KING) continue; if (dp.from[i] != SQ_NONE) - removed->push_back(make_index(perspective, dp.from[i], pc, ksq)); + removed.push_back(make_index(perspective, dp.from[i], pc, oriented_ksq)); if (dp.to[i] != SQ_NONE) - added->push_back(make_index(perspective, dp.to[i], pc, ksq)); + added.push_back(make_index(perspective, dp.to[i], pc, oriented_ksq)); } } - template class HalfKP; + int HalfKP::update_cost(StateInfo* st) { + return st->dirtyPiece.dirty_num; + } + + int HalfKP::refresh_cost(const Position& pos) { + return pos.count() - 2; + } + + bool HalfKP::requires_refresh(StateInfo* st, Color perspective) { + return st->dirtyPiece.piece[0] == make_piece(perspective, KING); + } } // namespace Stockfish::Eval::NNUE::Features diff --git a/src/nnue/features/half_kp.h b/src/nnue/features/half_kp.h index 14efb0895ed..a09c221b1ac 100644 --- a/src/nnue/features/half_kp.h +++ b/src/nnue/features/half_kp.h @@ -21,37 +21,88 @@ #ifndef NNUE_FEATURES_HALF_KP_H_INCLUDED #define NNUE_FEATURES_HALF_KP_H_INCLUDED +#include "../nnue_common.h" + #include "../../evaluate.h" -#include "features_common.h" +#include "../../misc.h" + +namespace Stockfish { + struct StateInfo; +} namespace Stockfish::Eval::NNUE::Features { // Feature HalfKP: Combination of the position of own king // and the position of pieces other than kings - template class HalfKP { + // unique number for each piece type on each square + enum { + PS_NONE = 0, + PS_W_PAWN = 1, + PS_B_PAWN = 1 * SQUARE_NB + 1, + PS_W_KNIGHT = 2 * SQUARE_NB + 1, + PS_B_KNIGHT = 3 * SQUARE_NB + 1, + PS_W_BISHOP = 4 * SQUARE_NB + 1, + PS_B_BISHOP = 5 * SQUARE_NB + 1, + PS_W_ROOK = 6 * SQUARE_NB + 1, + PS_B_ROOK = 7 * SQUARE_NB + 1, + PS_W_QUEEN = 8 * SQUARE_NB + 1, + PS_B_QUEEN = 9 * SQUARE_NB + 1, + PS_NB = 10 * SQUARE_NB + 1 + }; + + static constexpr IndexType PieceSquareIndex[COLOR_NB][PIECE_NB] = { + // convention: W - us, B - them + // viewed from other side, W and B are reversed + { PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_NONE, PS_NONE, + PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_NONE, PS_NONE }, + { PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_NONE, PS_NONE, + PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_NONE, PS_NONE } + }; + + // Orient a square according to perspective (rotates by 180 for black) + static Square orient(Color perspective, Square s); + + // Index of a feature for a given king position and another piece on some square + static IndexType make_index(Color perspective, Square s, Piece pc, Square ksq); + public: // Feature name static constexpr const char* Name = "HalfKP(Friend)"; + // Hash value embedded in the evaluation file - static constexpr std::uint32_t HashValue = - 0x5D69D5B9u ^ (AssociatedKing == Side::Friend); + static constexpr std::uint32_t HashValue = 0x5D69D5B8u; + // Number of feature dimensions static constexpr IndexType Dimensions = static_cast(SQUARE_NB) * static_cast(PS_NB); - // Maximum number of simultaneously active features - static constexpr IndexType MaxActiveDimensions = 30; // Kings don't count - // Trigger for full calculation instead of difference calculation - static constexpr TriggerEvent RefreshTrigger = TriggerEvent::FriendKingMoved; + + // Maximum number of simultaneously active features. 30 because kins are not included. + static constexpr IndexType MaxActiveDimensions = 30; // Get a list of indices for active features - static void append_active_indices(const Position& pos, Color perspective, - IndexList* active); + static void append_active_indices( + const Position& pos, + Color perspective, + ValueListInserter active); // Get a list of indices for recently changed features - static void append_changed_indices(const Position& pos, const DirtyPiece& dp, Color perspective, - IndexList* removed, IndexList* added); + static void append_changed_indices( + Square ksq, + StateInfo* st, + Color perspective, + ValueListInserter removed, + ValueListInserter added); + + // Returns the cost of updating one perspective, the most costly one. + // Assumes no refresh needed. + static int update_cost(StateInfo* st); + static int refresh_cost(const Position& pos); + + // Returns whether the change stored in this StateInfo means that + // a full accumulator refresh is required. + static bool requires_refresh(StateInfo* st, Color perspective); }; } // namespace Stockfish::Eval::NNUE::Features diff --git a/src/nnue/features/index_list.h b/src/nnue/features/index_list.h deleted file mode 100644 index edf0add1b45..00000000000 --- a/src/nnue/features/index_list.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -// Definition of index list of input features - -#ifndef NNUE_FEATURES_INDEX_LIST_H_INCLUDED -#define NNUE_FEATURES_INDEX_LIST_H_INCLUDED - -#include "../../position.h" -#include "../nnue_architecture.h" - -namespace Stockfish::Eval::NNUE::Features { - - // Class template used for feature index list - template - class ValueList { - - public: - std::size_t size() const { return size_; } - void resize(std::size_t size) { size_ = size; } - void push_back(const T& value) { values_[size_++] = value; } - T& operator[](std::size_t index) { return values_[index]; } - T* begin() { return values_; } - T* end() { return values_ + size_; } - const T& operator[](std::size_t index) const { return values_[index]; } - const T* begin() const { return values_; } - const T* end() const { return values_ + size_; } - - void swap(ValueList& other) { - const std::size_t max_size = std::max(size_, other.size_); - for (std::size_t i = 0; i < max_size; ++i) { - std::swap(values_[i], other.values_[i]); - } - std::swap(size_, other.size_); - } - - private: - T values_[MaxSize]; - std::size_t size_ = 0; - }; - - //Type of feature index list - class IndexList - : public ValueList { - }; - -} // namespace Stockfish::Eval::NNUE::Features - -#endif // NNUE_FEATURES_INDEX_LIST_H_INCLUDED diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index aeb5f2bdb08..72a151f8eaa 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -31,7 +31,7 @@ namespace Stockfish::Eval::NNUE { // Class that holds the result of affine transformation of input features struct alignas(CacheLineSize) Accumulator { std::int16_t - accumulation[2][RefreshTriggers.size()][TransformedFeatureDimensions]; + accumulation[2][TransformedFeatureDimensions]; AccumulatorState state[2]; }; diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index f59474df463..55a01fbe15d 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -21,18 +21,38 @@ #ifndef NNUE_ARCHITECTURE_H_INCLUDED #define NNUE_ARCHITECTURE_H_INCLUDED -// Defines the network structure -#include "architectures/halfkp_256x2-32-32.h" +#include "nnue_common.h" + +#include "features/half_kp.h" + +#include "layers/input_slice.h" +#include "layers/affine_transform.h" +#include "layers/clipped_relu.h" namespace Stockfish::Eval::NNUE { + // Input features used in evaluation function + using FeatureSet = Features::HalfKP; + + // Number of input feature dimensions after conversion + constexpr IndexType TransformedFeatureDimensions = 256; + + namespace Layers { + + // Define network structure + using InputLayer = InputSlice; + using HiddenLayer1 = ClippedReLU>; + using HiddenLayer2 = ClippedReLU>; + using OutputLayer = AffineTransform; + + } // namespace Layers + + using Network = Layers::OutputLayer; + static_assert(TransformedFeatureDimensions % MaxSimdWidth == 0, ""); static_assert(Network::OutputDimensions == 1, ""); static_assert(std::is_same::value, ""); - // Trigger for full calculation instead of difference calculation - constexpr auto RefreshTriggers = RawFeatures::RefreshTriggers; - } // namespace Stockfish::Eval::NNUE #endif // #ifndef NNUE_ARCHITECTURE_H_INCLUDED diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index 20eb27d402e..8c54f9baeeb 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -71,31 +71,6 @@ namespace Stockfish::Eval::NNUE { constexpr std::size_t MaxSimdWidth = 32; - // unique number for each piece type on each square - enum { - PS_NONE = 0, - PS_W_PAWN = 1, - PS_B_PAWN = 1 * SQUARE_NB + 1, - PS_W_KNIGHT = 2 * SQUARE_NB + 1, - PS_B_KNIGHT = 3 * SQUARE_NB + 1, - PS_W_BISHOP = 4 * SQUARE_NB + 1, - PS_B_BISHOP = 5 * SQUARE_NB + 1, - PS_W_ROOK = 6 * SQUARE_NB + 1, - PS_B_ROOK = 7 * SQUARE_NB + 1, - PS_W_QUEEN = 8 * SQUARE_NB + 1, - PS_B_QUEEN = 9 * SQUARE_NB + 1, - PS_NB = 10 * SQUARE_NB + 1 - }; - - constexpr uint32_t PieceSquareIndex[COLOR_NB][PIECE_NB] = { - // convention: W - us, B - them - // viewed from other side, W and B are reversed - { PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_NONE, PS_NONE, - PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_NONE, PS_NONE }, - { PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_NONE, PS_NONE, - PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_NONE, PS_NONE } - }; - // Type of input feature after conversion using TransformedFeatureType = std::uint8_t; using IndexType = std::uint32_t; diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index de4b49374f9..f4412749153 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -23,7 +23,8 @@ #include "nnue_common.h" #include "nnue_architecture.h" -#include "features/index_list.h" + +#include "../misc.h" #include // std::memset() @@ -96,7 +97,7 @@ namespace Stockfish::Eval::NNUE { using OutputType = TransformedFeatureType; // Number of input/output dimensions - static constexpr IndexType InputDimensions = RawFeatures::Dimensions; + static constexpr IndexType InputDimensions = FeatureSet::Dimensions; static constexpr IndexType OutputDimensions = HalfDimensions * 2; // Size of forward propagation buffer @@ -105,7 +106,7 @@ namespace Stockfish::Eval::NNUE { // Hash value embedded in the evaluation file static constexpr std::uint32_t get_hash_value() { - return RawFeatures::HashValue ^ OutputDimensions; + return FeatureSet::HashValue ^ OutputDimensions; } // Read network parameters @@ -161,9 +162,9 @@ namespace Stockfish::Eval::NNUE { auto out = reinterpret_cast<__m512i*>(&output[offset]); for (IndexType j = 0; j < NumChunks; ++j) { __m512i sum0 = _mm512_load_si512( - &reinterpret_cast(accumulation[perspectives[p]][0])[j * 2 + 0]); + &reinterpret_cast(accumulation[perspectives[p]])[j * 2 + 0]); __m512i sum1 = _mm512_load_si512( - &reinterpret_cast(accumulation[perspectives[p]][0])[j * 2 + 1]); + &reinterpret_cast(accumulation[perspectives[p]])[j * 2 + 1]); _mm512_store_si512(&out[j], _mm512_permutexvar_epi64(Control, _mm512_max_epi8(_mm512_packs_epi16(sum0, sum1), Zero))); } @@ -172,9 +173,9 @@ namespace Stockfish::Eval::NNUE { auto out = reinterpret_cast<__m256i*>(&output[offset]); for (IndexType j = 0; j < NumChunks; ++j) { __m256i sum0 = _mm256_load_si256( - &reinterpret_cast(accumulation[perspectives[p]][0])[j * 2 + 0]); + &reinterpret_cast(accumulation[perspectives[p]])[j * 2 + 0]); __m256i sum1 = _mm256_load_si256( - &reinterpret_cast(accumulation[perspectives[p]][0])[j * 2 + 1]); + &reinterpret_cast(accumulation[perspectives[p]])[j * 2 + 1]); _mm256_store_si256(&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8( _mm256_packs_epi16(sum0, sum1), Zero), Control)); } @@ -183,9 +184,9 @@ namespace Stockfish::Eval::NNUE { auto out = reinterpret_cast<__m128i*>(&output[offset]); for (IndexType j = 0; j < NumChunks; ++j) { __m128i sum0 = _mm_load_si128(&reinterpret_cast( - accumulation[perspectives[p]][0])[j * 2 + 0]); + accumulation[perspectives[p]])[j * 2 + 0]); __m128i sum1 = _mm_load_si128(&reinterpret_cast( - accumulation[perspectives[p]][0])[j * 2 + 1]); + accumulation[perspectives[p]])[j * 2 + 1]); const __m128i packedbytes = _mm_packs_epi16(sum0, sum1); _mm_store_si128(&out[j], @@ -203,9 +204,9 @@ namespace Stockfish::Eval::NNUE { auto out = reinterpret_cast<__m64*>(&output[offset]); for (IndexType j = 0; j < NumChunks; ++j) { __m64 sum0 = *(&reinterpret_cast( - accumulation[perspectives[p]][0])[j * 2 + 0]); + accumulation[perspectives[p]])[j * 2 + 0]); __m64 sum1 = *(&reinterpret_cast( - accumulation[perspectives[p]][0])[j * 2 + 1]); + accumulation[perspectives[p]])[j * 2 + 1]); const __m64 packedbytes = _mm_packs_pi16(sum0, sum1); out[j] = _mm_subs_pi8(_mm_adds_pi8(packedbytes, k0x80s), k0x80s); } @@ -214,13 +215,13 @@ namespace Stockfish::Eval::NNUE { const auto out = reinterpret_cast(&output[offset]); for (IndexType j = 0; j < NumChunks; ++j) { int16x8_t sum = reinterpret_cast( - accumulation[perspectives[p]][0])[j]; + accumulation[perspectives[p]])[j]; out[j] = vmax_s8(vqmovn_s16(sum), Zero); } #else for (IndexType j = 0; j < HalfDimensions; ++j) { - BiasType sum = accumulation[static_cast(perspectives[p])][0][j]; + BiasType sum = accumulation[static_cast(perspectives[p])][j]; output[offset + j] = static_cast( std::max(0, std::min(127, sum))); } @@ -233,7 +234,13 @@ namespace Stockfish::Eval::NNUE { } private: - void update_accumulator(const Position& pos, const Color c) const { + void update_accumulator(const Position& pos, const Color perspective) const { + + // The size must be enough to contain the largest possible update. + // That might depend on the feature set and generally relies on the + // feature set's update cost calculation to be correct and never + // allow updates with more added/removed features than MaxActiveDimensions. + using IndexList = ValueList; #ifdef VECTOR // Gcc-10.2 unnecessarily spills AVX2 registers if this array @@ -244,23 +251,19 @@ namespace Stockfish::Eval::NNUE { // Look for a usable accumulator of an earlier position. We keep track // of the estimated gain in terms of features to be added/subtracted. StateInfo *st = pos.state(), *next = nullptr; - int gain = pos.count() - 2; - while (st->accumulator.state[c] == EMPTY) + int gain = FeatureSet::refresh_cost(pos); + while (st->accumulator.state[perspective] == EMPTY) { - auto& dp = st->dirtyPiece; - // The first condition tests whether an incremental update is - // possible at all: if this side's king has moved, it is not possible. - static_assert(std::is_same_v>, - "Current code assumes that only FriendlyKingMoved refresh trigger is being used."); - if ( dp.piece[0] == make_piece(c, KING) - || (gain -= dp.dirty_num + 1) < 0) + // This governs when a full feature refresh is needed and how many + // updates are better than just one full refresh. + if ( FeatureSet::requires_refresh(st, perspective) + || (gain -= FeatureSet::update_cost(st) + 1) < 0) break; next = st; st = st->previous; } - if (st->accumulator.state[c] == COMPUTED) + if (st->accumulator.state[perspective] == COMPUTED) { if (next == nullptr) return; @@ -268,34 +271,32 @@ namespace Stockfish::Eval::NNUE { // Update incrementally in two steps. First, we update the "next" // accumulator. Then, we update the current accumulator (pos.state()). - // Gather all features to be updated. This code assumes HalfKP features - // only and doesn't support refresh triggers. - static_assert(std::is_same_v>, - RawFeatures>); - Features::IndexList removed[2], added[2]; - Features::HalfKP::append_changed_indices(pos, - next->dirtyPiece, c, &removed[0], &added[0]); + // Gather all features to be updated. + const Square ksq = pos.square(perspective); + IndexList removed[2], added[2]; + FeatureSet::append_changed_indices( + ksq, next, perspective, removed[0], added[0]); for (StateInfo *st2 = pos.state(); st2 != next; st2 = st2->previous) - Features::HalfKP::append_changed_indices(pos, - st2->dirtyPiece, c, &removed[1], &added[1]); + FeatureSet::append_changed_indices( + ksq, st2, perspective, removed[1], added[1]); // Mark the accumulators as computed. - next->accumulator.state[c] = COMPUTED; - pos.state()->accumulator.state[c] = COMPUTED; + next->accumulator.state[perspective] = COMPUTED; + pos.state()->accumulator.state[perspective] = COMPUTED; - // Now update the accumulators listed in info[], where the last element is a sentinel. - StateInfo *info[3] = + // Now update the accumulators listed in states_to_update[], where the last element is a sentinel. + StateInfo *states_to_update[3] = { next, next == pos.state() ? nullptr : pos.state(), nullptr }; #ifdef VECTOR for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) { // Load accumulator auto accTile = reinterpret_cast( - &st->accumulator.accumulation[c][0][j * TileHeight]); + &st->accumulator.accumulation[perspective][j * TileHeight]); for (IndexType k = 0; k < NumRegs; ++k) acc[k] = vec_load(&accTile[k]); - for (IndexType i = 0; info[i]; ++i) + for (IndexType i = 0; states_to_update[i]; ++i) { // Difference calculation for the deactivated features for (const auto index : removed[i]) @@ -317,19 +318,19 @@ namespace Stockfish::Eval::NNUE { // Store accumulator accTile = reinterpret_cast( - &info[i]->accumulator.accumulation[c][0][j * TileHeight]); + &states_to_update[i]->accumulator.accumulation[perspective][j * TileHeight]); for (IndexType k = 0; k < NumRegs; ++k) vec_store(&accTile[k], acc[k]); } } #else - for (IndexType i = 0; info[i]; ++i) + for (IndexType i = 0; states_to_update[i]; ++i) { - std::memcpy(info[i]->accumulator.accumulation[c][0], - st->accumulator.accumulation[c][0], + std::memcpy(states_to_update[i]->accumulator.accumulation[perspective], + st->accumulator.accumulation[perspective], HalfDimensions * sizeof(BiasType)); - st = info[i]; + st = states_to_update[i]; // Difference calculation for the deactivated features for (const auto index : removed[i]) @@ -337,7 +338,7 @@ namespace Stockfish::Eval::NNUE { const IndexType offset = HalfDimensions * index; for (IndexType j = 0; j < HalfDimensions; ++j) - st->accumulator.accumulation[c][0][j] -= weights[offset + j]; + st->accumulator.accumulation[perspective][j] -= weights[offset + j]; } // Difference calculation for the activated features @@ -346,7 +347,7 @@ namespace Stockfish::Eval::NNUE { const IndexType offset = HalfDimensions * index; for (IndexType j = 0; j < HalfDimensions; ++j) - st->accumulator.accumulation[c][0][j] += weights[offset + j]; + st->accumulator.accumulation[perspective][j] += weights[offset + j]; } } #endif @@ -355,9 +356,9 @@ namespace Stockfish::Eval::NNUE { { // Refresh the accumulator auto& accumulator = pos.state()->accumulator; - accumulator.state[c] = COMPUTED; - Features::IndexList active; - Features::HalfKP::append_active_indices(pos, c, &active); + accumulator.state[perspective] = COMPUTED; + IndexList active; + FeatureSet::append_active_indices(pos, perspective, active); #ifdef VECTOR for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) @@ -377,13 +378,13 @@ namespace Stockfish::Eval::NNUE { } auto accTile = reinterpret_cast( - &accumulator.accumulation[c][0][j * TileHeight]); + &accumulator.accumulation[perspective][j * TileHeight]); for (unsigned k = 0; k < NumRegs; k++) vec_store(&accTile[k], acc[k]); } #else - std::memcpy(accumulator.accumulation[c][0], biases, + std::memcpy(accumulator.accumulation[perspective], biases, HalfDimensions * sizeof(BiasType)); for (const auto index : active) @@ -391,7 +392,7 @@ namespace Stockfish::Eval::NNUE { const IndexType offset = HalfDimensions * index; for (IndexType j = 0; j < HalfDimensions; ++j) - accumulator.accumulation[c][0][j] += weights[offset + j]; + accumulator.accumulation[perspective][j] += weights[offset + j]; } #endif } @@ -405,8 +406,7 @@ namespace Stockfish::Eval::NNUE { using WeightType = std::int16_t; alignas(CacheLineSize) BiasType biases[HalfDimensions]; - alignas(CacheLineSize) - WeightType weights[HalfDimensions * InputDimensions]; + alignas(CacheLineSize) WeightType weights[HalfDimensions * InputDimensions]; }; } // namespace Stockfish::Eval::NNUE From c0ff241464338f7e5bb815b4f72c2a95d12244a0 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Sun, 25 Apr 2021 07:09:35 +0200 Subject: [PATCH 0540/1766] Thread based reduction tweak. For PV nodes at the first two plies no reductions are done for each fourth thread. STC (8 threads): LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 53992 W: 3334 L: 3167 D: 47491 Ptnml(0-2): 64, 2713, 21285, 2860, 74 https://tests.stockfishchess.org/tests/view/6083b2d695e7f1852abd277a LTC (8 threads): LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 64888 W: 1888 L: 1725 D: 61275 Ptnml(0-2): 14, 1556, 29146, 1709, 19 https://tests.stockfishchess.org/tests/view/6084249595e7f1852abd2795 closes https://github.com/official-stockfish/Stockfish/pull/3443 No functional change (for one thread) --- src/search.cpp | 3 ++- src/thread.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 1d841023039..99c2b09f9ef 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1185,7 +1185,8 @@ namespace { || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha || cutNode || (!PvNode && !formerPv && captureHistory[movedPiece][to_sq(move)][type_of(pos.captured_piece())] < 3678) - || thisThread->ttHitAverage < 432 * TtHitAverageResolution * TtHitAverageWindow / 1024)) + || thisThread->ttHitAverage < 432 * TtHitAverageResolution * TtHitAverageWindow / 1024) + && (!PvNode || ss->ply > 1 || thisThread->id() % 4 != 3)) { Depth r = reduction(improving, depth, moveCount); diff --git a/src/thread.h b/src/thread.h index 2b3dea0d7cc..4cf5dabb5b1 100644 --- a/src/thread.h +++ b/src/thread.h @@ -55,6 +55,7 @@ class Thread { void idle_loop(); void start_searching(); void wait_for_search_finished(); + int id() const { return idx; } Pawns::Table pawnsTable; Material::Table materialTable; From 33a858eaa1f792b3413384a3d0993dba36aca92e Mon Sep 17 00:00:00 2001 From: lonfom169 Date: Sat, 24 Apr 2021 21:37:47 -0300 Subject: [PATCH 0541/1766] More extensions if SE search is very low. More extensions for non-PV nodes if value from singular extension search is significantly below singularBeta. Passed STC: LLR: 2.97 (-2.94,2.94) <-0.50,2.50> Total: 25064 W: 2334 L: 2162 D: 20568 Ptnml(0-2): 82, 1720, 8768, 1868, 94 https://tests.stockfishchess.org/tests/view/6084ba7995e7f1852abd27e3 Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 67136 W: 2644 L: 2450 D: 62042 Ptnml(0-2): 46, 2134, 28990, 2376, 22 https://tests.stockfishchess.org/tests/view/6084d79195e7f1852abd27ee closes https://github.com/official-stockfish/Stockfish/pull/3445 Bench: 4075325 --- src/search.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 99c2b09f9ef..5542fc87366 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1130,6 +1130,8 @@ namespace { { extension = 1; singularQuietLMR = !ttCapture; + if (!PvNode && value < singularBeta - 140) + extension = 2; } // Multi-cut pruning From 84b42b3ab328e3f1b9829d8f5d967e4d45a45a39 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Tue, 27 Apr 2021 19:59:46 -0300 Subject: [PATCH 0542/1766] Simplify pawn moves generator This patch simplifies QUIET_CHECKS pawn move generator by merging discovery check move generator with direct check move generator. It also simplifies emptySquares instantiation. In addition, I added a comment in generate_moves() to clarify Check branches. STC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 112648 W: 9952 L: 9945 D: 92751 Ptnml(0-2): 369, 7682, 40195, 7729, 349 https://tests.stockfishchess.org/tests/view/6088226895e7f1852abd2978 LTC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 74656 W: 2797 L: 2765 D: 69094 Ptnml(0-2): 38, 2328, 32554, 2380, 28 https://tests.stockfishchess.org/tests/view/60884e5095e7f1852abd2994 closes https://github.com/official-stockfish/Stockfish/pull/3447 No functional change --- src/movegen.cpp | 43 ++++++++++++++----------------------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index bd9d0b62409..be16845095d 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -58,19 +58,16 @@ namespace { constexpr Direction UpLeft = (Us == WHITE ? NORTH_WEST : SOUTH_EAST); const Square ksq = pos.square(Them); - Bitboard emptySquares; + const Bitboard emptySquares = Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces(); + const Bitboard enemies = Type == EVASIONS ? pos.checkers() + : Type == CAPTURES ? target : pos.pieces(Them); Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB; Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB; - Bitboard enemies = (Type == EVASIONS ? pos.checkers() - : Type == CAPTURES ? target : pos.pieces(Them)); - // Single and double pawn pushes, no promotions if (Type != CAPTURES) { - emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces()); - Bitboard b1 = shift(pawnsNotOn7) & emptySquares; Bitboard b2 = shift(b1 & TRank3BB) & emptySquares; @@ -82,22 +79,12 @@ namespace { if (Type == QUIET_CHECKS) { - b1 &= pawn_attacks_bb(Them, ksq); - b2 &= pawn_attacks_bb(Them, ksq); - - // Add pawn pushes which give discovered check. This is possible only - // if the pawn is not on the same file as the enemy king, because we - // don't generate captures. Note that a possible discovered check - // promotion has been already generated amongst the captures. - Bitboard dcCandidateQuiets = pos.blockers_for_king(Them) & pawnsNotOn7; - if (dcCandidateQuiets) - { - Bitboard dc1 = shift(dcCandidateQuiets) & emptySquares & ~file_bb(ksq); - Bitboard dc2 = shift(dc1 & TRank3BB) & emptySquares; - - b1 |= dc1; - b2 |= dc2; - } + // To make a quiet check, you either make a direct check by pushing a pawn + // or push a blocker pawn that is not on the same file as the enemy king. + // Discovered check promotion has been already generated amongst the captures. + Bitboard dcCandidatePawns = pos.blockers_for_king(Them) & ~file_bb(ksq); + b1 &= pawn_attacks_bb(Them, ksq) | shift< Up>(dcCandidatePawns); + b2 &= pawn_attacks_bb(Them, ksq) | shift(dcCandidatePawns); } while (b1) @@ -116,16 +103,13 @@ namespace { // Promotions and underpromotions if (pawnsOn7) { - if (Type == CAPTURES) - emptySquares = ~pos.pieces(); - - if (Type == EVASIONS) - emptySquares &= target; - Bitboard b1 = shift(pawnsOn7) & enemies; Bitboard b2 = shift(pawnsOn7) & enemies; Bitboard b3 = shift(pawnsOn7) & emptySquares; + if (Type == EVASIONS) + b3 &= target; + while (b1) moveList = make_promotions(moveList, pop_lsb(b1), ksq); @@ -185,8 +169,9 @@ namespace { while (bb) { Square from = pop_lsb(bb); - Bitboard b = attacks_bb(from, pos.pieces()) & target; + + // To check, you either move freely a blocker or make a direct check. if (Checks && (Pt == QUEEN || !(pos.blockers_for_king(~Us) & from))) b &= pos.check_squares(Pt); From 6ad4f485d37556f5e09baae5a939f44ac5b51c84 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Sat, 24 Apr 2021 14:46:01 +0100 Subject: [PATCH 0543/1766] Change tempo with time and threads Introduce variable tempo for nnue depending on logarithm of estimated strength, where strength is the product of time and number of threads. The original idea here was that NNUE is best with a slightly different tempo value to classical, since its style of play is slightly different. It turns out that the best tempo for NNUE varies with strength of play, so a formula is used which gives about 19 for STC and 24 for LTC under current fishtest settings. STC 10+0.1: LLR: 2.94 (-2.94,2.94) {-0.20,1.10} Total: 120816 W: 11155 L: 10861 D: 98800 Ptnml(0-2): 406, 8728, 41933, 8848, 493 https://tests.stockfishchess.org/tests/view/60735b3a8141753378960534 LTC 60+0.6: LLR: 2.94 (-2.94,2.94) {0.20,0.90} Total: 35688 W: 1392 L: 1234 D: 33062 Ptnml(0-2): 23, 1079, 15473, 1255, 14 https://tests.stockfishchess.org/tests/view/6073ffbc814175337896057f Passed non-regression SMP test at LTC 20+0.2 (8 threads): LLR: 2.95 (-2.94,2.94) {-0.70,0.20} Total: 11008 W: 317 L: 267 D: 10424 Ptnml(0-2): 2, 245, 4962, 291, 4 https://tests.stockfishchess.org/tests/view/60749ea881417533789605a4 closes https://github.com/official-stockfish/Stockfish/pull/3426 Bench 4075325 --- src/evaluate.cpp | 3 ++- src/timeman.cpp | 8 ++++++++ src/timeman.h | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index ba3de70bb25..0fb9abdf240 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -33,6 +33,7 @@ #include "misc.h" #include "pawns.h" #include "thread.h" +#include "timeman.h" #include "uci.h" #include "incbin/incbin.h" @@ -1096,7 +1097,7 @@ Value Eval::evaluate(const Position& pos) { + material / 32 - 4 * pos.rule50_count(); - Value nnue = NNUE::evaluate(pos) * scale / 1024 + Tempo; + Value nnue = NNUE::evaluate(pos) * scale / 1024 + Time.tempoNNUE; if (pos.is_chess960()) nnue += fix_FRC(pos); diff --git a/src/timeman.cpp b/src/timeman.cpp index f742d1e4422..3236b6e9bbb 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -94,6 +94,14 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { optimumTime = TimePoint(optScale * timeLeft); maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, maxScale * optimumTime)); + if (Stockfish::Search::Limits.use_time_management()) + { + int strength = std::log( std::max(1, int(optimumTime * Threads.size() / 10))) * 60; + tempoNNUE = std::clamp( (strength + 264) / 24, 18, 30); + } + else + tempoNNUE = 28; // default for no time given + if (Options["Ponder"]) optimumTime += optimumTime / 4; } diff --git a/src/timeman.h b/src/timeman.h index b1878d65f25..4ac0b4be8a4 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -37,6 +37,7 @@ class TimeManagement { TimePoint(Threads.nodes_searched()) : now() - startTime; } int64_t availableNodes; // When in 'nodes as time' mode + int tempoNNUE; private: TimePoint startTime; From 33fadb5118a0ec3917ec3bbc255fafdf148627ec Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 29 Apr 2021 08:18:37 +0200 Subject: [PATCH 0544/1766] Add some more information on the UCI protocol Improve README.md: provide a link to the protocol, and document some non-standard options. fixes https://github.com/official-stockfish/Stockfish/issues/3446 closes https://github.com/official-stockfish/Stockfish/pull/3450 No functional change --- README.md | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 67fb5fa09ac..bc34ee1321a 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,14 @@ This distribution of Stockfish consists of the following files: * a file with the .nnue extension, storing the neural network for the NNUE evaluation. Binary distributions will have this file embedded. -## UCI options +## The UCI protocol and available options -Currently, Stockfish has the following UCI options: +The Universal Chess Interface (UCI) is a standard protocol used to communicate with a chess engine, +and is the recommended way to do so for typical graphical user interfaces (GUI) or chess tools. + +Stockfish implements most commands as described in [the UCI protocol](https://www.shredderchess.com/download/div/uci.zip) + +For users, the following UCI options, which can typically be set via a GUI, are available in Stockfish: * #### Threads The number of CPU threads used for searching a position. For best performance, set @@ -136,6 +141,24 @@ Currently, Stockfish has the following UCI options: * #### Debug Log File Write all communication to and from the engine into a text file. +For developers the following non-standard commands might be of interest, mainly useful for debugging: + + * #### compiler + Give information about the compiler and environment used for building a binary. + + * #### flip + Flips the side to move. + + * #### bench ttSize threads limit fenFile limitType evalType + Performs a standard benchmark using various options. The signature or standard node + count is obtained using all defaults. `bench` is currently `bench 16 1 13 default depth mixed`. + + * #### d + Display the current position, with ascii art and fen. + + * #### eval + Return the evaluation of the current position. + ## A note on classical evaluation versus NNUE evaluation Both approaches assign a value to a position that is used in alpha-beta (PVS) search From b1c8840f104d4d36b3f9a420b8b26a5e94c6dc18 Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Sat, 1 May 2021 10:18:57 +0200 Subject: [PATCH 0545/1766] Simplify check extension Simplify check extension, as it seems not to bring any strength and thus is no longer needed. STC https://tests.stockfishchess.org/tests/view/608c18e995e7f1852abd2b81 LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 54544 W: 4891 L: 4815 D: 44838 Ptnml(0-2): 186, 3889, 19081, 3895, 221 LTC https://tests.stockfishchess.org/tests/view/608c6ab195e7f1852abd2bc6 LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 51008 W: 1845 L: 1794 D: 47369 Ptnml(0-2): 31, 1591, 22206, 1648, 28 closes https://github.com/official-stockfish/Stockfish/pull/3452 bench: 3993071 --- src/position.h | 5 ----- src/search.cpp | 5 ----- 2 files changed, 10 deletions(-) diff --git a/src/position.h b/src/position.h index d470ef908e1..c226373b1f7 100644 --- a/src/position.h +++ b/src/position.h @@ -115,7 +115,6 @@ class Position { Bitboard blockers_for_king(Color c) const; Bitboard check_squares(PieceType pt) const; Bitboard pinners(Color c) const; - bool is_discovered_check_on_king(Color c, Move m) const; // Attacks to/from a given square Bitboard attackers_to(Square s) const; @@ -301,10 +300,6 @@ inline Bitboard Position::check_squares(PieceType pt) const { return st->checkSquares[pt]; } -inline bool Position::is_discovered_check_on_king(Color c, Move m) const { - return st->blockersForKing[c] & from_sq(m); -} - inline bool Position::pawn_passed(Color c, Square s) const { return !(pieces(~c, PAWN) & passed_pawn_span(c, s)); } diff --git a/src/search.cpp b/src/search.cpp index 5542fc87366..fd833b812d9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1155,11 +1155,6 @@ namespace { } } - // Check extension (~2 Elo) - else if ( givesCheck - && (pos.is_discovered_check_on_king(~us, move) || pos.see_ge(move))) - extension = 1; - // Add extension to new depth newDepth += extension; From ca250e969c9d235b2d8be1d5d088b8834aa374b9 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Sun, 2 May 2021 18:50:09 +0200 Subject: [PATCH 0546/1766] Add an UCI level command "export_net". This command writes the embedded net to the file `EvalFileDefaultName`. If there is no embedded net the command does nothing. fixes #3453 closes https://github.com/official-stockfish/Stockfish/pull/3454 No functional change --- README.md | 16 ++++++++++------ src/evaluate.cpp | 11 +++++++++++ src/evaluate.h | 1 + src/uci.cpp | 1 + 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index bc34ee1321a..013d4b32caa 100644 --- a/README.md +++ b/README.md @@ -143,22 +143,26 @@ For users, the following UCI options, which can typically be set via a GUI, are For developers the following non-standard commands might be of interest, mainly useful for debugging: - * #### compiler - Give information about the compiler and environment used for building a binary. - - * #### flip - Flips the side to move. - * #### bench ttSize threads limit fenFile limitType evalType Performs a standard benchmark using various options. The signature or standard node count is obtained using all defaults. `bench` is currently `bench 16 1 13 default depth mixed`. + * #### compiler + Give information about the compiler and environment used for building a binary. + * #### d Display the current position, with ascii art and fen. * #### eval Return the evaluation of the current position. + * #### export_net + If the binary contains an embedded net, save it in a file (named according to the default value of EvalFile). + + * #### flip + Flips the side to move. + + ## A note on classical evaluation versus NNUE evaluation Both approaches assign a value to a position that is used in alpha-beta (PVS) search diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 0fb9abdf240..f0784e8f915 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -47,7 +47,9 @@ // Note that this does not work in Microsoft Visual Studio. #if !defined(_MSC_VER) && !defined(NNUE_EMBEDDING_OFF) INCBIN(EmbeddedNNUE, EvalFileDefaultName); + constexpr bool gHasEmbeddedNet = true; #else + constexpr bool gHasEmbeddedNet = false; const unsigned char gEmbeddedNNUEData[1] = {0x0}; const unsigned char *const gEmbeddedNNUEEnd = &gEmbeddedNNUEData[1]; const unsigned int gEmbeddedNNUESize = 1; @@ -114,6 +116,15 @@ namespace Eval { } } + void NNUE::export_net() { + if constexpr (gHasEmbeddedNet) { + ofstream stream(EvalFileDefaultName, std::ios_base::binary); + stream.write(reinterpret_cast(gEmbeddedNNUEData), gEmbeddedNNUESize); + } else { + sync_cout << "No embedded network file." << sync_endl; + } + } + /// NNUE::verify() verifies that the last net used was loaded successfully void NNUE::verify() { diff --git a/src/evaluate.h b/src/evaluate.h index 6210bd5816b..b7525aab8bb 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -45,6 +45,7 @@ namespace Eval { Value evaluate(const Position& pos); bool load_eval(std::string name, std::istream& stream); void init(); + void export_net(); void verify(); } // namespace NNUE diff --git a/src/uci.cpp b/src/uci.cpp index 051ff2e02f4..64bb7a7cb59 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -277,6 +277,7 @@ void UCI::loop(int argc, char* argv[]) { else if (token == "d") sync_cout << pos << sync_endl; else if (token == "eval") trace_eval(pos); else if (token == "compiler") sync_cout << compiler_info() << sync_endl; + else if (token == "export_net") Eval::NNUE::export_net(); else if (!token.empty() && token[0] != '#') sync_cout << "Unknown command: " << cmd << sync_endl; From d777ea79fff0f651bd948c881946cd06bebd9381 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Wed, 5 May 2021 19:03:20 +0300 Subject: [PATCH 0547/1766] Cleanup of likelyFailLow logic This patch broadens and simplifies definition of PvNode that is likely to fail low. New definition can be described as following "If node was already researched at depth >= current depth and failed low there" which is more logical than the previous version and takes less space + allows to not recompute it every time during move loop. Passed simplification STC https://tests.stockfishchess.org/tests/view/609148bf95e7f1852abd2e82 LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 20128 W: 1865 L: 1751 D: 16512 Ptnml(0-2): 63, 1334, 7165, 1430, 72 Passed simplification LTC https://tests.stockfishchess.org/tests/view/6091691295e7f1852abd2e8b LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 95128 W: 3498 L: 3481 D: 88149 Ptnml(0-2): 41, 2956, 41549, 2981, 37 closes https://github.com/official-stockfish/Stockfish/pull/3455 Bench: 3933037 --- src/search.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index fd833b812d9..bcd53f9eefe 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1006,6 +1006,13 @@ namespace { value = bestValue; singularQuietLMR = moveCountPruning = false; + // Indicate PvNodes that will probably fail low if the node was searched + // at a depth equal or greater than the current depth, and the result of this search was a fail low. + bool likelyFailLow = PvNode + && ttMove + && (tte->bound() & BOUND_UPPER) + && tte->depth() >= depth; + // Mark this node as being searched ThreadHolding th(thisThread, posKey, ss->ply); @@ -1044,14 +1051,6 @@ namespace { movedPiece = pos.moved_piece(move); givesCheck = pos.gives_check(move); - // Indicate PvNodes that will probably fail low if node was searched with non-PV search - // at depth equal or greater to current depth and result of this search was far below alpha - bool likelyFailLow = PvNode - && ttMove - && (tte->bound() & BOUND_UPPER) - && ttValue < alpha + 200 + 100 * depth - && tte->depth() >= depth; - // Calculate new depth for this move newDepth = depth - 1; From 58054fd0fa6294510fc8cf76b0ba9673d5094c10 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Fri, 7 May 2021 12:24:12 +0200 Subject: [PATCH 0548/1766] Exporting the currently loaded network file This PR adds an ability to export any currently loaded network. The export_net command now takes an optional filename parameter. If the loaded net is not the embedded net the filename parameter is required. Two changes were required to support this: * the "architecture" string, which is really just a some kind of description in the net, is now saved into netDescription on load and correctly saved on export. * the AffineTransform scrambles weights for some architectures and sparsifies them, such that retrieving the index is hard. This is solved by having a temporary scrambled<->unscrambled index lookup table when loading the network, and the actual index is saved for each individual weight that makes it to canSaturate16. This increases the size of the canSaturate16 entries by 6 bytes. closes https://github.com/official-stockfish/Stockfish/pull/3456 No functional change --- README.md | 18 +++++++---- src/evaluate.cpp | 25 ++++++++++----- src/evaluate.h | 4 ++- src/nnue/evaluate_nnue.cpp | 46 +++++++++++++++++++++++++--- src/nnue/layers/affine_transform.h | 47 +++++++++++++++++++++++++---- src/nnue/layers/clipped_relu.h | 5 +++ src/nnue/layers/input_slice.h | 5 +++ src/nnue/nnue_common.h | 18 +++++++++++ src/nnue/nnue_feature_transformer.h | 9 ++++++ src/uci.cpp | 9 +++++- 10 files changed, 159 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 013d4b32caa..8d5ce8d0eda 100644 --- a/README.md +++ b/README.md @@ -24,13 +24,13 @@ This distribution of Stockfish consists of the following files: * Readme.md, the file you are currently reading. * Copying.txt, a text file containing the GNU General Public License version 3. - + * AUTHORS, a text file with the list of authors for the project * src, a subdirectory containing the full source code, including a Makefile that can be used to compile Stockfish on Unix-like systems. - * a file with the .nnue extension, storing the neural network for the NNUE + * a file with the .nnue extension, storing the neural network for the NNUE evaluation. Binary distributions will have this file embedded. ## The UCI protocol and available options @@ -156,8 +156,14 @@ For developers the following non-standard commands might be of interest, mainly * #### eval Return the evaluation of the current position. - * #### export_net - If the binary contains an embedded net, save it in a file (named according to the default value of EvalFile). + * #### export_net [filename] + Exports the currently loaded network to a file. + If the currently loaded network is the embedded network and the filename + is not specified then the network is saved to the file matching the name + of the embedded network, as defined in evaluate.h. + If the currently loaded network is not the embedded network (some net set + through the UCI setoption) then the filename parameter is required and the + network is saved into that file. * #### flip Flips the side to move. @@ -189,7 +195,7 @@ Stockfish binary, but the default value of the EvalFile UCI option is the name o that is guaranteed to be compatible with that binary. 2) to use the NNUE evaluation, the additional data file with neural network parameters -needs to be available. Normally, this file is already embedded in the binary or it +needs to be available. Normally, this file is already embedded in the binary or it can be downloaded. The filename for the default (recommended) net can be found as the default value of the `EvalFile` UCI option, with the format `nn-[SHA256 first 12 digits].nnue` (for instance, `nn-c157e0a5755b.nnue`). This file can be downloaded from @@ -202,7 +208,7 @@ replacing `[filename]` as needed. If the engine is searching a position that is not in the tablebases (e.g. a position with 8 pieces), it will access the tablebases during the search. -If the engine reports a very large score (typically 153.xx), this means +If the engine reports a very large score (typically 153.xx), this means it has found a winning line into a tablebase position. If the engine is given a position to search that is in the tablebases, it diff --git a/src/evaluate.cpp b/src/evaluate.cpp index f0784e8f915..c396e0f7575 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -47,9 +47,7 @@ // Note that this does not work in Microsoft Visual Studio. #if !defined(_MSC_VER) && !defined(NNUE_EMBEDDING_OFF) INCBIN(EmbeddedNNUE, EvalFileDefaultName); - constexpr bool gHasEmbeddedNet = true; #else - constexpr bool gHasEmbeddedNet = false; const unsigned char gEmbeddedNNUEData[1] = {0x0}; const unsigned char *const gEmbeddedNNUEEnd = &gEmbeddedNNUEData[1]; const unsigned int gEmbeddedNNUESize = 1; @@ -116,12 +114,23 @@ namespace Eval { } } - void NNUE::export_net() { - if constexpr (gHasEmbeddedNet) { - ofstream stream(EvalFileDefaultName, std::ios_base::binary); - stream.write(reinterpret_cast(gEmbeddedNNUEData), gEmbeddedNNUESize); + void NNUE::export_net(const std::optional& filename) { + std::string actualFilename; + if (filename.has_value()) { + actualFilename = filename.value(); } else { - sync_cout << "No embedded network file." << sync_endl; + if (eval_file_loaded != EvalFileDefaultName) { + sync_cout << "Failed to export a net. A non-embedded net can only be saved if the filename is specified." << sync_endl; + return; + } + actualFilename = EvalFileDefaultName; + } + + ofstream stream(actualFilename, std::ios_base::binary); + if (save_eval(stream)) { + sync_cout << "Network saved successfully to " << actualFilename << "." << sync_endl; + } else { + sync_cout << "Failed to export a net." << sync_endl; } } @@ -1128,7 +1137,7 @@ Value Eval::evaluate(const Position& pos) { bool lowPieceEndgame = pos.non_pawn_material() == BishopValueMg || (pos.non_pawn_material() < 2 * RookValueMg && pos.count() < 2); - v = classical || lowPieceEndgame ? Evaluation(pos).value() + v = classical || lowPieceEndgame ? Evaluation(pos).value() : adjusted_NNUE(); // If the classical eval is small and imbalance large, use NNUE nevertheless. diff --git a/src/evaluate.h b/src/evaluate.h index b7525aab8bb..128a7caefcc 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -20,6 +20,7 @@ #define EVALUATE_H_INCLUDED #include +#include #include "types.h" @@ -44,8 +45,9 @@ namespace Eval { Value evaluate(const Position& pos); bool load_eval(std::string name, std::istream& stream); + bool save_eval(std::ostream& stream); void init(); - void export_net(); + void export_net(const std::optional& filename); void verify(); } // namespace NNUE diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 0e539611671..e0d4b9117c7 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -39,6 +39,7 @@ namespace Stockfish::Eval::NNUE { // Evaluation function file name std::string fileName; + std::string netDescription; namespace Detail { @@ -68,6 +69,14 @@ namespace Stockfish::Eval::NNUE { return reference.read_parameters(stream); } + // Write evaluation function parameters + template + bool write_parameters(std::ostream& stream, const T& reference) { + + write_little_endian(stream, T::get_hash_value()); + return reference.write_parameters(stream); + } + } // namespace Detail // Initialize the evaluation function parameters @@ -78,7 +87,7 @@ namespace Stockfish::Eval::NNUE { } // Read network header - bool read_header(std::istream& stream, std::uint32_t* hashValue, std::string* architecture) + bool read_header(std::istream& stream, std::uint32_t* hashValue, std::string* desc) { std::uint32_t version, size; @@ -86,8 +95,18 @@ namespace Stockfish::Eval::NNUE { *hashValue = read_little_endian(stream); size = read_little_endian(stream); if (!stream || version != Version) return false; - architecture->resize(size); - stream.read(&(*architecture)[0], size); + desc->resize(size); + stream.read(&(*desc)[0], size); + return !stream.fail(); + } + + // Write network header + bool write_header(std::ostream& stream, std::uint32_t hashValue, const std::string& desc) + { + write_little_endian(stream, Version); + write_little_endian(stream, hashValue); + write_little_endian(stream, desc.size()); + stream.write(&desc[0], desc.size()); return !stream.fail(); } @@ -95,14 +114,22 @@ namespace Stockfish::Eval::NNUE { bool read_parameters(std::istream& stream) { std::uint32_t hashValue; - std::string architecture; - if (!read_header(stream, &hashValue, &architecture)) return false; + if (!read_header(stream, &hashValue, &netDescription)) return false; if (hashValue != HashValue) return false; if (!Detail::read_parameters(stream, *featureTransformer)) return false; if (!Detail::read_parameters(stream, *network)) return false; return stream && stream.peek() == std::ios::traits_type::eof(); } + // Write network parameters + bool write_parameters(std::ostream& stream) { + + if (!write_header(stream, HashValue, netDescription)) return false; + if (!Detail::write_parameters(stream, *featureTransformer)) return false; + if (!Detail::write_parameters(stream, *network)) return false; + return (bool)stream; + } + // Evaluation function. Perform differential calculation. Value evaluate(const Position& pos) { @@ -141,4 +168,13 @@ namespace Stockfish::Eval::NNUE { return read_parameters(stream); } + // Save eval, to a file stream or a memory stream + bool save_eval(std::ostream& stream) { + + if (fileName.empty()) + return false; + + return write_parameters(stream); + } + } // namespace Stockfish::Eval::NNUE diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 424fad5650f..fc1926912df 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -69,15 +69,19 @@ namespace Stockfish::Eval::NNUE::Layers { if (!previousLayer.read_parameters(stream)) return false; for (std::size_t i = 0; i < OutputDimensions; ++i) biases[i] = read_little_endian(stream); - for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) #if !defined (USE_SSSE3) + for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) weights[i] = read_little_endian(stream); #else - weights[ + std::unique_ptr indexMap = std::make_unique(OutputDimensions * PaddedInputDimensions); + for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) { + const uint32_t scrambledIdx = (i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 + i / PaddedInputDimensions * 4 + - i % 4 - ] = read_little_endian(stream); + i % 4; + weights[scrambledIdx] = read_little_endian(stream); + indexMap[scrambledIdx] = i; + } // Determine if eights of weight and input products can be summed using 16bits // without saturation. We assume worst case combinations of 0 and 127 for all inputs. @@ -109,7 +113,8 @@ namespace Stockfish::Eval::NNUE::Layers { IndexType idx = maxK / 2 * OutputDimensions * 4 + maxK % 2; sum[sign == -1] -= w[idx]; - canSaturate16.add(j, i + maxK / 2 * 4 + maxK % 2 + x * 2, w[idx]); + const uint32_t scrambledIdx = idx + i * OutputDimensions + j * 4 + x * 2; + canSaturate16.add(j, i + maxK / 2 * 4 + maxK % 2 + x * 2, w[idx], indexMap[scrambledIdx]); w[idx] = 0; } } @@ -125,6 +130,34 @@ namespace Stockfish::Eval::NNUE::Layers { return !stream.fail(); } + // Write network parameters + bool write_parameters(std::ostream& stream) const { + if (!previousLayer.write_parameters(stream)) return false; + for (std::size_t i = 0; i < OutputDimensions; ++i) + write_little_endian(stream, biases[i]); +#if !defined (USE_SSSE3) + for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) + write_little_endian(stream, weights[i]); +#else + std::unique_ptr unscrambledWeights = std::make_unique(OutputDimensions * PaddedInputDimensions); + for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) { + unscrambledWeights[i] = + weights[ + (i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 + + i / PaddedInputDimensions * 4 + + i % 4 + ]; + } + for (int i = 0; i < canSaturate16.count; ++i) + unscrambledWeights[canSaturate16.ids[i].wIdx] = canSaturate16.ids[i].w; + + for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) + write_little_endian(stream, unscrambledWeights[i]); +#endif + + return !stream.fail(); + } + // Forward propagation const OutputType* propagate( const TransformedFeatureType* transformedFeatures, char* buffer) const { @@ -444,12 +477,14 @@ namespace Stockfish::Eval::NNUE::Layers { struct CanSaturate { int count; struct Entry { + uint32_t wIdx; uint16_t out; uint16_t in; int8_t w; } ids[PaddedInputDimensions * OutputDimensions * 3 / 4]; - void add(int i, int j, int8_t w) { + void add(int i, int j, int8_t w, uint32_t wIdx) { + ids[count].wIdx = wIdx; ids[count].out = i; ids[count].in = j; ids[count].w = w; diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index 00809c507b3..f1ac2dfe644 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -59,6 +59,11 @@ namespace Stockfish::Eval::NNUE::Layers { return previousLayer.read_parameters(stream); } + // Write network parameters + bool write_parameters(std::ostream& stream) const { + return previousLayer.write_parameters(stream); + } + // Forward propagation const OutputType* propagate( const TransformedFeatureType* transformedFeatures, char* buffer) const { diff --git a/src/nnue/layers/input_slice.h b/src/nnue/layers/input_slice.h index f113b911239..bd4d74478de 100644 --- a/src/nnue/layers/input_slice.h +++ b/src/nnue/layers/input_slice.h @@ -53,6 +53,11 @@ class InputSlice { return true; } + // Read network parameters + bool write_parameters(std::ostream& /*stream*/) const { + return true; + } + // Forward propagation const OutputType* propagate( const TransformedFeatureType* transformedFeatures, diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index 8c54f9baeeb..d41e02377ac 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -99,6 +99,24 @@ namespace Stockfish::Eval::NNUE { return result; } + template + inline void write_little_endian(std::ostream& stream, IntType value) { + + std::uint8_t u[sizeof(IntType)]; + typename std::make_unsigned::type v = value; + + std::size_t i = 0; + // if constexpr to silence the warning about shift by 8 + if constexpr (sizeof(IntType) > 1) { + for (; i + 1 < sizeof(IntType); ++i) { + u[i] = v; + v >>= 8; + } + } + u[i] = v; + + stream.write(reinterpret_cast(u), sizeof(IntType)); + } } // namespace Stockfish::Eval::NNUE #endif // #ifndef NNUE_COMMON_H_INCLUDED diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index f4412749153..a4a8e98f9c5 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -118,6 +118,15 @@ namespace Stockfish::Eval::NNUE { return !stream.fail(); } + // Write network parameters + bool write_parameters(std::ostream& stream) const { + for (std::size_t i = 0; i < HalfDimensions; ++i) + write_little_endian(stream, biases[i]); + for (std::size_t i = 0; i < HalfDimensions * InputDimensions; ++i) + write_little_endian(stream, weights[i]); + return !stream.fail(); + } + // Convert input features void transform(const Position& pos, OutputType* output) const { update_accumulator(pos, WHITE); diff --git a/src/uci.cpp b/src/uci.cpp index 64bb7a7cb59..bb17b8d79b2 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -277,7 +277,14 @@ void UCI::loop(int argc, char* argv[]) { else if (token == "d") sync_cout << pos << sync_endl; else if (token == "eval") trace_eval(pos); else if (token == "compiler") sync_cout << compiler_info() << sync_endl; - else if (token == "export_net") Eval::NNUE::export_net(); + else if (token == "export_net") { + std::optional filename; + std::string f; + if (is >> skipws >> f) { + filename = f; + } + Eval::NNUE::export_net(filename); + } else if (!token.empty() && token[0] != '#') sync_cout << "Unknown command: " << cmd << sync_endl; From 602687801b73552fb8364bcc82191f932ddca8b7 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Tue, 11 May 2021 11:04:02 -0300 Subject: [PATCH 0549/1766] Simplify LMR as it seems not to bring any strength and thus is no longer needed. Tests for updating elo estimates: https://tests.stockfishchess.org/tests/view/6099ff123a33eb67a844f789 https://tests.stockfishchess.org/tests/view/60953e6695e7f1852abd305b Individual simplification tests: https://tests.stockfishchess.org/tests/view/6098cfc73a33eb67a844f6a1 https://tests.stockfishchess.org/tests/view/6095539495e7f1852abd308b LTC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 96984 W: 3624 L: 3608 D: 89752 Ptnml(0-2): 45, 3222, 41939, 3244, 42 https://tests.stockfishchess.org/tests/view/6099921a3a33eb67a844f74f closes https://github.com/official-stockfish/Stockfish/pull/3458 bench: 3836428 --- src/search.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index bcd53f9eefe..8f157b7ea54 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1206,16 +1206,11 @@ namespace { && thisThread->bestMoveChanges <= 2) r++; - // More reductions for late moves if position was not in previous PV - if ( moveCountPruning - && !formerPv) - r++; - // Decrease reduction if opponent's move count is high (~5 Elo) if ((ss-1)->moveCount > 13) r--; - // Decrease reduction if ttMove has been singularly extended (~3 Elo) + // Decrease reduction if ttMove has been singularly extended (~1 Elo) if (singularQuietLMR) r--; @@ -1228,7 +1223,7 @@ namespace { } else { - // Increase reduction if ttMove is a capture (~5 Elo) + // Increase reduction if ttMove is a capture (~3 Elo) if (ttCapture) r++; @@ -1239,13 +1234,6 @@ namespace { if (cutNode) 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 reverse_move() (~2 Elo) - else if ( type_of(move) == NORMAL - && !pos.see_ge(reverse_move(move))) - r -= 2 + ss->ttPv - (type_of(movedPiece) == PAWN); - ss->statScore = thisThread->mainHistory[us][from_to(move)] + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] From a0e2debe3f1d14f84984a9a2c1482dc41f695548 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Sun, 9 May 2021 16:33:41 -0300 Subject: [PATCH 0550/1766] Remove coordination between searching threads In summary, this revert #2204, as it seems not to bring any strength anymore, so it's no long needed. STC (5+0.05 @ 8 threads): LLR: 2.96 (-2.94,2.94) <-2.50,0.50> Total: 105728 W: 6406 L: 6393 D: 92929 Ptnml(0-2): 154, 5479, 41599, 5464, 168 https://tests.stockfishchess.org/tests/view/6096994095e7f1852abd3154 LTC (20+0.2 @ 8 threads): LLR: 2.96 (-2.94,2.94) <-2.50,0.50> Total: 26336 W: 774 L: 712 D: 24850 Ptnml(0-2): 9, 641, 11810, 695, 13 https://tests.stockfishchess.org/tests/view/6097c62995e7f1852abd31e8 closes https://github.com/official-stockfish/Stockfish/pull/3459 No functional change. --- src/search.cpp | 50 -------------------------------------------------- 1 file changed, 50 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 8f157b7ea54..3a0839d62db 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -102,49 +102,6 @@ namespace { Move best = MOVE_NONE; }; - // Breadcrumbs are used to mark nodes as being searched by a given thread - struct Breadcrumb { - std::atomic thread; - std::atomic key; - }; - std::array breadcrumbs; - - // ThreadHolding 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; - otherThread = false; - owning = false; - if (location) - { - // See if another already marked this location, if not, mark it ourselves - Thread* tmp = (*location).thread.load(std::memory_order_relaxed); - if (tmp == nullptr) - { - (*location).thread.store(thisThread, std::memory_order_relaxed); - (*location).key.store(posKey, std::memory_order_relaxed); - owning = true; - } - else if ( tmp != thisThread - && (*location).key.load(std::memory_order_relaxed) == posKey) - otherThread = true; - } - } - - ~ThreadHolding() { - if (owning) // Free the marked location - (*location).thread.store(nullptr, std::memory_order_relaxed); - } - - bool marked() { return otherThread; } - - private: - Breadcrumb* location; - bool otherThread, owning; - }; - template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode); @@ -1013,9 +970,6 @@ namespace { && (tte->bound() & BOUND_UPPER) && tte->depth() >= depth; - // Mark this node as being searched - ThreadHolding th(thisThread, posKey, ss->ply); - // Step 12. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. while ((move = mp.next_move(moveCountPruning)) != MOVE_NONE) @@ -1190,10 +1144,6 @@ namespace { if (thisThread->ttHitAverage > 537 * TtHitAverageResolution * TtHitAverageWindow / 1024) r--; - // Increase reduction if other threads are searching this position - if (th.marked()) - r++; - // Decrease reduction if position is or has been on the PV // and node is not likely to fail low. (~10 Elo) if ( ss->ttPv From b62af7ac1e78c1b35103dfe6110201d0b810aee0 Mon Sep 17 00:00:00 2001 From: EntityFX Date: Mon, 12 Apr 2021 13:42:35 +0300 Subject: [PATCH 0551/1766] E2K: added support for MCST Elbrus 2000 CPU architecture e2k (Elbrus 2000) - this is a VLIW/EPIC architecture, the like Intel Itanium (IA-64) architecture. The architecture has half native / half software support for most Intel/AMD SIMD (e.g. MMX/SSE/SSE2/SSE3/SSSE3/SSE4.1/SSE4.2/AES/AVX/AVX2 & 3DNow!/SSE4a/XOP/FMA4) via intrinsics. https://en.wikipedia.org/wiki/Elbrus_2000 closes https://github.com/official-stockfish/Stockfish/pull/3425 No functional change --- AUTHORS | 1 + src/Makefile | 15 ++++++++++++++- src/misc.cpp | 14 +++++++++++++- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index c12b98a038a..69d682f13f2 100644 --- a/AUTHORS +++ b/AUTHORS @@ -27,6 +27,7 @@ Andy Duplain Antoine Champion (antoinechampion) Aram Tumanian (atumanian) Arjun Temurnikar +Artem Solopiy (EntityFX) Auguste Pop Balint Pfliegel Ben Koshy (BKSpurgeon) diff --git a/src/Makefile b/src/Makefile index cdd2007fd2b..71105bdb65d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -97,6 +97,7 @@ ifeq ($(ARCH), $(filter $(ARCH), \ x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-bmi2 x86-64-avx2 \ x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \ x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-32 \ + e2k \ armv7 armv7-neon armv8 apple-silicon general-64 general-32)) SUPPORTED_ARCH=true else @@ -291,6 +292,17 @@ ifeq ($(ARCH),ppc-64) prefetch = yes endif +ifeq ($(findstring e2k,$(ARCH)),e2k) + arch = e2k + mmx = yes + bits = 64 + sse = yes + sse2 = yes + ssse3 = yes + sse41 = yes + popcnt = yes +endif + endif ### ========================================================================== @@ -514,7 +526,6 @@ ifeq ($(popcnt),yes) endif endif - ifeq ($(avx2),yes) CXXFLAGS += -DUSE_AVX2 ifeq ($(comp),$(filter $(comp),gcc clang mingw)) @@ -682,6 +693,7 @@ help: @echo "armv7 > ARMv7 32-bit" @echo "armv7-neon > ARMv7 32-bit with popcnt and neon" @echo "armv8 > ARMv8 64-bit with popcnt and neon" + @echo "e2k > Elbrus 2000" @echo "apple-silicon > Apple silicon ARM64" @echo "general-64 > unspecified 64-bit" @echo "general-32 > unspecified 32-bit" @@ -829,6 +841,7 @@ config-sanity: net @test "$(SUPPORTED_ARCH)" = "true" @test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \ test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || \ + test "$(arch)" = "e2k" || \ test "$(arch)" = "armv7" || test "$(arch)" = "armv8" || test "$(arch)" = "arm64" @test "$(bits)" = "32" || test "$(bits)" = "64" @test "$(prefetch)" = "yes" || test "$(prefetch)" = "no" diff --git a/src/misc.cpp b/src/misc.cpp index 918dc7a9534..9e7b7e37b8c 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -51,7 +51,7 @@ typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY); #include #endif -#if defined(__APPLE__) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) && !defined(_WIN32)) +#if defined(__APPLE__) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) && !defined(_WIN32)) || defined(__e2k__) #define POSIXALIGNEDALLOC #include #endif @@ -192,6 +192,18 @@ std::string compiler_info() { compiler += "(version "; compiler += stringify(_MSC_FULL_VER) "." stringify(_MSC_BUILD); compiler += ")"; + #elif defined(__e2k__) && defined(__LCC__) + #define dot_ver2(n) \ + compiler += (char)'.'; \ + compiler += (char)('0' + (n) / 10); \ + compiler += (char)('0' + (n) % 10); + + compiler += "MCST LCC "; + compiler += "(version "; + compiler += std::to_string(__LCC__ / 100); + dot_ver2(__LCC__ % 100) + dot_ver2(__LCC_MINOR__) + compiler += ")"; #elif __GNUC__ compiler += "g++ (GNUC) "; compiler += make_version_string(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); From 594e2ac9994f22cb7c5b74f3da3dbb512cbdb510 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Wed, 12 May 2021 09:38:26 -0300 Subject: [PATCH 0552/1766] Simplify LMR rule for non-checking captures We simplify away the complicated rule in LMR for "non-checking captures likely to be bad", as it seems not to bring any strength anymore. STC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 55256 W: 4972 L: 4897 D: 45387 Ptnml(0-2): 177, 3976, 19234, 4077, 164 https://tests.stockfishchess.org/tests/view/609adf3b3a33eb67a844f842 LTC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 10344 W: 437 L: 353 D: 9554 Ptnml(0-2): 1, 322, 4449, 392, 8 https://tests.stockfishchess.org/tests/view/609b3dfa3a33eb67a844f88e -- While at it, we also update the Elo estimate of the previous rule in LMR (see https://tests.stockfishchess.org/tests/view/609af2a63a33eb67a844f867). closes https://github.com/official-stockfish/Stockfish/pull/3460 Bench: 3840688 --- src/search.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 3a0839d62db..d16d9cad897 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1156,7 +1156,7 @@ namespace { && thisThread->bestMoveChanges <= 2) r++; - // Decrease reduction if opponent's move count is high (~5 Elo) + // Decrease reduction if opponent's move count is high (~1 Elo) if ((ss-1)->moveCount > 13) r--; @@ -1164,14 +1164,7 @@ namespace { if (singularQuietLMR) r--; - if (captureOrPromotion) - { - // Increase reduction for non-checking captures likely to be bad - if ( !givesCheck - && ss->staticEval + PieceValue[EG][pos.captured_piece()] + 210 * depth <= alpha) - r++; - } - else + if (!captureOrPromotion) { // Increase reduction if ttMove is a capture (~3 Elo) if (ttCapture) From bd756ee45ce1181a90ec149892ccf0e4cfe3639e Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Fri, 14 May 2021 17:35:32 +0200 Subject: [PATCH 0553/1766] Remove BoolConditions from tuning code Remove BoolConditions from tuning code, as the feature does not work and the code has not be touched in years. No functional change --- src/tune.cpp | 19 ------------------- src/tune.h | 34 ---------------------------------- 2 files changed, 53 deletions(-) diff --git a/src/tune.cpp b/src/tune.cpp index d9618efc9c7..ac91b606fb1 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -30,7 +30,6 @@ namespace Stockfish { bool Tune::update_on_last; const UCI::Option* LastOption = nullptr; -BoolConditions Conditions; static std::map TuneResults; string Tune::next(string& names, bool pop) { @@ -110,24 +109,6 @@ template<> void Tune::Entry::read_option() { template<> void Tune::Entry::init_option() {} template<> void Tune::Entry::read_option() { value(); } - -// Set binary conditions according to a probability that depends -// on the corresponding parameter value. - -void BoolConditions::set() { - - static PRNG rng(now()); - static bool startup = true; // To workaround fishtest bench - - for (size_t i = 0; i < binary.size(); i++) - binary[i] = !startup && (values[i] + int(rng.rand() % variance) > threshold); - - startup = false; - - for (size_t i = 0; i < binary.size(); i++) - sync_cout << binary[i] << sync_endl; -} - } // namespace Stockfish diff --git a/src/tune.h b/src/tune.h index c904c09dc94..b5c715b3caa 100644 --- a/src/tune.h +++ b/src/tune.h @@ -46,27 +46,6 @@ struct SetRange { #define SetDefaultRange SetRange(default_range) -/// BoolConditions struct is used to tune boolean conditions in the -/// code by toggling them on/off according to a probability that -/// depends on the value of a tuned integer parameter: for high -/// values of the parameter condition is always disabled, for low -/// values is always enabled, otherwise it is enabled with a given -/// probability that depnends on the parameter under tuning. - -struct BoolConditions { - void init(size_t size) { values.resize(size, defaultValue), binary.resize(size, 0); } - void set(); - - std::vector binary, values; - int defaultValue = 465, variance = 40, threshold = 500; - SetRange range = SetRange(0, 1000); -}; - -extern BoolConditions Conditions; - -inline void set_conditions() { Conditions.set(); } - - /// Tune class implements the 'magic' code that makes the setup of a fishtest /// tuning session as easy as it can be. Mainly you have just to remove const /// qualifiers from the variables you want to tune and flag them for tuning, so @@ -159,14 +138,6 @@ class Tune { return add(value, (next(names), std::move(names)), args...); } - // Template specialization for BoolConditions - template - int add(const SetRange& range, std::string&& names, BoolConditions& cond, Args&&... args) { - for (size_t size = cond.values.size(), i = 0; i < size; i++) - add(cond.range, next(names, i == size - 1) + "_" + std::to_string(i), cond.values[i]); - return add(range, std::move(names), args...); - } - std::vector> list; public: @@ -187,11 +158,6 @@ class Tune { #define UPDATE_ON_LAST() bool UNIQUE(p, __LINE__) = Tune::update_on_last = true -// Some macro to tune toggling of boolean conditions -#define CONDITION(x) (Conditions.binary[__COUNTER__] || (x)) -#define TUNE_CONDITIONS() int UNIQUE(c, __LINE__) = (Conditions.init(__COUNTER__), 0); \ - TUNE(Conditions, set_conditions) - } // namespace Stockfish #endif // #ifndef TUNE_H_INCLUDED From 24b8b3098bc24ec576b7d03ffb72b2908e6c8c80 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Thu, 13 May 2021 11:12:56 -0300 Subject: [PATCH 0554/1766] Remove early return in Probcut code We simplify away early return in ProbCut, as it seems not to bring any strength anymore. STC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 42632 W: 3705 L: 3617 D: 35310 Ptnml(0-2): 123, 2947, 15110, 2991, 145 https://tests.stockfishchess.org/tests/view/609c49da7746e3dc74ffae02 LTC: LLR: 2.96 (-2.94,2.94) <-2.50,0.50> Total: 35384 W: 1314 L: 1251 D: 32819 Ptnml(0-2): 11, 1130, 15355, 1177, 19 https://tests.stockfishchess.org/tests/view/609c71467746e3dc74ffae47 --- While at it, we also update the Elo estimate of ProbCut (see https://tests.stockfishchess.org/tests/view/609bfb597746e3dc74ffabe3). closes https://github.com/official-stockfish/Stockfish/pull/3462 bench: 3764662 --- src/search.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index d16d9cad897..788be984d13 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -847,7 +847,7 @@ namespace { probCutBeta = beta + 209 - 44 * improving; - // Step 9. ProbCut (~10 Elo) + // Step 9. ProbCut (~4 Elo) // 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 @@ -862,15 +862,6 @@ namespace { && ttValue != VALUE_NONE && ttValue < probCutBeta)) { - // if ttMove is a capture and value from transposition table is good enough produce probCut - // cutoff without digging into actual probCut search - if ( ss->ttHit - && tte->depth() >= depth - 3 - && ttValue != VALUE_NONE - && ttValue >= probCutBeta - && ttMove - && pos.capture_or_promotion(ttMove)) - return probCutBeta; assert(probCutBeta < VALUE_INFINITE); From c82f6f56a65759461f417602059ad7c97b9451aa Mon Sep 17 00:00:00 2001 From: bmc4 Date: Thu, 13 May 2021 23:47:41 -0300 Subject: [PATCH 0555/1766] Simplify LMR rules for statScore We simplify two parts of LMR which seem not to bring strength anymore. --- Individual Tests: https://tests.stockfishchess.org/tests/view/609d1cc15085663412d0856a https://tests.stockfishchess.org/tests/view/609cb0cc7746e3dc74ffae8d https://tests.stockfishchess.org/tests/view/609d1c9f5085663412d08568 --- LTC: LLR: 2.97 (-2.94,2.94) <-2.50,0.50> Total: 84184 W: 3093 L: 3066 D: 78025 Ptnml(0-2): 47, 2755, 36458, 2788, 44 https://tests.stockfishchess.org/tests/view/609d84615085663412d08e2f --- While at it, we also update the Elo estimate of the previous rule in LMR, see: https://tests.stockfishchess.org/tests/view/609a933c3a33eb67a844f7ca https://tests.stockfishchess.org/tests/view/609a959c3a33eb67a844f7d5 https://tests.stockfishchess.org/tests/view/609afff73a33eb67a844f870 --- closes https://github.com/official-stockfish/Stockfish/pull/3464 Bench: 4156523 --- src/search.cpp | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 788be984d13..e03016b61d9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1131,12 +1131,12 @@ namespace { { Depth r = reduction(improving, depth, moveCount); - // Decrease reduction if the ttHit running average is large + // Decrease reduction if the ttHit running average is large (~0 Elo) if (thisThread->ttHitAverage > 537 * TtHitAverageResolution * TtHitAverageWindow / 1024) r--; // Decrease reduction if position is or has been on the PV - // and node is not likely to fail low. (~10 Elo) + // and node is not likely to fail low. (~3 Elo) if ( ss->ttPv && !likelyFailLow) r -= 2; @@ -1162,9 +1162,10 @@ namespace { r++; // Increase reduction at root if failing high - r += rootNode ? thisThread->failedHighCnt * thisThread->failedHighCnt * moveCount / 512 : 0; + if (rootNode) + r += thisThread->failedHighCnt * thisThread->failedHighCnt * moveCount / 512; - // Increase reduction for cut nodes (~10 Elo) + // Increase reduction for cut nodes (~3 Elo) if (cutNode) r += 2; @@ -1174,20 +1175,8 @@ namespace { + (*contHist[3])[movedPiece][to_sq(move)] - 4741; - // Decrease/increase reduction by comparing opponent's stat score (~10 Elo) - if (ss->statScore >= -89 && (ss-1)->statScore < -116) - r--; - - else if ((ss-1)->statScore >= -112 && ss->statScore < -100) - r++; - // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - // If we are not in check use statScore, but if we are in check we use - // the sum of main history and first continuation history with an offset. - if (ss->inCheck) - r -= (thisThread->mainHistory[us][from_to(move)] - + (*contHist[0])[movedPiece][to_sq(move)] - 3833) / 16384; - else + if (!ss->inCheck) r -= ss->statScore / 14790; } From 61e1c66b7cb1dea9346a9b74e801e4da74ad7591 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sat, 15 May 2021 00:55:45 +0300 Subject: [PATCH 0556/1766] Simplification for countermoves based pruning Simplify away two extra conditions in countermoves based pruning. These conditions (both of them) were introduced quite a long time ago via speculative LTCs and seem to no longer bring any benefit. passed STC https://tests.stockfishchess.org/tests/view/609e81f35085663412d08f31 LLR: 2.96 (-2.94,2.94) <-2.50,0.50> Total: 28488 W: 2487 L: 2382 D: 23619 Ptnml(0-2): 87, 1919, 10123, 2032, 83 passed LTC https://tests.stockfishchess.org/tests/view/609e9c085085663412d08f59 LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 33176 W: 1219 L: 1155 D: 30802 Ptnml(0-2): 13, 1036, 14423, 1106, 10 closes https://github.com/official-stockfish/Stockfish/pull/3468 Bench: 4749514 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index e03016b61d9..bf6ba6c4cd6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1026,7 +1026,7 @@ namespace { else { // Countermoves based pruning (~20 Elo) - if ( lmrDepth < 4 + ((ss-1)->statScore > 0 || (ss-1)->moveCount == 1) + if ( lmrDepth < 4 && (*contHist[0])[movedPiece][to_sq(move)] < CounterMovePruneThreshold && (*contHist[1])[movedPiece][to_sq(move)] < CounterMovePruneThreshold) continue; From f90274d8ce1aad4ad0595aacbceb74b6cbe306a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Mon, 17 May 2021 09:13:34 +0200 Subject: [PATCH 0557/1766] Small clean-ups - Comment for Countemove pruning -> Continuation history - Fix comment in input_slice.h - Shorter lines in Makefile - Comment for scale factor - Fix comment for pinners in see_ge() - Change Thread.id() signature to size_t - Trailing space in reprosearch.sh - Add Douglas Matos Gomes to the AUTHORS file - Introduce comment for undo_null_move() - Use Stockfish coding style for export_net() - Change date in AUTHORS file closes https://github.com/official-stockfish/Stockfish/pull/3416 No functional change --- AUTHORS | 3 ++- src/Makefile | 6 ++---- src/evaluate.cpp | 28 ++++++++++++++++------------ src/nnue/layers/input_slice.h | 2 +- src/position.cpp | 9 ++++++--- src/search.cpp | 5 ++--- src/thread.h | 2 +- tests/reprosearch.sh | 2 +- 8 files changed, 31 insertions(+), 26 deletions(-) diff --git a/AUTHORS b/AUTHORS index 69d682f13f2..9042495fdc0 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,4 +1,4 @@ -# List of authors for Stockfish, as of March 31, 2021 +# List of authors for Stockfish, as of May 17, 2021 # Founders of the Stockfish project and fishtest infrastructure Tord Romstad (romstad) @@ -52,6 +52,7 @@ Dieter Dobbelaere (ddobbelaere) DiscanX Dominik Schlösser (domschl) double-beep +Douglas Matos Gomes (dsmsgms) Eduardo Cáceres (eduherminio) Eelco de Groot (KingDefender) Elvin Liu (solarlight2) diff --git a/src/Makefile b/src/Makefile index 71105bdb65d..660a13fb6bc 100644 --- a/src/Makefile +++ b/src/Makefile @@ -96,8 +96,7 @@ endif ifeq ($(ARCH), $(filter $(ARCH), \ x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-bmi2 x86-64-avx2 \ x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \ - x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-32 \ - e2k \ + x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-32 e2k \ armv7 armv7-neon armv8 apple-silicon general-64 general-32)) SUPPORTED_ARCH=true else @@ -840,8 +839,7 @@ config-sanity: net @test "$(optimize)" = "yes" || test "$(optimize)" = "no" @test "$(SUPPORTED_ARCH)" = "true" @test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \ - test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || \ - test "$(arch)" = "e2k" || \ + test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || test "$(arch)" = "e2k" || \ test "$(arch)" = "armv7" || test "$(arch)" = "armv8" || test "$(arch)" = "arm64" @test "$(bits)" = "32" || test "$(bits)" = "64" @test "$(prefetch)" = "yes" || test "$(prefetch)" = "no" diff --git a/src/evaluate.cpp b/src/evaluate.cpp index c396e0f7575..403d59dd70d 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -114,24 +114,28 @@ namespace Eval { } } + /// NNUE::export_net() exports the currently loaded network to a file void NNUE::export_net(const std::optional& filename) { std::string actualFilename; - if (filename.has_value()) { - actualFilename = filename.value(); - } else { - if (eval_file_loaded != EvalFileDefaultName) { - sync_cout << "Failed to export a net. A non-embedded net can only be saved if the filename is specified." << sync_endl; - return; - } - actualFilename = EvalFileDefaultName; + + if (filename.has_value()) + actualFilename = filename.value(); + else + { + if (eval_file_loaded != EvalFileDefaultName) + { + sync_cout << "Failed to export a net. A non-embedded net can only be saved if the filename is specified." << sync_endl; + return; + } + actualFilename = EvalFileDefaultName; } ofstream stream(actualFilename, std::ios_base::binary); - if (save_eval(stream)) { + + if (save_eval(stream)) sync_cout << "Network saved successfully to " << actualFilename << "." << sync_endl; - } else { + else sync_cout << "Failed to export a net." << sync_endl; - } } /// NNUE::verify() verifies that the last net used was loaded successfully @@ -927,7 +931,7 @@ namespace { Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK; int sf = me->scale_factor(pos, strongSide); - // If scale factor is not already specific, scale down via general heuristics + // If scale factor is not already specific, scale up/down via general heuristics if (sf == SCALE_FACTOR_NORMAL) { if (pos.opposite_bishops()) diff --git a/src/nnue/layers/input_slice.h b/src/nnue/layers/input_slice.h index bd4d74478de..b6bf1727b40 100644 --- a/src/nnue/layers/input_slice.h +++ b/src/nnue/layers/input_slice.h @@ -53,7 +53,7 @@ class InputSlice { return true; } - // Read network parameters + // Write network parameters bool write_parameters(std::ostream& /*stream*/) const { return true; } diff --git a/src/position.cpp b/src/position.cpp index 2b3be3f7767..f1c36156a3b 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -988,7 +988,7 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ } -/// Position::do(undo)_null_move() is used to do(undo) a "null move": it flips +/// Position::do_null_move() is used to do a "null move": it flips /// the side to move without executing any move on the board. void Position::do_null_move(StateInfo& newSt) { @@ -1027,6 +1027,9 @@ void Position::do_null_move(StateInfo& newSt) { assert(pos_is_ok()); } + +/// Position::undo_null_move() must be used to undo a "null move" + void Position::undo_null_move() { assert(!checkers()); @@ -1092,8 +1095,8 @@ bool Position::see_ge(Move m, Value threshold) const { if (!(stmAttackers = attackers & pieces(stm))) break; - // Don't allow pinned pieces to attack (except the king) as long as - // there are pinners on their original square. + // Don't allow pinned pieces to attack as long as there are + // pinners on their original square. if (pinners(~stm) & occupied) stmAttackers &= ~blockers_for_king(stm); diff --git a/src/search.cpp b/src/search.cpp index bf6ba6c4cd6..29b334ed5fb 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -862,7 +862,6 @@ namespace { && ttValue != VALUE_NONE && ttValue < probCutBeta)) { - assert(probCutBeta < VALUE_INFINITE); MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, &captureHistory); @@ -1025,7 +1024,7 @@ namespace { } else { - // Countermoves based pruning (~20 Elo) + // Continuation history based pruning (~20 Elo) if ( lmrDepth < 4 && (*contHist[0])[movedPiece][to_sq(move)] < CounterMovePruneThreshold && (*contHist[1])[movedPiece][to_sq(move)] < CounterMovePruneThreshold) @@ -1528,7 +1527,7 @@ namespace { [pos.moved_piece(move)] [to_sq(move)]; - // CounterMove based pruning + // Continuation history based pruning if ( !captureOrPromotion && bestValue > VALUE_TB_LOSS_IN_MAX_PLY && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < CounterMovePruneThreshold diff --git a/src/thread.h b/src/thread.h index 4cf5dabb5b1..5785fd25eb7 100644 --- a/src/thread.h +++ b/src/thread.h @@ -55,7 +55,7 @@ class Thread { void idle_loop(); void start_searching(); void wait_for_search_finished(); - int id() const { return idx; } + size_t id() const { return idx; } Pawns::Table pawnsTable; Material::Table materialTable; diff --git a/tests/reprosearch.sh b/tests/reprosearch.sh index 9fd847ff3a9..c1167f7f169 100755 --- a/tests/reprosearch.sh +++ b/tests/reprosearch.sh @@ -10,7 +10,7 @@ trap 'error ${LINENO}' ERR echo "reprosearch testing started" -# repeat two short games, separated by ucinewgame. +# repeat two short games, separated by ucinewgame. # with go nodes $nodes they should result in exactly # the same node count for each iteration. cat << EOF > repeat.exp From e8d64af1230fdac65bb0da246df3e7abe82e0838 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Tue, 18 May 2021 17:36:26 +0200 Subject: [PATCH 0558/1766] New NNUE architecture and net Introduces a new NNUE network architecture and associated network parameters, as obtained by a new pytorch trainer. The network is already very strong at short TC, without regression at longer TC, and has potential for further improvements. https://tests.stockfishchess.org/tests/view/60a159c65085663412d0921d TC: 10s+0.1s, 1 thread ELO: 21.74 +-3.4 (95%) LOS: 100.0% Total: 10000 W: 1559 L: 934 D: 7507 Ptnml(0-2): 38, 701, 2972, 1176, 113 https://tests.stockfishchess.org/tests/view/60a187005085663412d0925b TC: 60s+0.6s, 1 thread ELO: 5.85 +-1.7 (95%) LOS: 100.0% Total: 20000 W: 1381 L: 1044 D: 17575 Ptnml(0-2): 27, 885, 7864, 1172, 52 https://tests.stockfishchess.org/tests/view/60a2beede229097940a03806 TC: 20s+0.2s, 8 threads LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 34272 W: 1610 L: 1452 D: 31210 Ptnml(0-2): 30, 1285, 14350, 1439, 32 https://tests.stockfishchess.org/tests/view/60a2d687e229097940a03c72 TC: 60s+0.6s, 8 threads LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 45544 W: 1262 L: 1214 D: 43068 Ptnml(0-2): 12, 1129, 20442, 1177, 12 The network has been trained (by vondele) using the https://github.com/glinscott/nnue-pytorch/ trainer (started by glinscott), specifically the branch https://github.com/Sopel97/nnue-pytorch/tree/experiment_56. The data used are in 64 billion positions (193GB total) generated and scored with the current master net d8: https://drive.google.com/file/d/1hOOYSDKgOOp38ZmD0N4DV82TOLHzjUiF/view?usp=sharing d9: https://drive.google.com/file/d/1VlhnHL8f-20AXhGkILujnNXHwy9T-MQw/view?usp=sharing d10: https://drive.google.com/file/d/1ZC5upzBYMmMj1gMYCkt6rCxQG0GnO3Kk/view?usp=sharing fishtest_d9: https://drive.google.com/file/d/1GQHt0oNgKaHazwJFTRbXhlCN3FbUedFq/view?usp=sharing This network also contains a few architectural changes with respect to the current master: Size changed from 256x2-32-32-1 to 512x2-16-32-1 ~15-20% slower ~2x larger adds a special path for 16 valued ClippedReLU fixes affine transform code for 16 inputs/outputs, buy using InputDimensions instead of PaddedInputDimensions this is safe now because the inputs are processed in groups of 4 in the current affine transform code The feature set changed from HalfKP to HalfKAv2 Includes information about the kings like HalfKA Packs king features better, resulting in 8% size reduction compared to HalfKA The board is flipped for the black's perspective, instead of rotated like in the current master PSQT values for each feature the feature transformer now outputs a part that is fowarded directly to the output and allows learning piece values more directly than the previous network architecture. The effect is visible for high imbalance positions, where the current master network outputs evaluations skewed towards zero. 8 PSQT values per feature, chosen based on (popcount(pos.pieces()) - 1) / 4 initialized to classical material values on the start of the training 8 subnetworks (512x2->16->32->1), chosen based on (popcount(pos.pieces()) - 1) / 4 only one subnetwork is evaluated for any position, no or marginal speed loss A diagram of the network is available: https://user-images.githubusercontent.com/8037982/118656988-553a1700-b7eb-11eb-82ef-56a11cbebbf2.png A more complete description: https://github.com/glinscott/nnue-pytorch/blob/master/docs/nnue.md closes https://github.com/official-stockfish/Stockfish/pull/3474 Bench: 3806488 --- src/Makefile | 2 +- src/evaluate.cpp | 13 +- src/evaluate.h | 2 +- src/nnue/evaluate_nnue.cpp | 24 ++-- .../features/{half_kp.cpp => half_ka_v2.cpp} | 25 ++-- src/nnue/features/{half_kp.h => half_ka_v2.h} | 51 +++---- src/nnue/layers/affine_transform.h | 116 ++++----------- src/nnue/layers/clipped_relu.h | 50 +++++-- src/nnue/nnue_accumulator.h | 4 +- src/nnue/nnue_architecture.h | 10 +- src/nnue/nnue_common.h | 2 +- src/nnue/nnue_feature_transformer.h | 133 +++++++++++++++++- src/search.cpp | 6 +- 13 files changed, 265 insertions(+), 173 deletions(-) rename src/nnue/features/{half_kp.cpp => half_ka_v2.cpp} (77%) rename src/nnue/features/{half_kp.h => half_ka_v2.h} (73%) diff --git a/src/Makefile b/src/Makefile index 660a13fb6bc..066e7697dee 100644 --- a/src/Makefile +++ b/src/Makefile @@ -41,7 +41,7 @@ endif SRCS = benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.cpp main.cpp \ material.cpp misc.cpp movegen.cpp movepick.cpp pawns.cpp position.cpp psqt.cpp \ search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \ - nnue/evaluate_nnue.cpp nnue/features/half_kp.cpp + nnue/evaluate_nnue.cpp nnue/features/half_ka_v2.cpp OBJS = $(notdir $(SRCS:.cpp=.o)) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 403d59dd70d..256bd9944f3 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -120,7 +120,7 @@ namespace Eval { if (filename.has_value()) actualFilename = filename.value(); - else + else { if (eval_file_loaded != EvalFileDefaultName) { @@ -1116,10 +1116,8 @@ Value Eval::evaluate(const Position& pos) { // Scale and shift NNUE for compatibility with search and classical evaluation auto adjusted_NNUE = [&]() { - int material = pos.non_pawn_material() + 4 * PawnValueMg * pos.count(); - int scale = 580 - + material / 32 - - 4 * pos.rule50_count(); + + int scale = 903 + 28 * pos.count() + 28 * pos.non_pawn_material() / 1024; Value nnue = NNUE::evaluate(pos) * scale / 1024 + Time.tempoNNUE; @@ -1134,7 +1132,7 @@ Value Eval::evaluate(const Position& pos) { Value psq = Value(abs(eg_value(pos.psq_score()))); int r50 = 16 + pos.rule50_count(); bool largePsq = psq * 16 > (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50; - bool classical = largePsq || (psq > PawnValueMg / 4 && !(pos.this_thread()->nodes & 0xB)); + bool classical = largePsq; // Use classical evaluation for really low piece endgames. // One critical case is the draw for bishop + A/H file pawn vs naked king. @@ -1151,8 +1149,7 @@ Value Eval::evaluate(const Position& pos) { && !lowPieceEndgame && ( abs(v) * 16 < NNUEThreshold2 * r50 || ( pos.opposite_bishops() - && abs(v) * 16 < (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50 - && !(pos.this_thread()->nodes & 0xB)))) + && abs(v) * 16 < (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50))) v = adjusted_NNUE(); } diff --git a/src/evaluate.h b/src/evaluate.h index 128a7caefcc..ee4c175b34f 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-62ef826d1a6d.nnue" + #define EvalFileDefaultName "nn-8a08400ed089.nnue" namespace NNUE { diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index e0d4b9117c7..97cef81480f 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -35,7 +35,7 @@ namespace Stockfish::Eval::NNUE { LargePagePtr featureTransformer; // Evaluation function - AlignedPtr network; + AlignedPtr network[LayerStacks]; // Evaluation function file name std::string fileName; @@ -83,7 +83,8 @@ namespace Stockfish::Eval::NNUE { void initialize() { Detail::initialize(featureTransformer); - Detail::initialize(network); + for (std::size_t i = 0; i < LayerStacks; ++i) + Detail::initialize(network[i]); } // Read network header @@ -92,7 +93,7 @@ namespace Stockfish::Eval::NNUE { std::uint32_t version, size; version = read_little_endian(stream); - *hashValue = read_little_endian(stream); + *hashValue = read_little_endian(stream); size = read_little_endian(stream); if (!stream || version != Version) return false; desc->resize(size); @@ -117,7 +118,8 @@ namespace Stockfish::Eval::NNUE { if (!read_header(stream, &hashValue, &netDescription)) return false; if (hashValue != HashValue) return false; if (!Detail::read_parameters(stream, *featureTransformer)) return false; - if (!Detail::read_parameters(stream, *network)) return false; + for (std::size_t i = 0; i < LayerStacks; ++i) + if (!Detail::read_parameters(stream, *(network[i]))) return false; return stream && stream.peek() == std::ios::traits_type::eof(); } @@ -126,7 +128,8 @@ namespace Stockfish::Eval::NNUE { if (!write_header(stream, HashValue, netDescription)) return false; if (!Detail::write_parameters(stream, *featureTransformer)) return false; - if (!Detail::write_parameters(stream, *network)) return false; + for (std::size_t i = 0; i < LayerStacks; ++i) + if (!Detail::write_parameters(stream, *(network[i]))) return false; return (bool)stream; } @@ -154,10 +157,15 @@ namespace Stockfish::Eval::NNUE { ASSERT_ALIGNED(transformedFeatures, alignment); ASSERT_ALIGNED(buffer, alignment); - featureTransformer->transform(pos, transformedFeatures); - const auto output = network->propagate(transformedFeatures, buffer); + const std::size_t bucket = (pos.count() - 1) / 4; - return static_cast(output[0] / OutputScale); + const auto [psqt, lazy] = featureTransformer->transform(pos, transformedFeatures, bucket); + if (lazy) { + return static_cast(psqt / OutputScale); + } else { + const auto output = network[bucket]->propagate(transformedFeatures, buffer); + return static_cast((output[0] + psqt) / OutputScale); + } } // Load eval, from a file stream or a memory stream diff --git a/src/nnue/features/half_kp.cpp b/src/nnue/features/half_ka_v2.cpp similarity index 77% rename from src/nnue/features/half_kp.cpp rename to src/nnue/features/half_ka_v2.cpp index aa1deceece2..57f43e50f2f 100644 --- a/src/nnue/features/half_kp.cpp +++ b/src/nnue/features/half_ka_v2.cpp @@ -16,32 +16,32 @@ along with this program. If not, see . */ -//Definition of input features HalfKP of NNUE evaluation function +//Definition of input features HalfKAv2 of NNUE evaluation function -#include "half_kp.h" +#include "half_ka_v2.h" #include "../../position.h" namespace Stockfish::Eval::NNUE::Features { // Orient a square according to perspective (rotates by 180 for black) - inline Square HalfKP::orient(Color perspective, Square s) { - return Square(int(s) ^ (bool(perspective) * 63)); + inline Square HalfKAv2::orient(Color perspective, Square s) { + return Square(int(s) ^ (bool(perspective) * 56)); } // Index of a feature for a given king position and another piece on some square - inline IndexType HalfKP::make_index(Color perspective, Square s, Piece pc, Square ksq) { + inline IndexType HalfKAv2::make_index(Color perspective, Square s, Piece pc, Square ksq) { return IndexType(orient(perspective, s) + PieceSquareIndex[perspective][pc] + PS_NB * ksq); } // Get a list of indices for active features - void HalfKP::append_active_indices( + void HalfKAv2::append_active_indices( const Position& pos, Color perspective, ValueListInserter active ) { Square ksq = orient(perspective, pos.square(perspective)); - Bitboard bb = pos.pieces() & ~pos.pieces(KING); + Bitboard bb = pos.pieces(); while (bb) { Square s = pop_lsb(bb); @@ -52,7 +52,7 @@ namespace Stockfish::Eval::NNUE::Features { // append_changed_indices() : get a list of indices for recently changed features - void HalfKP::append_changed_indices( + void HalfKAv2::append_changed_indices( Square ksq, StateInfo* st, Color perspective, @@ -63,7 +63,6 @@ namespace Stockfish::Eval::NNUE::Features { Square oriented_ksq = orient(perspective, ksq); for (int i = 0; i < dp.dirty_num; ++i) { Piece pc = dp.piece[i]; - if (type_of(pc) == KING) continue; if (dp.from[i] != SQ_NONE) removed.push_back(make_index(perspective, dp.from[i], pc, oriented_ksq)); if (dp.to[i] != SQ_NONE) @@ -71,15 +70,15 @@ namespace Stockfish::Eval::NNUE::Features { } } - int HalfKP::update_cost(StateInfo* st) { + int HalfKAv2::update_cost(StateInfo* st) { return st->dirtyPiece.dirty_num; } - int HalfKP::refresh_cost(const Position& pos) { - return pos.count() - 2; + int HalfKAv2::refresh_cost(const Position& pos) { + return pos.count(); } - bool HalfKP::requires_refresh(StateInfo* st, Color perspective) { + bool HalfKAv2::requires_refresh(StateInfo* st, Color perspective) { return st->dirtyPiece.piece[0] == make_piece(perspective, KING); } diff --git a/src/nnue/features/half_kp.h b/src/nnue/features/half_ka_v2.h similarity index 73% rename from src/nnue/features/half_kp.h rename to src/nnue/features/half_ka_v2.h index a09c221b1ac..e4b2edd9fc0 100644 --- a/src/nnue/features/half_kp.h +++ b/src/nnue/features/half_ka_v2.h @@ -18,8 +18,8 @@ //Definition of input features HalfKP of NNUE evaluation function -#ifndef NNUE_FEATURES_HALF_KP_H_INCLUDED -#define NNUE_FEATURES_HALF_KP_H_INCLUDED +#ifndef NNUE_FEATURES_HALF_KA_V2_H_INCLUDED +#define NNUE_FEATURES_HALF_KA_V2_H_INCLUDED #include "../nnue_common.h" @@ -32,33 +32,34 @@ namespace Stockfish { namespace Stockfish::Eval::NNUE::Features { - // Feature HalfKP: Combination of the position of own king - // and the position of pieces other than kings - class HalfKP { + // Feature HalfKAv2: Combination of the position of own king + // and the position of pieces + class HalfKAv2 { // unique number for each piece type on each square enum { PS_NONE = 0, - PS_W_PAWN = 1, - PS_B_PAWN = 1 * SQUARE_NB + 1, - PS_W_KNIGHT = 2 * SQUARE_NB + 1, - PS_B_KNIGHT = 3 * SQUARE_NB + 1, - PS_W_BISHOP = 4 * SQUARE_NB + 1, - PS_B_BISHOP = 5 * SQUARE_NB + 1, - PS_W_ROOK = 6 * SQUARE_NB + 1, - PS_B_ROOK = 7 * SQUARE_NB + 1, - PS_W_QUEEN = 8 * SQUARE_NB + 1, - PS_B_QUEEN = 9 * SQUARE_NB + 1, - PS_NB = 10 * SQUARE_NB + 1 + PS_W_PAWN = 0, + PS_B_PAWN = 1 * SQUARE_NB, + PS_W_KNIGHT = 2 * SQUARE_NB, + PS_B_KNIGHT = 3 * SQUARE_NB, + PS_W_BISHOP = 4 * SQUARE_NB, + PS_B_BISHOP = 5 * SQUARE_NB, + PS_W_ROOK = 6 * SQUARE_NB, + PS_B_ROOK = 7 * SQUARE_NB, + PS_W_QUEEN = 8 * SQUARE_NB, + PS_B_QUEEN = 9 * SQUARE_NB, + PS_KING = 10 * SQUARE_NB, + PS_NB = 11 * SQUARE_NB }; static constexpr IndexType PieceSquareIndex[COLOR_NB][PIECE_NB] = { // convention: W - us, B - them // viewed from other side, W and B are reversed - { PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_NONE, PS_NONE, - PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_NONE, PS_NONE }, - { PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_NONE, PS_NONE, - PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_NONE, PS_NONE } + { PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE, + PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE }, + { PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE, + PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE } }; // Orient a square according to perspective (rotates by 180 for black) @@ -69,17 +70,17 @@ namespace Stockfish::Eval::NNUE::Features { public: // Feature name - static constexpr const char* Name = "HalfKP(Friend)"; + static constexpr const char* Name = "HalfKAv2(Friend)"; // Hash value embedded in the evaluation file - static constexpr std::uint32_t HashValue = 0x5D69D5B8u; + static constexpr std::uint32_t HashValue = 0x5f234cb8u; // Number of feature dimensions static constexpr IndexType Dimensions = static_cast(SQUARE_NB) * static_cast(PS_NB); - // Maximum number of simultaneously active features. 30 because kins are not included. - static constexpr IndexType MaxActiveDimensions = 30; + // Maximum number of simultaneously active features. + static constexpr IndexType MaxActiveDimensions = 32; // Get a list of indices for active features static void append_active_indices( @@ -107,4 +108,4 @@ namespace Stockfish::Eval::NNUE::Features { } // namespace Stockfish::Eval::NNUE::Features -#endif // #ifndef NNUE_FEATURES_HALF_KP_H_INCLUDED +#endif // #ifndef NNUE_FEATURES_HALF_KA_V2_H_INCLUDED diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index fc1926912df..9a3b778e6bb 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -69,62 +69,15 @@ namespace Stockfish::Eval::NNUE::Layers { if (!previousLayer.read_parameters(stream)) return false; for (std::size_t i = 0; i < OutputDimensions; ++i) biases[i] = read_little_endian(stream); -#if !defined (USE_SSSE3) for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) +#if !defined (USE_SSSE3) weights[i] = read_little_endian(stream); #else - std::unique_ptr indexMap = std::make_unique(OutputDimensions * PaddedInputDimensions); - for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) { - const uint32_t scrambledIdx = + weights[ (i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 + i / PaddedInputDimensions * 4 + - i % 4; - weights[scrambledIdx] = read_little_endian(stream); - indexMap[scrambledIdx] = i; - } - - // Determine if eights of weight and input products can be summed using 16bits - // without saturation. We assume worst case combinations of 0 and 127 for all inputs. - if (OutputDimensions > 1 && !stream.fail()) - { - canSaturate16.count = 0; -#if !defined(USE_VNNI) - for (IndexType i = 0; i < PaddedInputDimensions; i += 16) - for (IndexType j = 0; j < OutputDimensions; ++j) - for (int x = 0; x < 2; ++x) - { - WeightType* w = &weights[i * OutputDimensions + j * 4 + x * 2]; - int sum[2] = {0, 0}; - for (int k = 0; k < 8; ++k) - { - IndexType idx = k / 2 * OutputDimensions * 4 + k % 2; - sum[w[idx] < 0] += w[idx]; - } - for (int sign : { -1, 1 }) - while (sign * sum[sign == -1] > 258) - { - int maxK = 0, maxW = 0; - for (int k = 0; k < 8; ++k) - { - IndexType idx = k / 2 * OutputDimensions * 4 + k % 2; - if (maxW < sign * w[idx]) - maxK = k, maxW = sign * w[idx]; - } - - IndexType idx = maxK / 2 * OutputDimensions * 4 + maxK % 2; - sum[sign == -1] -= w[idx]; - const uint32_t scrambledIdx = idx + i * OutputDimensions + j * 4 + x * 2; - canSaturate16.add(j, i + maxK / 2 * 4 + maxK % 2 + x * 2, w[idx], indexMap[scrambledIdx]); - w[idx] = 0; - } - } - - // Non functional optimization for faster more linear access - std::sort(canSaturate16.ids, canSaturate16.ids + canSaturate16.count, - [](const typename CanSaturate::Entry& e1, const typename CanSaturate::Entry& e2) - { return e1.in == e2.in ? e1.out < e2.out : e1.in < e2.in; }); -#endif - } + i % 4 + ] = read_little_endian(stream); #endif return !stream.fail(); @@ -148,8 +101,6 @@ namespace Stockfish::Eval::NNUE::Layers { i % 4 ]; } - for (int i = 0; i < canSaturate16.count; ++i) - unscrambledWeights[canSaturate16.ids[i].wIdx] = canSaturate16.ids[i].w; for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) write_little_endian(stream, unscrambledWeights[i]); @@ -194,11 +145,11 @@ namespace Stockfish::Eval::NNUE::Layers { __m512i product1 = _mm512_maddubs_epi16(a1, b1); __m512i product2 = _mm512_maddubs_epi16(a2, b2); __m512i product3 = _mm512_maddubs_epi16(a3, b3); - product0 = _mm512_add_epi16(product0, product1); - product2 = _mm512_add_epi16(product2, product3); - product0 = _mm512_add_epi16(product0, product2); + product0 = _mm512_adds_epi16(product0, product1); product0 = _mm512_madd_epi16(product0, Ones512); - acc = _mm512_add_epi32(acc, product0); + product2 = _mm512_adds_epi16(product2, product3); + product2 = _mm512_madd_epi16(product2, Ones512); + acc = _mm512_add_epi32(acc, _mm512_add_epi32(product0, product2)); #endif }; @@ -236,11 +187,11 @@ namespace Stockfish::Eval::NNUE::Layers { __m256i product1 = _mm256_maddubs_epi16(a1, b1); __m256i product2 = _mm256_maddubs_epi16(a2, b2); __m256i product3 = _mm256_maddubs_epi16(a3, b3); - product0 = _mm256_add_epi16(product0, product1); - product2 = _mm256_add_epi16(product2, product3); - product0 = _mm256_add_epi16(product0, product2); + product0 = _mm256_adds_epi16(product0, product1); product0 = _mm256_madd_epi16(product0, Ones256); - acc = _mm256_add_epi32(acc, product0); + product2 = _mm256_adds_epi16(product2, product3); + product2 = _mm256_madd_epi16(product2, Ones256); + acc = _mm256_add_epi32(acc, _mm256_add_epi32(product0, product2)); #endif }; @@ -267,11 +218,11 @@ namespace Stockfish::Eval::NNUE::Layers { __m128i product1 = _mm_maddubs_epi16(a1, b1); __m128i product2 = _mm_maddubs_epi16(a2, b2); __m128i product3 = _mm_maddubs_epi16(a3, b3); - product0 = _mm_add_epi16(product0, product1); - product2 = _mm_add_epi16(product2, product3); - product0 = _mm_add_epi16(product0, product2); + product0 = _mm_adds_epi16(product0, product1); product0 = _mm_madd_epi16(product0, Ones128); - acc = _mm_add_epi32(acc, product0); + product2 = _mm_adds_epi16(product2, product3); + product2 = _mm_madd_epi16(product2, Ones128); + acc = _mm_add_epi32(acc, _mm_add_epi32(product0, product2)); }; #endif @@ -300,6 +251,8 @@ namespace Stockfish::Eval::NNUE::Layers { #endif #if defined (USE_SSSE3) + // Different layout, we process 4 inputs at a time, always. + static_assert(InputDimensions % 4 == 0); const auto output = reinterpret_cast(buffer); const auto inputVector = reinterpret_cast(input); @@ -310,7 +263,7 @@ namespace Stockfish::Eval::NNUE::Layers { // because then it is also an input dimension. if constexpr (OutputDimensions % OutputSimdWidth == 0) { - constexpr IndexType NumChunks = PaddedInputDimensions / 4; + constexpr IndexType NumChunks = InputDimensions / 4; const auto input32 = reinterpret_cast(input); vec_t* outptr = reinterpret_cast(output); @@ -329,8 +282,6 @@ namespace Stockfish::Eval::NNUE::Layers { for (int j = 0; j * OutputSimdWidth < OutputDimensions; ++j) vec_add_dpbusd_32x4(outptr[j], in0, col0[j], in1, col1[j], in2, col2[j], in3, col3[j]); } - for (int i = 0; i < canSaturate16.count; ++i) - output[canSaturate16.ids[i].out] += input[canSaturate16.ids[i].in] * canSaturate16.ids[i].w; } else if constexpr (OutputDimensions == 1) { @@ -377,17 +328,21 @@ namespace Stockfish::Eval::NNUE::Layers { auto output = reinterpret_cast(buffer); #if defined(USE_SSE2) - constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; + // At least a multiple of 16, with SSE2. + static_assert(InputDimensions % SimdWidth == 0); + constexpr IndexType NumChunks = InputDimensions / SimdWidth; const __m128i Zeros = _mm_setzero_si128(); const auto inputVector = reinterpret_cast(input); #elif defined(USE_MMX) - constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; + static_assert(InputDimensions % SimdWidth == 0); + constexpr IndexType NumChunks = InputDimensions / SimdWidth; const __m64 Zeros = _mm_setzero_si64(); const auto inputVector = reinterpret_cast(input); #elif defined(USE_NEON) - constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; + static_assert(InputDimensions % SimdWidth == 0); + constexpr IndexType NumChunks = InputDimensions / SimdWidth; const auto inputVector = reinterpret_cast(input); #endif @@ -473,25 +428,6 @@ namespace Stockfish::Eval::NNUE::Layers { alignas(CacheLineSize) BiasType biases[OutputDimensions]; alignas(CacheLineSize) WeightType weights[OutputDimensions * PaddedInputDimensions]; -#if defined (USE_SSSE3) - struct CanSaturate { - int count; - struct Entry { - uint32_t wIdx; - uint16_t out; - uint16_t in; - int8_t w; - } ids[PaddedInputDimensions * OutputDimensions * 3 / 4]; - - void add(int i, int j, int8_t w, uint32_t wIdx) { - ids[count].wIdx = wIdx; - ids[count].out = i; - ids[count].in = j; - ids[count].w = w; - ++count; - } - } canSaturate16; -#endif }; } // namespace Stockfish::Eval::NNUE::Layers diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index f1ac2dfe644..65455df4944 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -72,22 +72,42 @@ namespace Stockfish::Eval::NNUE::Layers { const auto output = reinterpret_cast(buffer); #if defined(USE_AVX2) - constexpr IndexType NumChunks = InputDimensions / SimdWidth; - const __m256i Zero = _mm256_setzero_si256(); - const __m256i Offsets = _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0); - const auto in = reinterpret_cast(input); - const auto out = reinterpret_cast<__m256i*>(output); - for (IndexType i = 0; i < NumChunks; ++i) { - const __m256i words0 = _mm256_srai_epi16(_mm256_packs_epi32( - _mm256_load_si256(&in[i * 4 + 0]), - _mm256_load_si256(&in[i * 4 + 1])), WeightScaleBits); - const __m256i words1 = _mm256_srai_epi16(_mm256_packs_epi32( - _mm256_load_si256(&in[i * 4 + 2]), - _mm256_load_si256(&in[i * 4 + 3])), WeightScaleBits); - _mm256_store_si256(&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8( - _mm256_packs_epi16(words0, words1), Zero), Offsets)); + if constexpr (InputDimensions % SimdWidth == 0) { + constexpr IndexType NumChunks = InputDimensions / SimdWidth; + const __m256i Zero = _mm256_setzero_si256(); + const __m256i Offsets = _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0); + const auto in = reinterpret_cast(input); + const auto out = reinterpret_cast<__m256i*>(output); + for (IndexType i = 0; i < NumChunks; ++i) { + const __m256i words0 = _mm256_srai_epi16(_mm256_packs_epi32( + _mm256_load_si256(&in[i * 4 + 0]), + _mm256_load_si256(&in[i * 4 + 1])), WeightScaleBits); + const __m256i words1 = _mm256_srai_epi16(_mm256_packs_epi32( + _mm256_load_si256(&in[i * 4 + 2]), + _mm256_load_si256(&in[i * 4 + 3])), WeightScaleBits); + _mm256_store_si256(&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8( + _mm256_packs_epi16(words0, words1), Zero), Offsets)); + } + } else { + constexpr IndexType NumChunks = InputDimensions / (SimdWidth / 2); + const __m128i Zero = _mm_setzero_si128(); + const auto in = reinterpret_cast(input); + const auto out = reinterpret_cast<__m128i*>(output); + for (IndexType i = 0; i < NumChunks; ++i) { + const __m128i words0 = _mm_srai_epi16(_mm_packs_epi32( + _mm_load_si128(&in[i * 4 + 0]), + _mm_load_si128(&in[i * 4 + 1])), WeightScaleBits); + const __m128i words1 = _mm_srai_epi16(_mm_packs_epi32( + _mm_load_si128(&in[i * 4 + 2]), + _mm_load_si128(&in[i * 4 + 3])), WeightScaleBits); + const __m128i packedbytes = _mm_packs_epi16(words0, words1); + _mm_store_si128(&out[i], _mm_max_epi8(packedbytes, Zero)); + } } - constexpr IndexType Start = NumChunks * SimdWidth; + constexpr IndexType Start = + InputDimensions % SimdWidth == 0 + ? InputDimensions / SimdWidth * SimdWidth + : InputDimensions / (SimdWidth / 2) * (SimdWidth / 2); #elif defined(USE_SSE2) constexpr IndexType NumChunks = InputDimensions / SimdWidth; diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index 72a151f8eaa..e24902c4c68 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -30,8 +30,8 @@ namespace Stockfish::Eval::NNUE { // Class that holds the result of affine transformation of input features struct alignas(CacheLineSize) Accumulator { - std::int16_t - accumulation[2][TransformedFeatureDimensions]; + std::int16_t accumulation[2][TransformedFeatureDimensions]; + std::int32_t psqtAccumulation[2][PSQTBuckets]; AccumulatorState state[2]; }; diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index 55a01fbe15d..879a39cdbe6 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -23,7 +23,7 @@ #include "nnue_common.h" -#include "features/half_kp.h" +#include "features/half_ka_v2.h" #include "layers/input_slice.h" #include "layers/affine_transform.h" @@ -32,16 +32,18 @@ namespace Stockfish::Eval::NNUE { // Input features used in evaluation function - using FeatureSet = Features::HalfKP; + using FeatureSet = Features::HalfKAv2; // Number of input feature dimensions after conversion - constexpr IndexType TransformedFeatureDimensions = 256; + constexpr IndexType TransformedFeatureDimensions = 512; + constexpr IndexType PSQTBuckets = 8; + constexpr IndexType LayerStacks = 8; namespace Layers { // Define network structure using InputLayer = InputSlice; - using HiddenLayer1 = ClippedReLU>; + using HiddenLayer1 = ClippedReLU>; using HiddenLayer2 = ClippedReLU>; using OutputLayer = AffineTransform; diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index d41e02377ac..dc70006120d 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -46,7 +46,7 @@ namespace Stockfish::Eval::NNUE { // Version of the evaluation file - constexpr std::uint32_t Version = 0x7AF32F16u; + constexpr std::uint32_t Version = 0x7AF32F20u; // Constant used in evaluation value calculation constexpr int OutputScale = 16; diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index a4a8e98f9c5..2c0a0c6d313 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -35,45 +35,82 @@ namespace Stockfish::Eval::NNUE { // vector registers. #define VECTOR + static_assert(PSQTBuckets == 8, "Assumed by the current choice of constants."); + #ifdef USE_AVX512 typedef __m512i vec_t; + typedef __m256i psqt_vec_t; #define vec_load(a) _mm512_load_si512(a) #define vec_store(a,b) _mm512_store_si512(a,b) #define vec_add_16(a,b) _mm512_add_epi16(a,b) #define vec_sub_16(a,b) _mm512_sub_epi16(a,b) + #define vec_load_psqt(a) _mm256_load_si256(a) + #define vec_store_psqt(a,b) _mm256_store_si256(a,b) + #define vec_add_psqt_32(a,b) _mm256_add_epi32(a,b) + #define vec_sub_psqt_32(a,b) _mm256_sub_epi32(a,b) + #define vec_zero_psqt() _mm256_setzero_si256() static constexpr IndexType NumRegs = 8; // only 8 are needed + static constexpr IndexType NumPsqtRegs = 1; #elif USE_AVX2 typedef __m256i vec_t; + typedef __m256i psqt_vec_t; #define vec_load(a) _mm256_load_si256(a) #define vec_store(a,b) _mm256_store_si256(a,b) #define vec_add_16(a,b) _mm256_add_epi16(a,b) #define vec_sub_16(a,b) _mm256_sub_epi16(a,b) + #define vec_load_psqt(a) _mm256_load_si256(a) + #define vec_store_psqt(a,b) _mm256_store_si256(a,b) + #define vec_add_psqt_32(a,b) _mm256_add_epi32(a,b) + #define vec_sub_psqt_32(a,b) _mm256_sub_epi32(a,b) + #define vec_zero_psqt() _mm256_setzero_si256() static constexpr IndexType NumRegs = 16; + static constexpr IndexType NumPsqtRegs = 1; #elif USE_SSE2 typedef __m128i vec_t; + typedef __m128i psqt_vec_t; #define vec_load(a) (*(a)) #define vec_store(a,b) *(a)=(b) #define vec_add_16(a,b) _mm_add_epi16(a,b) #define vec_sub_16(a,b) _mm_sub_epi16(a,b) + #define vec_load_psqt(a) (*(a)) + #define vec_store_psqt(a,b) *(a)=(b) + #define vec_add_psqt_32(a,b) _mm_add_epi32(a,b) + #define vec_sub_psqt_32(a,b) _mm_sub_epi32(a,b) + #define vec_zero_psqt() _mm_setzero_si128() static constexpr IndexType NumRegs = Is64Bit ? 16 : 8; + static constexpr IndexType NumPsqtRegs = 2; #elif USE_MMX typedef __m64 vec_t; + typedef std::int32_t psqt_vec_t; #define vec_load(a) (*(a)) #define vec_store(a,b) *(a)=(b) #define vec_add_16(a,b) _mm_add_pi16(a,b) #define vec_sub_16(a,b) _mm_sub_pi16(a,b) + #define vec_load_psqt(a) (*(a)) + #define vec_store_psqt(a,b) *(a)=(b) + #define vec_add_psqt_32(a,b) a+b + #define vec_sub_psqt_32(a,b) a-b + #define vec_zero_psqt() 0 static constexpr IndexType NumRegs = 8; + static constexpr IndexType NumPsqtRegs = 8; #elif USE_NEON typedef int16x8_t vec_t; + typedef int32x4_t psqt_vec_t; #define vec_load(a) (*(a)) #define vec_store(a,b) *(a)=(b) #define vec_add_16(a,b) vaddq_s16(a,b) #define vec_sub_16(a,b) vsubq_s16(a,b) + #define vec_load_psqt(a) (*(a)) + #define vec_store_psqt(a,b) *(a)=(b) + #define vec_add_psqt_32(a,b) vaddq_s32(a,b) + #define vec_sub_psqt_32(a,b) vsubq_s32(a,b) + #define vec_zero_psqt() psqt_vec_t{0} static constexpr IndexType NumRegs = 16; + static constexpr IndexType NumPsqtRegs = 2; #else #undef VECTOR @@ -87,9 +124,13 @@ namespace Stockfish::Eval::NNUE { // Number of output dimensions for one side static constexpr IndexType HalfDimensions = TransformedFeatureDimensions; + static constexpr int LazyThreshold = 1400; + #ifdef VECTOR static constexpr IndexType TileHeight = NumRegs * sizeof(vec_t) / 2; + static constexpr IndexType PsqtTileHeight = NumPsqtRegs * sizeof(psqt_vec_t) / 4; static_assert(HalfDimensions % TileHeight == 0, "TileHeight must divide HalfDimensions"); + static_assert(PSQTBuckets % PsqtTileHeight == 0, "PsqtTileHeight must divide PSQTBuckets"); #endif public: @@ -115,6 +156,8 @@ namespace Stockfish::Eval::NNUE { biases[i] = read_little_endian(stream); for (std::size_t i = 0; i < HalfDimensions * InputDimensions; ++i) weights[i] = read_little_endian(stream); + for (std::size_t i = 0; i < PSQTBuckets * InputDimensions; ++i) + psqtWeights[i] = read_little_endian(stream); return !stream.fail(); } @@ -128,11 +171,21 @@ namespace Stockfish::Eval::NNUE { } // Convert input features - void transform(const Position& pos, OutputType* output) const { + std::pair transform(const Position& pos, OutputType* output, int bucket) const { update_accumulator(pos, WHITE); update_accumulator(pos, BLACK); + const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()}; const auto& accumulation = pos.state()->accumulator.accumulation; + const auto& psqtAccumulation = pos.state()->accumulator.psqtAccumulation; + + const auto psqt = ( + psqtAccumulation[static_cast(perspectives[0])][bucket] + - psqtAccumulation[static_cast(perspectives[1])][bucket] + ) / 2; + + if (abs(psqt) > LazyThreshold * OutputScale) + return { psqt, true }; #if defined(USE_AVX512) constexpr IndexType NumChunks = HalfDimensions / (SimdWidth * 2); @@ -163,7 +216,6 @@ namespace Stockfish::Eval::NNUE { const int8x8_t Zero = {0}; #endif - const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()}; for (IndexType p = 0; p < 2; ++p) { const IndexType offset = HalfDimensions * p; @@ -240,6 +292,8 @@ namespace Stockfish::Eval::NNUE { #if defined(USE_MMX) _mm_empty(); #endif + + return { psqt, false }; } private: @@ -255,6 +309,7 @@ namespace Stockfish::Eval::NNUE { // Gcc-10.2 unnecessarily spills AVX2 registers if this array // is defined in the VECTOR code below, once in each branch vec_t acc[NumRegs]; + psqt_vec_t psqt[NumPsqtRegs]; #endif // Look for a usable accumulator of an earlier position. We keep track @@ -333,12 +388,52 @@ namespace Stockfish::Eval::NNUE { } } + for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j) + { + // Load accumulator + auto accTilePsqt = reinterpret_cast( + &st->accumulator.psqtAccumulation[perspective][j * PsqtTileHeight]); + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + psqt[k] = vec_load_psqt(&accTilePsqt[k]); + + for (IndexType i = 0; states_to_update[i]; ++i) + { + // Difference calculation for the deactivated features + for (const auto index : removed[i]) + { + const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; + auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + psqt[k] = vec_sub_psqt_32(psqt[k], columnPsqt[k]); + } + + // Difference calculation for the activated features + for (const auto index : added[i]) + { + const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; + auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]); + } + + // Store accumulator + accTilePsqt = reinterpret_cast( + &states_to_update[i]->accumulator.psqtAccumulation[perspective][j * PsqtTileHeight]); + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + vec_store_psqt(&accTilePsqt[k], psqt[k]); + } + } + #else for (IndexType i = 0; states_to_update[i]; ++i) { std::memcpy(states_to_update[i]->accumulator.accumulation[perspective], st->accumulator.accumulation[perspective], HalfDimensions * sizeof(BiasType)); + + for (std::size_t k = 0; k < PSQTBuckets; ++k) + states_to_update[i]->accumulator.psqtAccumulation[perspective][k] = st->accumulator.psqtAccumulation[perspective][k]; + st = states_to_update[i]; // Difference calculation for the deactivated features @@ -348,6 +443,9 @@ namespace Stockfish::Eval::NNUE { for (IndexType j = 0; j < HalfDimensions; ++j) st->accumulator.accumulation[perspective][j] -= weights[offset + j]; + + for (std::size_t k = 0; k < PSQTBuckets; ++k) + st->accumulator.psqtAccumulation[perspective][k] -= psqtWeights[index * PSQTBuckets + k]; } // Difference calculation for the activated features @@ -357,6 +455,9 @@ namespace Stockfish::Eval::NNUE { for (IndexType j = 0; j < HalfDimensions; ++j) st->accumulator.accumulation[perspective][j] += weights[offset + j]; + + for (std::size_t k = 0; k < PSQTBuckets; ++k) + st->accumulator.psqtAccumulation[perspective][k] += psqtWeights[index * PSQTBuckets + k]; } } #endif @@ -392,16 +493,42 @@ namespace Stockfish::Eval::NNUE { vec_store(&accTile[k], acc[k]); } + for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j) + { + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + psqt[k] = vec_zero_psqt(); + + for (const auto index : active) + { + const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; + auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); + + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]); + } + + auto accTilePsqt = reinterpret_cast( + &accumulator.psqtAccumulation[perspective][j * PsqtTileHeight]); + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + vec_store_psqt(&accTilePsqt[k], psqt[k]); + } + #else std::memcpy(accumulator.accumulation[perspective], biases, HalfDimensions * sizeof(BiasType)); + for (std::size_t k = 0; k < PSQTBuckets; ++k) + accumulator.psqtAccumulation[perspective][k] = 0; + for (const auto index : active) { const IndexType offset = HalfDimensions * index; for (IndexType j = 0; j < HalfDimensions; ++j) accumulator.accumulation[perspective][j] += weights[offset + j]; + + for (std::size_t k = 0; k < PSQTBuckets; ++k) + accumulator.psqtAccumulation[perspective][k] += psqtWeights[index * PSQTBuckets + k]; } #endif } @@ -413,9 +540,11 @@ namespace Stockfish::Eval::NNUE { using BiasType = std::int16_t; using WeightType = std::int16_t; + using PSQTWeightType = std::int32_t; alignas(CacheLineSize) BiasType biases[HalfDimensions]; alignas(CacheLineSize) WeightType weights[HalfDimensions * InputDimensions]; + alignas(CacheLineSize) PSQTWeightType psqtWeights[InputDimensions * PSQTBuckets]; }; } // namespace Stockfish::Eval::NNUE diff --git a/src/search.cpp b/src/search.cpp index 29b334ed5fb..ac026a79e1c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -66,7 +66,7 @@ namespace { // Futility margin Value futility_margin(Depth d, bool improving) { - return Value(234 * (d - improving)); + return Value(231 * (d - improving)); } // Reductions lookup table, initialized at startup @@ -801,7 +801,7 @@ namespace { && (ss-1)->statScore < 24185 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 24 * depth - 34 * improving + 162 * ss->ttPv + 159 + && ss->staticEval >= beta - 22 * depth - 34 * improving + 162 * ss->ttPv + 159 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -1172,7 +1172,7 @@ namespace { + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 4741; + - 4791; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) if (!ss->inCheck) From d37de3cb1de63da5b2f8e6978c45c5b36973063b Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Tue, 18 May 2021 10:02:20 +0300 Subject: [PATCH 0559/1766] Do more continuation history based pruning This patch increases lmrDepth threshold for continuation history based pruning in search. This part of code for a long time was known to be really TC sensitive - decreasing this threshold easily passed lower time controls but failed badly at LTC, on the other hand it increase was part of a tuning that resulted in being negative at STC but was +12 elo at 180+1.8. After recent simplification of special conditions that sometimes increase it from 4 to 5 it was logical to overall test at longer time controls if 5 is better than 4 with deeper searches. reduces strenght on STC https://tests.stockfishchess.org/tests/view/60a3a8bbce8ea25a3ef03c74 ELO: -2.57 +-2.0 (95%) LOS: 0.6% Total: 20000 W: 1820 L: 1968 D: 16212 Ptnml(0-2): 68, 1582, 6836, 1458, 56 Passed LTC with STC bounds https://tests.stockfishchess.org/tests/view/60a027395085663412d090ce LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 175256 W: 6774 L: 6548 D: 161934 Ptnml(0-2): 91, 5808, 75604, 6034, 91 Passed VLTC with LTC bounds https://tests.stockfishchess.org/tests/view/60a2bccce229097940a037a7 LLR: 2.96 (-2.94,2.94) <0.50,3.50> Total: 65736 W: 1224 L: 1092 D: 63420 Ptnml(0-2): 5, 1012, 30706, 1136, 9 closes https://github.com/official-stockfish/Stockfish/pull/3473 bench 3689330 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index ac026a79e1c..8f8d42c0287 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1025,7 +1025,7 @@ namespace { else { // Continuation history based pruning (~20 Elo) - if ( lmrDepth < 4 + if ( lmrDepth < 5 && (*contHist[0])[movedPiece][to_sq(move)] < CounterMovePruneThreshold && (*contHist[1])[movedPiece][to_sq(move)] < CounterMovePruneThreshold) continue; From 0faf81d1f6b8ebe1a64482145a634d2e7d15db94 Mon Sep 17 00:00:00 2001 From: Yohaan Seth Nathan <73843275+TheYoBots@users.noreply.github.com> Date: Wed, 19 May 2021 00:22:59 +0530 Subject: [PATCH 0560/1766] Use Markdown syntax in the readme provide direct links to the mentioned files. closes https://github.com/official-stockfish/Stockfish/pull/3477 No Functional Change --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8d5ce8d0eda..0f6caec9e3f 100644 --- a/README.md +++ b/README.md @@ -21,13 +21,13 @@ intrinsics available on most CPUs (sse2, avx2, neon, or similar). This distribution of Stockfish consists of the following files: - * Readme.md, the file you are currently reading. + * [Readme.md](https://github.com/official-stockfish/Stockfish/blob/master/README.md), the file you are currently reading. - * Copying.txt, a text file containing the GNU General Public License version 3. + * [Copying.txt](https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt), a text file containing the GNU General Public License version 3. - * AUTHORS, a text file with the list of authors for the project + * [AUTHORS](https://github.com/official-stockfish/Stockfish/blob/master/AUTHORS), a text file with the list of authors for the project - * src, a subdirectory containing the full source code, including a Makefile + * [src](https://github.com/official-stockfish/Stockfish/tree/master/src), a subdirectory containing the full source code, including a Makefile that can be used to compile Stockfish on Unix-like systems. * a file with the .nnue extension, storing the neural network for the NNUE @@ -330,4 +330,4 @@ you are distributing. If you make any changes to the source code, these changes must also be made available under the GPL. For full details, read the copy of the GPL v3 found in the file named -*Copying.txt*. +[*Copying.txt*](https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt). From 038487f95499665bf86ca5343d7a83f970d4b06e Mon Sep 17 00:00:00 2001 From: Fanael Linithien Date: Tue, 18 May 2021 19:17:59 +0200 Subject: [PATCH 0561/1766] Use packed 32-bit MMX operations for updating the PSQT accumulator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This improves the speed of NNUE by a bit on old hardware that code path is intended for, like a Pentium III 1.13 GHz: 10 repeats of "./stockfish bench 16 1 13 default depth NNUE": Before: 54 642 504 897 cycles (± 0.12%) 62 301 937 829 instructions (± 0.03%) After: 54 320 821 928 cycles (± 0.13%) 62 084 742 699 instructions (± 0.02%) Speed of go depth 20 from startpos: Before: 53103 nps After: 53856 nps closes https://github.com/official-stockfish/Stockfish/pull/3476 No functional change. --- src/nnue/nnue_feature_transformer.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 2c0a0c6d313..bfa2e25a3e0 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -84,18 +84,18 @@ namespace Stockfish::Eval::NNUE { #elif USE_MMX typedef __m64 vec_t; - typedef std::int32_t psqt_vec_t; + typedef __m64 psqt_vec_t; #define vec_load(a) (*(a)) #define vec_store(a,b) *(a)=(b) #define vec_add_16(a,b) _mm_add_pi16(a,b) #define vec_sub_16(a,b) _mm_sub_pi16(a,b) #define vec_load_psqt(a) (*(a)) #define vec_store_psqt(a,b) *(a)=(b) - #define vec_add_psqt_32(a,b) a+b - #define vec_sub_psqt_32(a,b) a-b - #define vec_zero_psqt() 0 + #define vec_add_psqt_32(a,b) _mm_add_pi32(a,b) + #define vec_sub_psqt_32(a,b) _mm_sub_pi32(a,b) + #define vec_zero_psqt() _mm_setzero_si64() static constexpr IndexType NumRegs = 8; - static constexpr IndexType NumPsqtRegs = 8; + static constexpr IndexType NumPsqtRegs = 4; #elif USE_NEON typedef int16x8_t vec_t; From 6b9a70ace8073f5ff4c50b4dd5ddc041cf9c819f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Prokop=20Rand=C3=A1=C4=8Dek?= Date: Wed, 12 May 2021 20:15:21 +0200 Subject: [PATCH 0562/1766] Use if instead of goto This PR inverts the if and removes goto in the generate_all function. closes https://github.com/official-stockfish/Stockfish/pull/3461 No functional change --- AUTHORS | 1 + src/movegen.cpp | 29 ++++++++++++++--------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/AUTHORS b/AUTHORS index 9042495fdc0..7165363f08e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -175,6 +175,7 @@ Stefan Geschwentner (locutus2) Stefano Cardanobile (Stefano80) Steinar Gunderson (sesse) Stéphane Nicolet (snicolet) +Prokop Randáček (ProkopRandacek) Thanar2 thaspel theo77186 diff --git a/src/movegen.cpp b/src/movegen.cpp index be16845095d..bb81aeac078 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -192,21 +192,20 @@ namespace { const Square ksq = pos.square(Us); Bitboard target; - if (Type == EVASIONS && more_than_one(pos.checkers())) - goto kingMoves; // Double check, only a king move can save the day - - target = Type == EVASIONS ? between_bb(ksq, lsb(pos.checkers())) - : Type == NON_EVASIONS ? ~pos.pieces( Us) - : Type == CAPTURES ? pos.pieces(~Us) - : ~pos.pieces( ); // QUIETS || QUIET_CHECKS - - moveList = generate_pawn_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, target); - -kingMoves: + // Skip generating non-king moves when in double check + if (Type != EVASIONS || !more_than_one(pos.checkers())) + { + target = Type == EVASIONS ? between_bb(ksq, lsb(pos.checkers())) + : Type == NON_EVASIONS ? ~pos.pieces( Us) + : Type == CAPTURES ? pos.pieces(~Us) + : ~pos.pieces( ); // QUIETS || QUIET_CHECKS + + moveList = generate_pawn_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); + } if (!Checks || pos.blockers_for_king(~Us) & ksq) { Bitboard b = attacks_bb(ksq) & (Type == EVASIONS ? ~pos.pieces(Us) : target); From 2c3f7619f9ef267cbaec5216b71e0e435dc1393b Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Wed, 19 May 2021 20:57:04 +0300 Subject: [PATCH 0563/1766] Simplify usage of LMR for captures This patch simplifies a lot of "enablers" for LMR when move is a capture or promotion. After it we will have only 2 conditions - if node is a cutNode or if it's an allNode that was not in PV, so all captures or promotions wouldn't go thru LMR at any PVnodes. passed STC https://tests.stockfishchess.org/tests/view/60a40117ce8ea25a3ef03ca7 LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 58976 W: 4875 L: 4807 D: 49294 Ptnml(0-2): 176, 3897, 21270, 3973, 172 passed LTC https://tests.stockfishchess.org/tests/view/60a43ff8ce8ea25a3ef03d18 LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 65272 W: 2203 L: 2165 D: 60904 Ptnml(0-2): 28, 1936, 28668, 1978, 26 closes https://github.com/official-stockfish/Stockfish/pull/3480 bench 4110764 --- src/search.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 8f8d42c0287..6e1d2b5321a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1121,11 +1121,8 @@ namespace { if ( depth >= 3 && moveCount > 1 + 2 * rootNode && ( !captureOrPromotion - || moveCountPruning - || ss->staticEval + PieceValue[EG][pos.captured_piece()] <= alpha || cutNode - || (!PvNode && !formerPv && captureHistory[movedPiece][to_sq(move)][type_of(pos.captured_piece())] < 3678) - || thisThread->ttHitAverage < 432 * TtHitAverageResolution * TtHitAverageWindow / 1024) + || (!PvNode && !formerPv)) && (!PvNode || ss->ply > 1 || thisThread->id() % 4 != 3)) { Depth r = reduction(improving, depth, moveCount); From 754fc8a8b5ca7466926d54465eeb1df4d4a481ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Wed, 19 May 2021 01:24:51 +0200 Subject: [PATCH 0564/1766] Remove Tempo The Tempo variable was introduced 10 years ago in our search because the classical evaluation function was antisymmetrical in White and Black by design to gain speed: Eval(White to play) = -Eval(Black to play) Nowadays our neural networks know which side is to play in a position when they evaluate a position and are trained on real games, so the neural network encodes the advantage of moving as an output of search. This patch shows that the Tempo variable is not necessary anymore. STC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 33512 W: 2805 L: 2709 D: 27998 Ptnml(0-2): 80, 2209, 12095, 2279, 93 https://tests.stockfishchess.org/tests/view/60a44ceace8ea25a3ef03d30 LTC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 53920 W: 1807 L: 1760 D: 50353 Ptnml(0-2): 16, 1617, 23650, 1658, 19 https://tests.stockfishchess.org/tests/view/60a477f0ce8ea25a3ef03d49 We also tried a match (20000 games) at STC using purely classical, result was neutral: https://tests.stockfishchess.org/tests/view/60a4eebcce8ea25a3ef03db5 Note: there are two locations left in search.cpp where we assume antisymmetry of evaluation (in relation with a speed optimization for null moves in lines 770 and 1439), but as the values are just used for heuristic pruning this approximation should not hurt too much because the order of magnitude is still true most of the time. closes https://github.com/official-stockfish/Stockfish/pull/3481 Bench: 4015864 --- src/evaluate.cpp | 4 ++-- src/search.cpp | 6 +++--- src/timeman.cpp | 8 -------- src/timeman.h | 1 - src/types.h | 1 - 5 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 256bd9944f3..543644ee96b 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1058,7 +1058,7 @@ namespace { v = (v / 16) * 16; // Side to move point of view - v = (pos.side_to_move() == WHITE ? v : -v) + Tempo; + v = (pos.side_to_move() == WHITE ? v : -v); return v; } @@ -1119,7 +1119,7 @@ Value Eval::evaluate(const Position& pos) { int scale = 903 + 28 * pos.count() + 28 * pos.non_pawn_material() / 1024; - Value nnue = NNUE::evaluate(pos) * scale / 1024 + Time.tempoNNUE; + Value nnue = NNUE::evaluate(pos) * scale / 1024; if (pos.is_chess960()) nnue += fix_FRC(pos); diff --git a/src/search.cpp b/src/search.cpp index 6e1d2b5321a..359a774fd70 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -767,7 +767,7 @@ namespace { if ((ss-1)->currentMove != MOVE_NULL) ss->staticEval = eval = evaluate(pos); else - ss->staticEval = eval = -(ss-1)->staticEval + 2 * Tempo; + ss->staticEval = eval = -(ss-1)->staticEval; // Save static evaluation into transposition table tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); @@ -776,7 +776,7 @@ namespace { // Use static evaluation difference to improve quiet move ordering if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture) { - int bonus = std::clamp(-depth * 4 * int((ss-1)->staticEval + ss->staticEval - 2 * Tempo), -1000, 1000); + int bonus = std::clamp(-depth * 4 * int((ss-1)->staticEval + ss->staticEval), -1000, 1000); thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus; } @@ -1436,7 +1436,7 @@ namespace { // and addition of two tempos ss->staticEval = bestValue = (ss-1)->currentMove != MOVE_NULL ? evaluate(pos) - : -(ss-1)->staticEval + 2 * Tempo; + : -(ss-1)->staticEval; // Stand pat. Return immediately if static value is at least beta if (bestValue >= beta) diff --git a/src/timeman.cpp b/src/timeman.cpp index 3236b6e9bbb..f742d1e4422 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -94,14 +94,6 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { optimumTime = TimePoint(optScale * timeLeft); maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, maxScale * optimumTime)); - if (Stockfish::Search::Limits.use_time_management()) - { - int strength = std::log( std::max(1, int(optimumTime * Threads.size() / 10))) * 60; - tempoNNUE = std::clamp( (strength + 264) / 24, 18, 30); - } - else - tempoNNUE = 28; // default for no time given - if (Options["Ponder"]) optimumTime += optimumTime / 4; } diff --git a/src/timeman.h b/src/timeman.h index 4ac0b4be8a4..b1878d65f25 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -37,7 +37,6 @@ class TimeManagement { TimePoint(Threads.nodes_searched()) : now() - startTime; } int64_t availableNodes; // When in 'nodes as time' mode - int tempoNNUE; private: TimePoint startTime; diff --git a/src/types.h b/src/types.h index efebce1a7b6..0bd4a1c4090 100644 --- a/src/types.h +++ b/src/types.h @@ -191,7 +191,6 @@ enum Value : int { BishopValueMg = 825, BishopValueEg = 915, RookValueMg = 1276, RookValueEg = 1380, QueenValueMg = 2538, QueenValueEg = 2682, - Tempo = 28, MidgameLimit = 15258, EndgameLimit = 3915 }; From f233ca1af4d36ded8ce924131f42bc4d0093ec6e Mon Sep 17 00:00:00 2001 From: Guy Vreuls Date: Fri, 21 May 2021 20:22:29 +0200 Subject: [PATCH 0565/1766] Compact position structures Reorder the structures data members in position.h to reduce padding. Passed STC: https://tests.stockfishchess.org/tests/view/60a8011fce8ea25a3ef04069 LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 14120 W: 1214 L: 1067 D: 11839 Ptnml(0-2): 26, 857, 5161, 976, 40 --- Also tested for speed locally by Joost: Result of 50 runs ================== base (./stockfish.master ) = 2254919 +/- 4439 test (./stockfish.patch ) = 2274003 +/- 5278 diff = +19084 +/- 6386 ================== speedup = +0.0085 P(speedup > 0) = 1.0000 --- closes https://github.com/official-stockfish/Stockfish/pull/3488 No functional change. --- src/position.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/position.h b/src/position.h index c226373b1f7..e6b072bc82d 100644 --- a/src/position.h +++ b/src/position.h @@ -51,11 +51,11 @@ struct StateInfo { // Not copied when making a move (will be recomputed anyhow) Key key; Bitboard checkersBB; - Piece capturedPiece; StateInfo* previous; Bitboard blockersForKing[COLOR_NB]; Bitboard pinners[COLOR_NB]; Bitboard checkSquares[PIECE_TYPE_NB]; + Piece capturedPiece; int repetition; // Used by NNUE @@ -192,11 +192,11 @@ class Position { int castlingRightsMask[SQUARE_NB]; Square castlingRookSquare[CASTLING_RIGHT_NB]; Bitboard castlingPath[CASTLING_RIGHT_NB]; + Thread* thisThread; + StateInfo* st; int gamePly; Color sideToMove; Score psq; - Thread* thisThread; - StateInfo* st; bool chess960; }; From fb2d175f97a12be6464fdcc20293682a192ca156 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 22 May 2021 07:00:53 +0200 Subject: [PATCH 0566/1766] Update default net to nn-7756374aaed3.nnue trained with pytorch using the master branch and recommended settings, same data set as previously used: python train.py ../../all_d9_fishd9_d8_d10_shuffle.binpack ../../all_d9_fishd9_d8_d10_shuffle.binpack \ --gpus 1 --threads 2 --num-workers 2 --batch-size 16384 --progress_bar_refresh_rate 300 \ --smart-fen-skipping --random-fen-skipping 3 --features=HalfKAv2^ --lambda=1.0 \ --max_epochs=400 --seed $RANDOM --default_root_dir exp/run_8 passed STC: LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 21424 W: 2078 L: 1907 D: 17439 Ptnml(0-2): 80, 1512, 7385, 1627, 108 https://tests.stockfishchess.org/tests/view/60a6c749ce8ea25a3ef03f4d passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 67912 W: 2851 L: 2648 D: 62413 Ptnml(0-2): 40, 2348, 28984, 2537, 47 https://tests.stockfishchess.org/tests/view/60a722ecce8ea25a3ef03fb9 closes https://github.com/official-stockfish/Stockfish/pull/3489 Bench: 3779522 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index ee4c175b34f..40622e933ea 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-8a08400ed089.nnue" + #define EvalFileDefaultName "nn-7756374aaed3.nnue" namespace NNUE { From 49c79aa15ce5a0de54fe0f4cef1037751af3d7d1 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Sat, 22 May 2021 02:41:52 -0300 Subject: [PATCH 0567/1766] Simplify reduction for consecutive fails Revert the heuristic introduced in #3184, by which we reduced more the late sons of the root position after consecutive fail highs. --- Before new net architecture: STC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 226336 W: 20373 L: 20500 D: 185463 Ptnml(0-2): 755, 16087, 79595, 15992, 739 https://tests.stockfishchess.org/tests/view/609dec205085663412d08e9d LTC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 67432 W: 2411 L: 2375 D: 62646 Ptnml(0-2): 33, 1944, 29714, 2004, 21 https://tests.stockfishchess.org/tests/view/609ee30f5085663412d08fc3 --- After new net architecture: STC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 141752 W: 11591 L: 11617 D: 118544 Ptnml(0-2): 387, 9231, 51674, 9189, 395 https://tests.stockfishchess.org/tests/view/60a4320ace8ea25a3ef03cfd LTC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 294072 W: 9825 L: 9950 D: 274297 Ptnml(0-2): 121, 8610, 129681, 8521, 103 https://tests.stockfishchess.org/tests/view/60a51b5ece8ea25a3ef03dcd --- closes https://github.com/official-stockfish/Stockfish/pull/3490 Bench: 3752892 --- src/search.cpp | 6 +----- src/thread.h | 1 - 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 359a774fd70..f8f956fa937 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -377,7 +377,7 @@ void Thread::search() { // Start with a small aspiration window and, in the case of a fail // high/low, re-search with a bigger window until we don't fail // high/low anymore. - failedHighCnt = 0; + int failedHighCnt = 0; while (true) { Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt - searchAgainCounter); @@ -1157,10 +1157,6 @@ namespace { if (ttCapture) r++; - // Increase reduction at root if failing high - if (rootNode) - r += thisThread->failedHighCnt * thisThread->failedHighCnt * moveCount / 512; - // Increase reduction for cut nodes (~3 Elo) if (cutNode) r += 2; diff --git a/src/thread.h b/src/thread.h index 5785fd25eb7..ae662880cef 100644 --- a/src/thread.h +++ b/src/thread.h @@ -75,7 +75,6 @@ class Thread { CapturePieceToHistory captureHistory; ContinuationHistory continuationHistory[2][2]; Score contempt; - int failedHighCnt; }; From ff4c22238a199625cf7f02be1816b07fc49f5d45 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Sat, 22 May 2021 02:47:23 -0300 Subject: [PATCH 0568/1766] Tuning Search This patch tunes constant in search.cpp STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 30648 W: 2580 L: 2410 D: 25658 Ptnml(0-2): 80, 1969, 11093, 2065, 117 https://tests.stockfishchess.org/tests/view/60a71d3cce8ea25a3ef03fae LTC: LLR: 2.95 (-2.94,2.94) <0.50,3.50> Total: 52896 W: 1776 L: 1617 D: 49503 Ptnml(0-2): 13, 1462, 23347, 1605, 21 https://tests.stockfishchess.org/tests/view/60a794ddce8ea25a3ef0400a closes https://github.com/official-stockfish/Stockfish/pull/3491 Bench: 4004731 --- src/search.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index f8f956fa937..143d0883a4d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -66,7 +66,7 @@ namespace { // Futility margin Value futility_margin(Depth d, bool improving) { - return Value(231 * (d - improving)); + return Value(214 * (d - improving)); } // Reductions lookup table, initialized at startup @@ -74,7 +74,7 @@ namespace { Depth reduction(bool i, Depth d, int mn) { int r = Reductions[d] * Reductions[mn]; - return (r + 503) / 1024 + (!i && r > 915); + return (r + 534) / 1024 + (!i && r > 904); } constexpr int futility_move_count(bool improving, Depth depth) { @@ -83,7 +83,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return d > 14 ? 66 : 6 * d * d + 231 * d - 206; + return d > 14 ? 73 : 6 * d * d + 229 * d - 215; } // Add a small random component to draw evaluations to avoid 3-fold blindness @@ -798,10 +798,10 @@ namespace { // Step 8. Null move search with verification search (~40 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 24185 + && (ss-1)->statScore < 23767 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 22 * depth - 34 * improving + 162 * ss->ttPv + 159 + && ss->staticEval >= beta - 20 * depth - 22 * improving + 168 * ss->ttPv + 159 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -809,7 +809,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and value - Depth R = (1062 + 68 * depth) / 256 + std::min(int(eval - beta) / 190, 3); + Depth R = (1090 + 81 * depth) / 256 + std::min(int(eval - beta) / 205, 3); ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -922,7 +922,7 @@ namespace { ttCapture = ttMove && pos.capture_or_promotion(ttMove); // Step 11. A small Probcut idea, when we are in check - probCutBeta = beta + 400; + probCutBeta = beta + 409; if ( ss->inCheck && !PvNode && depth >= 4 @@ -1073,7 +1073,7 @@ namespace { { extension = 1; singularQuietLMR = !ttCapture; - if (!PvNode && value < singularBeta - 140) + if (!PvNode && value < singularBeta - 93) extension = 2; } @@ -1165,11 +1165,11 @@ namespace { + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 4791; + - 4923; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) if (!ss->inCheck) - r -= ss->statScore / 14790; + r -= ss->statScore / 14721; } // In general we want to cap the LMR depth search at newDepth. But if From a2f01c07eb91524fc372bd82d6513ab058d3e043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sat, 22 May 2021 19:44:15 +0200 Subject: [PATCH 0569/1766] Sometimes change the (materialist, positional) balance Our new nets output two values for the side to move in the last layer. We can interpret the first value as a material evaluation of the position, and the second one as the dynamic, positional value of the location of pieces. This patch changes the balance for the (materialist, positional) parts of the score from (128, 128) to (121, 135) when the piece material is equal between the two players, but keeps the standard (128, 128) balance when one player is at least an exchange up. Passed STC: LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 15936 W: 1421 L: 1266 D: 13249 Ptnml(0-2): 37, 1037, 5694, 1134, 66 https://tests.stockfishchess.org/tests/view/60a82df9ce8ea25a3ef0408f Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 13904 W: 516 L: 410 D: 12978 Ptnml(0-2): 4, 374, 6088, 484, 2 https://tests.stockfishchess.org/tests/view/60a8bbf9ce8ea25a3ef04101 closes https://github.com/official-stockfish/Stockfish/pull/3492 Bench: 3856635 --- src/evaluate.cpp | 2 +- src/evaluate.h | 2 +- src/nnue/evaluate_nnue.cpp | 23 ++++++++++++++++++----- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 543644ee96b..c8094ca828d 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1119,7 +1119,7 @@ Value Eval::evaluate(const Position& pos) { int scale = 903 + 28 * pos.count() + 28 * pos.non_pawn_material() / 1024; - Value nnue = NNUE::evaluate(pos) * scale / 1024; + Value nnue = NNUE::evaluate(pos, true) * scale / 1024; if (pos.is_chess960()) nnue += fix_FRC(pos); diff --git a/src/evaluate.h b/src/evaluate.h index 40622e933ea..41aace67fcb 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -43,7 +43,7 @@ namespace Eval { namespace NNUE { - Value evaluate(const Position& pos); + Value evaluate(const Position& pos, bool adjusted = false); bool load_eval(std::string name, std::istream& stream); bool save_eval(std::ostream& stream); void init(); diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 97cef81480f..cee77fe97e2 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -134,7 +134,7 @@ namespace Stockfish::Eval::NNUE { } // Evaluation function. Perform differential calculation. - Value evaluate(const Position& pos) { + Value evaluate(const Position& pos, bool adjusted) { // We manually align the arrays on the stack because with gcc < 9.3 // overaligning stack variables with alignas() doesn't work correctly. @@ -158,13 +158,26 @@ namespace Stockfish::Eval::NNUE { ASSERT_ALIGNED(buffer, alignment); const std::size_t bucket = (pos.count() - 1) / 4; - const auto [psqt, lazy] = featureTransformer->transform(pos, transformedFeatures, bucket); - if (lazy) { + + if (lazy) return static_cast(psqt / OutputScale); - } else { + else + { const auto output = network[bucket]->propagate(transformedFeatures, buffer); - return static_cast((output[0] + psqt) / OutputScale); + + int materialist = psqt; + int positional = output[0]; + + int delta_npm = abs(pos.non_pawn_material(WHITE) - pos.non_pawn_material(BLACK)); + int entertainment = (adjusted && delta_npm <= BishopValueMg - KnightValueMg ? 7 : 0); + + int A = 128 - entertainment; + int B = 128 + entertainment; + + int sum = (A * materialist + B * positional) / 128; + + return static_cast( sum / OutputScale ); } } From e044068b435c0088171a2d02ad24a049354e6a5e Mon Sep 17 00:00:00 2001 From: bmc4 Date: Mon, 24 May 2021 08:15:04 -0300 Subject: [PATCH 0570/1766] Increased reduction for captures in LMR It now does, in LMR, an increased on reduction by 1 for captures in cut nodes. STC: LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 30656 W: 2565 L: 2397 D: 25694 Ptnml(0-2): 63, 2012, 11029, 2142, 82 https://tests.stockfishchess.org/tests/view/60a96733ce8ea25a3ef04178 LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 124840 W: 4139 L: 3878 D: 116823 Ptnml(0-2): 48, 3480, 55100, 3747, 45 https://tests.stockfishchess.org/tests/view/60a995f5ce8ea25a3ef041b7 closes https://github.com/official-stockfish/Stockfish/pull/3494 bench: 3864295 --- src/search.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 143d0883a4d..6cb42cc3a38 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1151,16 +1151,16 @@ namespace { if (singularQuietLMR) r--; + // Increase reduction for cut nodes (~3 Elo) + if (cutNode) + r += 1 + !captureOrPromotion; + if (!captureOrPromotion) { // Increase reduction if ttMove is a capture (~3 Elo) if (ttCapture) r++; - // Increase reduction for cut nodes (~3 Elo) - if (cutNode) - r += 2; - ss->statScore = thisThread->mainHistory[us][from_to(move)] + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] From 9d53129075177cb11b63b43236556051ba60f7dd Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Tue, 25 May 2021 13:09:40 +0200 Subject: [PATCH 0571/1766] Expose the lazy threshold for the feature transformer PSQT as a parameter. Definition of the lazy threshold moved to evaluate.cpp where all others are. Lazy threshold only used for real searches, not used for the "eval" call. This preserves the purity of NNUE evaluation, which is useful to verify consistency between the engine and the NNUE trainer. closes https://github.com/official-stockfish/Stockfish/pull/3499 No functional change --- src/evaluate.cpp | 13 +++++++------ src/evaluate.h | 2 +- src/nnue/evaluate_nnue.cpp | 4 ++-- src/nnue/nnue_feature_transformer.h | 6 ++---- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index c8094ca828d..04d41d5f6b0 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -214,11 +214,12 @@ using namespace Trace; namespace { // Threshold for lazy and space evaluation - constexpr Value LazyThreshold1 = Value(1565); - constexpr Value LazyThreshold2 = Value(1102); - constexpr Value SpaceThreshold = Value(11551); - constexpr Value NNUEThreshold1 = Value(682); - constexpr Value NNUEThreshold2 = Value(176); + constexpr Value LazyThreshold1 = Value(1565); + constexpr Value LazyThreshold2 = Value(1102); + constexpr Value LazyThresholdNNUE = Value(1400); + constexpr Value SpaceThreshold = Value(11551); + constexpr Value NNUEThreshold1 = Value(682); + constexpr Value NNUEThreshold2 = Value(176); // KingAttackWeights[PieceType] contains king attack weights by piece type constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 }; @@ -1119,7 +1120,7 @@ Value Eval::evaluate(const Position& pos) { int scale = 903 + 28 * pos.count() + 28 * pos.non_pawn_material() / 1024; - Value nnue = NNUE::evaluate(pos, true) * scale / 1024; + Value nnue = NNUE::evaluate(pos, true, LazyThresholdNNUE) * scale / 1024; if (pos.is_chess960()) nnue += fix_FRC(pos); diff --git a/src/evaluate.h b/src/evaluate.h index 41aace67fcb..6bc1f0b387b 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -43,7 +43,7 @@ namespace Eval { namespace NNUE { - Value evaluate(const Position& pos, bool adjusted = false); + Value evaluate(const Position& pos, bool adjusted = false, Value lazyThreshold = VALUE_INFINITE); bool load_eval(std::string name, std::istream& stream); bool save_eval(std::ostream& stream); void init(); diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index cee77fe97e2..99711cd5646 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -134,7 +134,7 @@ namespace Stockfish::Eval::NNUE { } // Evaluation function. Perform differential calculation. - Value evaluate(const Position& pos, bool adjusted) { + Value evaluate(const Position& pos, bool adjusted, Value lazyThreshold) { // We manually align the arrays on the stack because with gcc < 9.3 // overaligning stack variables with alignas() doesn't work correctly. @@ -158,7 +158,7 @@ namespace Stockfish::Eval::NNUE { ASSERT_ALIGNED(buffer, alignment); const std::size_t bucket = (pos.count() - 1) / 4; - const auto [psqt, lazy] = featureTransformer->transform(pos, transformedFeatures, bucket); + const auto [psqt, lazy] = featureTransformer->transform(pos, transformedFeatures, bucket, lazyThreshold); if (lazy) return static_cast(psqt / OutputScale); diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index bfa2e25a3e0..e81f54fa3e0 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -124,8 +124,6 @@ namespace Stockfish::Eval::NNUE { // Number of output dimensions for one side static constexpr IndexType HalfDimensions = TransformedFeatureDimensions; - static constexpr int LazyThreshold = 1400; - #ifdef VECTOR static constexpr IndexType TileHeight = NumRegs * sizeof(vec_t) / 2; static constexpr IndexType PsqtTileHeight = NumPsqtRegs * sizeof(psqt_vec_t) / 4; @@ -171,7 +169,7 @@ namespace Stockfish::Eval::NNUE { } // Convert input features - std::pair transform(const Position& pos, OutputType* output, int bucket) const { + std::pair transform(const Position& pos, OutputType* output, int bucket, Value lazyThreshold) const { update_accumulator(pos, WHITE); update_accumulator(pos, BLACK); @@ -184,7 +182,7 @@ namespace Stockfish::Eval::NNUE { - psqtAccumulation[static_cast(perspectives[1])][bucket] ) / 2; - if (abs(psqt) > LazyThreshold * OutputScale) + if (abs(psqt) > (int)lazyThreshold * OutputScale) return { psqt, true }; #if defined(USE_AVX512) From 83e0af288a8da69973721a8ae727d6c4a642e3f8 Mon Sep 17 00:00:00 2001 From: IIvec Date: Thu, 20 May 2021 20:02:32 +0200 Subject: [PATCH 0572/1766] Simplify the thread term for reduction formula Dependance on Threads.size() was removed Search::init() for the Reductions[] initialization. STC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 17376 W: 1024 L: 929 D: 15423 Ptnml(0-2): 24, 781, 6989, 864, 30 https://tests.stockfishchess.org/tests/view/60ac110812066fd2997957dc LTC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 145552 W: 3656 L: 3673 D: 138223 Ptnml(0-2): 37, 3351, 66014, 3340, 34 https://tests.stockfishchess.org/tests/view/60ac267412066fd299795825 closes https://github.com/official-stockfish/Stockfish/pull/3502 Bench 3864295 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 6cb42cc3a38..c69b240836e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -152,7 +152,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int((21.3 + 2 * std::log(Threads.size())) * std::log(i + 0.25 * std::log(i))); + Reductions[i] = int(21.3 * std::log(i + 0.25 * std::log(i))); } From 1b325bf86d02e02af8f693e7e1e70c8be5c7967b Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Wed, 26 May 2021 16:17:37 +0200 Subject: [PATCH 0573/1766] Less reduction for capture/promotions. Exclude captures/promotions at expected cut nodes (which also not a former PV node) from LMR if a response to the first previous opponent move. STC: LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 288656 W: 24886 L: 24413 D: 239357 Ptnml(0-2): 900, 19738, 102578, 20213, 899 https://tests.stockfishchess.org/tests/view/60ad505112066fd29979595b LTC: LLR: 2.97 (-2.94,2.94) <0.50,3.50> Total: 31344 W: 1107 L: 975 D: 29262 Ptnml(0-2): 12, 879, 13757, 1013, 11 https://tests.stockfishchess.org/tests/view/60adffce12066fd2997959d2 closes https://github.com/official-stockfish/Stockfish/pull/3500 Bench: 3827710 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index c69b240836e..9de8cc9c092 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1121,7 +1121,7 @@ namespace { if ( depth >= 3 && moveCount > 1 + 2 * rootNode && ( !captureOrPromotion - || cutNode + || (cutNode && (ss-1)->moveCount > 1) || (!PvNode && !formerPv)) && (!PvNode || ss->ply > 1 || thisThread->id() % 4 != 3)) { From f193778446acc6e60d7f0f99c6eb01489f89e962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Thu, 27 May 2021 01:10:00 +0200 Subject: [PATCH 0574/1766] Do not use lazy evaluation inside NNUE This simplification patch implements two changes: 1. it simplifies away the so-called "lazy" path in the NNUE evaluation internals, where we trusted the psqt head alone to avoid the costly "positional" head in some cases; 2. it raises a little bit the NNUEThreshold1 in evaluate.cpp (from 682 to 800), which increases the limit where we switched from NNUE eval to Classical eval. Both effects increase the number of positional evaluations done by our new net architecture, but the results of our tests below seem to indicate that the loss of speed will be compensated by the gain of eval quality. STC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 26280 W: 2244 L: 2137 D: 21899 Ptnml(0-2): 72, 1755, 9405, 1810, 98 https://tests.stockfishchess.org/tests/view/60ae73f112066fd299795a51 LTC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 20592 W: 750 L: 677 D: 19165 Ptnml(0-2): 9, 614, 8980, 681, 12 https://tests.stockfishchess.org/tests/view/60ae88e812066fd299795a82 closes https://github.com/official-stockfish/Stockfish/pull/3503 Bench: 3817907 --- src/evaluate.cpp | 10 ++++------ src/evaluate.h | 2 +- src/nnue/evaluate_nnue.cpp | 28 +++++++++++----------------- src/nnue/nnue_feature_transformer.h | 7 ++----- 4 files changed, 18 insertions(+), 29 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 04d41d5f6b0..40c41a8667a 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -216,9 +216,8 @@ namespace { // Threshold for lazy and space evaluation constexpr Value LazyThreshold1 = Value(1565); constexpr Value LazyThreshold2 = Value(1102); - constexpr Value LazyThresholdNNUE = Value(1400); constexpr Value SpaceThreshold = Value(11551); - constexpr Value NNUEThreshold1 = Value(682); + constexpr Value NNUEThreshold1 = Value(800); constexpr Value NNUEThreshold2 = Value(176); // KingAttackWeights[PieceType] contains king attack weights by piece type @@ -1120,7 +1119,7 @@ Value Eval::evaluate(const Position& pos) { int scale = 903 + 28 * pos.count() + 28 * pos.non_pawn_material() / 1024; - Value nnue = NNUE::evaluate(pos, true, LazyThresholdNNUE) * scale / 1024; + Value nnue = NNUE::evaluate(pos, true) * scale / 1024; if (pos.is_chess960()) nnue += fix_FRC(pos); @@ -1133,15 +1132,14 @@ Value Eval::evaluate(const Position& pos) { Value psq = Value(abs(eg_value(pos.psq_score()))); int r50 = 16 + pos.rule50_count(); bool largePsq = psq * 16 > (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50; - bool classical = largePsq; // Use classical evaluation for really low piece endgames. // One critical case is the draw for bishop + A/H file pawn vs naked king. bool lowPieceEndgame = pos.non_pawn_material() == BishopValueMg || (pos.non_pawn_material() < 2 * RookValueMg && pos.count() < 2); - v = classical || lowPieceEndgame ? Evaluation(pos).value() - : adjusted_NNUE(); + v = largePsq || lowPieceEndgame ? Evaluation(pos).value() // classical + : adjusted_NNUE(); // NNUE // If the classical eval is small and imbalance large, use NNUE nevertheless. // For the case of opposite colored bishops, switch to NNUE eval with small diff --git a/src/evaluate.h b/src/evaluate.h index 6bc1f0b387b..41aace67fcb 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -43,7 +43,7 @@ namespace Eval { namespace NNUE { - Value evaluate(const Position& pos, bool adjusted = false, Value lazyThreshold = VALUE_INFINITE); + Value evaluate(const Position& pos, bool adjusted = false); bool load_eval(std::string name, std::istream& stream); bool save_eval(std::ostream& stream); void init(); diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 99711cd5646..4a3c206b808 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -134,7 +134,7 @@ namespace Stockfish::Eval::NNUE { } // Evaluation function. Perform differential calculation. - Value evaluate(const Position& pos, bool adjusted, Value lazyThreshold) { + Value evaluate(const Position& pos, bool adjusted) { // We manually align the arrays on the stack because with gcc < 9.3 // overaligning stack variables with alignas() doesn't work correctly. @@ -158,27 +158,21 @@ namespace Stockfish::Eval::NNUE { ASSERT_ALIGNED(buffer, alignment); const std::size_t bucket = (pos.count() - 1) / 4; - const auto [psqt, lazy] = featureTransformer->transform(pos, transformedFeatures, bucket, lazyThreshold); + const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket); + const auto output = network[bucket]->propagate(transformedFeatures, buffer); - if (lazy) - return static_cast(psqt / OutputScale); - else - { - const auto output = network[bucket]->propagate(transformedFeatures, buffer); + int materialist = psqt; + int positional = output[0]; - int materialist = psqt; - int positional = output[0]; + int delta_npm = abs(pos.non_pawn_material(WHITE) - pos.non_pawn_material(BLACK)); + int entertainment = (adjusted && delta_npm <= BishopValueMg - KnightValueMg ? 7 : 0); - int delta_npm = abs(pos.non_pawn_material(WHITE) - pos.non_pawn_material(BLACK)); - int entertainment = (adjusted && delta_npm <= BishopValueMg - KnightValueMg ? 7 : 0); + int A = 128 - entertainment; + int B = 128 + entertainment; - int A = 128 - entertainment; - int B = 128 + entertainment; + int sum = (A * materialist + B * positional) / 128; - int sum = (A * materialist + B * positional) / 128; - - return static_cast( sum / OutputScale ); - } + return static_cast( sum / OutputScale ); } // Load eval, from a file stream or a memory stream diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index e81f54fa3e0..741d97cf119 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -169,7 +169,7 @@ namespace Stockfish::Eval::NNUE { } // Convert input features - std::pair transform(const Position& pos, OutputType* output, int bucket, Value lazyThreshold) const { + std::int32_t transform(const Position& pos, OutputType* output, int bucket) const { update_accumulator(pos, WHITE); update_accumulator(pos, BLACK); @@ -182,9 +182,6 @@ namespace Stockfish::Eval::NNUE { - psqtAccumulation[static_cast(perspectives[1])][bucket] ) / 2; - if (abs(psqt) > (int)lazyThreshold * OutputScale) - return { psqt, true }; - #if defined(USE_AVX512) constexpr IndexType NumChunks = HalfDimensions / (SimdWidth * 2); static_assert(HalfDimensions % (SimdWidth * 2) == 0); @@ -291,7 +288,7 @@ namespace Stockfish::Eval::NNUE { _mm_empty(); #endif - return { psqt, false }; + return psqt; } private: From 6174a37a3742e318654cdbc5e8852fa37e797c92 Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Thu, 27 May 2021 16:03:56 -0400 Subject: [PATCH 0575/1766] Remove Stat Reset at beta cutoff STC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 63936 W: 5350 L: 5288 D: 53298 Ptnml(0-2): 184, 4295, 22954, 4345, 190 https://tests.stockfishchess.org/tests/view/60affb4c12066fd299795c64 LTC: LLR: 2.96 (-2.94,2.94) <-2.50,0.50> Total: 35856 W: 1201 L: 1142 D: 33513 Ptnml(0-2): 7, 1031, 15795, 1086, 9 https://tests.stockfishchess.org/tests/view/60b0537812066fd299795cc6 closes https://github.com/official-stockfish/Stockfish/pull/3505 bench: 3831936 --- src/search.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 9de8cc9c092..74a92bc6d0e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1273,7 +1273,6 @@ namespace { else { assert(value >= beta); // Fail high - ss->statScore = 0; break; } } From 4c0299832555c3e35eb1f8779bedf7d1d2819cf1 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 29 May 2021 15:44:57 +0200 Subject: [PATCH 0576/1766] Simplify NNUE / classical evaluation selection for the new network architecture these rules can be simplified, closer to the original PSQT difference based again. passed STC LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 22656 W: 1979 L: 1868 D: 18809 Ptnml(0-2): 70, 1496, 8087, 1603, 72 https://tests.stockfishchess.org/tests/view/60b24579db3c4776cb89d122 passed LTC LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 30224 W: 1015 L: 953 D: 28256 Ptnml(0-2): 4, 860, 13330, 906, 12 https://tests.stockfishchess.org/tests/view/60b27613db3c4776cb89d145 closes https://github.com/official-stockfish/Stockfish/pull/3511 Bench: 3937626 --- src/evaluate.cpp | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 40c41a8667a..0e66fea62b2 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -218,7 +218,6 @@ namespace { constexpr Value LazyThreshold2 = Value(1102); constexpr Value SpaceThreshold = Value(11551); constexpr Value NNUEThreshold1 = Value(800); - constexpr Value NNUEThreshold2 = Value(176); // KingAttackWeights[PieceType] contains king attack weights by piece type constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 }; @@ -1127,29 +1126,14 @@ Value Eval::evaluate(const Position& pos) { return nnue; }; - // If there is PSQ imbalance we use the classical eval. We also introduce - // a small probability of using the classical eval when PSQ imbalance is small. + // If there is PSQ imbalance we use the classical eval. Value psq = Value(abs(eg_value(pos.psq_score()))); int r50 = 16 + pos.rule50_count(); bool largePsq = psq * 16 > (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50; - // Use classical evaluation for really low piece endgames. - // One critical case is the draw for bishop + A/H file pawn vs naked king. - bool lowPieceEndgame = pos.non_pawn_material() == BishopValueMg - || (pos.non_pawn_material() < 2 * RookValueMg && pos.count() < 2); - - v = largePsq || lowPieceEndgame ? Evaluation(pos).value() // classical - : adjusted_NNUE(); // NNUE - - // If the classical eval is small and imbalance large, use NNUE nevertheless. - // For the case of opposite colored bishops, switch to NNUE eval with small - // probability if the classical eval is less than the threshold. - if ( largePsq - && !lowPieceEndgame - && ( abs(v) * 16 < NNUEThreshold2 * r50 - || ( pos.opposite_bishops() - && abs(v) * 16 < (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50))) - v = adjusted_NNUE(); + v = largePsq ? Evaluation(pos).value() // classical + : adjusted_NNUE(); // NNUE + } // Damp down the evaluation linearly when shuffling From 5448cad29ee261706f6dcfd4a1fea779780eda33 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Sat, 29 May 2021 11:40:40 +0200 Subject: [PATCH 0577/1766] Fix export of the feature transformer. PSQT export was missing. fixes #3507 closes https://github.com/official-stockfish/Stockfish/pull/3508 No functional change --- src/nnue/nnue_feature_transformer.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 741d97cf119..c249d3e7018 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -165,6 +165,8 @@ namespace Stockfish::Eval::NNUE { write_little_endian(stream, biases[i]); for (std::size_t i = 0; i < HalfDimensions * InputDimensions; ++i) write_little_endian(stream, weights[i]); + for (std::size_t i = 0; i < PSQTBuckets * InputDimensions; ++i) + write_little_endian(stream, psqtWeights[i]); return !stream.fail(); } From e8418bb1b92800550264b9a4993f51208f8fe681 Mon Sep 17 00:00:00 2001 From: candirufish <38038147+candirufish@users.noreply.github.com> Date: Mon, 31 May 2021 06:47:35 +0200 Subject: [PATCH 0578/1766] Check Extension with Static Evaluation extension for checking moves, at higher depth and more decisive positions. stc: LLR: 2.97 (-2.94,2.94) <-0.50,2.50> Total: 87008 W: 7337 L: 7100 D: 72571 Ptnml(0-2): 264, 5737, 31270, 5964, 269 https://tests.stockfishchess.org/tests/view/60b1034787a1a67ae56c47b6 ltc: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 79320 W: 2629 L: 2432 D: 74259 Ptnml(0-2): 29, 2205, 35000, 2392, 34 https://tests.stockfishchess.org/tests/view/60b1ae0b87a1a67ae56c487c closes https://github.com/official-stockfish/Stockfish/pull/3514 Bench: 4447112 --- src/search.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 74a92bc6d0e..2b4e4f1e843 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1097,6 +1097,10 @@ namespace { return beta; } } + else if ( givesCheck + && depth > 6 + && abs(ss->staticEval) > Value(100)) + extension = 1; // Add extension to new depth newDepth += extension; From 9fd5b44d60c53357bb76d4c51f20d3d5aa31ebe3 Mon Sep 17 00:00:00 2001 From: "J. Oster" Date: Mon, 31 May 2021 17:46:40 +0200 Subject: [PATCH 0579/1766] Pre-initialize ss->ply We pre-initialize ss->ply over the whole stack. There is no need to re-assign the same value(s) over and over again while searching. Probably a tiny speedup on longer searches. Tested for no regression: STC LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 25784 W: 2205 L: 2101 D: 21478 Ptnml(0-2): 62, 1660, 9368, 1716, 86 https://tests.stockfishchess.org/tests/view/60b516c6457376eb8bca9dfa LTC LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 26200 W: 944 L: 878 D: 24378 Ptnml(0-2): 12, 732, 11545, 800, 11 https://tests.stockfishchess.org/tests/view/60b53652457376eb8bca9e0e closes https://github.com/official-stockfish/Stockfish/pull/3516 No functional change. --- src/search.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 2b4e4f1e843..dac326e2a27 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -253,7 +253,7 @@ void Thread::search() { // To allow access to (ss-7) up to (ss+2), the stack must be oversized. // The former is needed to allow update_continuation_histories(ss-1, ...), // which accesses its argument at ss-6, also near the root. - // The latter is needed for statScores and killer initialization. + // The latter is needed for statScore and killer initialization. Stack stack[MAX_PLY+10], *ss = stack+7; Move pv[MAX_PLY+1]; Value bestValue, alpha, beta, delta; @@ -268,6 +268,9 @@ void Thread::search() { for (int i = 7; i > 0; i--) (ss-i)->continuationHistory = &this->continuationHistory[0][0][NO_PIECE][0]; // Use as a sentinel + for (int i = 0; i <= MAX_PLY + 2; ++i) + (ss+i)->ply = i; + ss->pv = pv; bestValue = delta = alpha = -VALUE_INFINITE; @@ -607,7 +610,6 @@ namespace { assert(0 <= ss->ply && ss->ply < MAX_PLY); - (ss+1)->ply = ss->ply + 1; (ss+1)->ttPv = false; (ss+1)->excludedMove = bestMove = MOVE_NONE; (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; @@ -1379,7 +1381,6 @@ namespace { } Thread* thisThread = pos.this_thread(); - (ss+1)->ply = ss->ply + 1; bestMove = MOVE_NONE; ss->inCheck = pos.checkers(); moveCount = 0; From 95f73ff393e5b87b2a3b4195d698b6620fd4a123 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Mon, 31 May 2021 20:26:05 +0200 Subject: [PATCH 0580/1766] Remove formerPV variable. STC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 75672 W: 6546 L: 6496 D: 62630 Ptnml(0-2): 238, 5274, 26761, 5326, 237 https://tests.stockfishchess.org/tests/view/60b349c0ec0c03148cbed055 LTC: LLR: 2.98 (-2.94,2.94) <-2.50,0.50> Total: 137816 W: 4676 L: 4689 D: 128451 Ptnml(0-2): 52, 4237, 60354, 4202, 63 https://tests.stockfishchess.org/tests/view/60b38970ec0c03148cbed075 closes https://github.com/official-stockfish/Stockfish/pull/3515 Bench: 4892288 --- src/search.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index dac326e2a27..2c7c6fd671c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -564,7 +564,7 @@ namespace { Move ttMove, move, excludedMove, bestMove; Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue, probCutBeta; - bool formerPv, givesCheck, improving, didLMR, priorCapture; + bool givesCheck, improving, didLMR, priorCapture; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture, singularQuietLMR; Piece movedPiece; @@ -634,7 +634,6 @@ namespace { : ss->ttHit ? tte->move() : MOVE_NONE; if (!excludedMove) ss->ttPv = PvNode || (ss->ttHit && tte->is_pv()); - formerPv = ss->ttPv && !PvNode; // Update low ply history for previous move if we are near root and position is or has been in PV if ( ss->ttPv @@ -1064,8 +1063,8 @@ namespace { && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - ((formerPv + 4) * depth) / 2; - Depth singularDepth = (depth - 1 + 3 * formerPv) / 2; + Value singularBeta = ttValue - 2 * depth; + Depth singularDepth = (depth - 1) / 2; ss->excludedMove = move; value = search(pos, ss, singularBeta - 1, singularBeta, singularDepth, cutNode); @@ -1128,7 +1127,7 @@ namespace { && moveCount > 1 + 2 * rootNode && ( !captureOrPromotion || (cutNode && (ss-1)->moveCount > 1) - || (!PvNode && !formerPv)) + || !ss->ttPv) && (!PvNode || ss->ply > 1 || thisThread->id() % 4 != 3)) { Depth r = reduction(improving, depth, moveCount); From 4ada291429df815558c99cac56fb47ff31a6c6fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicolet?= Date: Wed, 2 Jun 2021 08:37:00 +0200 Subject: [PATCH 0581/1766] Typography change for bench --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0f6caec9e3f..926d568db7e 100644 --- a/README.md +++ b/README.md @@ -143,9 +143,9 @@ For users, the following UCI options, which can typically be set via a GUI, are For developers the following non-standard commands might be of interest, mainly useful for debugging: - * #### bench ttSize threads limit fenFile limitType evalType - Performs a standard benchmark using various options. The signature or standard node - count is obtained using all defaults. `bench` is currently `bench 16 1 13 default depth mixed`. + * #### bench *ttSize threads limit fenFile limitType evalType* + Performs a standard benchmark using various options. The signature of a version (standard node + count) is obtained using all defaults. `bench` is currently `bench 16 1 13 default depth mixed`. * #### compiler Give information about the compiler and environment used for building a binary. From d53071eff4e75bc77dc86f65c52358d8014cb71c Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 3 Jun 2021 13:53:28 +0200 Subject: [PATCH 0582/1766] Update default net to nn-7e66505906a6.nnue Trained with pytorch using the master branch and recommended settings, the data used is the previous 64B binpack enhanced with a 2B binpack generated using an opening book of positions for with the static eval is significantly different from d9 search. book : https://drive.google.com/file/d/1rHcKY5rv34kwku6g89OhnE8Bkfq3UWau/view?usp=sharing book generation: https://github.com/vondele/Stockfish/commit/3ce43ab0c4ce09c1fc5bca5ca27a248e67fddd24 binpack : https://drive.google.com/file/d/1rHcKY5rv34kwku6g89OhnE8Bkfq3UWau/view?usp=sharing ------- Data generation command: generate_training_data depth 9 count 31250000 random_multi_pv 2 random_multi_pv_diff 100 random_move_max_ply 8 random_move_count 3 set_recommended_uci_options eval_limit 32000 output_file_name output.binpack book wrongNNUE.epd seed ${RANDOM}${RANDOM} Training command: python train.py ../../all_d9_fishd9_d8_d10_wrong_shuffle.binpack ../../all_d9_fishd9_d8_d10_wrong_shuffle.binpack --gpus 1 --threads 2 --num-workers 2 --batch-size 16384 --progress_bar_refresh_rate 300 --smart-fen-skipping --random-fen-skipping 3 --features=HalfKAv2^ --lambda=1.0 --max_epochs=400 --seed $RANDOM --default_root_dir exp/run_5 ------- passed STC: https://tests.stockfishchess.org/tests/view/60b7c79a457376eb8bcaa104 LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 64592 W: 6254 L: 6028 D: 52310 Ptnml(0-2): 255, 4785, 22020, 4951, 285 passed LTC: https://tests.stockfishchess.org/tests/view/60b85307457376eb8bcaa182 LLR: 2.96 (-2.94,2.94) <0.50,3.50> Total: 45560 W: 1998 L: 1826 D: 41736 Ptnml(0-2): 36, 1604, 19335, 1762, 43 closes https://github.com/official-stockfish/Stockfish/pull/3521 Bench: 4364128 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 41aace67fcb..725d5a78b26 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-7756374aaed3.nnue" + #define EvalFileDefaultName "nn-7e66505906a6.nnue" namespace NNUE { From 9353e72103d4dc4880506c2f54b6426cbd182450 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Thu, 3 Jun 2021 19:46:55 +0100 Subject: [PATCH 0583/1766] Make extra time for bestMoveInstability dependent on rootdepth. This change allocates more base time to moves and makes the additional time added for best move instability dependent on rootdepth. STC 10+0.1 : LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 19432 W: 1711 L: 1553 D: 16168 Ptnml(0-2): 47, 1250, 6989, 1358, 72 https://tests.stockfishchess.org/tests/view/60b8cd41457376eb8bcaa1ad LTC 60+0.6 : LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 22480 W: 810 L: 693 D: 20977 Ptnml(0-2): 9, 603, 9902, 714, 12 https://tests.stockfishchess.org/tests/view/60b8e5bf457376eb8bcaa1e6 closes https://github.com/official-stockfish/Stockfish/pull/3526 Bench 4364128 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 2c7c6fd671c..100ddc719e1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -480,8 +480,8 @@ void Thread::search() { totBestMoveChanges += th->bestMoveChanges; th->bestMoveChanges = 0; } - double bestMoveInstability = 1 + 2 * totBestMoveChanges / Threads.size(); - + double bestMoveInstability = 1.073 + std::max(1.0, 2.25 - 9.9 / rootDepth) + * totBestMoveChanges / Threads.size(); double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability; // Cap used time in case of a single legal move for a better viewer experience in tournaments From 0b7cc8bd2f5a51de9f911fcdfb253b1c9f513897 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Thu, 3 Jun 2021 13:52:39 -0300 Subject: [PATCH 0584/1766] Introducing NodeType Root We transform rootNode into constexpr by adding a new NodeType `Root`, which causes a speed up. Local test: ``` Build Tester: 1.4.7.0 Windows 10 (Version 10.0, Build 0, 64-bit Edition) Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz SafeMode: No Running In VM: No HyperThreading Enabled: Yes CPU Warmup: Yes Command Line: bench Tests per Build: 25 ANOVA: n/a Engine# (NPS) Speedup Sp Conf. 95% S.S. patch (920.179,4) ---> master (906.329,2) ---> 1,528% 20.336,5 Yes No ``` --------- STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 98216 W: 8348 L: 8102 D: 81766 Ptnml(0-2): 295, 6357, 35549, 6621, 286 https://tests.stockfishchess.org/tests/view/60b797e2457376eb8bcaa0ab Yellow LTC: LLR: -2.95 (-2.94,2.94) <0.50,3.50> Total: 76936 W: 2651 L: 2626 D: 71659 Ptnml(0-2): 29, 2233, 33916, 2264, 26 https://tests.stockfishchess.org/tests/view/60b80d6d457376eb8bcaa145 closes https://github.com/official-stockfish/Stockfish/pull/3522 No functional change --- src/search.cpp | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 100ddc719e1..f634b2d2d67 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -59,7 +59,7 @@ using namespace Search; namespace { // Different node types, used as a template parameter - enum NodeType { NonPV, PV }; + enum NodeType { NonPV, PV, Root }; constexpr uint64_t TtHitAverageWindow = 4096; constexpr uint64_t TtHitAverageResolution = 1024; @@ -102,10 +102,10 @@ namespace { Move best = MOVE_NONE; }; - template + template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode); - template + template Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth = 0); Value value_to_tt(Value v, int ply); @@ -384,7 +384,7 @@ void Thread::search() { while (true) { Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt - searchAgainCounter); - bestValue = Stockfish::search(rootPos, ss, alpha, beta, adjustedDepth, false); + bestValue = Stockfish::search(rootPos, ss, alpha, beta, adjustedDepth, false); // Bring the best move to the front. It is critical that sorting // is done with a stable algorithm because all the values but the @@ -527,18 +527,18 @@ namespace { // search<>() is the main search function for both PV and non-PV nodes - template + template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) { - constexpr bool PvNode = NT == PV; - const bool rootNode = PvNode && ss->ply == 0; + constexpr bool PvNode = nodeType != NonPV; + constexpr bool rootNode = nodeType == Root; const Depth maxNextDepth = rootNode ? depth : depth + 1; // Check if we have an upcoming move which draws by repetition, or // if the opponent had an alternative move earlier to this position. - if ( pos.rule50_count() >= 3 + if ( !rootNode + && pos.rule50_count() >= 3 && alpha < VALUE_DRAW - && !rootNode && pos.has_game_cycle(ss->ply)) { alpha = value_draw(pos.this_thread()); @@ -548,7 +548,7 @@ namespace { // Dive into quiescence search when the depth reaches zero if (depth <= 0) - return qsearch(pos, ss, alpha, beta); + return qsearch(pos, ss, alpha, beta); assert(-VALUE_INFINITE <= alpha && alpha < beta && beta <= VALUE_INFINITE); assert(PvNode || (alpha == beta - 1)); @@ -1054,9 +1054,9 @@ namespace { // 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 >= 7 + if ( !rootNode + && depth >= 7 && move == ttMove - && !rootNode && !excludedMove // Avoid recursive singular search /* && ttValue != VALUE_NONE Already implicit in the next condition */ && abs(ttValue) < VALUE_KNOWN_WIN @@ -1351,10 +1351,11 @@ namespace { // qsearch() is the quiescence search function, which is called by the main search // function with zero depth, or recursively with further decreasing depth per call. - template + template Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { - constexpr bool PvNode = NT == PV; + static_assert(nodeType != Root); + constexpr bool PvNode = nodeType == PV; assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE); assert(PvNode || (alpha == beta - 1)); @@ -1532,7 +1533,7 @@ namespace { // Make and search the move pos.do_move(move, st, givesCheck); - value = -qsearch(pos, ss+1, -beta, -alpha, depth - 1); + value = -qsearch(pos, ss+1, -beta, -alpha, depth - 1); pos.undo_move(move); assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); From 4445965f9714402050119f9e6a76c6a8fc4f8d9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Wed, 2 Jun 2021 08:10:54 +0200 Subject: [PATCH 0585/1766] Makefile: better "make clean" for Windows Make clean should be really clean on Windows. Fixes issue https://github.com/official-stockfish/Stockfish/issues/3291 Closes https://github.com/official-stockfish/Stockfish/pull/3517 No functional change --- src/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Makefile b/src/Makefile index 066e7697dee..bd75ec52482 100644 --- a/src/Makefile +++ b/src/Makefile @@ -793,6 +793,9 @@ profileclean: @rm -rf profdir @rm -f bench.txt *.gcda *.gcno ./syzygy/*.gcda ./nnue/*.gcda ./nnue/features/*.gcda *.s @rm -f stockfish.profdata *.profraw + @rm -f stockfish.exe.lto_wrapper_args + @rm -f stockfish.exe.ltrans.out + @rm -f ./-lstdc++.res default: help From 8f081c86f7f8827ea35fc687e6f6591950cc8f90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Fri, 4 Jun 2021 13:56:40 +0200 Subject: [PATCH 0586/1766] Clean SIMD code a bit Cleaner vector code structure in feature transformer. This patch just regroups the parts of the inner loop for each SIMD instruction set. Tested for non-regression: LLR: 2.96 (-2.94,2.94) <-2.50,0.50> Total: 115760 W: 9835 L: 9831 D: 96094 Ptnml(0-2): 326, 7776, 41715, 7694, 369 https://tests.stockfishchess.org/tests/view/60b96b39457376eb8bcaa26e It would be nice if a future patch could use some of the macros at the top of the file to unify the code between the distincts SIMD instruction sets (of course, unifying the Relu will be the challenge). closes https://github.com/official-stockfish/Stockfish/pull/3506 No functional change --- src/nnue/nnue_feature_transformer.h | 184 ++++++++++++++++------------ 1 file changed, 105 insertions(+), 79 deletions(-) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index c249d3e7018..10b226b3113 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -180,118 +180,144 @@ namespace Stockfish::Eval::NNUE { const auto& psqtAccumulation = pos.state()->accumulator.psqtAccumulation; const auto psqt = ( - psqtAccumulation[static_cast(perspectives[0])][bucket] - - psqtAccumulation[static_cast(perspectives[1])][bucket] + psqtAccumulation[perspectives[0]][bucket] + - psqtAccumulation[perspectives[1]][bucket] ) / 2; + #if defined(USE_AVX512) + constexpr IndexType NumChunks = HalfDimensions / (SimdWidth * 2); static_assert(HalfDimensions % (SimdWidth * 2) == 0); const __m512i Control = _mm512_setr_epi64(0, 2, 4, 6, 1, 3, 5, 7); const __m512i Zero = _mm512_setzero_si512(); + for (IndexType p = 0; p < 2; ++p) + { + const IndexType offset = HalfDimensions * p; + auto out = reinterpret_cast<__m512i*>(&output[offset]); + for (IndexType j = 0; j < NumChunks; ++j) + { + __m512i sum0 = _mm512_load_si512(&reinterpret_cast + (accumulation[perspectives[p]])[j * 2 + 0]); + __m512i sum1 = _mm512_load_si512(&reinterpret_cast + (accumulation[perspectives[p]])[j * 2 + 1]); + + _mm512_store_si512(&out[j], _mm512_permutexvar_epi64(Control, + _mm512_max_epi8(_mm512_packs_epi16(sum0, sum1), Zero))); + } + } + return psqt; + #elif defined(USE_AVX2) + constexpr IndexType NumChunks = HalfDimensions / SimdWidth; constexpr int Control = 0b11011000; const __m256i Zero = _mm256_setzero_si256(); + for (IndexType p = 0; p < 2; ++p) + { + const IndexType offset = HalfDimensions * p; + auto out = reinterpret_cast<__m256i*>(&output[offset]); + for (IndexType j = 0; j < NumChunks; ++j) + { + __m256i sum0 = _mm256_load_si256(&reinterpret_cast + (accumulation[perspectives[p]])[j * 2 + 0]); + __m256i sum1 = _mm256_load_si256(&reinterpret_cast + (accumulation[perspectives[p]])[j * 2 + 1]); + + _mm256_store_si256(&out[j], _mm256_permute4x64_epi64( + _mm256_max_epi8(_mm256_packs_epi16(sum0, sum1), Zero), Control)); + } + } + return psqt; + #elif defined(USE_SSE2) - constexpr IndexType NumChunks = HalfDimensions / SimdWidth; - #ifdef USE_SSE41 + #ifdef USE_SSE41 + constexpr IndexType NumChunks = HalfDimensions / SimdWidth; const __m128i Zero = _mm_setzero_si128(); - #else + #else + constexpr IndexType NumChunks = HalfDimensions / SimdWidth; const __m128i k0x80s = _mm_set1_epi8(-128); - #endif + #endif + + for (IndexType p = 0; p < 2; ++p) + { + const IndexType offset = HalfDimensions * p; + auto out = reinterpret_cast<__m128i*>(&output[offset]); + for (IndexType j = 0; j < NumChunks; ++j) + { + __m128i sum0 = _mm_load_si128(&reinterpret_cast + (accumulation[perspectives[p]])[j * 2 + 0]); + __m128i sum1 = _mm_load_si128(&reinterpret_cast + (accumulation[perspectives[p]])[j * 2 + 1]); + const __m128i packedbytes = _mm_packs_epi16(sum0, sum1); + + #ifdef USE_SSE41 + _mm_store_si128(&out[j], _mm_max_epi8(packedbytes, Zero)); + #else + _mm_store_si128(&out[j], _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s)); + #endif + } + } + return psqt; #elif defined(USE_MMX) + constexpr IndexType NumChunks = HalfDimensions / SimdWidth; const __m64 k0x80s = _mm_set1_pi8(-128); + for (IndexType p = 0; p < 2; ++p) + { + const IndexType offset = HalfDimensions * p; + auto out = reinterpret_cast<__m64*>(&output[offset]); + for (IndexType j = 0; j < NumChunks; ++j) + { + __m64 sum0 = *(&reinterpret_cast(accumulation[perspectives[p]])[j * 2 + 0]); + __m64 sum1 = *(&reinterpret_cast(accumulation[perspectives[p]])[j * 2 + 1]); + const __m64 packedbytes = _mm_packs_pi16(sum0, sum1); + out[j] = _mm_subs_pi8(_mm_adds_pi8(packedbytes, k0x80s), k0x80s); + } + } + _mm_empty(); + return psqt; + #elif defined(USE_NEON) + constexpr IndexType NumChunks = HalfDimensions / (SimdWidth / 2); const int8x8_t Zero = {0}; - #endif - - for (IndexType p = 0; p < 2; ++p) { - const IndexType offset = HalfDimensions * p; - - #if defined(USE_AVX512) - auto out = reinterpret_cast<__m512i*>(&output[offset]); - for (IndexType j = 0; j < NumChunks; ++j) { - __m512i sum0 = _mm512_load_si512( - &reinterpret_cast(accumulation[perspectives[p]])[j * 2 + 0]); - __m512i sum1 = _mm512_load_si512( - &reinterpret_cast(accumulation[perspectives[p]])[j * 2 + 1]); - _mm512_store_si512(&out[j], _mm512_permutexvar_epi64(Control, - _mm512_max_epi8(_mm512_packs_epi16(sum0, sum1), Zero))); - } - #elif defined(USE_AVX2) - auto out = reinterpret_cast<__m256i*>(&output[offset]); - for (IndexType j = 0; j < NumChunks; ++j) { - __m256i sum0 = _mm256_load_si256( - &reinterpret_cast(accumulation[perspectives[p]])[j * 2 + 0]); - __m256i sum1 = _mm256_load_si256( - &reinterpret_cast(accumulation[perspectives[p]])[j * 2 + 1]); - _mm256_store_si256(&out[j], _mm256_permute4x64_epi64(_mm256_max_epi8( - _mm256_packs_epi16(sum0, sum1), Zero), Control)); - } + for (IndexType p = 0; p < 2; ++p) + { + const IndexType offset = HalfDimensions * p; + const auto out = reinterpret_cast(&output[offset]); + for (IndexType j = 0; j < NumChunks; ++j) + { + int16x8_t sum = reinterpret_cast(accumulation[perspectives[p]])[j]; + out[j] = vmax_s8(vqmovn_s16(sum), Zero); + } + } + return psqt; - #elif defined(USE_SSE2) - auto out = reinterpret_cast<__m128i*>(&output[offset]); - for (IndexType j = 0; j < NumChunks; ++j) { - __m128i sum0 = _mm_load_si128(&reinterpret_cast( - accumulation[perspectives[p]])[j * 2 + 0]); - __m128i sum1 = _mm_load_si128(&reinterpret_cast( - accumulation[perspectives[p]])[j * 2 + 1]); - const __m128i packedbytes = _mm_packs_epi16(sum0, sum1); - - _mm_store_si128(&out[j], - - #ifdef USE_SSE41 - _mm_max_epi8(packedbytes, Zero) #else - _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s) - #endif - - ); - } - - #elif defined(USE_MMX) - auto out = reinterpret_cast<__m64*>(&output[offset]); - for (IndexType j = 0; j < NumChunks; ++j) { - __m64 sum0 = *(&reinterpret_cast( - accumulation[perspectives[p]])[j * 2 + 0]); - __m64 sum1 = *(&reinterpret_cast( - accumulation[perspectives[p]])[j * 2 + 1]); - const __m64 packedbytes = _mm_packs_pi16(sum0, sum1); - out[j] = _mm_subs_pi8(_mm_adds_pi8(packedbytes, k0x80s), k0x80s); - } - #elif defined(USE_NEON) - const auto out = reinterpret_cast(&output[offset]); - for (IndexType j = 0; j < NumChunks; ++j) { - int16x8_t sum = reinterpret_cast( - accumulation[perspectives[p]])[j]; - out[j] = vmax_s8(vqmovn_s16(sum), Zero); - } + for (IndexType p = 0; p < 2; ++p) + { + const IndexType offset = HalfDimensions * p; + for (IndexType j = 0; j < HalfDimensions; ++j) + { + BiasType sum = accumulation[perspectives[p]][j]; + output[offset + j] = static_cast(std::max(0, std::min(127, sum))); + } + } + return psqt; - #else - for (IndexType j = 0; j < HalfDimensions; ++j) { - BiasType sum = accumulation[static_cast(perspectives[p])][j]; - output[offset + j] = static_cast( - std::max(0, std::min(127, sum))); - } #endif - } - #if defined(USE_MMX) - _mm_empty(); - #endif + } // end of function transform() + - return psqt; - } private: void update_accumulator(const Position& pos, const Color perspective) const { From 58307562b66597aeed9b5a7d76d2edeff53cf714 Mon Sep 17 00:00:00 2001 From: Guy Vreuls Date: Fri, 4 Jun 2021 12:20:27 +0200 Subject: [PATCH 0587/1766] Revert "Simplify En Passant" This reverts commit 9f8058bd26df1c3ca37b85f811026f1eb82e6524. Fixes the memory leak discussed in pull request #3523 https://github.com/official-stockfish/Stockfish/pull/3523 Passed non-regression STC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 76184 W: 6330 L: 6282 D: 63572 Ptnml(0-2): 202, 5047, 27564, 5059, 220 https://tests.stockfishchess.org/tests/view/60ba146c457376eb8bcaa2e2 closes https://github.com/official-stockfish/Stockfish/pull/3527 Benched to verify there is no functional change. Bench: 4364128 --- src/position.cpp | 56 +++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index f1c36156a3b..56cb34e8ec3 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -251,8 +251,6 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th set_castling_right(c, rsq); } - set_state(st); - // 4. En passant square. // Ignore if square is invalid or not on side to move relative rank 6. bool enpassant = false; @@ -266,24 +264,12 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th // a) side to move have a pawn threatening epSquare // b) there is an enemy pawn in front of epSquare // c) there is no piece on epSquare or behind epSquare - // d) enemy pawn didn't block a check of its own color by moving forward enpassant = pawn_attacks_bb(~sideToMove, st->epSquare) & pieces(sideToMove, PAWN) && (pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove))) - && !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove)))) - && ( file_of(square(sideToMove)) == file_of(st->epSquare) - || !(blockers_for_king(sideToMove) & (st->epSquare + pawn_push(~sideToMove)))); + && !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove)))); } - // It's necessary for st->previous to be intialized in this way because legality check relies on its existence - if (enpassant) { - st->previous = new StateInfo(); - remove_piece(st->epSquare - pawn_push(sideToMove)); - st->previous->checkersBB = attackers_to(square(~sideToMove)) & pieces(sideToMove); - st->previous->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), square(WHITE), st->previous->pinners[BLACK]); - st->previous->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square(BLACK), st->previous->pinners[WHITE]); - put_piece(make_piece(~sideToMove, PAWN), st->epSquare - pawn_push(sideToMove)); - } - else + if (!enpassant) st->epSquare = SQ_NONE; // 5-6. Halfmove clock and fullmove number @@ -295,6 +281,7 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th chess960 = isChess960; thisThread = th; + set_state(st); st->accumulator.state[WHITE] = Eval::NNUE::INIT; st->accumulator.state[BLACK] = Eval::NNUE::INIT; @@ -517,11 +504,23 @@ bool Position::legal(Move m) const { assert(color_of(moved_piece(m)) == us); assert(piece_on(square(us)) == make_piece(us, KING)); - // st->previous->blockersForKing consider capsq as empty. - // If pinned, it has to move along the king ray. + // En passant captures are a tricky special case. Because they are rather + // uncommon, we do it simply by testing whether the king is attacked after + // the move is made. if (type_of(m) == EN_PASSANT) - return !(st->previous->blockersForKing[sideToMove] & from) - || aligned(from, to, square(us)); + { + Square ksq = square(us); + Square capsq = to - pawn_push(us); + Bitboard occupied = (pieces() ^ from ^ capsq) | to; + + assert(to == ep_square()); + assert(moved_piece(m) == make_piece(us, PAWN)); + assert(piece_on(capsq) == make_piece(~us, PAWN)); + assert(piece_on(to) == NO_PIECE); + + return !(attacks_bb< ROOK>(ksq, occupied) & pieces(~us, QUEEN, ROOK)) + && !(attacks_bb(ksq, occupied) & pieces(~us, QUEEN, BISHOP)); + } // Castling moves generation does not check if the castling path is clear of // enemy attacks, it is delayed at a later time: now! @@ -654,15 +653,18 @@ bool Position::gives_check(Move m) const { case PROMOTION: return attacks_bb(promotion_type(m), to, pieces() ^ from) & square(~sideToMove); - // The double-pushed pawn blocked a check? En Passant will remove the blocker. - // The only discovery check that wasn't handle is through capsq and fromsq - // So the King must be in the same rank as fromsq to consider this possibility. - // st->previous->blockersForKing consider capsq as empty. + // En passant capture with check? We have already handled the case + // of direct checks and ordinary discovered check, so the only case we + // need to handle is the unusual case of a discovered check through + // the captured pawn. case EN_PASSANT: - return st->previous->checkersBB - || ( rank_of(square(~sideToMove)) == rank_of(from) - && st->previous->blockersForKing[~sideToMove] & from); + { + Square capsq = make_square(file_of(to), rank_of(from)); + Bitboard b = (pieces() ^ from ^ capsq) | to; + return (attacks_bb< ROOK>(square(~sideToMove), b) & pieces(sideToMove, QUEEN, ROOK)) + | (attacks_bb(square(~sideToMove), b) & pieces(sideToMove, QUEEN, BISHOP)); + } default: //CASTLING { // Castling is encoded as 'king captures the rook' From 98cbaa6c6b07ea594501ed7a53ea83feb7c8ea9c Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 3 Jun 2021 19:18:24 +0200 Subject: [PATCH 0588/1766] Enhance CI to error on leaks Add flags to valgrind in our Continuous Integration scripts, to error on memory leaks. closes https://github.com/official-stockfish/Stockfish/pull/3525 No functional change. --- tests/instrumented.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/instrumented.sh b/tests/instrumented.sh index bfb50e94c19..d30c8e35a57 100755 --- a/tests/instrumented.sh +++ b/tests/instrumented.sh @@ -13,7 +13,7 @@ case $1 in --valgrind) echo "valgrind testing started" prefix='' - exeprefix='valgrind --error-exitcode=42' + exeprefix='valgrind --error-exitcode=42 --errors-for-leak-kinds=all --leak-check=full' postfix='1>/dev/null' threads="1" ;; From 3802cdf9b69dd0a8256dbed7c25141ac5757d151 Mon Sep 17 00:00:00 2001 From: Guy Vreuls Date: Thu, 3 Jun 2021 16:46:05 +0200 Subject: [PATCH 0589/1766] Makefile: Extend sanitize support Enable compiling with multiple sanitizers at once. Syntax: make build ARCH=x86-64-avx512 debug=on sanitize="address undefined" closes https://github.com/official-stockfish/Stockfish/pull/3524 No functional change. --- src/Makefile | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Makefile b/src/Makefile index bd75ec52482..33a270fe19f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -61,9 +61,11 @@ endif # ---------------------------------------------------------------------------- # # debug = yes/no --- -DNDEBUG --- Enable/Disable debug mode -# sanitize = undefined/thread/no (-fsanitize ) +# sanitize = none/ ... (-fsanitize ) # --- ( undefined ) --- enable undefined behavior checks -# --- ( thread ) --- enable threading error checks +# --- ( thread ) --- enable threading error checks +# --- ( address ) --- enable memory access checks +# --- ...etc... --- see compiler documentation for supported sanitizers # optimize = yes/no --- (-O3/-fast etc.) --- Enable/Disable optimizations # arch = (name) --- (-arch) --- Target architecture # bits = 64/32 --- -DIS_64BIT --- 64-/32-bit operating system @@ -84,6 +86,10 @@ endif # Note that Makefile is space sensitive, so when adding new architectures # or modifying existing flags, you have to make sure there are no extra spaces # at the end of the line for flag values. +# +# Example of use for these flags: +# make build ARCH=x86-64-avx512 debug=on sanitize="address undefined" + ### 2.1. General and architecture defaults @@ -105,7 +111,7 @@ endif optimize = yes debug = no -sanitize = no +sanitize = none bits = 64 prefetch = no popcnt = no @@ -473,9 +479,9 @@ else endif ### 3.2.2 Debugging with undefined behavior sanitizers -ifneq ($(sanitize),no) - CXXFLAGS += -g3 -fsanitize=$(sanitize) - LDFLAGS += -fsanitize=$(sanitize) +ifneq ($(sanitize),none) + CXXFLAGS += -g3 $(addprefix -fsanitize=,$(sanitize)) + LDFLAGS += $(addprefix -fsanitize=,$(sanitize)) endif ### 3.3 Optimization @@ -838,7 +844,6 @@ config-sanity: net @echo "Testing config sanity. If this fails, try 'make help' ..." @echo "" @test "$(debug)" = "yes" || test "$(debug)" = "no" - @test "$(sanitize)" = "undefined" || test "$(sanitize)" = "thread" || test "$(sanitize)" = "address" || test "$(sanitize)" = "no" @test "$(optimize)" = "yes" || test "$(optimize)" = "no" @test "$(SUPPORTED_ARCH)" = "true" @test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \ From 999e142c548f61852ace7b8f025d71bc8a805e3c Mon Sep 17 00:00:00 2001 From: bmc4 Date: Sun, 6 Jun 2021 13:31:57 -0300 Subject: [PATCH 0590/1766] Reduce in LMR reduction on PvNode reduce reduction in LMR by 1 on PvNode. STC: LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 266080 W: 22438 L: 21996 D: 221646 Ptnml(0-2): 774, 17874, 95376, 18168, 848 https://tests.stockfishchess.org/tests/view/60bc0661457376eb8bcaa4bb LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 20144 W: 698 L: 587 D: 18859 Ptnml(0-2): 2, 529, 8906, 626, 9 https://tests.stockfishchess.org/tests/view/60bcc3f2457376eb8bcaa58d closes https://github.com/official-stockfish/Stockfish/pull/3534 bench: 5173012 --- src/search.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index f634b2d2d67..67f259fca36 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1132,6 +1132,9 @@ namespace { { Depth r = reduction(improving, depth, moveCount); + if (PvNode) + r--; + // Decrease reduction if the ttHit running average is large (~0 Elo) if (thisThread->ttHitAverage > 537 * TtHitAverageResolution * TtHitAverageWindow / 1024) r--; From 785b70809783430ff1e0bf856dac3b9bfa6fe826 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Mon, 7 Jun 2021 15:47:37 -0300 Subject: [PATCH 0591/1766] Simplify promotion move generator This patch removes Knight promotion checks from Captures. As a consequence, it also removes this underpromotion from qsearch. STC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 37776 W: 3113 L: 3023 D: 31640 Ptnml(0-2): 103, 2419, 13755, 2507, 104 https://tests.stockfishchess.org/tests/view/60be6a06457376eb8bcaa775 LTC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 39760 W: 1257 L: 1203 D: 37300 Ptnml(0-2): 11, 1079, 17646, 1133, 11 https://tests.stockfishchess.org/tests/view/60beb972457376eb8bcaa7c5 closes https://github.com/official-stockfish/Stockfish/pull/3536 Bench: 5530620 --- src/movegen.cpp | 24 ++++++++++-------------- src/search.cpp | 2 +- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index bb81aeac078..5f3ba90a479 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -26,21 +26,16 @@ namespace Stockfish { namespace { template - ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) { + ExtMove* make_promotions(ExtMove* moveList, Square to) { if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) - { *moveList++ = make(to - D, to, QUEEN); - if (attacks_bb(to) & ksq) - *moveList++ = make(to - D, to, KNIGHT); - } if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS) { *moveList++ = make(to - D, to, ROOK); *moveList++ = make(to - D, to, BISHOP); - if (!(attacks_bb(to) & ksq)) - *moveList++ = make(to - D, to, KNIGHT); + *moveList++ = make(to - D, to, KNIGHT); } return moveList; @@ -57,7 +52,6 @@ namespace { constexpr Direction UpRight = (Us == WHITE ? NORTH_EAST : SOUTH_WEST); constexpr Direction UpLeft = (Us == WHITE ? NORTH_WEST : SOUTH_EAST); - const Square ksq = pos.square(Them); const Bitboard emptySquares = Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces(); const Bitboard enemies = Type == EVASIONS ? pos.checkers() : Type == CAPTURES ? target : pos.pieces(Them); @@ -82,6 +76,7 @@ namespace { // To make a quiet check, you either make a direct check by pushing a pawn // or push a blocker pawn that is not on the same file as the enemy king. // Discovered check promotion has been already generated amongst the captures. + Square ksq = pos.square(Them); Bitboard dcCandidatePawns = pos.blockers_for_king(Them) & ~file_bb(ksq); b1 &= pawn_attacks_bb(Them, ksq) | shift< Up>(dcCandidatePawns); b2 &= pawn_attacks_bb(Them, ksq) | shift(dcCandidatePawns); @@ -111,13 +106,13 @@ namespace { b3 &= target; while (b1) - moveList = make_promotions(moveList, pop_lsb(b1), ksq); + moveList = make_promotions(moveList, pop_lsb(b1)); while (b2) - moveList = make_promotions(moveList, pop_lsb(b2), ksq); + moveList = make_promotions(moveList, pop_lsb(b2)); while (b3) - moveList = make_promotions(moveList, pop_lsb(b3), ksq); + moveList = make_promotions(moveList, pop_lsb(b3)); } // Standard and en passant captures @@ -206,6 +201,7 @@ namespace { moveList = generate_moves(pos, moveList, target); moveList = generate_moves(pos, moveList, target); } + if (!Checks || pos.blockers_for_king(~Us) & ksq) { Bitboard b = attacks_bb(ksq) & (Type == EVASIONS ? ~pos.pieces(Us) : target); @@ -227,10 +223,10 @@ namespace { } // namespace -/// Generates all pseudo-legal captures plus queen and checking knight promotions -/// Generates all pseudo-legal non-captures and underpromotions (except checking knight) +/// Generates all pseudo-legal captures plus queen promotions +/// Generates all pseudo-legal non-captures and underpromotions /// Generates all pseudo-legal check evasions when the side to move is in check -/// Generates all pseudo-legal non-captures giving check, except castling +/// Generates all pseudo-legal non-captures giving check, except castling and promotions /// Generates all pseudo-legal captures and non-captures /// /// Returns a pointer to the end of the move list. diff --git a/src/search.cpp b/src/search.cpp index 67f259fca36..5306bb9a008 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1464,7 +1464,7 @@ namespace { // Initialize a MovePicker object for the current position, and prepare // to search the moves. Because the depth is <= 0 here, only captures, - // queen and checking knight promotions, and other checks(only if depth >= DEPTH_QS_CHECKS) + // queen promotions, and other checks (only if depth >= DEPTH_QS_CHECKS) // will be generated. MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory, From 559942d64de9497c9d6d13b22307b4b9e1ce8ecf Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 9 Jun 2021 23:23:13 +0200 Subject: [PATCH 0592/1766] Limit double extensions Double extensions can lead to search explosions, for specific positions. Currently, however, these double extensions are worth about 10Elo and cannot be removed. This patch instead limits the number of double extensions given to a maximum of 3. This fixes https://github.com/official-stockfish/Stockfish/issues/3532 where the following testcase was shown to be problematic: ``` uci setoption name Hash value 4 setoption name Contempt value 0 ucinewgame position fen 8/Pk6/8/1p6/8/P1K5/8/6B1 w - - 37 130 go depth 20 ``` passed STC: https://tests.stockfishchess.org/tests/view/60c13161457376eb8bcaaa0f LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 73256 W: 6114 L: 6062 D: 61080 Ptnml(0-2): 222, 4912, 26306, 4968, 220 passed LTC: https://tests.stockfishchess.org/tests/view/60c196fb457376eb8bcaaa6b LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 166440 W: 5559 L: 5594 D: 155287 Ptnml(0-2): 106, 4921, 73197, 4894, 102 closes https://github.com/official-stockfish/Stockfish/pull/3544 Bench: 5067605 --- src/search.cpp | 14 ++++++++++---- src/search.h | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5306bb9a008..d04898e8570 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -610,10 +610,11 @@ namespace { assert(0 <= ss->ply && ss->ply < MAX_PLY); - (ss+1)->ttPv = false; + (ss+1)->ttPv = false; (ss+1)->excludedMove = bestMove = MOVE_NONE; - (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; - Square prevSq = to_sq((ss-1)->currentMove); + (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; + ss->doubleExtensions = (ss-1)->doubleExtensions; + Square prevSq = to_sq((ss-1)->currentMove); // Initialize statScore to zero for the grandchildren of the current position. // So statScore is shared between all grandchildren and only the first grandchild @@ -1074,7 +1075,11 @@ namespace { { extension = 1; singularQuietLMR = !ttCapture; - if (!PvNode && value < singularBeta - 93) + + // Avoid search explosion by limiting the number of double extensions to at most 3 + if ( !PvNode + && value < singularBeta - 93 + && ss->doubleExtensions < 3) extension = 2; } @@ -1105,6 +1110,7 @@ namespace { // Add extension to new depth newDepth += extension; + ss->doubleExtensions = (ss-1)->doubleExtensions + (extension == 2); // Speculative prefetch as early as possible prefetch(TT.first_entry(pos.key_after(move))); diff --git a/src/search.h b/src/search.h index 811b2e2a946..801baacc7f1 100644 --- a/src/search.h +++ b/src/search.h @@ -52,6 +52,7 @@ struct Stack { bool inCheck; bool ttPv; bool ttHit; + int doubleExtensions; }; From b84fa04db6ea5fc6d7d714539c11537bde64538b Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Wed, 9 Jun 2021 11:21:55 +0200 Subject: [PATCH 0593/1766] Read NNUE net faster Load feature transformer weights in bulk on little-endian machines. This is in particular useful to test new nets with c-chess-cli, see https://github.com/lucasart/c-chess-cli/issues/44 ``` $ time ./stockfish.exe uci Before : 0m0.914s After : 0m0.483s ``` No functional change --- src/misc.h | 13 +++-- src/nnue/nnue_common.h | 80 ++++++++++++++++++++++------- src/nnue/nnue_feature_transformer.h | 24 ++++----- src/syzygy/tbprobe.cpp | 3 -- 4 files changed, 81 insertions(+), 39 deletions(-) diff --git a/src/misc.h b/src/misc.h index 59ca6e376bd..dae37cd98f0 100644 --- a/src/misc.h +++ b/src/misc.h @@ -66,9 +66,10 @@ std::ostream& operator<<(std::ostream&, SyncCout); #define sync_cout std::cout << IO_LOCK #define sync_endl std::endl << IO_UNLOCK -// `ptr` must point to an array of size at least -// `sizeof(T) * N + alignment` bytes, where `N` is the -// number of elements in the array. + +// align_ptr_up() : get the first aligned element of an array. +// ptr must point to an array of size at least `sizeof(T) * N + alignment` bytes, +// where N is the number of elements in the array. template T* align_ptr_up(T* ptr) { @@ -78,6 +79,12 @@ T* align_ptr_up(T* ptr) return reinterpret_cast(reinterpret_cast((ptrint + (Alignment - 1)) / Alignment * Alignment)); } + +// IsLittleEndian : true if and only if the binary is compiled on a little endian machine +static inline const union { uint32_t i; char c[4]; } Le = { 0x01020304 }; +static inline const bool IsLittleEndian = (Le.c[0] == 4); + + template class ValueListInserter { public: diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index dc70006120d..390f61c35ad 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -24,6 +24,8 @@ #include #include +#include "../misc.h" // for IsLittleEndian + #if defined(USE_AVX2) #include @@ -86,37 +88,77 @@ namespace Stockfish::Eval::NNUE { // necessary to return a result with the byte ordering of the compiling machine. template inline IntType read_little_endian(std::istream& stream) { - IntType result; - std::uint8_t u[sizeof(IntType)]; - typename std::make_unsigned::type v = 0; - stream.read(reinterpret_cast(u), sizeof(IntType)); - for (std::size_t i = 0; i < sizeof(IntType); ++i) - v = (v << 8) | u[sizeof(IntType) - i - 1]; + if (IsLittleEndian) + stream.read(reinterpret_cast(&result), sizeof(IntType)); + else + { + std::uint8_t u[sizeof(IntType)]; + typename std::make_unsigned::type v = 0; + + stream.read(reinterpret_cast(u), sizeof(IntType)); + for (std::size_t i = 0; i < sizeof(IntType); ++i) + v = (v << 8) | u[sizeof(IntType) - i - 1]; + + std::memcpy(&result, &v, sizeof(IntType)); + } - std::memcpy(&result, &v, sizeof(IntType)); return result; } + // write_little_endian() is our utility to write an integer (signed or unsigned, any size) + // to a stream in little-endian order. We swap the byte order before the write if + // necessary to always write in little endian order, independantly of the byte + // ordering of the compiling machine. template inline void write_little_endian(std::ostream& stream, IntType value) { - std::uint8_t u[sizeof(IntType)]; - typename std::make_unsigned::type v = value; - - std::size_t i = 0; - // if constexpr to silence the warning about shift by 8 - if constexpr (sizeof(IntType) > 1) { - for (; i + 1 < sizeof(IntType); ++i) { - u[i] = v; - v >>= 8; - } + if (IsLittleEndian) + stream.write(reinterpret_cast(&value), sizeof(IntType)); + else + { + std::uint8_t u[sizeof(IntType)]; + typename std::make_unsigned::type v = value; + + std::size_t i = 0; + // if constexpr to silence the warning about shift by 8 + if constexpr (sizeof(IntType) > 1) + { + for (; i + 1 < sizeof(IntType); ++i) + { + u[i] = v; + v >>= 8; + } + } + u[i] = v; + + stream.write(reinterpret_cast(u), sizeof(IntType)); } - u[i] = v; + } + + // read_little_endian(s, out, N) : read integers in bulk from a little indian stream. + // This reads N integers from stream s and put them in array out. + template + inline void read_little_endian(std::istream& stream, IntType* out, std::size_t count) { + if (IsLittleEndian) + stream.read(reinterpret_cast(out), sizeof(IntType) * count); + else + for (std::size_t i = 0; i < count; ++i) + out[i] = read_little_endian(stream); + } - stream.write(reinterpret_cast(u), sizeof(IntType)); + // write_little_endian(s, out, N) : write integers in bulk to a little indian stream. + // This takes N integers from array values and writes them on stream s. + template + inline void write_little_endian(std::ostream& stream, const IntType* values, std::size_t count) { + if (IsLittleEndian) + stream.write(reinterpret_cast(values), sizeof(IntType) * count); + else + for (std::size_t i = 0; i < count; ++i) + write_little_endian(stream, values[i]); } + } // namespace Stockfish::Eval::NNUE #endif // #ifndef NNUE_COMMON_H_INCLUDED diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 10b226b3113..300ce3671c2 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -24,8 +24,6 @@ #include "nnue_common.h" #include "nnue_architecture.h" -#include "../misc.h" - #include // std::memset() namespace Stockfish::Eval::NNUE { @@ -150,23 +148,21 @@ namespace Stockfish::Eval::NNUE { // Read network parameters bool read_parameters(std::istream& stream) { - for (std::size_t i = 0; i < HalfDimensions; ++i) - biases[i] = read_little_endian(stream); - for (std::size_t i = 0; i < HalfDimensions * InputDimensions; ++i) - weights[i] = read_little_endian(stream); - for (std::size_t i = 0; i < PSQTBuckets * InputDimensions; ++i) - psqtWeights[i] = read_little_endian(stream); + + read_little_endian(stream, biases , HalfDimensions ); + read_little_endian(stream, weights , HalfDimensions * InputDimensions); + read_little_endian(stream, psqtWeights, PSQTBuckets * InputDimensions); + return !stream.fail(); } // Write network parameters bool write_parameters(std::ostream& stream) const { - for (std::size_t i = 0; i < HalfDimensions; ++i) - write_little_endian(stream, biases[i]); - for (std::size_t i = 0; i < HalfDimensions * InputDimensions; ++i) - write_little_endian(stream, weights[i]); - for (std::size_t i = 0; i < PSQTBuckets * InputDimensions; ++i) - write_little_endian(stream, psqtWeights[i]); + + write_little_endian(stream, biases , HalfDimensions ); + write_little_endian(stream, weights , HalfDimensions * InputDimensions); + write_little_endian(stream, psqtWeights, PSQTBuckets * InputDimensions); + return !stream.fail(); } diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 831c8259c53..a0ac727f600 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -105,9 +105,6 @@ template<> inline void swap_endian(uint8_t&) {} template T number(void* addr) { - static const union { uint32_t i; char c[4]; } Le = { 0x01020304 }; - static const bool IsLittleEndian = (Le.c[0] == 4); - T v; if ((uintptr_t)addr & (alignof(T) - 1)) // Unaligned pointer (very rare) From 7819412002374fac5ebf7bec1e49a25481f1c42b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sun, 13 Jun 2021 09:59:34 +0200 Subject: [PATCH 0594/1766] Clarify use of UCI options Update README.md to clarify use of UCI options closes https://github.com/official-stockfish/Stockfish/pull/3540 No functional change --- README.md | 14 ++++++++------ src/nnue/nnue_common.h | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 926d568db7e..f4ee2e3458c 100644 --- a/README.md +++ b/README.md @@ -35,12 +35,14 @@ This distribution of Stockfish consists of the following files: ## The UCI protocol and available options -The Universal Chess Interface (UCI) is a standard protocol used to communicate with a chess engine, -and is the recommended way to do so for typical graphical user interfaces (GUI) or chess tools. - -Stockfish implements most commands as described in [the UCI protocol](https://www.shredderchess.com/download/div/uci.zip) - -For users, the following UCI options, which can typically be set via a GUI, are available in Stockfish: +The Universal Chess Interface (UCI) is a standard protocol used to communicate with +a chess engine, and is the recommended way to do so for typical graphical user interfaces +(GUI) or chess tools. Stockfish implements the majority of it options as described +in [the UCI protocol](https://www.shredderchess.com/download/div/uci.zip). + +Developers can see the default values for UCI options available in Stockfish by typing +`./stockfish uci` in a terminal, but the majority of users will typically see them and +change them via a chess GUI. This is a list of available UCI options in Stockfish: * #### Threads The number of CPU threads used for searching a position. For best performance, set diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index 390f61c35ad..75ac7862791 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -148,7 +148,7 @@ namespace Stockfish::Eval::NNUE { out[i] = read_little_endian(stream); } - // write_little_endian(s, out, N) : write integers in bulk to a little indian stream. + // write_little_endian(s, values, N) : write integers in bulk to a little indian stream. // This takes N integers from array values and writes them on stream s. template inline void write_little_endian(std::ostream& stream, const IntType* values, std::size_t count) { From e1f181ee643dcaa92c606b74b3abd23dede136cd Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sat, 29 May 2021 06:39:14 +0300 Subject: [PATCH 0595/1766] Do less LMR extensions This patch restricts LMR extensions (of non-transposition table moves) from being used when the transposition table move was extended by two plies via singular extension. This may serve to limit search explosions in certain positions. This makes a lot of sense because the precondition for the tt-move to have been singular extended by two plies is that the result of the alternate search (with excluded the tt-move) has been a hard fail low: it is natural to later search less for non tt-moves in this situation. The current state of depth/extensions/reductions management is getting quite tricky in our search algo, see https://github.com/official-stockfish/Stockfish/pull/3546#issuecomment-860174549 for some discussion. Suggestions welcome! Passed STC https://tests.stockfishchess.org/tests/view/60c3f293457376eb8bcaac8d LLR: 2.95 (-2.94,2.94) <-0.50,2.50> Total: 117984 W: 9698 L: 9430 D: 98856 Ptnml(0-2): 315, 7708, 42703, 7926, 340 passed LTC https://tests.stockfishchess.org/tests/view/60c46ea5457376eb8bcaacc7 LLR: 2.97 (-2.94,2.94) <0.50,3.50> Total: 11280 W: 401 L: 302 D: 10577 Ptnml(0-2): 2, 271, 4998, 364, 5 closes https://github.com/official-stockfish/Stockfish/pull/3546 Bench: 4709974 --- src/search.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index d04898e8570..b8756d38139 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -954,6 +954,7 @@ namespace { value = bestValue; singularQuietLMR = moveCountPruning = false; + bool doubleExtension = false; // Indicate PvNodes that will probably fail low if the node was searched // at a depth equal or greater than the current depth, and the result of this search was a fail low. @@ -1080,7 +1081,10 @@ namespace { if ( !PvNode && value < singularBeta - 93 && ss->doubleExtensions < 3) + { extension = 2; + doubleExtension = true; + } } // Multi-cut pruning @@ -1188,8 +1192,8 @@ namespace { // In general we want to cap the LMR depth search at newDepth. But if // reductions are really negative and movecount is low, we allow this move - // to be searched deeper than the first move. - Depth d = std::clamp(newDepth - r, 1, newDepth + (r < -1 && moveCount <= 5)); + // to be searched deeper than the first move, unless ttMove was extended by 2. + Depth d = std::clamp(newDepth - r, 1, newDepth + (r < -1 && moveCount <= 5 && !doubleExtension)); value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); From ce4c523ad3defce2d19342d2dd1b43858ec33819 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Thu, 10 Jun 2021 17:43:42 +0200 Subject: [PATCH 0596/1766] Register count for feature transformer Compute optimal register count for feature transformer accumulation dynamically. This also introduces a change where AVX512 would only use 8 registers instead of 16 (now possible due to a 2x increase in feature transformer size). closes https://github.com/official-stockfish/Stockfish/pull/3543 No functional change --- src/nnue/nnue_feature_transformer.h | 74 +++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 15 deletions(-) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 300ce3671c2..ab05f8842a5 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -28,12 +28,17 @@ namespace Stockfish::Eval::NNUE { + using BiasType = std::int16_t; + using WeightType = std::int16_t; + using PSQTWeightType = std::int32_t; + // If vector instructions are enabled, we update and refresh the // accumulator tile by tile such that each tile fits in the CPU's // vector registers. #define VECTOR - static_assert(PSQTBuckets == 8, "Assumed by the current choice of constants."); + static_assert(PSQTBuckets % 8 == 0, + "Per feature PSQT values cannot be processed at granularity lower than 8 at a time."); #ifdef USE_AVX512 typedef __m512i vec_t; @@ -47,8 +52,7 @@ namespace Stockfish::Eval::NNUE { #define vec_add_psqt_32(a,b) _mm256_add_epi32(a,b) #define vec_sub_psqt_32(a,b) _mm256_sub_epi32(a,b) #define vec_zero_psqt() _mm256_setzero_si256() - static constexpr IndexType NumRegs = 8; // only 8 are needed - static constexpr IndexType NumPsqtRegs = 1; + #define NumRegistersSIMD 32 #elif USE_AVX2 typedef __m256i vec_t; @@ -62,8 +66,7 @@ namespace Stockfish::Eval::NNUE { #define vec_add_psqt_32(a,b) _mm256_add_epi32(a,b) #define vec_sub_psqt_32(a,b) _mm256_sub_epi32(a,b) #define vec_zero_psqt() _mm256_setzero_si256() - static constexpr IndexType NumRegs = 16; - static constexpr IndexType NumPsqtRegs = 1; + #define NumRegistersSIMD 16 #elif USE_SSE2 typedef __m128i vec_t; @@ -77,8 +80,7 @@ namespace Stockfish::Eval::NNUE { #define vec_add_psqt_32(a,b) _mm_add_epi32(a,b) #define vec_sub_psqt_32(a,b) _mm_sub_epi32(a,b) #define vec_zero_psqt() _mm_setzero_si128() - static constexpr IndexType NumRegs = Is64Bit ? 16 : 8; - static constexpr IndexType NumPsqtRegs = 2; + #define NumRegistersSIMD (Is64Bit ? 16 : 8) #elif USE_MMX typedef __m64 vec_t; @@ -92,8 +94,7 @@ namespace Stockfish::Eval::NNUE { #define vec_add_psqt_32(a,b) _mm_add_pi32(a,b) #define vec_sub_psqt_32(a,b) _mm_sub_pi32(a,b) #define vec_zero_psqt() _mm_setzero_si64() - static constexpr IndexType NumRegs = 8; - static constexpr IndexType NumPsqtRegs = 4; + #define NumRegistersSIMD 8 #elif USE_NEON typedef int16x8_t vec_t; @@ -107,14 +108,61 @@ namespace Stockfish::Eval::NNUE { #define vec_add_psqt_32(a,b) vaddq_s32(a,b) #define vec_sub_psqt_32(a,b) vsubq_s32(a,b) #define vec_zero_psqt() psqt_vec_t{0} - static constexpr IndexType NumRegs = 16; - static constexpr IndexType NumPsqtRegs = 2; + #define NumRegistersSIMD 16 #else #undef VECTOR #endif + + #ifdef VECTOR + + // Compute optimal SIMD register count for feature transformer accumulation. + + // We use __m* types as template arguments, which causes GCC to emit warnings + // about losing some attribute information. This is irrelevant to us as we + // only take their size, so the following pragma are harmless. + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wignored-attributes" + + template + static constexpr int BestRegisterCount() + { + #define RegisterSize sizeof(SIMDRegisterType) + #define LaneSize sizeof(LaneType) + + static_assert(RegisterSize >= LaneSize); + static_assert(MaxRegisters <= NumRegistersSIMD); + static_assert(MaxRegisters > 0); + static_assert(NumRegistersSIMD > 0); + static_assert(RegisterSize % LaneSize == 0); + static_assert((NumLanes * LaneSize) % RegisterSize == 0); + + const int ideal = (NumLanes * LaneSize) / RegisterSize; + if (ideal <= MaxRegisters) + return ideal; + + // Look for the largest divisor of the ideal register count that is smaller than MaxRegisters + for (int divisor = MaxRegisters; divisor > 1; --divisor) + if (ideal % divisor == 0) + return divisor; + + return 1; + } + + static constexpr int NumRegs = BestRegisterCount(); + static constexpr int NumPsqtRegs = BestRegisterCount(); + + #pragma GCC diagnostic pop + + #endif + + + // Input feature converter class FeatureTransformer { @@ -557,10 +605,6 @@ namespace Stockfish::Eval::NNUE { #endif } - using BiasType = std::int16_t; - using WeightType = std::int16_t; - using PSQTWeightType = std::int32_t; - alignas(CacheLineSize) BiasType biases[HalfDimensions]; alignas(CacheLineSize) WeightType weights[HalfDimensions * InputDimensions]; alignas(CacheLineSize) PSQTWeightType psqtWeights[InputDimensions * PSQTBuckets]; From f8c779dbe538315aa6f65556d0acf11640558504 Mon Sep 17 00:00:00 2001 From: JWmer Date: Sun, 13 Jun 2021 23:48:32 +0200 Subject: [PATCH 0597/1766] Update default net to nn-8e47cf062333.nnue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This net is the result of training on data used by the Leela project. More precisely, we shuffled T60 and T74 data kindly provided by borg (for different Tnn, the data is a result of Leela selfplay with differently sized Leela nets). The data is available at vondele's google drive: https://drive.google.com/drive/folders/1mftuzYdl9o6tBaceR3d_VBQIrgKJsFpl. The Leela data comes in small chunks of .binpack files. To shuffle them, we simply used a small python script to randomly rename the files, and then concatenated them using `cat`. As validation data we picked a file of T60 data. We will further investigate T74 data. The training for the NNUE architecture used 200 epochs with the Python trainer from the Stockfish project. Unlike the previous run we tried with this data, this run does not have adjusted scaling — not because we didn't want to, but because we forgot. However, this training randomly skips 40% more positions than previous run. The loss was very spiky and decreased slower than it does usually. Training loss: https://github.com/official-stockfish/images/blob/main/training-loss-8e47cf062333.png Validation loss: https://github.com/official-stockfish/images/blob/main/validation-loss-8e47cf062333.png This is the exact training command: python train.py --smart-fen-skipping --random-fen-skipping 14 --batch-size 16384 --threads 4 --num-workers 4 --gpus 1 trainingdata\training_data.binpack validationdata\val.binpack --- 10k STC result: ELO: 3.61 +-3.3 (95%) LOS: 98.4% Total: 10000 W: 1241 L: 1137 D: 7622 Ptnml(0-2): 68, 841, 3086, 929, 76 https://tests.stockfishchess.org/tests/view/60c67e50457376eb8bcaae70 10k LTC result: ELO: 2.71 +-2.4 (95%) LOS: 98.8% Total: 10000 W: 659 L: 581 D: 8760 Ptnml(0-2): 22, 485, 3900, 579, 14 https://tests.stockfishchess.org/tests/view/60c69deb457376eb8bcaae98 Passed LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 9648 W: 685 L: 545 D: 8418 Ptnml(0-2): 22, 448, 3740, 596, 18 https://tests.stockfishchess.org/tests/view/60c6d41c457376eb8bcaaecf --- closes https://github.com/official-stockfish/Stockfish/pull/3550 Bench: 4877339 --- AUTHORS | 3 ++- src/evaluate.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index 7165363f08e..7e63591a3b8 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,4 +1,4 @@ -# List of authors for Stockfish, as of May 17, 2021 +# List of authors for Stockfish, as of June 14, 2021 # Founders of the Stockfish project and fishtest infrastructure Tord Romstad (romstad) @@ -96,6 +96,7 @@ Joost VandeVondele (vondele) Jörg Oster (joergoster) Joseph Ellis (jhellis3) Joseph R. Prostko +Julian Willemer (NightlyKing) jundery Justin Blanchard (UncombedCoconut) Kelly Wilson diff --git a/src/evaluate.h b/src/evaluate.h index 725d5a78b26..c19b14ba8ae 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-7e66505906a6.nnue" + #define EvalFileDefaultName "nn-8e47cf062333.nnue" namespace NNUE { From 900f249f596417b35129f1dc357cd5392018e575 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Sat, 12 Jun 2021 20:45:14 +0200 Subject: [PATCH 0598/1766] Reduce the number of accumulator states Reduce from 3 to 2. Make the intent of the states clearer. STC: https://tests.stockfishchess.org/tests/view/60c50111457376eb8bcaad03 LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 61888 W: 5007 L: 4944 D: 51937 Ptnml(0-2): 164, 3947, 22649, 4030, 154 LTC: https://tests.stockfishchess.org/tests/view/60c52b1c457376eb8bcaad2c LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 20248 W: 688 L: 618 D: 18942 Ptnml(0-2): 7, 551, 8946, 605, 15 closes https://github.com/official-stockfish/Stockfish/pull/3548 No functional change. --- src/nnue/nnue_accumulator.h | 5 +---- src/nnue/nnue_feature_transformer.h | 10 +++++----- src/position.cpp | 10 ++++------ 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index e24902c4c68..d41ecf95b17 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -25,14 +25,11 @@ namespace Stockfish::Eval::NNUE { - // The accumulator of a StateInfo without parent is set to the INIT state - enum AccumulatorState { EMPTY, COMPUTED, INIT }; - // Class that holds the result of affine transformation of input features struct alignas(CacheLineSize) Accumulator { std::int16_t accumulation[2][TransformedFeatureDimensions]; std::int32_t psqtAccumulation[2][PSQTBuckets]; - AccumulatorState state[2]; + bool computed[2]; }; } // namespace Stockfish::Eval::NNUE diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index ab05f8842a5..59a965ac769 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -383,7 +383,7 @@ namespace Stockfish::Eval::NNUE { // of the estimated gain in terms of features to be added/subtracted. StateInfo *st = pos.state(), *next = nullptr; int gain = FeatureSet::refresh_cost(pos); - while (st->accumulator.state[perspective] == EMPTY) + while (st->previous && !st->accumulator.computed[perspective]) { // This governs when a full feature refresh is needed and how many // updates are better than just one full refresh. @@ -394,7 +394,7 @@ namespace Stockfish::Eval::NNUE { st = st->previous; } - if (st->accumulator.state[perspective] == COMPUTED) + if (st->accumulator.computed[perspective]) { if (next == nullptr) return; @@ -412,8 +412,8 @@ namespace Stockfish::Eval::NNUE { ksq, st2, perspective, removed[1], added[1]); // Mark the accumulators as computed. - next->accumulator.state[perspective] = COMPUTED; - pos.state()->accumulator.state[perspective] = COMPUTED; + next->accumulator.computed[perspective] = true; + pos.state()->accumulator.computed[perspective] = true; // Now update the accumulators listed in states_to_update[], where the last element is a sentinel. StateInfo *states_to_update[3] = @@ -533,7 +533,7 @@ namespace Stockfish::Eval::NNUE { { // Refresh the accumulator auto& accumulator = pos.state()->accumulator; - accumulator.state[perspective] = COMPUTED; + accumulator.computed[perspective] = true; IndexList active; FeatureSet::append_active_indices(pos, perspective, active); diff --git a/src/position.cpp b/src/position.cpp index 56cb34e8ec3..ba015d3c38e 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -282,8 +282,6 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th chess960 = isChess960; thisThread = th; set_state(st); - st->accumulator.state[WHITE] = Eval::NNUE::INIT; - st->accumulator.state[BLACK] = Eval::NNUE::INIT; assert(pos_is_ok()); @@ -704,8 +702,8 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { ++st->pliesFromNull; // Used by NNUE - st->accumulator.state[WHITE] = Eval::NNUE::EMPTY; - st->accumulator.state[BLACK] = Eval::NNUE::EMPTY; + st->accumulator.computed[WHITE] = false; + st->accumulator.computed[BLACK] = false; auto& dp = st->dirtyPiece; dp.dirty_num = 1; @@ -1005,8 +1003,8 @@ void Position::do_null_move(StateInfo& newSt) { st->dirtyPiece.dirty_num = 0; st->dirtyPiece.piece[0] = NO_PIECE; // Avoid checks in UpdateAccumulator() - st->accumulator.state[WHITE] = Eval::NNUE::EMPTY; - st->accumulator.state[BLACK] = Eval::NNUE::EMPTY; + st->accumulator.computed[WHITE] = false; + st->accumulator.computed[BLACK] = false; if (st->epSquare != SQ_NONE) { From 4c4e104cadceae678668f1738b5ca6296b2b4ef8 Mon Sep 17 00:00:00 2001 From: "J. Oster" Date: Mon, 14 Jun 2021 17:28:30 +0200 Subject: [PATCH 0599/1766] Fix a rare case of wrong TB ranking of a root move leading to a 3-fold repetition. With this small fix a draw ranking and thus a draw score is being applied. This works for both, ranking by dtz or wdl tables. Fixes https://github.com/official-stockfish/Stockfish/issues/3542 (No functional change without TBs.) Bench: 4877339 --- src/syzygy/tbprobe.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index a0ac727f600..96b2970f245 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1536,6 +1536,14 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves) { WDLScore wdl = -probe_wdl(pos, &result); dtz = dtz_before_zeroing(wdl); } + else if (pos.is_draw(1)) + { + // In case a root move leads to a draw by repetition or + // 50-move rule, we set dtz to zero. Note: since we are + // only 1 ply from the root, this must be a true 3-fold + // repetition inside the game history. + dtz = 0; + } else { // Otherwise, take dtz for the new position and correct by 1 ply @@ -1586,6 +1594,7 @@ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves) { ProbeState result; StateInfo st; + WDLScore wdl; bool rule50 = Options["Syzygy50MoveRule"]; @@ -1594,7 +1603,10 @@ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves) { { pos.do_move(m.pv[0], st); - WDLScore wdl = -probe_wdl(pos, &result); + if (pos.is_draw(1)) + wdl = WDLDraw; + else + wdl = -probe_wdl(pos, &result); pos.undo_move(m.pv[0]); From 8ec9e108664ce38fa98ccfb69f048d7d804f99f9 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Tue, 15 Jun 2021 12:49:23 +0200 Subject: [PATCH 0600/1766] New default net nn-33c9d39e5eb6.nnue As the previous net, this net is trained on Leela games as provided by borg. See also https://lczero.org/blog/2021/06/the-importance-of-open-data/ The particular data set, which is a mix of T60 and T74 data, is now available as a single binpack: https://drive.google.com/file/d/1RFkQES3DpsiJqsOtUshENtzPfFgUmEff/view?usp=sharing The training command was: python train.py ../../training_data_pylon.binpack ../../training_data_pylon.binpack --gpus 1 --threads 2 --num-workers 2 --batch-size 16384 --progress_bar_refresh_rate 300 --smart-fen-skipping --random-fen-skipping 10 --features=HalfKAv2^ --lambda=1.0 --max_epochs=440 --seed $RANDOM --default_root_dir exp/run_2 passed STC: https://tests.stockfishchess.org/tests/view/60c887cb457376eb8bcab054 LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 12792 W: 1483 L: 1311 D: 9998 Ptnml(0-2): 62, 989, 4131, 1143, 71 passed LTC: https://tests.stockfishchess.org/tests/view/60c8e5c4457376eb8bcab0f0 LLR: 2.95 (-2.94,2.94) <0.50,3.50> Total: 11272 W: 601 L: 477 D: 10194 Ptnml(0-2): 9, 421, 4657, 535, 14 also had strong LTC performance against another strong net of the series: https://tests.stockfishchess.org/tests/view/60c8c40d457376eb8bcab0c6 closes https://github.com/official-stockfish/Stockfish/pull/3557 Bench: 5032320 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index c19b14ba8ae..ef0b1040599 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-8e47cf062333.nnue" + #define EvalFileDefaultName "nn-33c9d39e5eb6.nnue" namespace NNUE { From 68bf362ea2385a641be9f5ed9ce2acdf55a1ecf1 Mon Sep 17 00:00:00 2001 From: proukornew Date: Fri, 14 May 2021 00:49:28 +0300 Subject: [PATCH 0601/1766] Fix for Cygwin's environment build-profile The Cygwin environment has two g++ compilers, each with a different problem for compiling Stockfish at the moment: (a) g++.exe : full posix build compiler, linked to cygwin dll. => This one has a problem embedding the net. (b) x86_64-w64-mingw32-g++.exe : native Windows build compiler. => This one manages to embed the net, but has a problem related to libgcov when we use the profile-build target of Stockfish. This patch solves the problem for compiler (b), so that our recommended command line if you want to build an optimized version of Stockfish on Cygwin becomes something like the following (you can change the ARCH value to whatever you want, but note the COMP and CXX variables pointing at the right compiler): ``` make -j profile-build ARCH=x86-64-modern COMP=mingw CXX=x86_64-w64-mingw32-c++.exe ``` closes https://github.com/official-stockfish/Stockfish/pull/3463 No functional change --- AUTHORS | 1 + src/Makefile | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index 7e63591a3b8..1334844fc6b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -23,6 +23,7 @@ Alfredo Menezes (lonfom169) Ali AlZhrani (Cooffe) Andrew Grant (AndyGrant) Andrey Neporada (nepal) +Andreï Vetrov (proukornew) Andy Duplain Antoine Champion (antoinechampion) Aram Tumanian (atumanian) diff --git a/src/Makefile b/src/Makefile index 33a270fe19f..9508e18be46 100644 --- a/src/Makefile +++ b/src/Makefile @@ -884,13 +884,13 @@ clang-profile-use: gcc-profile-make: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ - EXTRACXXFLAGS='-fprofile-generate' \ + EXTRACXXFLAGS='-fprofile-generate="./"' \ EXTRALDFLAGS='-lgcov' \ all gcc-profile-use: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ - EXTRACXXFLAGS='-fprofile-use -fno-peel-loops -fno-tracer' \ + EXTRACXXFLAGS='-fprofile-use="./" -fno-peel-loops -fno-tracer' \ EXTRALDFLAGS='-lgcov' \ all From 55e69dc88d84b627af938fe2a1ee69326db055d2 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Tue, 15 Jun 2021 20:56:09 -0300 Subject: [PATCH 0602/1766] Simplify reduction when best move doesn't change frequently. STC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 40400 W: 3468 L: 3377 D: 33555 Ptnml(0-2): 134, 2734, 14388, 2795, 149 https://tests.stockfishchess.org/tests/view/60c93e5a457376eb8bcab15f LTC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 34200 W: 1190 L: 1128 D: 31882 Ptnml(0-2): 22, 998, 15001, 1054, 25 https://tests.stockfishchess.org/tests/view/60c96a1a457376eb8bcab180 closes https://github.com/official-stockfish/Stockfish/pull/3559 bench: 5629669 --- src/search.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index b8756d38139..9bba563a779 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1157,7 +1157,6 @@ namespace { // Increase reduction at root and non-PV nodes when the best move does not change frequently if ( (rootNode || !PvNode) - && thisThread->rootDepth > 10 && thisThread->bestMoveChanges <= 2) r++; From 07c84480342ec156b1cbb651966521349ccfa6ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Thu, 17 Jun 2021 18:09:42 +0200 Subject: [PATCH 0603/1766] Revert "Fix for Cygwin's environment build-profile" This reverts commit "Fix for Cygwin's environment build-profile", as it was giving errors for "make clean" on some Windows environments. See comments in https://github.com/official-stockfish/Stockfish/commit/68bf362ea2385a641be9f5ed9ce2acdf55a1ecf1 Possibly somebody can propose a solution that would fix Cygwin builds and not break on other system too, stay tuned! :-) No functional change --- AUTHORS | 1 - src/Makefile | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index 1334844fc6b..7e63591a3b8 100644 --- a/AUTHORS +++ b/AUTHORS @@ -23,7 +23,6 @@ Alfredo Menezes (lonfom169) Ali AlZhrani (Cooffe) Andrew Grant (AndyGrant) Andrey Neporada (nepal) -Andreï Vetrov (proukornew) Andy Duplain Antoine Champion (antoinechampion) Aram Tumanian (atumanian) diff --git a/src/Makefile b/src/Makefile index 9508e18be46..33a270fe19f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -884,13 +884,13 @@ clang-profile-use: gcc-profile-make: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ - EXTRACXXFLAGS='-fprofile-generate="./"' \ + EXTRACXXFLAGS='-fprofile-generate' \ EXTRALDFLAGS='-lgcov' \ all gcc-profile-use: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ - EXTRACXXFLAGS='-fprofile-use="./" -fno-peel-loops -fno-tracer' \ + EXTRACXXFLAGS='-fprofile-use -fno-peel-loops -fno-tracer' \ EXTRALDFLAGS='-lgcov' \ all From 14b673d90f7f486becda4622eb1e8f4b63bb0785 Mon Sep 17 00:00:00 2001 From: ap Date: Fri, 18 Jun 2021 01:43:58 +0200 Subject: [PATCH 0604/1766] New default net nn-3b20abec10c1.nnue This net was created by @pleomati, who manually edited with an hex editor 10 values randomly chosen in the LCSFNet10 net (nn-6ad41a9207d0.nnue) to create this one. The LCSFNet10 net was trained by Joost VandeVondele from a dataset combining Stockfish games and Leela games (16x10^9 positions from SF self-play at depth 9, and 6.3x10^9 positions from Leela games, so overall 72% of Stockfish positions and 28% of Leela positions). passed STC 10+0.1: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 50888 W: 5881 L: 5654 D: 39353 Ptnml(0-2): 281, 4290, 16085, 4497, 291 https://tests.stockfishchess.org/tests/view/60cbfa68457376eb8bcab49a passed LTC 60+0.6: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 25480 W: 1498 L: 1338 D: 22644 Ptnml(0-2): 36, 1155, 10193, 1325, 31 https://tests.stockfishchess.org/tests/view/60cc4af8457376eb8bcab4d4 closes https://github.com/official-stockfish/Stockfish/pull/3564 Bench: 4904930 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index ef0b1040599..0e815d6bf57 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-33c9d39e5eb6.nnue" + #define EvalFileDefaultName "nn-3b20abec10c1.nnue" namespace NNUE { From 86afb6a7cf33640a4d7bb7dbb2a8203450e34938 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Sat, 19 Jun 2021 03:09:20 +0800 Subject: [PATCH 0605/1766] Update default net to nn-aa9d7eeb397e.nnue Optimization of vondele's nn-33c9d39e5eb6.nnue using SPSA https://tests.stockfishchess.org/tests/view/60ca68be457376eb8bcab28b Setting: ck values are default based on how large the parameters are The new values for this net are the raw values at the end of the tuning (80k games) The significant changes are in buckets 1 and 2 (5-12 pieces) so the main difference is in playing endgames if we compare it to nn-33c9. There is also change in bucket 7 (29-32 pieces) but not as substantial as the changes in buckets 1 and 2. If we interpret the changes based on an experiment a few months ago, this new net plays more optimistically during endgames and less optimistically during openings. STC: LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 49504 W: 4246 L: 4053 D: 41205 Ptnml(0-2): 140, 3282, 17749, 3407, 174 https://tests.stockfishchess.org/tests/view/60cbd752457376eb8bcab478 LTC: LLR: 2.95 (-2.94,2.94) <0.50,3.50> Total: 88720 W: 4926 L: 4651 D: 79143 Ptnml(0-2): 105, 4048, 35793, 4295, 119 https://tests.stockfishchess.org/tests/view/60cc7828457376eb8bcab4fa closes https://github.com/official-stockfish/Stockfish/pull/3566 Bench: 4758885 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 0e815d6bf57..10298882102 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-3b20abec10c1.nnue" + #define EvalFileDefaultName "nn-aa9d7eeb397e.nnue" namespace NNUE { From 07e6ceacd623402a8b83c834e95db77efaad3782 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Fri, 18 Jun 2021 12:03:03 +0200 Subject: [PATCH 0606/1766] Add basic github workflow move to github actions to replace travis CI. First version, testing on linux using gcc and clang. gcc build with sanitizers and valgrind. No functional change --- .github/workflows/stockfish.yml | 201 ++++++++++++++++++++++++++++++++ .travis.yml | 101 ---------------- README.md | 2 +- tests/instrumented.sh | 2 +- 4 files changed, 203 insertions(+), 103 deletions(-) create mode 100644 .github/workflows/stockfish.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml new file mode 100644 index 00000000000..8970fcd124a --- /dev/null +++ b/.github/workflows/stockfish.yml @@ -0,0 +1,201 @@ +name: Stockfish +on: + push: + branches: + - master + - tools + - github_ci + pull_request: + branches: + - master + - tools +jobs: + Stockfish: + name: ${{ matrix.config.name }} + runs-on: ${{ matrix.config.os }} + env: + COMPILER: ${{ matrix.config.compiler }} + COMP: ${{ matrix.config.comp }} + CXXFLAGS: "-Werror" + strategy: + matrix: + config: + - { + name: "Ubuntu 20.04 GCC", + os: ubuntu-20.04, + compiler: g++, + comp: gcc, + run_expensive_tests: true + } + - { + name: "Ubuntu 20.04 Clang", + os: ubuntu-20.04, + compiler: clang++, + comp: clang, + run_expensive_tests: false + } + + defaults: + run: + working-directory: src + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Download required packages + run: | + sudo apt update + sudo apt install expect valgrind g++-multilib + + - name: Download the used network from the fishtest framework + run: | + make net + + - name: Extract the bench number from the commit history + run: | + git log HEAD | grep "\b[Bb]ench[ :]\+[0-9]\{7\}" | head -n 1 | sed "s/[^0-9]*\([0-9]*\).*/\1/g" > git_sig + [ -s git_sig ] && echo "benchref=$(cat git_sig)" >> $GITHUB_ENV && echo "Reference bench:" $(cat git_sig) || echo "No bench found" + + - name: Check compiler + run: | + $COMPILER -v + + - name: Test help target + run: | + make help + + # x86-32 tests + + - name: Test debug x86-32 build + run: | + export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" + make clean + make -j2 ARCH=x86-32 optimize=no debug=yes build + ../tests/signature.sh $benchref + + - name: Test x86-32 build + run: | + make clean + make -j2 ARCH=x86-32 build + ../tests/signature.sh $benchref + + - name: Test x86-32-sse41-popcnt build + run: | + make clean + make -j2 ARCH=x86-32-sse41-popcnt build + ../tests/signature.sh $benchref + + - name: Test x86-32-sse2 build + run: | + make clean + make -j2 ARCH=x86-32-sse2 build + ../tests/signature.sh $benchref + + - name: Test general-32 build + run: | + make clean + make -j2 ARCH=general-32 build + ../tests/signature.sh $benchref + + # x86-64 tests + + - name: Test debug x86-64-modern build + run: | + export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" + make clean + make -j2 ARCH=x86-64-modern optimize=no debug=yes build + ../tests/signature.sh $benchref + + - name: Test x86-64-modern build + run: | + make clean + make -j2 ARCH=x86-64-modern build + ../tests/signature.sh $benchref + + - name: Test x86-64-ssse3 build + run: | + make clean + make -j2 ARCH=x86-64-ssse3 build + ../tests/signature.sh $benchref + + - name: Test x86-64-sse3-popcnt build + run: | + make clean + make -j2 ARCH=x86-64-sse3-popcnt build + ../tests/signature.sh $benchref + + - name: Test x86-64 build + run: | + make clean + make -j2 ARCH=x86-64 build + ../tests/signature.sh $benchref + + - name: Test general-64 build + run: | + make clean + make -j2 ARCH=general-64 build + ../tests/signature.sh $benchref + + # x86-64 with newer extensions tests + + - name: Compile x86-64-avx2 build + run: | + make clean + make -j2 ARCH=x86-64-avx2 build + + - name: Compile x86-64-bmi2 build + run: | + make clean + make -j2 ARCH=x86-64-bmi2 build + + - name: Compile x86-64-avx512 build + run: | + make clean + make -j2 ARCH=x86-64-avx512 build + + - name: Compile x86-64-vnni512 build + run: | + make clean + make -j2 ARCH=x86-64-vnni512 build + + - name: Compile x86-64-vnni256 build + run: | + make clean + make -j2 ARCH=x86-64-vnni256 build + + # Other tests + + - name: Check perft and search reproducibility + run: | + make clean + make -j2 ARCH=x86-64-modern build + ../tests/perft.sh + ../tests/reprosearch.sh + + # Sanitizers + + - name: Run under valgrind + if: ${{ matrix.config.run_expensive_tests }} + run: | + export CXXFLAGS="-O1 -fno-inline" + make clean + make -j2 ARCH=x86-64-modern debug=yes optimize=no build > /dev/null + ../tests/instrumented.sh --valgrind + ../tests/instrumented.sh --valgrind-thread + + - name: Run with UB sanitizer + if: ${{ matrix.config.run_expensive_tests }} + run: | + export CXXFLAGS="-O1 -fno-inline" + make clean + make -j2 ARCH=x86-64-modern sanitize=undefined optimize=no debug=yes build > /dev/null + ../tests/instrumented.sh --sanitizer-undefined + + - name: Run with thread sanitizer + if: ${{ matrix.config.run_expensive_tests }} + run: | + export CXXFLAGS="-O1 -fno-inline" + make clean + make -j2 ARCH=x86-64-modern sanitize=thread optimize=no debug=yes build > /dev/null + ../tests/instrumented.sh --sanitizer-thread diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 092c7f53d31..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,101 +0,0 @@ -language: cpp -dist: bionic - -matrix: - include: - - os: linux - compiler: gcc - addons: - apt: - packages: ['g++-8', 'g++-8-multilib', 'g++-multilib', 'valgrind', 'expect', 'curl'] - env: - - COMPILER=g++-8 - - COMP=gcc - - - os: linux - compiler: clang - addons: - apt: - packages: ['clang-10', 'llvm-10-dev', 'g++-multilib', 'valgrind', 'expect', 'curl'] - env: - - COMPILER=clang++-10 - - COMP=clang - - - os: osx - osx_image: xcode12 - compiler: gcc - env: - - COMPILER=g++ - - COMP=gcc - - - os: osx - osx_image: xcode12 - compiler: clang - env: - - COMPILER=clang++ - - COMP=clang - -branches: - only: - - master - -before_script: - - cd src - -script: - # Download net - - make net - - # Obtain bench reference from git log - - git log HEAD | grep "\b[Bb]ench[ :]\+[0-9]\{7\}" | head -n 1 | sed "s/[^0-9]*\([0-9]*\).*/\1/g" > git_sig - - export benchref=$(cat git_sig) - - echo "Reference bench:" $benchref - - # Compiler version string - - $COMPILER -v - - # test help target - - make help - - # Verify bench number against various builds - - export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" - - make clean && make -j2 ARCH=x86-64-modern optimize=no debug=yes build && ../tests/signature.sh $benchref - - export CXXFLAGS="-Werror" - - make clean && make -j2 ARCH=x86-64-modern build && ../tests/signature.sh $benchref - - make clean && make -j2 ARCH=x86-64-ssse3 build && ../tests/signature.sh $benchref - - make clean && make -j2 ARCH=x86-64-sse3-popcnt build && ../tests/signature.sh $benchref - - make clean && make -j2 ARCH=x86-64 build && ../tests/signature.sh $benchref - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=general-64 build && ../tests/signature.sh $benchref; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32 optimize=no debug=yes build && ../tests/signature.sh $benchref; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32-sse41-popcnt build && ../tests/signature.sh $benchref; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32-sse2 build && ../tests/signature.sh $benchref; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-32 build && ../tests/signature.sh $benchref; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=general-32 build && ../tests/signature.sh $benchref; fi - # workaround: exclude a custom version of llvm+clang, which doesn't find llvm-profdata on ubuntu - - if [[ "$TRAVIS_OS_NAME" != "linux" || "$COMP" == "gcc" ]]; then make clean && make -j2 ARCH=x86-64-modern profile-build && ../tests/signature.sh $benchref; fi - - # compile only for some more advanced architectures (might not run in travis) - - make clean && make -j2 ARCH=x86-64-avx2 build - - make clean && make -j2 ARCH=x86-64-bmi2 build - - make clean && make -j2 ARCH=x86-64-avx512 build - - make clean && make -j2 ARCH=x86-64-vnni512 build - - make clean && make -j2 ARCH=x86-64-vnni256 build - - # - # Check perft and reproducible search - - make clean && make -j2 ARCH=x86-64-modern build - - ../tests/perft.sh - - ../tests/reprosearch.sh - - # - # Valgrind - # - - export CXXFLAGS="-O1 -fno-inline" - - if [ -x "$(command -v valgrind )" ]; then make clean && make -j2 ARCH=x86-64-modern debug=yes optimize=no build > /dev/null && ../tests/instrumented.sh --valgrind; fi - - if [ -x "$(command -v valgrind )" ]; then ../tests/instrumented.sh --valgrind-thread; fi - - # - # Sanitizer - # - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-64-modern sanitize=undefined optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-undefined; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make clean && make -j2 ARCH=x86-64-modern sanitize=thread optimize=no debug=yes build > /dev/null && ../tests/instrumented.sh --sanitizer-thread; fi diff --git a/README.md b/README.md index f4ee2e3458c..0bbe4abbefb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## Overview -[![Build Status](https://travis-ci.org/official-stockfish/Stockfish.svg?branch=master)](https://travis-ci.org/official-stockfish/Stockfish) +[![Build Status](https://github.com/official-stockfish/Stockfish/actions/workflows/stockfish.yml/badge.svg)](https://github.com/official-stockfish/Stockfish/actions) [![Build Status](https://ci.appveyor.com/api/projects/status/github/official-stockfish/Stockfish?branch=master&svg=true)](https://ci.appveyor.com/project/mcostalba/stockfish/branch/master) [Stockfish](https://stockfishchess.org) is a free, powerful UCI chess engine diff --git a/tests/instrumented.sh b/tests/instrumented.sh index d30c8e35a57..e9455eabddc 100755 --- a/tests/instrumented.sh +++ b/tests/instrumented.sh @@ -98,7 +98,7 @@ cat << EOF > game.exp expect "bestmove" send "position fen 5rk1/1K4p1/8/8/3B4/8/8/8 b - - 0 1\n" - send "go depth 20\n" + send "go depth 10\n" expect "bestmove" send "quit\n" From adfb23c029e54c7522aadca1adf3e0b15fdcebcd Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 18 Jun 2021 23:50:01 +0200 Subject: [PATCH 0607/1766] Make net nn-50144f835024.nnue the default trained with the Python command c:\nnue>python train.py i:/bin/all.binpack i:/bin/all.binpack --gpus 1 --threads 4 --num-workers 30 --batch-size 16384 --progress_bar_refresh_rate 300 --smart-fen-skipping --random-fen-skipping 3 --features=HalfKAv2^ --lambda=1.0 --max_epochs=440 --seed %random%%random% --default_root_dir exp/run_8 --resume-from-model ./pt/nn-6ad41a9207d0.pt ` all.binpack equaled 4 parts Wrong_NNUE_2.binpack https://drive.google.com/file/d/1seGNOqcVdvK_vPNq98j-zV3XPE5zWAeq/view?usp=sharing plus two parts of Training_Data.binpack https://drive.google.com/file/d/1RFkQES3DpsiJqsOtUshENtzPfFgUmEff/view?usp=sharing Each set was concatenated together - make one large Wrong_NNUE 2 binpack and one large Training_Data of approximate size. They were then interleaved together. The idea was to give Wrong_NNUE.binpack closer to equal weighting with the Training _Data binpack . nn-6ad41a9207d0.pt was derived from a net vondele ran which passed STC quickly, but faltered in LTC. https://tests.stockfishchess.org/tests/view/60cba666457376eb8bcab443 STC: LLR: 2.95 (-2.94,2.94) <-0.50,2.50> Total: 18792 W: 2068 L: 1889 D: 14835 Ptnml(0-2): 82, 1480, 6117, 1611, 106 https://tests.stockfishchess.org/tests/view/60ccda8b457376eb8bcab568 LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 11376 W: 574 L: 454 D: 10348 Ptnml(0-2): 4, 412, 4747, 510, 15 https://tests.stockfishchess.org/tests/view/60ccf952457376eb8bcab58d closes https://github.com/official-stockfish/Stockfish/pull/3568 Bench: 4900906 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 10298882102..4e6bb1a6d25 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-aa9d7eeb397e.nnue" + #define EvalFileDefaultName "nn-50144f835024.nnue" namespace NNUE { From 0171b506ec96e2b8c8fcc7758cd5624ad26c0552 Mon Sep 17 00:00:00 2001 From: proukornew Date: Sat, 19 Jun 2021 00:52:46 +0300 Subject: [PATCH 0608/1766] Fix for Cygwin's environment build-profile (fixed) The Cygwin environment has two g++ compilers, each with a different problem for compiling Stockfish at the moment: (a) g++.exe : full posix build compiler, linked to cygwin dll. => This one has a problem embedding the net. (b) x86_64-w64-mingw32-g++.exe : native Windows build compiler. => This one manages to embed the net, but has a problem related to libgcov when we use the profile-build target of Stockfish. This patch solves the problem for compiler (b), so that our recommended command line if you want to build an optimized version of Stockfish on Cygwin becomes something like the following (you can change the ARCH value to whatever you want, but note the COMP and CXX variables pointing at the right compiler): ``` make -j profile-build ARCH=x86-64-modern COMP=mingw CXX=x86_64-w64-mingw32-c++.exe ``` closes https://github.com/official-stockfish/Stockfish/pull/3569 No functional change --- src/Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index 33a270fe19f..bf3c3560bbd 100644 --- a/src/Makefile +++ b/src/Makefile @@ -883,14 +883,15 @@ clang-profile-use: all gcc-profile-make: + @mkdir -p profdir $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ - EXTRACXXFLAGS='-fprofile-generate' \ + EXTRACXXFLAGS='-fprofile-generate=profdir' \ EXTRALDFLAGS='-lgcov' \ all gcc-profile-use: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ - EXTRACXXFLAGS='-fprofile-use -fno-peel-loops -fno-tracer' \ + EXTRACXXFLAGS='-fprofile-use=profdir -fno-peel-loops -fno-tracer' \ EXTRALDFLAGS='-lgcov' \ all From 2e745956c0478627c4822104fc48a079c2f15e77 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Thu, 17 Jun 2021 12:36:06 +0200 Subject: [PATCH 0609/1766] Change trace with NNUE eval support This patch adds some more output to the `eval` command. It adds a board display with estimated piece values (method is remove-piece, evaluate, put-piece), and splits the NNUE evaluation with (psqt,layers) for each bucket for the NNUE net. Example: ``` ./stockfish position fen 3Qb1k1/1r2ppb1/pN1n2q1/Pp1Pp1Pr/4P2p/4BP2/4B1R1/1R5K b - - 11 40 eval Contributing terms for the classical eval: +------------+-------------+-------------+-------------+ | Term | White | Black | Total | | | MG EG | MG EG | MG EG | +------------+-------------+-------------+-------------+ | Material | ---- ---- | ---- ---- | -0.73 -1.55 | | Imbalance | ---- ---- | ---- ---- | -0.21 -0.17 | | Pawns | 0.35 -0.00 | 0.19 -0.26 | 0.16 0.25 | | Knights | 0.04 -0.08 | 0.12 -0.01 | -0.08 -0.07 | | Bishops | -0.34 -0.87 | -0.17 -0.61 | -0.17 -0.26 | | Rooks | 0.12 0.00 | 0.08 0.00 | 0.04 0.00 | | Queens | 0.00 0.00 | -0.27 -0.07 | 0.27 0.07 | | Mobility | 0.84 1.76 | 0.01 0.66 | 0.83 1.10 | |King safety | -0.99 -0.17 | -0.72 -0.10 | -0.27 -0.07 | | Threats | 0.27 0.27 | 0.73 0.86 | -0.46 -0.59 | | Passed | 0.00 0.00 | 0.79 0.82 | -0.79 -0.82 | | Space | 0.61 0.00 | 0.24 0.00 | 0.37 0.00 | | Winnable | ---- ---- | ---- ---- | 0.00 -0.03 | +------------+-------------+-------------+-------------+ | Total | ---- ---- | ---- ---- | -1.03 -2.14 | +------------+-------------+-------------+-------------+ NNUE derived piece values: +-------+-------+-------+-------+-------+-------+-------+-------+ | | | | Q | b | | k | | | | | | +12.4 | -1.62 | | | | +-------+-------+-------+-------+-------+-------+-------+-------+ | | r | | | p | p | b | | | | -3.89 | | | -0.84 | -1.19 | -3.32 | | +-------+-------+-------+-------+-------+-------+-------+-------+ | p | N | | n | | | q | | | -1.81 | +3.71 | | -4.82 | | | -5.04 | | +-------+-------+-------+-------+-------+-------+-------+-------+ | P | p | | P | p | | P | r | | +1.16 | -0.91 | | +0.55 | +0.12 | | +0.50 | -4.02 | +-------+-------+-------+-------+-------+-------+-------+-------+ | | | | | P | | | p | | | | | | +2.33 | | | +1.17 | +-------+-------+-------+-------+-------+-------+-------+-------+ | | | | | B | P | | | | | | | | +4.79 | +1.54 | | | +-------+-------+-------+-------+-------+-------+-------+-------+ | | | | | B | | R | | | | | | | +4.54 | | +6.03 | | +-------+-------+-------+-------+-------+-------+-------+-------+ | | R | | | | | | K | | | +4.81 | | | | | | | +-------+-------+-------+-------+-------+-------+-------+-------+ NNUE network contributions (Black to move) +------------+------------+------------+------------+ | Bucket | Material | Positional | Total | | | (PSQT) | (Layers) | | +------------+------------+------------+------------+ | 0 | + 0.32 | - 1.46 | - 1.13 | | 1 | + 0.25 | - 0.68 | - 0.43 | | 2 | + 0.46 | - 1.72 | - 1.25 | | 3 | + 0.55 | - 1.80 | - 1.25 | | 4 | + 0.48 | - 1.77 | - 1.29 | | 5 | + 0.40 | - 2.00 | - 1.60 | | 6 | + 0.57 | - 2.12 | - 1.54 | <-- this bucket is used | 7 | + 3.38 | - 2.00 | + 1.37 | +------------+------------+------------+------------+ Classical evaluation -1.00 (white side) NNUE evaluation +1.54 (white side) Final evaluation +2.38 (white side) [with scaled NNUE, hybrid, ...] ``` Also renames the export_net() function to save_eval() while there. closes https://github.com/official-stockfish/Stockfish/pull/3562 No functional change --- src/evaluate.cpp | 83 +++++-------- src/evaluate.h | 11 +- src/nnue/evaluate_nnue.cpp | 248 +++++++++++++++++++++++++++++++++++++ src/position.h | 7 +- src/uci.cpp | 10 +- 5 files changed, 298 insertions(+), 61 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 0e66fea62b2..fe1366147f6 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -114,30 +114,6 @@ namespace Eval { } } - /// NNUE::export_net() exports the currently loaded network to a file - void NNUE::export_net(const std::optional& filename) { - std::string actualFilename; - - if (filename.has_value()) - actualFilename = filename.value(); - else - { - if (eval_file_loaded != EvalFileDefaultName) - { - sync_cout << "Failed to export a net. A non-embedded net can only be saved if the filename is specified." << sync_endl; - return; - } - actualFilename = EvalFileDefaultName; - } - - ofstream stream(actualFilename, std::ios_base::binary); - - if (save_eval(stream)) - sync_cout << "Network saved successfully to " << actualFilename << "." << sync_endl; - else - sync_cout << "Failed to export a net." << sync_endl; - } - /// NNUE::verify() verifies that the last net used was loaded successfully void NNUE::verify() { @@ -204,7 +180,7 @@ namespace Trace { else os << scores[t][WHITE] << " | " << scores[t][BLACK]; - os << " | " << scores[t][WHITE] - scores[t][BLACK] << "\n"; + os << " | " << scores[t][WHITE] - scores[t][BLACK] << " |\n"; return os; } } @@ -1150,7 +1126,7 @@ Value Eval::evaluate(const Position& pos) { /// descriptions and values of each evaluation term. Useful for debugging. /// Trace scores are from white's point of view -std::string Eval::trace(const Position& pos) { +std::string Eval::trace(Position& pos) { if (pos.checkers()) return "Final evaluation: none (in check)"; @@ -1167,39 +1143,48 @@ std::string Eval::trace(const Position& pos) { v = Evaluation(pos).value(); ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2) - << " Term | White | Black | Total \n" - << " | MG EG | MG EG | MG EG \n" - << " ------------+-------------+-------------+------------\n" - << " Material | " << Term(MATERIAL) - << " Imbalance | " << Term(IMBALANCE) - << " Pawns | " << Term(PAWN) - << " Knights | " << Term(KNIGHT) - << " Bishops | " << Term(BISHOP) - << " Rooks | " << Term(ROOK) - << " Queens | " << Term(QUEEN) - << " Mobility | " << Term(MOBILITY) - << " King safety | " << Term(KING) - << " Threats | " << Term(THREAT) - << " Passed | " << Term(PASSED) - << " Space | " << Term(SPACE) - << " Winnable | " << Term(WINNABLE) - << " ------------+-------------+-------------+------------\n" - << " Total | " << Term(TOTAL); + << " Contributing terms for the classical eval:\n" + << "+------------+-------------+-------------+-------------+\n" + << "| Term | White | Black | Total |\n" + << "| | MG EG | MG EG | MG EG |\n" + << "+------------+-------------+-------------+-------------+\n" + << "| Material | " << Term(MATERIAL) + << "| Imbalance | " << Term(IMBALANCE) + << "| Pawns | " << Term(PAWN) + << "| Knights | " << Term(KNIGHT) + << "| Bishops | " << Term(BISHOP) + << "| Rooks | " << Term(ROOK) + << "| Queens | " << Term(QUEEN) + << "| Mobility | " << Term(MOBILITY) + << "|King safety | " << Term(KING) + << "| Threats | " << Term(THREAT) + << "| Passed | " << Term(PASSED) + << "| Space | " << Term(SPACE) + << "| Winnable | " << Term(WINNABLE) + << "+------------+-------------+-------------+-------------+\n" + << "| Total | " << Term(TOTAL) + << "+------------+-------------+-------------+-------------+\n"; - v = pos.side_to_move() == WHITE ? v : -v; + if (Eval::useNNUE) + ss << '\n' << NNUE::trace(pos) << '\n'; - ss << "\nClassical evaluation: " << to_cp(v) << " (white side)\n"; + ss << std::showpoint << std::showpos << std::fixed << std::setprecision(2) << std::setw(15); + v = pos.side_to_move() == WHITE ? v : -v; + ss << "\nClassical evaluation " << to_cp(v) << " (white side)\n"; if (Eval::useNNUE) { - v = NNUE::evaluate(pos); + v = NNUE::evaluate(pos, false); v = pos.side_to_move() == WHITE ? v : -v; - ss << "\nNNUE evaluation: " << to_cp(v) << " (white side)\n"; + ss << "NNUE evaluation " << to_cp(v) << " (white side)\n"; } v = evaluate(pos); v = pos.side_to_move() == WHITE ? v : -v; - ss << "\nFinal evaluation: " << to_cp(v) << " (white side)\n"; + ss << "Final evaluation " << to_cp(v) << " (white side)"; + if (Eval::useNNUE) + ss << " [with scaled NNUE, hybrid, ...]"; + ss << "\n"; return ss.str(); } diff --git a/src/evaluate.h b/src/evaluate.h index 4e6bb1a6d25..f888dd9128c 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -30,7 +30,7 @@ class Position; namespace Eval { - std::string trace(const Position& pos); + std::string trace(Position& pos); Value evaluate(const Position& pos); extern bool useNNUE; @@ -43,13 +43,16 @@ namespace Eval { namespace NNUE { + std::string trace(Position& pos); Value evaluate(const Position& pos, bool adjusted = false); - bool load_eval(std::string name, std::istream& stream); - bool save_eval(std::ostream& stream); + void init(); - void export_net(const std::optional& filename); void verify(); + bool load_eval(std::string name, std::istream& stream); + bool save_eval(std::ostream& stream); + bool save_eval(const std::optional& filename); + } // namespace NNUE } // namespace Eval diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 4a3c206b808..a918b9255ea 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -20,6 +20,9 @@ #include #include +#include +#include +#include #include "../evaluate.h" #include "../position.h" @@ -175,6 +178,220 @@ namespace Stockfish::Eval::NNUE { return static_cast( sum / OutputScale ); } + struct NnueEvalTrace { + static_assert(LayerStacks == PSQTBuckets); + + Value psqt[LayerStacks]; + Value positional[LayerStacks]; + std::size_t correctBucket; + }; + + static NnueEvalTrace trace_evaluate(const Position& pos) { + + // We manually align the arrays on the stack because with gcc < 9.3 + // overaligning stack variables with alignas() doesn't work correctly. + + constexpr uint64_t alignment = CacheLineSize; + +#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) + TransformedFeatureType transformedFeaturesUnaligned[ + FeatureTransformer::BufferSize + alignment / sizeof(TransformedFeatureType)]; + char bufferUnaligned[Network::BufferSize + alignment]; + + auto* transformedFeatures = align_ptr_up(&transformedFeaturesUnaligned[0]); + auto* buffer = align_ptr_up(&bufferUnaligned[0]); +#else + alignas(alignment) + TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize]; + alignas(alignment) char buffer[Network::BufferSize]; +#endif + + ASSERT_ALIGNED(transformedFeatures, alignment); + ASSERT_ALIGNED(buffer, alignment); + + NnueEvalTrace t{}; + t.correctBucket = (pos.count() - 1) / 4; + for (std::size_t bucket = 0; bucket < LayerStacks; ++bucket) { + const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket); + const auto output = network[bucket]->propagate(transformedFeatures, buffer); + + int materialist = psqt; + int positional = output[0]; + + t.psqt[bucket] = static_cast( materialist / OutputScale ); + t.positional[bucket] = static_cast( positional / OutputScale ); + } + + return t; + } + + static const std::string PieceToChar(" PNBRQK pnbrqk"); + + // Requires the buffer to have capacity for at least 5 values + static void format_cp_compact(Value v, char* buffer) { + + buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' '); + + int cp = (int)(std::abs(100.0 * double(v) / PawnValueEg)); + + if (cp >= 10000) + { + buffer[1] = '0' + cp / 10000; cp %= 10000; + buffer[2] = '0' + cp / 1000; cp %= 1000; + buffer[3] = '0' + cp / 100; cp %= 100; + buffer[4] = ' '; + } + else if (cp >= 1000) + { + buffer[1] = '0' + cp / 1000; cp %= 1000; + buffer[2] = '0' + cp / 100; cp %= 100; + buffer[3] = '.'; + buffer[4] = '0' + cp / 10; + } + else + { + buffer[1] = '0' + cp / 100; cp %= 100; + buffer[2] = '.'; + buffer[3] = '0' + cp / 10; cp %= 10; + buffer[4] = '0' + cp / 1; + } + } + + // Requires the buffer to have capacity for at least 7 values + static void format_cp_aligned_dot(Value v, char* buffer) { + buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' '); + + int cp = (int)(std::abs(100.0 * double(v) / PawnValueEg)); + + if (cp >= 10000) + { + buffer[1] = '0' + cp / 10000; cp %= 10000; + buffer[2] = '0' + cp / 1000; cp %= 1000; + buffer[3] = '0' + cp / 100; cp %= 100; + buffer[4] = '.'; + buffer[5] = '0' + cp / 10; cp %= 10; + buffer[6] = '0' + cp; + } + else if (cp >= 1000) + { + buffer[1] = ' '; + buffer[2] = '0' + cp / 1000; cp %= 1000; + buffer[3] = '0' + cp / 100; cp %= 100; + buffer[4] = '.'; + buffer[5] = '0' + cp / 10; cp %= 10; + buffer[6] = '0' + cp; + } + else + { + buffer[1] = ' '; + buffer[2] = ' '; + buffer[3] = '0' + cp / 100; cp %= 100; + buffer[4] = '.'; + buffer[5] = '0' + cp / 10; cp %= 10; + buffer[6] = '0' + cp / 1; + } + } + + + // trace() returns a string with the value of each piece on a board, + // and a table for (PSQT, Layers) values bucket by bucket. + + std::string trace(Position& pos) { + + std::stringstream ss; + + char board[3*8+1][8*8+2]; + std::memset(board, ' ', sizeof(board)); + for (int row = 0; row < 3*8+1; ++row) + board[row][8*8+1] = '\0'; + + // A lambda to output one box of the board + auto writeSquare = [&board](File file, Rank rank, Piece pc, Value value) { + + const int x = ((int)file) * 8; + const int y = (7 - (int)rank) * 3; + for (int i = 1; i < 8; ++i) + board[y][x+i] = board[y+3][x+i] = '-'; + for (int i = 1; i < 3; ++i) + board[y+i][x] = board[y+i][x+8] = '|'; + board[y][x] = board[y][x+8] = board[y+3][x+8] = board[y+3][x] = '+'; + if (pc != NO_PIECE) + board[y+1][x+4] = PieceToChar[pc]; + if (value != VALUE_NONE) + format_cp_compact(value, &board[y+2][x+2]); + }; + + // We estimate the value of each piece by doing a differential evaluation from + // the current base eval, simulating the removal of the piece from its square. + Value base = evaluate(pos); + base = pos.side_to_move() == WHITE ? base : -base; + + for (File f = FILE_A; f <= FILE_H; ++f) + for (Rank r = RANK_1; r <= RANK_8; ++r) + { + Square sq = make_square(f, r); + Piece pc = pos.piece_on(sq); + Value v = VALUE_NONE; + + if (pc != NO_PIECE && type_of(pc) != KING) + { + auto st = pos.state(); + + pos.remove_piece(sq); + st->accumulator.computed[WHITE] = false; + st->accumulator.computed[BLACK] = false; + + Value eval = evaluate(pos); + eval = pos.side_to_move() == WHITE ? eval : -eval; + v = base - eval; + + pos.put_piece(pc, sq); + st->accumulator.computed[WHITE] = false; + st->accumulator.computed[BLACK] = false; + } + + writeSquare(f, r, pc, v); + } + + ss << " NNUE derived piece values:\n"; + for (int row = 0; row < 3*8+1; ++row) + ss << board[row] << '\n'; + ss << '\n'; + + auto t = trace_evaluate(pos); + + ss << " NNUE network contributions " + << (pos.side_to_move() == WHITE ? "(White to move)" : "(Black to move)") << std::endl + << "+------------+------------+------------+------------+\n" + << "| Bucket | Material | Positional | Total |\n" + << "| | (PSQT) | (Layers) | |\n" + << "+------------+------------+------------+------------+\n"; + + for (std::size_t bucket = 0; bucket < LayerStacks; ++bucket) + { + char buffer[3][8]; + std::memset(buffer, '\0', sizeof(buffer)); + + format_cp_aligned_dot(t.psqt[bucket], buffer[0]); + format_cp_aligned_dot(t.positional[bucket], buffer[1]); + format_cp_aligned_dot(t.psqt[bucket] + t.positional[bucket], buffer[2]); + + ss << "| " << bucket << " " + << " | " << buffer[0] << " " + << " | " << buffer[1] << " " + << " | " << buffer[2] << " " + << " |"; + if (bucket == t.correctBucket) + ss << " <-- this bucket is used"; + ss << '\n'; + } + + ss << "+------------+------------+------------+------------+\n"; + + return ss.str(); + } + + // Load eval, from a file stream or a memory stream bool load_eval(std::string name, std::istream& stream) { @@ -192,4 +409,35 @@ namespace Stockfish::Eval::NNUE { return write_parameters(stream); } + /// Save eval, to a file given by its name + bool save_eval(const std::optional& filename) { + + std::string actualFilename; + std::string msg; + + if (filename.has_value()) + actualFilename = filename.value(); + else + { + if (eval_file_loaded != EvalFileDefaultName) + { + msg = "Failed to export a net. A non-embedded net can only be saved if the filename is specified"; + + sync_cout << msg << sync_endl; + return false; + } + actualFilename = EvalFileDefaultName; + } + + std::ofstream stream(actualFilename, std::ios_base::binary); + bool saved = save_eval(stream); + + msg = saved ? "Network saved successfully to " + actualFilename + : "Failed to export a net"; + + sync_cout << msg << sync_endl; + return saved; + } + + } // namespace Stockfish::Eval::NNUE diff --git a/src/position.h b/src/position.h index e6b072bc82d..9f694a79b25 100644 --- a/src/position.h +++ b/src/position.h @@ -171,6 +171,9 @@ class Position { // Used by NNUE StateInfo* state() const; + void put_piece(Piece pc, Square s); + void remove_piece(Square s); + private: // Initialization helpers (used while setting up a position) void set_castling_right(Color c, Square rfrom); @@ -178,8 +181,6 @@ class Position { void set_check_info(StateInfo* si) const; // Other helpers - void put_piece(Piece pc, Square s); - void remove_piece(Square s); void move_piece(Square from, Square to); template void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto); @@ -386,7 +387,7 @@ inline void Position::remove_piece(Square s) { byTypeBB[ALL_PIECES] ^= s; byTypeBB[type_of(pc)] ^= s; byColorBB[color_of(pc)] ^= s; - /* board[s] = NO_PIECE; Not needed, overwritten by the capturing one */ + board[s] = NO_PIECE; pieceCount[pc]--; pieceCount[make_piece(color_of(pc), ALL_PIECES)]--; psq -= PSQT::psq[pc][s]; diff --git a/src/uci.cpp b/src/uci.cpp index bb17b8d79b2..d5ffa228319 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -277,13 +277,13 @@ void UCI::loop(int argc, char* argv[]) { else if (token == "d") sync_cout << pos << sync_endl; else if (token == "eval") trace_eval(pos); else if (token == "compiler") sync_cout << compiler_info() << sync_endl; - else if (token == "export_net") { + else if (token == "export_net") + { std::optional filename; std::string f; - if (is >> skipws >> f) { - filename = f; - } - Eval::NNUE::export_net(filename); + if (is >> skipws >> f) + filename = f; + Eval::NNUE::save_eval(filename); } else if (!token.empty() && token[0] != '#') sync_cout << "Unknown command: " << cmd << sync_endl; From ba01f4b95448bcb324755f4dd2a632a57c6e67bc Mon Sep 17 00:00:00 2001 From: MichaelB7 Date: Sat, 19 Jun 2021 09:57:09 -0400 Subject: [PATCH 0610/1766] Make net nn-75980ca503c6.nnue the default. trained with the Python command c:\nnue>python train.py i:/bin/all.binpack i:/bin/all.binpack --gpus 1 --threads 4 --num-workers 30 --batch-size 16384 --progress_bar_refresh_rate 300 --smart-fen-skipping --random-fen-skipping 3 --features=HalfKAv2^ --lambda=1.0 --max_epochs=440 --seed %random%%random% --default_root_dir exp/run_10 --resume-from-model ./pt/nn-3b20abec10c1.pt ` all.binpack equaled 4 parts Wrong_NNUE_2.binpack https://drive.google.com/file/d/1seGNOqcVdvK_vPNq98j-zV3XPE5zWAeq/view?usp=sharing plus two parts of Training_Data.binpack https://drive.google.com/file/d/1RFkQES3DpsiJqsOtUshENtzPfFgUmEff/view?usp=sharing Each set was concatenated together - making one large Wrong_NNUE 2 binpack and one large Training so the were approximately equal in size. They were then interleaved together. The idea was to give Wrong_NNUE.binpack closer to equal weighting with the Training_Data binpack . Net nn-3b20abec10c1.nnue was chosen as the --resume-from-model with the idea that through learning, the manually hex edited values will be learned and will not need to be manually adjusted going forward. They would also be fine tuned by the learning process. passed STC: https://tests.stockfishchess.org/tests/view/60cdf91e457376eb8bcab66f LLR: 2.95 (-2.94,2.94) <-0.50,2.50> Total: 18256 W: 1639 L: 1479 D: 15138 Ptnml(0-2): 59, 1179, 6505, 1313, 72 passed LTC: https://tests.stockfishchess.org/tests/view/60ce2166457376eb8bcab6e1 LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 18792 W: 654 L: 542 D: 17596 Ptnml(0-2): 9, 490, 8291, 592, 14 closes https://github.com/official-stockfish/Stockfish/pull/3570 Bench: 5020972 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index f888dd9128c..0bcfd37f529 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-50144f835024.nnue" + #define EvalFileDefaultName "nn-75980ca503c6.nnue" namespace NNUE { From 70ac5ecbb62b68382073b2367644945c1327ddfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sun, 20 Jun 2021 10:29:20 +0200 Subject: [PATCH 0611/1766] Keep more pawns and pieces when attacking This patch increase the weight of pawns and pieces from 28 to 32 in the scaling formula we apply to the output of the NNUE pure eval. Increasing this gradient for pawns and pieces means that Stockfish will try a little harder to keep material when she has the advantage, and try a little bit harder to escape into an endgame when she is under pressure. STC: LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 53168 W: 4371 L: 4177 D: 44620 Ptnml(0-2): 160, 3389, 19283, 3601, 151 https://tests.stockfishchess.org/tests/view/60cefd1d457376eb8bcab7ab LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 10888 W: 386 L: 288 D: 10214 Ptnml(0-2): 3, 260, 4821, 356, 4 https://tests.stockfishchess.org/tests/view/60cf709d2114332881e7352b closes https://github.com/official-stockfish/Stockfish/pull/3571 Bench: 4965430 --- src/evaluate.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index fe1366147f6..1366d0fb2ab 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1091,8 +1091,9 @@ Value Eval::evaluate(const Position& pos) { // Scale and shift NNUE for compatibility with search and classical evaluation auto adjusted_NNUE = [&]() { - - int scale = 903 + 28 * pos.count() + 28 * pos.non_pawn_material() / 1024; + int scale = 903 + + 32 * pos.count() + + 32 * pos.non_pawn_material() / 1024; Value nnue = NNUE::evaluate(pos, true) * scale / 1024; From ed436a36bade82422753f8be9c16d790232e9c91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Wed, 16 Jun 2021 07:23:26 +0200 Subject: [PATCH 0612/1766] Remove the Contempt UCI option This patch removes the UCI option for setting Contempt in classical evaluation. It is exactly equivalent to using Contempt=0 for the UCI contempt value and keeping the dynamic part in the algo (renaming this dynamic part `trend` to better describe what it does). We have tried quite hard to implement a working Contempt feature for NNUE but nothing really worked, so it is probably time to give up. Interested chess fans wishing to keep playing with the UCI option for Contempt and use it with the classical eval are urged to download the version tagged "SF_Classical" of Stockfish (dated 31 July 2020), as it was the last version where our search algorithm was tuned for the classical eval and is probably our strongest classical player ever: https://github.com/official-stockfish/Stockfish/tags Passed STC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 72904 W: 6228 L: 6175 D: 60501 Ptnml(0-2): 221, 5006, 25971, 5007, 247 https://tests.stockfishchess.org/tests/view/60c98bf9457376eb8bcab18d Passed LTC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 45168 W: 1601 L: 1547 D: 42020 Ptnml(0-2): 38, 1331, 19786, 1397, 32 https://tests.stockfishchess.org/tests/view/60c9c7fa457376eb8bcab1bb closes https://github.com/official-stockfish/Stockfish/pull/3575 Bench: 4947716 --- README.md | 8 -------- src/evaluate.cpp | 4 ++-- src/search.cpp | 22 +++++----------------- src/thread.h | 2 +- src/ucioption.cpp | 2 -- 5 files changed, 8 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 0bbe4abbefb..79db8170257 100644 --- a/README.md +++ b/README.md @@ -120,14 +120,6 @@ change them via a chess GUI. This is a list of available UCI options in Stockfis Limit Syzygy tablebase probing to positions with at most this many pieces left (including kings and pawns). - * #### Contempt - A positive value for contempt favors middle game positions and avoids draws, - effective for the classical evaluation only. - - * #### Analysis Contempt - By default, contempt is set to prefer the side to move. Set this option to "White" - or "Black" to analyse with contempt for that side, or "Off" to disable contempt. - * #### Move Overhead Assume a time delay of x ms due to network and GUI overheads. This is useful to avoid losses on time in those cases. diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 1366d0fb2ab..6a032fc055c 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -979,7 +979,7 @@ namespace { // Initialize score by reading the incrementally updated scores included in // the position object (material + piece square tables) and the material // imbalance. Score is computed internally from the white point of view. - Score score = pos.psq_score() + me->imbalance() + pos.this_thread()->contempt; + Score score = pos.psq_score() + me->imbalance() + pos.this_thread()->trend; // Probe the pawn hash table pe = Pawns::probe(pos); @@ -1139,7 +1139,7 @@ std::string Eval::trace(Position& pos) { std::memset(scores, 0, sizeof(scores)); - pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt + pos.this_thread()->trend = SCORE_ZERO; // Reset any dynamic contempt v = Evaluation(pos).value(); diff --git a/src/search.cpp b/src/search.cpp index 9bba563a779..62f02d835bf 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -312,19 +312,7 @@ void Thread::search() { multiPV = std::min(multiPV, rootMoves.size()); ttHitAverage = TtHitAverageWindow * TtHitAverageResolution / 2; - int ct = int(Options["Contempt"]) * PawnValueEg / 100; // From centipawns - - // In analysis mode, adjust contempt in accordance with user preference - if (Limits.infinite || Options["UCI_AnalyseMode"]) - ct = Options["Analysis Contempt"] == "Off" ? 0 - : Options["Analysis Contempt"] == "Both" ? ct - : Options["Analysis Contempt"] == "White" && us == BLACK ? -ct - : Options["Analysis Contempt"] == "Black" && us == WHITE ? -ct - : ct; - - // Evaluation score is from the white point of view - contempt = (us == WHITE ? make_score(ct, ct / 2) - : -make_score(ct, ct / 2)); + trend = SCORE_ZERO; int searchAgainCounter = 0; @@ -370,11 +358,11 @@ void Thread::search() { alpha = std::max(prev - delta,-VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE); - // Adjust contempt based on root move's previousScore (dynamic contempt) - int dct = ct + (113 - ct / 2) * prev / (abs(prev) + 147); + // Adjust trend based on root move's previousScore (dynamic contempt) + int tr = 113 * prev / (abs(prev) + 147); - contempt = (us == WHITE ? make_score(dct, dct / 2) - : -make_score(dct, dct / 2)); + trend = (us == WHITE ? make_score(tr, tr / 2) + : -make_score(tr, tr / 2)); } // Start with a small aspiration window and, in the case of a fail diff --git a/src/thread.h b/src/thread.h index ae662880cef..5bfa2359afd 100644 --- a/src/thread.h +++ b/src/thread.h @@ -74,7 +74,7 @@ class Thread { LowPlyHistory lowPlyHistory; CapturePieceToHistory captureHistory; ContinuationHistory continuationHistory[2][2]; - Score contempt; + Score trend; }; diff --git a/src/ucioption.cpp b/src/ucioption.cpp index d59c010017e..07b3027da70 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -61,8 +61,6 @@ void init(OptionsMap& o) { constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048; o["Debug Log File"] << Option("", on_logger); - o["Contempt"] << Option(24, -100, 100); - o["Analysis Contempt"] << Option("Both var Off var White var Black var Both", "Both"); o["Threads"] << Option(1, 1, 512, on_threads); o["Hash"] << Option(16, 1, MaxHashMB, on_hash_size); o["Clear Hash"] << Option(on_clear_hash); From 2e2865d34b6767e60ea55277bc6b1d642795ecc3 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 21 Jun 2021 08:23:50 +0200 Subject: [PATCH 0613/1766] Fix build error on OSX directly use integer version for cp calculation. fixes https://github.com/official-stockfish/Stockfish/issues/3573 closes https://github.com/official-stockfish/Stockfish/pull/3574 No functional change --- src/nnue/evaluate_nnue.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index a918b9255ea..8828ae517e3 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -232,7 +232,7 @@ namespace Stockfish::Eval::NNUE { buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' '); - int cp = (int)(std::abs(100.0 * double(v) / PawnValueEg)); + int cp = std::abs(100 * v / PawnValueEg); if (cp >= 10000) { @@ -261,7 +261,7 @@ namespace Stockfish::Eval::NNUE { static void format_cp_aligned_dot(Value v, char* buffer) { buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' '); - int cp = (int)(std::abs(100.0 * double(v) / PawnValueEg)); + int cp = std::abs(100 * v / PawnValueEg); if (cp >= 10000) { From 9b82414b67aa7d1279e1cc99a6970ab766025bfa Mon Sep 17 00:00:00 2001 From: MichaelB7 Date: Mon, 21 Jun 2021 08:10:35 -0400 Subject: [PATCH 0614/1766] Make net nn-190f102a22c3.nnue the default net. Trained with the pytorch trainer: https://github.com/glinscott/nnue-pytorch python train.py i:/bin/all.binpack i:/bin/all.binpack --gpus 1 --threads 4 --num-workers 30 --batch-size 16384 --progress_bar_refresh_rate 300 --smart-fen-skipping --random-fen-skipping 3 --features=HalfKAv2^ --lambda=1.0 --max_epochs=440 --seed %random%%random% --default_root_dir exp/run_17 --resume-from-model ./pt/nn-75980ca503c6.pt This run is thus started from the previous master net. all.binpack equaled 4 parts Wrong_NNUE_2.binpack https://drive.google.com/file/d/1seGNOqcVdvK_vPNq98j-zV3XPE5zWAeq/view?usp=sharing plus two parts of Training_Data.binpack https://drive.google.com/file/d/1RFkQES3DpsiJqsOtUshENtzPfFgUmEff/view?usp=sharing Each set was concatenated together - making one large Wrong_NNUE 2 binpack and one large Training so the were approximately equal in size. They were then interleaved together. The idea was to give Wrong_NNUE.binpack closer to equal weighting with the Training_Data binpack passed LTC https://tests.stockfishchess.org/tests/view/60d09f52b4c17000d679517f LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 32184 W: 1100 L: 970 D: 30114 Ptnml(0-2): 10, 878, 14193, 994, 17 passed STC https://tests.stockfishchess.org/tests/view/60d086c02114332881e7368e LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 11360 W: 1056 L: 906 D: 9398 Ptnml(0-2): 25, 735, 4026, 853, 41 closes https://github.com/official-stockfish/Stockfish/pull/3576 Bench: 4631244 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 0bcfd37f529..d909c7ce9a3 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-75980ca503c6.nnue" + #define EvalFileDefaultName "nn-190f102a22c3.nnue" namespace NNUE { From 0470bcef0e1962b4f8da15108170b991d3f90d0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Tue, 22 Jun 2021 09:08:37 +0200 Subject: [PATCH 0615/1766] Detect fortresses a little bit quicker In the so-called "hybrid" method of evaluation of current master, we use the classical eval (because of its speed) instead of the NNUE eval when the classical material balance approximation hints that the position is "winning enough" to rely on the classical eval. This trade-off idea between speed and accuracy works well in general, but in some fortress positions the classical eval is just bad. So in shuffling branches of the search tree, we (slowly) increase the thresehold so that eventually we don't trust classical anymore and switch to NNUE evaluation. This patch increases that threshold faster, so that we switch to NNUE quicker in shuffling branches. Idea is to incite Stockfish to spend less time in fortresses lines in the search tree, and spend more time searching the critical lines. passed STC: LLR: 2.96 (-2.94,2.94) <-0.50,2.50> Total: 47872 W: 3908 L: 3720 D: 40244 Ptnml(0-2): 122, 3053, 17419, 3199, 143 https://tests.stockfishchess.org/tests/view/60cef34b457376eb8bcab79d passed LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 73616 W: 2326 L: 2143 D: 69147 Ptnml(0-2): 21, 1940, 32705, 2119, 23 https://tests.stockfishchess.org/tests/view/60cf6d842114332881e73528 Retested at LTC against lastest master: LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 18264 W: 642 L: 532 D: 17090 Ptnml(0-2): 6, 479, 8055, 583, 9 https://tests.stockfishchess.org/tests/view/60d18cd540925195e7a6c351 closes https://github.com/official-stockfish/Stockfish/pull/3578 Bench: 5139233 --- src/evaluate.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 6a032fc055c..f9754795603 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -192,8 +192,7 @@ namespace { // Threshold for lazy and space evaluation constexpr Value LazyThreshold1 = Value(1565); constexpr Value LazyThreshold2 = Value(1102); - constexpr Value SpaceThreshold = Value(11551); - constexpr Value NNUEThreshold1 = Value(800); + constexpr Value SpaceThreshold = Value(11551); // KingAttackWeights[PieceType] contains king attack weights by piece type constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 }; @@ -1103,14 +1102,14 @@ Value Eval::evaluate(const Position& pos) { return nnue; }; - // If there is PSQ imbalance we use the classical eval. + // If there is PSQ imbalance we use the classical eval, but we switch to + // NNUE eval faster when shuffling or if the material on the board is high. + int r50 = pos.rule50_count(); Value psq = Value(abs(eg_value(pos.psq_score()))); - int r50 = 16 + pos.rule50_count(); - bool largePsq = psq * 16 > (NNUEThreshold1 + pos.non_pawn_material() / 64) * r50; - - v = largePsq ? Evaluation(pos).value() // classical - : adjusted_NNUE(); // NNUE + bool classical = psq * 5 > (750 + pos.non_pawn_material() / 64) * (5 + r50); + v = classical ? Evaluation(pos).value() // classical + : adjusted_NNUE(); // NNUE } // Damp down the evaluation linearly when shuffling From e47b74457ef12ff28ced317fef942cfad18b18a0 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Tue, 22 Jun 2021 19:33:14 -0300 Subject: [PATCH 0616/1766] Simplify Reductions Initialization passed STC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 45032 W: 3600 L: 3518 D: 37914 Ptnml(0-2): 111, 2893, 16435, 2957, 120 https://tests.stockfishchess.org/tests/view/60d2655d40925195e7a6c527 LTC: LLR: 3.00 (-2.94,2.94) <-2.50,0.50> Total: 25728 W: 786 L: 722 D: 24220 Ptnml(0-2): 5, 650, 11494, 706, 9 https://tests.stockfishchess.org/tests/view/60d2b14240925195e7a6c577 closes https://github.com/official-stockfish/Stockfish/pull/3584 bench: 4602977 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 62f02d835bf..a413bd3814f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -152,7 +152,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int(21.3 * std::log(i + 0.25 * std::log(i))); + Reductions[i] = int(21.9 * std::log(i)); } From dc4983327df7bf30eafd049aa777d0636ee4c0ed Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 23 Jun 2021 07:23:21 +0200 Subject: [PATCH 0617/1766] Update WDL model for NNUE This updates the WDL model based on the LTC statistics in June this year (10M games), so from pre-NNUE to NNUE based results. (for old results see, https://github.com/official-stockfish/Stockfish/pull/2778) As before the fit by the model to the data is quite good. closes https://github.com/official-stockfish/Stockfish/pull/3582 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 d5ffa228319..b3738a4a76b 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -207,13 +207,13 @@ namespace { // Coefficients of a 3rd order polynomial fit based on fishtest data // for two parameters needed to transform eval to the argument of a // logistic function. - double as[] = {-8.24404295, 64.23892342, -95.73056462, 153.86478679}; - double bs[] = {-3.37154371, 28.44489198, -56.67657741, 72.05858751}; + double as[] = {-3.68389304, 30.07065921, -60.52878723, 149.53378557}; + double bs[] = {-2.0181857, 15.85685038, -29.83452023, 47.59078827}; double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3]; double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3]; // Transform eval to centipawns with limited range - double x = std::clamp(double(100 * v) / PawnValueEg, -1000.0, 1000.0); + double x = std::clamp(double(100 * v) / PawnValueEg, -2000.0, 2000.0); // Return win rate in per mille (rounded to nearest) return int(0.5 + 1000 / (1 + std::exp((a - x) / b))); From b94a65187857721319ced93f5215ac72bbdfe472 Mon Sep 17 00:00:00 2001 From: MichaelB7 Date: Sun, 27 Jun 2021 11:26:09 -0400 Subject: [PATCH 0618/1766] Make net nn-956480d8378f.nnue the default Trained with the pytorch trainer: https://github.com/glinscott/nnue-pytorch python train.py i:/bin/all.binpack i:/bin/all.binpack --gpus 1 --threads 4 --num-workers 30 --batch-size 16384 --progress_bar_refresh_rate 300 --smart-fen-skipping --random-fen-skipping 3 --features=HalfKAv2^ --lambda=1.0 --max_epochs=440 --seed %random%%random% --default_root_dir exp/run_18 --resume-from-model ./pt/nn-75980ca503c6.pt This run is thus started from a previous master net. all.binpack equaled 4 parts Wrong_NNUE_2.binpack https://drive.google.com/file/d/1seGNOqcVdvK_vPNq98j-zV3XPE5zWAeq/view?usp=sharing plus two parts of Training_Data.binpack https://drive.google.com/file/d/1RFkQES3DpsiJqsOtUshENtzPfFgUmEff/view?usp=sharing Each set was concatenated together - making one large Wrong_NNUE 2 binpack and one large Training so the were approximately equal in size. They were then interleaved together. The idea was to give Wrong_NNUE.binpack closer to equal weighting with the Training_Data binpack passed STC: https://tests.stockfishchess.org/tests/view/60d0c0a7a8ec07dc34c072b2 LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 18440 W: 1693 L: 1531 D: 15216 Ptnml(0-2): 67, 1225, 6464, 1407, 57 passed LTC: https://tests.stockfishchess.org/tests/view/60d762793beab81350ac9d72 LLR: 2.98 (-2.94,2.94) <0.50,3.50> Total: 93120 W: 3152 L: 2933 D: 87035 Ptnml(0-2): 48, 2581, 41076, 2814, 41 passed LTC (rebased branch to current master): https://tests.stockfishchess.org/tests/view/60d85eeb3beab81350ac9e2b LLR: 2.96 (-2.94,2.94) <0.50,3.50> Total: 42688 W: 1347 L: 1206 D: 40135 Ptnml(0-2): 14, 1097, 18981, 1238, 14. closes https://github.com/official-stockfish/Stockfish/pull/3592 Bench: 4906727 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index d909c7ce9a3..5e12db45d5d 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-190f102a22c3.nnue" + #define EvalFileDefaultName "nn-956480d8378f.nnue" namespace NNUE { From 49283d3a6676e114b531d6a8b9e5f69000655912 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Mon, 28 Jun 2021 14:58:51 +0800 Subject: [PATCH 0619/1766] Update default net to nn-3475407dc199.nnue Optimization of eight subnetwork output layers of Michael's nn-190f102a22c3.nnue using SPSA https://tests.stockfishchess.org/tests/view/60d5510642a522cc50282ef3 Parameters: A total of 256 net weights and 8 net biases were tuned New best values: The raw values at the end of the tuning run were used (800k games, 5 seconds TC) Settings: default ck value and SPSA A is 30,000 (3.75% of the total number of games) STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 29064 W: 2435 L: 2269 D: 24360 Ptnml(0-2): 72, 1857, 10505, 2029, 69 https://tests.stockfishchess.org/tests/view/60d8ea123beab81350ac9eb6 LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 61848 W: 2055 L: 1884 D: 57909 Ptnml(0-2): 18, 1708, 27310, 1861, 27 https://tests.stockfishchess.org/tests/view/60d8f0393beab81350ac9ec6 closes https://github.com/official-stockfish/Stockfish/pull/3593 Bench: 4770936 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 5e12db45d5d..91da01da000 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-956480d8378f.nnue" + #define EvalFileDefaultName "nn-3475407dc199.nnue" namespace NNUE { From 2275923d3cbca85b433a4d16d40fae5c8de6784a Mon Sep 17 00:00:00 2001 From: Brad Knox <64992190+bknox83@users.noreply.github.com> Date: Tue, 29 Jun 2021 01:40:16 -0500 Subject: [PATCH 0620/1766] Update Top CPU Contributors closes https://github.com/official-stockfish/Stockfish/pull/3595 No functional change --- Top CPU Contributors.txt | 390 ++++++++++++++++++++------------------- 1 file changed, 203 insertions(+), 187 deletions(-) diff --git a/Top CPU Contributors.txt b/Top CPU Contributors.txt index f5347ea1541..dacc5781b2d 100644 --- a/Top CPU Contributors.txt +++ b/Top CPU Contributors.txt @@ -1,189 +1,205 @@ -Contributors to Fishtest with >10,000 CPU hours, as of Feb 15, 2021. +Contributors to Fishtest with >10,000 CPU hours, as of Jun 29, 2021. Thank you! -Username CPU Hours Games played ----------------------------------------------------- -noobpwnftw 23930906 1560559941 -dew 1169948 70333008 -mlang 957168 61657446 -mibere 703840 46867607 -tvijlbrief 517888 33379462 -JojoM 515404 30334272 -cw 443276 29385549 -crunchy 427035 27344275 -grandphish2 425794 26347253 -fastgm 414133 24519696 -gvreuls 377843 24708884 -CSU_Dynasty 338718 23030006 -Fisherman 326795 21820747 -TueRens 313730 19490246 -ctoks 298442 20052551 -velislav 270519 17355456 -bcross 241064 17196165 -glinscott 217799 13780820 -nordlandia 211692 13484886 -bking_US 198894 11876016 -drabel 191096 13129722 -leszek 189170 11446821 -mgrabiak 187153 12013300 -robal 181389 11539242 -Thanar 179852 12365359 -vdv 175274 9889046 -spams 157128 10319326 -marrco 150292 9401741 -sqrt2 147963 9724586 -CoffeeOne 137086 5022516 -vdbergh 137041 8926915 -malala 136182 8002293 -mhoram 132780 8398229 -xoto 124729 8652088 -davar 122092 7960001 -dsmith 122059 7570238 -Data 113305 8220352 -BrunoBanani 112960 7436849 -pemo 109598 5036441 -Dantist 106768 6431396 -MaZePallas 102741 6630419 -ElbertoOne 99028 7023771 -brabos 92118 6186135 -linrock 90903 6708639 -psk 89957 5984901 -sunu 88614 6020673 -sterni1971 86948 5613788 -Vizvezdenec 83761 5344740 -BRAVONE 81239 5054681 -nssy 76497 5259388 -cuistot 76366 4370584 -racerschmacer 75753 5442626 -teddybaer 75125 5407666 -Pking_cda 73776 5293873 -0x3C33 73133 4670293 -jromang 72117 5054915 -solarlight 70517 5028306 -dv8silencer 70287 3883992 -Bobo1239 68515 4652287 -manap 66273 4121774 -tinker 64321 4268390 -robnjr 57262 4053117 -Freja 56938 3733019 -ttruscott 56010 3680085 -rkl 54986 4150767 -renouve 53811 3501516 -finfish 51360 3370515 -eva42 51272 3599691 -rap 49985 3219146 -pb00067 49727 3298270 -amicic 49691 3042481 -ronaldjerum 47654 3240695 -bigpen0r 47278 3291647 -biffhero 46564 3111352 -VoyagerOne 45476 3452465 -eastorwest 45033 3071805 -speedycpu 43842 3003273 -jbwiebe 43305 2805433 -Antihistamine 41788 2761312 -mhunt 41735 2691355 -homyur 39893 2850481 -gri 39871 2515779 -oryx 38282 2944400 -Spprtr 38157 2470529 -SC 37290 2731014 -csnodgrass 36207 2688994 -jmdana 36157 2210661 -strelock 34716 2074055 -Garf 33800 2747562 -skiminki 33515 2055584 -EthanOConnor 33370 2090311 -slakovv 32915 2021889 -yurikvelo 32600 2255966 -Prcuvu 30377 2170122 -manapbk 30326 1770143 -anst 30301 2190091 -jkiiski 30136 1904470 -hyperbolic.tom 29840 2017394 -Pyafue 29650 1902349 -qurashee 27758 1509620 -OuaisBla 27636 1578800 -chriswk 26902 1868317 -achambord 26582 1767323 -Fifis 26376 1776853 -Patrick_G 26276 1801617 -yorkman 26193 1992080 -SFTUser 25182 1675689 -nabildanial 24942 1519409 -Sharaf_DG 24765 1786697 -ncfish1 24411 1520927 -agg177 23890 1395014 -JanErik 23408 1703875 -Isidor 23388 1680691 -Norabor 23164 1591830 -cisco2015 22895 1762069 -Zirie 22542 1472937 -team-oh 22272 1636708 -MazeOfGalious 21978 1629593 -sg4032 21945 1643065 -ianh2105 21725 1632562 -xor12 21628 1680365 -dex 21612 1467203 -nesoneg 21494 1463031 -jjoshua2 20997 1422689 -horst.prack 20878 1465656 -0xB00B1ES 20590 1208666 -sphinx 20515 1352368 -j3corre 20405 941444 -Adrian.Schmidt123 20316 1281436 -Ente 20017 1432602 -wei 19973 1745989 -rstoesser 19569 1293588 -eudhan 19274 1283717 -jundery 18445 1115855 -iisiraider 18247 1101015 -ville 17883 1384026 -chris 17698 1487385 -purplefishies 17595 1092533 -DMBK 17357 1279152 -DragonLord 17014 1162790 -dju 16515 929427 -IgorLeMasson 16064 1147232 -ako027ako 15671 1173203 -Nikolay.IT 15154 1068349 -Andrew Grant 15114 895539 -OssumOpossum 14857 1007129 -enedene 14476 905279 -bpfliegel 14298 884523 -jpulman 13982 870599 -joster 13794 950160 -Nesa92 13786 1114691 -crocogoat 13753 1114622 -Hjax 13535 915487 -Dark_wizzie 13422 1007152 -mpx86 12941 693640 -mabichito 12903 749391 -thijsk 12886 722107 -AdrianSA 12860 804972 -Flopzee 12698 894821 -fatmurphy 12547 853210 -scuzzi 12511 845761 -Karby 12429 735880 -SapphireBrand 12416 969604 -modolief 12386 896470 -pgontarz 12151 848794 -stocky 11954 699440 -mschmidt 11941 803401 -infinity 11470 727027 -torbjo 11395 729145 -Thomas A. Anderson 11372 732094 -d64 11263 789184 -Maxim 11129 804704 -snicolet 11106 869170 -MooTheCow 11008 694942 -savage84 10965 641068 -Rudolphous 10915 741268 -Wolfgang 10809 580032 -rpngn 10712 688203 -basepi 10637 744851 -michaelrpg 10409 735127 -dzjp 10343 732529 -ali-al-zhrani 10324 726502 -ols 10259 570669 -lbraesch 10252 647825 +Username CPU Hours Games played +----------------------------------------------------- +noobpwnftw 27649494 1834734733 +mlang 1426107 89454622 +dew 1380910 82831648 +mibere 703840 46867607 +grandphish2 692707 41737913 +tvijlbrief 669642 42371594 +JojoM 597778 35297180 +TueRens 519226 31823562 +cw 458421 30307421 +fastgm 439667 25950040 +gvreuls 436599 28177460 +crunchy 427035 27344275 +CSU_Dynasty 374765 25106278 +Fisherman 326901 21822979 +ctoks 325477 21767943 +velislav 295343 18844324 +linrock 292789 10624427 +bcross 278584 19488961 +okrout 262818 13803272 +pemo 245982 11376085 +glinscott 217799 13780820 +leszek 212346 12959025 +nordlandia 211692 13484886 +bking_US 198894 11876016 +drabel 196463 13450602 +robal 195473 12375650 +mgrabiak 187226 12016564 +Dantist 183202 10990484 +Thanar 179852 12365359 +vdv 175274 9889046 +spams 157128 10319326 +marrco 150295 9402141 +sqrt2 147963 9724586 +mhoram 141278 8901241 +CoffeeOne 137100 5024116 +vdbergh 137041 8926915 +malala 136182 8002293 +xoto 133702 9156676 +davar 122092 7960001 +dsmith 122059 7570238 +Data 113305 8220352 +BrunoBanani 112960 7436849 +MaZePallas 102823 6633619 +sterni1971 100532 5880772 +ElbertoOne 99028 7023771 +brabos 92118 6186135 +oz 92100 6486640 +psk 89957 5984901 +amicic 89156 5392305 +sunu 88851 6028873 +Vizvezdenec 83761 5344740 +0x3C33 82614 5271253 +BRAVONE 81239 5054681 +racerschmacer 80899 5759262 +cuistot 80300 4606144 +nssy 76497 5259388 +teddybaer 75125 5407666 +Pking_cda 73776 5293873 +jromang 72192 5057715 +solarlight 70517 5028306 +dv8silencer 70287 3883992 +Bobo1239 68515 4652287 +manap 66273 4121774 +skiminki 65088 4023328 +tinker 64333 4268790 +sschnee 60767 3500800 +qurashee 57344 3168264 +robnjr 57262 4053117 +Freja 56938 3733019 +ttruscott 56010 3680085 +rkl 55132 4164467 +renouve 53811 3501516 +finfish 51360 3370515 +eva42 51272 3599691 +rap 49985 3219146 +pb00067 49727 3298270 +ronaldjerum 47654 3240695 +bigpen0r 47653 3335327 +eastorwest 47585 3221629 +biffhero 46564 3111352 +VoyagerOne 45476 3452465 +yurikvelo 44834 3034550 +speedycpu 43842 3003273 +jbwiebe 43305 2805433 +Spprtr 42279 2680153 +DesolatedDodo 42007 2447516 +Antihistamine 41788 2761312 +mhunt 41735 2691355 +homyur 39893 2850481 +gri 39871 2515779 +Fifis 38776 2529121 +oryx 38724 2966648 +SC 37290 2731014 +csnodgrass 36207 2688994 +jmdana 36157 2210661 +strelock 34716 2074055 +rpngn 33951 2057395 +Garf 33922 2751802 +EthanOConnor 33370 2090311 +slakovv 32915 2021889 +manapbk 30987 1810399 +Prcuvu 30377 2170122 +anst 30301 2190091 +jkiiski 30136 1904470 +hyperbolic.tom 29840 2017394 +Pyafue 29650 1902349 +Wolfgang 29260 1658936 +zeryl 28156 1579911 +OuaisBla 27636 1578800 +DMBK 27051 1999456 +chriswk 26902 1868317 +achambord 26582 1767323 +Patrick_G 26276 1801617 +yorkman 26193 1992080 +SFTUser 25182 1675689 +nabildanial 24942 1519409 +Sharaf_DG 24765 1786697 +ncfish1 24411 1520927 +rodneyc 24227 1409514 +agg177 23890 1395014 +JanErik 23408 1703875 +Isidor 23388 1680691 +Norabor 23164 1591830 +cisco2015 22897 1762669 +Zirie 22542 1472937 +team-oh 22272 1636708 +MazeOfGalious 21978 1629593 +sg4032 21947 1643265 +ianh2105 21725 1632562 +xor12 21628 1680365 +dex 21612 1467203 +nesoneg 21494 1463031 +sphinx 21211 1384728 +jjoshua2 21001 1423089 +horst.prack 20878 1465656 +Ente 20865 1477066 +0xB00B1ES 20590 1208666 +j3corre 20405 941444 +Adrian.Schmidt123 20316 1281436 +wei 19973 1745989 +MaxKlaxxMiner 19850 1009176 +rstoesser 19569 1293588 +gopeto 19491 1174952 +eudhan 19274 1283717 +jundery 18445 1115855 +megaman7de 18377 1067540 +iisiraider 18247 1101015 +ville 17883 1384026 +chris 17698 1487385 +purplefishies 17595 1092533 +dju 17353 978595 +DragonLord 17014 1162790 +IgorLeMasson 16064 1147232 +ako027ako 15671 1173203 +chuckstablers 15289 891576 +Nikolay.IT 15154 1068349 +Andrew Grant 15114 895539 +OssumOpossum 14857 1007129 +Karby 14808 867120 +enedene 14476 905279 +bpfliegel 14298 884523 +mpx86 14019 759568 +jpulman 13982 870599 +crocogoat 13803 1117422 +joster 13794 950160 +Nesa92 13786 1114691 +Hjax 13535 915487 +jsys14 13459 785000 +Dark_wizzie 13422 1007152 +mabichito 12903 749391 +thijsk 12886 722107 +AdrianSA 12860 804972 +Flopzee 12698 894821 +fatmurphy 12547 853210 +Rudolphous 12520 832340 +scuzzi 12511 845761 +SapphireBrand 12416 969604 +modolief 12386 896470 +Machariel 12335 810784 +pgontarz 12151 848794 +stocky 11954 699440 +mschmidt 11941 803401 +Maxim 11543 836024 +infinity 11470 727027 +torbjo 11395 729145 +Thomas A. Anderson 11372 732094 +savage84 11358 670860 +d64 11263 789184 +MooTheCow 11237 720174 +snicolet 11106 869170 +ali-al-zhrani 11086 767926 +AndreasKrug 10875 887457 +pirt 10806 836519 +basepi 10637 744851 +michaelrpg 10508 739039 +dzjp 10343 732529 +aga 10302 622975 +ols 10259 570669 +lbraesch 10252 647825 +FormazChar 10059 757283 From 773dff020968f7a6f590cfd53e8fd89f12e15e36 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 28 Jun 2021 21:46:04 +0200 Subject: [PATCH 0621/1766] Stockfish 14 Official release version of Stockfish 14 Bench: 4770936 --- Today, we have the pleasure to announce Stockfish 14. As usual, downloads will be freely available at https://stockfishchess.org The engine is now significantly stronger than just a few months ago, and wins four times more game pairs than it loses against the previous release version [0]. Stockfish 14 is now at least 400 Elo ahead of Stockfish 7, a top engine in 2016 [1]. During the last five years, Stockfish has thus gained about 80 Elo per year. Stockfish 14 evaluates positions more accurately than Stockfish 13 as a result of two major steps forward in defining and training the efficiently updatable neural network (NNUE) that provides the evaluation for positions. First, the collaboration with the Leela Chess Zero team - announced previously [2] - has come to fruition. The LCZero team has provided a collection of billions of positions evaluated by Leela that we have combined with billions of positions evaluated by Stockfish to train the NNUE net that powers Stockfish 14. The fact that we could use and combine these datasets freely was essential for the progress made and demonstrates the power of open source and open data [3]. Second, the architecture of the NNUE network was significantly updated: the new network is not only larger, but more importantly, it deals better with large material imbalances and can specialize for multiple phases of the game [4]. A new project, kick-started by Gary Linscott and Tomasz Sobczyk, led to a GPU accelerated net trainer written in pytorch.[5] This tool allows for training high-quality nets in a couple of hours. Finally, this release features some search refinements, minor bug fixes and additional improvements. For example, Stockfish is now about 90 Elo stronger for chess960 (Fischer random chess) at short time control. The Stockfish project builds on a thriving community of enthusiasts (thanks everybody!) that contribute their expertise, time, and resources to build a free and open-source chess engine that is robust, widely available, and very strong. We invite our chess fans to join the fishtest testing framework and programmers to contribute to the project on github [6]. Stay safe and enjoy chess! The Stockfish team [0] https://tests.stockfishchess.org/tests/view/60dae5363beab81350aca077 [1] https://nextchessmove.com/dev-builds [2] https://stockfishchess.org/blog/2021/stockfish-13/ [3] https://lczero.org/blog/2021/06/the-importance-of-open-data/ [4] https://github.com/official-stockfish/Stockfish/commit/e8d64af1 [5] https://github.com/glinscott/nnue-pytorch/ [6] https://stockfishchess.org/get-involved/ --- src/misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index 9e7b7e37b8c..78227ee2820 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -67,7 +67,7 @@ namespace { /// Version number. If Version is left empty, then compile date in the format /// DD-MM-YY and show in engine_info. -const string Version = ""; +const string Version = "14"; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We From 7cfc1f9b150d387788a9b02360e49ba2a56505f7 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 3 Jul 2021 09:20:06 +0200 Subject: [PATCH 0622/1766] Restore development version No functional change --- src/misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index 78227ee2820..9e7b7e37b8c 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -67,7 +67,7 @@ namespace { /// Version number. If Version is left empty, then compile date in the format /// DD-MM-YY and show in engine_info. -const string Version = "14"; +const string Version = ""; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We From b51b094419e301b16ce8f639952993dd9abfcc43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Wed, 23 Jun 2021 09:55:42 +0200 Subject: [PATCH 0623/1766] Simplify format_cp_aligned_dot() closes https://github.com/official-stockfish/Stockfish/pull/3583 No functional change --- src/nnue/evaluate_nnue.cpp | 65 ++++++++++++-------------------------- 1 file changed, 21 insertions(+), 44 deletions(-) diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 8828ae517e3..891f8faad02 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -227,69 +227,46 @@ namespace Stockfish::Eval::NNUE { static const std::string PieceToChar(" PNBRQK pnbrqk"); - // Requires the buffer to have capacity for at least 5 values + + // format_cp_compact() converts a Value into (centi)pawns and writes it in a buffer. + // The buffer must have capacity for at least 5 chars. static void format_cp_compact(Value v, char* buffer) { buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' '); int cp = std::abs(100 * v / PawnValueEg); - if (cp >= 10000) { - buffer[1] = '0' + cp / 10000; cp %= 10000; - buffer[2] = '0' + cp / 1000; cp %= 1000; - buffer[3] = '0' + cp / 100; cp %= 100; - buffer[4] = ' '; + buffer[1] = '0' + cp / 10000; cp %= 10000; + buffer[2] = '0' + cp / 1000; cp %= 1000; + buffer[3] = '0' + cp / 100; cp %= 100; + buffer[4] = ' '; } else if (cp >= 1000) { - buffer[1] = '0' + cp / 1000; cp %= 1000; - buffer[2] = '0' + cp / 100; cp %= 100; - buffer[3] = '.'; - buffer[4] = '0' + cp / 10; + buffer[1] = '0' + cp / 1000; cp %= 1000; + buffer[2] = '0' + cp / 100; cp %= 100; + buffer[3] = '.'; + buffer[4] = '0' + cp / 10; } else { - buffer[1] = '0' + cp / 100; cp %= 100; - buffer[2] = '.'; - buffer[3] = '0' + cp / 10; cp %= 10; - buffer[4] = '0' + cp / 1; + buffer[1] = '0' + cp / 100; cp %= 100; + buffer[2] = '.'; + buffer[3] = '0' + cp / 10; cp %= 10; + buffer[4] = '0' + cp / 1; } } - // Requires the buffer to have capacity for at least 7 values + + // format_cp_aligned_dot() converts a Value into (centi)pawns and writes it in a buffer, + // always keeping two decimals. The buffer must have capacity for at least 7 chars. static void format_cp_aligned_dot(Value v, char* buffer) { - buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' '); - int cp = std::abs(100 * v / PawnValueEg); + buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' '); - if (cp >= 10000) - { - buffer[1] = '0' + cp / 10000; cp %= 10000; - buffer[2] = '0' + cp / 1000; cp %= 1000; - buffer[3] = '0' + cp / 100; cp %= 100; - buffer[4] = '.'; - buffer[5] = '0' + cp / 10; cp %= 10; - buffer[6] = '0' + cp; - } - else if (cp >= 1000) - { - buffer[1] = ' '; - buffer[2] = '0' + cp / 1000; cp %= 1000; - buffer[3] = '0' + cp / 100; cp %= 100; - buffer[4] = '.'; - buffer[5] = '0' + cp / 10; cp %= 10; - buffer[6] = '0' + cp; - } - else - { - buffer[1] = ' '; - buffer[2] = ' '; - buffer[3] = '0' + cp / 100; cp %= 100; - buffer[4] = '.'; - buffer[5] = '0' + cp / 10; cp %= 10; - buffer[6] = '0' + cp / 1; - } + double cp = 1.0 * std::abs(int(v)) / PawnValueEg; + sprintf(&buffer[1], "%6.2f", cp); } From d297d1d8a78166b609af112b6208ace7c645b2f3 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Wed, 30 Jun 2021 09:22:59 +0100 Subject: [PATCH 0624/1766] Simplify lazy_skip. Small speedup by removing operations in lazy_skip. STC 10+0.1 : LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 55088 W: 4553 L: 4482 D: 46053 Ptnml(0-2): 163, 3546, 20045, 3637, 153 https://tests.stockfishchess.org/tests/view/60daa2cb3beab81350aca04d LTC 60+0.6 : LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 46136 W: 1457 L: 1407 D: 43272 Ptnml(0-2): 10, 1282, 20442, 1316, 18 https://tests.stockfishchess.org/tests/view/60db0e753beab81350aca08e closes https://github.com/official-stockfish/Stockfish/pull/3599 Bench 5122403 --- src/evaluate.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index f9754795603..538214d3229 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -190,8 +190,8 @@ using namespace Trace; namespace { // Threshold for lazy and space evaluation - constexpr Value LazyThreshold1 = Value(1565); - constexpr Value LazyThreshold2 = Value(1102); + constexpr Value LazyThreshold1 = Value(3130); + constexpr Value LazyThreshold2 = Value(2204); constexpr Value SpaceThreshold = Value(11551); // KingAttackWeights[PieceType] contains king attack weights by piece type @@ -986,7 +986,7 @@ namespace { // Early exit if score is high auto lazy_skip = [&](Value lazyThreshold) { - return abs(mg_value(score) + eg_value(score)) / 2 > lazyThreshold + pos.non_pawn_material() / 64; + return abs(mg_value(score) + eg_value(score)) > lazyThreshold + pos.non_pawn_material() / 32; }; if (lazy_skip(LazyThreshold1)) From ec8dfe7315c865053afb198af2f1231a551c2c26 Mon Sep 17 00:00:00 2001 From: candirufish <38038147+candirufish@users.noreply.github.com> Date: Thu, 1 Jul 2021 19:51:41 +0200 Subject: [PATCH 0625/1766] no cut node reduction for killer moves. stc: LLR: 2.95 (-2.94,2.94) <-0.50,2.50> Total: 44344 W: 3474 L: 3294 D: 37576 Ptnml(0-2): 117, 2710, 16338, 2890, 117 https://tests.stockfishchess.org/tests/view/60d8ea673beab81350ac9eb8 ltc: LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 82600 W: 2638 L: 2441 D: 77521 Ptnml(0-2): 38, 2147, 36749, 2312, 54 https://tests.stockfishchess.org/tests/view/60d9048f3beab81350ac9eed closes https://github.com/official-stockfish/Stockfish/pull/3600 Bench: 5160239 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index a413bd3814f..5974cba731e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1157,8 +1157,8 @@ namespace { r--; // Increase reduction for cut nodes (~3 Elo) - if (cutNode) - r += 1 + !captureOrPromotion; + if (cutNode && move != ss->killers[0]) + r += 2; if (!captureOrPromotion) { From 516ad1c9bf7dceeeb055e250d8cd83598c01a531 Mon Sep 17 00:00:00 2001 From: Paul Mulders Date: Tue, 29 Jun 2021 11:13:54 +0200 Subject: [PATCH 0626/1766] Allow passing RTLIB=compiler-rt to make Not all linux users will have libatomic installed. When using clang as the system compiler with compiler-rt as the default runtime library instead of libgcc, atomic builtins may be provided by compiler-rt. This change allows such users to pass RTLIB=compiler-rt to make sure the build doesn't error out on the missing (unnecessary) libatomic. closes https://github.com/official-stockfish/Stockfish/pull/3597 No functional change --- src/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Makefile b/src/Makefile index bf3c3560bbd..1d972cffc28 100644 --- a/src/Makefile +++ b/src/Makefile @@ -386,10 +386,12 @@ ifeq ($(COMP),clang) ifneq ($(KERNEL),Darwin) ifneq ($(KERNEL),OpenBSD) ifneq ($(KERNEL),FreeBSD) + ifneq ($(RTLIB),compiler-rt) LDFLAGS += -latomic endif endif endif + endif ifeq ($(arch),$(filter $(arch),armv7 armv8)) ifeq ($(OS),Android) From 8fc297c50647317185d4c41b3443a0e686412681 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Sat, 3 Jul 2021 06:13:13 +0800 Subject: [PATCH 0627/1766] Update default net to nn-9e3c6298299a.nnue Optimization of nn-956480d8378f.nnue using SPSA https://tests.stockfishchess.org/tests/view/60da2bf63beab81350ac9fe7 Same method as described in PR #3593 STC: LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 17792 W: 1525 L: 1372 D: 14895 Ptnml(0-2): 28, 1156, 6401, 1257, 54 https://tests.stockfishchess.org/tests/view/60deffc59ea99d7c2d693c19 LTC: LLR: 2.96 (-2.94,2.94) <0.50,3.50> Total: 36544 W: 1245 L: 1109 D: 34190 Ptnml(0-2): 12, 988, 16139, 1118, 15 https://tests.stockfishchess.org/tests/view/60df11339ea99d7c2d693c22 closes https://github.com/official-stockfish/Stockfish/pull/3601 Bench: 4687476 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 91da01da000..54f20bafccc 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-3475407dc199.nnue" + #define EvalFileDefaultName "nn-9e3c6298299a.nnue" namespace NNUE { From 09b6d28391cf582d99897360b225bcbbe38dd1c6 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Tue, 6 Jul 2021 20:44:50 +0300 Subject: [PATCH 0628/1766] Remove futility pruning depth limit This patch removes futility pruning depth limit for child node futility pruning. In current master it was double capped by depth and by futility margin, which is also a function of depth, which didn't make much sense. passed STC https://tests.stockfishchess.org/tests/view/60e2418f9ea99d7c2d693e64 LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 116168 W: 9100 L: 9097 D: 97971 Ptnml(0-2): 319, 7496, 42476, 7449, 344 passed LTC https://tests.stockfishchess.org/tests/view/60e3374f9ea99d7c2d693f20 LLR: 2.96 (-2.94,2.94) <-2.50,0.50> Total: 43304 W: 1282 L: 1231 D: 40791 Ptnml(0-2): 8, 1126, 19335, 1173, 10 closes https://github.com/official-stockfish/Stockfish/pull/3606 bench 4965493 --- src/search.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 5974cba731e..008a60ede4a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -780,7 +780,6 @@ namespace { // Step 7. Futility pruning: child node (~50 Elo) if ( !PvNode - && depth < 9 && eval - futility_margin(depth, improving) >= beta && eval < VALUE_KNOWN_WIN) // Do not return unproven wins return eval; From f4986f45967c613d92657f46b5a50f12dae15398 Mon Sep 17 00:00:00 2001 From: pb00067 Date: Wed, 7 Jul 2021 14:32:54 +0200 Subject: [PATCH 0629/1766] SEE: simplify stm variable initialization Pull #3458 removed the only usage of pos.see_ge() moving pieces that don't belong to the side to move, so we can simplify this, adding an assert. closes https://github.com/official-stockfish/Stockfish/pull/3607 No functional change --- src/position.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/position.cpp b/src/position.cpp index ba015d3c38e..0686d245bc7 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1080,8 +1080,9 @@ bool Position::see_ge(Move m, Value threshold) const { if (swap <= 0) return true; + assert(color_of(piece_on(from)) == sideToMove); Bitboard occupied = pieces() ^ from ^ to; - Color stm = color_of(piece_on(from)); + Color stm = sideToMove; Bitboard attackers = attackers_to(to, occupied); Bitboard stmAttackers, bb; int res = 1; From dbd7f602d3c7622df294f87d7239b5aaf31f695f Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sun, 11 Jul 2021 00:09:15 +0300 Subject: [PATCH 0630/1766] Remove second futility pruning depth limit This patch removes futility pruning lmrDepth limit for futility pruning at parent nodes. Since it's already capped by margin that is a function of lmrDepth there is no need to extra cap it with lmrDepth. passed STC https://tests.stockfishchess.org/tests/view/60e9b5dfd1189bed71812777 LLR: 2.97 (-2.94,2.94) <-2.50,0.50> Total: 14872 W: 1264 L: 1145 D: 12463 Ptnml(0-2): 37, 942, 5369, 1041, 47 passed LTC https://tests.stockfishchess.org/tests/view/60e9c635d1189bed71812790 LLR: 2.96 (-2.94,2.94) <-2.50,0.50> Total: 40336 W: 1280 L: 1225 D: 37831 Ptnml(0-2): 24, 1057, 17960, 1094, 33 closes https://github.com/official-stockfish/Stockfish/pull/3612 bench: 5064969 --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 008a60ede4a..3cf50eb57be 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1021,8 +1021,7 @@ namespace { continue; // Futility pruning: parent node (~5 Elo) - if ( lmrDepth < 7 - && !ss->inCheck + if ( !ss->inCheck && ss->staticEval + 174 + 157 * lmrDepth <= alpha && (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] From 36f8d3806bb1c6e498ac8fd1a746c1714d1485a3 Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Mon, 12 Jul 2021 14:44:29 -0400 Subject: [PATCH 0631/1766] Don't save excluded move eval in TT STC: LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 17544 W: 1384 L: 1236 D: 14924 Ptnml(0-2): 37, 1031, 6499, 1157, 48 https://tests.stockfishchess.org/tests/view/60ec8d9bd1189bed71812999 LTC: LLR: 2.95 (-2.94,2.94) <0.50,3.50> Total: 26136 W: 823 L: 707 D: 24606 Ptnml(0-2): 6, 643, 11656, 755, 8 https://tests.stockfishchess.org/tests/view/60ecb11ed1189bed718129ba closes https://github.com/official-stockfish/Stockfish/pull/3614 Bench: 5505251 --- src/search.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/search.cpp b/src/search.cpp index 3cf50eb57be..fef1b518160 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -760,6 +760,7 @@ namespace { ss->staticEval = eval = -(ss-1)->staticEval; // Save static evaluation into transposition table + if(!excludedMove) tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); } From bc654257e74af3ffe837c0fff6e663a073ab8bbf Mon Sep 17 00:00:00 2001 From: Liam Keegan Date: Wed, 21 Jul 2021 09:33:13 +0200 Subject: [PATCH 0632/1766] Add macOS and windows to CI - macOS - system clang - gcc - windows / msys2 - mingw 64-bit gcc - mingw 32-bit gcc - minor code fixes to get new CI jobs to pass - code: suppress unused-parameter warning on 32-bit windows - Makefile: if arch=any on macos, don't specify arch at all fixes https://github.com/official-stockfish/Stockfish/issues/2958 closes https://github.com/official-stockfish/Stockfish/pull/3623 No functional change --- .github/workflows/stockfish.yml | 82 +++++++++++++++++++++++++++++++-- AUTHORS | 1 + src/Makefile | 8 +++- src/misc.cpp | 1 + 4 files changed, 87 insertions(+), 5 deletions(-) diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index 8970fcd124a..54b0cb1214a 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -25,29 +25,88 @@ jobs: os: ubuntu-20.04, compiler: g++, comp: gcc, - run_expensive_tests: true + run_expensive_tests: true, + run_32bit_tests: true, + run_64bit_tests: true, + shell: 'bash {0}' } - { name: "Ubuntu 20.04 Clang", os: ubuntu-20.04, compiler: clang++, comp: clang, - run_expensive_tests: false + run_expensive_tests: false, + run_32bit_tests: true, + run_64bit_tests: true, + shell: 'bash {0}' + } + - { + name: "MacOS 10.15 Apple Clang", + os: macos-10.15, + compiler: clang++, + comp: clang, + run_expensive_tests: false, + run_32bit_tests: false, + run_64bit_tests: true, + shell: 'bash {0}' + } + - { + name: "MacOS 10.15 GCC 10", + os: macos-10.15, + compiler: g++-10, + comp: gcc, + run_expensive_tests: false, + run_32bit_tests: false, + run_64bit_tests: true, + shell: 'bash {0}' + } + - { + name: "Windows 2019 Mingw-w64 GCC x86_64", + os: windows-2019, + compiler: g++, + comp: gcc, + run_expensive_tests: false, + run_32bit_tests: false, + run_64bit_tests: true, + msys_sys: 'mingw64', + msys_env: 'x86_64', + shell: 'msys2 {0}' + } + - { + name: "Windows 2019 Mingw-w64 GCC i686", + os: windows-2019, + compiler: g++, + comp: gcc, + run_expensive_tests: false, + run_32bit_tests: true, + run_64bit_tests: false, + msys_sys: 'mingw32', + msys_env: 'i686', + shell: 'msys2 {0}' } defaults: run: working-directory: src + shell: ${{ matrix.config.shell }} steps: - uses: actions/checkout@v2 with: fetch-depth: 0 - - name: Download required packages + - name: Download required linux packages + if: runner.os == 'Linux' run: | sudo apt update sudo apt install expect valgrind g++-multilib + - name: Setup msys and install required packages + if: runner.os == 'Windows' + uses: msys2/setup-msys2@v2 + with: + msystem: ${{matrix.config.msys_sys}} + install: mingw-w64-${{matrix.config.msys_env}}-gcc make git expect + - name: Download the used network from the fishtest framework run: | make net @@ -68,6 +127,7 @@ jobs: # x86-32 tests - name: Test debug x86-32 build + if: ${{ matrix.config.run_32bit_tests }} run: | export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" make clean @@ -75,24 +135,28 @@ jobs: ../tests/signature.sh $benchref - name: Test x86-32 build + if: ${{ matrix.config.run_32bit_tests }} run: | make clean make -j2 ARCH=x86-32 build ../tests/signature.sh $benchref - name: Test x86-32-sse41-popcnt build + if: ${{ matrix.config.run_32bit_tests }} run: | make clean make -j2 ARCH=x86-32-sse41-popcnt build ../tests/signature.sh $benchref - name: Test x86-32-sse2 build + if: ${{ matrix.config.run_32bit_tests }} run: | make clean make -j2 ARCH=x86-32-sse2 build ../tests/signature.sh $benchref - name: Test general-32 build + if: ${{ matrix.config.run_32bit_tests }} run: | make clean make -j2 ARCH=general-32 build @@ -101,6 +165,7 @@ jobs: # x86-64 tests - name: Test debug x86-64-modern build + if: ${{ matrix.config.run_64bit_tests }} run: | export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" make clean @@ -108,30 +173,35 @@ jobs: ../tests/signature.sh $benchref - name: Test x86-64-modern build + if: ${{ matrix.config.run_64bit_tests }} run: | make clean make -j2 ARCH=x86-64-modern build ../tests/signature.sh $benchref - name: Test x86-64-ssse3 build + if: ${{ matrix.config.run_64bit_tests }} run: | make clean make -j2 ARCH=x86-64-ssse3 build ../tests/signature.sh $benchref - name: Test x86-64-sse3-popcnt build + if: ${{ matrix.config.run_64bit_tests }} run: | make clean make -j2 ARCH=x86-64-sse3-popcnt build ../tests/signature.sh $benchref - name: Test x86-64 build + if: ${{ matrix.config.run_64bit_tests }} run: | make clean make -j2 ARCH=x86-64 build ../tests/signature.sh $benchref - name: Test general-64 build + if: matrix.config.run_64bit_tests run: | make clean make -j2 ARCH=general-64 build @@ -140,26 +210,31 @@ jobs: # x86-64 with newer extensions tests - name: Compile x86-64-avx2 build + if: ${{ matrix.config.run_64bit_tests }} run: | make clean make -j2 ARCH=x86-64-avx2 build - name: Compile x86-64-bmi2 build + if: ${{ matrix.config.run_64bit_tests }} run: | make clean make -j2 ARCH=x86-64-bmi2 build - name: Compile x86-64-avx512 build + if: ${{ matrix.config.run_64bit_tests }} run: | make clean make -j2 ARCH=x86-64-avx512 build - name: Compile x86-64-vnni512 build + if: ${{ matrix.config.run_64bit_tests }} run: | make clean make -j2 ARCH=x86-64-vnni512 build - name: Compile x86-64-vnni256 build + if: ${{ matrix.config.run_64bit_tests }} run: | make clean make -j2 ARCH=x86-64-vnni256 build @@ -167,6 +242,7 @@ jobs: # Other tests - name: Check perft and search reproducibility + if: ${{ matrix.config.run_64bit_tests }} run: | make clean make -j2 ARCH=x86-64-modern build diff --git a/AUTHORS b/AUTHORS index 7e63591a3b8..18471d4d0ec 100644 --- a/AUTHORS +++ b/AUTHORS @@ -107,6 +107,7 @@ Kojirion Krystian Kuzniarek (kuzkry) Leonardo Ljubičić (ICCF World Champion) Leonid Pechenik (lp--) +Liam Keegan (lkeegan) Linus Arver (listx) loco-loco Lub van den Berg (ElbertoOne) diff --git a/src/Makefile b/src/Makefile index 1d972cffc28..b021ba1214d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -405,8 +405,12 @@ ifeq ($(COMP),clang) endif ifeq ($(KERNEL),Darwin) - CXXFLAGS += -arch $(arch) -mmacosx-version-min=10.14 - LDFLAGS += -arch $(arch) -mmacosx-version-min=10.14 + CXXFLAGS += -mmacosx-version-min=10.14 + LDFLAGS += -mmacosx-version-min=10.14 + ifneq ($(arch),any) + CXXFLAGS += -arch $(arch) + LDFLAGS += -arch $(arch) + endif XCRUN = xcrun endif diff --git a/src/misc.cpp b/src/misc.cpp index 9e7b7e37b8c..3b071ccb108 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -378,6 +378,7 @@ void std_aligned_free(void* ptr) { static void* aligned_large_pages_alloc_windows(size_t allocSize) { #if !defined(_WIN64) + (void)allocSize; // suppress unused-parameter compiler warning return nullptr; #else From d957179df7285f8d032803661b378b7f1a80382e Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sun, 18 Jul 2021 13:51:14 +0300 Subject: [PATCH 0633/1766] Prune illegal moves in qsearch earlier The main idea is that illegal moves influencing search or qsearch obviously can't be any sort of good. The only reason why initially legality checks for search and qsearch were done after they actually can influence some heuristics is because legality check is expensive computationally. Eventually in search it was moved to the place where it makes sure that illegal moves can't influence search. This patch shows that the same can be done for qsearch + it passed STC with elo-gaining bounds + it removes 3 lines of code because one no longer needs to increment/decrement movecount on illegal moves. passed STC with elo-gaining bounds https://tests.stockfishchess.org/tests/view/60f20aefd1189bed71812da0 LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 61512 W: 4688 L: 4492 D: 52332 Ptnml(0-2): 139, 3730, 22848, 3874, 165 The same version functionally but with moving condition ever earlier passed LTC with simplification bounds. https://tests.stockfishchess.org/tests/view/60f292cad1189bed71812de9 LLR: 2.98 (-2.94,2.94) <-2.50,0.50> Total: 60944 W: 1724 L: 1685 D: 57535 Ptnml(0-2): 11, 1556, 27298, 1597, 10 closes https://github.com/official-stockfish/Stockfish/pull/3618 bench 4709569 --- src/search.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index fef1b518160..66402f0429a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1472,6 +1472,10 @@ namespace { { assert(is_ok(move)); + // Check for legality + if (!pos.legal(move)) + continue; + givesCheck = pos.gives_check(move); captureOrPromotion = pos.capture_or_promotion(move); @@ -1510,13 +1514,6 @@ namespace { // Speculative prefetch as early as possible prefetch(TT.first_entry(pos.key_after(move))); - // Check for legality just before making the move - if (!pos.legal(move)) - { - moveCount--; - continue; - } - ss->currentMove = move; ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck] [captureOrPromotion] From 760b7462bc89e4202ef6b43dc052b4364167ed4d Mon Sep 17 00:00:00 2001 From: pb00067 Date: Thu, 15 Jul 2021 20:56:21 +0200 Subject: [PATCH 0634/1766] Simplify lowply-history scoring logic STC: https://tests.stockfishchess.org/tests/view/60eee559d1189bed71812b16 LLR: 2.97 (-2.94,2.94) <-2.50,0.50> Total: 33976 W: 2523 L: 2431 D: 29022 Ptnml(0-2): 66, 2030, 12730, 2070, 92 LTC: https://tests.stockfishchess.org/tests/view/60eefa12d1189bed71812b24 LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 107240 W: 3053 L: 3046 D: 101141 Ptnml(0-2): 56, 2668, 48154, 2697, 45 closes https://github.com/official-stockfish/Stockfish/pull/3616 bench: 5199177 --- src/movepick.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 4ff4cff44b4..20640fe2b02 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -111,7 +111,7 @@ void MovePicker::score() { + (*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)] - + (ply < MAX_LPH ? std::min(4, depth / 3) * (*lowPlyHistory)[ply][from_to(m)] : 0); + + (ply < MAX_LPH ? 6 * (*lowPlyHistory)[ply][from_to(m)] : 0); else // Type == EVASIONS { From a85928e7ecf36431aee5bf48d46bfea4e6085cb7 Mon Sep 17 00:00:00 2001 From: Giacomo Lorenzetti Date: Sun, 18 Jul 2021 20:14:11 +0200 Subject: [PATCH 0635/1766] Apply good/bad history reduction also when inCheck Main idea is that, in some cases, 'in check' situations are not so different from 'not in check' ones. Trying to use piece count in order to select only a few 'in check' situations have failed LTC testing. It could be interesting to apply one of those ideas in other parts of the search function. passed STC: https://tests.stockfishchess.org/tests/view/60f1b68dd1189bed71812d40 LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 53472 W: 4078 L: 4008 D: 45386 Ptnml(0-2): 127, 3297, 19795, 3413, 104 passed LTC: https://tests.stockfishchess.org/tests/view/60f291e6d1189bed71812de3 LLR: 2.92 (-2.94,2.94) <-2.50,0.50> Total: 89712 W: 2651 L: 2632 D: 84429 Ptnml(0-2): 60, 2261, 40188, 2294, 53 closes https://github.com/official-stockfish/Stockfish/pull/3619 Bench: 5185789 --- AUTHORS | 1 + src/search.cpp | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index 18471d4d0ec..4d72314f77b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -69,6 +69,7 @@ gamander Gary Heckman (gheckman) George Sobala (gsobala) gguliash +Giacomo Lorenzetti (G-Lorenz) Gian-Carlo Pascutto (gcp) Gontran Lemaire (gonlem) Goodkov Vasiliy Aleksandrovich (goodkov) diff --git a/src/search.cpp b/src/search.cpp index 66402f0429a..c41df1f0fdc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1172,8 +1172,7 @@ namespace { - 4923; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - if (!ss->inCheck) - r -= ss->statScore / 14721; + r -= ss->statScore / 14721; } // In general we want to cap the LMR depth search at newDepth. But if From b939c805139e4b37f04fbf177f580c35ebe9f130 Mon Sep 17 00:00:00 2001 From: MichaelB7 Date: Sat, 24 Jul 2021 08:42:00 -0400 Subject: [PATCH 0636/1766] Update the default net to nn-76a8a7ffb820.nnue. combined work by Serio Vieri, Michael Byrne, and Jonathan D (aka SFisGod) based on top of previous developments, by restarts from good nets. Sergio generated the net https://tests.stockfishchess.org/api/nn/nn-d8609abe8caf.nnue: The initial net nn-d8609abe8caf.nnue is trained by generating around 16B of training data from the last master net nn-9e3c6298299a.nnue, then trained, continuing from the master net, with lambda=0.2 and sampling ratio of 1. Starting with LR=2e-3, dropping LR with a factor of 0.5 until it reaches LR=5e-4. in_scaling is set to 361. No other significant changes made to the pytorch trainer. Training data gen command (generates in chunks of 200k positions): generate_training_data min_depth 9 max_depth 11 count 200000 random_move_count 10 random_move_max_ply 80 random_multi_pv 12 random_multi_pv_diff 100 random_multi_pv_depth 8 write_min_ply 10 eval_limit 1500 book noob_3moves.epd output_file_name gendata/$(date +"%Y%m%d-%H%M")_${HOSTNAME}.binpack PyTorch trainer command (Note that this only trains for 20 epochs, repeatedly train until convergence): python train.py --features "HalfKAv2^" --max_epochs 20 --smart-fen-skipping --random-fen-skipping 500 --batch-size 8192 --default_root_dir $dir --seed $RANDOM --threads 4 --num-workers 32 --gpus $gpuids --track_grad_norm 2 --gradient_clip_val 0.05 --lambda 0.2 --log_every_n_steps 50 $resumeopt $data $val See https://github.com/sergiovieri/Stockfish/tree/tools_mod/rl for the scripts used to generate data. Based on that Michael generated nn-76a8a7ffb820.nnue in the following way: The net being submitted was trained with the pytorch trainer: https://github.com/glinscott/nnue-pytorch python train.py i:/bin/all.binpack i:/bin/all.binpack --gpus 1 --threads 4 --num-workers 30 --batch-size 16384 --progress_bar_refresh_rate 30 --smart-fen-skipping --random-fen-skipping 3 --features=HalfKAv2^ --auto_lr_find True --lambda=1.0 --max_epochs=240 --seed %random%%random% --default_root_dir exp/run_109 --resume-from-model ./pt/nn-d8609abe8caf.pt This run is thus started from Segio Vieri's net nn-d8609abe8caf.nnue all.binpack equaled 4 parts Wrong_NNUE_2.binpack https://drive.google.com/file/d/1seGNOqcVdvK_vPNq98j-zV3XPE5zWAeq/view?usp=sharing plus two parts of Training_Data.binpack https://drive.google.com/file/d/1RFkQES3DpsiJqsOtUshENtzPfFgUmEff/view?usp=sharing Each set was concatenated together - making one large Wrong_NNUE 2 binpack and one large Training so the were approximately equal in size. They were then interleaved together. The idea was to give Wrong_NNUE.binpack closer to equal weighting with the Training_Data binpack model.py modifications: loss = torch.pow(torch.abs(p - q), 2.6).mean() LR = 8.0e-5 calculated as follows: 1.5e-3*(.992^360) - the idea here was to take a highly trained net and just use all.binpack as a finishing micro refinement touch for the last 2 Elo or so. This net was discovered on the 59th epoch. optimizer = ranger.Ranger(train_params, betas=(.90, 0.999), eps=1.0e-7, gc_loc=False, use_gc=False) scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=1, gamma=0.992) For this micro optimization, I had set the period to "5" in train.py. This changes the checkpoint output so that every 5th checkpoint file is created The final touches were to adjust the NNUE scale, as was done by Jonathan in tests running at the same time. passed LTC https://tests.stockfishchess.org/tests/view/60fa45aed8a6b65b2f3a77a4 LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 53040 W: 1732 L: 1575 D: 49733 Ptnml(0-2): 14, 1432, 23474, 1583, 17 passed STC https://tests.stockfishchess.org/tests/view/60f9fee2d8a6b65b2f3a7775 LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 37928 W: 3178 L: 3001 D: 31749 Ptnml(0-2): 100, 2446, 13695, 2623, 100. closes https://github.com/official-stockfish/Stockfish/pull/3626 Bench: 5169957 --- src/evaluate.cpp | 4 ++-- src/evaluate.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 538214d3229..64f9172574a 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1090,7 +1090,7 @@ Value Eval::evaluate(const Position& pos) { // Scale and shift NNUE for compatibility with search and classical evaluation auto adjusted_NNUE = [&]() { - int scale = 903 + int scale = 883 + 32 * pos.count() + 32 * pos.non_pawn_material() / 1024; @@ -1106,7 +1106,7 @@ Value Eval::evaluate(const Position& pos) { // NNUE eval faster when shuffling or if the material on the board is high. int r50 = pos.rule50_count(); Value psq = Value(abs(eg_value(pos.psq_score()))); - bool classical = psq * 5 > (750 + pos.non_pawn_material() / 64) * (5 + r50); + bool classical = psq * 5 > (850 + pos.non_pawn_material() / 64) * (5 + r50); v = classical ? Evaluation(pos).value() // classical : adjusted_NNUE(); // NNUE diff --git a/src/evaluate.h b/src/evaluate.h index 54f20bafccc..0a580c61f2b 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-9e3c6298299a.nnue" + #define EvalFileDefaultName "nn-76a8a7ffb820.nnue" namespace NNUE { From 910d26b5c30bb68845e032a8f2aac82f19c96262 Mon Sep 17 00:00:00 2001 From: Giacomo Lorenzetti Date: Sat, 24 Jul 2021 22:03:29 +0200 Subject: [PATCH 0637/1766] Simplification in LMR This commit removes the `!captureOrPromotion` condition from ttCapture reduction and from good/bad history reduction (similar to #3619). passed STC: https://tests.stockfishchess.org/tests/view/60fc734ad8a6b65b2f3a7922 LLR: 2.97 (-2.94,2.94) <-2.50,0.50> Total: 48680 W: 3855 L: 3776 D: 41049 Ptnml(0-2): 118, 3145, 17744, 3206, 127 passed LTC: https://tests.stockfishchess.org/tests/view/60fce7d5d8a6b65b2f3a794c LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 86528 W: 2471 L: 2450 D: 81607 Ptnml(0-2): 28, 2203, 38777, 2232, 24 closes https://github.com/official-stockfish/Stockfish/pull/3629 Bench: 4951406 --- src/search.cpp | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c41df1f0fdc..f412d9d6f1a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1159,21 +1159,18 @@ namespace { if (cutNode && move != ss->killers[0]) r += 2; - if (!captureOrPromotion) - { - // Increase reduction if ttMove is a capture (~3 Elo) - if (ttCapture) - r++; - - ss->statScore = thisThread->mainHistory[us][from_to(move)] - + (*contHist[0])[movedPiece][to_sq(move)] - + (*contHist[1])[movedPiece][to_sq(move)] - + (*contHist[3])[movedPiece][to_sq(move)] - - 4923; - - // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - r -= ss->statScore / 14721; - } + // Increase reduction if ttMove is a capture (~3 Elo) + if (ttCapture) + r++; + + ss->statScore = thisThread->mainHistory[us][from_to(move)] + + (*contHist[0])[movedPiece][to_sq(move)] + + (*contHist[1])[movedPiece][to_sq(move)] + + (*contHist[3])[movedPiece][to_sq(move)] + - 4923; + + // Decrease/increase reduction for moves with a good/bad history (~30 Elo) + r -= ss->statScore / 14721; // In general we want to cap the LMR depth search at newDepth. But if // reductions are really negative and movecount is low, we allow this move From 237ed1ef8fddea77779e7fdcde8e4195d92f123b Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Sun, 25 Jul 2021 19:43:25 +0800 Subject: [PATCH 0638/1766] Update default net to nn-26abeed38351.nnue SPSA: https://tests.stockfishchess.org/tests/view/60fba335d8a6b65b2f3a7891 New best values: Half of the changes from the tuning run. Setting: nodestime=300 with 10+0.1 (approximate real TC is 2.5 seconds) The rest is the same as described in #3593 The change from nodestime=600 to 300 was suggested by gekkehenker to prevent time losses for some slow workers SFisGOD@94cd757#commitcomment-53324840 STC: LLR: 2.96 (-2.94,2.94) <-0.50,2.50> Total: 67448 W: 5241 L: 5036 D: 57171 Ptnml(0-2): 151, 4198, 24827, 4391, 157 https://tests.stockfishchess.org/tests/view/60fd50f2d8a6b65b2f3a798e LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 48752 W: 1504 L: 1358 D: 45890 Ptnml(0-2): 13, 1226, 21754, 1368, 15 https://tests.stockfishchess.org/tests/view/60fd7bb2d8a6b65b2f3a79a9 Closes https://github.com/official-stockfish/Stockfish/pull/3630 Bench: 5124774 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 0a580c61f2b..aa5c6260209 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-76a8a7ffb820.nnue" + #define EvalFileDefaultName "nn-26abeed38351.nnue" namespace NNUE { From e973eee919a8d450f095d102a0d52c196a8e7793 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Wed, 28 Jul 2021 00:43:58 +0800 Subject: [PATCH 0639/1766] Update default net to nn-56a5f1c4173a.nnue SPSA 1: https://tests.stockfishchess.org/tests/view/60fd24efd8a6b65b2f3a796e Parameters: A total of 256 net biases were tuned (hidden layer 2) New best values: Half of the changes from the tuning run New net: nn-5992d3ba79f3.nnue SPSA 2: https://tests.stockfishchess.org/tests/view/60fec7d6d8a6b65b2f3a7aa2 Parameters: A total of 128 net biases were tuned (hidden layer 1) New best values: Half of the changes from the tuning run New net: nn-56a5f1c4173a.nnue STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 140392 W: 10863 L: 10578 D: 118951 Ptnml(0-2): 347, 8754, 51718, 9021, 356 https://tests.stockfishchess.org/tests/view/610037e396b86d98abf6a79e LTC: LLR: 2.95 (-2.94,2.94) <0.50,3.50> Total: 14216 W: 454 L: 355 D: 13407 Ptnml(0-2): 4, 323, 6356, 420, 5 https://tests.stockfishchess.org/tests/view/61019995afad2da4f4ae3a3c Closes #3633 Bench: 4801359 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index aa5c6260209..e3730c8a44d 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-26abeed38351.nnue" + #define EvalFileDefaultName "nn-56a5f1c4173a.nnue" namespace NNUE { From 26edf9534ad571a6d26bf9db47d21776cbf45d54 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Tue, 27 Jul 2021 22:12:14 +0200 Subject: [PATCH 0640/1766] Avoid unnecessary stores in the affine transform This patch improves the codegen in the AffineTransform::forward function for architectures >=SSSE3. Current code works directly on memory and the compiler cannot see that the stores through outptr do not alias the loads through weights and input32. The solution implemented is to perform the affine transform with local variables as accumulators and only store the result to memory at the end. The number of accumulators required is OutputDimensions / OutputSimdWidth, which means that for the 1024->16 affine transform it requires 4 registers with SSSE3, 2 with AVX2, 1 with AVX512. It also cuts the number of stores required by NumRegs * 256 for each node evaluated. The local accumulators are expected to be assigned to registers, but even if this cannot be done in some case due to register pressure it will help the compiler to see that there is no aliasing between the loads and stores and may still result in better codegen. See https://godbolt.org/z/59aTKbbYc for codegen comparison. passed STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 140328 W: 10635 L: 10358 D: 119335 Ptnml(0-2): 302, 8339, 52636, 8554, 333 closes https://github.com/official-stockfish/Stockfish/pull/3634 No functional change --- src/nnue/layers/affine_transform.h | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 9a3b778e6bb..9a5f62c0a09 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -251,9 +251,6 @@ namespace Stockfish::Eval::NNUE::Layers { #endif #if defined (USE_SSSE3) - // Different layout, we process 4 inputs at a time, always. - static_assert(InputDimensions % 4 == 0); - const auto output = reinterpret_cast(buffer); const auto inputVector = reinterpret_cast(input); @@ -263,13 +260,18 @@ namespace Stockfish::Eval::NNUE::Layers { // because then it is also an input dimension. if constexpr (OutputDimensions % OutputSimdWidth == 0) { + static_assert(InputDimensions % 16 == 0); + constexpr IndexType NumChunks = InputDimensions / 4; + constexpr IndexType NumRegs = OutputDimensions / OutputSimdWidth; const auto input32 = reinterpret_cast(input); - vec_t* outptr = reinterpret_cast(output); - std::memcpy(output, biases, OutputDimensions * sizeof(OutputType)); + const vec_t* biasvec = reinterpret_cast(biases); + vec_t outs[NumRegs]; + for (IndexType k = 0; k < NumRegs; ++k) + outs[k] = biasvec[k]; - for (int i = 0; i < (int)NumChunks - 3; i += 4) + for (IndexType i = 0; i < NumChunks; i += 4) { const vec_t in0 = vec_set_32(input32[i + 0]); const vec_t in1 = vec_set_32(input32[i + 1]); @@ -279,12 +281,18 @@ namespace Stockfish::Eval::NNUE::Layers { const auto col1 = reinterpret_cast(&weights[(i + 1) * OutputDimensions * 4]); const auto col2 = reinterpret_cast(&weights[(i + 2) * OutputDimensions * 4]); const auto col3 = reinterpret_cast(&weights[(i + 3) * OutputDimensions * 4]); - for (int j = 0; j * OutputSimdWidth < OutputDimensions; ++j) - vec_add_dpbusd_32x4(outptr[j], in0, col0[j], in1, col1[j], in2, col2[j], in3, col3[j]); + for (IndexType k = 0; k < NumRegs; ++k) + vec_add_dpbusd_32x4(outs[k], in0, col0[k], in1, col1[k], in2, col2[k], in3, col3[k]); } + + vec_t* outptr = reinterpret_cast(output); + for (IndexType k = 0; k < NumRegs; ++k) + outptr[k] = outs[k]; } else if constexpr (OutputDimensions == 1) { + static_assert(InputDimensions % 4 == 0); + #if defined (USE_AVX512) if constexpr (PaddedInputDimensions % (SimdWidth * 2) != 0) { From a0fca67da4c5ae21bcc783acc03f3da2a6fbcd80 Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Sat, 31 Jul 2021 08:18:49 -0400 Subject: [PATCH 0641/1766] CMH Pruning Tweak replace CounterMovePruneThreshold by a depth dependent threshold STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 35512 W: 2718 L: 2552 D: 30242 Ptnml(0-2): 66, 2138, 13194, 2280, 78 https://tests.stockfishchess.org/tests/view/6104442fafad2da4f4ae3b94 LTC: LLR: 2.96 (-2.94,2.94) <0.50,3.50> Total: 36536 W: 1150 L: 1019 D: 34367 Ptnml(0-2): 10, 920, 16278, 1049, 11 https://tests.stockfishchess.org/tests/view/6104b033afad2da4f4ae3bbc closes https://github.com/official-stockfish/Stockfish/pull/3636 Bench: 5848718 --- src/search.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index f412d9d6f1a..6275cdf283e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -907,7 +907,7 @@ namespace { && !ttMove) depth -= 2; -moves_loop: // When in check, search starts from here +moves_loop: // When in check, search starts here ttCapture = ttMove && pos.capture_or_promotion(ttMove); @@ -1017,8 +1017,8 @@ namespace { { // Continuation history based pruning (~20 Elo) if ( lmrDepth < 5 - && (*contHist[0])[movedPiece][to_sq(move)] < CounterMovePruneThreshold - && (*contHist[1])[movedPiece][to_sq(move)] < CounterMovePruneThreshold) + && (*contHist[0])[movedPiece][to_sq(move)] < (depth == 1 ? 0 : -stat_bonus(depth-1)) + && (*contHist[1])[movedPiece][to_sq(move)] < (depth == 1 ? 0 : -stat_bonus(depth-1))) continue; // Futility pruning: parent node (~5 Elo) From 31ebd918ea54b8c9fccbc1e1fa269772ed0949df Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Mon, 2 Aug 2021 13:52:48 -0400 Subject: [PATCH 0642/1766] Futile pruning simplification Remove CMH conditions in futile pruning. STC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 93520 W: 7165 L: 7138 D: 79217 Ptnml(0-2): 222, 5923, 34427, 5982, 206 https://tests.stockfishchess.org/tests/view/61083104e50a153c346ef8df LTC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 59072 W: 1746 L: 1706 D: 55620 Ptnml(0-2): 13, 1562, 26353, 1588, 20 https://tests.stockfishchess.org/tests/view/610894f2e50a153c346ef913 closes https://github.com/official-stockfish/Stockfish/pull/3638 Bench: 5229673 --- src/search.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 6275cdf283e..187f21694ef 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1023,11 +1023,7 @@ namespace { // Futility pruning: parent node (~5 Elo) if ( !ss->inCheck - && ss->staticEval + 174 + 157 * lmrDepth <= alpha - && (*contHist[0])[movedPiece][to_sq(move)] - + (*contHist[1])[movedPiece][to_sq(move)] - + (*contHist[3])[movedPiece][to_sq(move)] - + (*contHist[5])[movedPiece][to_sq(move)] / 3 < 28255) + && ss->staticEval + 174 + 157 * lmrDepth <= alpha) continue; // Prune moves with negative SEE (~20 Elo) From 5cd42f6b0b30b7b3332367230aeef2efc93e5aad Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Tue, 3 Aug 2021 16:32:48 +0200 Subject: [PATCH 0643/1766] Simplify new cmh pruning thresholds by using directly a quadratic formula. This decouples also the stat bonus updates from the threshold which creates less dependencies for tuning of stat bonus parameters. Perhaps a further fine tuning of the now separated coefficients for constHist[0] and constHist[1] could give further gains. STC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 78384 W: 6134 L: 6090 D: 66160 Ptnml(0-2): 207, 5013, 28705, 5063, 204 https://tests.stockfishchess.org/tests/view/6106d235afad2da4f4ae3d4b LTC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 38176 W: 1149 L: 1095 D: 35932 Ptnml(0-2): 6, 1000, 17030, 1038, 14 https://tests.stockfishchess.org/tests/view/6107a080afad2da4f4ae3def closes https://github.com/official-stockfish/Stockfish/pull/3639 Bench: 5098146 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 187f21694ef..af99c90271a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1017,8 +1017,8 @@ namespace { { // Continuation history based pruning (~20 Elo) if ( lmrDepth < 5 - && (*contHist[0])[movedPiece][to_sq(move)] < (depth == 1 ? 0 : -stat_bonus(depth-1)) - && (*contHist[1])[movedPiece][to_sq(move)] < (depth == 1 ? 0 : -stat_bonus(depth-1))) + && (*contHist[0])[movedPiece][to_sq(move)] < 23 - 23 * depth * depth + && (*contHist[1])[movedPiece][to_sq(move)] < 23 - 23 * depth * depth) continue; // Futility pruning: parent node (~5 Elo) From 73ef5b8c4a3ef5e37cc62344d4393b1ee366b7b4 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Wed, 4 Aug 2021 19:26:06 +0800 Subject: [PATCH 0644/1766] Update default net to nn-46832cfbead3.nnue SPSA 1: https://tests.stockfishchess.org/tests/view/6100e7f096b86d98abf6a832 Parameters: A total of 256 net weights and 8 net biases were tuned (output layer) Base net: nn-56a5f1c4173a.nnue New net: nn-ec3c8e029926.nnue SPSA 2: https://tests.stockfishchess.org/tests/view/610733caafad2da4f4ae3da7 Parameters: A total of 256 net biases were tuned (hidden layer 2) Base net: nn-ec3c8e029926.nnue New net: nn-46832cfbead3.nnue STC: LLR: 2.98 (-2.94,2.94) <-0.50,2.50> Total: 50520 W: 3953 L: 3765 D: 42802 Ptnml(0-2): 138, 3063, 18678, 3235, 146 https://tests.stockfishchess.org/tests/view/610a79692a8a49ac5be793f4 LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 57256 W: 1723 L: 1566 D: 53967 Ptnml(0-2): 12, 1442, 25568, 1589, 17 https://tests.stockfishchess.org/tests/view/610ac5bb2a8a49ac5be79434 Closes https://github.com/official-stockfish/Stockfish/pull/3642 Bench: 5359314 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index e3730c8a44d..1ac224b69be 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-56a5f1c4173a.nnue" + #define EvalFileDefaultName "nn-46832cfbead3.nnue" namespace NNUE { From a1a83f38691ae3f39c827a46e59ae9875605aa3b Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Thu, 5 Aug 2021 08:50:24 -0400 Subject: [PATCH 0645/1766] SEE simplification Simplified SEE formula by removing std::min. Should also be easier to tune. STC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 22656 W: 1836 L: 1729 D: 19091 Ptnml(0-2): 54, 1426, 8267, 1521, 60 https://tests.stockfishchess.org/tests/view/610ae62f2a8a49ac5be79449 LTC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 26248 W: 806 L: 744 D: 24698 Ptnml(0-2): 6, 668, 11715, 728, 7 https://tests.stockfishchess.org/tests/view/610b17ad2a8a49ac5be79466 closes https://github.com/official-stockfish/Stockfish/pull/3643 bench: 4915145 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index af99c90271a..af8ddaba5a4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1027,7 +1027,7 @@ namespace { continue; // Prune moves with negative SEE (~20 Elo) - if (!pos.see_ge(move, Value(-(30 - std::min(lmrDepth, 18)) * lmrDepth * lmrDepth))) + if (!pos.see_ge(move, Value(-21 * lmrDepth * lmrDepth - 21 * lmrDepth))) continue; } } From dabaf2220fe0c77400a5f71a91952f510e6a126b Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 5 Aug 2021 16:34:37 +0200 Subject: [PATCH 0646/1766] Revert futility pruning patches reverts 09b6d28391cf582d99897360b225bcbbe38dd1c6 and dbd7f602d3c7622df294f87d7239b5aaf31f695f that significantly impact mate finding capabilities. For example on ChestUCI_23102018.epd, at 1M nodes, the number of mates found is nearly reduced 2x without these depth conditions: sf6 2091 sf7 2093 sf8 2107 sf9 2062 sf10 2208 sf11 2552 sf12 2563 sf13 2509 sf14 2427 master 1246 patched 2467 (script for testing at https://github.com/official-stockfish/Stockfish/files/6936412/matecheck.zip) closes https://github.com/official-stockfish/Stockfish/pull/3641 fixes https://github.com/official-stockfish/Stockfish/issues/3627 Bench: 5467570 --- src/search.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index af8ddaba5a4..c48b74bcc76 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -779,8 +779,10 @@ namespace { ? ss->staticEval > (ss-4)->staticEval || (ss-4)->staticEval == VALUE_NONE : ss->staticEval > (ss-2)->staticEval; - // Step 7. Futility pruning: child node (~50 Elo) + // Step 7. Futility pruning: child node (~50 Elo). + // The depth condition is important for mate finding. if ( !PvNode + && depth < 9 && eval - futility_margin(depth, improving) >= beta && eval < VALUE_KNOWN_WIN) // Do not return unproven wins return eval; @@ -989,7 +991,7 @@ namespace { // Calculate new depth for this move newDepth = depth - 1; - // Step 13. Pruning at shallow depth (~200 Elo) + // Step 13. Pruning at shallow depth (~200 Elo). Depth conditions are important for mate finding. if ( !rootNode && pos.non_pawn_material(us) && bestValue > VALUE_TB_LOSS_IN_MAX_PLY) @@ -1023,6 +1025,7 @@ namespace { // Futility pruning: parent node (~5 Elo) if ( !ss->inCheck + && lmrDepth < 7 && ss->staticEval + 174 + 157 * lmrDepth <= alpha) continue; From d61d38586ee35fd4d93445eb547e4af27cc86e6b Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Tue, 27 Jul 2021 15:00:22 +0200 Subject: [PATCH 0647/1766] New NNUE architecture and net Introduces a new NNUE network architecture and associated network parameters The summary of the changes: * Position for each perspective mirrored such that the king is on e..h files. Cuts the feature transformer size in half, while preserving enough knowledge to be good. See https://docs.google.com/document/d/1gTlrr02qSNKiXNZ_SuO4-RjK4MXBiFlLE6jvNqqMkAY/edit#heading=h.b40q4rb1w7on. * The number of neurons after the feature transformer increased two-fold, to 1024x2. This is possibly mostly due to the now very optimized feature transformer update code. * The number of neurons after the second layer is reduced from 16 to 8, to reduce the speed impact. This, perhaps surprisingly, doesn't harm the strength much. See https://docs.google.com/document/d/1gTlrr02qSNKiXNZ_SuO4-RjK4MXBiFlLE6jvNqqMkAY/edit#heading=h.6qkocr97fezq The AffineTransform code did not work out-of-the box with the smaller number of neurons after the second layer, so some temporary changes have been made to add a special case for InputDimensions == 8. Also additional 0 padding is added to the output for some archs that cannot process inputs by <=8 (SSE2, NEON). VNNI uses an implementation that can keep all outputs in the registers while reducing the number of loads by 3 for each 16 inputs, thanks to the reduced number of output neurons. However GCC is particularily bad at optimization here (and perhaps why the current way the affine transform is done even passed sprt) (see https://docs.google.com/document/d/1gTlrr02qSNKiXNZ_SuO4-RjK4MXBiFlLE6jvNqqMkAY/edit# for details) and more work will be done on this in the following days. I expect the current VNNI implementation to be improved and extended to other architectures. The network was trained with a slightly modified version of the pytorch trainer (https://github.com/glinscott/nnue-pytorch); the changes are in https://github.com/glinscott/nnue-pytorch/pull/143 The training utilized 2 datasets. dataset A - https://drive.google.com/file/d/1VlhnHL8f-20AXhGkILujnNXHwy9T-MQw/view?usp=sharing dataset B - as described in https://github.com/official-stockfish/Stockfish/commit/ba01f4b95448bcb324755f4dd2a632a57c6e67bc The training process was as following: train on dataset A for 350 epochs, take the best net in terms of elo at 20k nodes per move (it's fine to take anything from later stages of training). convert the .ckpt to .pt --resume-from-model from the .pt file, train on dataset B for <600 epochs, take the best net. Lambda=0.8, applied before the loss function. The first training command: python3 train.py \ ../nnue-pytorch-training/data/large_gensfen_multipvdiff_100_d9.binpack \ ../nnue-pytorch-training/data/large_gensfen_multipvdiff_100_d9.binpack \ --gpus "$3," \ --threads 1 \ --num-workers 1 \ --batch-size 16384 \ --progress_bar_refresh_rate 20 \ --smart-fen-skipping \ --random-fen-skipping 3 \ --features=HalfKAv2_hm^ \ --lambda=1.0 \ --max_epochs=600 \ --default_root_dir ../nnue-pytorch-training/experiment_$1/run_$2 The second training command: python3 serialize.py \ --features=HalfKAv2_hm^ \ ../nnue-pytorch-training/experiment_131/run_6/default/version_0/checkpoints/epoch-499.ckpt \ ../nnue-pytorch-training/experiment_$1/base/base.pt python3 train.py \ ../nnue-pytorch-training/data/michael_commit_b94a65.binpack \ ../nnue-pytorch-training/data/michael_commit_b94a65.binpack \ --gpus "$3," \ --threads 1 \ --num-workers 1 \ --batch-size 16384 \ --progress_bar_refresh_rate 20 \ --smart-fen-skipping \ --random-fen-skipping 3 \ --features=HalfKAv2_hm^ \ --lambda=0.8 \ --max_epochs=600 \ --resume-from-model ../nnue-pytorch-training/experiment_$1/base/base.pt \ --default_root_dir ../nnue-pytorch-training/experiment_$1/run_$2 STC: https://tests.stockfishchess.org/tests/view/611120b32a8a49ac5be798c4 LLR: 2.97 (-2.94,2.94) <-0.50,2.50> Total: 22480 W: 2434 L: 2251 D: 17795 Ptnml(0-2): 101, 1736, 7410, 1865, 128 LTC: https://tests.stockfishchess.org/tests/view/611152b32a8a49ac5be798ea LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 9776 W: 442 L: 333 D: 9001 Ptnml(0-2): 5, 295, 4180, 402, 6 closes https://github.com/official-stockfish/Stockfish/pull/3646 bench: 5189338 --- src/Makefile | 2 +- src/evaluate.h | 2 +- .../{half_ka_v2.cpp => half_ka_v2_hm.cpp} | 30 +-- .../{half_ka_v2.h => half_ka_v2_hm.h} | 31 ++- src/nnue/layers/affine_transform.h | 220 ++++++++++++++++-- src/nnue/nnue_architecture.h | 8 +- 6 files changed, 247 insertions(+), 46 deletions(-) rename src/nnue/features/{half_ka_v2.cpp => half_ka_v2_hm.cpp} (68%) rename src/nnue/features/{half_ka_v2.h => half_ka_v2_hm.h} (80%) diff --git a/src/Makefile b/src/Makefile index b021ba1214d..c1dab86ba03 100644 --- a/src/Makefile +++ b/src/Makefile @@ -41,7 +41,7 @@ endif SRCS = benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.cpp main.cpp \ material.cpp misc.cpp movegen.cpp movepick.cpp pawns.cpp position.cpp psqt.cpp \ search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \ - nnue/evaluate_nnue.cpp nnue/features/half_ka_v2.cpp + nnue/evaluate_nnue.cpp nnue/features/half_ka_v2_hm.cpp OBJS = $(notdir $(SRCS:.cpp=.o)) diff --git a/src/evaluate.h b/src/evaluate.h index 1ac224b69be..4430a28c807 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-46832cfbead3.nnue" + #define EvalFileDefaultName "nn-e8321e467bf6.nnue" namespace NNUE { diff --git a/src/nnue/features/half_ka_v2.cpp b/src/nnue/features/half_ka_v2_hm.cpp similarity index 68% rename from src/nnue/features/half_ka_v2.cpp rename to src/nnue/features/half_ka_v2_hm.cpp index 57f43e50f2f..098a6d60e4a 100644 --- a/src/nnue/features/half_ka_v2.cpp +++ b/src/nnue/features/half_ka_v2_hm.cpp @@ -16,31 +16,32 @@ along with this program. If not, see . */ -//Definition of input features HalfKAv2 of NNUE evaluation function +//Definition of input features HalfKAv2_hm of NNUE evaluation function -#include "half_ka_v2.h" +#include "half_ka_v2_hm.h" #include "../../position.h" namespace Stockfish::Eval::NNUE::Features { // Orient a square according to perspective (rotates by 180 for black) - inline Square HalfKAv2::orient(Color perspective, Square s) { - return Square(int(s) ^ (bool(perspective) * 56)); + inline Square HalfKAv2_hm::orient(Color perspective, Square s, Square ksq) { + return Square(int(s) ^ (bool(perspective) * SQ_A8) ^ ((file_of(ksq) < FILE_E) * SQ_H1)); } // Index of a feature for a given king position and another piece on some square - inline IndexType HalfKAv2::make_index(Color perspective, Square s, Piece pc, Square ksq) { - return IndexType(orient(perspective, s) + PieceSquareIndex[perspective][pc] + PS_NB * ksq); + inline IndexType HalfKAv2_hm::make_index(Color perspective, Square s, Piece pc, Square ksq) { + Square o_ksq = orient(perspective, ksq, ksq); + return IndexType(orient(perspective, s, ksq) + PieceSquareIndex[perspective][pc] + PS_NB * KingBuckets[o_ksq]); } // Get a list of indices for active features - void HalfKAv2::append_active_indices( + void HalfKAv2_hm::append_active_indices( const Position& pos, Color perspective, ValueListInserter active ) { - Square ksq = orient(perspective, pos.square(perspective)); + Square ksq = pos.square(perspective); Bitboard bb = pos.pieces(); while (bb) { @@ -52,7 +53,7 @@ namespace Stockfish::Eval::NNUE::Features { // append_changed_indices() : get a list of indices for recently changed features - void HalfKAv2::append_changed_indices( + void HalfKAv2_hm::append_changed_indices( Square ksq, StateInfo* st, Color perspective, @@ -60,25 +61,24 @@ namespace Stockfish::Eval::NNUE::Features { ValueListInserter added ) { const auto& dp = st->dirtyPiece; - Square oriented_ksq = orient(perspective, ksq); for (int i = 0; i < dp.dirty_num; ++i) { Piece pc = dp.piece[i]; if (dp.from[i] != SQ_NONE) - removed.push_back(make_index(perspective, dp.from[i], pc, oriented_ksq)); + removed.push_back(make_index(perspective, dp.from[i], pc, ksq)); if (dp.to[i] != SQ_NONE) - added.push_back(make_index(perspective, dp.to[i], pc, oriented_ksq)); + added.push_back(make_index(perspective, dp.to[i], pc, ksq)); } } - int HalfKAv2::update_cost(StateInfo* st) { + int HalfKAv2_hm::update_cost(StateInfo* st) { return st->dirtyPiece.dirty_num; } - int HalfKAv2::refresh_cost(const Position& pos) { + int HalfKAv2_hm::refresh_cost(const Position& pos) { return pos.count(); } - bool HalfKAv2::requires_refresh(StateInfo* st, Color perspective) { + bool HalfKAv2_hm::requires_refresh(StateInfo* st, Color perspective) { return st->dirtyPiece.piece[0] == make_piece(perspective, KING); } diff --git a/src/nnue/features/half_ka_v2.h b/src/nnue/features/half_ka_v2_hm.h similarity index 80% rename from src/nnue/features/half_ka_v2.h rename to src/nnue/features/half_ka_v2_hm.h index e4b2edd9fc0..2c1144f64fb 100644 --- a/src/nnue/features/half_ka_v2.h +++ b/src/nnue/features/half_ka_v2_hm.h @@ -18,8 +18,8 @@ //Definition of input features HalfKP of NNUE evaluation function -#ifndef NNUE_FEATURES_HALF_KA_V2_H_INCLUDED -#define NNUE_FEATURES_HALF_KA_V2_H_INCLUDED +#ifndef NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED +#define NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED #include "../nnue_common.h" @@ -32,9 +32,9 @@ namespace Stockfish { namespace Stockfish::Eval::NNUE::Features { - // Feature HalfKAv2: Combination of the position of own king - // and the position of pieces - class HalfKAv2 { + // Feature HalfKAv2_hm: Combination of the position of own king + // and the position of pieces. Position mirrored such that king always on e..h files. + class HalfKAv2_hm { // unique number for each piece type on each square enum { @@ -63,21 +63,32 @@ namespace Stockfish::Eval::NNUE::Features { }; // Orient a square according to perspective (rotates by 180 for black) - static Square orient(Color perspective, Square s); + static Square orient(Color perspective, Square s, Square ksq); // Index of a feature for a given king position and another piece on some square static IndexType make_index(Color perspective, Square s, Piece pc, Square ksq); public: // Feature name - static constexpr const char* Name = "HalfKAv2(Friend)"; + static constexpr const char* Name = "HalfKAv2_hm(Friend)"; // Hash value embedded in the evaluation file - static constexpr std::uint32_t HashValue = 0x5f234cb8u; + static constexpr std::uint32_t HashValue = 0x7f234cb8u; // Number of feature dimensions static constexpr IndexType Dimensions = - static_cast(SQUARE_NB) * static_cast(PS_NB); + static_cast(SQUARE_NB) * static_cast(PS_NB) / 2; + + static constexpr int KingBuckets[64] = { + -1, -1, -1, -1, 31, 30, 29, 28, + -1, -1, -1, -1, 27, 26, 25, 24, + -1, -1, -1, -1, 23, 22, 21, 20, + -1, -1, -1, -1, 19, 18, 17, 16, + -1, -1, -1, -1, 15, 14, 13, 12, + -1, -1, -1, -1, 11, 10, 9, 8, + -1, -1, -1, -1, 7, 6, 5, 4, + -1, -1, -1, -1, 3, 2, 1, 0 + }; // Maximum number of simultaneously active features. static constexpr IndexType MaxActiveDimensions = 32; @@ -108,4 +119,4 @@ namespace Stockfish::Eval::NNUE::Features { } // namespace Stockfish::Eval::NNUE::Features -#endif // #ifndef NNUE_FEATURES_HALF_KA_V2_H_INCLUDED +#endif // #ifndef NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 9a5f62c0a09..d131836865f 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -46,6 +46,11 @@ namespace Stockfish::Eval::NNUE::Layers { #elif defined (USE_SSSE3) static constexpr const IndexType OutputSimdWidth = SimdWidth / 4; #endif +#if defined (USE_AVX512) + static constexpr const IndexType InputSimdWidth = SimdWidth * 2; +#elif defined (USE_SSSE3) + static constexpr const IndexType InputSimdWidth = SimdWidth; +#endif // Size of forward propagation buffer used in this layer static constexpr std::size_t SelfBufferSize = @@ -72,6 +77,15 @@ namespace Stockfish::Eval::NNUE::Layers { for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) #if !defined (USE_SSSE3) weights[i] = read_little_endian(stream); +#elif defined (USE_VNNI) || defined (USE_AVX512) + if constexpr (OutputDimensions <= 8 && OutputDimensions != 1) + weights[i] = read_little_endian(stream); + else + weights[ + (i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 + + i / PaddedInputDimensions * 4 + + i % 4 + ] = read_little_endian(stream); #else weights[ (i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 + @@ -108,7 +122,6 @@ namespace Stockfish::Eval::NNUE::Layers { return !stream.fail(); } - // Forward propagation const OutputType* propagate( const TransformedFeatureType* transformedFeatures, char* buffer) const { @@ -123,6 +136,40 @@ namespace Stockfish::Eval::NNUE::Layers { return _mm512_reduce_add_epi32(sum) + bias; }; + [[maybe_unused]] auto m512_hadd128x16_interleave = []( + __m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3) -> __m512i { + + __m512i sum01a = _mm512_unpacklo_epi32(sum0, sum1); + __m512i sum01b = _mm512_unpackhi_epi32(sum0, sum1); + + __m512i sum23a = _mm512_unpacklo_epi32(sum2, sum3); + __m512i sum23b = _mm512_unpackhi_epi32(sum2, sum3); + + __m512i sum01 = _mm512_add_epi32(sum01a, sum01b); + __m512i sum23 = _mm512_add_epi32(sum23a, sum23b); + + __m512i sum0123a = _mm512_unpacklo_epi64(sum01, sum23); + __m512i sum0123b = _mm512_unpackhi_epi64(sum01, sum23); + + return _mm512_add_epi32(sum0123a, sum0123b); + }; + + [[maybe_unused]] auto m512_haddx4 = [m512_hadd128x16_interleave]( + __m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3, __m128i bias) -> __m128i { + + __m512i sum = m512_hadd128x16_interleave(sum0, sum1, sum2, sum3); + + __m256i sum256lo = _mm512_castsi512_si256(sum); + __m256i sum256hi = _mm512_extracti64x4_epi64(sum, 1); + + sum256lo = _mm256_add_epi32(sum256lo, sum256hi); + + __m128i sum128lo = _mm256_castsi256_si128(sum256lo); + __m128i sum128hi = _mm256_extracti128_si256(sum256lo, 1); + + return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias); + }; + [[maybe_unused]] auto m512_add_dpbusd_epi32 = [=](__m512i& acc, __m512i a, __m512i b) { #if defined (USE_VNNI) acc = _mm512_dpbusd_epi32(acc, a, b); @@ -133,6 +180,19 @@ namespace Stockfish::Eval::NNUE::Layers { #endif }; + [[maybe_unused]] auto m512_add_dpbusd_epi32x2 = [=](__m512i& acc, __m512i a0, __m512i b0, __m512i a1, __m512i b1) { +#if defined (USE_VNNI) + acc = _mm512_dpbusd_epi32(acc, a0, b0); + acc = _mm512_dpbusd_epi32(acc, a1, b1); +#else + __m512i product0 = _mm512_maddubs_epi16(a0, b0); + __m512i product1 = _mm512_maddubs_epi16(a1, b1); + product0 = _mm512_adds_epi16(product0, product1); + product0 = _mm512_madd_epi16(product0, Ones512); + acc = _mm512_add_epi32(acc, product0); +#endif + }; + [[maybe_unused]] auto m512_add_dpbusd_epi32x4 = [=](__m512i& acc, __m512i a0, __m512i b0, __m512i a1, __m512i b1, __m512i a2, __m512i b2, __m512i a3, __m512i b3) { #if defined (USE_VNNI) @@ -165,6 +225,18 @@ namespace Stockfish::Eval::NNUE::Layers { return _mm_cvtsi128_si32(sum128) + bias; }; + [[maybe_unused]] auto m256_haddx4 = [](__m256i sum0, __m256i sum1, __m256i sum2, __m256i sum3, __m128i bias) -> __m128i { + sum0 = _mm256_hadd_epi32(sum0, sum1); + sum2 = _mm256_hadd_epi32(sum2, sum3); + + sum0 = _mm256_hadd_epi32(sum0, sum2); + + __m128i sum128lo = _mm256_castsi256_si128(sum0); + __m128i sum128hi = _mm256_extracti128_si256(sum0, 1); + + return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias); + }; + [[maybe_unused]] auto m256_add_dpbusd_epi32 = [=](__m256i& acc, __m256i a, __m256i b) { #if defined (USE_VNNI) acc = _mm256_dpbusd_epi32(acc, a, b); @@ -175,6 +247,19 @@ namespace Stockfish::Eval::NNUE::Layers { #endif }; + [[maybe_unused]] auto m256_add_dpbusd_epi32x2 = [=](__m256i& acc, __m256i a0, __m256i b0, __m256i a1, __m256i b1) { +#if defined (USE_VNNI) + acc = _mm256_dpbusd_epi32(acc, a0, b0); + acc = _mm256_dpbusd_epi32(acc, a1, b1); +#else + __m256i product0 = _mm256_maddubs_epi16(a0, b0); + __m256i product1 = _mm256_maddubs_epi16(a1, b1); + product0 = _mm256_adds_epi16(product0, product1); + product0 = _mm256_madd_epi16(product0, Ones256); + acc = _mm256_add_epi32(acc, product0); +#endif + }; + [[maybe_unused]] auto m256_add_dpbusd_epi32x4 = [=](__m256i& acc, __m256i a0, __m256i b0, __m256i a1, __m256i b1, __m256i a2, __m256i b2, __m256i a3, __m256i b3) { #if defined (USE_VNNI) @@ -206,12 +291,27 @@ namespace Stockfish::Eval::NNUE::Layers { return _mm_cvtsi128_si32(sum) + bias; }; + [[maybe_unused]] auto m128_haddx4 = [](__m128i sum0, __m128i sum1, __m128i sum2, __m128i sum3, __m128i bias) -> __m128i { + sum0 = _mm_hadd_epi32(sum0, sum1); + sum2 = _mm_hadd_epi32(sum2, sum3); + sum0 = _mm_hadd_epi32(sum0, sum2); + return _mm_add_epi32(sum0, bias); + }; + [[maybe_unused]] auto m128_add_dpbusd_epi32 = [=](__m128i& acc, __m128i a, __m128i b) { __m128i product0 = _mm_maddubs_epi16(a, b); product0 = _mm_madd_epi16(product0, Ones128); acc = _mm_add_epi32(acc, product0); }; + [[maybe_unused]] auto m128_add_dpbusd_epi32x2 = [=](__m128i& acc, __m128i a0, __m128i b0, __m128i a1, __m128i b1) { + __m128i product0 = _mm_maddubs_epi16(a0, b0); + __m128i product1 = _mm_maddubs_epi16(a1, b1); + product0 = _mm_adds_epi16(product0, product1); + product0 = _mm_madd_epi16(product0, Ones128); + acc = _mm_add_epi32(acc, product0); + }; + [[maybe_unused]] auto m128_add_dpbusd_epi32x4 = [=](__m128i& acc, __m128i a0, __m128i b0, __m128i a1, __m128i b1, __m128i a2, __m128i b2, __m128i a3, __m128i b3) { __m128i product0 = _mm_maddubs_epi16(a0, b0); @@ -231,33 +331,116 @@ namespace Stockfish::Eval::NNUE::Layers { using vec_t = __m512i; #define vec_setzero _mm512_setzero_si512 #define vec_set_32 _mm512_set1_epi32 - auto& vec_add_dpbusd_32 = m512_add_dpbusd_epi32; - auto& vec_add_dpbusd_32x4 = m512_add_dpbusd_epi32x4; - auto& vec_hadd = m512_hadd; + [[maybe_unused]] auto& vec_add_dpbusd_32 = m512_add_dpbusd_epi32; + [[maybe_unused]] auto& vec_add_dpbusd_32x2 = m512_add_dpbusd_epi32x2; + [[maybe_unused]] auto& vec_add_dpbusd_32x4 = m512_add_dpbusd_epi32x4; + [[maybe_unused]] auto& vec_hadd = m512_hadd; + [[maybe_unused]] auto& vec_haddx4 = m512_haddx4; #elif defined (USE_AVX2) using vec_t = __m256i; #define vec_setzero _mm256_setzero_si256 #define vec_set_32 _mm256_set1_epi32 - auto& vec_add_dpbusd_32 = m256_add_dpbusd_epi32; - auto& vec_add_dpbusd_32x4 = m256_add_dpbusd_epi32x4; - auto& vec_hadd = m256_hadd; + [[maybe_unused]] auto& vec_add_dpbusd_32 = m256_add_dpbusd_epi32; + [[maybe_unused]] auto& vec_add_dpbusd_32x2 = m256_add_dpbusd_epi32x2; + [[maybe_unused]] auto& vec_add_dpbusd_32x4 = m256_add_dpbusd_epi32x4; + [[maybe_unused]] auto& vec_hadd = m256_hadd; + [[maybe_unused]] auto& vec_haddx4 = m256_haddx4; #elif defined (USE_SSSE3) using vec_t = __m128i; #define vec_setzero _mm_setzero_si128 #define vec_set_32 _mm_set1_epi32 - auto& vec_add_dpbusd_32 = m128_add_dpbusd_epi32; - auto& vec_add_dpbusd_32x4 = m128_add_dpbusd_epi32x4; - auto& vec_hadd = m128_hadd; + [[maybe_unused]] auto& vec_add_dpbusd_32 = m128_add_dpbusd_epi32; + [[maybe_unused]] auto& vec_add_dpbusd_32x2 = m128_add_dpbusd_epi32x2; + [[maybe_unused]] auto& vec_add_dpbusd_32x4 = m128_add_dpbusd_epi32x4; + [[maybe_unused]] auto& vec_hadd = m128_hadd; + [[maybe_unused]] auto& vec_haddx4 = m128_haddx4; #endif #if defined (USE_SSSE3) const auto output = reinterpret_cast(buffer); const auto inputVector = reinterpret_cast(input); +#endif + +#if defined (USE_VNNI) || defined (USE_AVX512) - static_assert(OutputDimensions % OutputSimdWidth == 0 || OutputDimensions == 1); + static_assert(OutputDimensions == 1 || OutputDimensions % 4 == 0); // OutputDimensions is either 1 or a multiple of SimdWidth // because then it is also an input dimension. + if constexpr (OutputDimensions <= 8 && OutputDimensions != 1) + { + constexpr IndexType NumChunks = PaddedInputDimensions / InputSimdWidth; + + static_assert(NumChunks % 2 == 0); + + const auto input_vec = reinterpret_cast(input); + const auto bias_vec = reinterpret_cast(biases); + auto out_vec = reinterpret_cast<__m128i*>(output); + + vec_t regs[OutputDimensions]; + for (IndexType k = 0; k < OutputDimensions; ++k) + regs[k] = vec_setzero(); + + for (IndexType i = 0; i < NumChunks / 2; ++i) + { + const vec_t in0 = input_vec[i * 2 + 0]; + const vec_t in1 = input_vec[i * 2 + 1]; + for (IndexType k = 0; k < OutputDimensions; ++k) + { + const vec_t w0 = reinterpret_cast(&weights[k * PaddedInputDimensions])[i * 2 + 0]; + const vec_t w1 = reinterpret_cast(&weights[k * PaddedInputDimensions])[i * 2 + 1]; + vec_add_dpbusd_32(regs[k], in0, w0); + vec_add_dpbusd_32(regs[k], in1, w1); + } + } + + for (IndexType k = 0; k < OutputDimensions / 4; ++k) + { + out_vec[k] = vec_haddx4( + regs[k * 4 + 0], + regs[k * 4 + 1], + regs[k * 4 + 2], + regs[k * 4 + 3], + bias_vec[k] + ); + } + } + else if constexpr (InputDimensions == 8) + { + const auto input32 = reinterpret_cast(input); + __m256i* outptr = reinterpret_cast<__m256i*>(output); + std::memcpy(output, biases, OutputDimensions * sizeof(OutputType)); + + const __m256i in0 = _mm256_set1_epi32(input32[0]); + const __m256i in1 = _mm256_set1_epi32(input32[1]); + const auto col0 = reinterpret_cast(&weights[0]); + const auto col1 = reinterpret_cast(&weights[OutputDimensions * 4]); + for (IndexType j = 0; j * 8 < OutputDimensions; ++j) + m256_add_dpbusd_epi32x2(outptr[j], in0, col0[j], in1, col1[j]); + } + else + +#elif defined (USE_SSSE3) + + if constexpr (OutputDimensions % OutputSimdWidth == 0 && InputDimensions == 8) + { + const auto input32 = reinterpret_cast(input); + vec_t* outptr = reinterpret_cast(output); + std::memcpy(output, biases, OutputDimensions * sizeof(OutputType)); + + const vec_t in0 = vec_set_32(input32[0]); + const vec_t in1 = vec_set_32(input32[1]); + const auto col0 = reinterpret_cast(&weights[0]); + const auto col1 = reinterpret_cast(&weights[OutputDimensions * 4]); + for (IndexType j = 0; j * OutputSimdWidth < OutputDimensions; ++j) + vec_add_dpbusd_32x2(outptr[j], in0, col0[j], in1, col1[j]); + } + else + +#endif + +#if defined (USE_SSSE3) + if constexpr (OutputDimensions % OutputSimdWidth == 0) { static_assert(InputDimensions % 16 == 0); @@ -337,8 +520,8 @@ namespace Stockfish::Eval::NNUE::Layers { #if defined(USE_SSE2) // At least a multiple of 16, with SSE2. - static_assert(InputDimensions % SimdWidth == 0); - constexpr IndexType NumChunks = InputDimensions / SimdWidth; + static_assert(PaddedInputDimensions % SimdWidth == 0); + constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; const __m128i Zeros = _mm_setzero_si128(); const auto inputVector = reinterpret_cast(input); @@ -349,8 +532,8 @@ namespace Stockfish::Eval::NNUE::Layers { const auto inputVector = reinterpret_cast(input); #elif defined(USE_NEON) - static_assert(InputDimensions % SimdWidth == 0); - constexpr IndexType NumChunks = InputDimensions / SimdWidth; + static_assert(PaddedInputDimensions % SimdWidth == 0); + constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; const auto inputVector = reinterpret_cast(input); #endif @@ -423,6 +606,13 @@ namespace Stockfish::Eval::NNUE::Layers { _mm_empty(); #endif +#endif + +#if (!defined (USE_SSSE3) && defined (USE_SSE2)) || defined (USE_NEON) + static_assert(SimdWidth <= 16, "Otherwise we run outside of the padding for the output."); + if constexpr (SimdWidth > OutputDimensions && OutputDimensions != 1) + for (IndexType i = OutputDimensions; i < SimdWidth; ++i) + output[i] = 0; #endif return output; diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index 879a39cdbe6..193a197d3b2 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -23,7 +23,7 @@ #include "nnue_common.h" -#include "features/half_ka_v2.h" +#include "features/half_ka_v2_hm.h" #include "layers/input_slice.h" #include "layers/affine_transform.h" @@ -32,10 +32,10 @@ namespace Stockfish::Eval::NNUE { // Input features used in evaluation function - using FeatureSet = Features::HalfKAv2; + using FeatureSet = Features::HalfKAv2_hm; // Number of input feature dimensions after conversion - constexpr IndexType TransformedFeatureDimensions = 512; + constexpr IndexType TransformedFeatureDimensions = 1024; constexpr IndexType PSQTBuckets = 8; constexpr IndexType LayerStacks = 8; @@ -43,7 +43,7 @@ namespace Stockfish::Eval::NNUE { // Define network structure using InputLayer = InputSlice; - using HiddenLayer1 = ClippedReLU>; + using HiddenLayer1 = ClippedReLU>; using HiddenLayer2 = ClippedReLU>; using OutputLayer = AffineTransform; From f10ebc2bdf52dbc413e74ed1e41c29bd9b818408 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 15 Aug 2021 15:11:04 +0200 Subject: [PATCH 0648/1766] Regenerate dependencies on code change fixes https://github.com/official-stockfish/Stockfish/issues/3658 dependencies are now regenerated for each code change, this adds some 1s overhead in compile time, but avoids potential miscompilations or build problems. closes https://github.com/official-stockfish/Stockfish/pull/3659 No functional change --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index c1dab86ba03..901ddd92453 100644 --- a/src/Makefile +++ b/src/Makefile @@ -912,7 +912,7 @@ icc-profile-use: EXTRACXXFLAGS='-prof_use -prof_dir ./profdir' \ all -.depend: +.depend: $(SRCS) -@$(CXX) $(DEPENDFLAGS) -MM $(SRCS) > $@ 2> /dev/null -include .depend From 1946a675677aee8e485b75525bd06184badc79f2 Mon Sep 17 00:00:00 2001 From: Torsten Hellwig Date: Wed, 18 Aug 2021 09:12:14 +0200 Subject: [PATCH 0649/1766] Update default net to nn-ac5605a608d6.nnue This net was created with the nnue-pytorch trainer, it used the previous master net as a starting point. The training data includes all T60 data (https://drive.google.com/drive/folders/1rzZkgIgw7G5vQMLr2hZNiUXOp7z80613), all T74 data (https://drive.google.com/drive/folders/1aFUv3Ih3-A8Vxw9064Kw_FU4sNhMHZU-) and the wrongNNUE_02_d9.binpack (https://drive.google.com/file/d/1seGNOqcVdvK_vPNq98j-zV3XPE5zWAeq). The Leela data were randomly named and then concatenated. All data was merged into one binpack using interleave_binpacks.py. python3 train.py \ ../data/t60_t74_wrong.binpack \ ../data/t60_t74_wrong.binpack \ --resume-from-model ../data/nn-e8321e467bf6.pt \ --gpus 1 \ --threads 4 \ --num-workers 1 \ --batch-size 16384 \ --progress_bar_refresh_rate 300 \ --random-fen-skipping 3 \ --features=HalfKAv2_hm^ \ --lambda=1.0 \ --max_epochs=600 \ --seed $RANDOM \ --default_root_dir ../output/exp_24 STC: LLR: 2.95 (-2.94,2.94) <-0.50,2.50> Total: 15320 W: 1415 L: 1257 D: 12648 Ptnml(0-2): 50, 1002, 5402, 1152, 54 https://tests.stockfishchess.org/tests/view/611c404a4977aa1525c9c97f LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 9440 W: 345 L: 248 D: 8847 Ptnml(0-2): 3, 222, 4175, 315, 5 https://tests.stockfishchess.org/tests/view/611c6c7d4977aa1525c9c996 LTC with UHO_XXL_+0.90_+1.19.epd: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 6232 W: 1638 L: 1459 D: 3135 Ptnml(0-2): 5, 592, 1744, 769, 6 https://tests.stockfishchess.org/tests/view/611c9b214977aa1525c9c9cb closes https://github.com/official-stockfish/Stockfish/pull/3664 Bench: 5375286 --- AUTHORS | 1 + src/evaluate.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 4d72314f77b..d7f90de0e35 100644 --- a/AUTHORS +++ b/AUTHORS @@ -186,6 +186,7 @@ Tom Truscott Tom Vijlbrief (tomtor) Tomasz Sobczyk (Sopel97) Torsten Franz (torfranz, tfranzer) +Torsten Hellwig (Torom) Tracey Emery (basepr1me) tttak Unai Corzo (unaiic) diff --git a/src/evaluate.h b/src/evaluate.h index 4430a28c807..322db398c1e 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-e8321e467bf6.nnue" + #define EvalFileDefaultName "nn-ac5605a608d6.nnue" namespace NNUE { From ccf0239bc468c1fb599785e0dea178e84279674d Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Fri, 13 Aug 2021 22:20:11 +0200 Subject: [PATCH 0650/1766] Improve handling of the debug log file. Fix handling of empty strings in uci options and reassigning of the log file Fixes https://github.com/official-stockfish/Stockfish/issues/3650 Closes https://github.com/official-stockfish/Stockfish/pull/3655 No functional change --- AUTHORS | 2 +- src/misc.cpp | 15 ++++++++------- src/ucioption.cpp | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/AUTHORS b/AUTHORS index d7f90de0e35..5b5bbf22b67 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,4 +1,4 @@ -# List of authors for Stockfish, as of June 14, 2021 +# List of authors for Stockfish # Founders of the Stockfish project and fishtest infrastructure Tord Romstad (romstad) diff --git a/src/misc.cpp b/src/misc.cpp index 3b071ccb108..f9c123374f2 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -110,7 +110,14 @@ class Logger { static Logger l; - if (!fname.empty() && !l.file.is_open()) + if (l.file.is_open()) + { + cout.rdbuf(l.out.buf); + cin.rdbuf(l.in.buf); + l.file.close(); + } + + if (!fname.empty()) { l.file.open(fname, ifstream::out); @@ -123,12 +130,6 @@ class Logger { cin.rdbuf(&l.in); cout.rdbuf(&l.out); } - else if (fname.empty() && l.file.is_open()) - { - cout.rdbuf(l.out.buf); - cin.rdbuf(l.in.buf); - l.file.close(); - } } }; diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 07b3027da70..0cafd3e92d0 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -164,7 +164,7 @@ Option& Option::operator=(const string& v) { assert(!type.empty()); - if ( (type != "button" && v.empty()) + if ( (type != "button" && type != "string" && v.empty()) || (type == "check" && v != "true" && v != "false") || (type == "spin" && (stof(v) < min || stof(v) > max))) return *this; From 18dcf1f09754284325157f2d270df10a09297958 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Mon, 16 Aug 2021 12:19:26 +0200 Subject: [PATCH 0651/1766] Optimize and tidy up affine transform code. The new network caused some issues initially due to the very narrow neuron set between the first two FC layers. Necessary changes were hacked together to make it work. This patch is a mature approach to make the affine transform code faster, more readable, and easier to maintain should the layer sizes change again. The following changes were made: * ClippedReLU always produces a multiple of 32 outputs. This is about as good of a solution for AffineTransform's SIMD requirements as it can get without a bigger rewrite. * All self-contained simd helpers are moved to a separate file (simd.h). Inline asm is utilized to work around GCC's issues with code generation and register assignment. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101693, https://godbolt.org/z/da76fY1n7 * AffineTransform has 2 specializations. While it's more lines of code due to the boilerplate, the logic in both is significantly reduced, as these two are impossible to nicely combine into one. 1) The first specialization is for cases when there's >=128 inputs. It uses a different approach to perform the affine transform and can make full use of AVX512 without any edge cases. Furthermore, it has higher theoretical throughput because less loads are needed in the hot path, requiring only a fixed amount of instructions for horizontal additions at the end, which are amortized by the large number of inputs. 2) The second specialization is made to handle smaller layers where performance is still necessary but edge cases need to be handled. AVX512 implementation for this was ommited by mistake, a remnant from the temporary implementation for the new... This could be easily reintroduced if needed. A slightly more detailed description of both implementations is in the code. Overall it should be a minor speedup, as shown on fishtest: passed STC: LLR: 2.96 (-2.94,2.94) <-0.50,2.50> Total: 51520 W: 4074 L: 3888 D: 43558 Ptnml(0-2): 111, 3136, 19097, 3288, 128 and various tests shown in the pull request closes https://github.com/official-stockfish/Stockfish/pull/3663 No functional change --- src/nnue/layers/affine_transform.h | 883 +++++++++++++---------------- src/nnue/layers/clipped_relu.h | 14 +- src/simd.h | 341 +++++++++++ 3 files changed, 755 insertions(+), 483 deletions(-) create mode 100644 src/simd.h diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index d131836865f..b28712780b2 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -22,13 +22,141 @@ #define NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED #include +#include +#include #include "../nnue_common.h" +#include "../../simd.h" + +/* + This file contains the definition for a fully connected layer (aka affine transform). + Two approaches are employed, depending on the sizes of the transform. + + Approach 1: + - used when the PaddedInputDimensions >= 128 + - uses AVX512 if possible + - processes inputs in batches of 2*InputSimdWidth + - so in batches of 128 for AVX512 + - the weight blocks of size InputSimdWidth are transposed such that + access is sequential + - N columns of the weight matrix are processed a time, where N + depends on the architecture (the amount of registers) + - accumulate + hadd is used + + Approach 2: + - used when the PaddedInputDimensions < 128 + - does not use AVX512 + - expected use-case is for when PaddedInputDimensions == 32 and InputDimensions <= 32. + - that's why AVX512 is hard to implement + - expected use-case is small layers + - not optimized as well as the approach 1 + - inputs are processed in chunks of 4, weights are respectively transposed + - accumulation happens directly to int32s +*/ namespace Stockfish::Eval::NNUE::Layers { - // Affine transformation layer +// Fallback implementation for older/other architectures. +// Identical for both approaches. Requires the input to be padded to at least 16 values. +#if !defined(USE_SSSE3) + template + static void affine_transform_non_ssse3(std::int32_t* output, const std::int8_t* weights, const std::int32_t* biases, const std::uint8_t* input) + { +# if defined(USE_SSE2) + // At least a multiple of 16, with SSE2. + static_assert(PaddedInputDimensions % 16 == 0); + constexpr IndexType NumChunks = PaddedInputDimensions / 16; + const __m128i Zeros = _mm_setzero_si128(); + const auto inputVector = reinterpret_cast(input); + +# elif defined(USE_MMX) + static_assert(InputDimensions % 8 == 0); + constexpr IndexType NumChunks = InputDimensions / 8; + const __m64 Zeros = _mm_setzero_si64(); + const auto inputVector = reinterpret_cast(input); + +# elif defined(USE_NEON) + static_assert(PaddedInputDimensions % 16 == 0); + constexpr IndexType NumChunks = PaddedInputDimensions / 16; + const auto inputVector = reinterpret_cast(input); +# endif + + for (IndexType i = 0; i < OutputDimensions; ++i) { + const IndexType offset = i * PaddedInputDimensions; + +# if defined(USE_SSE2) + __m128i sumLo = _mm_cvtsi32_si128(biases[i]); + __m128i sumHi = Zeros; + const auto row = reinterpret_cast(&weights[offset]); + for (IndexType j = 0; j < NumChunks; ++j) { + __m128i row_j = _mm_load_si128(&row[j]); + __m128i input_j = _mm_load_si128(&inputVector[j]); + __m128i extendedRowLo = _mm_srai_epi16(_mm_unpacklo_epi8(row_j, row_j), 8); + __m128i extendedRowHi = _mm_srai_epi16(_mm_unpackhi_epi8(row_j, row_j), 8); + __m128i extendedInputLo = _mm_unpacklo_epi8(input_j, Zeros); + __m128i extendedInputHi = _mm_unpackhi_epi8(input_j, Zeros); + __m128i productLo = _mm_madd_epi16(extendedRowLo, extendedInputLo); + __m128i productHi = _mm_madd_epi16(extendedRowHi, extendedInputHi); + sumLo = _mm_add_epi32(sumLo, productLo); + sumHi = _mm_add_epi32(sumHi, productHi); + } + __m128i sum = _mm_add_epi32(sumLo, sumHi); + __m128i sumHigh_64 = _mm_shuffle_epi32(sum, _MM_SHUFFLE(1, 0, 3, 2)); + sum = _mm_add_epi32(sum, sumHigh_64); + __m128i sum_second_32 = _mm_shufflelo_epi16(sum, _MM_SHUFFLE(1, 0, 3, 2)); + sum = _mm_add_epi32(sum, sum_second_32); + output[i] = _mm_cvtsi128_si32(sum); + +# elif defined(USE_MMX) + __m64 sumLo = _mm_cvtsi32_si64(biases[i]); + __m64 sumHi = Zeros; + const auto row = reinterpret_cast(&weights[offset]); + for (IndexType j = 0; j < NumChunks; ++j) { + __m64 row_j = row[j]; + __m64 input_j = inputVector[j]; + __m64 extendedRowLo = _mm_srai_pi16(_mm_unpacklo_pi8(row_j, row_j), 8); + __m64 extendedRowHi = _mm_srai_pi16(_mm_unpackhi_pi8(row_j, row_j), 8); + __m64 extendedInputLo = _mm_unpacklo_pi8(input_j, Zeros); + __m64 extendedInputHi = _mm_unpackhi_pi8(input_j, Zeros); + __m64 productLo = _mm_madd_pi16(extendedRowLo, extendedInputLo); + __m64 productHi = _mm_madd_pi16(extendedRowHi, extendedInputHi); + sumLo = _mm_add_pi32(sumLo, productLo); + sumHi = _mm_add_pi32(sumHi, productHi); + } + __m64 sum = _mm_add_pi32(sumLo, sumHi); + sum = _mm_add_pi32(sum, _mm_unpackhi_pi32(sum, sum)); + output[i] = _mm_cvtsi64_si32(sum); + +# elif defined(USE_NEON) + int32x4_t sum = {biases[i]}; + const auto row = reinterpret_cast(&weights[offset]); + for (IndexType j = 0; j < NumChunks; ++j) { + int16x8_t product = vmull_s8(inputVector[j * 2], row[j * 2]); + product = vmlal_s8(product, inputVector[j * 2 + 1], row[j * 2 + 1]); + sum = vpadalq_s16(sum, product); + } + output[i] = sum[0] + sum[1] + sum[2] + sum[3]; + +# else + std::int32_t sum = biases[i]; + for (IndexType j = 0; j < InputDimensions; ++j) { + sum += weights[offset + j] * input[j]; + } + output[i] = sum; +# endif + } + +# if defined(USE_MMX) + _mm_empty(); +# endif + } +#endif + + template + class AffineTransform; + + // A specialization for large inputs. template - class AffineTransform { + class AffineTransform= 2*64-1)>> { public: // Input/output type using InputType = typename PreviousLayer::OutputType; @@ -36,29 +164,49 @@ namespace Stockfish::Eval::NNUE::Layers { static_assert(std::is_same::value, ""); // Number of input/output dimensions - static constexpr IndexType InputDimensions = - PreviousLayer::OutputDimensions; + static constexpr IndexType InputDimensions = PreviousLayer::OutputDimensions; static constexpr IndexType OutputDimensions = OutDims; + static constexpr IndexType PaddedInputDimensions = - ceil_to_multiple(InputDimensions, MaxSimdWidth); -#if defined (USE_AVX512) - static constexpr const IndexType OutputSimdWidth = SimdWidth / 2; -#elif defined (USE_SSSE3) - static constexpr const IndexType OutputSimdWidth = SimdWidth / 4; -#endif + ceil_to_multiple(InputDimensions, MaxSimdWidth); + + static_assert(PaddedInputDimensions >= 128, "Something went wrong. This specialization should not have been chosen."); + #if defined (USE_AVX512) - static constexpr const IndexType InputSimdWidth = SimdWidth * 2; + static constexpr const IndexType InputSimdWidth = 64; + static constexpr const IndexType MaxNumOutputRegs = 16; +#elif defined (USE_AVX2) + static constexpr const IndexType InputSimdWidth = 32; + static constexpr const IndexType MaxNumOutputRegs = 8; #elif defined (USE_SSSE3) - static constexpr const IndexType InputSimdWidth = SimdWidth; + static constexpr const IndexType InputSimdWidth = 16; + static constexpr const IndexType MaxNumOutputRegs = 8; +#else + // The fallback implementation will not have permuted weights. + // We define these to avoid a lot of ifdefs later. + static constexpr const IndexType InputSimdWidth = 1; + static constexpr const IndexType MaxNumOutputRegs = 1; #endif + // A big block is a region in the weight matrix of the size [PaddedInputDimensions, NumOutputRegs]. + // A small block is a region of size [InputSimdWidth, 1] + + static constexpr const IndexType NumOutputRegs = std::min(MaxNumOutputRegs, OutputDimensions); + static constexpr const IndexType SmallBlockSize = InputSimdWidth; + static constexpr const IndexType BigBlockSize = NumOutputRegs * PaddedInputDimensions; + static constexpr const IndexType NumSmallBlocksInBigBlock = BigBlockSize / SmallBlockSize; + static constexpr const IndexType NumSmallBlocksPerOutput = PaddedInputDimensions / SmallBlockSize; + static constexpr const IndexType NumBigBlocks = OutputDimensions / NumOutputRegs; + + static_assert(OutputDimensions % NumOutputRegs == 0); + // Size of forward propagation buffer used in this layer static constexpr std::size_t SelfBufferSize = - ceil_to_multiple(OutputDimensions * sizeof(OutputType), CacheLineSize); + ceil_to_multiple(OutputDimensions * sizeof(OutputType), CacheLineSize); // Size of the forward propagation buffer used from the input layer to this layer static constexpr std::size_t BufferSize = - PreviousLayer::BufferSize + SelfBufferSize; + PreviousLayer::BufferSize + SelfBufferSize; // Hash value embedded in the evaluation file static constexpr std::uint32_t get_hash_value() { @@ -69,30 +217,35 @@ namespace Stockfish::Eval::NNUE::Layers { return hashValue; } + /* + Transposes the small blocks within a block. + Effectively means that weights can be traversed sequentially during inference. + */ + static IndexType get_weight_index(IndexType i) + { + const IndexType smallBlock = (i / SmallBlockSize) % NumSmallBlocksInBigBlock; + const IndexType smallBlockCol = smallBlock / NumSmallBlocksPerOutput; + const IndexType smallBlockRow = smallBlock % NumSmallBlocksPerOutput; + const IndexType bigBlock = i / BigBlockSize; + const IndexType rest = i % SmallBlockSize; + + const IndexType idx = + bigBlock * BigBlockSize + + smallBlockRow * SmallBlockSize * NumOutputRegs + + smallBlockCol * SmallBlockSize + + rest; + + return idx; + } + // Read network parameters bool read_parameters(std::istream& stream) { if (!previousLayer.read_parameters(stream)) return false; for (std::size_t i = 0; i < OutputDimensions; ++i) biases[i] = read_little_endian(stream); + for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) -#if !defined (USE_SSSE3) - weights[i] = read_little_endian(stream); -#elif defined (USE_VNNI) || defined (USE_AVX512) - if constexpr (OutputDimensions <= 8 && OutputDimensions != 1) - weights[i] = read_little_endian(stream); - else - weights[ - (i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 + - i / PaddedInputDimensions * 4 + - i % 4 - ] = read_little_endian(stream); -#else - weights[ - (i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 + - i / PaddedInputDimensions * 4 + - i % 4 - ] = read_little_endian(stream); -#endif + weights[get_weight_index(i)] = read_little_endian(stream); return !stream.fail(); } @@ -102,517 +255,285 @@ namespace Stockfish::Eval::NNUE::Layers { if (!previousLayer.write_parameters(stream)) return false; for (std::size_t i = 0; i < OutputDimensions; ++i) write_little_endian(stream, biases[i]); -#if !defined (USE_SSSE3) - for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) - write_little_endian(stream, weights[i]); -#else - std::unique_ptr unscrambledWeights = std::make_unique(OutputDimensions * PaddedInputDimensions); - for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) { - unscrambledWeights[i] = - weights[ - (i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 + - i / PaddedInputDimensions * 4 + - i % 4 - ]; - } for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) - write_little_endian(stream, unscrambledWeights[i]); -#endif + write_little_endian(stream, weights[get_weight_index(i)]); return !stream.fail(); } + // Forward propagation const OutputType* propagate( const TransformedFeatureType* transformedFeatures, char* buffer) const { const auto input = previousLayer.propagate( - transformedFeatures, buffer + SelfBufferSize); + transformedFeatures, buffer + SelfBufferSize); + OutputType* output = reinterpret_cast(buffer); #if defined (USE_AVX512) + using vec_t = __m512i; + #define vec_setzero _mm512_setzero_si512 + #define vec_set_32 _mm512_set1_epi32 + #define vec_add_dpbusd_32 Simd::m512_add_dpbusd_epi32 + #define vec_add_dpbusd_32x2 Simd::m512_add_dpbusd_epi32x2 + #define vec_hadd Simd::m512_hadd + #define vec_haddx4 Simd::m512_haddx4 +#elif defined (USE_AVX2) + using vec_t = __m256i; + #define vec_setzero _mm256_setzero_si256 + #define vec_set_32 _mm256_set1_epi32 + #define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32 + #define vec_add_dpbusd_32x2 Simd::m256_add_dpbusd_epi32x2 + #define vec_hadd Simd::m256_hadd + #define vec_haddx4 Simd::m256_haddx4 +#elif defined (USE_SSSE3) + using vec_t = __m128i; + #define vec_setzero _mm_setzero_si128 + #define vec_set_32 _mm_set1_epi32 + #define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32 + #define vec_add_dpbusd_32x2 Simd::m128_add_dpbusd_epi32x2 + #define vec_hadd Simd::m128_hadd + #define vec_haddx4 Simd::m128_haddx4 +#endif - [[maybe_unused]] const __m512i Ones512 = _mm512_set1_epi16(1); - - [[maybe_unused]] auto m512_hadd = [](__m512i sum, int bias) -> int { - return _mm512_reduce_add_epi32(sum) + bias; - }; - - [[maybe_unused]] auto m512_hadd128x16_interleave = []( - __m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3) -> __m512i { +#if defined (USE_SSSE3) + const vec_t* invec = reinterpret_cast(input); - __m512i sum01a = _mm512_unpacklo_epi32(sum0, sum1); - __m512i sum01b = _mm512_unpackhi_epi32(sum0, sum1); - __m512i sum23a = _mm512_unpacklo_epi32(sum2, sum3); - __m512i sum23b = _mm512_unpackhi_epi32(sum2, sum3); + // Perform accumulation to registers for each big block + for (IndexType bigBlock = 0; bigBlock < NumBigBlocks; ++bigBlock) + { + vec_t acc[NumOutputRegs] = { vec_setzero() }; + + // Each big block has NumOutputRegs small blocks in each "row", one per register. + // We process two small blocks at a time to save on one addition without VNNI. + for (IndexType smallBlock = 0; smallBlock < NumSmallBlocksPerOutput; smallBlock += 2) + { + const vec_t* weightvec = + reinterpret_cast( + weights + + bigBlock * BigBlockSize + + smallBlock * SmallBlockSize * NumOutputRegs); + + const vec_t in0 = invec[smallBlock + 0]; + const vec_t in1 = invec[smallBlock + 1]; + + for (IndexType k = 0; k < NumOutputRegs; ++k) + vec_add_dpbusd_32x2(acc[k], in0, weightvec[k], in1, weightvec[k + NumOutputRegs]); + } - __m512i sum01 = _mm512_add_epi32(sum01a, sum01b); - __m512i sum23 = _mm512_add_epi32(sum23a, sum23b); + // Horizontally add all accumulators. + if constexpr (NumOutputRegs % 4 == 0) + { + __m128i* outputvec = reinterpret_cast<__m128i*>(output); + const __m128i* biasvec = reinterpret_cast(biases); - __m512i sum0123a = _mm512_unpacklo_epi64(sum01, sum23); - __m512i sum0123b = _mm512_unpackhi_epi64(sum01, sum23); + for (IndexType k = 0; k < NumOutputRegs; k += 4) + { + const IndexType idx = (bigBlock * NumOutputRegs + k) / 4; + outputvec[idx] = vec_haddx4(acc[k+0], acc[k+1], acc[k+2], acc[k+3], biasvec[idx]); + } + } + else + { + for (IndexType k = 0; k < NumOutputRegs; ++k) + { + const IndexType idx = (bigBlock * NumOutputRegs + k); + output[idx] = vec_hadd(acc[k], biases[idx]); + } + } + } - return _mm512_add_epi32(sum0123a, sum0123b); - }; +# undef vec_setzero +# undef vec_set_32 +# undef vec_add_dpbusd_32 +# undef vec_add_dpbusd_32x2 +# undef vec_hadd +# undef vec_haddx4 +#else + // Use old implementation for the other architectures. + affine_transform_non_ssse3< + InputDimensions, + PaddedInputDimensions, + OutputDimensions>(output, weights, biases, input); - [[maybe_unused]] auto m512_haddx4 = [m512_hadd128x16_interleave]( - __m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3, __m128i bias) -> __m128i { +#endif - __m512i sum = m512_hadd128x16_interleave(sum0, sum1, sum2, sum3); + return output; + } - __m256i sum256lo = _mm512_castsi512_si256(sum); - __m256i sum256hi = _mm512_extracti64x4_epi64(sum, 1); + private: + using BiasType = OutputType; + using WeightType = std::int8_t; - sum256lo = _mm256_add_epi32(sum256lo, sum256hi); + PreviousLayer previousLayer; - __m128i sum128lo = _mm256_castsi256_si128(sum256lo); - __m128i sum128hi = _mm256_extracti128_si256(sum256lo, 1); + alignas(CacheLineSize) BiasType biases[OutputDimensions]; + alignas(CacheLineSize) WeightType weights[OutputDimensions * PaddedInputDimensions]; + }; - return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias); - }; + template + class AffineTransform> { + public: + // Input/output type + using InputType = typename PreviousLayer::OutputType; + using OutputType = std::int32_t; + static_assert(std::is_same::value, ""); - [[maybe_unused]] auto m512_add_dpbusd_epi32 = [=](__m512i& acc, __m512i a, __m512i b) { -#if defined (USE_VNNI) - acc = _mm512_dpbusd_epi32(acc, a, b); -#else - __m512i product0 = _mm512_maddubs_epi16(a, b); - product0 = _mm512_madd_epi16(product0, Ones512); - acc = _mm512_add_epi32(acc, product0); -#endif - }; + // Number of input/output dimensions + static constexpr IndexType InputDimensions = + PreviousLayer::OutputDimensions; + static constexpr IndexType OutputDimensions = OutDims; + static constexpr IndexType PaddedInputDimensions = + ceil_to_multiple(InputDimensions, MaxSimdWidth); - [[maybe_unused]] auto m512_add_dpbusd_epi32x2 = [=](__m512i& acc, __m512i a0, __m512i b0, __m512i a1, __m512i b1) { -#if defined (USE_VNNI) - acc = _mm512_dpbusd_epi32(acc, a0, b0); - acc = _mm512_dpbusd_epi32(acc, a1, b1); -#else - __m512i product0 = _mm512_maddubs_epi16(a0, b0); - __m512i product1 = _mm512_maddubs_epi16(a1, b1); - product0 = _mm512_adds_epi16(product0, product1); - product0 = _mm512_madd_epi16(product0, Ones512); - acc = _mm512_add_epi32(acc, product0); -#endif - }; - - [[maybe_unused]] auto m512_add_dpbusd_epi32x4 = [=](__m512i& acc, __m512i a0, __m512i b0, __m512i a1, __m512i b1, - __m512i a2, __m512i b2, __m512i a3, __m512i b3) { -#if defined (USE_VNNI) - acc = _mm512_dpbusd_epi32(acc, a0, b0); - acc = _mm512_dpbusd_epi32(acc, a1, b1); - acc = _mm512_dpbusd_epi32(acc, a2, b2); - acc = _mm512_dpbusd_epi32(acc, a3, b3); -#else - __m512i product0 = _mm512_maddubs_epi16(a0, b0); - __m512i product1 = _mm512_maddubs_epi16(a1, b1); - __m512i product2 = _mm512_maddubs_epi16(a2, b2); - __m512i product3 = _mm512_maddubs_epi16(a3, b3); - product0 = _mm512_adds_epi16(product0, product1); - product0 = _mm512_madd_epi16(product0, Ones512); - product2 = _mm512_adds_epi16(product2, product3); - product2 = _mm512_madd_epi16(product2, Ones512); - acc = _mm512_add_epi32(acc, _mm512_add_epi32(product0, product2)); -#endif - }; + static_assert(PaddedInputDimensions < 128, "Something went wrong. This specialization should not have been chosen."); +#if defined (USE_SSSE3) + static constexpr const IndexType OutputSimdWidth = SimdWidth / 4; + static constexpr const IndexType InputSimdWidth = SimdWidth; #endif -#if defined (USE_AVX2) - [[maybe_unused]] const __m256i Ones256 = _mm256_set1_epi16(1); - - [[maybe_unused]] auto m256_hadd = [](__m256i sum, int bias) -> int { - __m128i sum128 = _mm_add_epi32(_mm256_castsi256_si128(sum), _mm256_extracti128_si256(sum, 1)); - sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_BADC)); - sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_CDAB)); - return _mm_cvtsi128_si32(sum128) + bias; - }; - - [[maybe_unused]] auto m256_haddx4 = [](__m256i sum0, __m256i sum1, __m256i sum2, __m256i sum3, __m128i bias) -> __m128i { - sum0 = _mm256_hadd_epi32(sum0, sum1); - sum2 = _mm256_hadd_epi32(sum2, sum3); + // Size of forward propagation buffer used in this layer + static constexpr std::size_t SelfBufferSize = + ceil_to_multiple(OutputDimensions * sizeof(OutputType), CacheLineSize); - sum0 = _mm256_hadd_epi32(sum0, sum2); + // Size of the forward propagation buffer used from the input layer to this layer + static constexpr std::size_t BufferSize = + PreviousLayer::BufferSize + SelfBufferSize; - __m128i sum128lo = _mm256_castsi256_si128(sum0); - __m128i sum128hi = _mm256_extracti128_si256(sum0, 1); + // Hash value embedded in the evaluation file + static constexpr std::uint32_t get_hash_value() { + std::uint32_t hashValue = 0xCC03DAE4u; + hashValue += OutputDimensions; + hashValue ^= PreviousLayer::get_hash_value() >> 1; + hashValue ^= PreviousLayer::get_hash_value() << 31; + return hashValue; + } - return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias); - }; + static IndexType get_weight_index_scrambled(IndexType i) + { + return + (i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 + + i / PaddedInputDimensions * 4 + + i % 4; + } - [[maybe_unused]] auto m256_add_dpbusd_epi32 = [=](__m256i& acc, __m256i a, __m256i b) { -#if defined (USE_VNNI) - acc = _mm256_dpbusd_epi32(acc, a, b); + static IndexType get_weight_index(IndexType i) + { +#if defined (USE_SSSE3) + return get_weight_index_scrambled(i); #else - __m256i product0 = _mm256_maddubs_epi16(a, b); - product0 = _mm256_madd_epi16(product0, Ones256); - acc = _mm256_add_epi32(acc, product0); + return i; #endif - }; + } - [[maybe_unused]] auto m256_add_dpbusd_epi32x2 = [=](__m256i& acc, __m256i a0, __m256i b0, __m256i a1, __m256i b1) { -#if defined (USE_VNNI) - acc = _mm256_dpbusd_epi32(acc, a0, b0); - acc = _mm256_dpbusd_epi32(acc, a1, b1); -#else - __m256i product0 = _mm256_maddubs_epi16(a0, b0); - __m256i product1 = _mm256_maddubs_epi16(a1, b1); - product0 = _mm256_adds_epi16(product0, product1); - product0 = _mm256_madd_epi16(product0, Ones256); - acc = _mm256_add_epi32(acc, product0); -#endif - }; - - [[maybe_unused]] auto m256_add_dpbusd_epi32x4 = [=](__m256i& acc, __m256i a0, __m256i b0, __m256i a1, __m256i b1, - __m256i a2, __m256i b2, __m256i a3, __m256i b3) { -#if defined (USE_VNNI) - acc = _mm256_dpbusd_epi32(acc, a0, b0); - acc = _mm256_dpbusd_epi32(acc, a1, b1); - acc = _mm256_dpbusd_epi32(acc, a2, b2); - acc = _mm256_dpbusd_epi32(acc, a3, b3); -#else - __m256i product0 = _mm256_maddubs_epi16(a0, b0); - __m256i product1 = _mm256_maddubs_epi16(a1, b1); - __m256i product2 = _mm256_maddubs_epi16(a2, b2); - __m256i product3 = _mm256_maddubs_epi16(a3, b3); - product0 = _mm256_adds_epi16(product0, product1); - product0 = _mm256_madd_epi16(product0, Ones256); - product2 = _mm256_adds_epi16(product2, product3); - product2 = _mm256_madd_epi16(product2, Ones256); - acc = _mm256_add_epi32(acc, _mm256_add_epi32(product0, product2)); -#endif - }; + // Read network parameters + bool read_parameters(std::istream& stream) { + if (!previousLayer.read_parameters(stream)) return false; + for (std::size_t i = 0; i < OutputDimensions; ++i) + biases[i] = read_little_endian(stream); + for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) + weights[get_weight_index(i)] = read_little_endian(stream); -#endif -#if defined (USE_SSSE3) + return !stream.fail(); + } - [[maybe_unused]] const __m128i Ones128 = _mm_set1_epi16(1); - - [[maybe_unused]] auto m128_hadd = [](__m128i sum, int bias) -> int { - sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0x4E)); //_MM_PERM_BADC - sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0xB1)); //_MM_PERM_CDAB - return _mm_cvtsi128_si32(sum) + bias; - }; - - [[maybe_unused]] auto m128_haddx4 = [](__m128i sum0, __m128i sum1, __m128i sum2, __m128i sum3, __m128i bias) -> __m128i { - sum0 = _mm_hadd_epi32(sum0, sum1); - sum2 = _mm_hadd_epi32(sum2, sum3); - sum0 = _mm_hadd_epi32(sum0, sum2); - return _mm_add_epi32(sum0, bias); - }; - - [[maybe_unused]] auto m128_add_dpbusd_epi32 = [=](__m128i& acc, __m128i a, __m128i b) { - __m128i product0 = _mm_maddubs_epi16(a, b); - product0 = _mm_madd_epi16(product0, Ones128); - acc = _mm_add_epi32(acc, product0); - }; - - [[maybe_unused]] auto m128_add_dpbusd_epi32x2 = [=](__m128i& acc, __m128i a0, __m128i b0, __m128i a1, __m128i b1) { - __m128i product0 = _mm_maddubs_epi16(a0, b0); - __m128i product1 = _mm_maddubs_epi16(a1, b1); - product0 = _mm_adds_epi16(product0, product1); - product0 = _mm_madd_epi16(product0, Ones128); - acc = _mm_add_epi32(acc, product0); - }; - - [[maybe_unused]] auto m128_add_dpbusd_epi32x4 = [=](__m128i& acc, __m128i a0, __m128i b0, __m128i a1, __m128i b1, - __m128i a2, __m128i b2, __m128i a3, __m128i b3) { - __m128i product0 = _mm_maddubs_epi16(a0, b0); - __m128i product1 = _mm_maddubs_epi16(a1, b1); - __m128i product2 = _mm_maddubs_epi16(a2, b2); - __m128i product3 = _mm_maddubs_epi16(a3, b3); - product0 = _mm_adds_epi16(product0, product1); - product0 = _mm_madd_epi16(product0, Ones128); - product2 = _mm_adds_epi16(product2, product3); - product2 = _mm_madd_epi16(product2, Ones128); - acc = _mm_add_epi32(acc, _mm_add_epi32(product0, product2)); - }; + // Write network parameters + bool write_parameters(std::ostream& stream) const { + if (!previousLayer.write_parameters(stream)) return false; + for (std::size_t i = 0; i < OutputDimensions; ++i) + write_little_endian(stream, biases[i]); -#endif + for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) + write_little_endian(stream, weights[get_weight_index(i)]); -#if defined (USE_AVX512) - using vec_t = __m512i; - #define vec_setzero _mm512_setzero_si512 - #define vec_set_32 _mm512_set1_epi32 - [[maybe_unused]] auto& vec_add_dpbusd_32 = m512_add_dpbusd_epi32; - [[maybe_unused]] auto& vec_add_dpbusd_32x2 = m512_add_dpbusd_epi32x2; - [[maybe_unused]] auto& vec_add_dpbusd_32x4 = m512_add_dpbusd_epi32x4; - [[maybe_unused]] auto& vec_hadd = m512_hadd; - [[maybe_unused]] auto& vec_haddx4 = m512_haddx4; -#elif defined (USE_AVX2) + return !stream.fail(); + } + // Forward propagation + const OutputType* propagate( + const TransformedFeatureType* transformedFeatures, char* buffer) const { + const auto input = previousLayer.propagate( + transformedFeatures, buffer + SelfBufferSize); + const auto output = reinterpret_cast(buffer); + +#if defined (USE_AVX2) using vec_t = __m256i; #define vec_setzero _mm256_setzero_si256 #define vec_set_32 _mm256_set1_epi32 - [[maybe_unused]] auto& vec_add_dpbusd_32 = m256_add_dpbusd_epi32; - [[maybe_unused]] auto& vec_add_dpbusd_32x2 = m256_add_dpbusd_epi32x2; - [[maybe_unused]] auto& vec_add_dpbusd_32x4 = m256_add_dpbusd_epi32x4; - [[maybe_unused]] auto& vec_hadd = m256_hadd; - [[maybe_unused]] auto& vec_haddx4 = m256_haddx4; + #define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32 + #define vec_add_dpbusd_32x2 Simd::m256_add_dpbusd_epi32x2 + #define vec_add_dpbusd_32x4 Simd::m256_add_dpbusd_epi32x4 + #define vec_hadd Simd::m256_hadd + #define vec_haddx4 Simd::m256_haddx4 #elif defined (USE_SSSE3) using vec_t = __m128i; #define vec_setzero _mm_setzero_si128 #define vec_set_32 _mm_set1_epi32 - [[maybe_unused]] auto& vec_add_dpbusd_32 = m128_add_dpbusd_epi32; - [[maybe_unused]] auto& vec_add_dpbusd_32x2 = m128_add_dpbusd_epi32x2; - [[maybe_unused]] auto& vec_add_dpbusd_32x4 = m128_add_dpbusd_epi32x4; - [[maybe_unused]] auto& vec_hadd = m128_hadd; - [[maybe_unused]] auto& vec_haddx4 = m128_haddx4; + #define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32 + #define vec_add_dpbusd_32x2 Simd::m128_add_dpbusd_epi32x2 + #define vec_add_dpbusd_32x4 Simd::m128_add_dpbusd_epi32x4 + #define vec_hadd Simd::m128_hadd + #define vec_haddx4 Simd::m128_haddx4 #endif #if defined (USE_SSSE3) - const auto output = reinterpret_cast(buffer); const auto inputVector = reinterpret_cast(input); -#endif - -#if defined (USE_VNNI) || defined (USE_AVX512) - - static_assert(OutputDimensions == 1 || OutputDimensions % 4 == 0); - - // OutputDimensions is either 1 or a multiple of SimdWidth - // because then it is also an input dimension. - if constexpr (OutputDimensions <= 8 && OutputDimensions != 1) - { - constexpr IndexType NumChunks = PaddedInputDimensions / InputSimdWidth; - - static_assert(NumChunks % 2 == 0); - - const auto input_vec = reinterpret_cast(input); - const auto bias_vec = reinterpret_cast(biases); - auto out_vec = reinterpret_cast<__m128i*>(output); - - vec_t regs[OutputDimensions]; - for (IndexType k = 0; k < OutputDimensions; ++k) - regs[k] = vec_setzero(); - - for (IndexType i = 0; i < NumChunks / 2; ++i) - { - const vec_t in0 = input_vec[i * 2 + 0]; - const vec_t in1 = input_vec[i * 2 + 1]; - for (IndexType k = 0; k < OutputDimensions; ++k) - { - const vec_t w0 = reinterpret_cast(&weights[k * PaddedInputDimensions])[i * 2 + 0]; - const vec_t w1 = reinterpret_cast(&weights[k * PaddedInputDimensions])[i * 2 + 1]; - vec_add_dpbusd_32(regs[k], in0, w0); - vec_add_dpbusd_32(regs[k], in1, w1); - } - } - - for (IndexType k = 0; k < OutputDimensions / 4; ++k) - { - out_vec[k] = vec_haddx4( - regs[k * 4 + 0], - regs[k * 4 + 1], - regs[k * 4 + 2], - regs[k * 4 + 3], - bias_vec[k] - ); - } - } - else if constexpr (InputDimensions == 8) - { - const auto input32 = reinterpret_cast(input); - __m256i* outptr = reinterpret_cast<__m256i*>(output); - std::memcpy(output, biases, OutputDimensions * sizeof(OutputType)); - - const __m256i in0 = _mm256_set1_epi32(input32[0]); - const __m256i in1 = _mm256_set1_epi32(input32[1]); - const auto col0 = reinterpret_cast(&weights[0]); - const auto col1 = reinterpret_cast(&weights[OutputDimensions * 4]); - for (IndexType j = 0; j * 8 < OutputDimensions; ++j) - m256_add_dpbusd_epi32x2(outptr[j], in0, col0[j], in1, col1[j]); - } - else -#elif defined (USE_SSSE3) - - if constexpr (OutputDimensions % OutputSimdWidth == 0 && InputDimensions == 8) - { - const auto input32 = reinterpret_cast(input); - vec_t* outptr = reinterpret_cast(output); - std::memcpy(output, biases, OutputDimensions * sizeof(OutputType)); - - const vec_t in0 = vec_set_32(input32[0]); - const vec_t in1 = vec_set_32(input32[1]); - const auto col0 = reinterpret_cast(&weights[0]); - const auto col1 = reinterpret_cast(&weights[OutputDimensions * 4]); - for (IndexType j = 0; j * OutputSimdWidth < OutputDimensions; ++j) - vec_add_dpbusd_32x2(outptr[j], in0, col0[j], in1, col1[j]); - } - else - -#endif - -#if defined (USE_SSSE3) + static_assert(InputDimensions % 8 == 0); + static_assert(OutputDimensions % OutputSimdWidth == 0 || OutputDimensions == 1); if constexpr (OutputDimensions % OutputSimdWidth == 0) { - static_assert(InputDimensions % 16 == 0); - - constexpr IndexType NumChunks = InputDimensions / 4; - constexpr IndexType NumRegs = OutputDimensions / OutputSimdWidth; - - const auto input32 = reinterpret_cast(input); - const vec_t* biasvec = reinterpret_cast(biases); - vec_t outs[NumRegs]; + constexpr IndexType NumChunks = InputDimensions / 4; + constexpr IndexType NumRegs = OutputDimensions / OutputSimdWidth; + + const auto input32 = reinterpret_cast(input); + const vec_t* biasvec = reinterpret_cast(biases); + vec_t acc[NumRegs]; + for (IndexType k = 0; k < NumRegs; ++k) + acc[k] = biasvec[k]; + + for (IndexType i = 0; i < NumChunks; i += 2) + { + const vec_t in0 = vec_set_32(input32[i + 0]); + const vec_t in1 = vec_set_32(input32[i + 1]); + const auto col0 = reinterpret_cast(&weights[(i + 0) * OutputDimensions * 4]); + const auto col1 = reinterpret_cast(&weights[(i + 1) * OutputDimensions * 4]); for (IndexType k = 0; k < NumRegs; ++k) - outs[k] = biasvec[k]; - - for (IndexType i = 0; i < NumChunks; i += 4) - { - const vec_t in0 = vec_set_32(input32[i + 0]); - const vec_t in1 = vec_set_32(input32[i + 1]); - const vec_t in2 = vec_set_32(input32[i + 2]); - const vec_t in3 = vec_set_32(input32[i + 3]); - const auto col0 = reinterpret_cast(&weights[(i + 0) * OutputDimensions * 4]); - const auto col1 = reinterpret_cast(&weights[(i + 1) * OutputDimensions * 4]); - const auto col2 = reinterpret_cast(&weights[(i + 2) * OutputDimensions * 4]); - const auto col3 = reinterpret_cast(&weights[(i + 3) * OutputDimensions * 4]); - for (IndexType k = 0; k < NumRegs; ++k) - vec_add_dpbusd_32x4(outs[k], in0, col0[k], in1, col1[k], in2, col2[k], in3, col3[k]); - } + vec_add_dpbusd_32x2(acc[k], in0, col0[k], in1, col1[k]); + } - vec_t* outptr = reinterpret_cast(output); - for (IndexType k = 0; k < NumRegs; ++k) - outptr[k] = outs[k]; + vec_t* outptr = reinterpret_cast(output); + for (IndexType k = 0; k < NumRegs; ++k) + outptr[k] = acc[k]; } else if constexpr (OutputDimensions == 1) { - static_assert(InputDimensions % 4 == 0); - -#if defined (USE_AVX512) - if constexpr (PaddedInputDimensions % (SimdWidth * 2) != 0) - { - constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; - const auto inputVector256 = reinterpret_cast(input); - - __m256i sum0 = _mm256_setzero_si256(); - const auto row0 = reinterpret_cast(&weights[0]); - - for (int j = 0; j < (int)NumChunks; ++j) - { - const __m256i in = inputVector256[j]; - m256_add_dpbusd_epi32(sum0, in, row0[j]); - } - output[0] = m256_hadd(sum0, biases[0]); - } - else -#endif - { -#if defined (USE_AVX512) - constexpr IndexType NumChunks = PaddedInputDimensions / (SimdWidth * 2); -#else - constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; -#endif - vec_t sum0 = vec_setzero(); - const auto row0 = reinterpret_cast(&weights[0]); - - for (int j = 0; j < (int)NumChunks; ++j) - { - const vec_t in = inputVector[j]; - vec_add_dpbusd_32(sum0, in, row0[j]); - } - output[0] = vec_hadd(sum0, biases[0]); - } - } - -#else - -// Use old implementation for the other architectures. - - auto output = reinterpret_cast(buffer); - -#if defined(USE_SSE2) - // At least a multiple of 16, with SSE2. - static_assert(PaddedInputDimensions % SimdWidth == 0); - constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; - const __m128i Zeros = _mm_setzero_si128(); - const auto inputVector = reinterpret_cast(input); - -#elif defined(USE_MMX) - static_assert(InputDimensions % SimdWidth == 0); - constexpr IndexType NumChunks = InputDimensions / SimdWidth; - const __m64 Zeros = _mm_setzero_si64(); - const auto inputVector = reinterpret_cast(input); - -#elif defined(USE_NEON) - static_assert(PaddedInputDimensions % SimdWidth == 0); - constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; - const auto inputVector = reinterpret_cast(input); -#endif - - for (IndexType i = 0; i < OutputDimensions; ++i) { - const IndexType offset = i * PaddedInputDimensions; - -#if defined(USE_SSE2) - __m128i sumLo = _mm_cvtsi32_si128(biases[i]); - __m128i sumHi = Zeros; - const auto row = reinterpret_cast(&weights[offset]); - for (IndexType j = 0; j < NumChunks; ++j) { - __m128i row_j = _mm_load_si128(&row[j]); - __m128i input_j = _mm_load_si128(&inputVector[j]); - __m128i extendedRowLo = _mm_srai_epi16(_mm_unpacklo_epi8(row_j, row_j), 8); - __m128i extendedRowHi = _mm_srai_epi16(_mm_unpackhi_epi8(row_j, row_j), 8); - __m128i extendedInputLo = _mm_unpacklo_epi8(input_j, Zeros); - __m128i extendedInputHi = _mm_unpackhi_epi8(input_j, Zeros); - __m128i productLo = _mm_madd_epi16(extendedRowLo, extendedInputLo); - __m128i productHi = _mm_madd_epi16(extendedRowHi, extendedInputHi); - sumLo = _mm_add_epi32(sumLo, productLo); - sumHi = _mm_add_epi32(sumHi, productHi); - } - __m128i sum = _mm_add_epi32(sumLo, sumHi); - __m128i sumHigh_64 = _mm_shuffle_epi32(sum, _MM_SHUFFLE(1, 0, 3, 2)); - sum = _mm_add_epi32(sum, sumHigh_64); - __m128i sum_second_32 = _mm_shufflelo_epi16(sum, _MM_SHUFFLE(1, 0, 3, 2)); - sum = _mm_add_epi32(sum, sum_second_32); - output[i] = _mm_cvtsi128_si32(sum); - -#elif defined(USE_MMX) - __m64 sumLo = _mm_cvtsi32_si64(biases[i]); - __m64 sumHi = Zeros; - const auto row = reinterpret_cast(&weights[offset]); - for (IndexType j = 0; j < NumChunks; ++j) { - __m64 row_j = row[j]; - __m64 input_j = inputVector[j]; - __m64 extendedRowLo = _mm_srai_pi16(_mm_unpacklo_pi8(row_j, row_j), 8); - __m64 extendedRowHi = _mm_srai_pi16(_mm_unpackhi_pi8(row_j, row_j), 8); - __m64 extendedInputLo = _mm_unpacklo_pi8(input_j, Zeros); - __m64 extendedInputHi = _mm_unpackhi_pi8(input_j, Zeros); - __m64 productLo = _mm_madd_pi16(extendedRowLo, extendedInputLo); - __m64 productHi = _mm_madd_pi16(extendedRowHi, extendedInputHi); - sumLo = _mm_add_pi32(sumLo, productLo); - sumHi = _mm_add_pi32(sumHi, productHi); - } - __m64 sum = _mm_add_pi32(sumLo, sumHi); - sum = _mm_add_pi32(sum, _mm_unpackhi_pi32(sum, sum)); - output[i] = _mm_cvtsi64_si32(sum); - -#elif defined(USE_NEON) - int32x4_t sum = {biases[i]}; - const auto row = reinterpret_cast(&weights[offset]); - for (IndexType j = 0; j < NumChunks; ++j) { - int16x8_t product = vmull_s8(inputVector[j * 2], row[j * 2]); - product = vmlal_s8(product, inputVector[j * 2 + 1], row[j * 2 + 1]); - sum = vpadalq_s16(sum, product); - } - output[i] = sum[0] + sum[1] + sum[2] + sum[3]; - -#else - OutputType sum = biases[i]; - for (IndexType j = 0; j < InputDimensions; ++j) { - sum += weights[offset + j] * input[j]; + constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; + vec_t sum0 = vec_setzero(); + const auto row0 = reinterpret_cast(&weights[0]); + + for (int j = 0; j < (int)NumChunks; ++j) + { + const vec_t in = inputVector[j]; + vec_add_dpbusd_32(sum0, in, row0[j]); } - output[i] = sum; -#endif - + output[0] = vec_hadd(sum0, biases[0]); } -#if defined(USE_MMX) - _mm_empty(); -#endif -#endif - -#if (!defined (USE_SSSE3) && defined (USE_SSE2)) || defined (USE_NEON) - static_assert(SimdWidth <= 16, "Otherwise we run outside of the padding for the output."); - if constexpr (SimdWidth > OutputDimensions && OutputDimensions != 1) - for (IndexType i = OutputDimensions; i < SimdWidth; ++i) - output[i] = 0; +# undef vec_setzero +# undef vec_set_32 +# undef vec_add_dpbusd_32 +# undef vec_add_dpbusd_32x2 +# undef vec_add_dpbusd_32x4 +# undef vec_hadd +# undef vec_haddx4 +#else + // Use old implementation for the other architectures. + affine_transform_non_ssse3< + InputDimensions, + PaddedInputDimensions, + OutputDimensions>(output, weights, biases, input); #endif return output; diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index 65455df4944..c6f3ccade7d 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -35,9 +35,10 @@ namespace Stockfish::Eval::NNUE::Layers { static_assert(std::is_same::value, ""); // Number of input/output dimensions - static constexpr IndexType InputDimensions = - PreviousLayer::OutputDimensions; + static constexpr IndexType InputDimensions = PreviousLayer::OutputDimensions; static constexpr IndexType OutputDimensions = InputDimensions; + static constexpr IndexType PaddedOutputDimensions = + ceil_to_multiple(OutputDimensions, 32); // Size of forward propagation buffer used in this layer static constexpr std::size_t SelfBufferSize = @@ -179,6 +180,15 @@ namespace Stockfish::Eval::NNUE::Layers { output[i] = static_cast( std::max(0, std::min(127, input[i] >> WeightScaleBits))); } + + // Affine transform layers expect that there is at least + // ceil_to_multiple(OutputDimensions, 32) initialized values. + // We cannot do this in the affine transform because it requires + // preallocating space here. + for (IndexType i = OutputDimensions; i < PaddedOutputDimensions; ++i) { + output[i] = 0; + } + return output; } diff --git a/src/simd.h b/src/simd.h new file mode 100644 index 00000000000..584148f1260 --- /dev/null +++ b/src/simd.h @@ -0,0 +1,341 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef STOCKFISH_SIMD_H_INCLUDED +#define STOCKFISH_SIMD_H_INCLUDED + +#if defined(USE_AVX2) +# include + +#elif defined(USE_SSE41) +# include + +#elif defined(USE_SSSE3) +# include + +#elif defined(USE_SSE2) +# include + +#elif defined(USE_MMX) +# include + +#elif defined(USE_NEON) +# include +#endif + +// The inline asm is only safe for GCC, where it is necessary to get good codegen. +// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101693 +// Clang does fine without it. +// Play around here: https://godbolt.org/z/7EWqrYq51 +#if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) +#define USE_INLINE_ASM +#endif + +namespace Stockfish::Simd { + +#if defined (USE_AVX512) + + [[maybe_unused]] static int m512_hadd(__m512i sum, int bias) { + return _mm512_reduce_add_epi32(sum) + bias; + } + + /* + Parameters: + sum0 = [zmm0.i128[0], zmm0.i128[1], zmm0.i128[2], zmm0.i128[3]] + sum1 = [zmm1.i128[0], zmm1.i128[1], zmm1.i128[2], zmm1.i128[3]] + sum2 = [zmm2.i128[0], zmm2.i128[1], zmm2.i128[2], zmm2.i128[3]] + sum3 = [zmm3.i128[0], zmm3.i128[1], zmm3.i128[2], zmm3.i128[3]] + + Returns: + ret = [ + reduce_add_epi32(zmm0.i128[0]), reduce_add_epi32(zmm1.i128[0]), reduce_add_epi32(zmm2.i128[0]), reduce_add_epi32(zmm3.i128[0]), + reduce_add_epi32(zmm0.i128[1]), reduce_add_epi32(zmm1.i128[1]), reduce_add_epi32(zmm2.i128[1]), reduce_add_epi32(zmm3.i128[1]), + reduce_add_epi32(zmm0.i128[2]), reduce_add_epi32(zmm1.i128[2]), reduce_add_epi32(zmm2.i128[2]), reduce_add_epi32(zmm3.i128[2]), + reduce_add_epi32(zmm0.i128[3]), reduce_add_epi32(zmm1.i128[3]), reduce_add_epi32(zmm2.i128[3]), reduce_add_epi32(zmm3.i128[3]) + ] + */ + [[maybe_unused]] static __m512i m512_hadd128x16_interleave( + __m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3) { + + __m512i sum01a = _mm512_unpacklo_epi32(sum0, sum1); + __m512i sum01b = _mm512_unpackhi_epi32(sum0, sum1); + + __m512i sum23a = _mm512_unpacklo_epi32(sum2, sum3); + __m512i sum23b = _mm512_unpackhi_epi32(sum2, sum3); + + __m512i sum01 = _mm512_add_epi32(sum01a, sum01b); + __m512i sum23 = _mm512_add_epi32(sum23a, sum23b); + + __m512i sum0123a = _mm512_unpacklo_epi64(sum01, sum23); + __m512i sum0123b = _mm512_unpackhi_epi64(sum01, sum23); + + return _mm512_add_epi32(sum0123a, sum0123b); + } + + [[maybe_unused]] static __m128i m512_haddx4( + __m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3, + __m128i bias) { + + __m512i sum = m512_hadd128x16_interleave(sum0, sum1, sum2, sum3); + + __m256i sum256lo = _mm512_castsi512_si256(sum); + __m256i sum256hi = _mm512_extracti64x4_epi64(sum, 1); + + sum256lo = _mm256_add_epi32(sum256lo, sum256hi); + + __m128i sum128lo = _mm256_castsi256_si128(sum256lo); + __m128i sum128hi = _mm256_extracti128_si256(sum256lo, 1); + + return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias); + } + + [[maybe_unused]] static void m512_add_dpbusd_epi32( + __m512i& acc, + __m512i a, + __m512i b) { + +# if defined (USE_VNNI) +# if defined (USE_INLINE_ASM) + asm( + "vpdpbusd %[b], %[a], %[acc]\n\t" + : [acc]"+v"(acc) + : [a]"v"(a), [b]"vm"(b) + ); +# else + acc = _mm512_dpbusd_epi32(acc, a, b); +# endif +# else +# if defined (USE_INLINE_ASM) + __m512i tmp = _mm512_maddubs_epi16(a, b); + asm( + "vpmaddwd %[tmp], %[ones], %[tmp]\n\t" + "vpaddd %[acc], %[tmp], %[acc]\n\t" + : [acc]"+v"(acc), [tmp]"+&v"(tmp) + : [ones]"v"(_mm512_set1_epi16(1)) + ); +# else + __m512i product0 = _mm512_maddubs_epi16(a, b); + product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1)); + acc = _mm512_add_epi32(acc, product0); +# endif +# endif + } + + [[maybe_unused]] static void m512_add_dpbusd_epi32x2( + __m512i& acc, + __m512i a0, __m512i b0, + __m512i a1, __m512i b1) { + +# if defined (USE_VNNI) +# if defined (USE_INLINE_ASM) + asm( + "vpdpbusd %[b0], %[a0], %[acc]\n\t" + "vpdpbusd %[b1], %[a1], %[acc]\n\t" + : [acc]"+v"(acc) + : [a0]"v"(a0), [b0]"vm"(b0), [a1]"v"(a1), [b1]"vm"(b1) + ); +# else + acc = _mm512_dpbusd_epi32(acc, a0, b0); + acc = _mm512_dpbusd_epi32(acc, a1, b1); +# endif +# else +# if defined (USE_INLINE_ASM) + __m512i tmp0 = _mm512_maddubs_epi16(a0, b0); + __m512i tmp1 = _mm512_maddubs_epi16(a1, b1); + asm( + "vpaddsw %[tmp0], %[tmp1], %[tmp0]\n\t" + "vpmaddwd %[tmp0], %[ones], %[tmp0]\n\t" + "vpaddd %[acc], %[tmp0], %[acc]\n\t" + : [acc]"+v"(acc), [tmp0]"+&v"(tmp0) + : [tmp1]"v"(tmp1), [ones]"v"(_mm512_set1_epi16(1)) + ); +# else + __m512i product0 = _mm512_maddubs_epi16(a0, b0); + __m512i product1 = _mm512_maddubs_epi16(a1, b1); + product0 = _mm512_adds_epi16(product0, product1); + product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1)); + acc = _mm512_add_epi32(acc, product0); +# endif +# endif + } + +#endif + +#if defined (USE_AVX2) + + [[maybe_unused]] static int m256_hadd(__m256i sum, int bias) { + __m128i sum128 = _mm_add_epi32(_mm256_castsi256_si128(sum), _mm256_extracti128_si256(sum, 1)); + sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_BADC)); + sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_CDAB)); + return _mm_cvtsi128_si32(sum128) + bias; + } + + [[maybe_unused]] static __m128i m256_haddx4( + __m256i sum0, __m256i sum1, __m256i sum2, __m256i sum3, + __m128i bias) { + + sum0 = _mm256_hadd_epi32(sum0, sum1); + sum2 = _mm256_hadd_epi32(sum2, sum3); + + sum0 = _mm256_hadd_epi32(sum0, sum2); + + __m128i sum128lo = _mm256_castsi256_si128(sum0); + __m128i sum128hi = _mm256_extracti128_si256(sum0, 1); + + return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias); + } + + [[maybe_unused]] static void m256_add_dpbusd_epi32( + __m256i& acc, + __m256i a, + __m256i b) { + +# if defined (USE_VNNI) +# if defined (USE_INLINE_ASM) + asm( + "vpdpbusd %[b], %[a], %[acc]\n\t" + : [acc]"+v"(acc) + : [a]"v"(a), [b]"vm"(b) + ); +# else + acc = _mm256_dpbusd_epi32(acc, a, b); +# endif +# else +# if defined (USE_INLINE_ASM) + __m256i tmp = _mm256_maddubs_epi16(a, b); + asm( + "vpmaddwd %[tmp], %[ones], %[tmp]\n\t" + "vpaddd %[acc], %[tmp], %[acc]\n\t" + : [acc]"+v"(acc), [tmp]"+&v"(tmp) + : [ones]"v"(_mm256_set1_epi16(1)) + ); +# else + __m256i product0 = _mm256_maddubs_epi16(a, b); + product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1)); + acc = _mm256_add_epi32(acc, product0); +# endif +# endif + } + + [[maybe_unused]] static void m256_add_dpbusd_epi32x2( + __m256i& acc, + __m256i a0, __m256i b0, + __m256i a1, __m256i b1) { + +# if defined (USE_VNNI) +# if defined (USE_INLINE_ASM) + asm( + "vpdpbusd %[b0], %[a0], %[acc]\n\t" + "vpdpbusd %[b1], %[a1], %[acc]\n\t" + : [acc]"+v"(acc) + : [a0]"v"(a0), [b0]"vm"(b0), [a1]"v"(a1), [b1]"vm"(b1) + ); +# else + acc = _mm256_dpbusd_epi32(acc, a0, b0); + acc = _mm256_dpbusd_epi32(acc, a1, b1); +# endif +# else +# if defined (USE_INLINE_ASM) + __m256i tmp0 = _mm256_maddubs_epi16(a0, b0); + __m256i tmp1 = _mm256_maddubs_epi16(a1, b1); + asm( + "vpaddsw %[tmp0], %[tmp1], %[tmp0]\n\t" + "vpmaddwd %[tmp0], %[ones], %[tmp0]\n\t" + "vpaddd %[acc], %[tmp0], %[acc]\n\t" + : [acc]"+v"(acc), [tmp0]"+&v"(tmp0) + : [tmp1]"v"(tmp1), [ones]"v"(_mm256_set1_epi16(1)) + ); +# else + __m256i product0 = _mm256_maddubs_epi16(a0, b0); + __m256i product1 = _mm256_maddubs_epi16(a1, b1); + product0 = _mm256_adds_epi16(product0, product1); + product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1)); + acc = _mm256_add_epi32(acc, product0); +# endif +# endif + } + +#endif + +#if defined (USE_SSSE3) + + [[maybe_unused]] static int m128_hadd(__m128i sum, int bias) { + sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0x4E)); //_MM_PERM_BADC + sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0xB1)); //_MM_PERM_CDAB + return _mm_cvtsi128_si32(sum) + bias; + } + + [[maybe_unused]] static __m128i m128_haddx4( + __m128i sum0, __m128i sum1, __m128i sum2, __m128i sum3, + __m128i bias) { + + sum0 = _mm_hadd_epi32(sum0, sum1); + sum2 = _mm_hadd_epi32(sum2, sum3); + sum0 = _mm_hadd_epi32(sum0, sum2); + return _mm_add_epi32(sum0, bias); + } + + [[maybe_unused]] static void m128_add_dpbusd_epi32( + __m128i& acc, + __m128i a, + __m128i b) { + +# if defined (USE_INLINE_ASM) + __m128i tmp = _mm_maddubs_epi16(a, b); + asm( + "pmaddwd %[ones], %[tmp]\n\t" + "paddd %[tmp], %[acc]\n\t" + : [acc]"+v"(acc), [tmp]"+&v"(tmp) + : [ones]"v"(_mm_set1_epi16(1)) + ); +# else + __m128i product0 = _mm_maddubs_epi16(a, b); + product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1)); + acc = _mm_add_epi32(acc, product0); +# endif + } + + [[maybe_unused]] static void m128_add_dpbusd_epi32x2( + __m128i& acc, + __m128i a0, __m128i b0, + __m128i a1, __m128i b1) { + +# if defined (USE_INLINE_ASM) + __m128i tmp0 = _mm_maddubs_epi16(a0, b0); + __m128i tmp1 = _mm_maddubs_epi16(a1, b1); + asm( + "paddsw %[tmp1], %[tmp0]\n\t" + "pmaddwd %[ones], %[tmp0]\n\t" + "paddd %[tmp0], %[acc]\n\t" + : [acc]"+v"(acc), [tmp0]"+&v"(tmp0) + : [tmp1]"v"(tmp1), [ones]"v"(_mm_set1_epi16(1)) + ); +# else + __m128i product0 = _mm_maddubs_epi16(a0, b0); + __m128i product1 = _mm_maddubs_epi16(a1, b1); + product0 = _mm_adds_epi16(product0, product1); + product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1)); + acc = _mm_add_epi32(acc, product0); +# endif + } + +#endif + +} + +#endif // STOCKFISH_SIMD_H_INCLUDED From e57d2d9d4714fd085042f88a037be93772956105 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Mon, 7 Jun 2021 01:20:39 -0300 Subject: [PATCH 0652/1766] Simplify Null Move Search Reduction slightly simpler formula for reduction computation. first round of tests: STC: LLR: 2.97 (-2.94,2.94) <-2.50,0.50> Total: 15632 W: 1319 L: 1204 D: 13109 Ptnml(0-2): 33, 956, 5733, 1051, 43 https://tests.stockfishchess.org/tests/view/60bd03c7457376eb8bcaa600 LTC: LLR: 3.37 (-2.94,2.94) <-2.50,0.50> Total: 86296 W: 2814 L: 2779 D: 80703 Ptnml(0-2): 33, 2500, 38039, 2551, 25 https://tests.stockfishchess.org/tests/view/60bd1ff0457376eb8bcaa653 recent tests: STC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 23936 W: 1895 L: 1793 D: 20248 Ptnml(0-2): 40, 1470, 8869, 1526, 63 https://tests.stockfishchess.org/tests/view/611f9b7d4977aa1525c9cb6b LTC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 62568 W: 1750 L: 1713 D: 59105 Ptnml(0-2): 19, 1560, 28085, 1605, 15 https://tests.stockfishchess.org/tests/view/611fa4814977aa1525c9cb71 functional on high depth closes https://github.com/official-stockfish/Stockfish/pull/3535 Bench: 5375286 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index c48b74bcc76..83ed2aebcd4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -801,7 +801,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and value - Depth R = (1090 + 81 * depth) / 256 + std::min(int(eval - beta) / 205, 3); + Depth R = std::min(int(eval - beta) / 205, 3) + depth / 3 + 4; ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; From 939ffe454d28556ee7653af4d3322b8c8ba470be Mon Sep 17 00:00:00 2001 From: candirufish <38038147+candirufish@users.noreply.github.com> Date: Fri, 20 Aug 2021 10:37:22 +0200 Subject: [PATCH 0653/1766] do more LMR extensions for PV nodes LMR Pv and depth 6 Extension tweak: LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 52488 W: 1542 L: 1394 D: 49552 Ptnml(0-2): 18, 1253, 23552, 1405, 16 https://tests.stockfishchess.org/tests/view/611e49c34977aa1525c9caa7 STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 76216 W: 6000 L: 5784 D: 64432 Ptnml(0-2): 204, 4745, 28006, 4937, 216 https://tests.stockfishchess.org/tests/view/611e0e254977aa1525c9ca89 closes https://github.com/official-stockfish/Stockfish/pull/3666 Bench: 5046381 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 83ed2aebcd4..a487ed1305b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1173,8 +1173,8 @@ namespace { // In general we want to cap the LMR depth search at newDepth. But if // reductions are really negative and movecount is low, we allow this move - // to be searched deeper than the first move, unless ttMove was extended by 2. - Depth d = std::clamp(newDepth - r, 1, newDepth + (r < -1 && moveCount <= 5 && !doubleExtension)); + // to be searched deeper than the first move in specific cases. + Depth d = std::clamp(newDepth - r, 1, newDepth + (r < -1 && (moveCount <= 5 || (depth > 6 && PvNode)) && !doubleExtension)); value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); From 590447d7a162058ed1f68270c84aac4e2f256bb1 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Fri, 20 Aug 2021 18:30:27 +0800 Subject: [PATCH 0654/1766] Update default net to nn-517c4f68b5df.nnue SPSA: https://tests.stockfishchess.org/tests/view/611cf0da4977aa1525c9ca03 Parameters: 256 net weights and 8 net biases (output layer) Base net: nn-ac5605a608d6.nnue New net: nn-517c4f68b5df.nnue STC: LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 11600 W: 998 L: 851 D: 9751 Ptnml(0-2): 30, 705, 4186, 846, 33 https://tests.stockfishchess.org/tests/view/611f84524977aa1525c9cb5b LTC: LLR: 2.95 (-2.94,2.94) <0.50,3.50> Total: 9360 W: 338 L: 243 D: 8779 Ptnml(0-2): 0, 220, 4151, 303, 6 https://tests.stockfishchess.org/tests/view/611f8c5b4977aa1525c9cb64 closes https://github.com/official-stockfish/Stockfish/pull/3667 Bench: 4844618 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 322db398c1e..b85038779e2 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-ac5605a608d6.nnue" + #define EvalFileDefaultName "nn-517c4f68b5df.nnue" namespace NNUE { From d754ea50a8be3091f02ab279fb9069f453cc1bcc Mon Sep 17 00:00:00 2001 From: bmc4 Date: Sat, 21 Aug 2021 01:53:03 -0300 Subject: [PATCH 0655/1766] Simplify Declaration on Pawn Move Generation Removes possible micro-optimization in favor of readability. STC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 75432 W: 5824 L: 5777 D: 63831 Ptnml(0-2): 178, 4648, 28036, 4657, 197 https://tests.stockfishchess.org/tests/view/611fa7f84977aa1525c9cb75 LTC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 41200 W: 1156 L: 1106 D: 38938 Ptnml(0-2): 13, 981, 18562, 1031, 13 https://tests.stockfishchess.org/tests/view/611fcc694977aa1525c9cb9b Closes https://github.com/official-stockfish/Stockfish/pull/3669 No functional change --- src/movegen.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index 5f3ba90a479..5095bb7455e 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -52,9 +52,9 @@ namespace { constexpr Direction UpRight = (Us == WHITE ? NORTH_EAST : SOUTH_WEST); constexpr Direction UpLeft = (Us == WHITE ? NORTH_WEST : SOUTH_EAST); - const Bitboard emptySquares = Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces(); - const Bitboard enemies = Type == EVASIONS ? pos.checkers() - : Type == CAPTURES ? target : pos.pieces(Them); + const Bitboard emptySquares = ~pos.pieces(); + const Bitboard enemies = Type == EVASIONS ? pos.checkers() + : pos.pieces(Them); Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB; Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB; From af0d82792e21f0362b781fc671ae8b7a0642e61a Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 26 Aug 2021 22:44:49 +0200 Subject: [PATCH 0656/1766] Fix empty EvalFile option some GUIs send an empty string for EvalFile, in that case explicitly try the default name fixes https://github.com/official-stockfish/Stockfish/issues/3675 closes https://github.com/official-stockfish/Stockfish/pull/3678 No functional change. --- src/evaluate.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 64f9172574a..62d4be847ff 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -78,6 +78,8 @@ namespace Eval { return; string eval_file = string(Options["EvalFile"]); + if (eval_file.empty()) + eval_file = EvalFileDefaultName; #if defined(DEFAULT_NNUE_DIRECTORY) #define stringify2(x) #x @@ -118,16 +120,16 @@ namespace Eval { void NNUE::verify() { string eval_file = string(Options["EvalFile"]); + if (eval_file.empty()) + eval_file = EvalFileDefaultName; if (useNNUE && eval_file_loaded != eval_file) { - UCI::OptionsMap defaults; - UCI::init(defaults); string msg1 = "If the UCI option \"Use NNUE\" is set to true, network evaluation parameters compatible with the engine must be available."; string msg2 = "The option is set to true, but the network file " + eval_file + " was not loaded successfully."; string msg3 = "The UCI option EvalFile might need to specify the full path, including the directory name, to the network file."; - string msg4 = "The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/" + string(defaults["EvalFile"]); + string msg4 = "The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/" + std::string(EvalFileDefaultName); string msg5 = "The engine will be terminated now."; sync_cout << "info string ERROR: " << msg1 << sync_endl; From f30f231cbf74ddefd7881a653d0941a119c9b1bb Mon Sep 17 00:00:00 2001 From: ppigazzini Date: Sun, 22 Aug 2021 15:44:30 +0200 Subject: [PATCH 0657/1766] Use "pedantic" flag also for mingw This will avoid to run in fishtest a test where the linux machines exit from the building process and only the windows machines run the test. See: https://tests.stockfishchess.org/tests/view/61122d732a8a49ac5be79996 https://github.com/SFisGOD/Stockfish/commit/4e422577d6ebd1f6ecf606189190b8f6fb03f6c9#comments closes https://github.com/official-stockfish/Stockfish/pull/3671 No functional change. --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 901ddd92453..d92854bca19 100644 --- a/src/Makefile +++ b/src/Makefile @@ -368,7 +368,7 @@ ifeq ($(COMP),mingw) CXX=g++ endif - CXXFLAGS += -Wextra -Wshadow + CXXFLAGS += -pedantic -Wextra -Wshadow LDFLAGS += -static endif From 69eede7d0818be7bf7ae893dcd4408e9d621b2d2 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Thu, 26 Aug 2021 18:07:41 +0800 Subject: [PATCH 0658/1766] Update default net to nn-33495fe25081.nnue STC: LLR: 2.95 (-2.94,2.94) <-0.50,2.50> Total: 37368 W: 9621 L: 9391 D: 18356 Ptnml(0-2): 117, 4287, 9664, 4481, 135 https://tests.stockfishchess.org/tests/view/612768165318138ee1204977 LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 13328 W: 3446 L: 3246 D: 6636 Ptnml(0-2): 11, 1383, 3682, 1571, 17 https://tests.stockfishchess.org/tests/view/6127dc8d62d20cf82b5ad196 Closes https://github.com/official-stockfish/Stockfish/pull/3679 Bench: 5179347 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index b85038779e2..f73fc37ffcc 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-517c4f68b5df.nnue" + #define EvalFileDefaultName "nn-33495fe25081.nnue" namespace NNUE { From ad357e147a1b8481a04761d726ce1db14115a68f Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Fri, 27 Aug 2021 14:25:09 -0400 Subject: [PATCH 0659/1766] CMH Pruning Tweak Tweak pruning formula by adding up CMH values. STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 14608 W: 3837 L: 3641 D: 7130 Ptnml(0-2): 27, 1681, 3723, 1815, 58 https://tests.stockfishchess.org/tests/view/612792f362d20cf82b5ad156 LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 53520 W: 13580 L: 13276 D: 26664 Ptnml(0-2): 28, 5610, 15183, 5908, 31 https://tests.stockfishchess.org/tests/view/6127d27062d20cf82b5ad191 closes https://github.com/official-stockfish/Stockfish/pull/3682 Bench: 5186641 --- src/search.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index a487ed1305b..5aa506446c3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1018,9 +1018,10 @@ namespace { else { // Continuation history based pruning (~20 Elo) - if ( lmrDepth < 5 - && (*contHist[0])[movedPiece][to_sq(move)] < 23 - 23 * depth * depth - && (*contHist[1])[movedPiece][to_sq(move)] < 23 - 23 * depth * depth) + if (lmrDepth < 5 + && (*contHist[0])[movedPiece][to_sq(move)] + + (*contHist[1])[movedPiece][to_sq(move)] + + (*contHist[3])[movedPiece][to_sq(move)] < -3000 * depth + 3000) continue; // Futility pruning: parent node (~5 Elo) From 2807dcfab671bfc7a1bea79f5639dbbd505703ad Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Mon, 30 Aug 2021 00:19:46 +0800 Subject: [PATCH 0660/1766] Update default net to nn-735bba95dec0.nnue SPSA 1: https://tests.stockfishchess.org/tests/view/61286d8b62d20cf82b5ad1bd Parameters: A total of 256 net biases were tuned (hidden layer 2) Base net: nn-33495fe25081.nnue New net: nn-83e3cf2af92b.nnue SPSA 2: https://tests.stockfishchess.org/tests/view/6129cf2162d20cf82b5ad25f Parameters: A total of 64 net biases were tuned (hidden layer 1) Base net: nn-83e3cf2af92b.nnue New net: nn-69a528eaef35.nnue SPSA 3: https://tests.stockfishchess.org/tests/view/612a0dcb62d20cf82b5ad2a0 Parameters: 256 net weights and 8 net biases (output layer) Base net: nn-69a528eaef35.nnue New net: nn-735bba95dec0.nnue STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 95144 W: 24310 L: 23999 D: 46835 Ptnml(0-2): 232, 11059, 24748, 11232, 301 https://tests.stockfishchess.org/tests/view/612bb3be0fdf40644b4b9996 LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 33632 W: 8522 L: 8271 D: 16839 Ptnml(0-2): 18, 3511, 9516, 3744, 27 https://tests.stockfishchess.org/tests/view/612ce5b9bb4956d8b78eb5b3 Closes https://github.com/official-stockfish/Stockfish/pull/3685 Bench: 5600615 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index f73fc37ffcc..f26b95f4506 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-33495fe25081.nnue" + #define EvalFileDefaultName "nn-735bba95dec0.nnue" namespace NNUE { From e404a7d97c94890dda18ca3a16cfd15b5a7ef235 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Mon, 6 Sep 2021 00:17:46 +0300 Subject: [PATCH 0661/1766] Extend captures and promotions This patch introduces extension for captures and promotions. Every capture or promotion that is not the first move in the list gets extended at PvNodes and cutNodes. Special thanks to @locutus2 - all my previous attepmts that failed on this idea were done only for PvNodes - idea to include also cutNodes was based on his latest passed patch. STC https://tests.stockfishchess.org/tests/view/6134abf325b9b35584838574 LLR: 2.95 (-2.94,2.94) <-0.50,2.50> Total: 188920 W: 47754 L: 47304 D: 93862 Ptnml(0-2): 595, 21754, 49344, 22140, 627 LTC https://tests.stockfishchess.org/tests/view/613521de25b9b355848385d7 LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 8768 W: 2283 L: 2098 D: 4387 Ptnml(0-2): 7, 866, 2452, 1053, 6 closes https://github.com/official-stockfish/Stockfish/pull/3692 bench: 5564555 --- src/search.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 5aa506446c3..0d7575aa524 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1094,6 +1094,14 @@ namespace { return beta; } } + + // Capture extensions for PvNodes and cutNodes + else if ( (PvNode || cutNode) + && captureOrPromotion + && moveCount != 1) + extension = 1; + + // Check extensions else if ( givesCheck && depth > 6 && abs(ss->staticEval) > Value(100)) From be63ce1bb59d24cce3346be0e41923204ffcca13 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Sun, 5 Sep 2021 08:33:01 +0800 Subject: [PATCH 0662/1766] Update default net to nn-6762d36ad265.nnue SPSA 1: https://tests.stockfishchess.org/tests/view/612cdb1fbb4956d8b78eb5ab Parameters: A total of 256 net biases were tuned (hidden layer 2) Base net: nn-fe433fd8c7f6.nnue New net: nn-5f134823db04.nnue SPSA 2: https://tests.stockfishchess.org/tests/view/612fcde645091e810014af19 Parameters: A total of 64 net biases were tuned (hidden layer 1) Base net: nn-5f134823db04.nnue New net: nn-8eca5dd4e3f7.nnue SPSA 3: https://tests.stockfishchess.org/tests/view/6130822345091e810014af61 Parameters: 256 net weights and 8 net biases (output layer) Base net: nn-8eca5dd4e3f7.nnue New net: nn-4556108e4f00.nnue SPSA 4: https://tests.stockfishchess.org/tests/view/613287652ffb3c36aceb923c Parameters: A total of 256 net biases were tuned (hidden layer 2) Base net: nn-4556108e4f00.nnue New net: nn-6762d36ad265.nnue STC: LLR: 2.96 (-2.94,2.94) <-0.50,2.50> Total: 162776 W: 41220 L: 40807 D: 80749 Ptnml(0-2): 517, 18800, 42359, 19177, 535 https://tests.stockfishchess.org/tests/view/6134107125b9b35584838559 LTC: LLR: 2.95 (-2.94,2.94) <0.50,3.50> Total: 41056 W: 10428 L: 10156 D: 20472 Ptnml(0-2): 30, 4288, 11618, 4564, 28 https://tests.stockfishchess.org/tests/view/6134ad6525b9b3558483857a closes https://github.com/official-stockfish/Stockfish/pull/3691 Bench: 5812158 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index f26b95f4506..d7cc6e290fb 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-735bba95dec0.nnue" + #define EvalFileDefaultName "nn-6762d36ad265.nnue" namespace NNUE { From c31fc8d163ed2c5ff5697206f8a5eef65efa0044 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Mon, 6 Sep 2021 11:22:58 +0200 Subject: [PATCH 0663/1766] Improve history updates If a search failed low at an expected PV or CUT node do greater history updates. STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 95112 W: 24293 L: 23982 D: 46837 Ptnml(0-2): 285, 10893, 24906, 11170, 302 https://tests.stockfishchess.org/tests/view/6132aa1a2ffb3c36aceb926f LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 116352 W: 29450 L: 28975 D: 57927 Ptnml(0-2): 93, 12263, 32984, 12748, 88 https://tests.stockfishchess.org/tests/view/613394d12ffb3c36aceb92f4 closes https://github.com/official-stockfish/Stockfish/pull/3693 Bench: 6130736 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 0d7575aa524..6affa5107b0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1325,7 +1325,7 @@ namespace { // Bonus for prior countermove that caused the fail low else if ( (depth >= 3 || PvNode) && !priorCapture) - update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth)); + update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth + (PvNode || cutNode))); if (PvNode) bestValue = std::min(bestValue, maxValue); From b7b6b4ba18bc9d291c2c2fd300c22a3e008dcbb2 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Tue, 7 Sep 2021 13:22:20 +0200 Subject: [PATCH 0664/1766] Further improve history updates Now even double history updates if a search failed low at an expected PV or CUT node. STC: LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 30736 W: 7891 L: 7674 D: 15171 Ptnml(0-2): 90, 3477, 8017, 3694, 90 https://tests.stockfishchess.org/tests/view/61364ae30cd98ab40c0c9da5 LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 73600 W: 18684 L: 18326 D: 36590 Ptnml(0-2): 41, 7734, 20899, 8078, 48 https://tests.stockfishchess.org/tests/view/6136940f0cd98ab40c0c9df3 closes https://github.com/official-stockfish/Stockfish/pull/3694 Bench: 6030657 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 6affa5107b0..ed5dd6c2c7a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1325,7 +1325,7 @@ namespace { // Bonus for prior countermove that caused the fail low else if ( (depth >= 3 || PvNode) && !priorCapture) - update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth + (PvNode || cutNode))); + update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * (1 + (PvNode || cutNode))); if (PvNode) bestValue = std::min(bestValue, maxValue); From 30fdbf43283eb0d2f8e5ca325877d3e514758804 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Fri, 10 Sep 2021 11:38:50 +0300 Subject: [PATCH 0665/1766] Decrease depth for cutnodes with no tt move By analogy to existing logic of decreasing depth for PvNodes w/o tt move do the same for cutNodes. Passed STC https://tests.stockfishchess.org/tests/view/613abf5a689039fce12e1155 LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 90336 W: 23108 L: 22804 D: 44424 Ptnml(0-2): 286, 10316, 23642, 10656, 268 Passed LTC https://tests.stockfishchess.org/tests/view/613ae330689039fce12e1172 LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 37736 W: 9607 L: 9346 D: 18783 Ptnml(0-2): 21, 3917, 10730, 4180, 20 closes https://github.com/official-stockfish/Stockfish/pull/3697 bench 5891181 --- src/search.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index ed5dd6c2c7a..a4b17d9de6e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -903,12 +903,17 @@ namespace { ss->ttPv = ttPv; } - // Step 10. If the position is not in TT, decrease depth by 2 + // Step 10. If the position is not in TT, decrease depth by 2 or 1 depending on node type if ( PvNode && depth >= 6 && !ttMove) depth -= 2; + if ( cutNode + && depth >= 9 + && !ttMove) + depth--; + moves_loop: // When in check, search starts here ttCapture = ttMove && pos.capture_or_promotion(ttMove); From fd5e77950ea0742fb815b1b92f7ed9ac625d66cf Mon Sep 17 00:00:00 2001 From: xoto10 Date: Sun, 12 Sep 2021 09:19:38 +0100 Subject: [PATCH 0666/1766] Update 2 search parameters after tune. A tuning run on 3 search parameters was done with 200k games, narrow ranges (50-150%) and a small value for A (3% of total games) : https://tests.stockfishchess.org/tests/view/613b5f4b689039fce12e1220 STC 10+0.1 : LLR: 2.95 (-2.94,2.94) <-0.50,2.50> Total: 73112 W: 18800 L: 18520 D: 35792 Ptnml(0-2): 205, 8395, 19115, 8597, 244 https://tests.stockfishchess.org/tests/view/613cb8d2689039fce12e1308 LTC 60+0.6 : LLR: 2.95 (-2.94,2.94) <0.50,3.50> Total: 45616 W: 11604 L: 11321 D: 22691 Ptnml(0-2): 24, 4769, 12946, 5038, 31 https://tests.stockfishchess.org/tests/view/613d07048253e53e97b55b32 closes https://github.com/official-stockfish/Stockfish/pull/3698 Bench 6504816 --- src/search.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index a4b17d9de6e..0c05713c165 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -793,7 +793,7 @@ namespace { && (ss-1)->statScore < 23767 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 20 * depth - 22 * improving + 168 * ss->ttPv + 159 + && ss->staticEval >= beta - 20 * depth - 22 * improving + 168 * ss->ttPv + 177 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -1032,7 +1032,7 @@ namespace { // Futility pruning: parent node (~5 Elo) if ( !ss->inCheck && lmrDepth < 7 - && ss->staticEval + 174 + 157 * lmrDepth <= alpha) + && ss->staticEval + 172 + 157 * lmrDepth <= alpha) continue; // Prune moves with negative SEE (~20 Elo) @@ -1101,8 +1101,8 @@ namespace { } // Capture extensions for PvNodes and cutNodes - else if ( (PvNode || cutNode) - && captureOrPromotion + else if ( (PvNode || cutNode) + && captureOrPromotion && moveCount != 1) extension = 1; From 723f48dec0eb05204b51cace54b033a5a85a66b9 Mon Sep 17 00:00:00 2001 From: SFisGOD Date: Tue, 14 Sep 2021 00:28:33 +0800 Subject: [PATCH 0667/1766] Update default net to nn-13406b1dcbe0.nnue SPSA 1: https://tests.stockfishchess.org/tests/view/6134abc425b9b35584838572 Parameters: A total of 64 net biases were tuned (hidden layer 1) Base net: nn-6762d36ad265.nnue New net: nn-c9fdeea14cb2.nnue SPSA 2: https://tests.stockfishchess.org/tests/view/61355b7e25b9b3558483860e Parameters: 256 net weights and 8 net biases (output layer) Base net: nn-c9fdeea14cb2.nnue New net: nn-0ddc28184f4c.nnue SPSA 3: https://tests.stockfishchess.org/tests/view/613737be0cd98ab40c0c9e4e Parameters: A total of 256 net biases were tuned (hidden layer 2) Base net: nn-0ddc28184f4c.nnue New net: nn-2419828bb394.nnue SPSA 4: https://tests.stockfishchess.org/tests/view/613966ff689039fce12e0fe7 Parameters: A total of 64 net biases were tuned (hidden layer 1) Base net: nn-2419828bb394.nnue New net: nn-05d9b1ee3037.nnue SPSA 5: https://tests.stockfishchess.org/tests/view/613b4a38689039fce12e1209 Parameters: 256 net weights and 8 net biases (output layer) Base net: nn-05d9b1ee3037.nnue New net: nn-98c6ce0fc15f.nnue SPSA 6: https://tests.stockfishchess.org/tests/view/613e331515591e7c9ebc3fe9 Parameters: A total of 256 net biases were tuned (hidden layer 2) Base net: nn-98c6ce0fc15f.nnue New net: nn-13406b1dcbe0.nnue STC: LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 82008 W: 21044 L: 20752 D: 40212 Ptnml(0-2): 264, 9341, 21525, 9587, 287 https://tests.stockfishchess.org/tests/view/613f7c6cf29dda16fcca870c LTC: LLR: 2.96 (-2.94,2.94) <0.50,3.50> Total: 182928 W: 46258 L: 45602 D: 91068 Ptnml(0-2): 107, 19448, 51712, 20076, 121 https://tests.stockfishchess.org/tests/view/613fccb97315e7c73204a48c Closes #3703 Bench: 6658747 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index d7cc6e290fb..d7b3e55f20e 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-6762d36ad265.nnue" + #define EvalFileDefaultName "nn-13406b1dcbe0.nnue" namespace NNUE { From 5b47b4e6c04719e66559efe91e505569e0f7aafa Mon Sep 17 00:00:00 2001 From: xoto10 Date: Thu, 16 Sep 2021 08:43:53 +0100 Subject: [PATCH 0668/1766] Increase optimumTime by 10% STC 10+0.1 : LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 47032 W: 12078 L: 11841 D: 23113 Ptnml(0-2): 159, 5098, 12746, 5373, 140 https://tests.stockfishchess.org/tests/view/613f9df1f29dda16fcca8731 LTC 60+0.6 : LLR: 2.95 (-2.94,2.94) <0.50,3.50> Total: 66248 W: 16631 L: 16301 D: 33316 Ptnml(0-2): 44, 6560, 19578, 6906, 36 https://tests.stockfishchess.org/tests/view/6140603d7315e7c73204a4c1 Non-regression tests with other time control styles: Moves/Time 40/10+0 : LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 51640 W: 13350 L: 13254 D: 25036 Ptnml(0-2): 183, 5770, 13797, 5908, 162 https://tests.stockfishchess.org/tests/view/6141592b7315e7c73204a599 TCEC Style 10+0.01 : LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 20592 W: 5300 L: 5157 D: 10135 Ptnml(0-2): 81, 2240, 5544, 2317, 114 https://tests.stockfishchess.org/tests/view/61425bb27315e7c73204a6a2 Sudden death 15+0 : LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 127104 W: 32728 L: 32741 D: 61635 Ptnml(0-2): 735, 13973, 34149, 13960, 735 https://tests.stockfishchess.org/tests/view/614256a77315e7c73204a699 The first 3 tests were run with an initial version of the code, which was then modified to make the amount of extra time dependent on the size of increment. No increment gives no extra time, and the extra time given increases until an increment of 1% or more of remaining time gives 10% extra thinking time. closes https://github.com/official-stockfish/Stockfish/pull/3702 Bench 6658747 --- src/timeman.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/timeman.cpp b/src/timeman.cpp index f742d1e4422..d3713ee403a 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -68,6 +68,9 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { TimePoint timeLeft = std::max(TimePoint(1), limits.time[us] + limits.inc[us] * (mtg - 1) - moveOverhead * (2 + mtg)); + // Use extra time with larger increments + double optExtra = std::clamp(1.0 + 10.0 * limits.inc[us] / limits.time[us], 1.0, 1.1); + // A user may scale time usage by setting UCI option "Slow Mover" // Default is 100 and changing this value will probably lose elo. timeLeft = slowMover * timeLeft / 100; @@ -78,15 +81,16 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { if (limits.movestogo == 0) { optScale = std::min(0.0084 + std::pow(ply + 3.0, 0.5) * 0.0042, - 0.2 * limits.time[us] / double(timeLeft)); + 0.2 * limits.time[us] / double(timeLeft)) + * optExtra; maxScale = std::min(7.0, 4.0 + ply / 12.0); } // x moves in y seconds (+ z increment) else { - optScale = std::min((0.8 + ply / 128.0) / mtg, - 0.8 * limits.time[us] / double(timeLeft)); + optScale = std::min((0.88 + ply / 116.4) / mtg, + 0.88 * limits.time[us] / double(timeLeft)); maxScale = std::min(6.3, 1.5 + 0.11 * mtg); } From e8788d1b32c9356fa0a127952d48c3748d8ec826 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Mon, 20 Sep 2021 15:04:13 +0300 Subject: [PATCH 0669/1766] Combo of various parameter tweaks Combination of parameter tweaks in search, evaluation and time management. Original patches by snicolet xoto10 lonfom169 and Vizvezdenec. Includes: * Use bigger grain of positional evaluation more frequently (up to 1 exchange difference in non-pawn-material); * More extra time according to increment; * Increase margin for singular extensions; * Do more aggresive parent node futility pruning. Passed STC https://tests.stockfishchess.org/tests/view/6147deab3733d0e0dd9f313d LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 45488 W: 11691 L: 11450 D: 22347 Ptnml(0-2): 145, 5208, 11824, 5395, 172 Passed LTC https://tests.stockfishchess.org/tests/view/6147f1d53733d0e0dd9f3141 LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 62520 W: 15808 L: 15482 D: 31230 Ptnml(0-2): 43, 6439, 17960, 6785, 33 closes https://github.com/official-stockfish/Stockfish/pull/3710 bench 5575265 --- src/nnue/evaluate_nnue.cpp | 2 +- src/search.cpp | 6 +++--- src/timeman.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 891f8faad02..bfd6998793a 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -168,7 +168,7 @@ namespace Stockfish::Eval::NNUE { int positional = output[0]; int delta_npm = abs(pos.non_pawn_material(WHITE) - pos.non_pawn_material(BLACK)); - int entertainment = (adjusted && delta_npm <= BishopValueMg - KnightValueMg ? 7 : 0); + int entertainment = (adjusted && delta_npm <= RookValueMg - BishopValueMg ? 7 : 0); int A = 128 - entertainment; int B = 128 + entertainment; diff --git a/src/search.cpp b/src/search.cpp index 0c05713c165..b400b8f8b0c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1031,8 +1031,8 @@ namespace { // Futility pruning: parent node (~5 Elo) if ( !ss->inCheck - && lmrDepth < 7 - && ss->staticEval + 172 + 157 * lmrDepth <= alpha) + && lmrDepth < 8 + && ss->staticEval + 172 + 145 * lmrDepth <= alpha) continue; // Prune moves with negative SEE (~20 Elo) @@ -1057,7 +1057,7 @@ namespace { && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - 2 * depth; + Value singularBeta = ttValue - 3 * depth; Depth singularDepth = (depth - 1) / 2; ss->excludedMove = move; diff --git a/src/timeman.cpp b/src/timeman.cpp index d3713ee403a..69d1c96fa98 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -69,7 +69,7 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { limits.time[us] + limits.inc[us] * (mtg - 1) - moveOverhead * (2 + mtg)); // Use extra time with larger increments - double optExtra = std::clamp(1.0 + 10.0 * limits.inc[us] / limits.time[us], 1.0, 1.1); + double optExtra = std::clamp(1.0 + 12.0 * limits.inc[us] / limits.time[us], 1.0, 1.12); // A user may scale time usage by setting UCI option "Slow Mover" // Default is 100 and changing this value will probably lose elo. From 73018a03375b4b72ee482eb5a4a2152d7e4f0aac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Thu, 23 Sep 2021 23:19:06 +0200 Subject: [PATCH 0670/1766] Detect search explosions This patch detects some search explosions (due to double extensions in search.cpp) which can happen in some pathological positions, and takes measures to ensure progress in search even for these pathological situations. While a small number of double extensions can be useful during search (for example to resolve a tactical sequence), a sustained regime of double extensions leads to search explosion and a non-finishing search. See the discussion in https://github.com/official-stockfish/Stockfish/pull/3544 and the issue https://github.com/official-stockfish/Stockfish/issues/3532 . The implemented algorithm is the following: a) at each node during search, store the current depth in the stack. Double extensions are by definition levels of the stack where the depth at ply N is strictly higher than depth at ply N-1. b) during search, calculate for each thread a running average of the number of double extensions in the last 4096 visited nodes. c) if one thread has more than 2% of double extensions for a sustained period of time (6 millions consecutive nodes, or about 4 seconds on my iMac), we decide that this thread is in an explosion state and we calm down this thread by preventing it to do any double extension for the next 6 millions nodes. To calculate the running averages, we also introduced a auxiliary class generalizing the computations of ttHitAverage variable we already had in code. The implementation uses an exponential moving average of period 4096 and resolution 1/1024, and all computations are done with integers for efficiency. ----------- Example where the patch solves a search explosion: ``` ./stockfish ucinewgame position fen 8/Pk6/8/1p6/8/P1K5/8/6B1 w - - 37 130 go infinite ``` This algorithm does not affect search in normal, non-pathological positions. We verified, for instance, that the usual bench is unchanged up to depth 20 at least, and that the node numbers are unchanged for a search of the starting position at depth 32. ------------- See https://github.com/official-stockfish/Stockfish/pull/3714 Bench: 5575265 --- src/misc.h | 27 ++++++++++++++++++ src/search.cpp | 76 +++++++++++++++++++++++++++++++++++++++----------- src/search.h | 1 + src/thread.h | 8 ++++-- src/types.h | 5 ++++ 5 files changed, 99 insertions(+), 18 deletions(-) diff --git a/src/misc.h b/src/misc.h index dae37cd98f0..7a3369e8ed5 100644 --- a/src/misc.h +++ b/src/misc.h @@ -85,6 +85,33 @@ static inline const union { uint32_t i; char c[4]; } Le = { 0x01020304 }; static inline const bool IsLittleEndian = (Le.c[0] == 4); +// RunningAverage : a class to calculate a running average of a series of values. +// For efficiency, all computations are done with integers. +class RunningAverage { + public: + + // Constructor + RunningAverage() {} + + // Reset the running average to rational value p / q + void set(int64_t p, int64_t q) + { average = p * PERIOD * RESOLUTION / q; } + + // Update average with value v + void update(int64_t v) + { average = RESOLUTION * v + (PERIOD - 1) * average / PERIOD; } + + // Test if average is strictly greater than rational a / b + bool is_greater(int64_t a, int64_t b) + { return b * average > a * PERIOD * RESOLUTION ; } + + private : + static constexpr int64_t PERIOD = 4096; + static constexpr int64_t RESOLUTION = 1024; + int64_t average; +}; + + template class ValueListInserter { public: diff --git a/src/search.cpp b/src/search.cpp index b400b8f8b0c..10a9027b949 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -61,9 +61,6 @@ namespace { // Different node types, used as a template parameter enum NodeType { NonPV, PV, Root }; - constexpr uint64_t TtHitAverageWindow = 4096; - constexpr uint64_t TtHitAverageResolution = 1024; - // Futility margin Value futility_margin(Depth d, bool improving) { return Value(214 * (d - improving)); @@ -91,6 +88,30 @@ namespace { return VALUE_DRAW + Value(2 * (thisThread->nodes & 1) - 1); } + // Check if the current thread is in a search explosion + ExplosionState search_explosion(Thread* thisThread) { + + uint64_t nodesNow = thisThread->nodes; + bool explosive = thisThread->doubleExtensionAverage[WHITE].is_greater(2, 100) + || thisThread->doubleExtensionAverage[BLACK].is_greater(2, 100); + + if (explosive) + thisThread->nodesLastExplosive = nodesNow; + else + thisThread->nodesLastNormal = nodesNow; + + if ( explosive + && thisThread->state == EXPLOSION_NONE + && nodesNow - thisThread->nodesLastNormal > 6000000) + thisThread->state = MUST_CALM_DOWN; + + if ( thisThread->state == MUST_CALM_DOWN + && nodesNow - thisThread->nodesLastExplosive > 6000000) + thisThread->state = EXPLOSION_NONE; + + return thisThread->state; + } + // Skill structure is used to implement strength limit struct Skill { explicit Skill(int l) : level(l) {} @@ -310,8 +331,14 @@ void Thread::search() { multiPV = std::max(multiPV, (size_t)4); multiPV = std::min(multiPV, rootMoves.size()); - ttHitAverage = TtHitAverageWindow * TtHitAverageResolution / 2; + ttHitAverage.set(50, 100); // initialize the running average at 50% + doubleExtensionAverage[WHITE].set(0, 100); // initialize the running average at 0% + doubleExtensionAverage[BLACK].set(0, 100); // initialize the running average at 0% + + nodesLastExplosive = nodes; + nodesLastNormal = nodes; + state = EXPLOSION_NONE; trend = SCORE_ZERO; int searchAgainCounter = 0; @@ -518,6 +545,14 @@ namespace { template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) { + Thread* thisThread = pos.this_thread(); + + // Step 0. Limit search explosion + if ( ss->ply > 10 + && search_explosion(thisThread) == MUST_CALM_DOWN + && depth > (ss-1)->depth) + depth = (ss-1)->depth; + constexpr bool PvNode = nodeType != NonPV; constexpr bool rootNode = nodeType == Root; const Depth maxNextDepth = rootNode ? depth : depth + 1; @@ -554,12 +589,11 @@ namespace { Value bestValue, value, ttValue, eval, maxValue, probCutBeta; bool givesCheck, improving, didLMR, priorCapture; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, - ttCapture, singularQuietLMR; + ttCapture, singularQuietLMR, noLMRExtension; Piece movedPiece; int moveCount, captureCount, quietCount; // Step 1. Initialize node - Thread* thisThread = pos.this_thread(); ss->inCheck = pos.checkers(); priorCapture = pos.captured_piece(); Color us = pos.side_to_move(); @@ -602,8 +636,12 @@ namespace { (ss+1)->excludedMove = bestMove = MOVE_NONE; (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; ss->doubleExtensions = (ss-1)->doubleExtensions; + ss->depth = depth; Square prevSq = to_sq((ss-1)->currentMove); + // Update the running average statistics for double extensions + thisThread->doubleExtensionAverage[us].update(ss->depth > (ss-1)->depth); + // Initialize statScore to zero for the grandchildren of the current position. // So statScore is shared between all grandchildren and only the first grandchild // starts with statScore = 0. Later grandchildren start with the last calculated @@ -632,9 +670,8 @@ namespace { && is_ok((ss-1)->currentMove)) thisThread->lowPlyHistory[ss->ply - 1][from_to((ss-1)->currentMove)] << stat_bonus(depth - 5); - // thisThread->ttHitAverage can be used to approximate the running average of ttHit - thisThread->ttHitAverage = (TtHitAverageWindow - 1) * thisThread->ttHitAverage / TtHitAverageWindow - + TtHitAverageResolution * ss->ttHit; + // running average of ttHit + thisThread->ttHitAverage.update(ss->ttHit); // At non-PV nodes we check for an early TT cutoff if ( !PvNode @@ -948,8 +985,7 @@ namespace { ss->ply); value = bestValue; - singularQuietLMR = moveCountPruning = false; - bool doubleExtension = false; + singularQuietLMR = moveCountPruning = noLMRExtension = false; // Indicate PvNodes that will probably fail low if the node was searched // at a depth equal or greater than the current depth, and the result of this search was a fail low. @@ -1069,13 +1105,13 @@ namespace { extension = 1; singularQuietLMR = !ttCapture; - // Avoid search explosion by limiting the number of double extensions to at most 3 + // Avoid search explosion by limiting the number of double extensions if ( !PvNode && value < singularBeta - 93 && ss->doubleExtensions < 3) { extension = 2; - doubleExtension = true; + noLMRExtension = true; } } @@ -1146,7 +1182,7 @@ namespace { r--; // Decrease reduction if the ttHit running average is large (~0 Elo) - if (thisThread->ttHitAverage > 537 * TtHitAverageResolution * TtHitAverageWindow / 1024) + if (thisThread->ttHitAverage.is_greater(537, 1024)) r--; // Decrease reduction if position is or has been on the PV @@ -1187,8 +1223,16 @@ namespace { // In general we want to cap the LMR depth search at newDepth. But if // reductions are really negative and movecount is low, we allow this move - // to be searched deeper than the first move in specific cases. - Depth d = std::clamp(newDepth - r, 1, newDepth + (r < -1 && (moveCount <= 5 || (depth > 6 && PvNode)) && !doubleExtension)); + // to be searched deeper than the first move in specific cases (note that + // this may lead to hidden double extensions if newDepth got it own extension + // before). + int deeper = r >= -1 ? 0 + : noLMRExtension ? 0 + : moveCount <= 5 ? 1 + : (depth > 6 && PvNode) ? 1 + : 0; + + Depth d = std::clamp(newDepth - r, 1, newDepth + deeper); value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); diff --git a/src/search.h b/src/search.h index 801baacc7f1..ba9d067793a 100644 --- a/src/search.h +++ b/src/search.h @@ -47,6 +47,7 @@ struct Stack { Move excludedMove; Move killers[2]; Value staticEval; + Depth depth; int statScore; int moveCount; bool inCheck; diff --git a/src/thread.h b/src/thread.h index 5bfa2359afd..2475d2ec254 100644 --- a/src/thread.h +++ b/src/thread.h @@ -60,10 +60,14 @@ class Thread { Pawns::Table pawnsTable; Material::Table materialTable; size_t pvIdx, pvLast; - uint64_t ttHitAverage; + RunningAverage ttHitAverage; + RunningAverage doubleExtensionAverage[COLOR_NB]; + uint64_t nodesLastExplosive; + uint64_t nodesLastNormal; + std::atomic nodes, tbHits, bestMoveChanges; int selDepth, nmpMinPly; Color nmpColor; - std::atomic nodes, tbHits, bestMoveChanges; + ExplosionState state; Position rootPos; StateInfo rootState; diff --git a/src/types.h b/src/types.h index 0bd4a1c4090..fd643117072 100644 --- a/src/types.h +++ b/src/types.h @@ -173,6 +173,11 @@ enum Bound { BOUND_EXACT = BOUND_UPPER | BOUND_LOWER }; +enum ExplosionState { + EXPLOSION_NONE, + MUST_CALM_DOWN +}; + enum Value : int { VALUE_ZERO = 0, VALUE_DRAW = 0, From ff3fa0c664a5799e5531b0908018c59633d761bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Thu, 23 Sep 2021 11:20:03 +0200 Subject: [PATCH 0671/1766] Tweak doubly singular condition (Topo's patch) This patch relax a little bit the condition for doubly singular moves (ie moves that are so forced that we think that they deserve a local double extension of the search). We lower the margin and allow up to six such double extensions in the path between the root and the critical node. Original idea by Siad Daboul (@TopoIogist) in PR #3709 Tested with the previous commit: passed STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 33048 W: 8458 L: 8236 D: 16354 Ptnml(0-2): 120, 3701, 8660, 3923, 120 https://tests.stockfishchess.org/tests/view/614b24347bdc23e77ceb88fe passed LTC: LLR: 2.95 (-2.94,2.94) <0.50,3.50> Total: 54176 W: 13712 L: 13406 D: 27058 Ptnml(0-2): 36, 5653, 15399, 5969, 31 https://tests.stockfishchess.org/tests/view/614b3b727bdc23e77ceb8911 closes https://github.com/official-stockfish/Stockfish/pull/3714 Bench: 5792377 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 10a9027b949..9b56bd2b083 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1107,8 +1107,8 @@ namespace { // Avoid search explosion by limiting the number of double extensions if ( !PvNode - && value < singularBeta - 93 - && ss->doubleExtensions < 3) + && value < singularBeta - 75 + && ss->doubleExtensions <= 6) { extension = 2; noLMRExtension = true; From 00e34a758f2ca170986550a1f8f25dfe691ca511 Mon Sep 17 00:00:00 2001 From: OfekShochat Date: Thu, 23 Sep 2021 23:16:17 +0300 Subject: [PATCH 0672/1766] Range reductions adding reductions for when the delta between the static eval and the child's eval is consistently low. passed STC https://tests.stockfishchess.org/html/live_elo.html?614d7b3c7bdc23e77ceb8a5d LLR: 2.95 (-2.94,2.94) <-0.50,2.50> Total: 88872 W: 22672 L: 22366 D: 43834 Ptnml(0-2): 343, 10150, 23117, 10510, 316 passed LTC https://tests.stockfishchess.org/html/live_elo.html?614daf3e7bdc23e77ceb8a82 LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 24368 W: 6153 L: 5928 D: 12287 Ptnml(0-2): 13, 2503, 6937, 2708, 23 closes https://github.com/official-stockfish/Stockfish/pull/3717 Bench: 5443950 --- AUTHORS | 1 + src/search.cpp | 13 +++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index 5b5bbf22b67..be35127a706 100644 --- a/AUTHORS +++ b/AUTHORS @@ -143,6 +143,7 @@ Nikolay Kostov (NikolayIT) Nguyen Pham (nguyenpham) Norman Schmidt (FireFather) notruck +Ofek Shochat (OfekShochat, ghostway) Ondrej Mosnáček (WOnder93) Oskar Werkelin Ahlin Pablo Vazquez diff --git a/src/search.cpp b/src/search.cpp index 9b56bd2b083..ffeb511095d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -69,9 +69,9 @@ namespace { // Reductions lookup table, initialized at startup int Reductions[MAX_MOVES]; // [depth or moveNumber] - Depth reduction(bool i, Depth d, int mn) { + Depth reduction(bool i, Depth d, int mn, bool rangeReduction) { int r = Reductions[d] * Reductions[mn]; - return (r + 534) / 1024 + (!i && r > 904); + return (r + 534) / 1024 + (!i && r > 904) + rangeReduction; } constexpr int futility_move_count(bool improving, Depth depth) { @@ -954,6 +954,7 @@ namespace { moves_loop: // When in check, search starts here ttCapture = ttMove && pos.capture_or_promotion(ttMove); + int rangeReduction = 0; // Step 11. A small Probcut idea, when we are in check probCutBeta = beta + 409; @@ -1041,7 +1042,7 @@ namespace { moveCountPruning = moveCount >= futility_move_count(improving, depth); // Reduced depth of the next LMR search - int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), 0); + int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount, rangeReduction > 2), 0); if ( captureOrPromotion || givesCheck) @@ -1176,7 +1177,7 @@ namespace { || !ss->ttPv) && (!PvNode || ss->ply > 1 || thisThread->id() % 4 != 3)) { - Depth r = reduction(improving, depth, moveCount); + Depth r = reduction(improving, depth, moveCount, rangeReduction > 2); if (PvNode) r--; @@ -1236,6 +1237,10 @@ namespace { value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); + // Range reductions (~3 Elo) + if (ss->staticEval - value < 30 && depth > 7) + rangeReduction++; + // If the son is reduced and fails high it will be re-searched at full depth doFullDepthSearch = value > alpha && d < newDepth; didLMR = true; From 919da65d70f0041abbb0102133ed0abbf25b1af0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sat, 25 Sep 2021 19:37:47 +0200 Subject: [PATCH 0673/1766] Reduction instead of cutoff In master, during singular move analysis, when both the transposition value and a reduced search for the other moves seem to indicate a fail high, we heuristically prune the whole subtree and return an fail high score. This patch is a little bit more cautious in this case, and instead of the risky cutoff, we now search the ttMove with a reduced depth (by two plies). STC: https://tests.stockfishchess.org/tests/view/614dafe07bdc23e77ceb8a89 LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 46728 W: 11909 L: 11666 D: 23153 Ptnml(0-2): 181, 5288, 12168, 5561, 166 LTC: https://tests.stockfishchess.org/tests/view/614dc84abe4c07e0ecac3c95 LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 74520 W: 18809 L: 18450 D: 37261 Ptnml(0-2): 45, 7735, 21346, 8084, 50 closes https://github.com/official-stockfish/Stockfish/pull/3718 Bench: 5499262 --- src/search.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ffeb511095d..8db295f1fd8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1125,7 +1125,8 @@ namespace { return singularBeta; // If the eval of ttMove is greater than beta we try also if there is another - // move that pushes it over beta, if so also produce a cutoff. + // move that pushes it over beta, if so the position also has probably multiple + // moves giving fail highs. We will then reduce the ttMove (negative extension). else if (ttValue >= beta) { ss->excludedMove = move; @@ -1133,7 +1134,7 @@ namespace { ss->excludedMove = MOVE_NONE; if (value >= beta) - return beta; + extension = -2; } } From 21ad356c0900c9eba9b7b1f7453f934eab80f303 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 26 Sep 2021 06:39:27 +0200 Subject: [PATCH 0674/1766] Extend quiet tt moves at PvNodes Idea is to extend some quiet ttMoves if a lot of things indicate that the transposition table move is going to be a good move: 1) move being a killer - so being the best move in nearby node; 2) reply continuation history is really good. This is basically saying that move is good "in general" in this position, that it is a good reply to the opponent move and that it was the best in this position somewhere in search - so extending it makes a lot of sense. In general in past year we had a lot of extensions of different types, maybe there is something more in it :) passed STC LLR: 2.96 (-2.94,2.94) <-0.50,2.50> Total: 42944 W: 10932 L: 10695 D: 21317 Ptnml(0-2): 141, 4869, 11210, 5116, 136 https://tests.stockfishchess.org/tests/view/614cca8e7bdc23e77ceb89f0 passed LTC LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 156848 W: 39473 L: 38893 D: 78482 Ptnml(0-2): 125, 16327, 44913, 16961, 98 https://tests.stockfishchess.org/tests/view/614cf93d7bdc23e77ceb8a13 closes https://github.com/official-stockfish/Stockfish/pull/3719 Bench: 5714575 --- src/search.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 8db295f1fd8..48694cb2ded 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1147,7 +1147,14 @@ namespace { // Check extensions else if ( givesCheck && depth > 6 - && abs(ss->staticEval) > Value(100)) + && abs(ss->staticEval) > 100) + extension = 1; + + // Quiet ttMove extensions + else if ( PvNode + && move == ttMove + && move == ss->killers[0] + && (*contHist[0])[movedPiece][to_sq(move)] >= 10000) extension = 1; // Add extension to new depth @@ -1223,11 +1230,10 @@ namespace { // Decrease/increase reduction for moves with a good/bad history (~30 Elo) r -= ss->statScore / 14721; - // In general we want to cap the LMR depth search at newDepth. But if - // reductions are really negative and movecount is low, we allow this move - // to be searched deeper than the first move in specific cases (note that - // this may lead to hidden double extensions if newDepth got it own extension - // before). + // In general we want to cap the LMR depth search at newDepth. But if reductions + // are really negative and movecount is low, we allow this move to be searched + // deeper than the first move (this may lead to hidden double extensions if + // newDepth got its own extension before). int deeper = r >= -1 ? 0 : noLMRExtension ? 0 : moveCount <= 5 ? 1 From 135caee606c86ade9e9c199ef469661c374eb9ba Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 3 Oct 2021 11:27:40 +0300 Subject: [PATCH 0675/1766] Increase reductions with thread count Respin of multi-thread idea that was simplified away recently: basically doing more reductions with thread count since Lazy SMP naturally widens search. With drawish book this idea got simplified away but with less drawish book it again gains elo, maybe trying to reinstall other ideas that were simplified away previously can be beneficial. passed STC LLR: 2.96 (-2.94,2.94) <-0.50,2.50> Total: 39736 W: 10205 L: 9986 D: 19545 Ptnml(0-2): 45, 4254, 11064, 4447, 58 https://tests.stockfishchess.org/tests/view/615750702d02f48db3961b00 passed LTC LLR: 2.97 (-2.94,2.94) <0.50,3.50> Total: 60352 W: 15530 L: 15218 D: 29604 Ptnml(0-2): 24, 5900, 18016, 6212, 24 https://tests.stockfishchess.org/tests/view/6157d8935488e26ea5eace7f closes https://github.com/official-stockfish/Stockfish/pull/3724 Bench 5714575 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 48694cb2ded..3eb8e9e1ee8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -173,7 +173,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int(21.9 * std::log(i)); + Reductions[i] = int((21.9 + std::log(Threads.size()) / 2) * std::log(i)); } From 371b522e9ed9cab91274ff111c0bf4b0f6ec3340 Mon Sep 17 00:00:00 2001 From: "J. Oster" Date: Tue, 5 Oct 2021 12:02:25 +0200 Subject: [PATCH 0676/1766] Time-management fix in MultiPV mode. When playing games in MultiPV mode we must take care to only track the best move changing for the first PV line. Otherwise, SF will spend most of its time for the initial moves after the book exit. This has been observed and reported on Discord, but can also be seen in games played in Stefan Pohl's MultiPV experiment. Tested with MultiPV=4. STC: https://tests.stockfishchess.org/tests/view/615c24b59d256038a969b990 LLR: 2.95 (-2.94,2.94) <-0.50,2.50> Total: 1744 W: 694 L: 447 D: 603 Ptnml(0-2): 32, 125, 358, 278, 79 LTC: https://tests.stockfishchess.org/tests/view/615c31769d256038a969b993 LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 2048 W: 723 L: 525 D: 800 Ptnml(0-2): 10, 158, 511, 314, 31 closes https://github.com/official-stockfish/Stockfish/pull/3729 Bench: 5714575 --- src/search.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 3eb8e9e1ee8..df4af54a5ee 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1314,9 +1314,11 @@ namespace { for (Move* m = (ss+1)->pv; *m != MOVE_NONE; ++m) rm.pv.push_back(*m); - // We record how often the best move has been changed in each - // iteration. This information is used for time management and LMR - if (moveCount > 1) + // We record how often the best move has been changed in each iteration. + // This information is used for time management and LMR. In MultiPV mode, + // we must take care to only do this for the first PV line. + if ( moveCount > 1 + && !thisThread->pvIdx) ++thisThread->bestMoveChanges; } else From 329bdbd9cfa270dd7141e5184180fbde1b5898b4 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Tue, 5 Oct 2021 22:14:13 +0200 Subject: [PATCH 0677/1766] Improve the Chess960 correction for cornered bishops As Chess960 patches can not be tested on fishtest, this was locally tuned and tested: Elo: 2.36 +- 1.07 LOS: 0.999992 closes https://github.com/official-stockfish/Stockfish/pull/3730 Bench: 5714575 --- src/evaluate.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 62d4be847ff..58dd0026fdd 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1053,26 +1053,22 @@ namespace { if ( pos.piece_on(SQ_A1) == W_BISHOP && pos.piece_on(SQ_B2) == W_PAWN) - correction += !pos.empty(SQ_B3) ? -CorneredBishop * 4 - : -CorneredBishop * 3; + correction -= CorneredBishop; if ( pos.piece_on(SQ_H1) == W_BISHOP && pos.piece_on(SQ_G2) == W_PAWN) - correction += !pos.empty(SQ_G3) ? -CorneredBishop * 4 - : -CorneredBishop * 3; + correction -= CorneredBishop; if ( pos.piece_on(SQ_A8) == B_BISHOP && pos.piece_on(SQ_B7) == B_PAWN) - correction += !pos.empty(SQ_B6) ? CorneredBishop * 4 - : CorneredBishop * 3; + correction += CorneredBishop; if ( pos.piece_on(SQ_H8) == B_BISHOP && pos.piece_on(SQ_G7) == B_PAWN) - correction += !pos.empty(SQ_G6) ? CorneredBishop * 4 - : CorneredBishop * 3; + correction += CorneredBishop; - return pos.side_to_move() == WHITE ? Value(correction) - : -Value(correction); + return pos.side_to_move() == WHITE ? Value(5 * correction) + : -Value(5 * correction); } } // namespace Eval From 54a989930ebed200c3278c725151e26a2c0da37a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Mon, 4 Oct 2021 20:37:26 +0200 Subject: [PATCH 0678/1766] Capping stat bonus at 2000 This patch updates the stat_bonus() function (used in the history tables to help move ordering), keeping the same quadratic for small depths but changing the values for depth >= 9: The old bonus formula was increasing from zero at depth 1 to 4100 at depth 14, then used the strange, small value of 73 for all depths >= 15. The new bonus formula increases from 0 at depth 1 to 2000 at depth 8, then keeps 2000 for all depths >= 8. passed STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 169624 W: 42875 L: 42454 D: 84295 Ptnml(0-2): 585, 19340, 44557, 19729, 601 https://tests.stockfishchess.org/tests/view/615bd69e9d256038a969b97c passed LTC: LLR: 3.07 (-2.94,2.94) <0.50,3.50> Total: 37336 W: 9456 L: 9191 D: 18689 Ptnml(0-2): 20, 3810, 10747, 4067, 24 https://tests.stockfishchess.org/tests/view/615c75d99d256038a969b9b2 closes https://github.com/official-stockfish/Stockfish/pull/3731 Bench: 6261865 --- src/search.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index df4af54a5ee..ef691500e64 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -80,7 +80,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return d > 14 ? 73 : 6 * d * d + 229 * d - 215; + return std::min((6 * d + 229) * d - 215 , 2000); } // Add a small random component to draw evaluations to avoid 3-fold blindness @@ -1709,8 +1709,8 @@ namespace { PieceType captured = type_of(pos.piece_on(to_sq(bestMove))); bonus1 = stat_bonus(depth + 1); - bonus2 = bestValue > beta + PawnValueMg ? bonus1 // larger bonus - : std::min(bonus1, stat_bonus(depth)); // smaller bonus + bonus2 = bestValue > beta + PawnValueMg ? bonus1 // larger bonus + : stat_bonus(depth); // smaller bonus if (!pos.capture_or_promotion(bestMove)) { From f21a66f70dce4e9d72031a13d25ac530bbafc830 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Thu, 27 May 2021 16:04:47 +0100 Subject: [PATCH 0679/1766] Small clean-up, Sept 2021 Closes https://github.com/official-stockfish/Stockfish/pull/3485 No functional change --- AUTHORS | 1 + src/Makefile | 6 +++--- src/evaluate.cpp | 12 ++++++------ src/evaluate.h | 2 +- src/movepick.h | 4 ++-- src/nnue/evaluate_nnue.cpp | 2 +- src/search.cpp | 3 +-- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/AUTHORS b/AUTHORS index be35127a706..56725e98d68 100644 --- a/AUTHORS +++ b/AUTHORS @@ -21,6 +21,7 @@ Alexander Kure Alexander Pagel (Lolligerhans) Alfredo Menezes (lonfom169) Ali AlZhrani (Cooffe) +Andrei Vetrov (proukornew) Andrew Grant (AndyGrant) Andrey Neporada (nepal) Andy Duplain diff --git a/src/Makefile b/src/Makefile index d92854bca19..fea597e76cb 100644 --- a/src/Makefile +++ b/src/Makefile @@ -88,7 +88,7 @@ endif # at the end of the line for flag values. # # Example of use for these flags: -# make build ARCH=x86-64-avx512 debug=on sanitize="address undefined" +# make build ARCH=x86-64-avx512 debug=yes sanitize="address undefined" ### 2.1. General and architecture defaults @@ -517,7 +517,7 @@ ifeq ($(bits),64) CXXFLAGS += -DIS_64BIT endif -### 3.5 prefetch +### 3.5 prefetch and popcount ifeq ($(prefetch),yes) ifeq ($(sse),yes) CXXFLAGS += -msse @@ -526,7 +526,6 @@ else CXXFLAGS += -DNO_PREFETCH endif -### 3.6 popcnt ifeq ($(popcnt),yes) ifeq ($(arch),$(filter $(arch),ppc64 armv7 armv8 arm64)) CXXFLAGS += -DUSE_POPCNT @@ -537,6 +536,7 @@ ifeq ($(popcnt),yes) endif endif +### 3.6 SIMD architectures ifeq ($(avx2),yes) CXXFLAGS += -DUSE_AVX2 ifeq ($(comp),$(filter $(comp),gcc clang mingw)) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 58dd0026fdd..2195f9136fd 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -61,7 +61,7 @@ namespace Stockfish { namespace Eval { bool useNNUE; - string eval_file_loaded = "None"; + string currentEvalFileName = "None"; /// NNUE::init() tries to load a NNUE network at startup time, or when the engine /// receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue" @@ -90,13 +90,13 @@ namespace Eval { #endif for (string directory : dirs) - if (eval_file_loaded != eval_file) + if (currentEvalFileName != eval_file) { if (directory != "") { ifstream stream(directory + eval_file, ios::binary); if (load_eval(eval_file, stream)) - eval_file_loaded = eval_file; + currentEvalFileName = eval_file; } if (directory == "" && eval_file == EvalFileDefaultName) @@ -111,7 +111,7 @@ namespace Eval { istream stream(&buffer); if (load_eval(eval_file, stream)) - eval_file_loaded = eval_file; + currentEvalFileName = eval_file; } } } @@ -123,7 +123,7 @@ namespace Eval { if (eval_file.empty()) eval_file = EvalFileDefaultName; - if (useNNUE && eval_file_loaded != eval_file) + if (useNNUE && currentEvalFileName != eval_file) { string msg1 = "If the UCI option \"Use NNUE\" is set to true, network evaluation parameters compatible with the engine must be available."; @@ -1081,7 +1081,7 @@ Value Eval::evaluate(const Position& pos) { Value v; - if (!Eval::useNNUE) + if (!useNNUE) v = Evaluation(pos).value(); else { diff --git a/src/evaluate.h b/src/evaluate.h index d7b3e55f20e..e2cdb21019a 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -34,7 +34,7 @@ namespace Eval { Value evaluate(const Position& pos); extern bool useNNUE; - extern std::string eval_file_loaded; + extern std::string currentEvalFileName; // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the diff --git a/src/movepick.h b/src/movepick.h index c76d49572b8..2932d9a8a0d 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -89,8 +89,8 @@ enum StatsType { NoCaptures, Captures }; typedef Stats ButterflyHistory; /// At higher depths LowPlyHistory records successful quiet moves near the root -/// and quiet moves which are/were in the PV (ttPv). It is cleared with each new -/// search and filled during iterative deepening. +/// and quiet moves which are/were in the PV (ttPv). LowPlyHistory is populated during +/// iterative deepening and at each new search the data is shifted down by 2 plies constexpr int MAX_LPH = 4; typedef Stats LowPlyHistory; diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index bfd6998793a..7f5925a7c68 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -396,7 +396,7 @@ namespace Stockfish::Eval::NNUE { actualFilename = filename.value(); else { - if (eval_file_loaded != EvalFileDefaultName) + if (currentEvalFileName != EvalFileDefaultName) { msg = "Failed to export a net. A non-embedded net can only be saved if the filename is specified"; diff --git a/src/search.cpp b/src/search.cpp index ef691500e64..ac6b8608848 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -790,7 +790,6 @@ namespace { else { // In case of null move search use previous static eval with a different sign - // and addition of two tempos if ((ss-1)->currentMove != MOVE_NULL) ss->staticEval = eval = evaluate(pos); else @@ -1187,6 +1186,7 @@ namespace { { Depth r = reduction(improving, depth, moveCount, rangeReduction > 2); + // Decrease reduction if on the PV (~1 Elo) if (PvNode) r--; @@ -1499,7 +1499,6 @@ namespace { } else // In case of null move search use previous static eval with a different sign - // and addition of two tempos ss->staticEval = bestValue = (ss-1)->currentMove != MOVE_NULL ? evaluate(pos) : -(ss-1)->staticEval; From c8459b18ba2d6ddc76d6db90d6eab346ed682e69 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 9 Oct 2021 02:15:43 +0300 Subject: [PATCH 0680/1766] Reduce more if multiple moves exceed alpha Idea of this patch is the following: in case we already have four moves that exceeded alpha in the current node, the probability of finding fifth should be reasonably low. Note that four is completely arbitrary - there could and probably should be some tweaks, both in tweaking best move count threshold for more reductions and tweaking how they work - for example making more reductions with best move count linearly. passed STC: https://tests.stockfishchess.org/tests/view/615f614783dd501a05b0aee2 LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 141816 W: 36056 L: 35686 D: 70074 Ptnml(0-2): 499, 15131, 39273, 15511, 494 passed LTC: https://tests.stockfishchess.org/tests/view/615fdff683dd501a05b0af35 LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 68536 W: 17221 L: 16891 D: 34424 Ptnml(0-2): 38, 6573, 20725, 6885, 47 closes https://github.com/official-stockfish/Stockfish/pull/3736 Bench: 6131513 --- src/search.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ac6b8608848..73fc8a55fef 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -591,13 +591,13 @@ namespace { bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture, singularQuietLMR, noLMRExtension; Piece movedPiece; - int moveCount, captureCount, quietCount; + int moveCount, captureCount, quietCount, bestMoveCount; // Step 1. Initialize node ss->inCheck = pos.checkers(); priorCapture = pos.captured_piece(); Color us = pos.side_to_move(); - moveCount = captureCount = quietCount = ss->moveCount = 0; + moveCount = bestMoveCount = captureCount = quietCount = ss->moveCount = 0; bestValue = -VALUE_INFINITE; maxValue = VALUE_INFINITE; @@ -1186,8 +1186,9 @@ namespace { { Depth r = reduction(improving, depth, moveCount, rangeReduction > 2); - // Decrease reduction if on the PV (~1 Elo) - if (PvNode) + // Decrease reduction if on the PV (~2 Elo) + if ( PvNode + && bestMoveCount <= 3) r--; // Decrease reduction if the ttHit running average is large (~0 Elo) @@ -1340,7 +1341,10 @@ namespace { update_pv(ss->pv, move, (ss+1)->pv); if (PvNode && value < beta) // Update alpha! Always alpha < beta + { alpha = value; + bestMoveCount++; + } else { assert(value >= beta); // Fail high From 673841301b0cc6ed78c4db3e6ec2a0b9a010c8cb Mon Sep 17 00:00:00 2001 From: Joseph Ellis Date: Wed, 13 Oct 2021 11:10:50 -0500 Subject: [PATCH 0681/1766] Simplify multi-cut condition Now that the multi-cut condition is safer, we can avoid the cost of the sub-search. STC: https://tests.stockfishchess.org/tests/view/6165fd9283dd501a05b0b2fe LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 18648 W: 4745 L: 4600 D: 9303 Ptnml(0-2): 47, 2111, 4887, 2208, 71 LTC: https://tests.stockfishchess.org/tests/view/616629ea83dd501a05b0b320 LLR: 2.96 (-2.94,2.94) <-2.50,0.50> Total: 41704 W: 10407 L: 10302 D: 20995 Ptnml(0-2): 35, 4425, 11823, 4538, 31 closes https://github.com/official-stockfish/Stockfish/pull/3738 Bench: 5905086 --- src/search.cpp | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 73fc8a55fef..28049e87dcd 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1123,18 +1123,9 @@ namespace { else if (singularBeta >= beta) return singularBeta; - // If the eval of ttMove is greater than beta we try also if there is another - // move that pushes it over beta, if so the position also has probably multiple - // moves giving fail highs. We will then reduce the ttMove (negative extension). + // If the eval of ttMove is greater than beta, we reduce it (negative extension) else if (ttValue >= beta) - { - ss->excludedMove = move; - value = search(pos, ss, beta - 1, beta, (depth + 3) / 2, cutNode); - ss->excludedMove = MOVE_NONE; - - if (value >= beta) - extension = -2; - } + extension = -2; } // Capture extensions for PvNodes and cutNodes From 0bddd942b4d096ff31132a4c3e7aef016d0f2d41 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Thu, 14 Oct 2021 00:44:46 -0300 Subject: [PATCH 0682/1766] Simplify ttHitAverage away Simplify ttHitAverage away, which was introduced in the following commit: [here](https://github.com/BM123499/Stockfish/commit/fe124896b241b4791454fd151da10101ad48f6d7) A few tweaks with Elo gaining bounds have been tried to keep the code, but they all failed: https://tests.stockfishchess.org/tests/view/61656f7683dd501a05b0b292 https://tests.stockfishchess.org/tests/view/6165c0ca83dd501a05b0b2ca https://tests.stockfishchess.org/tests/view/6165bf9683dd501a05b0b2c8 https://tests.stockfishchess.org/tests/view/6165719483dd501a05b0b29b https://tests.stockfishchess.org/tests/view/6166c7fd83dd501a05b0b353 https://tests.stockfishchess.org/tests/view/6166c63b83dd501a05b0b350 STC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 58504 W: 14781 L: 14694 D: 29029 Ptnml(0-2): 175, 6718, 15426, 6711, 222 https://tests.stockfishchess.org/tests/view/6165112c83dd501a05b0b257 LTC: LLR: 2.96 (-2.94,2.94) <-2.50,0.50> Total: 33480 W: 8448 L: 8332 D: 16700 Ptnml(0-2): 21, 3569, 9447, 3679, 24 https://tests.stockfishchess.org/tests/view/61656fcf83dd501a05b0b294 change https://github.com/official-stockfish/Stockfish/pull/3739 bench: 4540339 --- src/search.cpp | 8 -------- src/thread.h | 1 - 2 files changed, 9 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 28049e87dcd..bdd139cec4d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -332,7 +332,6 @@ void Thread::search() { multiPV = std::min(multiPV, rootMoves.size()); - ttHitAverage.set(50, 100); // initialize the running average at 50% doubleExtensionAverage[WHITE].set(0, 100); // initialize the running average at 0% doubleExtensionAverage[BLACK].set(0, 100); // initialize the running average at 0% @@ -670,9 +669,6 @@ namespace { && is_ok((ss-1)->currentMove)) thisThread->lowPlyHistory[ss->ply - 1][from_to((ss-1)->currentMove)] << stat_bonus(depth - 5); - // running average of ttHit - thisThread->ttHitAverage.update(ss->ttHit); - // At non-PV nodes we check for an early TT cutoff if ( !PvNode && ss->ttHit @@ -1182,10 +1178,6 @@ namespace { && bestMoveCount <= 3) r--; - // Decrease reduction if the ttHit running average is large (~0 Elo) - if (thisThread->ttHitAverage.is_greater(537, 1024)) - r--; - // Decrease reduction if position is or has been on the PV // and node is not likely to fail low. (~3 Elo) if ( ss->ttPv diff --git a/src/thread.h b/src/thread.h index 2475d2ec254..e04d303a271 100644 --- a/src/thread.h +++ b/src/thread.h @@ -60,7 +60,6 @@ class Thread { Pawns::Table pawnsTable; Material::Table materialTable; size_t pvIdx, pvLast; - RunningAverage ttHitAverage; RunningAverage doubleExtensionAverage[COLOR_NB]; uint64_t nodesLastExplosive; uint64_t nodesLastNormal; From 580698e5e57f40dcba52b92a7f0c7a0e9ab09437 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 6 Oct 2021 19:16:02 +0200 Subject: [PATCH 0683/1766] Compute ttCapture earlier Compute ttCapture earlier, and reuse. passed STC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 74128 W: 18640 L: 18578 D: 36910 Ptnml(0-2): 224, 7970, 20649, 7962, 259 https://tests.stockfishchess.org/tests/view/615dd9fa1a32f4036ac7fc4d closes https://github.com/official-stockfish/Stockfish/pull/3734 No functional change --- src/search.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index bdd139cec4d..1aaf53d84d1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -658,6 +658,7 @@ namespace { ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] : ss->ttHit ? tte->move() : MOVE_NONE; + ttCapture = ttMove && pos.capture_or_promotion(ttMove); if (!excludedMove) ss->ttPv = PvNode || (ss->ttHit && tte->is_pv()); @@ -683,7 +684,7 @@ namespace { if (ttValue >= beta) { // Bonus for a quiet ttMove that fails high - if (!pos.capture_or_promotion(ttMove)) + if (!ttCapture) update_quiet_stats(pos, ss, ttMove, stat_bonus(depth), depth); // Extra penalty for early quiet moves of the previous ply @@ -691,7 +692,7 @@ namespace { 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)) + else if (!ttCapture) { int penalty = -stat_bonus(depth); thisThread->mainHistory[us][from_to(ttMove)] << penalty; @@ -948,7 +949,6 @@ namespace { moves_loop: // When in check, search starts here - ttCapture = ttMove && pos.capture_or_promotion(ttMove); int rangeReduction = 0; // Step 11. A small Probcut idea, when we are in check From 4231d99ab408674115623f42f7ff89f3f189ca23 Mon Sep 17 00:00:00 2001 From: Stefano Cardanobile Date: Thu, 14 Oct 2021 22:26:42 +0200 Subject: [PATCH 0684/1766] Smooth improving Smooth dependency on improvement margin in null move search. STC LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 17384 W: 4468 L: 4272 D: 8644 Ptnml(0-2): 42, 1919, 4592, 2079, 60 https://tests.stockfishchess.org/tests/view/61689b8a1e5f6627cc1c0fdc LTC LLR: 2.94 (-2.94,2.94) <0.50,3.50> Total: 45648 W: 11525 L: 11243 D: 22880 Ptnml(0-2): 26, 4731, 13036, 4997, 34 https://tests.stockfishchess.org/tests/view/6168a12c1e5f6627cc1c0fe3 It would be interesting to test if the other pruning/reduction heuristics in master which are using the improving variable (ie the sign of improvement) could benefit from a smooth function of the improvement value (or maybe a Relu of the improvement value). closes https://github.com/official-stockfish/Stockfish/pull/3740 Bench: 4916775 --- src/search.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 1aaf53d84d1..9f005587aa4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -590,7 +590,7 @@ namespace { bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture, singularQuietLMR, noLMRExtension; Piece movedPiece; - int moveCount, captureCount, quietCount, bestMoveCount; + int moveCount, captureCount, quietCount, bestMoveCount, improvement; // Step 1. Initialize node ss->inCheck = pos.checkers(); @@ -766,6 +766,7 @@ namespace { // Skip early pruning when in check ss->staticEval = eval = VALUE_NONE; improving = false; + improvement = 0; goto moves_loop; } else if (ss->ttHit) @@ -804,13 +805,15 @@ namespace { thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus; } - // Set up improving flag that is used in various pruning heuristics - // We define position as improving if static evaluation of position is better - // Than the previous static evaluation at our turn - // In case of us being in check at our previous move we look at move prior to it - improving = (ss-2)->staticEval == VALUE_NONE - ? ss->staticEval > (ss-4)->staticEval || (ss-4)->staticEval == VALUE_NONE - : ss->staticEval > (ss-2)->staticEval; + // Set up the improvement variable, which is the difference between the current + // static evaluation and the previous static evaluation at our turn (if we were + // in check at our previous move we look at the move prior to it). The improvement + // margin and the improving flag are used in various pruning heuristics. + improvement = (ss-2)->staticEval != VALUE_NONE ? ss->staticEval - (ss-2)->staticEval + : (ss-4)->staticEval != VALUE_NONE ? ss->staticEval - (ss-4)->staticEval + : 200; + + improving = improvement > 0; // Step 7. Futility pruning: child node (~50 Elo). // The depth condition is important for mate finding. @@ -826,7 +829,7 @@ namespace { && (ss-1)->statScore < 23767 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 20 * depth - 22 * improving + 168 * ss->ttPv + 177 + && ss->staticEval >= beta - 20 * depth - improvement / 15 + 168 * ss->ttPv + 177 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) From 6847be2c752b3f41906be87af7f3cde4e31be5d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sun, 17 Oct 2021 11:56:35 +0200 Subject: [PATCH 0685/1766] Allow some LMR double extensions Allow some LMR double extensions for the second and third sons of each node. STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 170320 W: 42608 L: 42187 D: 85525 Ptnml(0-2): 516, 19635, 44422, 20086, 501 https://tests.stockfishchess.org/tests/view/616a9e3899b580bf37797cf4 LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 74400 W: 18783 L: 18423 D: 37194 Ptnml(0-2): 46, 7812, 21129, 8162, 51 https://tests.stockfishchess.org/tests/view/616b378499b580bf37797d61 closes https://github.com/official-stockfish/Stockfish/pull/3742 Bench: 4877152 --- src/search.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 9f005587aa4..39bb1fdb948 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1219,13 +1219,13 @@ namespace { // In general we want to cap the LMR depth search at newDepth. But if reductions // are really negative and movecount is low, we allow this move to be searched - // deeper than the first move (this may lead to hidden double extensions if - // newDepth got its own extension before). - int deeper = r >= -1 ? 0 - : noLMRExtension ? 0 - : moveCount <= 5 ? 1 - : (depth > 6 && PvNode) ? 1 - : 0; + // deeper than the first move (this may lead to hidden double extensions). + int deeper = r >= -1 ? 0 + : noLMRExtension ? 0 + : moveCount <= 3 && r <= -3 ? 2 + : moveCount <= 5 ? 1 + : PvNode && depth > 6 ? 1 + : 0; Depth d = std::clamp(newDepth - r, 1, newDepth + deeper); From 8a74c089286913f24a641aa37532006088d0f438 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sun, 17 Oct 2021 13:06:33 +0200 Subject: [PATCH 0686/1766] Remove noLMRExtension flag This simplification patch removes the noLMRExtension flag. It was introduced in June (see following link for that commit), but does not seem to be necessary anymore. Link: https://github.com/official-stockfish/Stockfish/commit/e1f181ee643dcaa92c606b74b3abd23dede136cd STC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 21200 W: 5369 L: 5228 D: 10603 Ptnml(0-2): 67, 2355, 5616, 2494, 68 https://tests.stockfishchess.org/tests/view/616c03d299b580bf37797dcb LTC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 37536 W: 9387 L: 9278 D: 18871 Ptnml(0-2): 23, 3988, 10643, 4085, 29 https://tests.stockfishchess.org/tests/view/616c10f499b580bf37797ddd closes https://github.com/official-stockfish/Stockfish/pull/3743 Bench: 4792969 --- src/search.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 39bb1fdb948..9a2bd415659 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -588,7 +588,7 @@ namespace { Value bestValue, value, ttValue, eval, maxValue, probCutBeta; bool givesCheck, improving, didLMR, priorCapture; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, - ttCapture, singularQuietLMR, noLMRExtension; + ttCapture, singularQuietLMR; Piece movedPiece; int moveCount, captureCount, quietCount, bestMoveCount, improvement; @@ -984,7 +984,7 @@ namespace { ss->ply); value = bestValue; - singularQuietLMR = moveCountPruning = noLMRExtension = false; + singularQuietLMR = moveCountPruning = false; // Indicate PvNodes that will probably fail low if the node was searched // at a depth equal or greater than the current depth, and the result of this search was a fail low. @@ -1108,10 +1108,7 @@ namespace { if ( !PvNode && value < singularBeta - 75 && ss->doubleExtensions <= 6) - { extension = 2; - noLMRExtension = true; - } } // Multi-cut pruning @@ -1221,7 +1218,6 @@ namespace { // are really negative and movecount is low, we allow this move to be searched // deeper than the first move (this may lead to hidden double extensions). int deeper = r >= -1 ? 0 - : noLMRExtension ? 0 : moveCount <= 3 && r <= -3 ? 2 : moveCount <= 5 ? 1 : PvNode && depth > 6 ? 1 From f7494961de13c3341a1ca2d05ea5f3d28fa35d31 Mon Sep 17 00:00:00 2001 From: Stefano Cardanobile Date: Sun, 17 Oct 2021 19:01:45 +0200 Subject: [PATCH 0687/1766] Reformat Eval::evaluate() Non functional simplification: the goal of this patch is to make the style in the evaluate() function similar to the rest of the code. passed STC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 95608 W: 24058 L: 24026 D: 47524 Ptnml(0-2): 292, 10379, 26396, 10479, 258 https://tests.stockfishchess.org/tests/view/616c64fd99b580bf37797e4f closes https://github.com/official-stockfish/Stockfish/pull/3744 Non-functional change --- src/evaluate.cpp | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 2195f9136fd..2f1d5067d01 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1081,33 +1081,22 @@ Value Eval::evaluate(const Position& pos) { Value v; - if (!useNNUE) - v = Evaluation(pos).value(); + // Deciding between classical and NNUE eval: for high PSQ imbalance we use classical, + // but we switch to NNUE during long shuffling or with high material on the board. + + if ( !useNNUE + || abs(eg_value(pos.psq_score())) * 5 > (850 + pos.non_pawn_material() / 64) * (5 + pos.rule50_count())) + v = Evaluation(pos).value(); // classical else { - // Scale and shift NNUE for compatibility with search and classical evaluation - auto adjusted_NNUE = [&]() - { - int scale = 883 - + 32 * pos.count() - + 32 * pos.non_pawn_material() / 1024; - - Value nnue = NNUE::evaluate(pos, true) * scale / 1024; - - if (pos.is_chess960()) - nnue += fix_FRC(pos); - - return nnue; - }; + int scale = 883 + + 32 * pos.count() + + 32 * pos.non_pawn_material() / 1024; - // If there is PSQ imbalance we use the classical eval, but we switch to - // NNUE eval faster when shuffling or if the material on the board is high. - int r50 = pos.rule50_count(); - Value psq = Value(abs(eg_value(pos.psq_score()))); - bool classical = psq * 5 > (850 + pos.non_pawn_material() / 64) * (5 + r50); + v = NNUE::evaluate(pos, true) * scale / 1024; // NNUE - v = classical ? Evaluation(pos).value() // classical - : adjusted_NNUE(); // NNUE + if (pos.is_chess960()) + v += fix_FRC(pos); } // Damp down the evaluation linearly when shuffling From 67d06164833857d3497010e952fd0d2c5f00c095 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Mon, 18 Oct 2021 09:03:12 -0300 Subject: [PATCH 0688/1766] Simplify probCutCount away Simplify away the limitation in number of moves in probCut. STC: LLR: 2.96 (-2.94,2.94) <-2.50,0.50> Total: 286768 W: 71888 L: 72133 D: 142747 Ptnml(0-2): 983, 33084, 75471, 32887, 959 https://tests.stockfishchess.org/tests/view/616c9b9b90e1312a3cd0ef0a LTC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 69312 W: 17243 L: 17176 D: 34893 Ptnml(0-2): 42, 7452, 19614, 7493, 55 https://tests.stockfishchess.org/tests/view/616cebbf4f95b438f7a85f93 closes https://github.com/official-stockfish/Stockfish/pull/3745 bench: 5005810 --- src/search.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 9a2bd415659..55a689567b4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -893,19 +893,16 @@ namespace { assert(probCutBeta < VALUE_INFINITE); MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, &captureHistory); - int probCutCount = 0; bool ttPv = ss->ttPv; ss->ttPv = false; - while ( (move = mp.next_move()) != MOVE_NONE - && probCutCount < 2 + 2 * cutNode) + while ((move = mp.next_move()) != MOVE_NONE) 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[ss->inCheck] From b37054c310876850f6ff65b19f6cdb5f941c57dc Mon Sep 17 00:00:00 2001 From: bmc4 Date: Tue, 19 Oct 2021 09:23:30 -0300 Subject: [PATCH 0689/1766] Simplify evaluate condition on search Remove condition for MOVE_NULL on search. STC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 47544 W: 11968 L: 11864 D: 23712 Ptnml(0-2): 150, 5535, 12318, 5599, 170 https://tests.stockfishchess.org/tests/view/616e37143799eb91f1f071ee LTC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 67472 W: 16938 L: 16870 D: 33664 Ptnml(0-2): 49, 7119, 19331, 7189, 48 https://tests.stockfishchess.org/tests/view/616e3fab3799eb91f1f071f1 closes https://github.com/official-stockfish/Stockfish/pull/3746 bench: 5255771 --- src/search.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 55a689567b4..e7e33edef8f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -787,11 +787,7 @@ namespace { } else { - // In case of null move search use previous static eval with a different sign - if ((ss-1)->currentMove != MOVE_NULL) - ss->staticEval = eval = evaluate(pos); - else - ss->staticEval = eval = -(ss-1)->staticEval; + ss->staticEval = eval = evaluate(pos); // Save static evaluation into transposition table if(!excludedMove) From 4af1ae82c6b017a3d0cac04ad1ad916a7e26a529 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Tue, 19 Oct 2021 15:44:08 -0300 Subject: [PATCH 0690/1766] Adjust TTdepth acceptance on early cutoff STC: LLR: 2.94 (-2.94,2.94) <-0.50,2.50> Total: 63784 W: 16185 L: 15917 D: 31682 Ptnml(0-2): 231, 7309, 16531, 7603, 218 https://tests.stockfishchess.org/tests/view/616ed03a942d40685e3237c0 LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 12728 W: 3268 L: 3072 D: 6388 Ptnml(0-2): 8, 1298, 3563, 1480, 15 https://tests.stockfishchess.org/tests/view/616ef156942d40685e32380a closes https://github.com/official-stockfish/Stockfish/pull/3748 bench: 7050445 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index e7e33edef8f..b30af89f131 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -673,7 +673,7 @@ namespace { // At non-PV nodes we check for an early TT cutoff if ( !PvNode && ss->ttHit - && tte->depth() >= depth + && tte->depth() > depth && ttValue != VALUE_NONE // Possible in case of TT access race && (ttValue >= beta ? (tte->bound() & BOUND_LOWER) : (tte->bound() & BOUND_UPPER))) From 42a895d9c988e6d2db2cbae43a4992c853efee6e Mon Sep 17 00:00:00 2001 From: bmc4 Date: Wed, 20 Oct 2021 10:37:20 -0300 Subject: [PATCH 0691/1766] Simplify null move search condition Remove `ss->ttPv` condition on null move search condition STC: LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 80832 W: 20276 L: 20221 D: 40335 Ptnml(0-2): 267, 9335, 21168, 9368, 278 https://tests.stockfishchess.org/tests/view/616ed4a0942d40685e3237c6 LTC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 54184 W: 13464 L: 13377 D: 27343 Ptnml(0-2): 37, 5758, 15435, 5805, 57 https://tests.stockfishchess.org/tests/view/616ef71f40f619782fd4f72d closes https://github.com/official-stockfish/Stockfish/pull/3750 bench: 6201607 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index b30af89f131..65e496b88ab 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -825,7 +825,7 @@ namespace { && (ss-1)->statScore < 23767 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 20 * depth - improvement / 15 + 168 * ss->ttPv + 177 + && ss->staticEval >= beta - 20 * depth - improvement / 15 + 177 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) From 8a8640a761f266979c7130a49ebbc9ed4c680102 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Thu, 21 Oct 2021 09:39:28 +0200 Subject: [PATCH 0692/1766] Double extend more often via LMR Allow for first three moves always a two plies deeper LMR search. STC: LLR: 2.96 (-2.94,2.94) <-2.50,0.50> Total: 206096 W: 51966 L: 52093 D: 102037 Ptnml(0-2): 664, 23817, 54293, 23530, 744 https://tests.stockfishchess.org/tests/view/616f197d40f619782fd4f75a LTC: LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 62384 W: 15567 L: 15492 D: 31325 Ptnml(0-2): 40, 6633, 17777, 6696, 46 https://tests.stockfishchess.org/tests/view/616ffa1b4f0b65a0e231e682 closes https://github.com/official-stockfish/Stockfish/pull/3752 Bench: 6154836 --- src/search.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 65e496b88ab..f511cce656e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -790,8 +790,8 @@ namespace { ss->staticEval = eval = evaluate(pos); // Save static evaluation into transposition table - if(!excludedMove) - tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); + if (!excludedMove) + tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); } // Use static evaluation difference to improve quiet move ordering @@ -1210,11 +1210,11 @@ namespace { // In general we want to cap the LMR depth search at newDepth. But if reductions // are really negative and movecount is low, we allow this move to be searched // deeper than the first move (this may lead to hidden double extensions). - int deeper = r >= -1 ? 0 - : moveCount <= 3 && r <= -3 ? 2 - : moveCount <= 5 ? 1 - : PvNode && depth > 6 ? 1 - : 0; + int deeper = r >= -1 ? 0 + : moveCount <= 3 ? 2 + : moveCount <= 5 ? 1 + : PvNode && depth > 6 ? 1 + : 0; Depth d = std::clamp(newDepth - r, 1, newDepth + deeper); From 644f6d47909f3cf2bd3e70485db792789ccad89e Mon Sep 17 00:00:00 2001 From: mstembera Date: Tue, 19 Oct 2021 10:49:02 -0700 Subject: [PATCH 0693/1766] Simplify away ValueListInserter plus minor cleanups STC: https://tests.stockfishchess.org/tests/view/616f059b40f619782fd4f73f LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 84992 W: 21244 L: 21197 D: 42551 Ptnml(0-2): 279, 9005, 23868, 9078, 266 closes https://github.com/official-stockfish/Stockfish/pull/3749 No functional change --- src/misc.h | 17 ----------------- src/nnue/features/half_ka_v2_hm.cpp | 18 ++++++++---------- src/nnue/features/half_ka_v2_hm.h | 22 ++++++++++++---------- src/nnue/nnue_feature_transformer.h | 9 ++++----- 4 files changed, 24 insertions(+), 42 deletions(-) diff --git a/src/misc.h b/src/misc.h index 7a3369e8ed5..718e5558178 100644 --- a/src/misc.h +++ b/src/misc.h @@ -111,22 +111,6 @@ class RunningAverage { int64_t average; }; - -template -class ValueListInserter { -public: - ValueListInserter(T* v, std::size_t& s) : - values(v), - size(&s) - { - } - - void push_back(const T& value) { values[(*size)++] = value; } -private: - T* values; - std::size_t* size; -}; - template class ValueList { @@ -140,7 +124,6 @@ class ValueList { const T& operator[](std::size_t index) const { return values_[index]; } const T* begin() const { return values_; } const T* end() const { return values_ + size_; } - operator ValueListInserter() { return ValueListInserter(values_, size_); } void swap(ValueList& other) { const std::size_t maxSize = std::max(size_, other.size_); diff --git a/src/nnue/features/half_ka_v2_hm.cpp b/src/nnue/features/half_ka_v2_hm.cpp index 098a6d60e4a..6face2172a4 100644 --- a/src/nnue/features/half_ka_v2_hm.cpp +++ b/src/nnue/features/half_ka_v2_hm.cpp @@ -39,7 +39,7 @@ namespace Stockfish::Eval::NNUE::Features { void HalfKAv2_hm::append_active_indices( const Position& pos, Color perspective, - ValueListInserter active + IndexList& active ) { Square ksq = pos.square(perspective); Bitboard bb = pos.pieces(); @@ -55,22 +55,20 @@ namespace Stockfish::Eval::NNUE::Features { void HalfKAv2_hm::append_changed_indices( Square ksq, - StateInfo* st, + const DirtyPiece& dp, Color perspective, - ValueListInserter removed, - ValueListInserter added + IndexList& removed, + IndexList& added ) { - const auto& dp = st->dirtyPiece; for (int i = 0; i < dp.dirty_num; ++i) { - Piece pc = dp.piece[i]; if (dp.from[i] != SQ_NONE) - removed.push_back(make_index(perspective, dp.from[i], pc, ksq)); + removed.push_back(make_index(perspective, dp.from[i], dp.piece[i], ksq)); if (dp.to[i] != SQ_NONE) - added.push_back(make_index(perspective, dp.to[i], pc, ksq)); + added.push_back(make_index(perspective, dp.to[i], dp.piece[i], ksq)); } } - int HalfKAv2_hm::update_cost(StateInfo* st) { + int HalfKAv2_hm::update_cost(const StateInfo* st) { return st->dirtyPiece.dirty_num; } @@ -78,7 +76,7 @@ namespace Stockfish::Eval::NNUE::Features { return pos.count(); } - bool HalfKAv2_hm::requires_refresh(StateInfo* st, Color perspective) { + bool HalfKAv2_hm::requires_refresh(const StateInfo* st, Color perspective) { return st->dirtyPiece.piece[0] == make_piece(perspective, KING); } diff --git a/src/nnue/features/half_ka_v2_hm.h b/src/nnue/features/half_ka_v2_hm.h index 2c1144f64fb..c7b1a68df71 100644 --- a/src/nnue/features/half_ka_v2_hm.h +++ b/src/nnue/features/half_ka_v2_hm.h @@ -50,7 +50,7 @@ namespace Stockfish::Eval::NNUE::Features { PS_W_QUEEN = 8 * SQUARE_NB, PS_B_QUEEN = 9 * SQUARE_NB, PS_KING = 10 * SQUARE_NB, - PS_NB = 11 * SQUARE_NB + PS_NB = 11 * SQUARE_NB }; static constexpr IndexType PieceSquareIndex[COLOR_NB][PIECE_NB] = { @@ -85,36 +85,38 @@ namespace Stockfish::Eval::NNUE::Features { -1, -1, -1, -1, 23, 22, 21, 20, -1, -1, -1, -1, 19, 18, 17, 16, -1, -1, -1, -1, 15, 14, 13, 12, - -1, -1, -1, -1, 11, 10, 9, 8, - -1, -1, -1, -1, 7, 6, 5, 4, - -1, -1, -1, -1, 3, 2, 1, 0 + -1, -1, -1, -1, 11, 10, 9, 8, + -1, -1, -1, -1, 7, 6, 5, 4, + -1, -1, -1, -1, 3, 2, 1, 0 }; // Maximum number of simultaneously active features. static constexpr IndexType MaxActiveDimensions = 32; + using IndexList = ValueList; // Get a list of indices for active features static void append_active_indices( const Position& pos, Color perspective, - ValueListInserter active); + IndexList& active); // Get a list of indices for recently changed features static void append_changed_indices( Square ksq, - StateInfo* st, + const DirtyPiece& dp, Color perspective, - ValueListInserter removed, - ValueListInserter added); + IndexList& removed, + IndexList& added + ); // Returns the cost of updating one perspective, the most costly one. // Assumes no refresh needed. - static int update_cost(StateInfo* st); + static int update_cost(const StateInfo* st); static int refresh_cost(const Position& pos); // Returns whether the change stored in this StateInfo means that // a full accumulator refresh is required. - static bool requires_refresh(StateInfo* st, Color perspective); + static bool requires_refresh(const StateInfo* st, Color perspective); }; } // namespace Stockfish::Eval::NNUE::Features diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 59a965ac769..0297b3233a1 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -370,7 +370,6 @@ namespace Stockfish::Eval::NNUE { // That might depend on the feature set and generally relies on the // feature set's update cost calculation to be correct and never // allow updates with more added/removed features than MaxActiveDimensions. - using IndexList = ValueList; #ifdef VECTOR // Gcc-10.2 unnecessarily spills AVX2 registers if this array @@ -404,12 +403,12 @@ namespace Stockfish::Eval::NNUE { // Gather all features to be updated. const Square ksq = pos.square(perspective); - IndexList removed[2], added[2]; + FeatureSet::IndexList removed[2], added[2]; FeatureSet::append_changed_indices( - ksq, next, perspective, removed[0], added[0]); + ksq, next->dirtyPiece, perspective, removed[0], added[0]); for (StateInfo *st2 = pos.state(); st2 != next; st2 = st2->previous) FeatureSet::append_changed_indices( - ksq, st2, perspective, removed[1], added[1]); + ksq, st2->dirtyPiece, perspective, removed[1], added[1]); // Mark the accumulators as computed. next->accumulator.computed[perspective] = true; @@ -534,7 +533,7 @@ namespace Stockfish::Eval::NNUE { // Refresh the accumulator auto& accumulator = pos.state()->accumulator; accumulator.computed[perspective] = true; - IndexList active; + FeatureSet::IndexList active; FeatureSet::append_active_indices(pos, perspective, active); #ifdef VECTOR From 2214fcecf7ae5d1d4165596bcd238b6e6bc909c1 Mon Sep 17 00:00:00 2001 From: Stefano Cardanobile Date: Tue, 19 Oct 2021 22:03:26 +0200 Subject: [PATCH 0694/1766] Rewrite NNUE evaluation adjustments Make the eval code in the evaluate_nnue.cpp more similar to the rest of the codebase: * remove multiple variable assignment * make if conditions explicit and indent on multiple lines passed STC LLR: 2.93 (-2.94,2.94) <-2.50,0.50> Total: 59032 W: 14834 L: 14751 D: 29447 Ptnml(0-2): 176, 6310, 16459, 6397, 174 https://tests.stockfishchess.org/tests/view/616f250540f619782fd4f76d closes https://github.com/official-stockfish/Stockfish/pull/3753 No functional change --- src/nnue/evaluate_nnue.cpp | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 7f5925a7c68..6e40deab9ac 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -143,6 +143,7 @@ namespace Stockfish::Eval::NNUE { // overaligning stack variables with alignas() doesn't work correctly. constexpr uint64_t alignment = CacheLineSize; + int delta = 7; #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) TransformedFeatureType transformedFeaturesUnaligned[ @@ -162,20 +163,14 @@ namespace Stockfish::Eval::NNUE { const std::size_t bucket = (pos.count() - 1) / 4; const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket); - const auto output = network[bucket]->propagate(transformedFeatures, buffer); + const auto positional = network[bucket]->propagate(transformedFeatures, buffer)[0]; - int materialist = psqt; - int positional = output[0]; - - int delta_npm = abs(pos.non_pawn_material(WHITE) - pos.non_pawn_material(BLACK)); - int entertainment = (adjusted && delta_npm <= RookValueMg - BishopValueMg ? 7 : 0); - - int A = 128 - entertainment; - int B = 128 + entertainment; - - int sum = (A * materialist + B * positional) / 128; - - return static_cast( sum / OutputScale ); + // Give more value to positional evaluation when material is balanced + if ( adjusted + && abs(pos.non_pawn_material(WHITE) - pos.non_pawn_material(BLACK)) <= RookValueMg - BishopValueMg) + return static_cast(((128 - delta) * psqt + (128 + delta) * positional) / 128 / OutputScale); + else + return static_cast((psqt + positional) / OutputScale); } struct NnueEvalTrace { From 927a84d310fb41222518ea80d398933a0ba3e5b7 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Fri, 22 Oct 2021 10:35:42 -0300 Subject: [PATCH 0695/1766] Increase TTdepth acceptance some Threads Increase TTdepth acceptance only on half of the Threads STC: LLR: 2.96 (-2.94,2.94) <-0.50,2.50> Total: 19272 W: 4956 L: 4766 D: 9550 Ptnml(0-2): 25, 1989, 5423, 2169, 30 https://tests.stockfishchess.org/tests/view/6172be6238cb9784038af9a7 LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 23688 W: 6111 L: 5897 D: 11680 Ptnml(0-2): 2, 2275, 7081, 2479, 7 https://tests.stockfishchess.org/tests/view/6172e32938cb9784038af9c7 closes https://github.com/official-stockfish/Stockfish/pull/3754 No functional change in the single-threaded case --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index f511cce656e..71634c56a85 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -673,7 +673,7 @@ namespace { // At non-PV nodes we check for an early TT cutoff if ( !PvNode && ss->ttHit - && tte->depth() > depth + && tte->depth() > depth - (thisThread->id() % 2 == 1) && ttValue != VALUE_NONE // Possible in case of TT access race && (ttValue >= beta ? (tte->bound() & BOUND_LOWER) : (tte->bound() & BOUND_UPPER))) From fc8213c7df7422c0c321db5fb066cbd08d3bf3f8 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Fri, 22 Oct 2021 03:04:28 +0300 Subject: [PATCH 0696/1766] Tuning of a Null Move Parameter STC: LLR: 2.99 (-2.94,2.94) <-0.50,2.50> Total: 78744 W: 19956 L: 19664 D: 39124 Ptnml(0-2): 259, 9005, 20573, 9255, 280 https://tests.stockfishchess.org/tests/view/6172017a38cb9784038af947 LTC: LLR: 2.95 (-2.94,2.94) <0.50,3.50> Total: 68528 W: 17309 L: 16964 D: 34255 Ptnml(0-2): 41, 7194, 19455, 7527, 47 https://tests.stockfishchess.org/tests/view/6172994d38cb9784038af983 closes https://github.com/official-stockfish/Stockfish/pull/3756 bench: 6689428 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 71634c56a85..0aa590f4024 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -825,7 +825,7 @@ namespace { && (ss-1)->statScore < 23767 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 20 * depth - improvement / 15 + 177 + && ss->staticEval >= beta - 20 * depth - improvement / 15 + 204 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) From 1163d972a9a1e480d9130c5fabbf869cdb7f7ecb Mon Sep 17 00:00:00 2001 From: bmc4 Date: Sat, 23 Oct 2021 09:22:41 -0300 Subject: [PATCH 0697/1766] Simplify LMR multiThread condition STC (8 threads): LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 110584 W: 27818 L: 27807 D: 54959 Ptnml(0-2): 156, 12089, 30791, 12100, 156 https://tests.stockfishchess.org/tests/view/6172ef436ce927be325583a9 LTC (8 threads): LLR: 2.94 (-2.94,2.94) <-2.50,0.50> Total: 23632 W: 6025 L: 5903 D: 11704 Ptnml(0-2): 5, 2292, 7100, 2414, 5 https://tests.stockfishchess.org/tests/view/6173cf096ce927be32558412 closes https://github.com/official-stockfish/Stockfish/pull/3757 No functional change (in the single-threaded case) Bench: 6689428 --- src/search.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 0aa590f4024..8becdd3f827 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1159,10 +1159,9 @@ namespace { // cases where we extend a son if it has good chances to be "interesting". if ( depth >= 3 && moveCount > 1 + 2 * rootNode - && ( !captureOrPromotion - || (cutNode && (ss-1)->moveCount > 1) - || !ss->ttPv) - && (!PvNode || ss->ply > 1 || thisThread->id() % 4 != 3)) + && ( !ss->ttPv + || !captureOrPromotion + || (cutNode && (ss-1)->moveCount > 1))) { Depth r = reduction(improving, depth, moveCount, rangeReduction > 2); From 8557f35aa5c0f1ea45b2ccba5ebfd0165dbd33da Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Sun, 24 Oct 2021 16:07:00 +0200 Subject: [PATCH 0698/1766] Double extend search even more via LMR Allow now for the first five moves a two plies deeper LMR search. STC: LLR: 2.96 (-2.94,2.94) <-2.50,0.50> Total: 99608 W: 25143 L: 25115 D: 49350 Ptnml(0-2): 291, 11444, 26328, 11428, 313 https://tests.stockfishchess.org/tests/view/61718c9438cb9784038af8d7 LTC: LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 52064 W: 13234 L: 13145 D: 25685 Ptnml(0-2): 35, 5431, 15014, 5514, 38 https://tests.stockfishchess.org/tests/view/6171e13e38cb9784038af928 closes https://github.com/official-stockfish/Stockfish/pull/3760 Bench: 7222293 --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 8becdd3f827..36329f8fe42 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1210,8 +1210,7 @@ namespace { // are really negative and movecount is low, we allow this move to be searched // deeper than the first move (this may lead to hidden double extensions). int deeper = r >= -1 ? 0 - : moveCount <= 3 ? 2 - : moveCount <= 5 ? 1 + : moveCount <= 5 ? 2 : PvNode && depth > 6 ? 1 : 0; From 2c86ae196df8b2a1197e0de1853a6458e404a976 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 23 Oct 2021 15:02:59 +0200 Subject: [PATCH 0699/1766] Adjust ButterflyHistory decay parameter passed STC: LLR: 2.98 (-2.94,2.94) <-0.50,2.50> Total: 26680 W: 6807 L: 6593 D: 13280 Ptnml(0-2): 73, 3007, 6989, 3175, 96 https://tests.stockfishchess.org/tests/view/6174094e6ce927be32558441 passed LTC: LLR: 2.98 (-2.94,2.94) <0.50,3.50> Total: 21104 W: 5403 L: 5185 D: 10516 Ptnml(0-2): 8, 2160, 6001, 2372, 11 https://tests.stockfishchess.org/tests/view/61744927351812fe5f969864 closes https://github.com/official-stockfish/Stockfish/pull/3761 Bench: 6334068 --- src/movepick.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/movepick.h b/src/movepick.h index 2932d9a8a0d..7d78886fb64 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -86,7 +86,7 @@ enum StatsType { NoCaptures, Captures }; /// unsuccessful during the current search, and is used for reduction and move /// ordering decisions. It uses 2 tables (one for each color) indexed by /// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards -typedef Stats ButterflyHistory; +typedef Stats ButterflyHistory; /// At higher depths LowPlyHistory records successful quiet moves near the root /// and quiet moves which are/were in the PV (ttPv). LowPlyHistory is populated during From 385deefd807b512a1c404f5cc4c2045f265a800d Mon Sep 17 00:00:00 2001 From: mstembera Date: Fri, 22 Oct 2021 20:01:05 -0700 Subject: [PATCH 0700/1766] Fix sometimes incorrect key for prefetches STC https://tests.stockfishchess.org/tests/view/61737b4f6ce927be32558401 LLR: 2.95 (-2.94,2.94) <-2.50,0.50> Total: 138712 W: 34914 L: 34942 D: 68856 Ptnml(0-2): 421, 14817, 38894, 14817, 407 Very minor tweak since Position::key() depends on the 50 move rule counter. Comments: https://github.com/mstembera/Stockfish/commit/cddde31eed505cdf0c4fc8ff96b89f6e39c797e1 closes https://github.com/official-stockfish/Stockfish/pull/3759 No functional change --- src/position.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/position.cpp b/src/position.cpp index 0686d245bc7..ae1da017770 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1013,9 +1013,9 @@ void Position::do_null_move(StateInfo& newSt) { } st->key ^= Zobrist::side; + ++st->rule50; prefetch(TT.first_entry(key())); - ++st->rule50; st->pliesFromNull = 0; sideToMove = ~sideToMove; From 7262fd5d14810b7b495b5038e348a448fda1bcc3 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 27 Oct 2021 20:14:41 +0200 Subject: [PATCH 0701/1766] Stockfish 14.1 Official release version of Stockfish 14.1 Bench: 6334068 --- Today, we have the pleasure to announce Stockfish 14.1. As usual, downloads will be freely available at stockfishchess.org/download [1]. With Stockfish 14.1 our users get access to the strongest chess engine available today. In the period leading up to this release, Stockfish convincingly won several chess engine tournaments, including the TCEC 21 superfinal, the TCEC Cup 9, and the Computer Chess Championship for Fischer Random Chess (Chess960). In the latter tournament, Stockfish was undefeated in 599 out of 600 games played. Compared to Stockfish 14, this release introduces a more advanced NNUE architecture and various search improvements. In self play testing, using a book of balanced openings, Stockfish 14.1 wins three times more game pairs than it loses [2]. At this high level, draws are very common, so the Elo difference to Stockfish 14 is about 17 Elo. The NNUE evaluation method, introduced to top level chess with Stockfish 12 about one year ago [3], has now been adopted by several other strong CPU based chess engines. The Stockfish project builds on a thriving community of enthusiasts (thanks everybody!) that contribute their expertise, time, and resources to build a free and open-source chess engine that is robust, widely available, and very strong. We invite our chess fans to join the fishtest testing framework and programmers to contribute to the project [4]. Stay safe and enjoy chess! The Stockfish team [1] https://stockfishchess.org/download/ [2] https://tests.stockfishchess.org/tests/view/6175c320af70c2be1788fa2b [3] https://github.com/official-stockfish/Stockfish/discussions/3628 [4] https://stockfishchess.org/get-involved/ --- src/misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index f9c123374f2..bb3a641bca9 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -67,7 +67,7 @@ namespace { /// Version number. If Version is left empty, then compile date in the format /// DD-MM-YY and show in engine_info. -const string Version = ""; +const string Version = "14.1"; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We From 717d6c5ed503543c66eb7a2c10e8a97c88d99862 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 29 Oct 2021 17:45:09 +0200 Subject: [PATCH 0702/1766] Widen the aspiration window for larger evals passed STC LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 36840 W: 9359 L: 9134 D: 18347 Ptnml(0-2): 111, 4130, 9722, 4337, 120 https://tests.stockfishchess.org/tests/view/617c601301c6d0988731d10a passed LTC LLR: 2.98 (-2.94,2.94) <0.50,3.50> Total: 64824 W: 16377 L: 16043 D: 32404 Ptnml(0-2): 27, 6712, 18618, 7010, 45 https://tests.stockfishchess.org/tests/view/617c720d01c6d0988731d114 closes https://github.com/official-stockfish/Stockfish/pull/3768 Bench: 7683058 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 36329f8fe42..14de2377b14 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -380,7 +380,7 @@ void Thread::search() { if (rootDepth >= 4) { Value prev = rootMoves[pvIdx].previousScore; - delta = Value(17); + delta = Value(17) + int(prev) * prev / 16384; alpha = std::max(prev - delta,-VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE); From a8330d5c3bfeb4c9d5c55083223792e0989bb9c6 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Sun, 31 Oct 2021 12:09:40 +0100 Subject: [PATCH 0703/1766] Do more deeper LMR searches. At expected cut nodes allow at least one ply deeper LMR search for the first seventh moves. STC: LLR: 2.93 (-2.94,2.94) <-0.50,2.50> Total: 42880 W: 10964 L: 10738 D: 21178 Ptnml(0-2): 105, 4565, 11883, 4773, 114 https://tests.stockfishchess.org/tests/view/6179abd7a9b1d8fbcc4ee6f4 LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.50> Total: 66872 W: 16930 L: 16603 D: 33339 Ptnml(0-2): 36, 6509, 20024, 6826, 41 https://tests.stockfishchess.org/tests/view/617a30fb2fbca9ca65972b5e closes https://github.com/official-stockfish/Stockfish/pull/3770 Bench: 6295536 --- src/search.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 14de2377b14..c7c8e782711 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1209,10 +1209,11 @@ namespace { // In general we want to cap the LMR depth search at newDepth. But if reductions // are really negative and movecount is low, we allow this move to be searched // deeper than the first move (this may lead to hidden double extensions). - int deeper = r >= -1 ? 0 - : moveCount <= 5 ? 2 - : PvNode && depth > 6 ? 1 - : 0; + int deeper = r >= -1 ? 0 + : moveCount <= 5 ? 2 + : PvNode && depth > 6 ? 1 + : cutNode && moveCount <= 7 ? 1 + : 0; Depth d = std::clamp(newDepth - r, 1, newDepth + deeper); From 0e89d6e7546d26a19a108d047b489d9ba6f7970c Mon Sep 17 00:00:00 2001 From: Michel Van den Bergh Date: Sun, 31 Oct 2021 19:35:30 +0100 Subject: [PATCH 0704/1766] Do not output to stderr during the build. To help with debugging, the worker sends the output of stderr (suitable truncated) to the action log on the server, in case a build fails. For this to work it is important that there is no spurious output to stderr. closes https://github.com/official-stockfish/Stockfish/pull/3773 No functional change --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index fea597e76cb..5c52661b784 100644 --- a/src/Makefile +++ b/src/Makefile @@ -751,7 +751,7 @@ profile-build: net config-sanity objclean profileclean $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_make) @echo "" @echo "Step 2/4. Running benchmark for pgo-build ..." - $(PGOBENCH) > /dev/null + $(PGOBENCH) 2>&1 | tail -n 4 @echo "" @echo "Step 3/4. Building optimized executable ..." $(MAKE) ARCH=$(ARCH) COMP=$(COMP) objclean From ef4822aa8d5945d490acca674eb1db8c3c38e9d5 Mon Sep 17 00:00:00 2001 From: xefoci7612 Date: Sun, 10 Oct 2021 14:03:51 +0200 Subject: [PATCH 0705/1766] Simplify Skill implementation Currently we handle the UCI_Elo with a double randomization. This seems not necessary and a bit involuted. This patch removes the first randomization and unifies the 2 cases. closes https://github.com/official-stockfish/Stockfish/pull/3769 No functional change. --- AUTHORS | 1 + src/search.cpp | 41 +++++++++++++++++++---------------------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/AUTHORS b/AUTHORS index 56725e98d68..35ccdaf5235 100644 --- a/AUTHORS +++ b/AUTHORS @@ -194,6 +194,7 @@ tttak Unai Corzo (unaiic) Uri Blass (uriblass) Vince Negri (cuddlestmonkey) +xefoci7612 zz4032 diff --git a/src/search.cpp b/src/search.cpp index c7c8e782711..075be835a1a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -112,14 +112,22 @@ namespace { return thisThread->state; } - // Skill structure is used to implement strength limit + // Skill structure is used to implement strength limit. If we have an uci_elo then + // we convert it 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. struct Skill { - explicit Skill(int l) : level(l) {} - bool enabled() const { return level < 20; } - bool time_to_pick(Depth depth) const { return depth == 1 + level; } + Skill(int skill_level, int uci_elo) { + if (uci_elo) + level = std::clamp(std::pow((uci_elo - 1346.6) / 143.4, 1 / 0.806), 0.0, 20.0); + else + level = double(skill_level); + } + bool enabled() const { return level < 20.0; } + bool time_to_pick(Depth depth) const { return depth == 1 + int(level); } Move pick_best(size_t multiPV); - int level; + double level; Move best = MOVE_NONE; }; @@ -243,10 +251,11 @@ void MainThread::search() { Time.availableNodes += Limits.inc[us] - Threads.nodes_searched(); Thread* bestThread = this; + Skill skill = Skill(Options["Skill Level"], Options["UCI_LimitStrength"] ? int(Options["UCI_Elo"]) : 0); if ( int(Options["MultiPV"]) == 1 && !Limits.depth - && !(Skill(Options["Skill Level"]).enabled() || int(Options["UCI_LimitStrength"])) + && !skill.enabled() && rootMoves[0].pv[0] != MOVE_NONE) bestThread = Threads.get_best_thread(); @@ -311,19 +320,7 @@ void Thread::search() { std::fill(&lowPlyHistory[MAX_LPH - 2][0], &lowPlyHistory.back().back() + 1, 0); size_t multiPV = size_t(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()); - double floatLevel = Options["UCI_LimitStrength"] ? - std::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); + Skill skill(Options["Skill Level"], Options["UCI_LimitStrength"] ? int(Options["UCI_Elo"]) : 0); // When playing with strength handicap enable MultiPV search that we will // use behind the scenes to retrieve a set of possible moves. @@ -1780,8 +1777,8 @@ namespace { // RootMoves are already sorted by score in descending order Value topScore = rootMoves[0].score; int delta = std::min(topScore - rootMoves[multiPV - 1].score, PawnValueMg); - int weakness = 120 - 2 * level; int maxScore = -VALUE_INFINITE; + double weakness = 120 - 2 * level; // Choose best move. For each move score we add two terms, both dependent on // weakness. One is deterministic and bigger for weaker levels, and one is @@ -1789,8 +1786,8 @@ namespace { for (size_t i = 0; i < multiPV; ++i) { // This is our magic formula - int push = ( weakness * int(topScore - rootMoves[i].score) - + delta * (rng.rand() % weakness)) / 128; + int push = int(( weakness * int(topScore - rootMoves[i].score) + + delta * (rng.rand() % int(weakness))) / 128); if (rootMoves[i].score + push >= maxScore) { From 5a223afe4cfdc2cbb0092c86ddc20fd260e1ac0d Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 1 Nov 2021 06:28:37 +0100 Subject: [PATCH 0706/1766] Restore development version No functional change --- src/misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index bb3a641bca9..f9c123374f2 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -67,7 +67,7 @@ namespace { /// Version number. If Version is left empty, then compile date in the format /// DD-MM-YY and show in engine_info. -const string Version = "14.1"; +const string Version = ""; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We From 11c6cf720d4cdd882bc0f2c36e25910cf77fb57b Mon Sep 17 00:00:00 2001 From: lonfom169 Date: Wed, 27 Oct 2021 18:08:24 -0300 Subject: [PATCH 0707/1766] More futility pruning Expand maximum allowed eval by 50% in futility pruning, above the VALUE_KNOWN_WIN. STC: LLR: 2.95 (-2.94,2.94) <-0.50,2.50> Total: 128208 W: 32534 L: 32192 D: 63482 Ptnml(0-2): 298, 13484, 36216, 13790, 316 https://tests.stockfishchess.org/tests/view/6179c069a9b1d8fbcc4ee716 LTC: LLR: 2.96 (-2.94,2.94) <0.50,3.50> Total: 89816 W: 22645 L: 22265 D: 44906 Ptnml(0-2): 41, 8404, 27650, 8760, 53 https://tests.stockfishchess.org/tests/view/617ad728f411ea45cc39f895 closes https://github.com/official-stockfish/Stockfish/pull/3767 bench: 6804175 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 075be835a1a..a712ce87bb7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -813,7 +813,7 @@ namespace { if ( !PvNode && depth < 9 && eval - futility_margin(depth, improving) >= beta - && eval < VALUE_KNOWN_WIN) // Do not return unproven wins + && eval < 15000) // 50% larger than VALUE_KNOWN_WIN, but smaller than TB wins. return eval; // Step 8. Null move search with verification search (~40 Elo) From c2b9134c6e7637ea375b4755a6f96dc772c6bb17 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Thu, 4 Nov 2021 18:35:01 +0300 Subject: [PATCH 0708/1766] Do more reductions at Pv nodes with low delta This patch increases reduction for PvNodes that have their delta (difference between beta and alpha) significantly reduced compared to what it was at root. passed STC https://tests.stockfishchess.org/tests/view/617f9063af49befdeee40226 LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 220840 W: 55752 L: 55150 D: 109938 Ptnml(0-2): 583, 24982, 58712, 25536, 607 passed LTC https://tests.stockfishchess.org/tests/view/61815de959e71df00dcc42ed LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 79000 W: 19937 L: 19562 D: 39501 Ptnml(0-2): 36, 8190, 22674, 8563, 37 closes https://github.com/official-stockfish/Stockfish/pull/3774 bench: 6717808 --- src/search.cpp | 6 ++++++ src/thread.h | 1 + 2 files changed, 7 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index a712ce87bb7..46c95ef0979 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -625,6 +625,8 @@ namespace { if (alpha >= beta) return alpha; } + else + thisThread->rootDelta = beta - alpha; assert(0 <= ss->ply && ss->ply < MAX_PLY); @@ -1167,6 +1169,10 @@ namespace { && bestMoveCount <= 3) r--; + // Increases reduction for PvNodes that have small window + if (PvNode && beta - alpha < thisThread->rootDelta / 4) + r++; + // Decrease reduction if position is or has been on the PV // and node is not likely to fail low. (~3 Elo) if ( ss->ttPv diff --git a/src/thread.h b/src/thread.h index e04d303a271..fae866c4f30 100644 --- a/src/thread.h +++ b/src/thread.h @@ -72,6 +72,7 @@ class Thread { StateInfo rootState; Search::RootMoves rootMoves; Depth rootDepth, completedDepth; + Value rootDelta; CounterMoveHistory counterMoves; ButterflyHistory mainHistory; LowPlyHistory lowPlyHistory; From 45e5e65a28ce7e304c279fabf5f8a83cced73013 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 1 Nov 2021 08:40:33 +0100 Subject: [PATCH 0709/1766] do not store qsearch positions in TT as exact. in qsearch don't store positions in TT with the exact flag. passed STC: https://tests.stockfishchess.org/tests/view/617f9a29af49befdeee40231 LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 155568 W: 39003 L: 39022 D: 77543 Ptnml(0-2): 403, 17854, 41305, 17803, 419 passed LTC: https://tests.stockfishchess.org/tests/view/6180d47259e71df00dcc42a5 LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 79640 W: 19993 L: 19910 D: 39737 Ptnml(0-2): 37, 8356, 22957, 8427, 43 closes https://github.com/official-stockfish/Stockfish/pull/3775 Bench: 7531210 --- src/search.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 46c95ef0979..6c894c17ce8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1416,13 +1416,12 @@ namespace { Key posKey; Move ttMove, move, bestMove; Depth ttDepth; - Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha; + Value bestValue, value, ttValue, futilityValue, futilityBase; bool pvHit, givesCheck, captureOrPromotion; int moveCount; if (PvNode) { - oldAlpha = alpha; // To flag BOUND_EXACT when eval above alpha and no available moves (ss+1)->pv = pv; ss->pv[0] = MOVE_NONE; } @@ -1612,8 +1611,7 @@ namespace { // Save gathered info in transposition table tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit, - bestValue >= beta ? BOUND_LOWER : - PvNode && bestValue > oldAlpha ? BOUND_EXACT : BOUND_UPPER, + bestValue >= beta ? BOUND_LOWER : BOUND_UPPER, ttDepth, bestMove, ss->staticEval); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); From a0259d8ab9661b7f625474d2cbe18481ef69bbf2 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Fri, 5 Nov 2021 13:49:28 +0100 Subject: [PATCH 0710/1766] Tweak initial aspiration window. Maintain for each root move an exponential average of the search value with a weight ratio of 2:1 (new value vs old values). Then the average score is used as the center of the initial aspiration window instead of the previous score. Stats indicate (see PR) that the deviation for previous score is in general greater than using average score, so later seems a better estimation of the next search value. This is probably the reason this patch succeded besides smoothing the sometimes wild swings in search score. An additional observation is that at higher depth previous score is above but average score below zero. So for average score more/less fail/low highs should be occur than previous score. STC: LLR: 2.97 (-2.94,2.94) <0.00,2.50> Total: 59792 W: 15106 L: 14792 D: 29894 Ptnml(0-2): 144, 6718, 15869, 7010, 155 https://tests.stockfishchess.org/tests/view/61841612d7a085ad008eef06 LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 46448 W: 11835 L: 11537 D: 23076 Ptnml(0-2): 21, 4756, 13374, 5050, 23 https://tests.stockfishchess.org/tests/view/618463abd7a085ad008eef3e closes https://github.com/official-stockfish/Stockfish/pull/3776 Bench: 6719976 --- src/search.cpp | 4 +++- src/search.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 6c894c17ce8..090a79319f4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -376,7 +376,7 @@ void Thread::search() { // Reset aspiration window starting size if (rootDepth >= 4) { - Value prev = rootMoves[pvIdx].previousScore; + Value prev = rootMoves[pvIdx].averageScore; delta = Value(17) + int(prev) * prev / 16384; alpha = std::max(prev - delta,-VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE); @@ -1280,6 +1280,8 @@ namespace { RootMove& rm = *std::find(thisThread->rootMoves.begin(), thisThread->rootMoves.end(), move); + rm.averageScore = rm.averageScore != -VALUE_INFINITE ? (2 * value + rm.averageScore) / 3 : value; + // PV move or new best move? if (moveCount == 1 || value > alpha) { diff --git a/src/search.h b/src/search.h index ba9d067793a..7a5d5bdf478 100644 --- a/src/search.h +++ b/src/search.h @@ -73,6 +73,7 @@ struct RootMove { Value score = -VALUE_INFINITE; Value previousScore = -VALUE_INFINITE; + Value averageScore = -VALUE_INFINITE; int selDepth = 0; int tbRank = 0; Value tbScore; From 7b278aab9f61620b9dba31896b38aeea1eb911e2 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 7 Nov 2021 11:01:03 +0100 Subject: [PATCH 0711/1766] Reduce use of lazyEval In case the evaluation at root is large, discourage the use of lazyEval. This fixes https://github.com/official-stockfish/Stockfish/issues/3772 or at least improves it significantly. In this case, poor play with large odds can be observed, in extreme cases leading to a loss despite large advantage: r1bq1b1r/ppp3p1/3p1nkp/n3p3/2B1P2N/2NPB3/PPP2PPP/R3K2R b KQ - 5 9 With this patch the poor move is only considered up to depth 13, in master up to depth 28. The patch did not pass at LTC with Elo gainer bounds, but with slightly positive Elo nevertheless (95% LOS). STC: LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 40368 W: 10318 L: 10041 D: 20009 Ptnml(0-2): 103, 4493, 10725, 4750, 113 https://tests.stockfishchess.org/tests/view/61800ad259e71df00dcc420d LTC: LLR: -2.94 (-2.94,2.94) <0.50,3.00> Total: 212288 W: 52997 L: 52692 D: 106599 Ptnml(0-2): 112, 22038, 61549, 22323, 122 https://tests.stockfishchess.org/tests/view/618050d959e71df00dcc426d closes https://github.com/official-stockfish/Stockfish/pull/3780 Bench: 7127040 --- src/evaluate.cpp | 5 ++++- src/search.cpp | 2 +- src/thread.h | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 2f1d5067d01..a503b0c8339 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -988,7 +988,9 @@ namespace { // Early exit if score is high auto lazy_skip = [&](Value lazyThreshold) { - return abs(mg_value(score) + eg_value(score)) > lazyThreshold + pos.non_pawn_material() / 32; + return abs(mg_value(score) + eg_value(score)) > lazyThreshold + + std::abs(pos.this_thread()->bestValue) * 5 / 4 + + pos.non_pawn_material() / 32; }; if (lazy_skip(LazyThreshold1)) @@ -1126,6 +1128,7 @@ std::string Eval::trace(Position& pos) { std::memset(scores, 0, sizeof(scores)); pos.this_thread()->trend = SCORE_ZERO; // Reset any dynamic contempt + pos.this_thread()->bestValue = VALUE_ZERO; // Reset bestValue for lazyEval v = Evaluation(pos).value(); diff --git a/src/search.cpp b/src/search.cpp index 090a79319f4..11d1df326bc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -286,7 +286,7 @@ void Thread::search() { // The latter is needed for statScore and killer initialization. Stack stack[MAX_PLY+10], *ss = stack+7; Move pv[MAX_PLY+1]; - Value bestValue, alpha, beta, delta; + Value alpha, beta, delta; Move lastBestMove = MOVE_NONE; Depth lastBestMoveDepth = 0; MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr); diff --git a/src/thread.h b/src/thread.h index fae866c4f30..387937390c1 100644 --- a/src/thread.h +++ b/src/thread.h @@ -64,6 +64,7 @@ class Thread { uint64_t nodesLastExplosive; uint64_t nodesLastNormal; std::atomic nodes, tbHits, bestMoveChanges; + Value bestValue; int selDepth, nmpMinPly; Color nmpColor; ExplosionState state; From c4a1390f4e485b8b6a2cf43504a968a3c8a4317d Mon Sep 17 00:00:00 2001 From: bmc4 Date: Mon, 8 Nov 2021 08:46:43 -0300 Subject: [PATCH 0712/1766] Simplify away the Reverse Move penalty This simplifies the penalty for reverse move introduced in https://github.com/official-stockfish/Stockfish/pull/2294 . STC: LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 81696 W: 20627 L: 20540 D: 40529 Ptnml(0-2): 221, 9390, 21559, 9437, 241 https://tests.stockfishchess.org/tests/view/618810acd7a085ad008ef1cc LTC: LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 44136 W: 11021 L: 10890 D: 22225 Ptnml(0-2): 28, 4570, 12746, 4691, 33 https://tests.stockfishchess.org/tests/view/61885686d7a085ad008ef20b closes https://github.com/official-stockfish/Stockfish/pull/3781 bench: 6547978 --- src/search.cpp | 4 ---- src/types.h | 4 ---- 2 files changed, 8 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 11d1df326bc..f2e11e35b52 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1756,10 +1756,6 @@ namespace { thisThread->mainHistory[us][from_to(move)] << bonus; update_continuation_histories(ss, pos.moved_piece(move), to_sq(move), bonus); - // Penalty for reversed move in case of moved piece not being a pawn - if (type_of(pos.moved_piece(move)) != PAWN) - thisThread->mainHistory[us][from_to(reverse_move(move))] << -bonus; - // Update countermove history if (is_ok((ss-1)->currentMove)) { diff --git a/src/types.h b/src/types.h index fd643117072..02cd19def77 100644 --- a/src/types.h +++ b/src/types.h @@ -470,10 +470,6 @@ 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 1a5c21dc56a30f4af3979fb47ec3681dc7077195 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 7 Nov 2021 14:42:46 +0100 Subject: [PATCH 0713/1766] Tune a few NNUE related scaling parameters passed STC LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 102480 W: 26099 L: 25708 D: 50673 Ptnml(0-2): 282, 11637, 27003, 12044, 274 https://tests.stockfishchess.org/tests/view/618820e3d7a085ad008ef1dd passed LTC LLR: 2.93 (-2.94,2.94) <0.50,3.00> Total: 165512 W: 41689 L: 41112 D: 82711 Ptnml(0-2): 82, 17255, 47510, 17822, 87 https://tests.stockfishchess.org/tests/view/6188b470d7a085ad008ef239 closes https://github.com/official-stockfish/Stockfish/pull/3784 Bench: 6339548 --- src/evaluate.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index a503b0c8339..9fdadbb74f3 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1091,9 +1091,9 @@ Value Eval::evaluate(const Position& pos) { v = Evaluation(pos).value(); // classical else { - int scale = 883 - + 32 * pos.count() - + 32 * pos.non_pawn_material() / 1024; + int scale = 898 + + 24 * pos.count() + + 33 * pos.non_pawn_material() / 1024; v = NNUE::evaluate(pos, true) * scale / 1024; // NNUE @@ -1102,7 +1102,7 @@ Value Eval::evaluate(const Position& pos) { } // Damp down the evaluation linearly when shuffling - v = v * (100 - pos.rule50_count()) / 100; + v = v * (207 - pos.rule50_count()) / 207; // Guarantee evaluation does not hit the tablebase range v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); From 9048ac00db12a9ac48bff9b9eb145b30ff88d984 Mon Sep 17 00:00:00 2001 From: noobpwnftw Date: Sat, 13 Nov 2021 06:38:52 +0800 Subject: [PATCH 0714/1766] Fix processor group binding under Windows. Starting with Windows Build 20348 the behavior of the numa API has been changed: https://docs.microsoft.com/en-us/windows/win32/procthread/numa-support Old code only worked because there was probably a limit on how many cores/threads can reside within one NUMA node, and the OS creates extra NUMA nodes when necessary, however the actual mechanism of core binding is done by "Processor Groups"(https://docs.microsoft.com/en-us/windows/win32/procthread/processor-groups). With a newer OS, one NUMA node can have many such "Processor Groups" and we should just consistently use the number of groups to bind the threads instead of deriving the topology from the number of NUMA nodes. This change is required to spread threads on all cores on Windows 11 with a 3990X CPU. It has only 1 NUMA node with 2 groups of 64 threads each. closes https://github.com/official-stockfish/Stockfish/pull/3787 No functional change. --- src/misc.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index f9c123374f2..0af20e10d58 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -502,7 +502,7 @@ void bindThisThread(size_t) {} int best_group(size_t idx) { int threads = 0; - int nodes = 0; + int groups = 0; int cores = 0; DWORD returnLength = 0; DWORD byteOffset = 0; @@ -530,8 +530,8 @@ int best_group(size_t idx) { while (byteOffset < returnLength) { - if (ptr->Relationship == RelationNumaNode) - nodes++; + if (ptr->Relationship == RelationGroup) + groups += ptr->Group.MaximumGroupCount; else if (ptr->Relationship == RelationProcessorCore) { @@ -546,23 +546,23 @@ int best_group(size_t idx) { free(buffer); - std::vector groups; + std::vector core_groups; - // Run as many threads as possible on the same node until core limit is - // reached, then move on filling the next node. - for (int n = 0; n < nodes; n++) - for (int i = 0; i < cores / nodes; i++) - groups.push_back(n); + // Run as many threads as possible on the same group until core limit is + // reached, then move on filling the next group. + for (int n = 0; n < groups; n++) + for (int i = 0; i < cores / groups; i++) + core_groups.push_back(n); // In case a core has more than one logical processor (we assume 2) and we // have still threads to allocate, then spread them evenly across available - // nodes. + // groups. for (int t = 0; t < threads - cores; t++) - groups.push_back(t % nodes); + core_groups.push_back(t % groups); // If we still have more threads than the total number of logical processors // then return -1 and let the OS to decide what to do. - return idx < groups.size() ? groups[idx] : -1; + return idx < core_groups.size() ? core_groups[idx] : -1; } From f5df517145890ecee3d855e98470241b68645b87 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 14 Nov 2021 21:35:00 +0300 Subject: [PATCH 0715/1766] Simplify Pv nodes related logic in LMR Instead of having 2 separate conditions for Pv nodes reductions we can actually write them together. Despite it's not being strictly logically the same bench actually doesn't change up to depth 20, so them interacting is really rare and thus it's just a removal of extra PvNode check most of the time. passed STC: https://tests.stockfishchess.org/tests/view/618ce27cd7a085ad008ef4e9 LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 37488 W: 9424 L: 9279 D: 18785 Ptnml(0-2): 90, 3903, 10634, 4006, 111 passed LTC: https://tests.stockfishchess.org/tests/view/618d2585d7a085ad008ef527 LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 49968 W: 12449 L: 12331 D: 25188 Ptnml(0-2): 27, 4745, 15309, 4889, 14 closes https://github.com/official-stockfish/Stockfish/pull/3792 Bench: 6339548 --- src/search.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index f2e11e35b52..fabb0ff07ac 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1164,15 +1164,12 @@ namespace { { Depth r = reduction(improving, depth, moveCount, rangeReduction > 2); - // Decrease reduction if on the PV (~2 Elo) + // Decrease reduction at some PvNodes (~2 Elo) if ( PvNode - && bestMoveCount <= 3) + && bestMoveCount <= 3 + && beta - alpha >= thisThread->rootDelta / 4) r--; - // Increases reduction for PvNodes that have small window - if (PvNode && beta - alpha < thisThread->rootDelta / 4) - r++; - // Decrease reduction if position is or has been on the PV // and node is not likely to fail low. (~3 Elo) if ( ss->ttPv From a5a89b27c8e3225fb453d603bc4515d32bb351c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Fri, 19 Nov 2021 20:04:35 +0100 Subject: [PATCH 0716/1766] Introduce Optimism Current master implements a scaling of the raw NNUE output value with a formula equivalent to 'eval = alpha * NNUE_output', where the scale factor alpha varies between 1.8 (for early middle game) and 0.9 (for pure endgames). This feature allows Stockfish to keep material on the board when she thinks she has the advantage, and to seek exchanges and simplifications when she thinks she has to defend. This patch slightly offsets the turning point between these two strategies, by adding to Stockfish's evaluation a small "optimism" value before actually doing the scaling. The effect is that SF will play a little bit more risky, trying to keep the tension a little bit longer when she is defending, and keeping even more material on the board when she has an advantage. We note that this patch is similar in spirit to the old "Contempt" idea we used to have in classical Stockfish, but this implementation differs in two key points: a) it has been tested as an Elo-gainer against master; b) the values output by the search are not changed on average by the implementation (in other words, the optimism value changes the tension/exchange strategy, but a displayed value of 1.0 pawn has the same signification before and after the patch). See the old comment https://github.com/official-stockfish/Stockfish/pull/1361#issuecomment-359165141 for some images illustrating the ideas. ------- finished yellow at STC: LLR: -2.94 (-2.94,2.94) <0.00,2.50> Total: 165048 W: 41705 L: 41611 D: 81732 Ptnml(0-2): 565, 18959, 43245, 19327, 428 https://tests.stockfishchess.org/tests/view/61942a3dcd645dc8291c876b passed LTC: LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 121656 W: 30762 L: 30287 D: 60607 Ptnml(0-2): 87, 12558, 35032, 13095, 56 https://tests.stockfishchess.org/tests/view/61962c58cd645dc8291c8877 ------- How to continue from there? a) the shape (slope and amplitude) of the sigmoid used to compute the optimism value could be tweaked to try to gain more Elo, so the parameters of the sigmoid function in line 391 of search.cpp could be tuned with SPSA. Manual tweaking is also possible using this Desmos page: https://www.desmos.com/calculator/jhh83sqq92 b) in a similar vein, with two recents patches affecting the scaling of the NNUE evaluation in evaluate.cpp, now could be a good time to try a round of SPSA tuning of the NNUE network; c) this patch will tend to keep tension in middlegame a little bit longer, so any patch improving the defensive aspect of play via search extensions in risky, tactical positions would be welcome. ------- closes https://github.com/official-stockfish/Stockfish/pull/3797 Bench: 6184852 --- src/evaluate.cpp | 19 +++++++++++++------ src/misc.h | 27 +++++++++++++++++++++++++++ src/search.cpp | 15 ++++++++++----- src/thread.h | 1 + 4 files changed, 51 insertions(+), 11 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 9fdadbb74f3..c1d3d159b6f 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1091,11 +1091,15 @@ Value Eval::evaluate(const Position& pos) { v = Evaluation(pos).value(); // classical else { - int scale = 898 - + 24 * pos.count() - + 33 * pos.non_pawn_material() / 1024; + int scale = 898 + + 24 * pos.count() + + 33 * pos.non_pawn_material() / 1024; - v = NNUE::evaluate(pos, true) * scale / 1024; // NNUE + Value nnue = NNUE::evaluate(pos, true); // NNUE + Color stm = pos.side_to_move(); + Value optimism = pos.this_thread()->optimism[stm]; + + v = (nnue + optimism) * scale / 1024 - optimism; if (pos.is_chess960()) v += fix_FRC(pos); @@ -1127,8 +1131,11 @@ std::string Eval::trace(Position& pos) { std::memset(scores, 0, sizeof(scores)); - pos.this_thread()->trend = SCORE_ZERO; // Reset any dynamic contempt - pos.this_thread()->bestValue = VALUE_ZERO; // Reset bestValue for lazyEval + // Reset any global variable used in eval + pos.this_thread()->trend = SCORE_ZERO; + pos.this_thread()->bestValue = VALUE_ZERO; + pos.this_thread()->optimism[WHITE] = VALUE_ZERO; + pos.this_thread()->optimism[BLACK] = VALUE_ZERO; v = Evaluation(pos).value(); diff --git a/src/misc.h b/src/misc.h index 718e5558178..c17441306c1 100644 --- a/src/misc.h +++ b/src/misc.h @@ -138,6 +138,33 @@ class ValueList { std::size_t size_ = 0; }; + +/// sigmoid(t, x0, y0, C, P, Q) implements a sigmoid-like function using only integers, +/// with the following properties: +/// +/// - sigmoid is centered in (x0, y0) +/// - sigmoid has amplitude [-P/Q , P/Q] instead of [-1 , +1] +/// - limit is (y0 - P/Q) when t tends to -infinity +/// - limit is (y0 + P/Q) when t tends to +infinity +/// - the slope can be adjusted using C > 0, smaller C giving a steeper sigmoid +/// - the slope of the sigmoid when t = x0 is P/(Q*C) +/// - sigmoid is increasing with t when P > 0 and Q > 0 +/// - to get a decreasing sigmoid, call with -t, or change sign of P +/// - mean value of the sigmoid is y0 +/// +/// Use to draw the sigmoid + +inline int64_t sigmoid(int64_t t, int64_t x0, + int64_t y0, + int64_t C, + int64_t P, + int64_t Q) +{ + assert(C > 0); + return y0 + P * (t-x0) / (Q * (std::abs(t-x0) + C)) ; +} + + /// xorshift64star Pseudo-Random Number Generator /// This class is based on original code written and dedicated /// to the public domain by Sebastiano Vigna (2014). diff --git a/src/search.cpp b/src/search.cpp index fabb0ff07ac..1dfadd21ab6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -334,8 +334,10 @@ void Thread::search() { nodesLastExplosive = nodes; nodesLastNormal = nodes; - state = EXPLOSION_NONE; - trend = SCORE_ZERO; + state = EXPLOSION_NONE; + trend = SCORE_ZERO; + optimism[ us] = Value(25); + optimism[~us] = -optimism[us]; int searchAgainCounter = 0; @@ -381,11 +383,14 @@ void Thread::search() { alpha = std::max(prev - delta,-VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE); - // Adjust trend based on root move's previousScore (dynamic contempt) - int tr = 113 * prev / (abs(prev) + 147); - + // Adjust trend and optimism based on root move's previousScore + int tr = sigmoid(prev, 0, 0, 147, 113, 1); trend = (us == WHITE ? make_score(tr, tr / 2) : -make_score(tr, tr / 2)); + + int opt = sigmoid(prev, 0, 25, 147, 14464, 256); + optimism[ us] = Value(opt); + optimism[~us] = -optimism[us]; } // Start with a small aspiration window and, in the case of a fail diff --git a/src/thread.h b/src/thread.h index 387937390c1..cd206faab98 100644 --- a/src/thread.h +++ b/src/thread.h @@ -68,6 +68,7 @@ class Thread { int selDepth, nmpMinPly; Color nmpColor; ExplosionState state; + Value optimism[COLOR_NB]; Position rootPos; StateInfo rootState; From a943b1d28d673814ee1f3de4a2ae4b8e091f1e4c Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 21 Nov 2021 20:57:12 +0100 Subject: [PATCH 0717/1766] Remove appveyor CI retire msvc support and corresponding CI. No active development happens on msvc, and build is much slower or wrong. gcc (mingw) is our toolchain of choice also on windows, and the latter is tested. No functional change --- appveyor.yml | 88 ---------------------------------------------------- 1 file changed, 88 deletions(-) delete mode 100644 appveyor.yml diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index ab6084090a4..00000000000 --- a/appveyor.yml +++ /dev/null @@ -1,88 +0,0 @@ -version: 1.0.{build} -clone_depth: 50 - -branches: - only: - - master - -# Operating system (build VM template) -os: Visual Studio 2019 - -# Build platform, i.e. x86, x64, AnyCPU. This setting is optional. -platform: - - x86 - - x64 - -# build Configuration, i.e. Debug, Release, etc. -configuration: - - Debug - - Release - -matrix: - # The build fail immediately once one of the job fails - fast_finish: true - -# Scripts that are called at very beginning, before repo cloning -init: - - cmake --version - - msbuild /version - -before_build: - - ps: | - # Get sources - $src = get-childitem -Path *.cpp -Recurse | select -ExpandProperty FullName - $src = $src -join ' ' - $src = $src.Replace("\", "/") - - # Build CMakeLists.txt - $t = 'cmake_minimum_required(VERSION 3.17)', - 'project(Stockfish)', - 'set(CMAKE_CXX_STANDARD 17)', - 'set(CMAKE_CXX_STANDARD_REQUIRED ON)', - 'set (CMAKE_CXX_EXTENSIONS OFF)', - 'set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/src)', - 'set(source_files', $src, ')', - 'add_executable(stockfish ${source_files})' - - # Write CMakeLists.txt withouth BOM - $MyPath = (Get-Item -Path "." -Verbose).FullName + '\CMakeLists.txt' - $Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False - [System.IO.File]::WriteAllLines($MyPath, $t, $Utf8NoBomEncoding) - - # Obtain bench reference from git log - $b = git log HEAD | sls "\b[Bb]ench[ :]+[0-9]{7}" | select -first 1 - $bench = $b -match '\D+(\d+)' | % { $matches[1] } - Write-Host "Reference bench:" $bench - $g = "Visual Studio 16 2019" - If (${env:PLATFORM} -eq 'x64') { $a = "x64" } - If (${env:PLATFORM} -eq 'x86') { $a = "Win32" } - cmake -G "${g}" -A ${a} . - Write-Host "Generated files for: " $g $a - -build_script: - - cmake --build . --config %CONFIGURATION% -- /verbosity:minimal - - ps: | - # Download default NNUE net from fishtest - $nnuenet = Get-Content -Path src\evaluate.h | Select-String -CaseSensitive -Pattern "EvalFileDefaultName" | Select-String -CaseSensitive -Pattern "nn-[a-z0-9]{12}.nnue" - $dummy = $nnuenet -match "(?nn-[a-z0-9]{12}.nnue)" - $nnuenet = $Matches.nnuenet - Write-Host "Default net:" $nnuenet - $nnuedownloadurl = "https://tests.stockfishchess.org/api/nn/$nnuenet" - $nnuefilepath = "src\${env:CONFIGURATION}\$nnuenet" - if (Test-Path -Path $nnuefilepath) { - Write-Host "Already available." - } else { - Write-Host "Downloading $nnuedownloadurl to $nnuefilepath" - Invoke-WebRequest -Uri $nnuedownloadurl -OutFile $nnuefilepath - } - -before_test: - - cd src/%CONFIGURATION% - - stockfish bench 2> out.txt >NUL - - ps: | - # Verify bench number - $s = (gc "./out.txt" | out-string) - $r = ($s -match 'Nodes searched \D+(\d+)' | % { $matches[1] }) - Write-Host "Engine bench:" $r - Write-Host "Reference bench:" $bench - If ($r -ne $bench) { exit 1 } From 7218ec4df9fef1146a451b71f0ed3bfd8123c9f9 Mon Sep 17 00:00:00 2001 From: noobpwnftw Date: Sat, 20 Nov 2021 17:57:08 +0800 Subject: [PATCH 0718/1766] Revert and fix earlier windows NUMA patch revert https://github.com/official-stockfish/Stockfish/commit/9048ac00db12a9ac48bff9b9eb145b30ff88d984 due to core spread problem and fix new OS compatibility with another method. This code assumes that if one NUMA node has more than one processor groups, they are created equal(having equal amount of cores assigned to each of the groups), and also the total number of available cores contained in such groups are equal to the number of available cores within one NUMA node because of how best_node function works. closes https://github.com/official-stockfish/Stockfish/pull/3798 fixes https://github.com/official-stockfish/Stockfish/pull/3787 No functional change. --- src/misc.cpp | 54 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index 0af20e10d58..4dfa9f0ce2f 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -36,6 +36,7 @@ typedef bool(*fun1_t)(LOGICAL_PROCESSOR_RELATIONSHIP, PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, PDWORD); typedef bool(*fun2_t)(USHORT, PGROUP_AFFINITY); typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY); +typedef bool(*fun4_t)(USHORT, PGROUP_AFFINITY, USHORT, PUSHORT); } #endif @@ -495,14 +496,14 @@ void bindThisThread(size_t) {} #else -/// best_group() retrieves logical processor information using Windows specific -/// API and returns the best group id for the thread with index idx. Original +/// best_node() retrieves logical processor information using Windows specific +/// API and returns the best node id for the thread with index idx. Original /// code from Texel by Peter Österlund. -int best_group(size_t idx) { +int best_node(size_t idx) { int threads = 0; - int groups = 0; + int nodes = 0; int cores = 0; DWORD returnLength = 0; DWORD byteOffset = 0; @@ -530,8 +531,8 @@ int best_group(size_t idx) { while (byteOffset < returnLength) { - if (ptr->Relationship == RelationGroup) - groups += ptr->Group.MaximumGroupCount; + if (ptr->Relationship == RelationNumaNode) + nodes++; else if (ptr->Relationship == RelationProcessorCore) { @@ -546,23 +547,23 @@ int best_group(size_t idx) { free(buffer); - std::vector core_groups; + std::vector groups; - // Run as many threads as possible on the same group until core limit is - // reached, then move on filling the next group. - for (int n = 0; n < groups; n++) - for (int i = 0; i < cores / groups; i++) - core_groups.push_back(n); + // Run as many threads as possible on the same node until core limit is + // reached, then move on filling the next node. + for (int n = 0; n < nodes; n++) + for (int i = 0; i < cores / nodes; i++) + groups.push_back(n); // In case a core has more than one logical processor (we assume 2) and we // have still threads to allocate, then spread them evenly across available - // groups. + // nodes. for (int t = 0; t < threads - cores; t++) - core_groups.push_back(t % groups); + groups.push_back(t % nodes); // If we still have more threads than the total number of logical processors // then return -1 and let the OS to decide what to do. - return idx < core_groups.size() ? core_groups[idx] : -1; + return idx < groups.size() ? groups[idx] : -1; } @@ -571,22 +572,35 @@ int best_group(size_t idx) { void bindThisThread(size_t idx) { // Use only local variables to be thread-safe - int group = best_group(idx); + int node = best_node(idx); - if (group == -1) + if (node == -1) return; // Early exit if the needed API are not available at runtime HMODULE k32 = GetModuleHandle("Kernel32.dll"); auto fun2 = (fun2_t)(void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMaskEx"); auto fun3 = (fun3_t)(void(*)())GetProcAddress(k32, "SetThreadGroupAffinity"); + auto fun4 = (fun4_t)(void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMask2"); if (!fun2 || !fun3) return; - GROUP_AFFINITY affinity; - if (fun2(group, &affinity)) - fun3(GetCurrentThread(), &affinity, nullptr); + if (!fun4) { + GROUP_AFFINITY affinity; + if (fun2(node, &affinity)) + fun3(GetCurrentThread(), &affinity, nullptr); + } else { + // If a numa node has more than one processor group, we assume they are + // sized equal and we spread threads evenly across the groups. + USHORT elements, returnedElements; + elements = GetMaximumProcessorGroupCount(); + GROUP_AFFINITY *affinity = (GROUP_AFFINITY*)malloc( + elements * sizeof(GROUP_AFFINITY)); + if (fun4(node, affinity, elements, &returnedElements)) + fun3(GetCurrentThread(), &affinity[idx % returnedElements], nullptr); + free(affinity); + } } #endif From 092b27a6d0174f619fff9a53099ac9fdc5c2cb4e Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Tue, 23 Nov 2021 20:40:32 +0100 Subject: [PATCH 0719/1766] Less futility pruning. Disable futility pruning at former PV nodes stored in the transposition table. STC: LLR: 2.96 (-2.94,2.94) <0.00,2.50> Total: 102256 W: 25708 L: 25318 D: 51230 Ptnml(0-2): 276, 11511, 27168, 11893, 280 https://tests.stockfishchess.org/tests/view/61990b3135c7c6348cb602db LTC: LLR: 2.96 (-2.94,2.94) <0.50,3.00> Total: 183304 W: 46027 L: 45408 D: 91869 Ptnml(0-2): 96, 19029, 52778, 19658, 91 https://tests.stockfishchess.org/tests/view/619a0d1b35c7c6348cb603bc closes https://github.com/official-stockfish/Stockfish/pull/3804 Bench: 7334766 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 1dfadd21ab6..92ec9eca77d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -817,7 +817,7 @@ namespace { // Step 7. Futility pruning: child node (~50 Elo). // The depth condition is important for mate finding. - if ( !PvNode + if ( !ss->ttPv && depth < 9 && eval - futility_margin(depth, improving) >= beta && eval < 15000) // 50% larger than VALUE_KNOWN_WIN, but smaller than TB wins. From 0ac8aca893dd2052f8433e0b4a3d65073266b00f Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Thu, 25 Nov 2021 20:55:52 +0300 Subject: [PATCH 0720/1766] Use fraction of history heuristics in futility pruning This idea is somewhat of a respin of smth we had in futility pruning and that was simplified away - dependence of it not only on static evaluation of position but also on move history heuristics. Instead of aborting it when they are high there we use fraction of their sum to adjust static eval pruning criteria. passed STC https://tests.stockfishchess.org/tests/view/619bd438c0a4ea18ba95a27d LLR: 2.93 (-2.94,2.94) <0.00,2.50> Total: 113704 W: 29284 L: 28870 D: 55550 Ptnml(0-2): 357, 12884, 30044, 13122, 445 passed LTC https://tests.stockfishchess.org/tests/view/619cb8f0c0a4ea18ba95a334 LLR: 2.96 (-2.94,2.94) <0.50,3.00> Total: 147136 W: 37307 L: 36770 D: 73059 Ptnml(0-2): 107, 15279, 42265, 15804, 113 closes https://github.com/official-stockfish/Stockfish/pull/3805 bench 6777918 --- src/search.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 92ec9eca77d..12c89de457b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1054,17 +1054,19 @@ namespace { } else { + int history = (*contHist[0])[movedPiece][to_sq(move)] + + (*contHist[1])[movedPiece][to_sq(move)] + + (*contHist[3])[movedPiece][to_sq(move)]; + // Continuation history based pruning (~20 Elo) - if (lmrDepth < 5 - && (*contHist[0])[movedPiece][to_sq(move)] - + (*contHist[1])[movedPiece][to_sq(move)] - + (*contHist[3])[movedPiece][to_sq(move)] < -3000 * depth + 3000) + if ( lmrDepth < 5 + && history < -3000 * depth + 3000) continue; // Futility pruning: parent node (~5 Elo) if ( !ss->inCheck && lmrDepth < 8 - && ss->staticEval + 172 + 145 * lmrDepth <= alpha) + && ss->staticEval + 172 + 145 * lmrDepth + history / 256 <= alpha) continue; // Prune moves with negative SEE (~20 Elo) From 9ee58dc7a75d7cdc09935f8782e927bb19700afe Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 24 Nov 2021 22:13:35 +0100 Subject: [PATCH 0721/1766] Update default net to nn-3678835b1d3d.nnue New net trained with nnue-pytorch, started from the master net on a data set of Leela (T60.binpack+T74.binpck) and Stockfish data (wrongIsRight_nodes5000pv2.binpack), available as a single interleaved binpack: https://drive.google.com/file/d/12uWZIA3F2cNbraAzQNb1jgf3tq_6HkTr/view?usp=sharing The nnue-pytorch branch used is https://github.com/vondele/nnue-pytorch/tree/wdl, which has the new feature to filter positions based on the likelihood of the current evaluation leading to the game outcome. It should make it less likely to try to learn from misevaluated positions. Standard options have been used, starting from the master net: --gpus 1 --threads 4 --num-workers 4 --batch-size 16384 --progress_bar_refresh_rate 300 --smart-fen-skipping --random-fen-skipping 12 --features=HalfKAv2_hm^ --lambda=1.0 Testing with games shows neutral Elo at STC, and good performance at LTC: STC: https://tests.stockfishchess.org/tests/view/619eb597c0a4ea18ba95a4dc ELO: -0.44 +-1.8 (95%) LOS: 31.2% Total: 40000 W: 10447 L: 10498 D: 19055 Ptnml(0-2): 254, 4576, 10260, 4787, 123 LTC: https://tests.stockfishchess.org/tests/view/619f6e87c0a4ea18ba95a53f ELO: 3.30 +-1.8 (95%) LOS: 100.0% Total: 33062 W: 8560 L: 8246 D: 16256 Ptnml(0-2): 54, 3358, 9352, 3754, 13 passed LTC SPRT: https://tests.stockfishchess.org/tests/view/61a0864e8967bbf894416e65 LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 29376 W: 7663 L: 7396 D: 14317 Ptnml(0-2): 67, 3017, 8205, 3380, 19 closes https://github.com/official-stockfish/Stockfish/pull/3808 Bench: 7011501 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index e2cdb21019a..e5340c7f14c 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-13406b1dcbe0.nnue" + #define EvalFileDefaultName "nn-3678835b1d3d.nnue" namespace NNUE { From 4bb11e823fb292b8cc81ac873ccca9827af462be Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 26 Nov 2021 22:10:00 +0100 Subject: [PATCH 0722/1766] Tune NNUE scaling params passed STC: https://tests.stockfishchess.org/tests/view/61a156f89e83391467a2b2cc LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 22816 W: 5896 L: 5646 D: 11274 Ptnml(0-2): 55, 2567, 5961, 2723, 102 passed LTC: https://tests.stockfishchess.org/tests/view/61a1cf3d9e83391467a2b30b LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 17904 W: 4658 L: 4424 D: 8822 Ptnml(0-2): 6, 1821, 5079, 2025, 21 closes https://github.com/official-stockfish/Stockfish/pull/3811 Bench: 7218806 --- src/evaluate.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index c1d3d159b6f..1dc701cfe09 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1091,9 +1091,9 @@ Value Eval::evaluate(const Position& pos) { v = Evaluation(pos).value(); // classical else { - int scale = 898 - + 24 * pos.count() - + 33 * pos.non_pawn_material() / 1024; + int scale = 1049 + + 8 * pos.count() + + 20 * pos.non_pawn_material() / 1024; Value nnue = NNUE::evaluate(pos, true); // NNUE Color stm = pos.side_to_move(); From 8bb5a436b240d22e3e9729a1cd094169941afd7c Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 27 Nov 2021 16:38:37 +0300 Subject: [PATCH 0723/1766] Adjust usage of history in futility pruning This patch refines 0ac8aca893dd2052f8433e0b4a3d65073266b00f that uses history heuristics in futility pruning. Now it adds main history of the move to in and also increases effect by factor of 2. passed STC https://tests.stockfishchess.org/tests/view/61a156829e83391467a2b2c9 LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 68464 W: 17920 L: 17587 D: 32957 Ptnml(0-2): 239, 7711, 18025, 7992, 265 passed LTC https://tests.stockfishchess.org/tests/view/61a1bde99e83391467a2b305 LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 26088 W: 6926 L: 6674 D: 12488 Ptnml(0-2): 18, 2619, 7531, 2845, 31 closes https://github.com/official-stockfish/Stockfish/pull/3812 bench 6804653 --- src/search.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 12c89de457b..8f9fa62c832 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1063,10 +1063,12 @@ namespace { && history < -3000 * depth + 3000) continue; + history += thisThread->mainHistory[us][from_to(move)]; + // Futility pruning: parent node (~5 Elo) if ( !ss->inCheck && lmrDepth < 8 - && ss->staticEval + 172 + 145 * lmrDepth + history / 256 <= alpha) + && ss->staticEval + 172 + 145 * lmrDepth + history / 128 <= alpha) continue; // Prune moves with negative SEE (~20 Elo) From af050e5eed43f2d360bc6d38a9d9ef64b6ce6ad8 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 28 Nov 2021 15:19:18 +0300 Subject: [PATCH 0724/1766] Refine futility pruning for parent nodes This patch is a result of refining of tuning vondele did after new net passed and some hand-made values adjustements - excluding changes in other pruning heuristics and rounding value of history divisor to the nearest power of 2. With this patch futility pruning becomes more aggressive and history influence on it is doubled again. passed STC https://tests.stockfishchess.org/tests/view/61a2c4c1a26505c2278c150d LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 33848 W: 8841 L: 8574 D: 16433 Ptnml(0-2): 100, 3745, 8988, 3970, 121 passed LTC https://tests.stockfishchess.org/tests/view/61a327ffa26505c2278c26d9 LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 22272 W: 5856 L: 5614 D: 10802 Ptnml(0-2): 12, 2230, 6412, 2468, 14 closes https://github.com/official-stockfish/Stockfish/pull/3814 bench 6302543 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 8f9fa62c832..807d261472f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1068,7 +1068,7 @@ namespace { // Futility pruning: parent node (~5 Elo) if ( !ss->inCheck && lmrDepth < 8 - && ss->staticEval + 172 + 145 * lmrDepth + history / 128 <= alpha) + && ss->staticEval + 142 + 139 * lmrDepth + history / 64 <= alpha) continue; // Prune moves with negative SEE (~20 Elo) From e4a0c6c75950bf27b6dc32490a1102499643126b Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 28 Nov 2021 19:26:10 +0100 Subject: [PATCH 0725/1766] Update default net to nn-4f56ecfca5b7.nnue New net trained with nnue-pytorch, started from a master net on a data set of Leela (T60.binpack+T74.binpck) Stockfish data (wrongIsRight_nodes5000pv2.binpack), and Michael Babigian's conversion of T60 Leela data (including TB7 rescoring) (farseer.binpack) available as a single interleaved binpack: https://drive.google.com/file/d/1_sQoWBl31WAxNXma2v45004CIVltytP8/view?usp=sharing The nnue-pytorch branch used is https://github.com/vondele/nnue-pytorch/tree/wdl passed STC: https://tests.stockfishchess.org/tests/view/61a3cc729f0c43dae1c71f1b LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 49152 W: 12842 L: 12544 D: 23766 Ptnml(0-2): 154, 5542, 12904, 5804, 172 passed LTC: https://tests.stockfishchess.org/tests/view/61a43c6260afd064f2d724f1 LLR: 2.96 (-2.94,2.94) <0.50,3.00> Total: 25528 W: 6676 L: 6425 D: 12427 Ptnml(0-2): 9, 2593, 7315, 2832, 15 closes https://github.com/official-stockfish/Stockfish/pull/3816 Bench: 6885242 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index e5340c7f14c..77c3b4e7d2d 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-3678835b1d3d.nnue" + #define EvalFileDefaultName "nn-4f56ecfca5b7.nnue" namespace NNUE { From ca3c1c5f3a5f011edd04af856cdd59bdaefe423d Mon Sep 17 00:00:00 2001 From: noobpwnftw Date: Tue, 30 Nov 2021 09:22:07 +0800 Subject: [PATCH 0726/1766] Enable compilation on older Windows systems Improve compatibility of the last NUMA patch when running under older versions of Windows, for instance Windows Server 2003. Reported by user "g3g6" in the following comments: https://github.com/official-stockfish/Stockfish/commit/7218ec4df9fef1146a451b71f0ed3bfd8123c9f9 Closes https://github.com/official-stockfish/Stockfish/pull/3821 No functional change --- src/misc.cpp | 27 ++++++++++++++++----------- src/search.cpp | 2 +- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index 4dfa9f0ce2f..294b7c8f85b 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -37,6 +37,7 @@ typedef bool(*fun1_t)(LOGICAL_PROCESSOR_RELATIONSHIP, typedef bool(*fun2_t)(USHORT, PGROUP_AFFINITY); typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY); typedef bool(*fun4_t)(USHORT, PGROUP_AFFINITY, USHORT, PUSHORT); +typedef WORD(*fun5_t)(); } #endif @@ -514,7 +515,8 @@ int best_node(size_t idx) { if (!fun1) return -1; - // First call to get returnLength. We expect it to fail due to null buffer + // First call to GetLogicalProcessorInformationEx() to get returnLength. + // We expect the call to fail due to null buffer. if (fun1(RelationAll, nullptr, &returnLength)) return -1; @@ -522,7 +524,7 @@ int best_node(size_t idx) { SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *buffer, *ptr; ptr = buffer = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)malloc(returnLength); - // Second call, now we expect to succeed + // Second call to GetLogicalProcessorInformationEx(), now we expect to succeed if (!fun1(RelationAll, buffer, &returnLength)) { free(buffer); @@ -582,23 +584,26 @@ void bindThisThread(size_t idx) { auto fun2 = (fun2_t)(void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMaskEx"); auto fun3 = (fun3_t)(void(*)())GetProcAddress(k32, "SetThreadGroupAffinity"); auto fun4 = (fun4_t)(void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMask2"); + auto fun5 = (fun5_t)(void(*)())GetProcAddress(k32, "GetMaximumProcessorGroupCount"); if (!fun2 || !fun3) return; - if (!fun4) { + if (!fun4 || !fun5) + { GROUP_AFFINITY affinity; - if (fun2(node, &affinity)) - fun3(GetCurrentThread(), &affinity, nullptr); - } else { + if (fun2(node, &affinity)) // GetNumaNodeProcessorMaskEx + fun3(GetCurrentThread(), &affinity, nullptr); // SetThreadGroupAffinity + } + else + { // If a numa node has more than one processor group, we assume they are // sized equal and we spread threads evenly across the groups. USHORT elements, returnedElements; - elements = GetMaximumProcessorGroupCount(); - GROUP_AFFINITY *affinity = (GROUP_AFFINITY*)malloc( - elements * sizeof(GROUP_AFFINITY)); - if (fun4(node, affinity, elements, &returnedElements)) - fun3(GetCurrentThread(), &affinity[idx % returnedElements], nullptr); + elements = fun5(); // GetMaximumProcessorGroupCount + GROUP_AFFINITY *affinity = (GROUP_AFFINITY*)malloc(elements * sizeof(GROUP_AFFINITY)); + if (fun4(node, affinity, elements, &returnedElements)) // GetNumaNodeProcessorMask2 + fun3(GetCurrentThread(), &affinity[idx % returnedElements], nullptr); // SetThreadGroupAffinity free(affinity); } } diff --git a/src/search.cpp b/src/search.cpp index 807d261472f..6e41f48de30 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1063,7 +1063,7 @@ namespace { && history < -3000 * depth + 3000) continue; - history += thisThread->mainHistory[us][from_to(move)]; + history += thisThread->mainHistory[us][from_to(move)]; // Futility pruning: parent node (~5 Elo) if ( !ss->inCheck From 282644f1413db0ccb58e2e1793d5007c58ff8ce2 Mon Sep 17 00:00:00 2001 From: pb00067 Date: Fri, 26 Nov 2021 08:03:15 +0100 Subject: [PATCH 0727/1766] Remove depth dependence and use same limit (2000) as stat_bonus STC: https://tests.stockfishchess.org/tests/view/619df59dc0a4ea18ba95a424 LLR: 2.96 (-2.94,2.94) <-2.25,0.25> Total: 83728 W: 21329 L: 21242 D: 41157 Ptnml(0-2): 297, 9669, 21847, 9752, 299 LTC: https://tests.stockfishchess.org/tests/view/619e64d7c0a4ea18ba95a475 LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 79888 W: 20238 L: 20155 D: 39495 Ptnml(0-2): 57, 8391, 22980, 8444, 73 closes https://github.com/official-stockfish/Stockfish/pull/3806 bench: 6792010 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 6e41f48de30..c4ed73372df 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -801,7 +801,7 @@ namespace { // Use static evaluation difference to improve quiet move ordering if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture) { - int bonus = std::clamp(-depth * 4 * int((ss-1)->staticEval + ss->staticEval), -1000, 1000); + int bonus = std::clamp(-16 * int((ss-1)->staticEval + ss->staticEval), -2000, 2000); thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus; } From 64f21ecdae0402bfde4e9f4e61097c99bcae343e Mon Sep 17 00:00:00 2001 From: hengyu Date: Fri, 26 Nov 2021 18:25:03 +0800 Subject: [PATCH 0728/1766] Small clean-up remove unneeded calculation. closes https://github.com/official-stockfish/Stockfish/pull/3807 No functional change. --- src/nnue/evaluate_nnue.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 6e40deab9ac..bd473294d15 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -234,7 +234,7 @@ namespace Stockfish::Eval::NNUE { { buffer[1] = '0' + cp / 10000; cp %= 10000; buffer[2] = '0' + cp / 1000; cp %= 1000; - buffer[3] = '0' + cp / 100; cp %= 100; + buffer[3] = '0' + cp / 100; buffer[4] = ' '; } else if (cp >= 1000) From 4b86ef8c4f8755850b38f2eca026cb9da20c4d01 Mon Sep 17 00:00:00 2001 From: Michael Ortmann <41313082+michaelortmann@users.noreply.github.com> Date: Tue, 30 Nov 2021 21:01:34 +0100 Subject: [PATCH 0729/1766] Fix typos in comments, adjust readme closes https://github.com/official-stockfish/Stockfish/pull/3822 also adjusts readme as requested in https://github.com/official-stockfish/Stockfish/pull/3816 No functional change --- README.md | 8 ++++++-- src/nnue/nnue_common.h | 2 +- src/syzygy/tbprobe.cpp | 20 ++++++++++---------- src/syzygy/tbprobe.h | 2 +- src/tune.h | 2 +- tests/reprosearch.sh | 2 +- 6 files changed, 20 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 79db8170257..330d19edd1f 100644 --- a/README.md +++ b/README.md @@ -175,8 +175,12 @@ on the evaluations of millions of positions at moderate search depth. The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward. It can be evaluated efficiently on CPUs, and exploits the fact that only parts of the neural network need to be updated after a typical chess move. -[The nodchip repository](https://github.com/nodchip/Stockfish) provides additional -tools to train and develop the NNUE networks. On CPUs supporting modern vector instructions +[The nodchip repository](https://github.com/nodchip/Stockfish) provided the first version of +the needed tools to train and develop the NNUE networks. Today, more advanced training tools are available +in [the nnue-pytorch repository](https://github.com/glinscott/nnue-pytorch/), while data generation tools +are available in [a dedicated branch](https://github.com/official-stockfish/Stockfish/tree/tools). + +On CPUs supporting modern vector instructions (avx2 and similar), the NNUE evaluation results in much stronger playing strength, even if the nodes per second computed by the engine is somewhat lower (roughly 80% of nps is typical). diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index 75ac7862791..74eaae17c2d 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -109,7 +109,7 @@ namespace Stockfish::Eval::NNUE { // write_little_endian() is our utility to write an integer (signed or unsigned, any size) // to a stream in little-endian order. We swap the byte order before the write if - // necessary to always write in little endian order, independantly of the byte + // necessary to always write in little endian order, independently of the byte // ordering of the compiling machine. template inline void write_little_endian(std::ostream& stream, IntType value) { diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 96b2970f245..41e867c00a3 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -769,7 +769,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu goto encode_remaining; // With pawns we have finished special treatments } - // In positions withouth pawns, we further flip the squares to ensure leading + // In positions without pawns, we further flip the squares to ensure leading // piece is below RANK_5. if (rank_of(squares[0]) > RANK_4) for (int i = 0; i < size; ++i) @@ -812,7 +812,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu // Rs "together" in 62 * 61 / 2 ways (we divide by 2 because rooks can be // swapped and still get the same position.) // - // In case we have at least 3 unique pieces (inlcuded kings) we encode them + // In case we have at least 3 unique pieces (included kings) we encode them // together. if (entry->hasUniquePieces) { @@ -827,7 +827,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu + (squares[1] - adjust1)) * 62 + squares[2] - adjust2; - // First piece is on a1-h8 diagonal, second below: map this occurence to + // First piece is on a1-h8 diagonal, second below: map this occurrence to // 6 to differentiate from the above case, rank_of() maps a1-d4 diagonal // to 0...3 and finally MapB1H1H7[] maps the b1-h1-h7 triangle to 0..27. else if (off_A1H8(squares[1])) @@ -857,7 +857,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu idx *= d->groupIdx[0]; Square* groupSq = squares + d->groupLen[0]; - // Encode remainig pawns then pieces according to square, in ascending order + // Encode remaining pawns then pieces according to square, in ascending order bool remainingPawns = entry->hasPawns && entry->pawnCount[1]; while (d->groupLen[++next]) @@ -885,7 +885,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu // Group together pieces that will be encoded together. The general rule is that // a group contains pieces of same type and color. The exception is the leading -// group that, in case of positions withouth pawns, can be formed by 3 different +// group that, in case of positions without pawns, can be formed by 3 different // pieces (default) or by the king pair when there is not a unique piece apart // from the kings. When there are pawns, pawns are always first in pieces[]. // @@ -917,7 +917,7 @@ void set_groups(T& e, PairsData* d, int order[], File f) { // // This ensures unique encoding for the whole position. The order of the // groups is a per-table parameter and could not follow the canonical leading - // pawns/pieces -> remainig pawns -> remaining pieces. In particular the + // pawns/pieces -> remaining pawns -> remaining pieces. In particular the // first group is at order[0] position and the remaining pawns, when present, // are at order[1] position. bool pp = e.hasPawns && e.pawnCount[1]; // Pawns on both sides @@ -937,7 +937,7 @@ void set_groups(T& e, PairsData* d, int order[], File f) { d->groupIdx[1] = idx; idx *= Binomial[d->groupLen[1]][48 - d->groupLen[0]]; } - else // Remainig pieces + else // Remaining pieces { d->groupIdx[next] = idx; idx *= Binomial[d->groupLen[next]][freeSquares]; @@ -947,7 +947,7 @@ void set_groups(T& e, PairsData* d, int order[], File f) { d->groupIdx[n] = idx; } -// In Recursive Pairing each symbol represents a pair of childern symbols. So +// In Recursive Pairing each symbol represents a pair of children symbols. So // read d->btree[] symbols data and expand each one in his left and right child // symbol until reaching the leafs that represent the symbol value. uint8_t set_symlen(PairsData* d, Sym s, std::vector& visited) { @@ -1317,7 +1317,7 @@ void Tablebases::init(const std::string& paths) { for (auto p : bothOnDiagonal) MapKK[p.first][p.second] = code++; - // Binomial[] stores the Binomial Coefficents using Pascal rule. There + // Binomial[] stores the Binomial Coefficients using Pascal rule. There // are Binomial[k][n] ways to choose k elements from a set of n elements. Binomial[0][0] = 1; @@ -1337,7 +1337,7 @@ void Tablebases::init(const std::string& paths) { for (int leadPawnsCnt = 1; leadPawnsCnt <= 5; ++leadPawnsCnt) for (File f = FILE_A; f <= FILE_D; ++f) { - // Restart the index at every file because TB table is splitted + // Restart the index at every file because TB table is split // by file, so we can reuse the same index for different files. int idx = 0; diff --git a/src/syzygy/tbprobe.h b/src/syzygy/tbprobe.h index 56734af9bdc..cf61b767590 100644 --- a/src/syzygy/tbprobe.h +++ b/src/syzygy/tbprobe.h @@ -38,7 +38,7 @@ enum WDLScore { // Possible states after a probing operation enum ProbeState { FAIL = 0, // Probe failed (missing file table) - OK = 1, // Probe succesful + OK = 1, // Probe successful CHANGE_STM = -1, // DTZ should check the other side ZEROING_BEST_MOVE = 2 // Best move zeroes DTZ (capture or pawn move) }; diff --git a/src/tune.h b/src/tune.h index b5c715b3caa..53d52a65b3b 100644 --- a/src/tune.h +++ b/src/tune.h @@ -84,7 +84,7 @@ class Tune { static Tune& instance() { static Tune t; return t; } // Singleton - // Use polymorphism to accomodate Entry of different types in the same vector + // Use polymorphism to accommodate Entry of different types in the same vector struct EntryBase { virtual ~EntryBase() = default; virtual void init_option() = 0; diff --git a/tests/reprosearch.sh b/tests/reprosearch.sh index c1167f7f169..e16ba4aed91 100755 --- a/tests/reprosearch.sh +++ b/tests/reprosearch.sh @@ -43,7 +43,7 @@ cat << EOF > repeat.exp expect eof EOF -# to increase the likelyhood of finding a non-reproducible case, +# to increase the likelihood of finding a non-reproducible case, # the allowed number of nodes are varied systematically for i in `seq 1 20` do From 95a2ac1e073a6f7c3af8aeef2fc4fe8cbd6650cf Mon Sep 17 00:00:00 2001 From: bmc4 Date: Mon, 29 Nov 2021 09:30:07 -0300 Subject: [PATCH 0730/1766] Simplify reduction on rootNode when bestMoveChanges is high The reduction introduced in #3736 also consider on rootNode, so we don't have to reduce again. STC: LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 28736 W: 7494 L: 7329 D: 13913 Ptnml(0-2): 95, 3247, 7503, 3444, 79 https://tests.stockfishchess.org/tests/view/61a3abe01b7fdf52228e74d8 LTC: LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 47816 W: 12434 L: 12308 D: 23074 Ptnml(0-2): 37, 4972, 13755, 5116, 28 https://tests.stockfishchess.org/tests/view/61a3c3e39f0c43dae1c71d71 closes https://github.com/official-stockfish/Stockfish/pull/3817 bench: 6331638 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c4ed73372df..70dc4b2100c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1185,8 +1185,8 @@ namespace { && !likelyFailLow) r -= 2; - // Increase reduction at root and non-PV nodes when the best move does not change frequently - if ( (rootNode || !PvNode) + // Increase reduction at non-PV nodes when the best move does not change frequently + if ( !PvNode && thisThread->bestMoveChanges <= 2) r++; From c1f9a359e8e319d832ee5a55277dab996dd29d25 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Mon, 29 Nov 2021 14:51:35 -0300 Subject: [PATCH 0731/1766] Correctly reset bestMoveChanges for searches not using time management (e.g. analysis, fixed node game play etc), bestMoveChanges was not reset during search iterations. As LMR uses this quantity, search was somewhat weaker. Tested using fixed node playing games: ``` ./c-chess-cli -each nodes=10000 option.Hash=16 -engine cmd=../Stockfish/src/fix -engine cmd=../Stockfish/src/master -concurrency 6 -openings file=../books/UHO_XXL_+0.90_+1.19.epd -games 10000 Score of Stockfish Fix vs Stockfish Master: 3187 - 3028 - 3785 [0.508] 10000 ./c-chess-cli -each nodes=30000 option.Hash=16 -engine cmd=../Stockfish/src/fix -engine cmd=../Stockfish/src/master -concurrency 6 -openings file=../books/UHO_XXL_+0.90_+1.19.epd -games 10000 Score of Stockfish Fix vs Stockfish Master: 2946 - 2834 - 4220 [0.506] 10000 ``` closes https://github.com/official-stockfish/Stockfish/pull/3818 bench: 5061979 --- src/search.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 70dc4b2100c..c926c1d2064 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -477,6 +477,13 @@ void Thread::search() { if (skill.enabled() && skill.time_to_pick(rootDepth)) skill.pick_best(multiPV); + // Use part of the gained time from a previous stable move for the current move + for (Thread* th : Threads) + { + totBestMoveChanges += th->bestMoveChanges; + th->bestMoveChanges = 0; + } + // Do we have time for the next iteration? Can we stop searching now? if ( Limits.use_time_management() && !Threads.stop @@ -489,13 +496,6 @@ void Thread::search() { // If the bestMove is stable over several iterations, reduce time accordingly timeReduction = lastBestMoveDepth + 9 < completedDepth ? 1.92 : 0.95; double reduction = (1.47 + mainThread->previousTimeReduction) / (2.32 * timeReduction); - - // Use part of the gained time from a previous stable move for the current move - for (Thread* th : Threads) - { - totBestMoveChanges += th->bestMoveChanges; - th->bestMoveChanges = 0; - } double bestMoveInstability = 1.073 + std::max(1.0, 2.25 - 9.9 / rootDepth) * totBestMoveChanges / Threads.size(); double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability; From c9977aa0a89c83bf21651bffd3b6f10c344ccc46 Mon Sep 17 00:00:00 2001 From: Gian-Carlo Pascutto Date: Wed, 1 Dec 2021 23:36:14 +0100 Subject: [PATCH 0732/1766] Add AVX-VNNI support for Alder Lake and later. In their infinite wisdom, Intel axed AVX512 from Alder Lake chips (well, not entirely, but we kind of want to use the Gracemont cores for chess!) but still added VNNI support. Confusingly enough, this is not the same as VNNI256 support. This adds a specific AVX-VNNI target that will use this AVX-VNNI mode, by prefixing the VNNI instructions with the appropriate VEX prefix, and avoiding AVX512 usage. This is about 1% faster on P cores: Result of 20 runs ================== base (./clang-bmi2 ) = 3306337 +/- 7519 test (./clang-vnni ) = 3344226 +/- 7388 diff = +37889 +/- 4153 speedup = +0.0115 P(speedup > 0) = 1.0000 But a nice 3% faster on E cores: Result of 20 runs ================== base (./clang-bmi2 ) = 1938054 +/- 28257 test (./clang-vnni ) = 1994606 +/- 31756 diff = +56552 +/- 3735 speedup = +0.0292 P(speedup > 0) = 1.0000 This was measured on Clang 13. GCC 11.2 appears to generate worse code for Alder Lake, though the speedup on the E cores is similar. It is possible to run the engine specifically on the P or E using binding, for example in linux it is possible to use (for an 8 P + 8 E setup like i9-12900K): taskset -c 0-15 ./stockfish taskset -c 16-23 ./stockfish where the first call binds to the P-cores and the second to the E-cores. closes https://github.com/official-stockfish/Stockfish/pull/3824 No functional change --- src/Makefile | 26 ++++++++++++++++++++++++-- src/simd.h | 13 ++++++++++--- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/Makefile b/src/Makefile index 5c52661b784..a9333a22f46 100644 --- a/src/Makefile +++ b/src/Makefile @@ -78,6 +78,7 @@ endif # ssse3 = yes/no --- -mssse3 --- Use Intel Supplemental Streaming SIMD Extensions 3 # sse41 = yes/no --- -msse4.1 --- Use Intel Streaming SIMD Extensions 4.1 # avx2 = yes/no --- -mavx2 --- Use Intel Advanced Vector Extensions 2 +# avxvnni = yes/no --- -mavxvnni --- Use Intel Vector Neural Network Instructions AVX # avx512 = yes/no --- -mavx512bw --- Use Intel Advanced Vector Extensions 512 # vnni256 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 256 # vnni512 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 512 @@ -100,8 +101,8 @@ endif # explicitly check for the list of supported architectures (as listed with make help), # the user can override with `make ARCH=x86-32-vnni256 SUPPORTED_ARCH=true` ifeq ($(ARCH), $(filter $(ARCH), \ - x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-bmi2 x86-64-avx2 \ - x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \ + x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-avxvnni x86-64-bmi2 \ + x86-64-avx2 x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \ x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-32 e2k \ armv7 armv7-neon armv8 apple-silicon general-64 general-32)) SUPPORTED_ARCH=true @@ -122,6 +123,7 @@ sse2 = no ssse3 = no sse41 = no avx2 = no +avxvnni = no avx512 = no vnni256 = no vnni512 = no @@ -192,6 +194,17 @@ ifeq ($(findstring -avx2,$(ARCH)),-avx2) avx2 = yes endif +ifeq ($(findstring -avxvnni,$(ARCH)),-avxvnni) + popcnt = yes + sse = yes + sse2 = yes + ssse3 = yes + sse41 = yes + avx2 = yes + avxvnni = yes + pext = yes +endif + ifeq ($(findstring -bmi2,$(ARCH)),-bmi2) popcnt = yes sse = yes @@ -544,6 +557,13 @@ ifeq ($(avx2),yes) endif endif +ifeq ($(avxvnni),yes) + CXXFLAGS += -DUSE_VNNI -DUSE_AVXVNNI + ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + CXXFLAGS += -mavxvnni + endif +endif + ifeq ($(avx512),yes) CXXFLAGS += -DUSE_AVX512 ifeq ($(comp),$(filter $(comp),gcc clang mingw)) @@ -689,6 +709,7 @@ help: @echo "x86-64-vnni512 > x86 64-bit with vnni support 512bit wide" @echo "x86-64-vnni256 > x86 64-bit with vnni support 256bit wide" @echo "x86-64-avx512 > x86 64-bit with avx512 support" + @echo "x86-64-avxvnni > x86 64-bit with avxvnni support" @echo "x86-64-bmi2 > x86 64-bit with bmi2 support" @echo "x86-64-avx2 > x86 64-bit with avx2 support" @echo "x86-64-sse41-popcnt > x86 64-bit with sse41 and popcnt support" @@ -837,6 +858,7 @@ config-sanity: net @echo "ssse3: '$(ssse3)'" @echo "sse41: '$(sse41)'" @echo "avx2: '$(avx2)'" + @echo "avxvnni: '$(avxvnni)'" @echo "avx512: '$(avx512)'" @echo "vnni256: '$(vnni256)'" @echo "vnni512: '$(vnni512)'" diff --git a/src/simd.h b/src/simd.h index 584148f1260..1ac98067f5e 100644 --- a/src/simd.h +++ b/src/simd.h @@ -46,6 +46,13 @@ #define USE_INLINE_ASM #endif +// Use either the AVX512 or AVX-VNNI version of the VNNI instructions. +#if defined(USE_AVXVNNI) +#define VNNI_PREFIX "%{vex%} " +#else +#define VNNI_PREFIX "" +#endif + namespace Stockfish::Simd { #if defined (USE_AVX512) @@ -208,7 +215,7 @@ namespace Stockfish::Simd { # if defined (USE_VNNI) # if defined (USE_INLINE_ASM) asm( - "vpdpbusd %[b], %[a], %[acc]\n\t" + VNNI_PREFIX "vpdpbusd %[b], %[a], %[acc]\n\t" : [acc]"+v"(acc) : [a]"v"(a), [b]"vm"(b) ); @@ -240,8 +247,8 @@ namespace Stockfish::Simd { # if defined (USE_VNNI) # if defined (USE_INLINE_ASM) asm( - "vpdpbusd %[b0], %[a0], %[acc]\n\t" - "vpdpbusd %[b1], %[a1], %[acc]\n\t" + VNNI_PREFIX "vpdpbusd %[b0], %[a0], %[acc]\n\t" + VNNI_PREFIX "vpdpbusd %[b1], %[a1], %[acc]\n\t" : [acc]"+v"(acc) : [a0]"v"(a0), [b0]"vm"(b0), [a1]"v"(a1), [b1]"vm"(b1) ); From e4b7403f127a36a35bbace9f833ab43babd98a6c Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Thu, 2 Dec 2021 23:41:23 +0300 Subject: [PATCH 0733/1766] Do more aggressive pruning for some node types This patch allows more aggressive futility/see based pruning for PV nodes with low delta and non-pv nodes. Fixes some white space issues. Passed STC https://tests.stockfishchess.org/tests/view/61a5ed33d16c530b5dcc27cc LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 182088 W: 47121 L: 46584 D: 88383 Ptnml(0-2): 551, 20687, 48037, 21212, 557 Passed LTC https://tests.stockfishchess.org/tests/view/61a74dfdbd5c4360bcded0ac LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 87136 W: 22494 L: 22103 D: 42539 Ptnml(0-2): 38, 8918, 25272, 9295, 45 closes https://github.com/official-stockfish/Stockfish/pull/3828 closes https://github.com/official-stockfish/Stockfish/pull/3829 bench 4332259 --- src/misc.cpp | 8 ++++---- src/misc.h | 2 +- src/search.cpp | 2 ++ 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index 294b7c8f85b..b46786dff08 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -515,7 +515,7 @@ int best_node(size_t idx) { if (!fun1) return -1; - // First call to GetLogicalProcessorInformationEx() to get returnLength. + // First call to GetLogicalProcessorInformationEx() to get returnLength. // We expect the call to fail due to null buffer. if (fun1(RelationAll, nullptr, &returnLength)) return -1; @@ -589,13 +589,13 @@ void bindThisThread(size_t idx) { if (!fun2 || !fun3) return; - if (!fun4 || !fun5) + if (!fun4 || !fun5) { GROUP_AFFINITY affinity; if (fun2(node, &affinity)) // GetNumaNodeProcessorMaskEx fun3(GetCurrentThread(), &affinity, nullptr); // SetThreadGroupAffinity - } - else + } + else { // If a numa node has more than one processor group, we assume they are // sized equal and we spread threads evenly across the groups. diff --git a/src/misc.h b/src/misc.h index c17441306c1..062b420a422 100644 --- a/src/misc.h +++ b/src/misc.h @@ -141,7 +141,7 @@ class ValueList { /// sigmoid(t, x0, y0, C, P, Q) implements a sigmoid-like function using only integers, /// with the following properties: -/// +/// /// - sigmoid is centered in (x0, y0) /// - sigmoid has amplitude [-P/Q , P/Q] instead of [-1 , +1] /// - limit is (y0 - P/Q) when t tends to -infinity diff --git a/src/search.cpp b/src/search.cpp index c926c1d2064..5e022e664ca 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1065,6 +1065,8 @@ namespace { history += thisThread->mainHistory[us][from_to(move)]; + lmrDepth = std::max(0, lmrDepth - (beta - alpha < thisThread->rootDelta / 4)); + // Futility pruning: parent node (~5 Elo) if ( !ss->inCheck && lmrDepth < 8 From 327060232a3ded6ed19113f234a2f9ba9bf65b02 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 2 Dec 2021 06:58:40 +0100 Subject: [PATCH 0734/1766] Update default net to nn-cdf1785602d6.nnue Same process as in https://github.com/official-stockfish/Stockfish/commit/e4a0c6c75950bf27b6dc32490a1102499643126b with the training started from the current master net. passed STC: LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 38224 W: 10023 L: 9742 D: 18459 Ptnml(0-2): 133, 4328, 9940, 4547, 164 https://tests.stockfishchess.org/tests/view/61a8611e4ed77d629d4e836e passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 115176 W: 29783 L: 29321 D: 56072 Ptnml(0-2): 68, 12039, 32936, 12453, 92 https://tests.stockfishchess.org/tests/view/61a8963e4ed77d629d4e8d9b closes https://github.com/official-stockfish/Stockfish/pull/3830 Bench: 4829419 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 77c3b4e7d2d..873a72dc40f 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-4f56ecfca5b7.nnue" + #define EvalFileDefaultName "nn-cdf1785602d6.nnue" namespace NNUE { From a6a9d828ab4cc35ca0f64207e2ff818a391d1939 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Sat, 4 Dec 2021 09:09:09 -0300 Subject: [PATCH 0735/1766] Simplifies bestMoveChanges from LMR As bestMoveChanges is only reset on mainThread and it could change how other threads search, a multi-threads test was made. STC: LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 146776 W: 37934 L: 37941 D: 70901 Ptnml(0-2): 477, 15644, 41173, 15597, 497 https://tests.stockfishchess.org/tests/view/61a8f9f34ed77d629d4ea2d6 LTC: LLR: 3.11 (-2.94,2.94) <-2.25,0.25> Total: 114040 W: 29314 L: 29269 D: 55457 Ptnml(0-2): 50, 10584, 35722, 10599, 65 https://tests.stockfishchess.org/tests/view/61a9d4bf9e8855bba1a35c4f (SMP, 8 threads) STC: LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 23888 W: 6308 L: 6143 D: 11437 Ptnml(0-2): 36, 2557, 6600, 2708, 43 https://tests.stockfishchess.org/tests/view/61ac27a756fcf33bce7d3677 closes https://github.com/official-stockfish/Stockfish/pull/3831 bench: 4829419 --- src/search.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5e022e664ca..28e16609203 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1187,9 +1187,8 @@ namespace { && !likelyFailLow) r -= 2; - // Increase reduction at non-PV nodes when the best move does not change frequently - if ( !PvNode - && thisThread->bestMoveChanges <= 2) + // Increase reduction at non-PV nodes + if (!PvNode) r++; // Decrease reduction if opponent's move count is high (~1 Elo) From 18f2b12cd019212b9c7103cc63e3bf64a5be7c3c Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Sun, 5 Dec 2021 16:18:33 +0100 Subject: [PATCH 0736/1766] Tweak time management Use for adjustment of the falling eval time factor now also the difference between previous best average score and current best score. STC: LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 109216 W: 28296 L: 27900 D: 53020 Ptnml(0-2): 312, 11759, 30148, 11999, 390 https://tests.stockfishchess.org/tests/view/61aafa8d1b31b85bcfa29d9c LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.00> Total: 54096 W: 14091 L: 13787 D: 26218 Ptnml(0-2): 29, 5124, 16447, 5410, 38 https://tests.stockfishchess.org/tests/view/61abbbbd56fcf33bce7d1d64 closes https://github.com/official-stockfish/Stockfish/pull/3833 Bench: 4829419 --- src/search.cpp | 4 +++- src/thread.cpp | 1 + src/thread.h | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 28e16609203..99c4743174d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -260,6 +260,7 @@ void MainThread::search() { bestThread = Threads.get_best_thread(); bestPreviousScore = bestThread->rootMoves[0].score; + bestPreviousAverageScore = bestThread->rootMoves[0].averageScore; // Send again PV info if we have a new best thread if (bestThread != this) @@ -489,7 +490,8 @@ void Thread::search() { && !Threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (318 + 6 * (mainThread->bestPreviousScore - bestValue) + double fallingEval = (142 + 6 * (mainThread->bestPreviousScore - bestValue) + + 6 * (mainThread->bestPreviousAverageScore - bestValue) + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 825.0; fallingEval = std::clamp(fallingEval, 0.5, 1.5); diff --git a/src/thread.cpp b/src/thread.cpp index da8e1d05be9..c03079d18e3 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -162,6 +162,7 @@ void ThreadPool::clear() { main()->callsCnt = 0; main()->bestPreviousScore = VALUE_INFINITE; + main()->bestPreviousAverageScore = VALUE_INFINITE; main()->previousTimeReduction = 1.0; } diff --git a/src/thread.h b/src/thread.h index cd206faab98..37c6452b9b7 100644 --- a/src/thread.h +++ b/src/thread.h @@ -95,6 +95,7 @@ struct MainThread : public Thread { double previousTimeReduction; Value bestPreviousScore; + Value bestPreviousAverageScore; Value iterValue[4]; int callsCnt; bool stopOnPonderhit; From 7d44b43b3ceb2eebc756709432a0e291f885a1d2 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Sun, 5 Dec 2021 16:29:51 +0100 Subject: [PATCH 0737/1766] Tweak history initialization Initialize continuation history with a slighlty negative value -71 instead of zero. The idea is, because the most history entries will be later negative anyway, to shift the starting values a little bit in the "correct" direction. Of course the effect of initialization dimishes with greater depth so I had the apprehension that the LTC test would be difficult to pass, but it passed. STC: LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 34520 W: 9076 L: 8803 D: 16641 Ptnml(0-2): 136, 3837, 9047, 4098, 142 https://tests.stockfishchess.org/tests/view/61aa52e39e8855bba1a3776b LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.00> Total: 75568 W: 19620 L: 19254 D: 36694 Ptnml(0-2): 44, 7773, 21796, 8115, 56 https://tests.stockfishchess.org/tests/view/61aa87d39e8855bba1a383a5 closes https://github.com/official-stockfish/Stockfish/pull/3834 Bench: 4674029 --- src/thread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thread.cpp b/src/thread.cpp index c03079d18e3..ed3accc5f0b 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -67,7 +67,7 @@ void Thread::clear() { { for (auto& to : continuationHistory[inCheck][c]) for (auto& h : to) - h->fill(0); + h->fill(-71); continuationHistory[inCheck][c][NO_PIECE][0]->fill(Search::CounterMovePruneThreshold - 1); } } From a3d425cf55494a4f86dc52a3d3b24c1657cbbc61 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Mon, 6 Dec 2021 03:52:44 +0300 Subject: [PATCH 0738/1766] Assign extra bonus for previous move that caused a fail low more often This patch allows to assign extra bonus for previous move that caused a fail low not only for PvNodes and cutNodes but also fo some allNodes - namely if the best result we could've got from the search is still far below alpha. passed STC https://tests.stockfishchess.org/tests/view/61aa26a49e8855bba1a36d96 LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 73808 W: 19183 L: 18842 D: 35783 Ptnml(0-2): 251, 8257, 19564, 8564, 268 passed LTC https://tests.stockfishchess.org/tests/view/61aa7dc29e8855bba1a3814f LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 142416 W: 36717 L: 36192 D: 69507 Ptnml(0-2): 106, 14799, 40862, 15346, 95 closes https://github.com/official-stockfish/Stockfish/pull/3835 bench 4724181 --- src/search.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 99c4743174d..edeed9c0304 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1380,7 +1380,15 @@ namespace { // Bonus for prior countermove that caused the fail low else if ( (depth >= 3 || PvNode) && !priorCapture) - update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * (1 + (PvNode || cutNode))); + { + //Assign extra bonus if current node is PvNode or cutNode + //or fail low was really bad + bool extraBonus = PvNode + || cutNode + || bestValue < alpha - 94 * depth; + + update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * (1 + extraBonus)); + } if (PvNode) bestValue = std::min(bestValue, maxValue); From b82d93ece484f833c994b40d9eddd959ba20ef92 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 4 Dec 2021 23:18:46 +0100 Subject: [PATCH 0739/1766] Update default net to nn-63376713ba63.nnue. same data set as previous trained nets, tuned the wdl model slightly for training. https://github.com/vondele/nnue-pytorch/tree/wdlTweak1 passed STC: https://tests.stockfishchess.org/tests/view/61abe9e456fcf33bce7d2834 LLR: 2.93 (-2.94,2.94) <0.00,2.50> Total: 31720 W: 8385 L: 8119 D: 15216 Ptnml(0-2): 117, 3534, 8273, 3838, 98 passed LTC: https://tests.stockfishchess.org/tests/view/61ac293756fcf33bce7d36cf LLR: 2.96 (-2.94,2.94) <0.50,3.00> Total: 136136 W: 35255 L: 34741 D: 66140 Ptnml(0-2): 114, 14217, 38894, 14727, 116 closes https://github.com/official-stockfish/Stockfish/pull/3836 Bench: 4667742 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 873a72dc40f..87cb65f36db 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-cdf1785602d6.nnue" + #define EvalFileDefaultName "nn-63376713ba63.nnue" namespace NNUE { From 4766dfc3956f78d853c5e0c4636d6f90fd93df9a Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Thu, 2 Dec 2021 12:29:11 +0100 Subject: [PATCH 0740/1766] Optimize FT activation and affine transform for NEON. This patch optimizes the NEON implementation in two ways. The activation layer after the feature transformer is rewritten to make it easier for the compiler to see through dependencies and unroll. This in itself is a minimal, but a positive improvement. Other architectures could benefit from this too in the future. This is not an algorithmic change. The affine transform for large matrices (first layer after FT) on NEON now utilizes the same optimized code path as >=SSSE3, which makes the memory accesses more sequential and makes better use of the available registers, which allows for code that has longer dependency chains. Benchmarks from Redshift#161, profile-build with apple clang george@Georges-MacBook-Air nets % ./stockfish-b82d93 bench 2>&1 | tail -4 (current master) =========================== Total time (ms) : 2167 Nodes searched : 4667742 Nodes/second : 2154011 george@Georges-MacBook-Air nets % ./stockfish-7377b8 bench 2>&1 | tail -4 (this patch) =========================== Total time (ms) : 1842 Nodes searched : 4667742 Nodes/second : 2534061 This is a solid 18% improvement overall, larger in a bench with NNUE-only, not mixed. Improvement is also observed on armv7-neon (Raspberry Pi, and older phones), around 5% speedup. No changes for architectures other than NEON. closes https://github.com/official-stockfish/Stockfish/pull/3837 No functional changes. --- src/Makefile | 8 +++- src/nnue/layers/affine_transform.h | 64 +++++++++++++++++------------ src/nnue/nnue_feature_transformer.h | 13 ++++-- src/simd.h | 39 ++++++++++++++++++ 4 files changed, 94 insertions(+), 30 deletions(-) diff --git a/src/Makefile b/src/Makefile index a9333a22f46..3cf97873d8a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -128,6 +128,7 @@ avx512 = no vnni256 = no vnni512 = no neon = no +arm_version = 0 STRIP = strip ### 2.2 Architecture specific @@ -275,6 +276,7 @@ ifeq ($(ARCH),armv7) arch = armv7 prefetch = yes bits = 32 + arm_version = 7 endif ifeq ($(ARCH),armv7-neon) @@ -283,6 +285,7 @@ ifeq ($(ARCH),armv7-neon) popcnt = yes neon = yes bits = 32 + arm_version = 7 endif ifeq ($(ARCH),armv8) @@ -290,6 +293,7 @@ ifeq ($(ARCH),armv8) prefetch = yes popcnt = yes neon = yes + arm_version = 8 endif ifeq ($(ARCH),apple-silicon) @@ -297,6 +301,7 @@ ifeq ($(ARCH),apple-silicon) prefetch = yes popcnt = yes neon = yes + arm_version = 8 endif ifeq ($(ARCH),ppc-32) @@ -614,7 +619,7 @@ ifeq ($(mmx),yes) endif ifeq ($(neon),yes) - CXXFLAGS += -DUSE_NEON + CXXFLAGS += -DUSE_NEON=$(arm_version) ifeq ($(KERNEL),Linux) ifneq ($(COMP),ndk) ifneq ($(arch),armv8) @@ -863,6 +868,7 @@ config-sanity: net @echo "vnni256: '$(vnni256)'" @echo "vnni512: '$(vnni512)'" @echo "neon: '$(neon)'" + @echo "arm_version: '$(arm_version)'" @echo "" @echo "Flags:" @echo "CXX: $(CXX)" diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index b28712780b2..11038d69b1c 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -75,8 +75,7 @@ namespace Stockfish::Eval::NNUE::Layers { const auto inputVector = reinterpret_cast(input); # elif defined(USE_NEON) - static_assert(PaddedInputDimensions % 16 == 0); - constexpr IndexType NumChunks = PaddedInputDimensions / 16; + constexpr IndexType NumChunks = (InputDimensions + 15) / 16; const auto inputVector = reinterpret_cast(input); # endif @@ -181,6 +180,9 @@ namespace Stockfish::Eval::NNUE::Layers { #elif defined (USE_SSSE3) static constexpr const IndexType InputSimdWidth = 16; static constexpr const IndexType MaxNumOutputRegs = 8; +#elif defined (USE_NEON) + static constexpr const IndexType InputSimdWidth = 8; + static constexpr const IndexType MaxNumOutputRegs = 8; #else // The fallback implementation will not have permuted weights. // We define these to avoid a lot of ifdefs later. @@ -270,52 +272,64 @@ namespace Stockfish::Eval::NNUE::Layers { OutputType* output = reinterpret_cast(buffer); #if defined (USE_AVX512) - using vec_t = __m512i; - #define vec_setzero _mm512_setzero_si512 - #define vec_set_32 _mm512_set1_epi32 - #define vec_add_dpbusd_32 Simd::m512_add_dpbusd_epi32 + using acc_vec_t = __m512i; + using bias_vec_t = __m128i; + using weight_vec_t = __m512i; + using in_vec_t = __m512i; + #define vec_zero _mm512_setzero_si512() #define vec_add_dpbusd_32x2 Simd::m512_add_dpbusd_epi32x2 #define vec_hadd Simd::m512_hadd #define vec_haddx4 Simd::m512_haddx4 #elif defined (USE_AVX2) - using vec_t = __m256i; - #define vec_setzero _mm256_setzero_si256 - #define vec_set_32 _mm256_set1_epi32 - #define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32 + using acc_vec_t = __m256i; + using bias_vec_t = __m128i; + using weight_vec_t = __m256i; + using in_vec_t = __m256i; + #define vec_zero _mm256_setzero_si256() #define vec_add_dpbusd_32x2 Simd::m256_add_dpbusd_epi32x2 #define vec_hadd Simd::m256_hadd #define vec_haddx4 Simd::m256_haddx4 #elif defined (USE_SSSE3) - using vec_t = __m128i; - #define vec_setzero _mm_setzero_si128 - #define vec_set_32 _mm_set1_epi32 - #define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32 + using acc_vec_t = __m128i; + using bias_vec_t = __m128i; + using weight_vec_t = __m128i; + using in_vec_t = __m128i; + #define vec_zero _mm_setzero_si128() #define vec_add_dpbusd_32x2 Simd::m128_add_dpbusd_epi32x2 #define vec_hadd Simd::m128_hadd #define vec_haddx4 Simd::m128_haddx4 +#elif defined (USE_NEON) + using acc_vec_t = int32x4_t; + using bias_vec_t = int32x4_t; + using weight_vec_t = int8x8_t; + using in_vec_t = int8x8_t; + #define vec_zero {0} + #define vec_add_dpbusd_32x2 Simd::neon_m128_add_dpbusd_epi32x2 + #define vec_hadd Simd::neon_m128_hadd + #define vec_haddx4 Simd::neon_m128_haddx4 #endif -#if defined (USE_SSSE3) - const vec_t* invec = reinterpret_cast(input); +#if defined (USE_SSSE3) || defined (USE_NEON) + const in_vec_t* invec = reinterpret_cast(input); // Perform accumulation to registers for each big block for (IndexType bigBlock = 0; bigBlock < NumBigBlocks; ++bigBlock) { - vec_t acc[NumOutputRegs] = { vec_setzero() }; + acc_vec_t acc[NumOutputRegs] = { vec_zero }; // Each big block has NumOutputRegs small blocks in each "row", one per register. // We process two small blocks at a time to save on one addition without VNNI. for (IndexType smallBlock = 0; smallBlock < NumSmallBlocksPerOutput; smallBlock += 2) { - const vec_t* weightvec = - reinterpret_cast( + const weight_vec_t* weightvec = + reinterpret_cast( weights + bigBlock * BigBlockSize + smallBlock * SmallBlockSize * NumOutputRegs); - const vec_t in0 = invec[smallBlock + 0]; - const vec_t in1 = invec[smallBlock + 1]; + const in_vec_t in0 = invec[smallBlock + 0]; + const in_vec_t in1 = invec[smallBlock + 1]; for (IndexType k = 0; k < NumOutputRegs; ++k) vec_add_dpbusd_32x2(acc[k], in0, weightvec[k], in1, weightvec[k + NumOutputRegs]); @@ -324,8 +338,8 @@ namespace Stockfish::Eval::NNUE::Layers { // Horizontally add all accumulators. if constexpr (NumOutputRegs % 4 == 0) { - __m128i* outputvec = reinterpret_cast<__m128i*>(output); - const __m128i* biasvec = reinterpret_cast(biases); + bias_vec_t* outputvec = reinterpret_cast(output); + const bias_vec_t* biasvec = reinterpret_cast(biases); for (IndexType k = 0; k < NumOutputRegs; k += 4) { @@ -343,9 +357,7 @@ namespace Stockfish::Eval::NNUE::Layers { } } -# undef vec_setzero -# undef vec_set_32 -# undef vec_add_dpbusd_32 +# undef vec_zero # undef vec_add_dpbusd_32x2 # undef vec_hadd # undef vec_haddx4 diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 0297b3233a1..4f6a174a486 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -336,10 +336,17 @@ namespace Stockfish::Eval::NNUE { { const IndexType offset = HalfDimensions * p; const auto out = reinterpret_cast(&output[offset]); - for (IndexType j = 0; j < NumChunks; ++j) + + constexpr IndexType UnrollFactor = 16; + static_assert(UnrollFactor % UnrollFactor == 0); + for (IndexType j = 0; j < NumChunks; j += UnrollFactor) { - int16x8_t sum = reinterpret_cast(accumulation[perspectives[p]])[j]; - out[j] = vmax_s8(vqmovn_s16(sum), Zero); + int16x8_t sums[UnrollFactor]; + for (IndexType i = 0; i < UnrollFactor; ++i) + sums[i] = reinterpret_cast(accumulation[perspectives[p]])[j+i]; + + for (IndexType i = 0; i < UnrollFactor; ++i) + out[j+i] = vmax_s8(vqmovn_s16(sums[i]), Zero); } } return psqt; diff --git a/src/simd.h b/src/simd.h index 1ac98067f5e..ffa54d9627b 100644 --- a/src/simd.h +++ b/src/simd.h @@ -343,6 +343,45 @@ namespace Stockfish::Simd { #endif +#if defined (USE_NEON) + + [[maybe_unused]] static int neon_m128_reduce_add_epi32(int32x4_t s) { +# if USE_NEON >= 8 + return vaddvq_s32(s); +# else + return s[0] + s[1] + s[2] + s[3]; +# endif + } + + [[maybe_unused]] static int neon_m128_hadd(int32x4_t sum, int bias) { + return neon_m128_reduce_add_epi32(sum) + bias; + } + + [[maybe_unused]] static int32x4_t neon_m128_haddx4( + int32x4_t sum0, int32x4_t sum1, int32x4_t sum2, int32x4_t sum3, + int32x4_t bias) { + + int32x4_t hsums { + neon_m128_reduce_add_epi32(sum0), + neon_m128_reduce_add_epi32(sum1), + neon_m128_reduce_add_epi32(sum2), + neon_m128_reduce_add_epi32(sum3) + }; + return vaddq_s32(hsums, bias); + } + + [[maybe_unused]] static void neon_m128_add_dpbusd_epi32x2( + int32x4_t& acc, + int8x8_t a0, int8x8_t b0, + int8x8_t a1, int8x8_t b1) { + + int16x8_t product = vmull_s8(a0, b0); + product = vmlal_s8(product, a1, b1); + acc = vpadalq_s16(acc, product); + } + +#endif + } #endif // STOCKFISH_SIMD_H_INCLUDED From c228f3196ae9965250f317c8784012c2485228c4 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Tue, 7 Dec 2021 19:46:21 +0300 Subject: [PATCH 0741/1766] Introduce post-lmr extensions This idea is somewhat similar to extentions in LMR but has a different flavour. If result of LMR was really good - thus exceeded alpha by some pretty big given margin, we can extend move after LMR in full depth search with 0 window. The idea is that this move is probably a fail high with somewhat of a big probability so extending it makes a lot of sense passed STC https://tests.stockfishchess.org/tests/view/61ad45ea56fcf33bce7d74b7 LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 59680 W: 15531 L: 15215 D: 28934 Ptnml(0-2): 193, 6711, 15734, 6991, 211 passed LTC https://tests.stockfishchess.org/tests/view/61ad9ff356fcf33bce7d8646 LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 59104 W: 15321 L: 14992 D: 28791 Ptnml(0-2): 53, 6023, 17065, 6364, 47 closes https://github.com/official-stockfish/Stockfish/pull/3838 bench 4881329 --- src/search.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index edeed9c0304..f3a96ae29fe 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1165,6 +1165,8 @@ namespace { // Step 15. Make the move pos.do_move(move, st, givesCheck); + bool doDeeperSearch = false; + // Step 16. Late moves reduction / extension (LMR, ~200 Elo) // We use various heuristics for the sons of a node after the first son has // been searched. In general we would like to reduce them, but there are many @@ -1237,6 +1239,7 @@ namespace { // If the son is reduced and fails high it will be re-searched at full depth doFullDepthSearch = value > alpha && d < newDepth; + doDeeperSearch = value > alpha + 88; didLMR = true; } else @@ -1248,7 +1251,7 @@ namespace { // Step 17. Full depth search when LMR is skipped or fails high if (doFullDepthSearch) { - value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode); + value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth + doDeeperSearch, !cutNode); // If the move passed LMR update its stats if (didLMR && !captureOrPromotion) From 94514199123874c0029afb6e00634f26741d90db Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Wed, 8 Dec 2021 12:23:39 +0100 Subject: [PATCH 0742/1766] Improve transposition table remplacement strategy Increase chance that PV node replaces old entry in transposition table. STC: LLR: 2.93 (-2.94,2.94) <0.00,2.50> Total: 46744 W: 12108 L: 11816 D: 22820 Ptnml(0-2): 156, 5221, 12344, 5477, 174 https://tests.stockfishchess.org/tests/view/61ae068356fcf33bce7d99d0 LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 88464 W: 22912 L: 22513 D: 43039 Ptnml(0-2): 84, 9133, 25393, 9544, 78 https://tests.stockfishchess.org/tests/view/61ae973656fcf33bce7db3e1 closes https://github.com/official-stockfish/Stockfish/pull/3839 Bench: 5292488 --- src/tt.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tt.cpp b/src/tt.cpp index 1f495ca9d12..4af6c9f11a5 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -40,9 +40,9 @@ void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) move16 = (uint16_t)m; // Overwrite less valuable entries (cheapest checks first) - if (b == BOUND_EXACT + if ( b == BOUND_EXACT || (uint16_t)k != key16 - || d - DEPTH_OFFSET > depth8 - 4) + || d - DEPTH_OFFSET + 2 * pv > depth8 - 4) { assert(d > DEPTH_OFFSET); assert(d < 256 + DEPTH_OFFSET); From 8e8234593190804babd36b8a8862e2bb1fc65bb7 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Thu, 9 Dec 2021 19:33:06 +0300 Subject: [PATCH 0743/1766] Adjust singular extension depth restriction This patch is a modification of original idea by lonfom169 which had a good yellow run - do singular extension search with depth threshold 6 unless this is a PvNode with is a part of a PV line - for them set threshold to 8 instead. Passed STC https://tests.stockfishchess.org/tests/view/61b1080406b4c2dcb1b1128c LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 84352 W: 21917 L: 21555 D: 40880 Ptnml(0-2): 288, 9524, 22185, 9896, 283 Passed LTC https://tests.stockfishchess.org/tests/view/61b1860a06b4c2dcb1b134a1 LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 63520 W: 16575 L: 16237 D: 30708 Ptnml(0-2): 27, 6519, 18350, 6817, 47 https://github.com/official-stockfish/Stockfish/pull/3840 bench 4729473 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index f3a96ae29fe..cded8614ad9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1089,7 +1089,7 @@ namespace { // 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 ( !rootNode - && depth >= 7 + && depth >= 6 + 2 * (PvNode && tte->is_pv()) && move == ttMove && !excludedMove // Avoid recursive singular search /* && ttValue != VALUE_NONE Already implicit in the next condition */ From 9db6ca8592c2c372cc28d8c463dd9eeca464e893 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 11 Dec 2021 15:23:55 +0100 Subject: [PATCH 0744/1766] Update Top CPU Contributors closes https://github.com/official-stockfish/Stockfish/pull/3842 No functional change --- Top CPU Contributors.txt | 424 ++++++++++++++++++++------------------- 1 file changed, 221 insertions(+), 203 deletions(-) diff --git a/Top CPU Contributors.txt b/Top CPU Contributors.txt index dacc5781b2d..f0ec51f9291 100644 --- a/Top CPU Contributors.txt +++ b/Top CPU Contributors.txt @@ -1,205 +1,223 @@ -Contributors to Fishtest with >10,000 CPU hours, as of Jun 29, 2021. +Contributors to Fishtest with >10,000 CPU hours, as of 2021-12-11. Thank you! -Username CPU Hours Games played ------------------------------------------------------ -noobpwnftw 27649494 1834734733 -mlang 1426107 89454622 -dew 1380910 82831648 -mibere 703840 46867607 -grandphish2 692707 41737913 -tvijlbrief 669642 42371594 -JojoM 597778 35297180 -TueRens 519226 31823562 -cw 458421 30307421 -fastgm 439667 25950040 -gvreuls 436599 28177460 -crunchy 427035 27344275 -CSU_Dynasty 374765 25106278 -Fisherman 326901 21822979 -ctoks 325477 21767943 -velislav 295343 18844324 -linrock 292789 10624427 -bcross 278584 19488961 -okrout 262818 13803272 -pemo 245982 11376085 -glinscott 217799 13780820 -leszek 212346 12959025 -nordlandia 211692 13484886 -bking_US 198894 11876016 -drabel 196463 13450602 -robal 195473 12375650 -mgrabiak 187226 12016564 -Dantist 183202 10990484 -Thanar 179852 12365359 -vdv 175274 9889046 -spams 157128 10319326 -marrco 150295 9402141 -sqrt2 147963 9724586 -mhoram 141278 8901241 -CoffeeOne 137100 5024116 -vdbergh 137041 8926915 -malala 136182 8002293 -xoto 133702 9156676 -davar 122092 7960001 -dsmith 122059 7570238 -Data 113305 8220352 -BrunoBanani 112960 7436849 -MaZePallas 102823 6633619 -sterni1971 100532 5880772 -ElbertoOne 99028 7023771 -brabos 92118 6186135 -oz 92100 6486640 -psk 89957 5984901 -amicic 89156 5392305 -sunu 88851 6028873 -Vizvezdenec 83761 5344740 -0x3C33 82614 5271253 -BRAVONE 81239 5054681 -racerschmacer 80899 5759262 -cuistot 80300 4606144 -nssy 76497 5259388 -teddybaer 75125 5407666 -Pking_cda 73776 5293873 -jromang 72192 5057715 -solarlight 70517 5028306 -dv8silencer 70287 3883992 -Bobo1239 68515 4652287 -manap 66273 4121774 -skiminki 65088 4023328 -tinker 64333 4268790 -sschnee 60767 3500800 -qurashee 57344 3168264 -robnjr 57262 4053117 -Freja 56938 3733019 -ttruscott 56010 3680085 -rkl 55132 4164467 -renouve 53811 3501516 -finfish 51360 3370515 -eva42 51272 3599691 -rap 49985 3219146 -pb00067 49727 3298270 -ronaldjerum 47654 3240695 -bigpen0r 47653 3335327 -eastorwest 47585 3221629 -biffhero 46564 3111352 -VoyagerOne 45476 3452465 -yurikvelo 44834 3034550 -speedycpu 43842 3003273 -jbwiebe 43305 2805433 -Spprtr 42279 2680153 -DesolatedDodo 42007 2447516 -Antihistamine 41788 2761312 -mhunt 41735 2691355 -homyur 39893 2850481 -gri 39871 2515779 -Fifis 38776 2529121 -oryx 38724 2966648 -SC 37290 2731014 -csnodgrass 36207 2688994 -jmdana 36157 2210661 -strelock 34716 2074055 -rpngn 33951 2057395 -Garf 33922 2751802 -EthanOConnor 33370 2090311 -slakovv 32915 2021889 -manapbk 30987 1810399 -Prcuvu 30377 2170122 -anst 30301 2190091 -jkiiski 30136 1904470 -hyperbolic.tom 29840 2017394 -Pyafue 29650 1902349 -Wolfgang 29260 1658936 -zeryl 28156 1579911 -OuaisBla 27636 1578800 -DMBK 27051 1999456 -chriswk 26902 1868317 -achambord 26582 1767323 -Patrick_G 26276 1801617 -yorkman 26193 1992080 -SFTUser 25182 1675689 -nabildanial 24942 1519409 -Sharaf_DG 24765 1786697 -ncfish1 24411 1520927 -rodneyc 24227 1409514 -agg177 23890 1395014 -JanErik 23408 1703875 -Isidor 23388 1680691 -Norabor 23164 1591830 -cisco2015 22897 1762669 -Zirie 22542 1472937 -team-oh 22272 1636708 -MazeOfGalious 21978 1629593 -sg4032 21947 1643265 -ianh2105 21725 1632562 -xor12 21628 1680365 -dex 21612 1467203 -nesoneg 21494 1463031 -sphinx 21211 1384728 -jjoshua2 21001 1423089 -horst.prack 20878 1465656 -Ente 20865 1477066 -0xB00B1ES 20590 1208666 -j3corre 20405 941444 -Adrian.Schmidt123 20316 1281436 -wei 19973 1745989 -MaxKlaxxMiner 19850 1009176 -rstoesser 19569 1293588 -gopeto 19491 1174952 -eudhan 19274 1283717 -jundery 18445 1115855 -megaman7de 18377 1067540 -iisiraider 18247 1101015 -ville 17883 1384026 -chris 17698 1487385 -purplefishies 17595 1092533 -dju 17353 978595 -DragonLord 17014 1162790 -IgorLeMasson 16064 1147232 -ako027ako 15671 1173203 -chuckstablers 15289 891576 -Nikolay.IT 15154 1068349 -Andrew Grant 15114 895539 -OssumOpossum 14857 1007129 -Karby 14808 867120 -enedene 14476 905279 -bpfliegel 14298 884523 -mpx86 14019 759568 -jpulman 13982 870599 -crocogoat 13803 1117422 -joster 13794 950160 -Nesa92 13786 1114691 -Hjax 13535 915487 -jsys14 13459 785000 -Dark_wizzie 13422 1007152 -mabichito 12903 749391 -thijsk 12886 722107 -AdrianSA 12860 804972 -Flopzee 12698 894821 -fatmurphy 12547 853210 -Rudolphous 12520 832340 -scuzzi 12511 845761 -SapphireBrand 12416 969604 -modolief 12386 896470 -Machariel 12335 810784 -pgontarz 12151 848794 -stocky 11954 699440 -mschmidt 11941 803401 -Maxim 11543 836024 -infinity 11470 727027 -torbjo 11395 729145 -Thomas A. Anderson 11372 732094 -savage84 11358 670860 -d64 11263 789184 -MooTheCow 11237 720174 -snicolet 11106 869170 -ali-al-zhrani 11086 767926 -AndreasKrug 10875 887457 -pirt 10806 836519 -basepi 10637 744851 -michaelrpg 10508 739039 -dzjp 10343 732529 -aga 10302 622975 -ols 10259 570669 -lbraesch 10252 647825 -FormazChar 10059 757283 +Username CPU Hours Games played +------------------------------------------------------------------ +noobpwnftw 29832955 2061725095 +mlang 2345848 162775694 +dew 1559902 93935940 +technologov 1154365 43136020 +grandphish2 988545 60725563 +tvijlbrief 795993 51894442 +TueRens 716909 45503672 +mibere 703840 46867607 +JojoM 675708 41046762 +okrout 648513 49953162 +linrock 563226 16184599 +pemo 520578 26816487 +gvreuls 501232 32662374 +cw 495103 33171295 +fastgm 476917 28660004 +crunchy 427035 27344275 +CSU_Dynasty 407300 27580858 +ctoks 386452 25757397 +oz 343309 25441096 +Fisherman 327231 21829379 +velislav 320318 20642978 +bcross 319459 22485737 +leszek 277442 17495865 +Dantist 229980 14568674 +mgrabiak 225684 14774260 +glinscott 217799 13780820 +nordlandia 211692 13484886 +robal 209847 13427680 +drabel 200340 13727458 +bking_US 198894 11876016 +Thanar 179852 12365359 +vdv 175274 9889046 +mhoram 166293 10796647 +spams 157128 10319326 +marrco 150300 9402229 +sqrt2 147963 9724586 +vdbergh 137186 8938965 +CoffeeOne 137100 5024116 +malala 136182 8002293 +xoto 133759 9159372 +davar 122092 7960001 +dsmith 122059 7570238 +amicic 119659 7937885 +Data 113305 8220352 +BrunoBanani 112960 7436849 +rpngn 109031 7392547 +CypressChess 108321 7759588 +MaZePallas 102823 6633619 +sterni1971 100532 5880772 +sunu 100167 7040199 +ElbertoOne 99028 7023771 +skiminki 98121 6478170 +brabos 92118 6186135 +psk 89957 5984901 +cuistot 88420 5225234 +DesolatedDodo 88356 5779482 +racerschmacer 85711 6119610 +Vizvezdenec 83761 5344740 +0x3C33 82614 5271253 +BRAVONE 81239 5054681 +nssy 76497 5259388 +teddybaer 75125 5407666 +Pking_cda 73776 5293873 +jromang 72192 5057715 +solarlight 70517 5028306 +dv8silencer 70287 3883992 +Bobo1239 68515 4652287 +manap 66273 4121774 +sschnee 64563 3633680 +tinker 64333 4268790 +zeryl 63290 4179159 +qurashee 61208 3429862 +yurikvelo 60387 4169900 +robnjr 57262 4053117 +Freja 56938 3733019 +ttruscott 56010 3680085 +rkl 55132 4164467 +renouve 53811 3501516 +finfish 51360 3370515 +eva42 51272 3599691 +Wolfgang 51248 3218932 +eastorwest 50311 3409935 +rap 49985 3219146 +pb00067 49727 3298270 +bigpen0r 47667 3336927 +ronaldjerum 47654 3240695 +MaxKlaxxMiner 47584 2972142 +biffhero 46564 3111352 +Spprtr 45877 2995437 +Fifis 45843 3088497 +VoyagerOne 45476 3452465 +speedycpu 43842 3003273 +jbwiebe 43305 2805433 +Antihistamine 41788 2761312 +mhunt 41735 2691355 +megaman7de 40060 2625050 +homyur 39893 2850481 +gri 39871 2515779 +oryx 38860 2976488 +SC 37299 2731694 +csnodgrass 36207 2688994 +jmdana 36157 2210661 +Garf 36113 2897580 +strelock 34716 2074055 +EthanOConnor 33370 2090311 +slakovv 32915 2021889 +manapbk 30987 1810399 +DMBK 30675 2383552 +Prcuvu 30377 2170122 +anst 30301 2190091 +jkiiski 30136 1904470 +hyperbolic.tom 29840 2017394 +chuckstablers 29659 2093438 +Pyafue 29650 1902349 +tolkki963 28171 1716386 +OuaisBla 27636 1578800 +armo9494 27224 2221042 +chriswk 26902 1868317 +achambord 26582 1767323 +gopeto 26355 1717722 +Patrick_G 26276 1801617 +yorkman 26193 1992080 +SFTUser 25182 1675689 +nabildanial 24942 1519409 +Sharaf_DG 24765 1786697 +ncfish1 24411 1520927 +rodneyc 24275 1410450 +agg177 23890 1395014 +JanErik 23408 1703875 +Isidor 23388 1680691 +Norabor 23339 1602636 +cisco2015 22897 1762669 +Zirie 22542 1472937 +Ente 22486 1606268 +team-oh 22272 1636708 +MazeOfGalious 21978 1629593 +sg4032 21947 1643265 +ianh2105 21725 1632562 +xor12 21628 1680365 +dex 21612 1467203 +nesoneg 21494 1463031 +sphinx 21211 1384728 +jjoshua2 21001 1423089 +horst.prack 20878 1465656 +0xB00B1ES 20590 1208666 +j3corre 20405 941444 +Adrian.Schmidt123 20316 1281436 +wei 19973 1745989 +rstoesser 19569 1293588 +eudhan 19274 1283717 +vulcan 18871 1729392 +user213718 18590 1271128 +jundery 18445 1115855 +iisiraider 18247 1101015 +ville 17883 1384026 +chris 17698 1487385 +purplefishies 17595 1092533 +dju 17353 978595 +DragonLord 17014 1162790 +IgorLeMasson 16064 1147232 +ako027ako 15671 1173203 +Nikolay.IT 15154 1068349 +Andrew Grant 15114 895539 +OssumOpossum 14857 1007129 +Karby 14808 867120 +enedene 14476 905279 +jsys14 14318 843704 +bpfliegel 14298 884523 +mpx86 14019 759568 +jpulman 13982 870599 +kdave 13933 1045550 +crocogoat 13803 1117422 +joster 13794 950160 +Nesa92 13786 1114691 +mbeier 13650 1044928 +AndreasKrug 13624 1090613 +Hjax 13535 915487 +Dark_wizzie 13422 1007152 +Ulysses 13392 1021264 +Calis007 13267 873236 +Rudolphous 13244 883140 +spcc 13085 917006 +Machariel 13010 863104 +mabichito 12903 749391 +thijsk 12886 722107 +AdrianSA 12860 804972 +Flopzee 12698 894821 +fatmurphy 12547 853210 +scuzzi 12511 845761 +SapphireBrand 12416 969604 +modolief 12386 896470 +fishtester 12320 873882 +Farseer 12249 694108 +pgontarz 12151 848794 +stocky 11954 699440 +mschmidt 11941 803401 +Maxim 11543 836024 +infinity 11470 727027 +aga 11409 695071 +torbjo 11395 729145 +pirt 11392 886291 +Thomas A. Anderson 11372 732094 +savage84 11358 670860 +FormazChar 11304 847663 +d64 11263 789184 +dbernier 11258 805102 +MooTheCow 11237 720174 +snicolet 11106 869170 +ali-al-zhrani 11098 768494 +basepi 10637 744851 +Cubox 10621 826448 +michaelrpg 10509 739239 +OIVAS7572 10420 995586 +dzjp 10343 732529 +infinigon 10319 776158 +Garruk 10314 702629 +ols 10259 570669 +lbraesch 10252 647825 From d579db34a3f7a26cd5502e79dafde2a80614d645 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Fri, 10 Dec 2021 18:42:05 +0100 Subject: [PATCH 0745/1766] Simplify falling eval time factor. Remove the difference to previous best score in falling eval calculation. As compensation double the effect of the difference to previous best average score. STC: LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 86944 W: 22363 L: 22285 D: 42296 Ptnml(0-2): 273, 9227, 24396, 9301, 275 https://tests.stockfishchess.org/tests/view/61b111ce06b4c2dcb1b11546 LTC: LLR: 2.96 (-2.94,2.94) <-2.25,0.25> Total: 134944 W: 34606 L: 34596 D: 65742 Ptnml(0-2): 66, 12941, 41456, 12935, 74 https://tests.stockfishchess.org/tests/view/61b19ca206b4c2dcb1b13a8b closes https://github.com/official-stockfish/Stockfish/pull/3841 Bench: 4729473 --- src/search.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index cded8614ad9..687bf0a9d2f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -490,9 +490,8 @@ void Thread::search() { && !Threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (142 + 6 * (mainThread->bestPreviousScore - bestValue) - + 6 * (mainThread->bestPreviousAverageScore - bestValue) - + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 825.0; + double fallingEval = (142 + 12 * (mainThread->bestPreviousAverageScore - bestValue) + + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 825.0; fallingEval = std::clamp(fallingEval, 0.5, 1.5); // If the bestMove is stable over several iterations, reduce time accordingly From ea1ddb6aef8bb6ee3056988af7cfd32b2f52f960 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 12 Dec 2021 17:09:21 +0100 Subject: [PATCH 0746/1766] Update default net to nn-d93927199b3d.nnue Using the same dataset as before but slightly reduced initial LR as in https://github.com/vondele/nnue-pytorch/tree/tweakLR1 passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 51368 W: 13492 L: 13191 D: 24685 Ptnml(0-2): 168, 5767, 13526, 6042, 181 https://tests.stockfishchess.org/tests/view/61b61f43dffbe89a3580b529 passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 45128 W: 11763 L: 11469 D: 21896 Ptnml(0-2): 24, 4583, 13063, 4863, 31 https://tests.stockfishchess.org/tests/view/61b6612edffbe89a3580c447 closes https://github.com/official-stockfish/Stockfish/pull/3848 Bench: 5121336 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 87cb65f36db..5a9260fbbc7 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-63376713ba63.nnue" + #define EvalFileDefaultName "nn-d93927199b3d.nnue" namespace NNUE { From c6edf33f539f160d4a7009f3aac25e7ec5668eda Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Tue, 7 Dec 2021 23:03:35 +0100 Subject: [PATCH 0747/1766] Remove NNUE scaling term remove pawns scaling, probably correlated with piece scaling, and might be less useful with the recent improved nets. Might allow for another tune of the scaling params. passed STC https://tests.stockfishchess.org/tests/view/61afdb2e56fcf33bce7df31a LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 280864 W: 72198 L: 72399 D: 136267 Ptnml(0-2): 854, 32356, 74346, 31889, 987 passed LTC https://tests.stockfishchess.org/tests/view/61b233a606b4c2dcb1b16140 LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 400136 W: 102669 L: 103012 D: 194455 Ptnml(0-2): 212, 42005, 116047, 41522, 282 closes https://github.com/official-stockfish/Stockfish/pull/3851 Bench: 4735679 --- src/evaluate.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 1dc701cfe09..24445f48292 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1091,8 +1091,7 @@ Value Eval::evaluate(const Position& pos) { v = Evaluation(pos).value(); // classical else { - int scale = 1049 - + 8 * pos.count() + int scale = 1136 + 20 * pos.non_pawn_material() / 1024; Value nnue = NNUE::evaluate(pos, true); // NNUE From 3bea736a2a63247d242316bd25d8901b1414d9c0 Mon Sep 17 00:00:00 2001 From: farseer Date: Wed, 15 Dec 2021 16:28:16 -0800 Subject: [PATCH 0748/1766] Update default net to nn-4401e826ebcc.nnue Using data T60 12/1/20 to 11/2/2021, T74 4/22/21 to 7/27/21, T75 6/3/21 to 10/16/21, T76 (half of the randomly interleaved dataset due to a mistake merging) 11/10/21 to 11/21/21, wrongIsRight_nodes5000pv2.binpack, and WrongIsRight-Reloaded.binpack combined and shuffled position by position. Trained with LR=4.375e-4 and WDL filtering enabled: python train.py --smart-fen-skipping --random-fen-skipping 0 --features=HalfKAv2_hm^ --lambda=1.0 --max_epochs=800 --seed 910688689 --batch-size 16384 --progress_bar_refresh_rate 30 --threads 4 --num-workers 4 --gpus 1 --resume-from-model C:\msys64\home\Mike\nnue-pytorch\9b3d.pt E:\trainingdata\T60-T74-T75-T76-WiR-WiRR-PbyP.binpack E:\trainingdata\T60-T74-T75-T76-WiR-WiRR-PbyP.binpack Passed STC LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 41848 W: 10962 L: 10676 D: 20210 Elo +2.16 Ptnml(0-2): 142, 4699, 11016, 4865, 202 https://tests.stockfishchess.org/tests/view/61ba886857a0d0f327c2cfd6 Passed LTC LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 27776 W: 7208 L: 6953 D: 13615 Elo + 3.00 Ptnml(0-2): 14, 2808, 8007, 3027, 32 https://tests.stockfishchess.org/tests/view/61baae4d57a0d0f327c2d96f closes https://github.com/official-stockfish/Stockfish/pull/3856 Bench: 4667591 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 5a9260fbbc7..e258fdb6bab 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-d93927199b3d.nnue" + #define EvalFileDefaultName "nn-4401e826ebcc.nnue" namespace NNUE { From 0889210262ab5a14bc6fcd261125b919120637a0 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Tue, 14 Dec 2021 12:00:45 -0300 Subject: [PATCH 0749/1766] Simplify away singularQuietLMR While at it, we also update the Elo estimate of reduction at non-PV nodes (source: https://tests.stockfishchess.org/tests/view/61acf97156fcf33bce7d6303 ) STC: LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 243632 W: 62874 L: 63022 D: 117736 Ptnml(0-2): 810, 28024, 64249, 27970, 763 https://tests.stockfishchess.org/tests/view/61b8b1b7dffbe89a35814c0d LTC: LLR: 2.93 (-2.94,2.94) <-2.25,0.25> Total: 91392 W: 23520 L: 23453 D: 44419 Ptnml(0-2): 51, 9568, 26387, 9643, 47 https://tests.stockfishchess.org/tests/view/61b97316dffbe89a35817da7 closes https://github.com/official-stockfish/Stockfish/pull/3854 bench: 4217785 --- src/search.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 687bf0a9d2f..01d97646206 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -590,8 +590,7 @@ namespace { Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue, probCutBeta; bool givesCheck, improving, didLMR, priorCapture; - bool captureOrPromotion, doFullDepthSearch, moveCountPruning, - ttCapture, singularQuietLMR; + bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture; Piece movedPiece; int moveCount, captureCount, quietCount, bestMoveCount, improvement; @@ -982,7 +981,7 @@ namespace { ss->ply); value = bestValue; - singularQuietLMR = moveCountPruning = false; + moveCountPruning = false; // Indicate PvNodes that will probably fail low if the node was searched // at a depth equal or greater than the current depth, and the result of this search was a fail low. @@ -1106,7 +1105,6 @@ namespace { if (value < singularBeta) { extension = 1; - singularQuietLMR = !ttCapture; // Avoid search explosion by limiting the number of double extensions if ( !PvNode @@ -1190,7 +1188,7 @@ namespace { && !likelyFailLow) r -= 2; - // Increase reduction at non-PV nodes + // Increase reduction at non-PV nodes (~3 Elo) if (!PvNode) r++; @@ -1198,10 +1196,6 @@ namespace { if ((ss-1)->moveCount > 13) r--; - // Decrease reduction if ttMove has been singularly extended (~1 Elo) - if (singularQuietLMR) - r--; - // Increase reduction for cut nodes (~3 Elo) if (cutNode && move != ss->killers[0]) r += 2; From dc5d9bdfee70f4267d9a49ad71e5ee478dd50ca5 Mon Sep 17 00:00:00 2001 From: pb00067 Date: Wed, 15 Dec 2021 08:49:44 +0100 Subject: [PATCH 0750/1766] Remove lowPly history Seems that after pull request #3731 (Capping stat bonus at 2000) this heuristic is no longer useful. STC: https://tests.stockfishchess.org/tests/view/61b8d0e2dffbe89a35815444 LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 30672 W: 7974 L: 7812 D: 14886 Ptnml(0-2): 106, 3436, 8072, 3634, 88 LTC: https://tests.stockfishchess.org/tests/view/61b8e90cdffbe89a35815a67 LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 42448 W: 10884 L: 10751 D: 20813 Ptnml(0-2): 23, 4394, 12267, 4507, 33 closes https://github.com/official-stockfish/Stockfish/pull/3853 bench: 4474950 --- src/movepick.cpp | 28 ++++++++++++++++------------ src/movepick.h | 18 ++++-------------- src/search.cpp | 27 +++++---------------------- src/thread.cpp | 1 - src/thread.h | 1 - 5 files changed, 25 insertions(+), 50 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 20640fe2b02..6aa1954b5ae 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -56,11 +56,14 @@ namespace { /// ordering is at the current node. /// MovePicker constructor for the main search -MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const LowPlyHistory* lp, - const CapturePieceToHistory* cph, const PieceToHistory** ch, Move cm, const Move* killers, int pl) - : pos(p), mainHistory(mh), lowPlyHistory(lp), captureHistory(cph), continuationHistory(ch), - ttMove(ttm), refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d), ply(pl) { - +MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, + const CapturePieceToHistory* cph, + const PieceToHistory** ch, + Move cm, + const Move* killers) + : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), + ttMove(ttm), refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d) +{ assert(d > 0); stage = (pos.checkers() ? EVASION_TT : MAIN_TT) + @@ -69,9 +72,11 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist /// MovePicker constructor for quiescence search MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, - const CapturePieceToHistory* cph, const PieceToHistory** ch, Square rs) - : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), ttMove(ttm), recaptureSquare(rs), depth(d) { - + const CapturePieceToHistory* cph, + const PieceToHistory** ch, + Square rs) + : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), ttMove(ttm), recaptureSquare(rs), depth(d) +{ assert(d <= 0); stage = (pos.checkers() ? EVASION_TT : QSEARCH_TT) + @@ -83,8 +88,8 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist /// MovePicker constructor for ProbCut: we generate captures with SEE greater /// than or equal to the given threshold. MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph) - : pos(p), captureHistory(cph), ttMove(ttm), threshold(th) { - + : pos(p), captureHistory(cph), ttMove(ttm), threshold(th) +{ assert(!pos.checkers()); stage = PROBCUT_TT + !(ttm && pos.capture(ttm) @@ -110,8 +115,7 @@ void MovePicker::score() { + 2 * (*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)] - + (ply < MAX_LPH ? 6 * (*lowPlyHistory)[ply][from_to(m)] : 0); + + (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)]; else // Type == EVASIONS { diff --git a/src/movepick.h b/src/movepick.h index 7d78886fb64..426bac89bd9 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -88,12 +88,6 @@ enum StatsType { NoCaptures, Captures }; /// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards typedef Stats ButterflyHistory; -/// At higher depths LowPlyHistory records successful quiet moves near the root -/// and quiet moves which are/were in the PV (ttPv). LowPlyHistory is populated during -/// iterative deepening and at each new search the data is shifted down by 2 plies -constexpr int MAX_LPH = 4; -typedef Stats LowPlyHistory; - /// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous /// move, see www.chessprogramming.org/Countermove_Heuristic typedef Stats CounterMoveHistory; @@ -123,18 +117,16 @@ class MovePicker { public: MovePicker(const MovePicker&) = delete; MovePicker& operator=(const MovePicker&) = delete; - MovePicker(const Position&, Move, Value, const CapturePieceToHistory*); MovePicker(const Position&, Move, Depth, const ButterflyHistory*, const CapturePieceToHistory*, const PieceToHistory**, - Square); + Move, + const Move*); MovePicker(const Position&, Move, Depth, const ButterflyHistory*, - const LowPlyHistory*, const CapturePieceToHistory*, const PieceToHistory**, - Move, - const Move*, - int); + Square); + MovePicker(const Position&, Move, Value, const CapturePieceToHistory*); Move next_move(bool skipQuiets = false); private: @@ -145,7 +137,6 @@ class MovePicker { const Position& pos; const ButterflyHistory* mainHistory; - const LowPlyHistory* lowPlyHistory; const CapturePieceToHistory* captureHistory; const PieceToHistory** continuationHistory; Move ttMove; @@ -154,7 +145,6 @@ class MovePicker { Square recaptureSquare; Value threshold; Depth depth; - int ply; ExtMove moves[MAX_MOVES]; }; diff --git a/src/search.cpp b/src/search.cpp index 01d97646206..2abfbaf2f3d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -141,7 +141,7 @@ namespace { Value value_from_tt(Value v, int ply, int r50c); void update_pv(Move* pv, Move move, Move* childPv); void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus); - void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus, int depth); + void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus); void update_all_stats(const Position& pos, Stack* ss, Move bestMove, Value bestValue, Value beta, Square prevSq, Move* quietsSearched, int quietCount, Move* capturesSearched, int captureCount, Depth depth); @@ -317,9 +317,6 @@ void Thread::search() { mainThread->iterValue[i] = mainThread->bestPreviousScore; } - std::copy(&lowPlyHistory[2][0], &lowPlyHistory.back().back() + 1, &lowPlyHistory[0][0]); - std::fill(&lowPlyHistory[MAX_LPH - 2][0], &lowPlyHistory.back().back() + 1, 0); - size_t multiPV = size_t(Options["MultiPV"]); Skill skill(Options["Skill Level"], Options["UCI_LimitStrength"] ? int(Options["UCI_Elo"]) : 0); @@ -666,14 +663,6 @@ namespace { if (!excludedMove) ss->ttPv = PvNode || (ss->ttHit && tte->is_pv()); - // Update low ply history for previous move if we are near root and position is or has been in PV - if ( ss->ttPv - && depth > 12 - && ss->ply - 1 < MAX_LPH - && !priorCapture - && is_ok((ss-1)->currentMove)) - thisThread->lowPlyHistory[ss->ply - 1][from_to((ss-1)->currentMove)] << stat_bonus(depth - 5); - // At non-PV nodes we check for an early TT cutoff if ( !PvNode && ss->ttHit @@ -689,7 +678,7 @@ namespace { { // Bonus for a quiet ttMove that fails high if (!ttCapture) - update_quiet_stats(pos, ss, ttMove, stat_bonus(depth), depth); + update_quiet_stats(pos, ss, ttMove, stat_bonus(depth)); // Extra penalty for early quiet moves of the previous ply if ((ss-1)->moveCount <= 2 && !priorCapture) @@ -973,12 +962,10 @@ namespace { Move countermove = thisThread->counterMoves[pos.piece_on(prevSq)][prevSq]; MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, - &thisThread->lowPlyHistory, &captureHistory, contHist, countermove, - ss->killers, - ss->ply); + ss->killers); value = bestValue; moveCountPruning = false; @@ -1708,7 +1695,7 @@ namespace { if (!pos.capture_or_promotion(bestMove)) { // Increase stats for the best move in case it was a quiet move - update_quiet_stats(pos, ss, bestMove, bonus2, depth); + update_quiet_stats(pos, ss, bestMove, bonus2); // Decrease stats for all non-best quiet moves for (int i = 0; i < quietCount; ++i) @@ -1755,7 +1742,7 @@ namespace { // update_quiet_stats() updates move sorting heuristics - void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus, int depth) { + void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus) { // Update killers if (ss->killers[0] != move) @@ -1775,10 +1762,6 @@ namespace { Square prevSq = to_sq((ss-1)->currentMove); thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] = move; } - - // Update low ply history - if (depth > 11 && ss->ply < MAX_LPH) - thisThread->lowPlyHistory[ss->ply][from_to(move)] << stat_bonus(depth - 7); } // When playing with strength handicap, choose best move among a set of RootMoves diff --git a/src/thread.cpp b/src/thread.cpp index ed3accc5f0b..099efbad24c 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -59,7 +59,6 @@ void Thread::clear() { counterMoves.fill(MOVE_NONE); mainHistory.fill(0); - lowPlyHistory.fill(0); captureHistory.fill(0); for (bool inCheck : { false, true }) diff --git a/src/thread.h b/src/thread.h index 37c6452b9b7..6bc1be658f8 100644 --- a/src/thread.h +++ b/src/thread.h @@ -77,7 +77,6 @@ class Thread { Value rootDelta; CounterMoveHistory counterMoves; ButterflyHistory mainHistory; - LowPlyHistory lowPlyHistory; CapturePieceToHistory captureHistory; ContinuationHistory continuationHistory[2][2]; Score trend; From 939b694bfda27017c34a8cdccc81f2bb2ef44079 Mon Sep 17 00:00:00 2001 From: George Sobala Date: Mon, 13 Dec 2021 15:29:31 +0000 Subject: [PATCH 0751/1766] Fix for profile-build failure using gcc on MacOS Fixes https://github.com/official-stockfish/Stockfish/issues/3846 , where the profiling SF binary generated by GCC on MacOS would launch but failed to quit. Tested with gcc-8, gcc9, gcc10, gcc-11. The problem can be fixed by adding -fvisibility=hidden to the compiler flags, see for example the following piece of Apple documentation: https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/CppRuntimeEnv/Articles/SymbolVisibility.html For instance this now works: make -j8 profile-build ARCH=x86-64-avx2 COMP=gcc COMPCXX=g++-11 No functional change --- src/Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Makefile b/src/Makefile index 3cf97873d8a..5e2637729a1 100644 --- a/src/Makefile +++ b/src/Makefile @@ -459,6 +459,9 @@ else ifeq ($(comp),clang) else profile_make = gcc-profile-make profile_use = gcc-profile-use + ifeq ($(KERNEL),Darwin) + EXTRAPROFILEFLAGS = -fvisibility=hidden + endif endif ### Travis CI script uses COMPILER to overwrite CXX @@ -920,12 +923,14 @@ gcc-profile-make: @mkdir -p profdir $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ EXTRACXXFLAGS='-fprofile-generate=profdir' \ + EXTRACXXFLAGS+=$(EXTRAPROFILEFLAGS) \ EXTRALDFLAGS='-lgcov' \ all gcc-profile-use: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ EXTRACXXFLAGS='-fprofile-use=profdir -fno-peel-loops -fno-tracer' \ + EXTRACXXFLAGS+=$(EXTRAPROFILEFLAGS) \ EXTRALDFLAGS='-lgcov' \ all From 0a318cdddf8b6bdd05c2e0ee9b3b61a031d398ed Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 18 Dec 2021 11:51:19 +0300 Subject: [PATCH 0752/1766] Adjust reductions based on current node delta and root delta This patch is a follow up of previous 2 patches that introduced more reductions for PV nodes with low delta and more pruning for nodes with low delta. Instead of writing separate heuristics now it adjust reductions based on delta / rootDelta - it allows to remove 3 separate adjustements of pruning/LMR in different places and also makes reduction dependence on delta and rootDelta smoother. Also now it works for all pruning heuristics and not just 2. Passed STC https://tests.stockfishchess.org/tests/view/61ba9b6c57a0d0f327c2d48b LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 79192 W: 20513 L: 20163 D: 38516 Ptnml(0-2): 238, 8900, 21024, 9142, 292 passed LTC https://tests.stockfishchess.org/tests/view/61baf77557a0d0f327c2eb8e LLR: 2.96 (-2.94,2.94) <0.50,3.00> Total: 158400 W: 41134 L: 40572 D: 76694 Ptnml(0-2): 101, 16372, 45745, 16828, 154 closes https://github.com/official-stockfish/Stockfish/pull/3862 bench 4651538 --- src/search.cpp | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 2abfbaf2f3d..8a4db351859 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -69,9 +69,9 @@ namespace { // Reductions lookup table, initialized at startup int Reductions[MAX_MOVES]; // [depth or moveNumber] - Depth reduction(bool i, Depth d, int mn, bool rangeReduction) { + Depth reduction(bool i, Depth d, int mn, bool rangeReduction, Value delta, Value rootDelta) { int r = Reductions[d] * Reductions[mn]; - return (r + 534) / 1024 + (!i && r > 904) + rangeReduction; + return (r + 1358 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 904) + rangeReduction; } constexpr int futility_move_count(bool improving, Depth depth) { @@ -1015,6 +1015,8 @@ namespace { // Calculate new depth for this move newDepth = depth - 1; + Value delta = beta - alpha; + // Step 13. Pruning at shallow depth (~200 Elo). Depth conditions are important for mate finding. if ( !rootNode && pos.non_pawn_material(us) @@ -1024,7 +1026,7 @@ namespace { moveCountPruning = moveCount >= futility_move_count(improving, depth); // Reduced depth of the next LMR search - int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount, rangeReduction > 2), 0); + int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount, rangeReduction > 2, delta, thisThread->rootDelta), 0); if ( captureOrPromotion || givesCheck) @@ -1052,8 +1054,6 @@ namespace { history += thisThread->mainHistory[us][from_to(move)]; - lmrDepth = std::max(0, lmrDepth - (beta - alpha < thisThread->rootDelta / 4)); - // Futility pruning: parent node (~5 Elo) if ( !ss->inCheck && lmrDepth < 8 @@ -1161,12 +1161,11 @@ namespace { || !captureOrPromotion || (cutNode && (ss-1)->moveCount > 1))) { - Depth r = reduction(improving, depth, moveCount, rangeReduction > 2); + Depth r = reduction(improving, depth, moveCount, rangeReduction > 2, delta, thisThread->rootDelta); // Decrease reduction at some PvNodes (~2 Elo) if ( PvNode - && bestMoveCount <= 3 - && beta - alpha >= thisThread->rootDelta / 4) + && bestMoveCount <= 3) r--; // Decrease reduction if position is or has been on the PV @@ -1175,10 +1174,6 @@ namespace { && !likelyFailLow) r -= 2; - // Increase reduction at non-PV nodes (~3 Elo) - if (!PvNode) - r++; - // Decrease reduction if opponent's move count is high (~1 Elo) if ((ss-1)->moveCount > 13) r--; From fb7d3ab32ebd7a6514bded459b4aa442276f6cda Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 19 Dec 2021 02:30:45 +0300 Subject: [PATCH 0753/1766] Reintroduce futility pruning for captures This is a reintroduction of an idea that was simplified away approximately 1 year ago. There are some tweaks to it : a) exclude promotions; b) exclude Pv Nodes from it - Pv Nodes logic for captures is really different from non Pv nodes so it makes a lot of sense; c) use a big grain of capture history - idea is taken from my recent patches in futility pruning. passed STC https://tests.stockfishchess.org/tests/view/61bd90f857a0d0f327c373b7 LLR: 2.96 (-2.94,2.94) <0.00,2.50> Total: 86640 W: 22474 L: 22110 D: 42056 Ptnml(0-2): 268, 9732, 22963, 10082, 275 passed LTC https://tests.stockfishchess.org/tests/view/61be094457a0d0f327c38aa3 LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 23240 W: 6079 L: 5838 D: 11323 Ptnml(0-2): 14, 2261, 6824, 2512, 9 https://github.com/official-stockfish/Stockfish/pull/3864 bench 4493723 --- src/search.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 8a4db351859..d2ceee9fd1a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1037,6 +1037,16 @@ namespace { && captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] < 0) continue; + // Futility pruning for captures + if ( !pos.empty(to_sq(move)) + && !givesCheck + && !PvNode + && lmrDepth < 6 + && !ss->inCheck + && ss->staticEval + 342 + 238 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] + + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 8 < alpha) + continue; + // SEE based pruning if (!pos.see_ge(move, Value(-218) * depth)) // (~25 Elo) continue; From ca51b456493a4663b2d0f82273f67a3e499244a1 Mon Sep 17 00:00:00 2001 From: George Sobala Date: Mon, 13 Dec 2021 16:05:35 +0000 Subject: [PATCH 0754/1766] Fixes build failure on Apple M1 Silicon This pull request selectively avoids `-mdynamic-no-pic` for gcc on Apple Silicon (there was no problem with the default clang compiler). fixes https://github.com/official-stockfish/Stockfish/issues/3847 closes https://github.com/official-stockfish/Stockfish/pull/3850 No functional change --- src/Makefile | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Makefile b/src/Makefile index 5e2637729a1..f00df79fa41 100644 --- a/src/Makefile +++ b/src/Makefile @@ -522,11 +522,17 @@ ifeq ($(optimize),yes) endif endif - ifeq ($(comp),$(filter $(comp),gcc clang icc)) - ifeq ($(KERNEL),Darwin) - CXXFLAGS += -mdynamic-no-pic - endif - endif + ifeq ($(KERNEL),Darwin) + ifeq ($(comp),$(filter $(comp),clang icc)) + CXXFLAGS += -mdynamic-no-pic + endif + + ifeq ($(comp),gcc) + ifneq ($(arch),arm64) + CXXFLAGS += -mdynamic-no-pic + endif + endif + endif ifeq ($(comp),clang) CXXFLAGS += -fexperimental-new-pass-manager From 74776dbcd57a1bdf2d5328e5750cc1d00244e17d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sun, 19 Dec 2021 15:15:30 +0100 Subject: [PATCH 0755/1766] Simplification in evaluate_nnue.cpp Removes the test on non-pawn-material before applying the positional/materialistic bonus. Passed STC: LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 46904 W: 12197 L: 12059 D: 22648 Ptnml(0-2): 170, 5243, 12479, 5399, 161 https://tests.stockfishchess.org/tests/view/61be57cf57a0d0f327c3999d Passed LTC: LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 18760 W: 4958 L: 4790 D: 9012 Ptnml(0-2): 14, 1942, 5301, 2108, 15 https://tests.stockfishchess.org/tests/view/61bed1fb57a0d0f327c3afa9 closes https://github.com/official-stockfish/Stockfish/pull/3866 Bench: 4826206 --- src/nnue/evaluate_nnue.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index bd473294d15..a534753add5 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -165,12 +165,11 @@ namespace Stockfish::Eval::NNUE { const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket); const auto positional = network[bucket]->propagate(transformedFeatures, buffer)[0]; - // Give more value to positional evaluation when material is balanced - if ( adjusted - && abs(pos.non_pawn_material(WHITE) - pos.non_pawn_material(BLACK)) <= RookValueMg - BishopValueMg) - return static_cast(((128 - delta) * psqt + (128 + delta) * positional) / 128 / OutputScale); + // Give more value to positional evaluation when adjusted flag is set + if (adjusted) + return static_cast(((128 - delta) * psqt + (128 + delta) * positional) / 128 / OutputScale); else - return static_cast((psqt + positional) / OutputScale); + return static_cast((psqt + positional) / OutputScale); } struct NnueEvalTrace { From 2c30956a13986454dcc5dd8c7daa1b27d5a77a1b Mon Sep 17 00:00:00 2001 From: bmc4 Date: Fri, 17 Dec 2021 21:14:34 -0300 Subject: [PATCH 0756/1766] Remove Capture Extension This revert the patch #3692, probably can be simplified after the introduction of #3838. Fixed-game test: ELO: -1.41 +-1.8 (95%) LOS: 5.9% Total: 20000 W: 1552 L: 1633 D: 16815 Ptnml(0-2): 38, 1242, 7517, 1169, 34 https://tests.stockfishchess.org/tests/view/61bc1a2057a0d0f327c32a3c STC: LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 44528 W: 11619 L: 11478 D: 21431 Ptnml(0-2): 146, 5020, 11771, 5201, 126 https://tests.stockfishchess.org/tests/view/61bc638c57a0d0f327c338fe LTC: LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 34136 W: 8847 L: 8704 D: 16585 Ptnml(0-2): 23, 3475, 9925, 3626, 19 https://tests.stockfishchess.org/tests/view/61bcb24257a0d0f327c34813 closes https://github.com/official-stockfish/Stockfish/pull/3863 Bench: 4054695 --- src/search.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index d2ceee9fd1a..eb0ac5bbd02 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1123,12 +1123,6 @@ namespace { extension = -2; } - // Capture extensions for PvNodes and cutNodes - else if ( (PvNode || cutNode) - && captureOrPromotion - && moveCount != 1) - extension = 1; - // Check extensions else if ( givesCheck && depth > 6 From 22e92d23d24ade1053cf543624d0efd01387e886 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Mon, 20 Dec 2021 08:08:09 -0300 Subject: [PATCH 0757/1766] Remove Capture history pruning Fixed number of games. (book: 8moves_v3.png): ELO: -0.69 +-1.8 (95%) LOS: 22.1% Total: 20000 W: 1592 L: 1632 D: 16776 Ptnml(0-2): 44, 1194, 7566, 1150, 46 https://tests.stockfishchess.org/tests/view/61bb8eb657a0d0f327c30ce8 STC: LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 139976 W: 36039 L: 36036 D: 67901 Ptnml(0-2): 435, 16138, 36885, 16049, 481 https://tests.stockfishchess.org/tests/view/61be731857a0d0f327c39ea2 LTC: LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 70656 W: 18284 L: 18189 D: 34183 Ptnml(0-2): 34, 7317, 20529, 7416, 32 https://tests.stockfishchess.org/tests/view/61bf39b657a0d0f327c3c37b closes https://github.com/official-stockfish/Stockfish/pull/3867 bench: 4281737 --- src/search.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index eb0ac5bbd02..988166df9bb 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1031,12 +1031,6 @@ namespace { if ( captureOrPromotion || givesCheck) { - // Capture history based pruning when the move doesn't give check - if ( !givesCheck - && lmrDepth < 1 - && captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] < 0) - continue; - // Futility pruning for captures if ( !pos.empty(to_sq(move)) && !givesCheck From 88f17a814d80cdb32ccf9efb7b56bb36d7bf052b Mon Sep 17 00:00:00 2001 From: bmc4 Date: Mon, 20 Dec 2021 16:20:08 -0300 Subject: [PATCH 0758/1766] Update Elo estimates for terms in search This updates estimates from 2yr ago #2401, and adds missing terms. All tests run at 10+0.1 (STC), 20000 games, error bars +- 1.8 Elo, book 8moves_v3.png. A table of Elo values with the links to the corresponding tests can be found at the PR closes https://github.com/official-stockfish/Stockfish/pull/3868 Non-functional Change --- src/evaluate.cpp | 2 +- src/search.cpp | 56 ++++++++++++++++++++++++------------------------ 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 24445f48292..28689f1d416 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1083,7 +1083,7 @@ Value Eval::evaluate(const Position& pos) { Value v; - // Deciding between classical and NNUE eval: for high PSQ imbalance we use classical, + // Deciding between classical and NNUE eval (~10 Elo): for high PSQ imbalance we use classical, // but we switch to NNUE during long shuffling or with high material on the board. if ( !useNNUE diff --git a/src/search.cpp b/src/search.cpp index 988166df9bb..f5e9779c812 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -671,20 +671,20 @@ namespace { && (ttValue >= beta ? (tte->bound() & BOUND_LOWER) : (tte->bound() & BOUND_UPPER))) { - // If ttMove is quiet, update move sorting heuristics on TT hit + // If ttMove is quiet, update move sorting heuristics on TT hit (~1 Elo) if (ttMove) { if (ttValue >= beta) { - // Bonus for a quiet ttMove that fails high + // Bonus for a quiet ttMove that fails high (~3 Elo) if (!ttCapture) update_quiet_stats(pos, ss, ttMove, stat_bonus(depth)); - // Extra penalty for early quiet moves of the previous ply + // Extra penalty for early quiet moves of the previous ply (~0 Elo) 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 + // Penalty for a quiet ttMove that fails low (~1 Elo) else if (!ttCapture) { int penalty = -stat_bonus(depth); @@ -773,7 +773,7 @@ namespace { if (eval == VALUE_DRAW) eval = value_draw(thisThread); - // Can ttValue be used as a better position evaluation? + // ttValue can be used as a better position evaluation (~4 Elo) if ( ttValue != VALUE_NONE && (tte->bound() & (ttValue > eval ? BOUND_LOWER : BOUND_UPPER))) eval = ttValue; @@ -787,7 +787,7 @@ namespace { tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); } - // Use static evaluation difference to improve quiet move ordering + // Use static evaluation difference to improve quiet move ordering (~3 Elo) if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture) { int bonus = std::clamp(-16 * int((ss-1)->staticEval + ss->staticEval), -2000, 2000); @@ -804,7 +804,7 @@ namespace { improving = improvement > 0; - // Step 7. Futility pruning: child node (~50 Elo). + // Step 7. Futility pruning: child node (~25 Elo). // The depth condition is important for mate finding. if ( !ss->ttPv && depth < 9 @@ -812,7 +812,7 @@ namespace { && eval < 15000) // 50% larger than VALUE_KNOWN_WIN, but smaller than TB wins. return eval; - // Step 8. Null move search with verification search (~40 Elo) + // Step 8. Null move search with verification search (~22 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL && (ss-1)->statScore < 23767 @@ -925,7 +925,7 @@ namespace { ss->ttPv = ttPv; } - // Step 10. If the position is not in TT, decrease depth by 2 or 1 depending on node type + // Step 10. If the position is not in TT, decrease depth by 2 or 1 depending on node type (~3 Elo) if ( PvNode && depth >= 6 && !ttMove) @@ -940,7 +940,7 @@ namespace { int rangeReduction = 0; - // Step 11. A small Probcut idea, when we are in check + // Step 11. A small Probcut idea, when we are in check (~0 Elo) probCutBeta = beta + 409; if ( ss->inCheck && !PvNode @@ -1017,12 +1017,12 @@ namespace { Value delta = beta - alpha; - // Step 13. Pruning at shallow depth (~200 Elo). Depth conditions are important for mate finding. + // Step 13. Pruning at shallow depth (~98 Elo). Depth conditions are important for mate finding. if ( !rootNode && pos.non_pawn_material(us) && bestValue > VALUE_TB_LOSS_IN_MAX_PLY) { - // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold + // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold (~7 Elo) moveCountPruning = moveCount >= futility_move_count(improving, depth); // Reduced depth of the next LMR search @@ -1031,18 +1031,18 @@ namespace { if ( captureOrPromotion || givesCheck) { - // Futility pruning for captures + // Futility pruning for captures (~0 Elo) if ( !pos.empty(to_sq(move)) && !givesCheck && !PvNode && lmrDepth < 6 && !ss->inCheck - && ss->staticEval + 342 + 238 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] + && ss->staticEval + 342 + 238 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 8 < alpha) continue; - // SEE based pruning - if (!pos.see_ge(move, Value(-218) * depth)) // (~25 Elo) + // SEE based pruning (~9 Elo) + if (!pos.see_ge(move, Value(-218) * depth)) continue; } else @@ -1051,28 +1051,28 @@ namespace { + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)]; - // Continuation history based pruning (~20 Elo) + // Continuation history based pruning (~2 Elo) if ( lmrDepth < 5 && history < -3000 * depth + 3000) continue; history += thisThread->mainHistory[us][from_to(move)]; - // Futility pruning: parent node (~5 Elo) + // Futility pruning: parent node (~9 Elo) if ( !ss->inCheck && lmrDepth < 8 && ss->staticEval + 142 + 139 * lmrDepth + history / 64 <= alpha) continue; - // Prune moves with negative SEE (~20 Elo) + // Prune moves with negative SEE (~3 Elo) if (!pos.see_ge(move, Value(-21 * lmrDepth * lmrDepth - 21 * lmrDepth))) continue; } } - // Step 14. Extensions (~75 Elo) + // Step 14. Extensions (~66 Elo) - // Singular extension search (~70 Elo). If all moves but one fail low on a + // Singular extension search (~58 Elo). If all moves but one fail low on a // search of (alpha-s, beta-s), and just one fails high on (alpha, beta), // 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 @@ -1117,13 +1117,13 @@ namespace { extension = -2; } - // Check extensions + // Check extensions (~1 Elo) else if ( givesCheck && depth > 6 && abs(ss->staticEval) > 100) extension = 1; - // Quiet ttMove extensions + // Quiet ttMove extensions (~0 Elo) else if ( PvNode && move == ttMove && move == ss->killers[0] @@ -1149,7 +1149,7 @@ namespace { bool doDeeperSearch = false; - // Step 16. Late moves reduction / extension (LMR, ~200 Elo) + // Step 16. Late moves reduction / extension (LMR, ~98 Elo) // We use various heuristics for the sons of a node after the first son has // been searched. In general we would like to reduce them, but there are many // cases where we extend a son if it has good chances to be "interesting". @@ -1467,7 +1467,7 @@ namespace { if ((ss->staticEval = bestValue = tte->eval()) == VALUE_NONE) ss->staticEval = bestValue = evaluate(pos); - // Can ttValue be used as a better position evaluation? + // ttValue can be used as a better position evaluation (~7 Elo) if ( ttValue != VALUE_NONE && (tte->bound() & (ttValue > bestValue ? BOUND_LOWER : BOUND_UPPER))) bestValue = ttValue; @@ -1522,7 +1522,7 @@ namespace { moveCount++; - // Futility pruning and moveCount pruning + // Futility pruning and moveCount pruning (~5 Elo) if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY && !givesCheck && futilityBase > -VALUE_KNOWN_WIN @@ -1547,7 +1547,7 @@ namespace { } } - // Do not search moves with negative SEE values + // Do not search moves with negative SEE values (~5 Elo) if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY && !pos.see_ge(move)) continue; @@ -1561,7 +1561,7 @@ namespace { [pos.moved_piece(move)] [to_sq(move)]; - // Continuation history based pruning + // Continuation history based pruning (~2 Elo) if ( !captureOrPromotion && bestValue > VALUE_TB_LOSS_IN_MAX_PLY && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < CounterMovePruneThreshold From 0a6168089de8df15339e2c8f6604158d7434b678 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Wed, 22 Dec 2021 07:54:10 +0300 Subject: [PATCH 0759/1766] Fall back to NNUE if classical evaluation is much lower than threshold The idea is that if classical eval returns a value much lower than the threshold of its usage it most likely means that position isn't that simple so we need the more precise NNUE evaluation. passed STC: https://tests.stockfishchess.org/tests/view/61bf3e7557a0d0f327c3c47a LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 108072 W: 28007 L: 27604 D: 52461 Ptnml(0-2): 352, 12147, 28650, 12520, 367 passed LTC: https://tests.stockfishchess.org/tests/view/61c0581657a0d0f327c3fa0c LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 155096 W: 40392 L: 39841 D: 74863 Ptnml(0-2): 88, 15983, 44843, 16558, 76 closes https://github.com/official-stockfish/Stockfish/pull/3869 bench 4310422 --- src/evaluate.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 28689f1d416..8bc516957c8 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1086,10 +1086,17 @@ Value Eval::evaluate(const Position& pos) { // Deciding between classical and NNUE eval (~10 Elo): for high PSQ imbalance we use classical, // but we switch to NNUE during long shuffling or with high material on the board. + bool classical = false; + if ( !useNNUE || abs(eg_value(pos.psq_score())) * 5 > (850 + pos.non_pawn_material() / 64) * (5 + pos.rule50_count())) + { v = Evaluation(pos).value(); // classical - else + classical = abs(v) >= 300; + } + + // If result of a classical evaluation is much lower than threshold fall back to NNUE + if (!classical && useNNUE) { int scale = 1136 + 20 * pos.non_pawn_material() / 1024; From 7d82f0d1f4e25892e1901d25cf5cf6f6a2606c2a Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 17 Dec 2021 19:34:30 +0100 Subject: [PATCH 0760/1766] Update default net to nn-ac07bd334b62.nnue Trained with essentially the same data as provided and used by Farseer (mbabigian) for the previous master net. T60T70wIsRightFarseerT60T74T75T76.binpack (99GB): ['T60T70wIsRightFarseer.binpack', 'farseerT74.binpack', 'farseerT75.binpack', 'farseerT76.binpack'] using the trainer branch tweakLR1PR (https://github.com/glinscott/nnue-pytorch/pull/158) and `--gpus 1 --threads 4 --num-workers 4 --batch-size 16384 --progress_bar_refresh_rate 300 --smart-fen-skipping --random-fen-skipping 12 --features=HalfKAv2_hm^ --lambda=1.00` options passed STC: LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 108280 W: 28042 L: 27636 D: 52602 Ptnml(0-2): 328, 12382, 28401, 12614, 415 https://tests.stockfishchess.org/tests/view/61bcd8c257a0d0f327c34fbd passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 259296 W: 66974 L: 66175 D: 126147 Ptnml(0-2): 146, 27096, 74452, 27721, 233 https://tests.stockfishchess.org/tests/view/61bda70957a0d0f327c37817 closes https://github.com/official-stockfish/Stockfish/pull/3870 Bench: 4633875 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index e258fdb6bab..d0c825eb3d3 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-4401e826ebcc.nnue" + #define EvalFileDefaultName "nn-ac07bd334b62.nnue" namespace NNUE { From 93b14a17d168e87e7f05fc09e3ba93e737b0757e Mon Sep 17 00:00:00 2001 From: bmc4 Date: Sat, 25 Dec 2021 08:54:16 -0300 Subject: [PATCH 0761/1766] Don't direct prune a move if it's a retake STC: LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 36304 W: 9499 L: 9226 D: 17579 Ptnml(0-2): 96, 4102, 9508, 4325, 121 https://tests.stockfishchess.org/tests/view/61c7069ae68b2a714b6dca27 LTC: LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 93824 W: 24478 L: 24068 D: 45278 Ptnml(0-2): 70, 9644, 27082, 10038, 78 https://tests.stockfishchess.org/tests/view/61c725fee68b2a714b6dcfa2 closes https://github.com/official-stockfish/Stockfish/pull/3871 Bench: 4106806 --- src/search.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index f5e9779c812..a7629717742 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1503,10 +1503,11 @@ namespace { // to search the moves. Because the depth is <= 0 here, only captures, // queen promotions, and other checks (only if depth >= DEPTH_QS_CHECKS) // will be generated. + Square prevSq = to_sq((ss-1)->currentMove); MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory, contHist, - to_sq((ss-1)->currentMove)); + prevSq); // Loop through the moves until no moves remain or a beta cutoff occurs while ((move = mp.next_move()) != MOVE_NONE) @@ -1525,6 +1526,7 @@ namespace { // Futility pruning and moveCount pruning (~5 Elo) if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY && !givesCheck + && to_sq(move) != prevSq && futilityBase > -VALUE_KNOWN_WIN && type_of(move) != PROMOTION) { From 1066119083ffc8a697becbeb75a48f95add1a92d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicolet?= Date: Thu, 30 Dec 2021 11:23:40 +0100 Subject: [PATCH 0762/1766] Tweak optimism with complexity This patch increases the optimism bonus for "complex positions", where the complexity is measured as the absolute value of the difference between material and the sophisticated NNUE evaluation (idea by Joost VandeVondele). Also rename some variables in evaluate() while there. passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 88392 W: 23150 L: 22781 D: 42461 Ptnml(0-2): 318, 9961, 23257, 10354, 306 https://tests.stockfishchess.org/tests/view/61cbbedee68b2a714b6eb110 passed LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.00> Total: 37848 W: 10043 L: 9766 D: 18039 Ptnml(0-2): 26, 3815, 10961, 4100, 22 https://tests.stockfishchess.org/tests/view/61cc0cc3e68b2a714b6ec28c Closes https://github.com/official-stockfish/Stockfish/pull/3875 Follow-up from https://github.com/official-stockfish/Stockfish/commit/a5a89b27c8e3225fb453d603bc4515d32bb351c3 Bench: 4125221 --- src/evaluate.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 8bc516957c8..edff9185c2d 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1082,29 +1082,28 @@ namespace { Value Eval::evaluate(const Position& pos) { Value v; + bool useClassical = false; // Deciding between classical and NNUE eval (~10 Elo): for high PSQ imbalance we use classical, // but we switch to NNUE during long shuffling or with high material on the board. - - bool classical = false; - if ( !useNNUE || abs(eg_value(pos.psq_score())) * 5 > (850 + pos.non_pawn_material() / 64) * (5 + pos.rule50_count())) { v = Evaluation(pos).value(); // classical - classical = abs(v) >= 300; + useClassical = abs(v) >= 300; } // If result of a classical evaluation is much lower than threshold fall back to NNUE - if (!classical && useNNUE) + if (useNNUE && !useClassical) { - int scale = 1136 - + 20 * pos.non_pawn_material() / 1024; - Value nnue = NNUE::evaluate(pos, true); // NNUE + int scale = 1136 + 20 * pos.non_pawn_material() / 1024; Color stm = pos.side_to_move(); Value optimism = pos.this_thread()->optimism[stm]; + Value psq = (stm == WHITE ? 1 : -1) * eg_value(pos.psq_score()); + int complexity = abs(nnue - psq) / 256; + optimism *= (1 + complexity); v = (nnue + optimism) * scale / 1024 - optimism; if (pos.is_chess960()) From 061f98a9e3ae439fac13b4b2dda4be47977994b1 Mon Sep 17 00:00:00 2001 From: lonfom169 Date: Thu, 30 Dec 2021 22:50:20 -0300 Subject: [PATCH 0763/1766] Smooth out doDeeperSearch Adjust threshold based on the difference between newDepth and LMR depth. With more reduction, bigger fail-high is required in order to perform the deeper search. STC: LLR: 2.96 (-2.94,2.94) <0.00,2.50> Total: 93576 W: 24133 L: 23758 D: 45685 Ptnml(0-2): 260, 10493, 24935, 10812, 288 https://tests.stockfishchess.org/tests/view/61cbb5cee68b2a714b6eaf09 LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 109280 W: 28198 L: 27754 D: 53328 Ptnml(0-2): 60, 11225, 31637, 11647, 71 https://tests.stockfishchess.org/tests/view/61cc03fee68b2a714b6ec091 closes https://github.com/official-stockfish/Stockfish/pull/3877 Bench: 4464723 --- src/search.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index a7629717742..32a6ae611d5 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -550,7 +550,7 @@ namespace { if ( ss->ply > 10 && search_explosion(thisThread) == MUST_CALM_DOWN && depth > (ss-1)->depth) - depth = (ss-1)->depth; + depth = (ss-1)->depth; constexpr bool PvNode = nodeType != NonPV; constexpr bool rootNode = nodeType == Root; @@ -1212,7 +1212,7 @@ namespace { // If the son is reduced and fails high it will be re-searched at full depth doFullDepthSearch = value > alpha && d < newDepth; - doDeeperSearch = value > alpha + 88; + doDeeperSearch = value > (alpha + 62 + 20 * (newDepth - d)); didLMR = true; } else @@ -1280,7 +1280,7 @@ namespace { rm.pv.push_back(*m); // We record how often the best move has been changed in each iteration. - // This information is used for time management and LMR. In MultiPV mode, + // This information is used for time management. In MultiPV mode, // we must take care to only do this for the first PV line. if ( moveCount > 1 && !thisThread->pvIdx) From 0b41887527f1d7264ee5644dfa53e00ab64441b1 Mon Sep 17 00:00:00 2001 From: lonfom169 Date: Sat, 1 Jan 2022 14:09:08 -0300 Subject: [PATCH 0764/1766] Simplify away rangeReduction Remove rangeReduction, introduced in [#3717](https://github.com/official-stockfish/Stockfish/pull/3717), as it seemingly doesn't bring enough ELO anymore. It might be interesting to add new forms of reduction or tune the reduction formula in the future. STC: LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 45008 W: 12114 L: 11972 D: 20922 Ptnml(0-2): 174, 5031, 11952, 5173, 174 https://tests.stockfishchess.org/tests/view/61d08b7b069ca917749c9f6f LTC: LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 30792 W: 8235 L: 8086 D: 14471 Ptnml(0-2): 24, 3162, 8882, 3297, 31 https://tests.stockfishchess.org/tests/view/61d0a6ad069ca917749ca420 closes https://github.com/official-stockfish/Stockfish/pull/3878 Bench: 4048312 --- src/search.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 32a6ae611d5..2fd8245ec2c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -69,9 +69,9 @@ namespace { // Reductions lookup table, initialized at startup int Reductions[MAX_MOVES]; // [depth or moveNumber] - Depth reduction(bool i, Depth d, int mn, bool rangeReduction, Value delta, Value rootDelta) { + Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) { int r = Reductions[d] * Reductions[mn]; - return (r + 1358 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 904) + rangeReduction; + return (r + 1358 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 904); } constexpr int futility_move_count(bool improving, Depth depth) { @@ -938,8 +938,6 @@ namespace { moves_loop: // When in check, search starts here - int rangeReduction = 0; - // Step 11. A small Probcut idea, when we are in check (~0 Elo) probCutBeta = beta + 409; if ( ss->inCheck @@ -1026,7 +1024,7 @@ namespace { moveCountPruning = moveCount >= futility_move_count(improving, depth); // Reduced depth of the next LMR search - int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount, rangeReduction > 2, delta, thisThread->rootDelta), 0); + int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount, delta, thisThread->rootDelta), 0); if ( captureOrPromotion || givesCheck) @@ -1159,7 +1157,7 @@ namespace { || !captureOrPromotion || (cutNode && (ss-1)->moveCount > 1))) { - Depth r = reduction(improving, depth, moveCount, rangeReduction > 2, delta, thisThread->rootDelta); + Depth r = reduction(improving, depth, moveCount, delta, thisThread->rootDelta); // Decrease reduction at some PvNodes (~2 Elo) if ( PvNode @@ -1206,10 +1204,6 @@ namespace { value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); - // Range reductions (~3 Elo) - if (ss->staticEval - value < 30 && depth > 7) - rangeReduction++; - // If the son is reduced and fails high it will be re-searched at full depth doFullDepthSearch = value > alpha && d < newDepth; doDeeperSearch = value > (alpha + 62 + 20 * (newDepth - d)); From ad926d34c0105d523bfa5cb92cbcf9f337d54c08 Mon Sep 17 00:00:00 2001 From: Brad Knox <64992190+bknox83@users.noreply.github.com> Date: Mon, 3 Jan 2022 20:33:26 -0600 Subject: [PATCH 0765/1766] Update copyright years Happy New Year! closes https://github.com/official-stockfish/Stockfish/pull/3881 No functional change --- src/Makefile | 2 +- src/benchmark.cpp | 2 +- src/bitbase.cpp | 2 +- src/bitboard.cpp | 2 +- src/bitboard.h | 2 +- src/endgame.cpp | 2 +- src/endgame.h | 2 +- src/evaluate.cpp | 2 +- src/evaluate.h | 2 +- src/main.cpp | 2 +- src/material.cpp | 2 +- src/material.h | 2 +- src/misc.cpp | 2 +- src/misc.h | 2 +- src/movegen.cpp | 2 +- src/movegen.h | 2 +- src/movepick.cpp | 2 +- src/movepick.h | 2 +- src/nnue/evaluate_nnue.cpp | 2 +- src/nnue/evaluate_nnue.h | 2 +- src/nnue/features/half_ka_v2_hm.cpp | 2 +- src/nnue/features/half_ka_v2_hm.h | 2 +- src/nnue/layers/affine_transform.h | 2 +- src/nnue/layers/clipped_relu.h | 2 +- src/nnue/layers/input_slice.h | 2 +- src/nnue/nnue_accumulator.h | 2 +- src/nnue/nnue_architecture.h | 2 +- src/nnue/nnue_common.h | 2 +- src/nnue/nnue_feature_transformer.h | 2 +- src/pawns.cpp | 2 +- src/pawns.h | 2 +- src/position.cpp | 2 +- src/position.h | 2 +- src/psqt.cpp | 2 +- src/psqt.h | 2 +- src/search.cpp | 2 +- src/search.h | 2 +- src/simd.h | 2 +- src/syzygy/tbprobe.cpp | 2 +- src/syzygy/tbprobe.h | 2 +- src/thread.cpp | 2 +- src/thread.h | 2 +- src/thread_win32_osx.h | 2 +- src/timeman.cpp | 2 +- src/timeman.h | 2 +- src/tt.cpp | 2 +- src/tt.h | 2 +- src/tune.cpp | 2 +- src/tune.h | 2 +- src/types.h | 2 +- src/uci.cpp | 2 +- src/uci.h | 2 +- src/ucioption.cpp | 2 +- 53 files changed, 53 insertions(+), 53 deletions(-) diff --git a/src/Makefile b/src/Makefile index f00df79fa41..406f029f397 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,5 +1,5 @@ # Stockfish, a UCI chess playing engine derived from Glaurung 2.1 -# Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) +# Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) # # Stockfish is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 7945a4535ca..02ff2f9d0b1 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/bitbase.cpp b/src/bitbase.cpp index 27bf4095478..84300baf92a 100644 --- a/src/bitbase.cpp +++ b/src/bitbase.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 6b84b51e036..fd0ba235cb4 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/bitboard.h b/src/bitboard.h index b29f3e24fa1..2b6e2a6920c 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/endgame.cpp b/src/endgame.cpp index a44d3a1c5e6..e773e7a9119 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/endgame.h b/src/endgame.h index 146111b9579..e79f696fa3a 100644 --- a/src/endgame.h +++ b/src/endgame.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/evaluate.cpp b/src/evaluate.cpp index edff9185c2d..ab71fa67df0 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/evaluate.h b/src/evaluate.h index d0c825eb3d3..57a7687d776 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/main.cpp b/src/main.cpp index 62e0ed525d7..fad0ef8449b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/material.cpp b/src/material.cpp index 9d17af208c4..1567358af48 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/material.h b/src/material.h index 26535a535af..3ca169ce0b9 100644 --- a/src/material.h +++ b/src/material.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/misc.cpp b/src/misc.cpp index b46786dff08..41c59b3fd88 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/misc.h b/src/misc.h index 062b420a422..688d00e7e55 100644 --- a/src/misc.h +++ b/src/misc.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/movegen.cpp b/src/movegen.cpp index 5095bb7455e..c7a3c29bc04 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/movegen.h b/src/movegen.h index 3f895f05ad4..bbb35b39159 100644 --- a/src/movegen.h +++ b/src/movegen.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/movepick.cpp b/src/movepick.cpp index 6aa1954b5ae..694b9222fe5 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/movepick.h b/src/movepick.h index 426bac89bd9..e2cbfcdee33 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index a534753add5..862b2003388 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/evaluate_nnue.h b/src/nnue/evaluate_nnue.h index c7fa4a96f55..2e4f1f5098d 100644 --- a/src/nnue/evaluate_nnue.h +++ b/src/nnue/evaluate_nnue.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/features/half_ka_v2_hm.cpp b/src/nnue/features/half_ka_v2_hm.cpp index 6face2172a4..07a1d7a154d 100644 --- a/src/nnue/features/half_ka_v2_hm.cpp +++ b/src/nnue/features/half_ka_v2_hm.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/features/half_ka_v2_hm.h b/src/nnue/features/half_ka_v2_hm.h index c7b1a68df71..1e6da0bfe2b 100644 --- a/src/nnue/features/half_ka_v2_hm.h +++ b/src/nnue/features/half_ka_v2_hm.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 11038d69b1c..4e85a5fe4b1 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index c6f3ccade7d..0da5e821011 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/layers/input_slice.h b/src/nnue/layers/input_slice.h index b6bf1727b40..8f526b745f7 100644 --- a/src/nnue/layers/input_slice.h +++ b/src/nnue/layers/input_slice.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index d41ecf95b17..600483b5cfd 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index 193a197d3b2..8867fac72fc 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index 74eaae17c2d..1bce00ae465 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 4f6a174a486..f4024dce83b 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/pawns.cpp b/src/pawns.cpp index 70fb6f23782..6e509133abc 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/pawns.h b/src/pawns.h index 124619d66a0..af0370fc41a 100644 --- a/src/pawns.h +++ b/src/pawns.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/position.cpp b/src/position.cpp index ae1da017770..ec9229ea337 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/position.h b/src/position.h index 9f694a79b25..8dbf1493c28 100644 --- a/src/position.h +++ b/src/position.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/psqt.cpp b/src/psqt.cpp index 33a3e00c91d..ca5664c259f 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/psqt.h b/src/psqt.h index 7abb14830c1..4ee0e379b14 100644 --- a/src/psqt.h +++ b/src/psqt.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/search.cpp b/src/search.cpp index 2fd8245ec2c..86af39183fb 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/search.h b/src/search.h index 7a5d5bdf478..806295a1100 100644 --- a/src/search.h +++ b/src/search.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/simd.h b/src/simd.h index ffa54d9627b..7b9e8fb23b7 100644 --- a/src/simd.h +++ b/src/simd.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 41e867c00a3..a1315244299 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/syzygy/tbprobe.h b/src/syzygy/tbprobe.h index cf61b767590..c2917fef636 100644 --- a/src/syzygy/tbprobe.h +++ b/src/syzygy/tbprobe.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/thread.cpp b/src/thread.cpp index 099efbad24c..30177a3915a 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/thread.h b/src/thread.h index 6bc1be658f8..a6b0b5a0bc9 100644 --- a/src/thread.h +++ b/src/thread.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/thread_win32_osx.h b/src/thread_win32_osx.h index a21674cc68f..77d1c3c7ef3 100644 --- a/src/thread_win32_osx.h +++ b/src/thread_win32_osx.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/timeman.cpp b/src/timeman.cpp index 69d1c96fa98..0400401e779 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/timeman.h b/src/timeman.h index b1878d65f25..a86f07693a0 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/tt.cpp b/src/tt.cpp index 4af6c9f11a5..c7118aea3fc 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/tt.h b/src/tt.h index d915d92e43f..03fe3e143d1 100644 --- a/src/tt.h +++ b/src/tt.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/tune.cpp b/src/tune.cpp index ac91b606fb1..a885845f750 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/tune.h b/src/tune.h index 53d52a65b3b..75ab484acf0 100644 --- a/src/tune.h +++ b/src/tune.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/types.h b/src/types.h index 02cd19def77..a3a873fa4e7 100644 --- a/src/types.h +++ b/src/types.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/uci.cpp b/src/uci.cpp index b3738a4a76b..741241b3271 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/uci.h b/src/uci.h index d3160109d6e..5bb24a4ec65 100644 --- a/src/uci.h +++ b/src/uci.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 0cafd3e92d0..922fa34fe93 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by From 2efda17c2ac4253dec7c566268363f4dfd391b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Fri, 7 Jan 2022 07:55:50 +0100 Subject: [PATCH 0766/1766] Update AUTHORS and CPU contributors files closes https://github.com/official-stockfish/Stockfish/pull/3882 No functional change --- AUTHORS | 1 + Top CPU Contributors.txt | 118 ++++++++++++++++++++------------------- 2 files changed, 61 insertions(+), 58 deletions(-) diff --git a/AUTHORS b/AUTHORS index 35ccdaf5235..d7e6dc98663 100644 --- a/AUTHORS +++ b/AUTHORS @@ -132,6 +132,7 @@ Michael Whiteley (protonspring) Michel Van den Bergh (vdbergh) Miguel Lahoz (miguel-l) Mikael Bäckman (mbootsector) +Mike Babigian (Farseer) Mira Miroslav Fontán (Hexik) Moez Jellouli (MJZ1977) diff --git a/Top CPU Contributors.txt b/Top CPU Contributors.txt index f0ec51f9291..718d7cca177 100644 --- a/Top CPU Contributors.txt +++ b/Top CPU Contributors.txt @@ -1,68 +1,69 @@ -Contributors to Fishtest with >10,000 CPU hours, as of 2021-12-11. +Contributors to Fishtest with >10,000 CPU hours, as of 2022-01-08. Thank you! Username CPU Hours Games played ------------------------------------------------------------------ -noobpwnftw 29832955 2061725095 -mlang 2345848 162775694 -dew 1559902 93935940 -technologov 1154365 43136020 -grandphish2 988545 60725563 +noobpwnftw 30323785 2111752181 +mlang 2597136 178003354 +dew 1598255 95747056 +technologov 1395130 59347018 +grandphish2 1028906 63396841 tvijlbrief 795993 51894442 -TueRens 716909 45503672 +TueRens 737922 46359276 +okrout 719183 57150314 mibere 703840 46867607 -JojoM 675708 41046762 -okrout 648513 49953162 -linrock 563226 16184599 -pemo 520578 26816487 -gvreuls 501232 32662374 -cw 495103 33171295 -fastgm 476917 28660004 +JojoM 689134 42001146 +linrock 594355 16779359 +pemo 575248 28386103 +gvreuls 509219 33205908 +cw 500695 33575803 +fastgm 479238 28830588 crunchy 427035 27344275 -CSU_Dynasty 407300 27580858 -ctoks 386452 25757397 -oz 343309 25441096 +CSU_Dynasty 410969 27877556 +ctoks 393901 26299629 +oz 354661 26331020 Fisherman 327231 21829379 -velislav 320318 20642978 -bcross 319459 22485737 -leszek 277442 17495865 -Dantist 229980 14568674 -mgrabiak 225684 14774260 +bcross 325119 22871639 +velislav 320581 20663382 +leszek 291605 18475167 +Dantist 239411 15236750 +mgrabiak 229336 15004308 glinscott 217799 13780820 +robal 211837 13563250 nordlandia 211692 13484886 -robal 209847 13427680 -drabel 200340 13727458 +drabel 200377 13730626 bking_US 198894 11876016 Thanar 179852 12365359 -vdv 175274 9889046 -mhoram 166293 10796647 +vdv 175535 9904264 +mhoram 173134 11257113 spams 157128 10319326 marrco 150300 9402229 sqrt2 147963 9724586 -vdbergh 137186 8938965 +vdbergh 137425 8954767 CoffeeOne 137100 5024116 malala 136182 8002293 xoto 133759 9159372 -davar 122092 7960001 +davar 122113 7961971 dsmith 122059 7570238 amicic 119659 7937885 +rpngn 118952 8100045 Data 113305 8220352 BrunoBanani 112960 7436849 -rpngn 109031 7392547 CypressChess 108321 7759588 MaZePallas 102823 6633619 sterni1971 100532 5880772 sunu 100167 7040199 ElbertoOne 99028 7023771 -skiminki 98121 6478170 +skiminki 98123 6478402 +DesolatedDodo 93686 6139198 brabos 92118 6186135 +cuistot 90357 5350988 psk 89957 5984901 -cuistot 88420 5225234 -DesolatedDodo 88356 5779482 -racerschmacer 85711 6119610 +racerschmacer 85712 6119648 Vizvezdenec 83761 5344740 0x3C33 82614 5271253 BRAVONE 81239 5054681 +sschnee 78091 4678078 nssy 76497 5259388 teddybaer 75125 5407666 Pking_cda 73776 5293873 @@ -70,59 +71,59 @@ jromang 72192 5057715 solarlight 70517 5028306 dv8silencer 70287 3883992 Bobo1239 68515 4652287 +zeryl 68203 4516139 manap 66273 4121774 -sschnee 64563 3633680 tinker 64333 4268790 -zeryl 63290 4179159 +yurikvelo 61692 4262042 qurashee 61208 3429862 -yurikvelo 60387 4169900 robnjr 57262 4053117 Freja 56938 3733019 ttruscott 56010 3680085 rkl 55132 4164467 +Wolfgang 54087 3415872 renouve 53811 3501516 finfish 51360 3370515 eva42 51272 3599691 -Wolfgang 51248 3218932 -eastorwest 50311 3409935 +eastorwest 51055 3451203 rap 49985 3219146 pb00067 49727 3298270 +Spprtr 48260 3141959 bigpen0r 47667 3336927 ronaldjerum 47654 3240695 MaxKlaxxMiner 47584 2972142 biffhero 46564 3111352 -Spprtr 45877 2995437 Fifis 45843 3088497 VoyagerOne 45476 3452465 speedycpu 43842 3003273 jbwiebe 43305 2805433 +megaman7de 43042 2823256 Antihistamine 41788 2761312 mhunt 41735 2691355 -megaman7de 40060 2625050 homyur 39893 2850481 gri 39871 2515779 -oryx 38860 2976488 +oryx 38867 2976992 SC 37299 2731694 +Garf 37213 2986270 csnodgrass 36207 2688994 jmdana 36157 2210661 -Garf 36113 2897580 strelock 34716 2074055 EthanOConnor 33370 2090311 slakovv 32915 2021889 +Calis007 32024 2163604 manapbk 30987 1810399 DMBK 30675 2383552 Prcuvu 30377 2170122 anst 30301 2190091 +armo9494 30198 2438202 jkiiski 30136 1904470 +tolkki963 29918 1822290 hyperbolic.tom 29840 2017394 chuckstablers 29659 2093438 Pyafue 29650 1902349 -tolkki963 28171 1716386 +gopeto 28881 1896862 OuaisBla 27636 1578800 -armo9494 27224 2221042 chriswk 26902 1868317 achambord 26582 1767323 -gopeto 26355 1717722 Patrick_G 26276 1801617 yorkman 26193 1992080 SFTUser 25182 1675689 @@ -135,8 +136,8 @@ JanErik 23408 1703875 Isidor 23388 1680691 Norabor 23339 1602636 cisco2015 22897 1762669 +Ente 22810 1628234 Zirie 22542 1472937 -Ente 22486 1606268 team-oh 22272 1636708 MazeOfGalious 21978 1629593 sg4032 21947 1643265 @@ -151,10 +152,11 @@ horst.prack 20878 1465656 j3corre 20405 941444 Adrian.Schmidt123 20316 1281436 wei 19973 1745989 +belzedar94 19818 1434252 +user213718 19608 1334650 rstoesser 19569 1293588 eudhan 19274 1283717 vulcan 18871 1729392 -user213718 18590 1271128 jundery 18445 1115855 iisiraider 18247 1101015 ville 17883 1384026 @@ -163,52 +165,53 @@ purplefishies 17595 1092533 dju 17353 978595 DragonLord 17014 1162790 IgorLeMasson 16064 1147232 +Roady 15677 1121476 ako027ako 15671 1173203 +kdave 15539 1160356 Nikolay.IT 15154 1068349 Andrew Grant 15114 895539 OssumOpossum 14857 1007129 +spcc 14838 1034050 Karby 14808 867120 enedene 14476 905279 -jsys14 14318 843704 +fishtester 14411 1016252 +jsys14 14340 844792 bpfliegel 14298 884523 +AndreasKrug 14096 1126301 mpx86 14019 759568 jpulman 13982 870599 -kdave 13933 1045550 +Ulysses 13977 1073410 crocogoat 13803 1117422 joster 13794 950160 Nesa92 13786 1114691 mbeier 13650 1044928 -AndreasKrug 13624 1090613 Hjax 13535 915487 Dark_wizzie 13422 1007152 -Ulysses 13392 1021264 -Calis007 13267 873236 Rudolphous 13244 883140 -spcc 13085 917006 Machariel 13010 863104 mabichito 12903 749391 thijsk 12886 722107 AdrianSA 12860 804972 Flopzee 12698 894821 +infinigon 12638 933684 fatmurphy 12547 853210 scuzzi 12511 845761 SapphireBrand 12416 969604 modolief 12386 896470 -fishtester 12320 873882 Farseer 12249 694108 pgontarz 12151 848794 stocky 11954 699440 mschmidt 11941 803401 Maxim 11543 836024 infinity 11470 727027 +pirt 11434 889369 aga 11409 695071 torbjo 11395 729145 -pirt 11392 886291 Thomas A. Anderson 11372 732094 savage84 11358 670860 FormazChar 11304 847663 +dbernier 11274 806566 d64 11263 789184 -dbernier 11258 805102 MooTheCow 11237 720174 snicolet 11106 869170 ali-al-zhrani 11098 768494 @@ -217,7 +220,6 @@ Cubox 10621 826448 michaelrpg 10509 739239 OIVAS7572 10420 995586 dzjp 10343 732529 -infinigon 10319 776158 -Garruk 10314 702629 +Garruk 10332 703905 ols 10259 570669 lbraesch 10252 647825 From 9ad0ea73825bf463e842fb1fd41b5acaa8a1cfe1 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 6 Jan 2022 07:44:41 +0100 Subject: [PATCH 0767/1766] Tune a few parameters related to evaluation based on a SPSA tune (using Autoselect) https://tests.stockfishchess.org/tests/view/61d5aa63a314fed318a57046 passed STC: LLR: 2.93 (-2.94,2.94) <0.00,2.50> Total: 61960 W: 16640 L: 16316 D: 29004 Ptnml(0-2): 278, 6934, 16204, 7314, 250 https://tests.stockfishchess.org/tests/view/61d7fe4af5fd40f357469a8d passed LTC: LLR: 2.97 (-2.94,2.94) <0.50,3.00> Total: 79408 W: 21994 L: 21618 D: 35796 Ptnml(0-2): 106, 7887, 23331, 8285, 95 https://tests.stockfishchess.org/tests/view/61d836b7f5fd40f35746a3d5 closes https://github.com/official-stockfish/Stockfish/pull/3883 Bench: 4266621 --- src/evaluate.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index ab71fa67df0..4f3843f864f 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -192,8 +192,8 @@ using namespace Trace; namespace { // Threshold for lazy and space evaluation - constexpr Value LazyThreshold1 = Value(3130); - constexpr Value LazyThreshold2 = Value(2204); + constexpr Value LazyThreshold1 = Value(3631); + constexpr Value LazyThreshold2 = Value(2084); constexpr Value SpaceThreshold = Value(11551); // KingAttackWeights[PieceType] contains king attack weights by piece type @@ -1087,10 +1087,10 @@ Value Eval::evaluate(const Position& pos) { // Deciding between classical and NNUE eval (~10 Elo): for high PSQ imbalance we use classical, // but we switch to NNUE during long shuffling or with high material on the board. if ( !useNNUE - || abs(eg_value(pos.psq_score())) * 5 > (850 + pos.non_pawn_material() / 64) * (5 + pos.rule50_count())) + || abs(eg_value(pos.psq_score())) * 5 > (849 + pos.non_pawn_material() / 64) * (5 + pos.rule50_count())) { v = Evaluation(pos).value(); // classical - useClassical = abs(v) >= 300; + useClassical = abs(v) >= 298; } // If result of a classical evaluation is much lower than threshold fall back to NNUE @@ -1101,9 +1101,9 @@ Value Eval::evaluate(const Position& pos) { Color stm = pos.side_to_move(); Value optimism = pos.this_thread()->optimism[stm]; Value psq = (stm == WHITE ? 1 : -1) * eg_value(pos.psq_score()); - int complexity = abs(nnue - psq) / 256; + int complexity = 35 * abs(nnue - psq) / 256; - optimism *= (1 + complexity); + optimism = optimism * (44 + complexity) / 32; v = (nnue + optimism) * scale / 1024 - optimism; if (pos.is_chess960()) @@ -1111,7 +1111,7 @@ Value Eval::evaluate(const Position& pos) { } // Damp down the evaluation linearly when shuffling - v = v * (207 - pos.rule50_count()) / 207; + v = v * (208 - pos.rule50_count()) / 208; // Guarantee evaluation does not hit the tablebase range v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); From c5a280c0125b71cdf43bf326551bc95bb4dbc014 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 7 Jan 2022 22:57:09 +0100 Subject: [PATCH 0768/1766] Tune FRC trapped Bishop patch now that fishtest can deal with FRC, retune this correction. Add an additional fen to bench with cornered B and N. passed STC: LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 49672 W: 7358 L: 7082 D: 35232 Ptnml(0-2): 241, 4329, 15458, 4529, 279 https://tests.stockfishchess.org/tests/view/61d8b7bf9fea7913d9c63cb7 passed LTC: LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 86688 W: 8308 L: 8007 D: 70373 Ptnml(0-2): 92, 4943, 32989, 5212, 108 https://tests.stockfishchess.org/tests/view/61d92dcb9fea7913d9c650ad closes https://github.com/official-stockfish/Stockfish/pull/3884 Bench: 4326560 --- src/benchmark.cpp | 1 + src/evaluate.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 02ff2f9d0b1..e1c025adb9a 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -87,6 +87,7 @@ const vector Defaults = { // Chess 960 "setoption name UCI_Chess960 value true", "bbqnnrkr/pppppppp/8/8/8/8/PPPPPPPP/BBQNNRKR w HFhf - 0 1 moves g2g3 d7d5 d2d4 c8h3 c1g5 e8d6 g5e7 f7f6", + "nqbnrkrb/pppppppp/8/8/8/8/PPPPPPPP/NQBNRKRB w KQkq - 0 1", "setoption name UCI_Chess960 value false" }; diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 4f3843f864f..a5c049a88ad 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1069,8 +1069,8 @@ namespace { && pos.piece_on(SQ_G7) == B_PAWN) correction += CorneredBishop; - return pos.side_to_move() == WHITE ? Value(5 * correction) - : -Value(5 * correction); + return pos.side_to_move() == WHITE ? Value(3 * correction) + : -Value(3 * correction); } } // namespace Eval From 44b1ba89a95f394f3e180eb508f2d7798417c86e Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Mon, 10 Jan 2022 21:29:25 +0300 Subject: [PATCH 0769/1766] Adjust pruning constants This patch is a modification of original tuning done by vondele that failed yellow. Value differences are divided by 2. Passed STC https://tests.stockfishchess.org/tests/view/61d918239fea7913d9c64cdf LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 98968 W: 26248 L: 25858 D: 46862 Ptnml(0-2): 392, 11085, 26156, 11443, 408 Passed LTC https://tests.stockfishchess.org/tests/view/61d99e3c9fea7913d9c663e4 LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 215232 W: 58191 L: 57492 D: 99549 Ptnml(0-2): 271, 22124, 62138, 22801, 282 closes https://github.com/official-stockfish/Stockfish/pull/3885 bench 4572746 --- src/search.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 86af39183fb..58873c8981f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1040,7 +1040,7 @@ namespace { continue; // SEE based pruning (~9 Elo) - if (!pos.see_ge(move, Value(-218) * depth)) + if (!pos.see_ge(move, Value(-217) * depth)) continue; } else @@ -1051,7 +1051,7 @@ namespace { // Continuation history based pruning (~2 Elo) if ( lmrDepth < 5 - && history < -3000 * depth + 3000) + && history < -3875 * (depth - 1)) continue; history += thisThread->mainHistory[us][from_to(move)]; @@ -1059,7 +1059,7 @@ namespace { // Futility pruning: parent node (~9 Elo) if ( !ss->inCheck && lmrDepth < 8 - && ss->staticEval + 142 + 139 * lmrDepth + history / 64 <= alpha) + && ss->staticEval + 138 + 137 * lmrDepth + history / 64 <= alpha) continue; // Prune moves with negative SEE (~3 Elo) From c5d45d3220f74045aff249c47abd91d8d663b748 Mon Sep 17 00:00:00 2001 From: pschneider1968 <36973164+pschneider1968@users.noreply.github.com> Date: Mon, 27 Dec 2021 21:16:04 +0100 Subject: [PATCH 0770/1766] Fix Makefile for compilation with clang on Windows use static compilation and added exclusion of -latomic for Clang/MSYS2 as per ppigazzini's suggestion fixes #3872 closes https://github.com/official-stockfish/Stockfish/pull/3873 No functional change --- src/Makefile | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Makefile b/src/Makefile index 406f029f397..0e889888549 100644 --- a/src/Makefile +++ b/src/Makefile @@ -404,12 +404,14 @@ ifeq ($(COMP),clang) ifneq ($(KERNEL),Darwin) ifneq ($(KERNEL),OpenBSD) ifneq ($(KERNEL),FreeBSD) + ifneq ($(findstring MINGW,$(KERNEL)),MINGW) ifneq ($(RTLIB),compiler-rt) LDFLAGS += -latomic endif endif endif endif + endif ifeq ($(arch),$(filter $(arch),armv7 armv8)) ifeq ($(OS),Android) @@ -420,6 +422,11 @@ ifeq ($(COMP),clang) CXXFLAGS += -m$(bits) LDFLAGS += -m$(bits) endif + + ifeq ($(findstring MINGW,$(KERNEL)),MINGW) + LDFLAGS += -static + endif + endif ifeq ($(KERNEL),Darwin) From 7678d63cf2323e51c01e60cdff4ac3d685313790 Mon Sep 17 00:00:00 2001 From: Rui Coelho Date: Thu, 13 Jan 2022 18:30:53 +0000 Subject: [PATCH 0771/1766] Use complexity in search This patch uses the complexity measure (from #3875) as a heuristic for null move pruning. Hopefully, there may be room to use it in other pruning techniques. I would like to thank vondele and locutus2 for the feedback and suggestions during testing. Passed STC LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 35000 W: 9624 L: 9347 D: 16029 Ptnml(0-2): 156, 3894, 9137, 4143, 170 https://tests.stockfishchess.org/tests/view/61dda784c65bf87d6c45ab80 Passed LTC LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 230776 W: 64227 L: 63454 D: 103095 Ptnml(0-2): 1082, 23100, 66380, 23615, 1211 https://tests.stockfishchess.org/tests/view/61ddd0cf3ddbc32543e72c2b Closes https://github.com/official-stockfish/Stockfish/pull/3890 Bench: 4464962 --- AUTHORS | 1 + src/search.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index d7e6dc98663..f49c1db0ede 100644 --- a/AUTHORS +++ b/AUTHORS @@ -166,6 +166,7 @@ Rodrigo Exterckötter Tjäder Ron Britvich (Britvich) Ronald de Man (syzygy1, syzygy) rqs +Rui Coelho (ruicoelhopedro) Ryan Schmitt Ryan Takker Sami Kiminki (skiminki) diff --git a/src/search.cpp b/src/search.cpp index 58873c8981f..c81496d17a3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -589,7 +589,7 @@ namespace { bool givesCheck, improving, didLMR, priorCapture; bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture; Piece movedPiece; - int moveCount, captureCount, quietCount, bestMoveCount, improvement; + int moveCount, captureCount, quietCount, bestMoveCount, improvement, complexity; // Step 1. Initialize node ss->inCheck = pos.checkers(); @@ -760,6 +760,7 @@ namespace { ss->staticEval = eval = VALUE_NONE; improving = false; improvement = 0; + complexity = 0; goto moves_loop; } else if (ss->ttHit) @@ -803,6 +804,7 @@ namespace { : 200; improving = improvement > 0; + complexity = abs(ss->staticEval - (us == WHITE ? eg_value(pos.psq_score()) : -eg_value(pos.psq_score()))); // Step 7. Futility pruning: child node (~25 Elo). // The depth condition is important for mate finding. @@ -818,7 +820,7 @@ namespace { && (ss-1)->statScore < 23767 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 20 * depth - improvement / 15 + 204 + && ss->staticEval >= beta - 20 * depth - improvement / 15 + 204 + complexity / 25 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) From d11101e4c6cd6aafca06e07d24e1c7026c92f6e7 Mon Sep 17 00:00:00 2001 From: proukornew Date: Fri, 17 Dec 2021 01:30:23 +0300 Subject: [PATCH 0772/1766] Improve logic on mingw There is no need to point g++, if we explicitly choose mingw. Now for cygwin: make COMP=mingw ARCH=x86-64-modern build closes https://github.com/official-stockfish/Stockfish/pull/3860 No functional change --- src/Makefile | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/Makefile b/src/Makefile index 0e889888549..90e596afe92 100644 --- a/src/Makefile +++ b/src/Makefile @@ -368,22 +368,18 @@ endif ifeq ($(COMP),mingw) comp=mingw - ifeq ($(KERNEL),Linux) - ifeq ($(bits),64) - ifeq ($(shell which x86_64-w64-mingw32-c++-posix),) - CXX=x86_64-w64-mingw32-c++ - else - CXX=x86_64-w64-mingw32-c++-posix - endif + ifeq ($(bits),64) + ifeq ($(shell which x86_64-w64-mingw32-c++-posix 2> /dev/null),) + CXX=x86_64-w64-mingw32-c++ else - ifeq ($(shell which i686-w64-mingw32-c++-posix),) - CXX=i686-w64-mingw32-c++ - else - CXX=i686-w64-mingw32-c++-posix - endif + CXX=x86_64-w64-mingw32-c++-posix endif else - CXX=g++ + ifeq ($(shell which i686-w64-mingw32-c++-posix 2> /dev/null),) + CXX=i686-w64-mingw32-c++ + else + CXX=i686-w64-mingw32-c++-posix + endif endif CXXFLAGS += -pedantic -Wextra -Wshadow From 2b0372319d2a6797c49cb24dca5da221a669e36a Mon Sep 17 00:00:00 2001 From: Rui Coelho Date: Mon, 17 Jan 2022 16:51:20 +0000 Subject: [PATCH 0773/1766] Use average complexity for time management This patch is a variant of the idea by locutus2 (https://tests.stockfishchess.org/tests/view/61e1f24cb1f9959fe5d88168) to adjust the total time depending on the average complexity of the position. Passed STC LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 39664 W: 10765 L: 10487 D: 18412 Ptnml(0-2): 162, 4213, 10837, 4425, 195 https://tests.stockfishchess.org/tests/view/61e2df8b65a644da8c9ea708 Passed LTC LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 127656 W: 34505 L: 34028 D: 59123 Ptnml(0-2): 116, 12435, 38261, 12888, 128 https://tests.stockfishchess.org/tests/view/61e31db5babab931824dff5e closes https://github.com/official-stockfish/Stockfish/pull/3892 Bench: 4464962 --- src/misc.h | 3 +++ src/search.cpp | 8 +++++++- src/thread.h | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/misc.h b/src/misc.h index 688d00e7e55..b962673384b 100644 --- a/src/misc.h +++ b/src/misc.h @@ -105,6 +105,9 @@ class RunningAverage { bool is_greater(int64_t a, int64_t b) { return b * average > a * PERIOD * RESOLUTION ; } + int64_t value() + { return average / (PERIOD * RESOLUTION); } + private : static constexpr int64_t PERIOD = 4096; static constexpr int64_t RESOLUTION = 1024; diff --git a/src/search.cpp b/src/search.cpp index c81496d17a3..c9d5da64c2f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -329,6 +329,7 @@ void Thread::search() { doubleExtensionAverage[WHITE].set(0, 100); // initialize the running average at 0% doubleExtensionAverage[BLACK].set(0, 100); // initialize the running average at 0% + complexityAverage.set(232, 1); nodesLastExplosive = nodes; nodesLastNormal = nodes; @@ -496,7 +497,10 @@ void Thread::search() { double reduction = (1.47 + mainThread->previousTimeReduction) / (2.32 * timeReduction); double bestMoveInstability = 1.073 + std::max(1.0, 2.25 - 9.9 / rootDepth) * totBestMoveChanges / Threads.size(); - double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability; + int complexity = mainThread->complexityAverage.value(); + double complexPosition = std::clamp(1.0 + (complexity - 232) / 1750.0, 0.5, 1.5); + + double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability * complexPosition; // Cap used time in case of a single legal move for a better viewer experience in tournaments // yielding correct scores and sufficiently fast moves. @@ -806,6 +810,8 @@ namespace { improving = improvement > 0; complexity = abs(ss->staticEval - (us == WHITE ? eg_value(pos.psq_score()) : -eg_value(pos.psq_score()))); + thisThread->complexityAverage.update(complexity); + // Step 7. Futility pruning: child node (~25 Elo). // The depth condition is important for mate finding. if ( !ss->ttPv diff --git a/src/thread.h b/src/thread.h index a6b0b5a0bc9..c3d38f3cf31 100644 --- a/src/thread.h +++ b/src/thread.h @@ -61,6 +61,7 @@ class Thread { Material::Table materialTable; size_t pvIdx, pvLast; RunningAverage doubleExtensionAverage[COLOR_NB]; + RunningAverage complexityAverage; uint64_t nodesLastExplosive; uint64_t nodesLastNormal; std::atomic nodes, tbHits, bestMoveChanges; From 48bf1a386f031947d059a0dc26616366b0f2d5d3 Mon Sep 17 00:00:00 2001 From: ppigazzini Date: Wed, 19 Jan 2022 16:56:38 +0100 Subject: [PATCH 0774/1766] Add msys2 Clang x86_64 to GitHub Action matrix Also use Windows Server 2022 virtual environment for msys2 builds. closes https://github.com/official-stockfish/Stockfish/pull/3893 No functional change --- .github/workflows/stockfish.yml | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index 54b0cb1214a..33126a11e35 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -61,8 +61,8 @@ jobs: shell: 'bash {0}' } - { - name: "Windows 2019 Mingw-w64 GCC x86_64", - os: windows-2019, + name: "Windows 2022 Mingw-w64 GCC x86_64", + os: windows-2022, compiler: g++, comp: gcc, run_expensive_tests: false, @@ -73,8 +73,8 @@ jobs: shell: 'msys2 {0}' } - { - name: "Windows 2019 Mingw-w64 GCC i686", - os: windows-2019, + name: "Windows 2022 Mingw-w64 GCC i686", + os: windows-2022, compiler: g++, comp: gcc, run_expensive_tests: false, @@ -84,6 +84,18 @@ jobs: msys_env: 'i686', shell: 'msys2 {0}' } + - { + name: "Windows 2022 Mingw-w64 Clang x86_64", + os: windows-2022, + compiler: clang++, + comp: clang, + run_expensive_tests: false, + run_32bit_tests: false, + run_64bit_tests: true, + msys_sys: 'clang64', + msys_env: 'clang-x86_64', + shell: 'msys2 {0}' + } defaults: run: @@ -105,7 +117,7 @@ jobs: uses: msys2/setup-msys2@v2 with: msystem: ${{matrix.config.msys_sys}} - install: mingw-w64-${{matrix.config.msys_env}}-gcc make git expect + install: mingw-w64-${{matrix.config.msys_env}}-${{matrix.config.comp}} make git expect - name: Download the used network from the fishtest framework run: | From 67062637f48e7d63e750dee63ba9e881ee6d9382 Mon Sep 17 00:00:00 2001 From: ppigazzini Date: Mon, 17 Jan 2022 14:03:16 +0100 Subject: [PATCH 0775/1766] Improve Makefile for Windows native builds A Windows Native Build (WNB) can be done: - on Windows, using a recent mingw-w64 g++/clang compiler distributed by msys2, cygwin and others - on Linux, using mingw-w64 g++ to cross compile Improvements: - check for a WNB in a proper way and set a variable to simplify the code - set the proper EXE for a WNB - use the proper name for the mingw-w64 clang compiler - use the static linking for a WNB - use wine to make a PGO cross compile on Linux (also with Intel SDE) - enable the LTO build for mingw-w64 g++ compiler - set `lto=auto` to use the make's job server, if available, or otherwise to fall back to autodetection of the number of CPU threads - clean up all the temporary LTO files saved in the local directory Tested on: - msys2 MINGW64 (g++), UCRT64 (g++), MINGW32 (g++), CLANG64 (clang) environments - cygwin mingw-w64 g++ - Ubuntu 18.04 & 21.10 mingw-w64 PGO cross compile (also with Intel SDE) closes #3891 No functional change --- src/Makefile | 80 +++++++++++++++++++++++++--------------------------- 1 file changed, 39 insertions(+), 41 deletions(-) diff --git a/src/Makefile b/src/Makefile index 90e596afe92..1b121299488 100644 --- a/src/Makefile +++ b/src/Makefile @@ -19,11 +19,27 @@ ### Section 1. General Configuration ### ========================================================================== +### Establish the operating system name +KERNEL = $(shell uname -s) +ifeq ($(KERNEL),Linux) + OS = $(shell uname -o) +endif + +### Target Windows OS +ifeq ($(OS),Windows_NT) + target_windows = yes +else ifeq ($(COMP),mingw) + target_windows = yes + ifeq ($(WINE_PATH),) + WINE_PATH = $(shell which wine) + endif +endif + ### Executable name -ifeq ($(COMP),mingw) -EXE = stockfish.exe +ifeq ($(target_windows),yes) + EXE = stockfish.exe else -EXE = stockfish + EXE = stockfish endif ### Installation dir definitions @@ -32,9 +48,9 @@ BINDIR = $(PREFIX)/bin ### Built-in benchmark for pgo-builds ifeq ($(SDE_PATH),) - PGOBENCH = ./$(EXE) bench + PGOBENCH = $(WINE_PATH) ./$(EXE) bench else - PGOBENCH = $(SDE_PATH) -- ./$(EXE) bench + PGOBENCH = $(SDE_PATH) -- $(WINE_PATH) ./$(EXE) bench endif ### Source and object files @@ -47,12 +63,6 @@ OBJS = $(notdir $(SRCS:.cpp=.o)) VPATH = syzygy:nnue:nnue/features -### Establish the operating system name -KERNEL = $(shell uname -s) -ifeq ($(KERNEL),Linux) - OS = $(shell uname -o) -endif - ### ========================================================================== ### Section 2. High-level Configuration ### ========================================================================== @@ -365,6 +375,10 @@ ifeq ($(COMP),gcc) endif endif +ifeq ($(target_windows),yes) + LDFLAGS += -static +endif + ifeq ($(COMP),mingw) comp=mingw @@ -381,9 +395,7 @@ ifeq ($(COMP),mingw) CXX=i686-w64-mingw32-c++-posix endif endif - CXXFLAGS += -pedantic -Wextra -Wshadow - LDFLAGS += -static endif ifeq ($(COMP),icc) @@ -395,19 +407,19 @@ endif ifeq ($(COMP),clang) comp=clang CXX=clang++ + ifeq ($(target_windows),yes) + CXX=x86_64-w64-mingw32-clang++ + endif + CXXFLAGS += -pedantic -Wextra -Wshadow - ifneq ($(KERNEL),Darwin) - ifneq ($(KERNEL),OpenBSD) - ifneq ($(KERNEL),FreeBSD) - ifneq ($(findstring MINGW,$(KERNEL)),MINGW) + ifeq ($(filter $(KERNEL),Darwin OpenBSD FreeBSD),) + ifeq ($(target_windows),) ifneq ($(RTLIB),compiler-rt) LDFLAGS += -latomic endif endif endif - endif - endif ifeq ($(arch),$(filter $(arch),armv7 armv8)) ifeq ($(OS),Android) @@ -418,11 +430,6 @@ ifeq ($(COMP),clang) CXXFLAGS += -m$(bits) LDFLAGS += -m$(bits) endif - - ifeq ($(findstring MINGW,$(KERNEL)),MINGW) - LDFLAGS += -static - endif - endif ifeq ($(KERNEL),Darwin) @@ -656,9 +663,7 @@ ifeq ($(optimize),yes) ifeq ($(debug), no) ifeq ($(comp),clang) CXXFLAGS += -flto - ifneq ($(findstring MINGW,$(KERNEL)),) - CXXFLAGS += -fuse-ld=lld - else ifneq ($(findstring MSYS,$(KERNEL)),) + ifeq ($(target_windows),yes) CXXFLAGS += -fuse-ld=lld endif LDFLAGS += $(CXXFLAGS) @@ -669,25 +674,17 @@ ifeq ($(debug), no) ifeq ($(gccisclang),) CXXFLAGS += -flto LDFLAGS += $(CXXFLAGS) -flto=jobserver - ifneq ($(findstring MINGW,$(KERNEL)),) - LDFLAGS += -save-temps - else ifneq ($(findstring MSYS,$(KERNEL)),) - LDFLAGS += -save-temps - endif else CXXFLAGS += -flto LDFLAGS += $(CXXFLAGS) endif -# To use LTO and static linking on windows, the tool chain requires a recent gcc: -# gcc version 10.1 in msys2 or TDM-GCC version 9.2 are known to work, older might not. -# So, only enable it for a cross from Linux by default. +# To use LTO and static linking on Windows, +# the tool chain requires gcc version 10.1 or later. else ifeq ($(comp),mingw) - ifeq ($(KERNEL),Linux) ifneq ($(arch),i386) - CXXFLAGS += -flto - LDFLAGS += $(CXXFLAGS) -flto=jobserver - endif + CXXFLAGS += -flto=auto + LDFLAGS += $(CXXFLAGS) -save-temps endif endif endif @@ -843,8 +840,9 @@ profileclean: @rm -rf profdir @rm -f bench.txt *.gcda *.gcno ./syzygy/*.gcda ./nnue/*.gcda ./nnue/features/*.gcda *.s @rm -f stockfish.profdata *.profraw - @rm -f stockfish.exe.lto_wrapper_args - @rm -f stockfish.exe.ltrans.out + @rm -f stockfish.*args* + @rm -f stockfish.*lt* + @rm -f stockfish.res @rm -f ./-lstdc++.res default: From 77cf5704b6deda52171dafeb2fae370273ebd797 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 20 Jan 2022 17:49:27 +0100 Subject: [PATCH 0776/1766] Revert -flto=auto on mingw causes issues on some installations (glinscott/fishtest#1255). closes https://github.com/official-stockfish/Stockfish/pull/3898 No functional change --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 1b121299488..5f3e739f788 100644 --- a/src/Makefile +++ b/src/Makefile @@ -683,7 +683,7 @@ ifeq ($(debug), no) # the tool chain requires gcc version 10.1 or later. else ifeq ($(comp),mingw) ifneq ($(arch),i386) - CXXFLAGS += -flto=auto + CXXFLAGS += -flto LDFLAGS += $(CXXFLAGS) -save-temps endif endif From 9083050be692b2d9a4f281e78b967755e00cfc39 Mon Sep 17 00:00:00 2001 From: "J. Oster" Date: Sat, 15 Jan 2022 18:18:52 +0100 Subject: [PATCH 0777/1766] Simplify limiting extensions. Replace the current method for limiting extensions to avoid search getting stuck with a much simpler method. the test position in https://github.com/official-stockfish/Stockfish/commit/73018a03375b4b72ee482eb5a4a2152d7e4f0aac can still be searched without stuck search. fixes #3815 where the search now makes progress with rootDepth shows robust behavior in a d10 search for 1M positions. passed STC https://tests.stockfishchess.org/tests/view/61e303e3babab931824dfb18 LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 57568 W: 15449 L: 15327 D: 26792 Ptnml(0-2): 243, 6211, 15779, 6283, 268 passed LTC https://tests.stockfishchess.org/tests/view/61e3586cbabab931824e091c LLR: 2.96 (-2.94,2.94) <-2.25,0.25> Total: 128200 W: 34632 L: 34613 D: 58955 Ptnml(0-2): 124, 12559, 38710, 12588, 119 closes https://github.com/official-stockfish/Stockfish/pull/3899 Bench: 4550528 --- src/search.cpp | 152 +++++++++++++++++++------------------------------ src/thread.h | 7 +-- 2 files changed, 59 insertions(+), 100 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c9d5da64c2f..792c9729642 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -88,30 +88,6 @@ namespace { return VALUE_DRAW + Value(2 * (thisThread->nodes & 1) - 1); } - // Check if the current thread is in a search explosion - ExplosionState search_explosion(Thread* thisThread) { - - uint64_t nodesNow = thisThread->nodes; - bool explosive = thisThread->doubleExtensionAverage[WHITE].is_greater(2, 100) - || thisThread->doubleExtensionAverage[BLACK].is_greater(2, 100); - - if (explosive) - thisThread->nodesLastExplosive = nodesNow; - else - thisThread->nodesLastNormal = nodesNow; - - if ( explosive - && thisThread->state == EXPLOSION_NONE - && nodesNow - thisThread->nodesLastNormal > 6000000) - thisThread->state = MUST_CALM_DOWN; - - if ( thisThread->state == MUST_CALM_DOWN - && nodesNow - thisThread->nodesLastExplosive > 6000000) - thisThread->state = EXPLOSION_NONE; - - return thisThread->state; - } - // Skill structure is used to implement strength limit. If we have an uci_elo then // we convert it 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) @@ -327,16 +303,11 @@ void Thread::search() { multiPV = std::min(multiPV, rootMoves.size()); - doubleExtensionAverage[WHITE].set(0, 100); // initialize the running average at 0% - doubleExtensionAverage[BLACK].set(0, 100); // initialize the running average at 0% complexityAverage.set(232, 1); - nodesLastExplosive = nodes; - nodesLastNormal = nodes; - state = EXPLOSION_NONE; - trend = SCORE_ZERO; - optimism[ us] = Value(25); - optimism[~us] = -optimism[us]; + trend = SCORE_ZERO; + optimism[ us] = Value(25); + optimism[~us] = -optimism[us]; int searchAgainCounter = 0; @@ -548,14 +519,6 @@ namespace { template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) { - Thread* thisThread = pos.this_thread(); - - // Step 0. Limit search explosion - if ( ss->ply > 10 - && search_explosion(thisThread) == MUST_CALM_DOWN - && depth > (ss-1)->depth) - depth = (ss-1)->depth; - constexpr bool PvNode = nodeType != NonPV; constexpr bool rootNode = nodeType == Root; const Depth maxNextDepth = rootNode ? depth : depth + 1; @@ -596,6 +559,7 @@ namespace { int moveCount, captureCount, quietCount, bestMoveCount, improvement, complexity; // Step 1. Initialize node + Thread* thisThread = pos.this_thread(); ss->inCheck = pos.checkers(); priorCapture = pos.captured_piece(); Color us = pos.side_to_move(); @@ -643,9 +607,6 @@ namespace { ss->depth = depth; Square prevSq = to_sq((ss-1)->currentMove); - // Update the running average statistics for double extensions - thisThread->doubleExtensionAverage[us].update(ss->depth > (ss-1)->depth); - // Initialize statScore to zero for the grandchildren of the current position. // So statScore is shared between all grandchildren and only the first grandchild // starts with statScore = 0. Later grandchildren start with the last calculated @@ -1077,64 +1038,67 @@ namespace { } // Step 14. Extensions (~66 Elo) - - // Singular extension search (~58 Elo). If all moves but one fail low on a - // search of (alpha-s, beta-s), and just one fails high on (alpha, beta), - // 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 ( !rootNode - && depth >= 6 + 2 * (PvNode && tte->is_pv()) - && move == ttMove - && !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) + // We take care to not overdo to avoid search getting stuck. + if (ss->ply < thisThread->rootDepth * 2) { - Value singularBeta = ttValue - 3 * depth; - Depth singularDepth = (depth - 1) / 2; + // Singular extension search (~58 Elo). If all moves but one fail low on a + // search of (alpha-s, beta-s), and just one fails high on (alpha, beta), + // 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 ( !rootNode + && depth >= 6 + 2 * (PvNode && tte->is_pv()) + && move == ttMove + && !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) + { + Value singularBeta = ttValue - 3 * depth; + Depth singularDepth = (depth - 1) / 2; - ss->excludedMove = move; - value = search(pos, ss, singularBeta - 1, singularBeta, singularDepth, cutNode); - ss->excludedMove = MOVE_NONE; + ss->excludedMove = move; + value = search(pos, ss, singularBeta - 1, singularBeta, singularDepth, cutNode); + ss->excludedMove = MOVE_NONE; - if (value < singularBeta) - { - extension = 1; + if (value < singularBeta) + { + extension = 1; - // Avoid search explosion by limiting the number of double extensions - if ( !PvNode - && value < singularBeta - 75 - && ss->doubleExtensions <= 6) - extension = 2; + // Avoid search explosion by limiting the number of double extensions + if ( !PvNode + && value < singularBeta - 75 + && ss->doubleExtensions <= 6) + extension = 2; + } + + // Multi-cut pruning + // Our ttMove is assumed to fail high, and now we failed high also on a reduced + // search without the ttMove. So we assume this expected Cut-node is not singular, + // that multiple moves fail high, and we can prune the whole subtree by returning + // a soft bound. + else if (singularBeta >= beta) + return singularBeta; + + // If the eval of ttMove is greater than beta, we reduce it (negative extension) + else if (ttValue >= beta) + extension = -2; } - // Multi-cut pruning - // Our ttMove is assumed to fail high, and now we failed high also on a reduced - // search without the ttMove. So we assume this expected Cut-node is not singular, - // that multiple moves fail high, and we can prune the whole subtree by returning - // a soft bound. - else if (singularBeta >= beta) - return singularBeta; - - // If the eval of ttMove is greater than beta, we reduce it (negative extension) - else if (ttValue >= beta) - extension = -2; - } + // Check extensions (~1 Elo) + else if ( givesCheck + && depth > 6 + && abs(ss->staticEval) > 100) + extension = 1; - // Check extensions (~1 Elo) - else if ( givesCheck - && depth > 6 - && abs(ss->staticEval) > 100) - extension = 1; - - // Quiet ttMove extensions (~0 Elo) - else if ( PvNode - && move == ttMove - && move == ss->killers[0] - && (*contHist[0])[movedPiece][to_sq(move)] >= 10000) - extension = 1; + // Quiet ttMove extensions (~0 Elo) + else if ( PvNode + && move == ttMove + && move == ss->killers[0] + && (*contHist[0])[movedPiece][to_sq(move)] >= 10000) + extension = 1; + } // Add extension to new depth newDepth += extension; diff --git a/src/thread.h b/src/thread.h index c3d38f3cf31..594a8ea29c5 100644 --- a/src/thread.h +++ b/src/thread.h @@ -60,16 +60,11 @@ class Thread { Pawns::Table pawnsTable; Material::Table materialTable; size_t pvIdx, pvLast; - RunningAverage doubleExtensionAverage[COLOR_NB]; RunningAverage complexityAverage; - uint64_t nodesLastExplosive; - uint64_t nodesLastNormal; std::atomic nodes, tbHits, bestMoveChanges; - Value bestValue; int selDepth, nmpMinPly; Color nmpColor; - ExplosionState state; - Value optimism[COLOR_NB]; + Value bestValue, optimism[COLOR_NB]; Position rootPos; StateInfo rootState; From bddd38c45e1f1457e1435cd066036e98f54d7397 Mon Sep 17 00:00:00 2001 From: pschneider1968 <36973164+pschneider1968@users.noreply.github.com> Date: Fri, 21 Jan 2022 14:11:53 +0100 Subject: [PATCH 0778/1766] Fix Makefile for Android NDK cross-compile For cross-compiling to Android on windows, the Makefile needs some tweaks. Tested with Android NDK 23.1.7779620 and 21.4.7075529, using Windows 10 with clean MSYS2 environment (i.e. no MINGW/GCC/Clang toolchain in PATH) and Fedora 35, with build target: build ARCH=armv8 COMP=ndk The resulting binary runs fine inside Droidfish on my Samsung Galaxy Note20 Ultra and Samsung Galaxy Tab S7+ Other builds tested to exclude regressions: MINGW64/Clang64 build on Windows; MINGW64 cross build, native Clang and GCC builds on Fedora. wiki docs https://github.com/glinscott/fishtest/wiki/Cross-compiling-Stockfish-for-Android-on-Windows-and-Linux closes https://github.com/official-stockfish/Stockfish/pull/3901 No functional change --- src/Makefile | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Makefile b/src/Makefile index 5f3e739f788..9666f138463 100644 --- a/src/Makefile +++ b/src/Makefile @@ -27,7 +27,9 @@ endif ### Target Windows OS ifeq ($(OS),Windows_NT) - target_windows = yes + ifneq ($(COMP),ndk) + target_windows = yes + endif else ifeq ($(COMP),mingw) target_windows = yes ifeq ($(WINE_PATH),) @@ -451,11 +453,19 @@ ifeq ($(COMP),ndk) ifeq ($(arch),armv7) CXX=armv7a-linux-androideabi16-clang++ CXXFLAGS += -mthumb -march=armv7-a -mfloat-abi=softfp -mfpu=neon - STRIP=arm-linux-androideabi-strip + ifneq ($(shell which arm-linux-androideabi-strip 2>/dev/null),) + STRIP=arm-linux-androideabi-strip + else + STRIP=llvm-strip + endif endif ifeq ($(arch),armv8) CXX=aarch64-linux-android21-clang++ - STRIP=aarch64-linux-android-strip + ifneq ($(shell which aarch64-linux-android-strip 2>/dev/null),) + STRIP=aarch64-linux-android-strip + else + STRIP=llvm-strip + endif endif LDFLAGS += -static-libstdc++ -pie -lm -latomic endif @@ -801,7 +811,7 @@ strip: install: -mkdir -p -m 755 $(BINDIR) -cp $(EXE) $(BINDIR) - -strip $(BINDIR)/$(EXE) + $(STRIP) $(BINDIR)/$(EXE) # clean all clean: objclean profileclean @@ -833,7 +843,7 @@ net: # clean binaries and objects objclean: - @rm -f $(EXE) *.o ./syzygy/*.o ./nnue/*.o ./nnue/features/*.o + @rm -f stockfish stockfish.exe *.o ./syzygy/*.o ./nnue/*.o ./nnue/features/*.o # clean auxiliary profiling files profileclean: From 8b4afcf8f7a38ee3060f39d563aad5956fe723d7 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Tue, 25 Jan 2022 02:22:03 +0300 Subject: [PATCH 0779/1766] Scale child node futility pruning with previous move history. Idea is to do more futility pruning if previous move has bad histories and less if it has good histories. passed STC https://tests.stockfishchess.org/tests/view/61e3757fbabab931824e0db7 LLR: 2.96 (-2.94,2.94) <0.00,2.50> Total: 156816 W: 42282 L: 41777 D: 72757 Ptnml(0-2): 737, 17775, 40913, 18212, 771 passed LTC https://tests.stockfishchess.org/tests/view/61e43496928632f7813a5535 LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 349968 W: 94612 L: 93604 D: 161752 Ptnml(0-2): 300, 35934, 101550, 36858, 342 closes https://github.com/official-stockfish/Stockfish/pull/3903 bench 4720954 --- src/search.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 792c9729642..c3360ad14d4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -777,7 +777,8 @@ namespace { // The depth condition is important for mate finding. if ( !ss->ttPv && depth < 9 - && eval - futility_margin(depth, improving) >= beta + && eval - futility_margin(depth, improving) - (ss-1)->statScore / 256 >= beta + && eval >= beta && eval < 15000) // 50% larger than VALUE_KNOWN_WIN, but smaller than TB wins. return eval; From 90d051952f4fce415f09f316e24e1701aafa7a92 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 29 Jan 2022 06:39:40 +0300 Subject: [PATCH 0780/1766] Do stats updates after LMR for captures Since captures that are in LMR use continuation histories of corresponding quiet moves it makes sense to update this histories if this capture passes LMR by analogy to existing logic for quiet moves. Passed STC https://tests.stockfishchess.org/tests/view/61f367eef7fba9f1a4f1318b LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 208464 W: 56006 L: 55407 D: 97051 Ptnml(0-2): 964, 23588, 54655, 23935, 1090 Passed LTC https://tests.stockfishchess.org/tests/view/61f41e34f7fba9f1a4f15241 LLR: 2.96 (-2.94,2.94) <0.50,3.00> Total: 69144 W: 18793 L: 18441 D: 31910 Ptnml(0-2): 65, 6982, 20142, 7302, 81 closes https://github.com/official-stockfish/Stockfish/pull/3910 bench 4637392 --- src/search.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index c3360ad14d4..8d5ad97975d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1194,11 +1194,14 @@ namespace { value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth + doDeeperSearch, !cutNode); // If the move passed LMR update its stats - if (didLMR && !captureOrPromotion) + if (didLMR) { int bonus = value > alpha ? stat_bonus(newDepth) : -stat_bonus(newDepth); + if (captureOrPromotion) + bonus /= 4; + update_continuation_histories(ss, movedPiece, to_sq(move), bonus); } } From 50200de5af09db3a9143082e4e66baef11b4be2a Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Mon, 31 Jan 2022 07:25:10 +0300 Subject: [PATCH 0781/1766] Cleanup and update CPU contributors closes https://github.com/official-stockfish/Stockfish/pull/3917 No functional change --- Top CPU Contributors.txt | 113 ++++++++++++++++++++------------------- src/types.h | 5 -- 2 files changed, 59 insertions(+), 59 deletions(-) diff --git a/Top CPU Contributors.txt b/Top CPU Contributors.txt index 718d7cca177..4bc96cde7aa 100644 --- a/Top CPU Contributors.txt +++ b/Top CPU Contributors.txt @@ -1,90 +1,91 @@ -Contributors to Fishtest with >10,000 CPU hours, as of 2022-01-08. +Contributors to Fishtest with >10,000 CPU hours, as of 2022-02-05. Thank you! Username CPU Hours Games played ------------------------------------------------------------------ -noobpwnftw 30323785 2111752181 -mlang 2597136 178003354 -dew 1598255 95747056 -technologov 1395130 59347018 -grandphish2 1028906 63396841 +noobpwnftw 30730952 2158431735 +mlang 2729669 187335452 +technologov 1696847 74478658 +dew 1635640 97483012 +grandphish2 1062754 64955639 tvijlbrief 795993 51894442 -TueRens 737922 46359276 -okrout 719183 57150314 +okrout 773704 63465204 +TueRens 766198 47770388 mibere 703840 46867607 -JojoM 689134 42001146 -linrock 594355 16779359 -pemo 575248 28386103 -gvreuls 509219 33205908 -cw 500695 33575803 -fastgm 479238 28830588 +JojoM 703005 42689868 +pemo 634102 29868807 +linrock 626939 17408017 +gvreuls 517442 33605006 +cw 503905 33850487 +fastgm 482847 29004732 crunchy 427035 27344275 -CSU_Dynasty 410969 27877556 -ctoks 393901 26299629 -oz 354661 26331020 +CSU_Dynasty 415864 28116776 +ctoks 403102 26737127 +oz 357710 26490208 +bcross 331095 23165889 Fisherman 327231 21829379 -bcross 325119 22871639 -velislav 320581 20663382 -leszek 291605 18475167 -Dantist 239411 15236750 -mgrabiak 229336 15004308 +velislav 321708 20729264 +leszek 303654 19063973 +Dantist 251015 15843226 +mgrabiak 231973 15162494 glinscott 217799 13780820 -robal 211837 13563250 +robal 213960 13665726 nordlandia 211692 13484886 -drabel 200377 13730626 +drabel 200914 13755384 bking_US 198894 11876016 +mhoram 180229 11610075 Thanar 179852 12365359 -vdv 175535 9904264 -mhoram 173134 11257113 +vdv 175544 9904472 spams 157128 10319326 marrco 150300 9402229 sqrt2 147963 9724586 -vdbergh 137425 8954767 +vdbergh 137429 8955089 CoffeeOne 137100 5024116 malala 136182 8002293 xoto 133759 9159372 -davar 122113 7961971 +rpngn 131285 8657757 +davar 122661 7996937 dsmith 122059 7570238 amicic 119659 7937885 -rpngn 118952 8100045 Data 113305 8220352 BrunoBanani 112960 7436849 CypressChess 108321 7759588 MaZePallas 102823 6633619 sterni1971 100532 5880772 sunu 100167 7040199 +DesolatedDodo 99038 6414626 ElbertoOne 99028 7023771 skiminki 98123 6478402 -DesolatedDodo 93686 6139198 brabos 92118 6186135 -cuistot 90357 5350988 +cuistot 90358 5351004 psk 89957 5984901 racerschmacer 85712 6119648 Vizvezdenec 83761 5344740 +sschnee 83003 4840890 0x3C33 82614 5271253 BRAVONE 81239 5054681 -sschnee 78091 4678078 nssy 76497 5259388 teddybaer 75125 5407666 Pking_cda 73776 5293873 +zeryl 73335 4774257 jromang 72192 5057715 solarlight 70517 5028306 dv8silencer 70287 3883992 Bobo1239 68515 4652287 -zeryl 68203 4516139 manap 66273 4121774 tinker 64333 4268790 -yurikvelo 61692 4262042 +yurikvelo 63371 4335060 qurashee 61208 3429862 robnjr 57262 4053117 +Wolfgang 57014 3561352 Freja 56938 3733019 ttruscott 56010 3680085 rkl 55132 4164467 -Wolfgang 54087 3415872 renouve 53811 3501516 finfish 51360 3370515 eva42 51272 3599691 -eastorwest 51055 3451203 +Calis007 51182 3131552 +eastorwest 51058 3451555 rap 49985 3219146 pb00067 49727 3298270 Spprtr 48260 3141959 @@ -92,11 +93,11 @@ bigpen0r 47667 3336927 ronaldjerum 47654 3240695 MaxKlaxxMiner 47584 2972142 biffhero 46564 3111352 +megaman7de 45992 2952006 Fifis 45843 3088497 VoyagerOne 45476 3452465 speedycpu 43842 3003273 jbwiebe 43305 2805433 -megaman7de 43042 2823256 Antihistamine 41788 2761312 mhunt 41735 2691355 homyur 39893 2850481 @@ -109,18 +110,17 @@ jmdana 36157 2210661 strelock 34716 2074055 EthanOConnor 33370 2090311 slakovv 32915 2021889 -Calis007 32024 2163604 +armo9494 32129 2551682 +tolkki963 32114 1932256 manapbk 30987 1810399 DMBK 30675 2383552 Prcuvu 30377 2170122 anst 30301 2190091 -armo9494 30198 2438202 jkiiski 30136 1904470 -tolkki963 29918 1822290 +gopeto 29886 1979118 hyperbolic.tom 29840 2017394 chuckstablers 29659 2093438 Pyafue 29650 1902349 -gopeto 28881 1896862 OuaisBla 27636 1578800 chriswk 26902 1868317 achambord 26582 1767323 @@ -132,11 +132,12 @@ Sharaf_DG 24765 1786697 ncfish1 24411 1520927 rodneyc 24275 1410450 agg177 23890 1395014 +belzedar94 23707 1593860 JanErik 23408 1703875 Isidor 23388 1680691 Norabor 23339 1602636 +Ente 23093 1642458 cisco2015 22897 1762669 -Ente 22810 1628234 Zirie 22542 1472937 team-oh 22272 1636708 MazeOfGalious 21978 1629593 @@ -148,12 +149,12 @@ nesoneg 21494 1463031 sphinx 21211 1384728 jjoshua2 21001 1423089 horst.prack 20878 1465656 +user213718 20783 1379584 0xB00B1ES 20590 1208666 j3corre 20405 941444 Adrian.Schmidt123 20316 1281436 wei 19973 1745989 -belzedar94 19818 1434252 -user213718 19608 1334650 +Roady 19848 1335928 rstoesser 19569 1293588 eudhan 19274 1283717 vulcan 18871 1729392 @@ -163,24 +164,24 @@ ville 17883 1384026 chris 17698 1487385 purplefishies 17595 1092533 dju 17353 978595 +kdave 17183 1242754 DragonLord 17014 1162790 +thirdlife 16996 447356 +spcc 16932 1130940 +fishtester 16644 1123000 +Ulysses 16490 1184400 IgorLeMasson 16064 1147232 -Roady 15677 1121476 ako027ako 15671 1173203 -kdave 15539 1160356 Nikolay.IT 15154 1068349 Andrew Grant 15114 895539 OssumOpossum 14857 1007129 -spcc 14838 1034050 Karby 14808 867120 +AndreasKrug 14608 1152093 enedene 14476 905279 -fishtester 14411 1016252 jsys14 14340 844792 bpfliegel 14298 884523 -AndreasKrug 14096 1126301 mpx86 14019 759568 jpulman 13982 870599 -Ulysses 13977 1073410 crocogoat 13803 1117422 joster 13794 950160 Nesa92 13786 1114691 @@ -188,12 +189,13 @@ mbeier 13650 1044928 Hjax 13535 915487 Dark_wizzie 13422 1007152 Rudolphous 13244 883140 +MarcusTullius 13221 843169 Machariel 13010 863104 mabichito 12903 749391 thijsk 12886 722107 AdrianSA 12860 804972 +infinigon 12807 937332 Flopzee 12698 894821 -infinigon 12638 933684 fatmurphy 12547 853210 scuzzi 12511 845761 SapphireBrand 12416 969604 @@ -202,19 +204,21 @@ Farseer 12249 694108 pgontarz 12151 848794 stocky 11954 699440 mschmidt 11941 803401 +dbernier 11609 818636 Maxim 11543 836024 +pirt 11516 894513 infinity 11470 727027 -pirt 11434 889369 aga 11409 695071 torbjo 11395 729145 Thomas A. Anderson 11372 732094 savage84 11358 670860 -FormazChar 11304 847663 -dbernier 11274 806566 +markkulix 11331 739098 +FormazChar 11308 847735 d64 11263 789184 MooTheCow 11237 720174 snicolet 11106 869170 ali-al-zhrani 11098 768494 +whelanh 11067 235676 basepi 10637 744851 Cubox 10621 826448 michaelrpg 10509 739239 @@ -223,3 +227,4 @@ dzjp 10343 732529 Garruk 10332 703905 ols 10259 570669 lbraesch 10252 647825 +Jackfish 10098 682338 diff --git a/src/types.h b/src/types.h index a3a873fa4e7..cf42bc9f8ff 100644 --- a/src/types.h +++ b/src/types.h @@ -173,11 +173,6 @@ enum Bound { BOUND_EXACT = BOUND_UPPER | BOUND_LOWER }; -enum ExplosionState { - EXPLOSION_NONE, - MUST_CALM_DOWN -}; - enum Value : int { VALUE_ZERO = 0, VALUE_DRAW = 0, From e178a09c47e340e5183d9bd2d331741aa837ba8a Mon Sep 17 00:00:00 2001 From: ppigazzini Date: Mon, 31 Jan 2022 14:37:45 +0100 Subject: [PATCH 0782/1766] Drop sse from target "x86-32" have maximal compatibility on legacy target arch, now supporting AMD Athlon The old behavior can anyway be selected by the user if needed, for example make -j profile-build ARCH=x86-32 sse=yes fixes #3904 closes https://github.com/official-stockfish/Stockfish/pull/3918 No functional change --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 9666f138463..eff8bacae0a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -152,7 +152,7 @@ ifeq ($(findstring x86,$(ARCH)),x86) ifeq ($(findstring x86-32,$(ARCH)),x86-32) arch = i386 bits = 32 - sse = yes + sse = no mmx = yes else arch = x86_64 From 95d7369e54f20715345cf5408040f3c7d1ec8415 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Fri, 4 Feb 2022 22:42:41 +0300 Subject: [PATCH 0783/1766] Introduce movecount pruning for quiet check evasions in qsearch Idea of this patch is that we usually don't consider quiet check evasions as "good" ones and prefer capture based ones instead. So it makes sense to think that if in qsearch 2 quiet check evasions failed to produce anything good 3rd and further ones wouldn't be good either. passed STC https://tests.stockfishchess.org/tests/view/61fc1b1ed508ec6a1c9f397c LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 58800 W: 15947 L: 15626 D: 27227 Ptnml(0-2): 273, 6568, 15462, 6759, 338 passed LTC https://tests.stockfishchess.org/tests/view/61fcc56dd508ec6a1c9f5619 LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 89544 W: 24208 L: 23810 D: 41526 Ptnml(0-2): 81, 9038, 26134, 9440, 79 closes https://github.com/official-stockfish/Stockfish/pull/3920 bench 4830082 --- src/search.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 8d5ad97975d..c0f77f8afbc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1479,6 +1479,8 @@ namespace { contHist, prevSq); + int quietCheckEvasions = 0; + // Loop through the moves until no moves remain or a beta cutoff occurs while ((move = mp.next_move()) != MOVE_NONE) { @@ -1540,6 +1542,15 @@ namespace { && (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < CounterMovePruneThreshold) continue; + // movecount pruning for quiet check evasions + if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY + && quietCheckEvasions > 1 + && !captureOrPromotion + && ss->inCheck) + continue; + + quietCheckEvasions += !captureOrPromotion && ss->inCheck; + // Make and search the move pos.do_move(move, st, givesCheck); value = -qsearch(pos, ss+1, -beta, -alpha, depth - 1); From 4d3950c6eb80c932af00f6495668d5c5adf3701b Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 5 Feb 2022 04:03:02 +0300 Subject: [PATCH 0784/1766] Reintroduce razoring Razoring was simplified away some years ago, this patch reintroduces it in a slightly different form. Now for low depths if eval is far below alpha we check if qsearch can push it above alpha - and if it can't we return a fail low. passed STC https://tests.stockfishchess.org/tests/view/61fbf968d508ec6a1c9f3274 LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 226120 W: 61106 L: 60472 D: 104542 Ptnml(0-2): 1118, 25592, 59080, 26078, 1192 passed LTC https://tests.stockfishchess.org/tests/view/61fcc569d508ec6a1c9f5617 LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 113128 W: 30851 L: 30397 D: 51880 Ptnml(0-2): 114, 11483, 32926, 11917, 124 closes https://github.com/official-stockfish/Stockfish/pull/3921 bench 4684080 --- src/search.cpp | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c0f77f8afbc..6ca3b56ec92 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -773,7 +773,17 @@ namespace { thisThread->complexityAverage.update(complexity); - // Step 7. Futility pruning: child node (~25 Elo). + // Step 7. Razoring. + // If eval is really low check with qsearch if it can exceed alpha, if it can't, + // return a fail low. + if (!PvNode && depth <= 6 && eval < alpha - 400 - 300 * depth * depth) + { + value = qsearch(pos, ss, alpha - 1, alpha); + if (value < alpha) + return value; + } + + // Step 8. Futility pruning: child node (~25 Elo). // The depth condition is important for mate finding. if ( !ss->ttPv && depth < 9 @@ -782,7 +792,7 @@ namespace { && eval < 15000) // 50% larger than VALUE_KNOWN_WIN, but smaller than TB wins. return eval; - // Step 8. Null move search with verification search (~22 Elo) + // Step 9. Null move search with verification search (~22 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL && (ss-1)->statScore < 23767 @@ -834,7 +844,7 @@ namespace { probCutBeta = beta + 209 - 44 * improving; - // Step 9. ProbCut (~4 Elo) + // Step 10. ProbCut (~4 Elo) // 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 @@ -895,7 +905,7 @@ namespace { ss->ttPv = ttPv; } - // Step 10. If the position is not in TT, decrease depth by 2 or 1 depending on node type (~3 Elo) + // Step 11. If the position is not in TT, decrease depth by 2 or 1 depending on node type (~3 Elo) if ( PvNode && depth >= 6 && !ttMove) @@ -908,7 +918,7 @@ namespace { moves_loop: // When in check, search starts here - // Step 11. A small Probcut idea, when we are in check (~0 Elo) + // Step 12. A small Probcut idea, when we are in check (~0 Elo) probCutBeta = beta + 409; if ( ss->inCheck && !PvNode @@ -945,7 +955,7 @@ namespace { && (tte->bound() & BOUND_UPPER) && tte->depth() >= depth; - // Step 12. Loop through all pseudo-legal moves until no moves remain + // Step 13. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. while ((move = mp.next_move(moveCountPruning)) != MOVE_NONE) { @@ -985,7 +995,7 @@ namespace { Value delta = beta - alpha; - // Step 13. Pruning at shallow depth (~98 Elo). Depth conditions are important for mate finding. + // Step 14. Pruning at shallow depth (~98 Elo). Depth conditions are important for mate finding. if ( !rootNode && pos.non_pawn_material(us) && bestValue > VALUE_TB_LOSS_IN_MAX_PLY) @@ -1038,7 +1048,7 @@ namespace { } } - // Step 14. Extensions (~66 Elo) + // Step 15. Extensions (~66 Elo) // We take care to not overdo to avoid search getting stuck. if (ss->ply < thisThread->rootDepth * 2) { @@ -1115,12 +1125,12 @@ namespace { [movedPiece] [to_sq(move)]; - // Step 15. Make the move + // Step 16. Make the move pos.do_move(move, st, givesCheck); bool doDeeperSearch = false; - // Step 16. Late moves reduction / extension (LMR, ~98 Elo) + // Step 17. Late moves reduction / extension (LMR, ~98 Elo) // We use various heuristics for the sons of a node after the first son has // been searched. In general we would like to reduce them, but there are many // cases where we extend a son if it has good chances to be "interesting". @@ -1188,7 +1198,7 @@ namespace { didLMR = false; } - // Step 17. Full depth search when LMR is skipped or fails high + // Step 18. Full depth search when LMR is skipped or fails high if (doFullDepthSearch) { value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth + doDeeperSearch, !cutNode); @@ -1218,12 +1228,12 @@ namespace { std::min(maxNextDepth, newDepth), false); } - // Step 18. Undo move + // Step 19. Undo move pos.undo_move(move); assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); - // Step 19. Check for a new best move + // Step 20. Check for a new best move // Finished searching the move. If a stop occurred, the return value of // the search cannot be trusted, and we return immediately without // updating best move, PV and TT. @@ -1306,7 +1316,7 @@ namespace { return VALUE_DRAW; */ - // Step 20. Check for mate and stalemate + // Step 21. Check for mate and stalemate // All legal moves have been searched and if there are no legal moves, it // must be a mate or a stalemate. If we are in a singular extension search then // return a fail low score. From 08ac4e9db5d763edb788f3b01ea5c3bac494defa Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Mon, 7 Feb 2022 13:32:21 +0300 Subject: [PATCH 0785/1766] Do less depth reduction in null move pruning for complex positions This patch makes us reduce less depth in null move pruning if complexity is high enough. Thus, null move pruning now depends in two distinct ways on complexity, while being the only search heuristic that exploits complexity so far. passed STC https://tests.stockfishchess.org/tests/view/61fde60fd508ec6a1c9f7754 LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 170000 W: 45555 L: 45027 D: 79418 Ptnml(0-2): 760, 19352, 44359, 19658, 871 passed LTC https://tests.stockfishchess.org/tests/view/61fe91febf46cb834cbd5c90 LLR: 2.96 (-2.94,2.94) <0.50,3.00> Total: 145272 W: 39182 L: 38651 D: 67439 Ptnml(0-2): 127, 14864, 42157, 15327, 161 closes https://github.com/official-stockfish/Stockfish/pull/3923 bench 4461945 --- src/search.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 6ca3b56ec92..426f7937220 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -776,7 +776,9 @@ namespace { // Step 7. Razoring. // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. - if (!PvNode && depth <= 6 && eval < alpha - 400 - 300 * depth * depth) + if ( !PvNode + && depth <= 6 + && eval < alpha - 400 - 300 * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -805,8 +807,8 @@ namespace { { assert(eval - beta >= 0); - // Null move dynamic reduction based on depth and value - Depth R = std::min(int(eval - beta) / 205, 3) + depth / 3 + 4; + // Null move dynamic reduction based on depth, eval and complexity of position + Depth R = std::min(int(eval - beta) / 205, 3) + depth / 3 + 4 - (complexity > 500); ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; From b0b31558a236301aa8578030a8a3109856634fa9 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Wed, 9 Feb 2022 17:39:21 +0300 Subject: [PATCH 0786/1766] Big search tuning Most credits for this patch should go to @candirufish. Based on his big search tuning (1M games at 20+0.1s) https://tests.stockfishchess.org/tests/view/61fc7a6ed508ec6a1c9f4b7d with some hand polishing on top of it, which includes : a) correcting trend sigmoid - for some reason original tuning resulted in it being negative. This heuristic was proven to be worth some elo for years so reversing it sign is probably some random artefact; b) remove changes to continuation history based pruning - this heuristic historically was really good at providing green STCs and then failing at LTC miserably if we tried to make it more strict, original tuning was done at short time control and thus it became more strict - which doesn't scale to longer time controls; c) remove changes to improvement - not really indended :). passed STC https://tests.stockfishchess.org/tests/view/6203526e88ae2c84271c2ee2 LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 16840 W: 4604 L: 4363 D: 7873 Ptnml(0-2): 82, 1780, 4449, 2033, 76 passed LTC https://tests.stockfishchess.org/tests/view/620376e888ae2c84271c35d4 LLR: 2.96 (-2.94,2.94) <0.50,3.00> Total: 17232 W: 4771 L: 4542 D: 7919 Ptnml(0-2): 14, 1655, 5048, 1886, 13 closes https://github.com/official-stockfish/Stockfish/pull/3926 bench 5030992 --- src/search.cpp | 93 +++++++++++++++++++++++++------------------------- 1 file changed, 46 insertions(+), 47 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 426f7937220..5bc9de7b92f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -63,7 +63,7 @@ namespace { // Futility margin Value futility_margin(Depth d, bool improving) { - return Value(214 * (d - improving)); + return Value(171 * (d - improving)); } // Reductions lookup table, initialized at startup @@ -71,7 +71,7 @@ namespace { Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) { int r = Reductions[d] * Reductions[mn]; - return (r + 1358 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 904); + return (r + 1575 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 1011); } constexpr int futility_move_count(bool improving, Depth depth) { @@ -80,7 +80,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return std::min((6 * d + 229) * d - 215 , 2000); + return std::min((7 * d + 254) * d - 206 , 1990); } // Add a small random component to draw evaluations to avoid 3-fold blindness @@ -157,7 +157,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int((21.9 + std::log(Threads.size()) / 2) * std::log(i)); + Reductions[i] = int((21.5 + std::log(Threads.size()) / 2) * std::log(i)); } @@ -303,10 +303,10 @@ void Thread::search() { multiPV = std::min(multiPV, rootMoves.size()); - complexityAverage.set(232, 1); + complexityAverage.set(190, 1); trend = SCORE_ZERO; - optimism[ us] = Value(25); + optimism[ us] = Value(34); optimism[~us] = -optimism[us]; int searchAgainCounter = 0; @@ -349,16 +349,16 @@ void Thread::search() { if (rootDepth >= 4) { Value prev = rootMoves[pvIdx].averageScore; - delta = Value(17) + int(prev) * prev / 16384; + delta = Value(16) + int(prev) * prev / 16384; alpha = std::max(prev - delta,-VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE); // Adjust trend and optimism based on root move's previousScore - int tr = sigmoid(prev, 0, 0, 147, 113, 1); + int tr = sigmoid(prev, 6, 13, 96, 110, 1); trend = (us == WHITE ? make_score(tr, tr / 2) : -make_score(tr, tr / 2)); - int opt = sigmoid(prev, 0, 25, 147, 14464, 256); + int opt = sigmoid(prev, 7, 21, 94, 14786, 221); optimism[ us] = Value(opt); optimism[~us] = -optimism[us]; } @@ -413,7 +413,7 @@ void Thread::search() { else break; - delta += delta / 4 + 5; + delta += delta / 4 + 3; assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE); } @@ -459,17 +459,17 @@ void Thread::search() { && !Threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (142 + 12 * (mainThread->bestPreviousAverageScore - bestValue) - + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 825.0; + double fallingEval = (87 + 12 * (mainThread->bestPreviousAverageScore - bestValue) + + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 777.20; fallingEval = std::clamp(fallingEval, 0.5, 1.5); // If the bestMove is stable over several iterations, reduce time accordingly - timeReduction = lastBestMoveDepth + 9 < completedDepth ? 1.92 : 0.95; - double reduction = (1.47 + mainThread->previousTimeReduction) / (2.32 * timeReduction); + timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.70 : 0.91; + double reduction = (1.59 + mainThread->previousTimeReduction) / (2.33 * timeReduction); double bestMoveInstability = 1.073 + std::max(1.0, 2.25 - 9.9 / rootDepth) * totBestMoveChanges / Threads.size(); int complexity = mainThread->complexityAverage.value(); - double complexPosition = std::clamp(1.0 + (complexity - 232) / 1750.0, 0.5, 1.5); + double complexPosition = std::clamp(1.0 + (complexity - 312) / 1750.0, 0.5, 1.5); double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability * complexPosition; @@ -490,7 +490,7 @@ void Thread::search() { } else if ( Threads.increaseDepth && !mainThread->ponder - && Time.elapsed() > totalTime * 0.58) + && Time.elapsed() > totalTime * 0.55) Threads.increaseDepth = false; else Threads.increaseDepth = true; @@ -788,19 +788,19 @@ namespace { // Step 8. Futility pruning: child node (~25 Elo). // The depth condition is important for mate finding. if ( !ss->ttPv - && depth < 9 + && depth < 8 && eval - futility_margin(depth, improving) - (ss-1)->statScore / 256 >= beta && eval >= beta - && eval < 15000) // 50% larger than VALUE_KNOWN_WIN, but smaller than TB wins. + && eval < 17548) // 50% larger than VALUE_KNOWN_WIN, but smaller than TB wins. return eval; // Step 9. Null move search with verification search (~22 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 23767 + && (ss-1)->statScore < 13706 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 20 * depth - improvement / 15 + 204 + complexity / 25 + && ss->staticEval >= beta - 19 * depth - improvement / 15 + 200 + complexity / 25 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -844,13 +844,13 @@ namespace { } } - probCutBeta = beta + 209 - 44 * improving; + probCutBeta = beta + 229 - 47 * improving; // Step 10. ProbCut (~4 Elo) // 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 > 4 + && depth > 3 && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY // if value from transposition table is lower than probCutBeta, don't attempt probCut // there and in further interactions with transposition table cutoff depth is set to depth - 3 @@ -871,7 +871,6 @@ namespace { if (move != excludedMove && pos.legal(move)) { assert(pos.capture_or_promotion(move)); - assert(depth >= 5); captureOrPromotion = true; @@ -909,19 +908,19 @@ namespace { // Step 11. If the position is not in TT, decrease depth by 2 or 1 depending on node type (~3 Elo) if ( PvNode - && depth >= 6 + && depth >= 4 && !ttMove) depth -= 2; if ( cutNode - && depth >= 9 + && depth >= 7 && !ttMove) depth--; moves_loop: // When in check, search starts here // Step 12. A small Probcut idea, when we are in check (~0 Elo) - probCutBeta = beta + 409; + probCutBeta = beta + 401; if ( ss->inCheck && !PvNode && depth >= 4 @@ -1017,12 +1016,12 @@ namespace { && !PvNode && lmrDepth < 6 && !ss->inCheck - && ss->staticEval + 342 + 238 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] + && ss->staticEval + 392 + 207 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 8 < alpha) continue; // SEE based pruning (~9 Elo) - if (!pos.see_ge(move, Value(-217) * depth)) + if (!pos.see_ge(move, Value(-200) * depth)) continue; } else @@ -1040,12 +1039,12 @@ namespace { // Futility pruning: parent node (~9 Elo) if ( !ss->inCheck - && lmrDepth < 8 - && ss->staticEval + 138 + 137 * lmrDepth + history / 64 <= alpha) + && lmrDepth < 11 + && ss->staticEval + 131 + 137 * lmrDepth + history / 64 <= alpha) continue; // Prune moves with negative SEE (~3 Elo) - if (!pos.see_ge(move, Value(-21 * lmrDepth * lmrDepth - 21 * lmrDepth))) + if (!pos.see_ge(move, Value(-25 * lmrDepth * lmrDepth - 29 * lmrDepth))) continue; } } @@ -1081,7 +1080,7 @@ namespace { // Avoid search explosion by limiting the number of double extensions if ( !PvNode - && value < singularBeta - 75 + && value < singularBeta - 71 && ss->doubleExtensions <= 6) extension = 2; } @@ -1101,15 +1100,15 @@ namespace { // Check extensions (~1 Elo) else if ( givesCheck - && depth > 6 - && abs(ss->staticEval) > 100) + && depth > 7 + && abs(ss->staticEval) > 128) extension = 1; // Quiet ttMove extensions (~0 Elo) else if ( PvNode && move == ttMove && move == ss->killers[0] - && (*contHist[0])[movedPiece][to_sq(move)] >= 10000) + && (*contHist[0])[movedPiece][to_sq(move)] >= 8932) extension = 1; } @@ -1136,8 +1135,8 @@ namespace { // We use various heuristics for the sons of a node after the first son has // been searched. In general we would like to reduce them, but there are many // cases where we extend a son if it has good chances to be "interesting". - if ( depth >= 3 - && moveCount > 1 + 2 * rootNode + if ( depth >= 2 + && moveCount > 1 + rootNode && ( !ss->ttPv || !captureOrPromotion || (cutNode && (ss-1)->moveCount > 1))) @@ -1146,7 +1145,7 @@ namespace { // Decrease reduction at some PvNodes (~2 Elo) if ( PvNode - && bestMoveCount <= 3) + && bestMoveCount <= 4) r--; // Decrease reduction if position is or has been on the PV @@ -1156,7 +1155,7 @@ namespace { r -= 2; // Decrease reduction if opponent's move count is high (~1 Elo) - if ((ss-1)->moveCount > 13) + if ((ss-1)->moveCount > 7) r--; // Increase reduction for cut nodes (~3 Elo) @@ -1171,18 +1170,18 @@ namespace { + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 4923; + - 4142; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - r -= ss->statScore / 14721; + r -= ss->statScore / 15328; // In general we want to cap the LMR depth search at newDepth. But if reductions // are really negative and movecount is low, we allow this move to be searched // deeper than the first move (this may lead to hidden double extensions). int deeper = r >= -1 ? 0 : moveCount <= 5 ? 2 - : PvNode && depth > 6 ? 1 - : cutNode && moveCount <= 7 ? 1 + : PvNode && depth > 4 ? 1 + : cutNode && moveCount <= 5 ? 1 : 0; Depth d = std::clamp(newDepth - r, 1, newDepth + deeper); @@ -1191,7 +1190,7 @@ namespace { // If the son is reduced and fails high it will be re-searched at full depth doFullDepthSearch = value > alpha && d < newDepth; - doDeeperSearch = value > (alpha + 62 + 20 * (newDepth - d)); + doDeeperSearch = value > (alpha + 80 + 20 * (newDepth - d)); didLMR = true; } else @@ -1212,7 +1211,7 @@ namespace { : -stat_bonus(newDepth); if (captureOrPromotion) - bonus /= 4; + bonus /= 5; update_continuation_histories(ss, movedPiece, to_sq(move), bonus); } @@ -1343,7 +1342,7 @@ namespace { //or fail low was really bad bool extraBonus = PvNode || cutNode - || bestValue < alpha - 94 * depth; + || bestValue < alpha - 99 * depth; update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * (1 + extraBonus)); } @@ -1474,7 +1473,7 @@ namespace { if (PvNode && bestValue > alpha) alpha = bestValue; - futilityBase = bestValue + 155; + futilityBase = bestValue + 127; } const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, From cb9c2594fcedc881ae8f8bfbfdf130cf89840e4c Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Sat, 27 Nov 2021 15:17:02 +0100 Subject: [PATCH 0787/1766] Update architecture to "SFNNv4". Update network to nn-6877cd24400e.nnue. Architecture: The diagram of the "SFNNv4" architecture: https://user-images.githubusercontent.com/8037982/153455685-cbe3a038-e158-4481-844d-9d5fccf5c33a.png The most important architectural changes are the following: * 1024x2 [activated] neurons are pairwise, elementwise multiplied (not quite pairwise due to implementation details, see diagram), which introduces a non-linearity that exhibits similar benefits to previously tested sigmoid activation (quantmoid4), while being slightly faster. * The following layer has therefore 2x less inputs, which we compensate by having 2 more outputs. It is possible that reducing the number of outputs might be beneficial (as we had it as low as 8 before). The layer is now 1024->16. * The 16 outputs are split into 15 and 1. The 1-wide output is added to the network output (after some necessary scaling due to quantization differences). The 15-wide is activated and follows the usual path through a set of linear layers. The additional 1-wide output is at least neutral, but has shown a slightly positive trend in training compared to networks without it (all 16 outputs through the usual path), and allows possibly an additional stage of lazy evaluation to be introduced in the future. Additionally, the inference code was rewritten and no longer uses a recursive implementation. This was necessitated by the splitting of the 16-wide intermediate result into two, which was impossible to do with the old implementation with ugly hacks. This is hopefully overall for the better. First session: The first session was training a network from scratch (random initialization). The exact trainer used was slightly different (older) from the one used in the second session, but it should not have a measurable effect. The purpose of this session is to establish a strong network base for the second session. Small deviations in strength do not harm the learnability in the second session. The training was done using the following command: python3 train.py \ /home/sopel/nnue/nnue-pytorch-training/data/nodes5000pv2_UHO.binpack \ /home/sopel/nnue/nnue-pytorch-training/data/nodes5000pv2_UHO.binpack \ --gpus "$3," \ --threads 4 \ --num-workers 4 \ --batch-size 16384 \ --progress_bar_refresh_rate 20 \ --random-fen-skipping 3 \ --features=HalfKAv2_hm^ \ --lambda=1.0 \ --gamma=0.992 \ --lr=8.75e-4 \ --max_epochs=400 \ --default_root_dir ../nnue-pytorch-training/experiment_$1/run_$2 Every 20th net was saved and its playing strength measured against some baseline at 25k nodes per move with pure NNUE evaluation (modified binary). The exact setup is not important as long as it's consistent. The purpose is to sift good candidates from bad ones. The dataset can be found https://drive.google.com/file/d/1UQdZN_LWQ265spwTBwDKo0t1WjSJKvWY/view Second session: The second training session was done starting from the best network (as determined by strength testing) from the first session. It is important that it's resumed from a .pt model and NOT a .ckpt model. The conversion can be performed directly using serialize.py The LR schedule was modified to use gamma=0.995 instead of gamma=0.992 and LR=4.375e-4 instead of LR=8.75e-4 to flatten the LR curve and allow for longer training. The training was then running for 800 epochs instead of 400 (though it's possibly mostly noise after around epoch 600). The training was done using the following command: The training was done using the following command: python3 train.py \ /data/sopel/nnue/nnue-pytorch-training/data/T60T70wIsRightFarseerT60T74T75T76.binpack \ /data/sopel/nnue/nnue-pytorch-training/data/T60T70wIsRightFarseerT60T74T75T76.binpack \ --gpus "$3," \ --threads 4 \ --num-workers 4 \ --batch-size 16384 \ --progress_bar_refresh_rate 20 \ --random-fen-skipping 3 \ --features=HalfKAv2_hm^ \ --lambda=1.0 \ --gamma=0.995 \ --lr=4.375e-4 \ --max_epochs=800 \ --resume-from-model /data/sopel/nnue/nnue-pytorch-training/data/exp295/nn-epoch399.pt \ --default_root_dir ../nnue-pytorch-training/experiment_$1/run_$run_id In particular note that we now use lambda=1.0 instead of lambda=0.8 (previous nets), because tests show that WDL-skipping introduced by vondele performs better with lambda=1.0. Nets were being saved every 20th epoch. In total 16 runs were made with these settings and the best nets chosen according to playing strength at 25k nodes per move with pure NNUE evaluation - these are the 4 nets that have been put on fishtest. The dataset can be found either at ftp://ftp.chessdb.cn/pub/sopel/data_sf/T60T70wIsRightFarseerT60T74T75T76.binpack in its entirety (download might be painfully slow because hosted in China) or can be assembled in the following way: Get the https://github.com/official-stockfish/Stockfish/blob/5640ad48ae5881223b868362c1cbeb042947f7b4/script/interleave_binpacks.py script. Download T60T70wIsRightFarseer.binpack https://drive.google.com/file/d/1_sQoWBl31WAxNXma2v45004CIVltytP8/view Download farseerT74.binpack http://trainingdata.farseer.org/T74-May13-End.7z Download farseerT75.binpack http://trainingdata.farseer.org/T75-June3rd-End.7z Download farseerT76.binpack http://trainingdata.farseer.org/T76-Nov10th-End.7z Run python3 interleave_binpacks.py T60T70wIsRightFarseer.binpack farseerT74.binpack farseerT75.binpack farseerT76.binpack T60T70wIsRightFarseerT60T74T75T76.binpack Tests: STC: https://tests.stockfishchess.org/tests/view/6203fb85d71106ed12a407b7 LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 16952 W: 4775 L: 4521 D: 7656 Ptnml(0-2): 133, 1818, 4318, 2076, 131 LTC: https://tests.stockfishchess.org/tests/view/62041e68d71106ed12a40e85 LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 14944 W: 4138 L: 3907 D: 6899 Ptnml(0-2): 21, 1499, 4202, 1728, 22 closes https://github.com/official-stockfish/Stockfish/pull/3927 Bench: 4919707 --- src/evaluate.h | 2 +- src/nnue/evaluate_nnue.cpp | 17 +-- src/nnue/layers/affine_transform.h | 91 +++++-------- src/nnue/layers/clipped_relu.h | 35 ++--- src/nnue/layers/input_slice.h | 73 ---------- src/nnue/nnue_architecture.h | 119 ++++++++++++---- src/nnue/nnue_feature_transformer.h | 202 +++++++++++++--------------- 7 files changed, 237 insertions(+), 302 deletions(-) delete mode 100644 src/nnue/layers/input_slice.h diff --git a/src/evaluate.h b/src/evaluate.h index 57a7687d776..1934c9bddf0 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-ac07bd334b62.nnue" + #define EvalFileDefaultName "nn-6877cd24400e.nnue" namespace NNUE { diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 862b2003388..0fd58462b78 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -148,22 +148,18 @@ namespace Stockfish::Eval::NNUE { #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) TransformedFeatureType transformedFeaturesUnaligned[ FeatureTransformer::BufferSize + alignment / sizeof(TransformedFeatureType)]; - char bufferUnaligned[Network::BufferSize + alignment]; auto* transformedFeatures = align_ptr_up(&transformedFeaturesUnaligned[0]); - auto* buffer = align_ptr_up(&bufferUnaligned[0]); #else alignas(alignment) TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize]; - alignas(alignment) char buffer[Network::BufferSize]; #endif ASSERT_ALIGNED(transformedFeatures, alignment); - ASSERT_ALIGNED(buffer, alignment); const std::size_t bucket = (pos.count() - 1) / 4; const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket); - const auto positional = network[bucket]->propagate(transformedFeatures, buffer)[0]; + const auto positional = network[bucket]->propagate(transformedFeatures); // Give more value to positional evaluation when adjusted flag is set if (adjusted) @@ -190,27 +186,20 @@ namespace Stockfish::Eval::NNUE { #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) TransformedFeatureType transformedFeaturesUnaligned[ FeatureTransformer::BufferSize + alignment / sizeof(TransformedFeatureType)]; - char bufferUnaligned[Network::BufferSize + alignment]; auto* transformedFeatures = align_ptr_up(&transformedFeaturesUnaligned[0]); - auto* buffer = align_ptr_up(&bufferUnaligned[0]); #else alignas(alignment) TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize]; - alignas(alignment) char buffer[Network::BufferSize]; #endif ASSERT_ALIGNED(transformedFeatures, alignment); - ASSERT_ALIGNED(buffer, alignment); NnueEvalTrace t{}; t.correctBucket = (pos.count() - 1) / 4; for (std::size_t bucket = 0; bucket < LayerStacks; ++bucket) { - const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket); - const auto output = network[bucket]->propagate(transformedFeatures, buffer); - - int materialist = psqt; - int positional = output[0]; + const auto materialist = featureTransformer->transform(pos, transformedFeatures, bucket); + const auto positional = network[bucket]->propagate(transformedFeatures); t.psqt[bucket] = static_cast( materialist / OutputScale ); t.positional[bucket] = static_cast( positional / OutputScale ); diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 4e85a5fe4b1..22451915ba1 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -63,19 +63,17 @@ namespace Stockfish::Eval::NNUE::Layers { { # if defined(USE_SSE2) // At least a multiple of 16, with SSE2. - static_assert(PaddedInputDimensions % 16 == 0); - constexpr IndexType NumChunks = PaddedInputDimensions / 16; + constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 16) / 16; const __m128i Zeros = _mm_setzero_si128(); const auto inputVector = reinterpret_cast(input); # elif defined(USE_MMX) - static_assert(InputDimensions % 8 == 0); - constexpr IndexType NumChunks = InputDimensions / 8; + constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 8) / 8; const __m64 Zeros = _mm_setzero_si64(); const auto inputVector = reinterpret_cast(input); # elif defined(USE_NEON) - constexpr IndexType NumChunks = (InputDimensions + 15) / 16; + constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 16) / 16; const auto inputVector = reinterpret_cast(input); # endif @@ -150,24 +148,27 @@ namespace Stockfish::Eval::NNUE::Layers { } #endif - template + template class AffineTransform; // A specialization for large inputs. - template - class AffineTransform= 2*64-1)>> { + template + class AffineTransform(InDims, MaxSimdWidth) >= 2*64)>> { public: // Input/output type - using InputType = typename PreviousLayer::OutputType; + using InputType = std::uint8_t; using OutputType = std::int32_t; - static_assert(std::is_same::value, ""); // Number of input/output dimensions - static constexpr IndexType InputDimensions = PreviousLayer::OutputDimensions; + static constexpr IndexType InputDimensions = InDims; static constexpr IndexType OutputDimensions = OutDims; static constexpr IndexType PaddedInputDimensions = ceil_to_multiple(InputDimensions, MaxSimdWidth); + static constexpr IndexType PaddedOutputDimensions = + ceil_to_multiple(OutputDimensions, MaxSimdWidth); + + using OutputBuffer = OutputType[PaddedOutputDimensions]; static_assert(PaddedInputDimensions >= 128, "Something went wrong. This specialization should not have been chosen."); @@ -202,20 +203,12 @@ namespace Stockfish::Eval::NNUE::Layers { static_assert(OutputDimensions % NumOutputRegs == 0); - // Size of forward propagation buffer used in this layer - static constexpr std::size_t SelfBufferSize = - ceil_to_multiple(OutputDimensions * sizeof(OutputType), CacheLineSize); - - // Size of the forward propagation buffer used from the input layer to this layer - static constexpr std::size_t BufferSize = - PreviousLayer::BufferSize + SelfBufferSize; - // Hash value embedded in the evaluation file - static constexpr std::uint32_t get_hash_value() { + static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) { std::uint32_t hashValue = 0xCC03DAE4u; hashValue += OutputDimensions; - hashValue ^= PreviousLayer::get_hash_value() >> 1; - hashValue ^= PreviousLayer::get_hash_value() << 31; + hashValue ^= prevHash >> 1; + hashValue ^= prevHash << 31; return hashValue; } @@ -242,7 +235,6 @@ namespace Stockfish::Eval::NNUE::Layers { // Read network parameters bool read_parameters(std::istream& stream) { - if (!previousLayer.read_parameters(stream)) return false; for (std::size_t i = 0; i < OutputDimensions; ++i) biases[i] = read_little_endian(stream); @@ -254,7 +246,6 @@ namespace Stockfish::Eval::NNUE::Layers { // Write network parameters bool write_parameters(std::ostream& stream) const { - if (!previousLayer.write_parameters(stream)) return false; for (std::size_t i = 0; i < OutputDimensions; ++i) write_little_endian(stream, biases[i]); @@ -266,10 +257,7 @@ namespace Stockfish::Eval::NNUE::Layers { // Forward propagation const OutputType* propagate( - const TransformedFeatureType* transformedFeatures, char* buffer) const { - const auto input = previousLayer.propagate( - transformedFeatures, buffer + SelfBufferSize); - OutputType* output = reinterpret_cast(buffer); + const InputType* input, OutputType* output) const { #if defined (USE_AVX512) using acc_vec_t = __m512i; @@ -312,7 +300,6 @@ namespace Stockfish::Eval::NNUE::Layers { #if defined (USE_SSSE3) || defined (USE_NEON) const in_vec_t* invec = reinterpret_cast(input); - // Perform accumulation to registers for each big block for (IndexType bigBlock = 0; bigBlock < NumBigBlocks; ++bigBlock) { @@ -377,26 +364,28 @@ namespace Stockfish::Eval::NNUE::Layers { using BiasType = OutputType; using WeightType = std::int8_t; - PreviousLayer previousLayer; - alignas(CacheLineSize) BiasType biases[OutputDimensions]; alignas(CacheLineSize) WeightType weights[OutputDimensions * PaddedInputDimensions]; }; - template - class AffineTransform> { + template + class AffineTransform(InDims, MaxSimdWidth) < 2*64)>> { public: // Input/output type - using InputType = typename PreviousLayer::OutputType; + // Input/output type + using InputType = std::uint8_t; using OutputType = std::int32_t; - static_assert(std::is_same::value, ""); // Number of input/output dimensions - static constexpr IndexType InputDimensions = - PreviousLayer::OutputDimensions; + static constexpr IndexType InputDimensions = InDims; static constexpr IndexType OutputDimensions = OutDims; + static constexpr IndexType PaddedInputDimensions = - ceil_to_multiple(InputDimensions, MaxSimdWidth); + ceil_to_multiple(InputDimensions, MaxSimdWidth); + static constexpr IndexType PaddedOutputDimensions = + ceil_to_multiple(OutputDimensions, MaxSimdWidth); + + using OutputBuffer = OutputType[PaddedOutputDimensions]; static_assert(PaddedInputDimensions < 128, "Something went wrong. This specialization should not have been chosen."); @@ -405,20 +394,12 @@ namespace Stockfish::Eval::NNUE::Layers { static constexpr const IndexType InputSimdWidth = SimdWidth; #endif - // Size of forward propagation buffer used in this layer - static constexpr std::size_t SelfBufferSize = - ceil_to_multiple(OutputDimensions * sizeof(OutputType), CacheLineSize); - - // Size of the forward propagation buffer used from the input layer to this layer - static constexpr std::size_t BufferSize = - PreviousLayer::BufferSize + SelfBufferSize; - // Hash value embedded in the evaluation file - static constexpr std::uint32_t get_hash_value() { + static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) { std::uint32_t hashValue = 0xCC03DAE4u; hashValue += OutputDimensions; - hashValue ^= PreviousLayer::get_hash_value() >> 1; - hashValue ^= PreviousLayer::get_hash_value() << 31; + hashValue ^= prevHash >> 1; + hashValue ^= prevHash << 31; return hashValue; } @@ -441,7 +422,6 @@ namespace Stockfish::Eval::NNUE::Layers { // Read network parameters bool read_parameters(std::istream& stream) { - if (!previousLayer.read_parameters(stream)) return false; for (std::size_t i = 0; i < OutputDimensions; ++i) biases[i] = read_little_endian(stream); for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) @@ -452,7 +432,6 @@ namespace Stockfish::Eval::NNUE::Layers { // Write network parameters bool write_parameters(std::ostream& stream) const { - if (!previousLayer.write_parameters(stream)) return false; for (std::size_t i = 0; i < OutputDimensions; ++i) write_little_endian(stream, biases[i]); @@ -463,10 +442,7 @@ namespace Stockfish::Eval::NNUE::Layers { } // Forward propagation const OutputType* propagate( - const TransformedFeatureType* transformedFeatures, char* buffer) const { - const auto input = previousLayer.propagate( - transformedFeatures, buffer + SelfBufferSize); - const auto output = reinterpret_cast(buffer); + const InputType* input, OutputType* output) const { #if defined (USE_AVX2) using vec_t = __m256i; @@ -491,12 +467,11 @@ namespace Stockfish::Eval::NNUE::Layers { #if defined (USE_SSSE3) const auto inputVector = reinterpret_cast(input); - static_assert(InputDimensions % 8 == 0); static_assert(OutputDimensions % OutputSimdWidth == 0 || OutputDimensions == 1); if constexpr (OutputDimensions % OutputSimdWidth == 0) { - constexpr IndexType NumChunks = InputDimensions / 4; + constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 8) / 4; constexpr IndexType NumRegs = OutputDimensions / OutputSimdWidth; const auto input32 = reinterpret_cast(input); @@ -555,8 +530,6 @@ namespace Stockfish::Eval::NNUE::Layers { using BiasType = OutputType; using WeightType = std::int8_t; - PreviousLayer previousLayer; - alignas(CacheLineSize) BiasType biases[OutputDimensions]; alignas(CacheLineSize) WeightType weights[OutputDimensions * PaddedInputDimensions]; }; diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index 0da5e821011..ffd2e3b76a9 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -26,51 +26,41 @@ namespace Stockfish::Eval::NNUE::Layers { // Clipped ReLU - template + template class ClippedReLU { public: // Input/output type - using InputType = typename PreviousLayer::OutputType; + using InputType = std::int32_t; using OutputType = std::uint8_t; - static_assert(std::is_same::value, ""); // Number of input/output dimensions - static constexpr IndexType InputDimensions = PreviousLayer::OutputDimensions; + static constexpr IndexType InputDimensions = InDims; static constexpr IndexType OutputDimensions = InputDimensions; static constexpr IndexType PaddedOutputDimensions = ceil_to_multiple(OutputDimensions, 32); - // Size of forward propagation buffer used in this layer - static constexpr std::size_t SelfBufferSize = - ceil_to_multiple(OutputDimensions * sizeof(OutputType), CacheLineSize); - - // Size of the forward propagation buffer used from the input layer to this layer - static constexpr std::size_t BufferSize = - PreviousLayer::BufferSize + SelfBufferSize; + using OutputBuffer = OutputType[PaddedOutputDimensions]; // Hash value embedded in the evaluation file - static constexpr std::uint32_t get_hash_value() { + static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) { std::uint32_t hashValue = 0x538D24C7u; - hashValue += PreviousLayer::get_hash_value(); + hashValue += prevHash; return hashValue; } // Read network parameters - bool read_parameters(std::istream& stream) { - return previousLayer.read_parameters(stream); + bool read_parameters(std::istream&) { + return true; } // Write network parameters - bool write_parameters(std::ostream& stream) const { - return previousLayer.write_parameters(stream); + bool write_parameters(std::ostream&) const { + return true; } // Forward propagation const OutputType* propagate( - const TransformedFeatureType* transformedFeatures, char* buffer) const { - const auto input = previousLayer.propagate( - transformedFeatures, buffer + SelfBufferSize); - const auto output = reinterpret_cast(buffer); + const InputType* input, OutputType* output) const { #if defined(USE_AVX2) if constexpr (InputDimensions % SimdWidth == 0) { @@ -191,9 +181,6 @@ namespace Stockfish::Eval::NNUE::Layers { return output; } - - private: - PreviousLayer previousLayer; }; } // namespace Stockfish::Eval::NNUE::Layers diff --git a/src/nnue/layers/input_slice.h b/src/nnue/layers/input_slice.h deleted file mode 100644 index 8f526b745f7..00000000000 --- a/src/nnue/layers/input_slice.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -// NNUE evaluation function layer InputSlice definition - -#ifndef NNUE_LAYERS_INPUT_SLICE_H_INCLUDED -#define NNUE_LAYERS_INPUT_SLICE_H_INCLUDED - -#include "../nnue_common.h" - -namespace Stockfish::Eval::NNUE::Layers { - -// Input layer -template -class InputSlice { - public: - // Need to maintain alignment - static_assert(Offset % MaxSimdWidth == 0, ""); - - // Output type - using OutputType = TransformedFeatureType; - - // Output dimensionality - static constexpr IndexType OutputDimensions = OutDims; - - // Size of forward propagation buffer used from the input layer to this layer - static constexpr std::size_t BufferSize = 0; - - // Hash value embedded in the evaluation file - static constexpr std::uint32_t get_hash_value() { - std::uint32_t hashValue = 0xEC42E90Du; - hashValue ^= OutputDimensions ^ (Offset << 10); - return hashValue; - } - - // Read network parameters - bool read_parameters(std::istream& /*stream*/) { - return true; - } - - // Write network parameters - bool write_parameters(std::ostream& /*stream*/) const { - return true; - } - - // Forward propagation - const OutputType* propagate( - const TransformedFeatureType* transformedFeatures, - char* /*buffer*/) const { - return transformedFeatures + Offset; - } - - private: -}; - -} // namespace Stockfish::Eval::NNUE::Layers - -#endif // #ifndef NNUE_LAYERS_INPUT_SLICE_H_INCLUDED diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index 8867fac72fc..725b40fb43d 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -25,35 +25,106 @@ #include "features/half_ka_v2_hm.h" -#include "layers/input_slice.h" #include "layers/affine_transform.h" #include "layers/clipped_relu.h" -namespace Stockfish::Eval::NNUE { - - // Input features used in evaluation function - using FeatureSet = Features::HalfKAv2_hm; - - // Number of input feature dimensions after conversion - constexpr IndexType TransformedFeatureDimensions = 1024; - constexpr IndexType PSQTBuckets = 8; - constexpr IndexType LayerStacks = 8; - - namespace Layers { +#include "../misc.h" - // Define network structure - using InputLayer = InputSlice; - using HiddenLayer1 = ClippedReLU>; - using HiddenLayer2 = ClippedReLU>; - using OutputLayer = AffineTransform; - - } // namespace Layers - - using Network = Layers::OutputLayer; +namespace Stockfish::Eval::NNUE { - static_assert(TransformedFeatureDimensions % MaxSimdWidth == 0, ""); - static_assert(Network::OutputDimensions == 1, ""); - static_assert(std::is_same::value, ""); +// Input features used in evaluation function +using FeatureSet = Features::HalfKAv2_hm; + +// Number of input feature dimensions after conversion +constexpr IndexType TransformedFeatureDimensions = 1024; +constexpr IndexType PSQTBuckets = 8; +constexpr IndexType LayerStacks = 8; + +struct Network +{ + static constexpr int FC_0_OUTPUTS = 15; + static constexpr int FC_1_OUTPUTS = 32; + + Layers::AffineTransform fc_0; + Layers::ClippedReLU ac_0; + Layers::AffineTransform fc_1; + Layers::ClippedReLU ac_1; + Layers::AffineTransform fc_2; + + // Hash value embedded in the evaluation file + static constexpr std::uint32_t get_hash_value() { + // input slice hash + std::uint32_t hashValue = 0xEC42E90Du; + hashValue ^= TransformedFeatureDimensions * 2; + + hashValue = decltype(fc_0)::get_hash_value(hashValue); + hashValue = decltype(ac_0)::get_hash_value(hashValue); + hashValue = decltype(fc_1)::get_hash_value(hashValue); + hashValue = decltype(ac_1)::get_hash_value(hashValue); + hashValue = decltype(fc_2)::get_hash_value(hashValue); + + return hashValue; + } + + // Read network parameters + bool read_parameters(std::istream& stream) { + if (!fc_0.read_parameters(stream)) return false; + if (!ac_0.read_parameters(stream)) return false; + if (!fc_1.read_parameters(stream)) return false; + if (!ac_1.read_parameters(stream)) return false; + if (!fc_2.read_parameters(stream)) return false; + return true; + } + + // Read network parameters + bool write_parameters(std::ostream& stream) const { + if (!fc_0.write_parameters(stream)) return false; + if (!ac_0.write_parameters(stream)) return false; + if (!fc_1.write_parameters(stream)) return false; + if (!ac_1.write_parameters(stream)) return false; + if (!fc_2.write_parameters(stream)) return false; + return true; + } + + std::int32_t propagate(const TransformedFeatureType* transformedFeatures) + { + constexpr uint64_t alignment = CacheLineSize; + + struct Buffer + { + alignas(CacheLineSize) decltype(fc_0)::OutputBuffer fc_0_out; + alignas(CacheLineSize) decltype(ac_0)::OutputBuffer ac_0_out; + alignas(CacheLineSize) decltype(fc_1)::OutputBuffer fc_1_out; + alignas(CacheLineSize) decltype(ac_1)::OutputBuffer ac_1_out; + alignas(CacheLineSize) decltype(fc_2)::OutputBuffer fc_2_out; + }; + +#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) + char bufferRaw[sizeof(Buffer) + alignment]; + char* bufferRawAligned = align_ptr_up(&bufferRaw[0]); + Buffer& buffer = *(new (bufferRawAligned) Buffer); +#else + alignas(alignment) Buffer buffer; +#endif + + fc_0.propagate(transformedFeatures, buffer.fc_0_out); + ac_0.propagate(buffer.fc_0_out, buffer.ac_0_out); + fc_1.propagate(buffer.ac_0_out, buffer.fc_1_out); + ac_1.propagate(buffer.fc_1_out, buffer.ac_1_out); + fc_2.propagate(buffer.ac_1_out, buffer.fc_2_out); + + // buffer.fc_0_out[FC_0_OUTPUTS] is such that 1.0 is equal to 127*(1<(&output[offset]); - for (IndexType j = 0; j < NumChunks; ++j) - { - __m512i sum0 = _mm512_load_si512(&reinterpret_cast - (accumulation[perspectives[p]])[j * 2 + 0]); - __m512i sum1 = _mm512_load_si512(&reinterpret_cast - (accumulation[perspectives[p]])[j * 2 + 1]); + const IndexType offset = (HalfDimensions / 2) * p; - _mm512_store_si512(&out[j], _mm512_permutexvar_epi64(Control, - _mm512_max_epi8(_mm512_packs_epi16(sum0, sum1), Zero))); - } - } - return psqt; +#if defined(USE_AVX512) - #elif defined(USE_AVX2) + constexpr IndexType OutputChunkSize = 512 / 8; + static_assert((HalfDimensions / 2) % OutputChunkSize == 0); + constexpr IndexType NumOutputChunks = HalfDimensions / 2 / OutputChunkSize; - constexpr IndexType NumChunks = HalfDimensions / SimdWidth; - constexpr int Control = 0b11011000; - const __m256i Zero = _mm256_setzero_si256(); + const __m512i Zero = _mm512_setzero_si512(); + const __m512i One = _mm512_set1_epi16(127); + const __m512i Control = _mm512_setr_epi64(0, 2, 4, 6, 1, 3, 5, 7); - for (IndexType p = 0; p < 2; ++p) - { - const IndexType offset = HalfDimensions * p; - auto out = reinterpret_cast<__m256i*>(&output[offset]); - for (IndexType j = 0; j < NumChunks; ++j) + const __m512i* in0 = reinterpret_cast(&(accumulation[perspectives[p]][0])); + const __m512i* in1 = reinterpret_cast(&(accumulation[perspectives[p]][HalfDimensions / 2])); + __m512i* out = reinterpret_cast< __m512i*>(output + offset); + + for (IndexType j = 0; j < NumOutputChunks; j += 1) { - __m256i sum0 = _mm256_load_si256(&reinterpret_cast - (accumulation[perspectives[p]])[j * 2 + 0]); - __m256i sum1 = _mm256_load_si256(&reinterpret_cast - (accumulation[perspectives[p]])[j * 2 + 1]); + const __m512i sum0a = _mm512_max_epi16(_mm512_min_epi16(in0[j * 2 + 0], One), Zero); + const __m512i sum0b = _mm512_max_epi16(_mm512_min_epi16(in0[j * 2 + 1], One), Zero); + const __m512i sum1a = _mm512_max_epi16(_mm512_min_epi16(in1[j * 2 + 0], One), Zero); + const __m512i sum1b = _mm512_max_epi16(_mm512_min_epi16(in1[j * 2 + 1], One), Zero); - _mm256_store_si256(&out[j], _mm256_permute4x64_epi64( - _mm256_max_epi8(_mm256_packs_epi16(sum0, sum1), Zero), Control)); + const __m512i pa = _mm512_srli_epi16(_mm512_mullo_epi16(sum0a, sum1a), 7); + const __m512i pb = _mm512_srli_epi16(_mm512_mullo_epi16(sum0b, sum1b), 7); + + out[j] = _mm512_permutexvar_epi64(Control, _mm512_packs_epi16(pa, pb)); } - } - return psqt; - #elif defined(USE_SSE2) +#elif defined(USE_AVX2) - #ifdef USE_SSE41 - constexpr IndexType NumChunks = HalfDimensions / SimdWidth; - const __m128i Zero = _mm_setzero_si128(); - #else - constexpr IndexType NumChunks = HalfDimensions / SimdWidth; - const __m128i k0x80s = _mm_set1_epi8(-128); - #endif + constexpr IndexType OutputChunkSize = 256 / 8; + static_assert((HalfDimensions / 2) % OutputChunkSize == 0); + constexpr IndexType NumOutputChunks = HalfDimensions / 2 / OutputChunkSize; - for (IndexType p = 0; p < 2; ++p) - { - const IndexType offset = HalfDimensions * p; - auto out = reinterpret_cast<__m128i*>(&output[offset]); - for (IndexType j = 0; j < NumChunks; ++j) + const __m256i Zero = _mm256_setzero_si256(); + const __m256i One = _mm256_set1_epi16(127); + constexpr int Control = 0b11011000; + + const __m256i* in0 = reinterpret_cast(&(accumulation[perspectives[p]][0])); + const __m256i* in1 = reinterpret_cast(&(accumulation[perspectives[p]][HalfDimensions / 2])); + __m256i* out = reinterpret_cast< __m256i*>(output + offset); + + for (IndexType j = 0; j < NumOutputChunks; j += 1) { - __m128i sum0 = _mm_load_si128(&reinterpret_cast - (accumulation[perspectives[p]])[j * 2 + 0]); - __m128i sum1 = _mm_load_si128(&reinterpret_cast - (accumulation[perspectives[p]])[j * 2 + 1]); - const __m128i packedbytes = _mm_packs_epi16(sum0, sum1); - - #ifdef USE_SSE41 - _mm_store_si128(&out[j], _mm_max_epi8(packedbytes, Zero)); - #else - _mm_store_si128(&out[j], _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s)); - #endif + const __m256i sum0a = _mm256_max_epi16(_mm256_min_epi16(in0[j * 2 + 0], One), Zero); + const __m256i sum0b = _mm256_max_epi16(_mm256_min_epi16(in0[j * 2 + 1], One), Zero); + const __m256i sum1a = _mm256_max_epi16(_mm256_min_epi16(in1[j * 2 + 0], One), Zero); + const __m256i sum1b = _mm256_max_epi16(_mm256_min_epi16(in1[j * 2 + 1], One), Zero); + + const __m256i pa = _mm256_srli_epi16(_mm256_mullo_epi16(sum0a, sum1a), 7); + const __m256i pb = _mm256_srli_epi16(_mm256_mullo_epi16(sum0b, sum1b), 7); + + out[j] = _mm256_permute4x64_epi64(_mm256_packs_epi16(pa, pb), Control); } - } - return psqt; - #elif defined(USE_MMX) +#elif defined(USE_SSE2) - constexpr IndexType NumChunks = HalfDimensions / SimdWidth; - const __m64 k0x80s = _mm_set1_pi8(-128); + constexpr IndexType OutputChunkSize = 128 / 8; + static_assert((HalfDimensions / 2) % OutputChunkSize == 0); + constexpr IndexType NumOutputChunks = HalfDimensions / 2 / OutputChunkSize; - for (IndexType p = 0; p < 2; ++p) - { - const IndexType offset = HalfDimensions * p; - auto out = reinterpret_cast<__m64*>(&output[offset]); - for (IndexType j = 0; j < NumChunks; ++j) + const __m128i Zero = _mm_setzero_si128(); + const __m128i One = _mm_set1_epi16(127); + + const __m128i* in0 = reinterpret_cast(&(accumulation[perspectives[p]][0])); + const __m128i* in1 = reinterpret_cast(&(accumulation[perspectives[p]][HalfDimensions / 2])); + __m128i* out = reinterpret_cast< __m128i*>(output + offset); + + for (IndexType j = 0; j < NumOutputChunks; j += 1) { - __m64 sum0 = *(&reinterpret_cast(accumulation[perspectives[p]])[j * 2 + 0]); - __m64 sum1 = *(&reinterpret_cast(accumulation[perspectives[p]])[j * 2 + 1]); - const __m64 packedbytes = _mm_packs_pi16(sum0, sum1); - out[j] = _mm_subs_pi8(_mm_adds_pi8(packedbytes, k0x80s), k0x80s); + const __m128i sum0a = _mm_max_epi16(_mm_min_epi16(in0[j * 2 + 0], One), Zero); + const __m128i sum0b = _mm_max_epi16(_mm_min_epi16(in0[j * 2 + 1], One), Zero); + const __m128i sum1a = _mm_max_epi16(_mm_min_epi16(in1[j * 2 + 0], One), Zero); + const __m128i sum1b = _mm_max_epi16(_mm_min_epi16(in1[j * 2 + 1], One), Zero); + + const __m128i pa = _mm_srli_epi16(_mm_mullo_epi16(sum0a, sum1a), 7); + const __m128i pb = _mm_srli_epi16(_mm_mullo_epi16(sum0b, sum1b), 7); + + out[j] = _mm_packs_epi16(pa, pb); } - } - _mm_empty(); - return psqt; - #elif defined(USE_NEON) +#elif defined(USE_NEON) - constexpr IndexType NumChunks = HalfDimensions / (SimdWidth / 2); - const int8x8_t Zero = {0}; + constexpr IndexType OutputChunkSize = 128 / 8; + static_assert((HalfDimensions / 2) % OutputChunkSize == 0); + constexpr IndexType NumOutputChunks = HalfDimensions / 2 / OutputChunkSize; - for (IndexType p = 0; p < 2; ++p) - { - const IndexType offset = HalfDimensions * p; - const auto out = reinterpret_cast(&output[offset]); + const int16x8_t Zero = vdupq_n_s16(0); + const int16x8_t One = vdupq_n_s16(127); - constexpr IndexType UnrollFactor = 16; - static_assert(UnrollFactor % UnrollFactor == 0); - for (IndexType j = 0; j < NumChunks; j += UnrollFactor) + const int16x8_t* in0 = reinterpret_cast(&(accumulation[perspectives[p]][0])); + const int16x8_t* in1 = reinterpret_cast(&(accumulation[perspectives[p]][HalfDimensions / 2])); + int8x16_t* out = reinterpret_cast< int8x16_t*>(output + offset); + + for (IndexType j = 0; j < NumOutputChunks; j += 1) { - int16x8_t sums[UnrollFactor]; - for (IndexType i = 0; i < UnrollFactor; ++i) - sums[i] = reinterpret_cast(accumulation[perspectives[p]])[j+i]; + const int16x8_t sum0a = vmaxq_s16(vminq_s16(in0[j * 2 + 0], One), Zero); + const int16x8_t sum0b = vmaxq_s16(vminq_s16(in0[j * 2 + 1], One), Zero); + const int16x8_t sum1a = vmaxq_s16(vminq_s16(in1[j * 2 + 0], One), Zero); + const int16x8_t sum1b = vmaxq_s16(vminq_s16(in1[j * 2 + 1], One), Zero); + + const int8x8_t pa = vshrn_n_s16(vmulq_s16(sum0a, sum1a), 7); + const int8x8_t pb = vshrn_n_s16(vmulq_s16(sum0b, sum1b), 7); - for (IndexType i = 0; i < UnrollFactor; ++i) - out[j+i] = vmax_s8(vqmovn_s16(sums[i]), Zero); + out[j] = vcombine_s8(pa, pb); } - } - return psqt; - #else +#else - for (IndexType p = 0; p < 2; ++p) - { - const IndexType offset = HalfDimensions * p; - for (IndexType j = 0; j < HalfDimensions; ++j) - { - BiasType sum = accumulation[perspectives[p]][j]; - output[offset + j] = static_cast(std::max(0, std::min(127, sum))); + for (IndexType j = 0; j < HalfDimensions / 2; ++j) { + BiasType sum0 = accumulation[static_cast(perspectives[p])][j + 0]; + BiasType sum1 = accumulation[static_cast(perspectives[p])][j + HalfDimensions / 2]; + sum0 = std::max(0, std::min(127, sum0)); + sum1 = std::max(0, std::min(127, sum1)); + output[offset + j] = static_cast(sum0 * sum1 / 128); } + +#endif } - return psqt; - #endif + return psqt; } // end of function transform() From 3ec6e1d2450183ed4975cf569b5a1286cb9d8369 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 12 Feb 2022 20:08:45 +0300 Subject: [PATCH 0788/1766] Big search tuning (version 2) One more tuning - this one includes newly introduced heuristics and some other parameters that were not included in previous one. Result of 400k games at 20+0.2 "as is". Tuning is continuing since there is probably a lot more elo to gain. STC: https://tests.stockfishchess.org/tests/view/620782edd71106ed12a497d1 LLR: 2.99 (-2.94,2.94) <0.00,2.50> Total: 38504 W: 10260 L: 9978 D: 18266 Ptnml(0-2): 142, 4249, 10230, 4447, 184 LTC: https://tests.stockfishchess.org/tests/view/6207a243d71106ed12a49d07 LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 25176 W: 6793 L: 6546 D: 11837 Ptnml(0-2): 20, 2472, 7360, 2713, 23 closes https://github.com/official-stockfish/Stockfish/pull/3931 Bench: 4784796 --- src/search.cpp | 98 +++++++++++++++++++++++++------------------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5bc9de7b92f..0b98bb27029 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -63,7 +63,7 @@ namespace { // Futility margin Value futility_margin(Depth d, bool improving) { - return Value(171 * (d - improving)); + return Value(147 * (d - improving)); } // Reductions lookup table, initialized at startup @@ -71,7 +71,7 @@ namespace { Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) { int r = Reductions[d] * Reductions[mn]; - return (r + 1575 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 1011); + return (r + 1627 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 992); } constexpr int futility_move_count(bool improving, Depth depth) { @@ -80,7 +80,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return std::min((7 * d + 254) * d - 206 , 1990); + return std::min((8 * d + 281) * d - 241 , 1949); } // Add a small random component to draw evaluations to avoid 3-fold blindness @@ -157,7 +157,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int((21.5 + std::log(Threads.size()) / 2) * std::log(i)); + Reductions[i] = int((21.14 + std::log(Threads.size()) / 2) * std::log(i)); } @@ -303,10 +303,10 @@ void Thread::search() { multiPV = std::min(multiPV, rootMoves.size()); - complexityAverage.set(190, 1); + complexityAverage.set(211, 1); trend = SCORE_ZERO; - optimism[ us] = Value(34); + optimism[ us] = Value(33); optimism[~us] = -optimism[us]; int searchAgainCounter = 0; @@ -349,16 +349,16 @@ void Thread::search() { if (rootDepth >= 4) { Value prev = rootMoves[pvIdx].averageScore; - delta = Value(16) + int(prev) * prev / 16384; + delta = Value(19) + int(prev) * prev / 18321; alpha = std::max(prev - delta,-VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE); // Adjust trend and optimism based on root move's previousScore - int tr = sigmoid(prev, 6, 13, 96, 110, 1); + int tr = sigmoid(prev, 4, 11, 92, 119, 1); trend = (us == WHITE ? make_score(tr, tr / 2) : -make_score(tr, tr / 2)); - int opt = sigmoid(prev, 7, 21, 94, 14786, 221); + int opt = sigmoid(prev, 9, 18, 115, 12250, 187); optimism[ us] = Value(opt); optimism[~us] = -optimism[us]; } @@ -413,7 +413,7 @@ void Thread::search() { else break; - delta += delta / 4 + 3; + delta += delta / 4 + 2; assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE); } @@ -459,17 +459,17 @@ void Thread::search() { && !Threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (87 + 12 * (mainThread->bestPreviousAverageScore - bestValue) - + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 777.20; + double fallingEval = (66 + 12 * (mainThread->bestPreviousAverageScore - bestValue) + + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 809.70; fallingEval = std::clamp(fallingEval, 0.5, 1.5); // If the bestMove is stable over several iterations, reduce time accordingly - timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.70 : 0.91; - double reduction = (1.59 + mainThread->previousTimeReduction) / (2.33 * timeReduction); + timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.73 : 0.94; + double reduction = (1.66 + mainThread->previousTimeReduction) / (2.35 * timeReduction); double bestMoveInstability = 1.073 + std::max(1.0, 2.25 - 9.9 / rootDepth) * totBestMoveChanges / Threads.size(); int complexity = mainThread->complexityAverage.value(); - double complexPosition = std::clamp(1.0 + (complexity - 312) / 1750.0, 0.5, 1.5); + double complexPosition = std::clamp(1.0 + (complexity - 293) / 1525.0, 0.5, 1.5); double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability * complexPosition; @@ -490,7 +490,7 @@ void Thread::search() { } else if ( Threads.increaseDepth && !mainThread->ponder - && Time.elapsed() > totalTime * 0.55) + && Time.elapsed() > totalTime * 0.49) Threads.increaseDepth = false; else Threads.increaseDepth = true; @@ -766,7 +766,7 @@ namespace { // margin and the improving flag are used in various pruning heuristics. improvement = (ss-2)->staticEval != VALUE_NONE ? ss->staticEval - (ss-2)->staticEval : (ss-4)->staticEval != VALUE_NONE ? ss->staticEval - (ss-4)->staticEval - : 200; + : 184; improving = improvement > 0; complexity = abs(ss->staticEval - (us == WHITE ? eg_value(pos.psq_score()) : -eg_value(pos.psq_score()))); @@ -778,7 +778,7 @@ namespace { // return a fail low. if ( !PvNode && depth <= 6 - && eval < alpha - 400 - 300 * depth * depth) + && eval < alpha - 486 - 314 * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -791,16 +791,16 @@ namespace { && depth < 8 && eval - futility_margin(depth, improving) - (ss-1)->statScore / 256 >= beta && eval >= beta - && eval < 17548) // 50% larger than VALUE_KNOWN_WIN, but smaller than TB wins. + && eval < 22266) // larger than VALUE_KNOWN_WIN, but smaller than TB wins. return eval; // Step 9. Null move search with verification search (~22 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 13706 + && (ss-1)->statScore < 15075 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 19 * depth - improvement / 15 + 200 + complexity / 25 + && ss->staticEval >= beta - 18 * depth - improvement / 19 + 215 + complexity / 30 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -808,7 +808,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth, eval and complexity of position - Depth R = std::min(int(eval - beta) / 205, 3) + depth / 3 + 4 - (complexity > 500); + Depth R = std::min(int(eval - beta) / 184, 4) + depth / 3 + 4 - (complexity > 799); ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -844,13 +844,13 @@ namespace { } } - probCutBeta = beta + 229 - 47 * improving; + probCutBeta = beta + 204 - 52 * improving; // Step 10. ProbCut (~4 Elo) // 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 > 3 + && depth > 4 && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY // if value from transposition table is lower than probCutBeta, don't attempt probCut // there and in further interactions with transposition table cutoff depth is set to depth - 3 @@ -908,12 +908,12 @@ namespace { // Step 11. If the position is not in TT, decrease depth by 2 or 1 depending on node type (~3 Elo) if ( PvNode - && depth >= 4 + && depth >= 3 && !ttMove) depth -= 2; if ( cutNode - && depth >= 7 + && depth >= 8 && !ttMove) depth--; @@ -923,7 +923,7 @@ namespace { probCutBeta = beta + 401; if ( ss->inCheck && !PvNode - && depth >= 4 + && depth >= 2 && ttCapture && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3 @@ -1014,14 +1014,14 @@ namespace { if ( !pos.empty(to_sq(move)) && !givesCheck && !PvNode - && lmrDepth < 6 + && lmrDepth < 7 && !ss->inCheck - && ss->staticEval + 392 + 207 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] - + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 8 < alpha) + && ss->staticEval + 424 + 138 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] + + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 7 < alpha) continue; // SEE based pruning (~9 Elo) - if (!pos.see_ge(move, Value(-200) * depth)) + if (!pos.see_ge(move, Value(-214) * depth)) continue; } else @@ -1040,11 +1040,11 @@ namespace { // Futility pruning: parent node (~9 Elo) if ( !ss->inCheck && lmrDepth < 11 - && ss->staticEval + 131 + 137 * lmrDepth + history / 64 <= alpha) + && ss->staticEval + 147 + 125 * lmrDepth + history / 64 <= alpha) continue; // Prune moves with negative SEE (~3 Elo) - if (!pos.see_ge(move, Value(-25 * lmrDepth * lmrDepth - 29 * lmrDepth))) + if (!pos.see_ge(move, Value(-23 * lmrDepth * lmrDepth - 31 * lmrDepth))) continue; } } @@ -1067,7 +1067,7 @@ namespace { && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - 3 * depth; + Value singularBeta = ttValue - 4 * depth; Depth singularDepth = (depth - 1) / 2; ss->excludedMove = move; @@ -1080,8 +1080,8 @@ namespace { // Avoid search explosion by limiting the number of double extensions if ( !PvNode - && value < singularBeta - 71 - && ss->doubleExtensions <= 6) + && value < singularBeta - 52 + && ss->doubleExtensions <= 8) extension = 2; } @@ -1100,15 +1100,15 @@ namespace { // Check extensions (~1 Elo) else if ( givesCheck - && depth > 7 - && abs(ss->staticEval) > 128) + && depth > 8 + && abs(ss->staticEval) > 81) extension = 1; // Quiet ttMove extensions (~0 Elo) else if ( PvNode && move == ttMove && move == ss->killers[0] - && (*contHist[0])[movedPiece][to_sq(move)] >= 8932) + && (*contHist[0])[movedPiece][to_sq(move)] >= 7546) extension = 1; } @@ -1145,7 +1145,7 @@ namespace { // Decrease reduction at some PvNodes (~2 Elo) if ( PvNode - && bestMoveCount <= 4) + && bestMoveCount <= 3) r--; // Decrease reduction if position is or has been on the PV @@ -1170,18 +1170,18 @@ namespace { + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 4142; + - 4123; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - r -= ss->statScore / 15328; + r -= ss->statScore / 17417; // In general we want to cap the LMR depth search at newDepth. But if reductions // are really negative and movecount is low, we allow this move to be searched // deeper than the first move (this may lead to hidden double extensions). int deeper = r >= -1 ? 0 : moveCount <= 5 ? 2 - : PvNode && depth > 4 ? 1 - : cutNode && moveCount <= 5 ? 1 + : PvNode && depth > 3 ? 1 + : cutNode && moveCount <= 7 ? 1 : 0; Depth d = std::clamp(newDepth - r, 1, newDepth + deeper); @@ -1190,7 +1190,7 @@ namespace { // If the son is reduced and fails high it will be re-searched at full depth doFullDepthSearch = value > alpha && d < newDepth; - doDeeperSearch = value > (alpha + 80 + 20 * (newDepth - d)); + doDeeperSearch = value > (alpha + 76 + 11 * (newDepth - d)); didLMR = true; } else @@ -1211,7 +1211,7 @@ namespace { : -stat_bonus(newDepth); if (captureOrPromotion) - bonus /= 5; + bonus /= 6; update_continuation_histories(ss, movedPiece, to_sq(move), bonus); } @@ -1335,14 +1335,14 @@ namespace { quietsSearched, quietCount, capturesSearched, captureCount, depth); // Bonus for prior countermove that caused the fail low - else if ( (depth >= 3 || PvNode) + else if ( (depth >= 4 || PvNode) && !priorCapture) { //Assign extra bonus if current node is PvNode or cutNode //or fail low was really bad bool extraBonus = PvNode || cutNode - || bestValue < alpha - 99 * depth; + || bestValue < alpha - 71 * depth; update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * (1 + extraBonus)); } @@ -1473,7 +1473,7 @@ namespace { if (PvNode && bestValue > alpha) alpha = bestValue; - futilityBase = bestValue + 127; + futilityBase = bestValue + 139; } const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, From 84b1940fcae95bb0a641dda9e85cb96f8c21cd22 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Thu, 17 Feb 2022 10:54:07 +0300 Subject: [PATCH 0789/1766] Tune search at very long time control This patch is a result of tuning done by user @candirufish after 150k games. Since the tuned values were really interesting and touched heuristics that are known for their non-linear scaling I decided to run limited games LTC match, even if the STC test was really bad (which was expected). After seeing the results of the LTC match, I also run a VLTC (very long time control) SPRTtest, which passed. The main difference is in extensions: this patch allows much more singular/double extensions, both in terms of allowing them at lower depths and with lesser margins. Failed STC: https://tests.stockfishchess.org/tests/view/620d66643ec80158c0cd3b46 LLR: -2.94 (-2.94,2.94) <0.00,2.50> Total: 4968 W: 1194 L: 1398 D: 2376 Ptnml(0-2): 47, 633, 1294, 497, 13 Performed well at LTC in a fixed-length match: https://tests.stockfishchess.org/tests/view/620d66823ec80158c0cd3b4a ELO: 3.36 +-1.8 (95%) LOS: 100.0% Total: 30000 W: 7966 L: 7676 D: 14358 Ptnml(0-2): 36, 2936, 8755, 3248, 25 Passed VLTC SPRT test: https://tests.stockfishchess.org/tests/view/620da11a26f5b17ec884f939 LLR: 2.96 (-2.94,2.94) <0.50,3.00> Total: 4400 W: 1326 L: 1127 D: 1947 Ptnml(0-2): 13, 309, 1348, 526, 4 closes https://github.com/official-stockfish/Stockfish/pull/3937 Bench: 6318903 --- src/search.cpp | 88 +++++++++++++++++++++++++------------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 0b98bb27029..e6420931705 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -63,7 +63,7 @@ namespace { // Futility margin Value futility_margin(Depth d, bool improving) { - return Value(147 * (d - improving)); + return Value(168 * (d - improving)); } // Reductions lookup table, initialized at startup @@ -71,7 +71,7 @@ namespace { Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) { int r = Reductions[d] * Reductions[mn]; - return (r + 1627 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 992); + return (r + 1463 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 1010); } constexpr int futility_move_count(bool improving, Depth depth) { @@ -80,7 +80,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return std::min((8 * d + 281) * d - 241 , 1949); + return std::min((9 * d + 270) * d - 311 , 2145); } // Add a small random component to draw evaluations to avoid 3-fold blindness @@ -157,7 +157,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int((21.14 + std::log(Threads.size()) / 2) * std::log(i)); + Reductions[i] = int((20.81 + std::log(Threads.size()) / 2) * std::log(i)); } @@ -303,10 +303,10 @@ void Thread::search() { multiPV = std::min(multiPV, rootMoves.size()); - complexityAverage.set(211, 1); + complexityAverage.set(202, 1); trend = SCORE_ZERO; - optimism[ us] = Value(33); + optimism[ us] = Value(39); optimism[~us] = -optimism[us]; int searchAgainCounter = 0; @@ -349,16 +349,16 @@ void Thread::search() { if (rootDepth >= 4) { Value prev = rootMoves[pvIdx].averageScore; - delta = Value(19) + int(prev) * prev / 18321; + delta = Value(16) + int(prev) * prev / 19178; alpha = std::max(prev - delta,-VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE); // Adjust trend and optimism based on root move's previousScore - int tr = sigmoid(prev, 4, 11, 92, 119, 1); + int tr = sigmoid(prev, 3, 8, 90, 125, 1); trend = (us == WHITE ? make_score(tr, tr / 2) : -make_score(tr, tr / 2)); - int opt = sigmoid(prev, 9, 18, 115, 12250, 187); + int opt = sigmoid(prev, 8, 17, 144, 13966, 183); optimism[ us] = Value(opt); optimism[~us] = -optimism[us]; } @@ -459,17 +459,17 @@ void Thread::search() { && !Threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (66 + 12 * (mainThread->bestPreviousAverageScore - bestValue) - + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 809.70; + double fallingEval = (69 + 12 * (mainThread->bestPreviousAverageScore - bestValue) + + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 781.4; fallingEval = std::clamp(fallingEval, 0.5, 1.5); // If the bestMove is stable over several iterations, reduce time accordingly - timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.73 : 0.94; - double reduction = (1.66 + mainThread->previousTimeReduction) / (2.35 * timeReduction); + timeReduction = lastBestMoveDepth + 10 < completedDepth ? 1.63 : 0.73; + double reduction = (1.56 + mainThread->previousTimeReduction) / (2.20 * timeReduction); double bestMoveInstability = 1.073 + std::max(1.0, 2.25 - 9.9 / rootDepth) * totBestMoveChanges / Threads.size(); int complexity = mainThread->complexityAverage.value(); - double complexPosition = std::clamp(1.0 + (complexity - 293) / 1525.0, 0.5, 1.5); + double complexPosition = std::clamp(1.0 + (complexity - 326) / 1618.1, 0.5, 1.5); double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability * complexPosition; @@ -490,7 +490,7 @@ void Thread::search() { } else if ( Threads.increaseDepth && !mainThread->ponder - && Time.elapsed() > totalTime * 0.49) + && Time.elapsed() > totalTime * 0.43) Threads.increaseDepth = false; else Threads.increaseDepth = true; @@ -766,7 +766,7 @@ namespace { // margin and the improving flag are used in various pruning heuristics. improvement = (ss-2)->staticEval != VALUE_NONE ? ss->staticEval - (ss-2)->staticEval : (ss-4)->staticEval != VALUE_NONE ? ss->staticEval - (ss-4)->staticEval - : 184; + : 175; improving = improvement > 0; complexity = abs(ss->staticEval - (us == WHITE ? eg_value(pos.psq_score()) : -eg_value(pos.psq_score()))); @@ -777,8 +777,8 @@ namespace { // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. if ( !PvNode - && depth <= 6 - && eval < alpha - 486 - 314 * depth * depth) + && depth <= 7 + && eval < alpha - 348 - 258 * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -791,16 +791,16 @@ namespace { && depth < 8 && eval - futility_margin(depth, improving) - (ss-1)->statScore / 256 >= beta && eval >= beta - && eval < 22266) // larger than VALUE_KNOWN_WIN, but smaller than TB wins. + && eval < 26305) // larger than VALUE_KNOWN_WIN, but smaller than TB wins. return eval; // Step 9. Null move search with verification search (~22 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 15075 + && (ss-1)->statScore < 14695 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 18 * depth - improvement / 19 + 215 + complexity / 30 + && ss->staticEval >= beta - 15 * depth - improvement / 15 + 198 + complexity / 28 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -808,7 +808,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth, eval and complexity of position - Depth R = std::min(int(eval - beta) / 184, 4) + depth / 3 + 4 - (complexity > 799); + Depth R = std::min(int(eval - beta) / 147, 5) + depth / 3 + 4 - (complexity > 753); ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -844,7 +844,7 @@ namespace { } } - probCutBeta = beta + 204 - 52 * improving; + probCutBeta = beta + 179 - 46 * improving; // Step 10. ProbCut (~4 Elo) // If we have a good enough capture and a reduced search returns a value @@ -920,7 +920,7 @@ namespace { moves_loop: // When in check, search starts here // Step 12. A small Probcut idea, when we are in check (~0 Elo) - probCutBeta = beta + 401; + probCutBeta = beta + 481; if ( ss->inCheck && !PvNode && depth >= 2 @@ -1014,14 +1014,14 @@ namespace { if ( !pos.empty(to_sq(move)) && !givesCheck && !PvNode - && lmrDepth < 7 + && lmrDepth < 6 && !ss->inCheck - && ss->staticEval + 424 + 138 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] - + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 7 < alpha) + && ss->staticEval + 281 + 179 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] + + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 6 < alpha) continue; // SEE based pruning (~9 Elo) - if (!pos.see_ge(move, Value(-214) * depth)) + if (!pos.see_ge(move, Value(-203) * depth)) continue; } else @@ -1040,11 +1040,11 @@ namespace { // Futility pruning: parent node (~9 Elo) if ( !ss->inCheck && lmrDepth < 11 - && ss->staticEval + 147 + 125 * lmrDepth + history / 64 <= alpha) + && ss->staticEval + 122 + 138 * lmrDepth + history / 60 <= alpha) continue; // Prune moves with negative SEE (~3 Elo) - if (!pos.see_ge(move, Value(-23 * lmrDepth * lmrDepth - 31 * lmrDepth))) + if (!pos.see_ge(move, Value(-25 * lmrDepth * lmrDepth - 20 * lmrDepth))) continue; } } @@ -1059,7 +1059,7 @@ namespace { // 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 ( !rootNode - && depth >= 6 + 2 * (PvNode && tte->is_pv()) + && depth >= 4 + 2 * (PvNode && tte->is_pv()) && move == ttMove && !excludedMove // Avoid recursive singular search /* && ttValue != VALUE_NONE Already implicit in the next condition */ @@ -1067,7 +1067,7 @@ namespace { && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - 4 * depth; + Value singularBeta = ttValue - 3 * depth; Depth singularDepth = (depth - 1) / 2; ss->excludedMove = move; @@ -1080,7 +1080,7 @@ namespace { // Avoid search explosion by limiting the number of double extensions if ( !PvNode - && value < singularBeta - 52 + && value < singularBeta - 26 && ss->doubleExtensions <= 8) extension = 2; } @@ -1100,15 +1100,15 @@ namespace { // Check extensions (~1 Elo) else if ( givesCheck - && depth > 8 - && abs(ss->staticEval) > 81) + && depth > 9 + && abs(ss->staticEval) > 71) extension = 1; // Quiet ttMove extensions (~0 Elo) else if ( PvNode && move == ttMove && move == ss->killers[0] - && (*contHist[0])[movedPiece][to_sq(move)] >= 7546) + && (*contHist[0])[movedPiece][to_sq(move)] >= 5491) extension = 1; } @@ -1170,18 +1170,18 @@ namespace { + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 4123; + - 4334; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - r -= ss->statScore / 17417; + r -= ss->statScore / 15914; // In general we want to cap the LMR depth search at newDepth. But if reductions // are really negative and movecount is low, we allow this move to be searched // deeper than the first move (this may lead to hidden double extensions). int deeper = r >= -1 ? 0 - : moveCount <= 5 ? 2 - : PvNode && depth > 3 ? 1 - : cutNode && moveCount <= 7 ? 1 + : moveCount <= 4 ? 2 + : PvNode && depth > 4 ? 1 + : cutNode && moveCount <= 8 ? 1 : 0; Depth d = std::clamp(newDepth - r, 1, newDepth + deeper); @@ -1190,7 +1190,7 @@ namespace { // If the son is reduced and fails high it will be re-searched at full depth doFullDepthSearch = value > alpha && d < newDepth; - doDeeperSearch = value > (alpha + 76 + 11 * (newDepth - d)); + doDeeperSearch = value > (alpha + 78 + 11 * (newDepth - d)); didLMR = true; } else @@ -1342,7 +1342,7 @@ namespace { //or fail low was really bad bool extraBonus = PvNode || cutNode - || bestValue < alpha - 71 * depth; + || bestValue < alpha - 70 * depth; update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * (1 + extraBonus)); } @@ -1473,7 +1473,7 @@ namespace { if (PvNode && bestValue > alpha) alpha = bestValue; - futilityBase = bestValue + 139; + futilityBase = bestValue + 118; } const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, From 2da1d1bf571e3fd2e1d6cf56b76a7504de1a9453 Mon Sep 17 00:00:00 2001 From: ppigazzini Date: Sun, 6 Feb 2022 19:20:30 +0100 Subject: [PATCH 0790/1766] Add ARM NDK to Github Actions matrix - set the variable only for the required tests to keep simple the yml file - use NDK 21.x until will be fixed the Stockfish static build problem with NDK 23.x - set the test for armv7, armv7-neon, armv8 builds: - use armv7a-linux-androideabi21-clang++ compiler for armv7 armv7-neon - enforce a static build - silence the Warning for the unused compilation flag "-pie" with the static build, otherwise the Github workflow stops - use qemu to bench the build and get the signature Many thanks to @pschneider1968 that made all the hard work with NDK :) closes https://github.com/official-stockfish/Stockfish/pull/3924 No functional change --- .github/workflows/stockfish.yml | 68 +++++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 12 deletions(-) diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index 33126a11e35..f1741ed8b37 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -5,6 +5,7 @@ on: - master - tools - github_ci + - github_ci_armv7 pull_request: branches: - master @@ -20,6 +21,12 @@ jobs: strategy: matrix: config: + # set the variable for the required tests: + # run_expensive_tests: true + # run_32bit_tests: true + # run_64bit_tests: true + # run_armv8_tests: true + # run_armv7_tests: true - { name: "Ubuntu 20.04 GCC", os: ubuntu-20.04, @@ -35,18 +42,31 @@ jobs: os: ubuntu-20.04, compiler: clang++, comp: clang, - run_expensive_tests: false, run_32bit_tests: true, run_64bit_tests: true, shell: 'bash {0}' } + - { + name: "Ubuntu 20.04 NDK armv8", + os: ubuntu-20.04, + compiler: aarch64-linux-android21-clang++, + comp: ndk, + run_armv8_tests: true, + shell: 'bash {0}' + } + - { + name: "Ubuntu 20.04 NDK armv7", + os: ubuntu-20.04, + compiler: armv7a-linux-androideabi21-clang++, + comp: ndk, + run_armv7_tests: true, + shell: 'bash {0}' + } - { name: "MacOS 10.15 Apple Clang", os: macos-10.15, compiler: clang++, comp: clang, - run_expensive_tests: false, - run_32bit_tests: false, run_64bit_tests: true, shell: 'bash {0}' } @@ -55,8 +75,6 @@ jobs: os: macos-10.15, compiler: g++-10, comp: gcc, - run_expensive_tests: false, - run_32bit_tests: false, run_64bit_tests: true, shell: 'bash {0}' } @@ -65,8 +83,6 @@ jobs: os: windows-2022, compiler: g++, comp: gcc, - run_expensive_tests: false, - run_32bit_tests: false, run_64bit_tests: true, msys_sys: 'mingw64', msys_env: 'x86_64', @@ -77,9 +93,7 @@ jobs: os: windows-2022, compiler: g++, comp: gcc, - run_expensive_tests: false, run_32bit_tests: true, - run_64bit_tests: false, msys_sys: 'mingw32', msys_env: 'i686', shell: 'msys2 {0}' @@ -89,8 +103,6 @@ jobs: os: windows-2022, compiler: clang++, comp: clang, - run_expensive_tests: false, - run_32bit_tests: false, run_64bit_tests: true, msys_sys: 'clang64', msys_env: 'clang-x86_64', @@ -110,7 +122,7 @@ jobs: if: runner.os == 'Linux' run: | sudo apt update - sudo apt install expect valgrind g++-multilib + sudo apt install expect valgrind g++-multilib qemu-user - name: Setup msys and install required packages if: runner.os == 'Windows' @@ -130,6 +142,7 @@ jobs: - name: Check compiler run: | + export PATH=$PATH:$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin $COMPILER -v - name: Test help target @@ -251,6 +264,37 @@ jobs: make clean make -j2 ARCH=x86-64-vnni256 build + # armv8 tests + + - name: Test armv8 build + if: ${{ matrix.config.run_armv8_tests }} + run: | + export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH + export LDFLAGS="-static -Wno-unused-command-line-argument" + make clean + make -j2 ARCH=armv8 build + ../tests/signature.sh $benchref + + # armv7 tests + + - name: Test armv7 build + if: ${{ matrix.config.run_armv7_tests }} + run: | + export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH + export LDFLAGS="-static -Wno-unused-command-line-argument" + make clean + make -j2 ARCH=armv7 build + ../tests/signature.sh $benchref + + - name: Test armv7-neon build + if: ${{ matrix.config.run_armv7_tests }} + run: | + export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH + export LDFLAGS="-static -Wno-unused-command-line-argument" + make clean + make -j2 ARCH=armv7-neon build + ../tests/signature.sh $benchref + # Other tests - name: Check perft and search reproducibility From abef3e86f42fd4953d28cc7c3381601475d11346 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 20 Feb 2022 17:36:19 +0100 Subject: [PATCH 0791/1766] Fix clang warning on unused variable mark variable as used. fixes https://github.com/official-stockfish/Stockfish/issues/3900 closes https://github.com/official-stockfish/Stockfish/pull/3941 No functional change --- src/evaluate.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index a5c049a88ad..923564cbfe8 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -108,6 +108,7 @@ namespace Eval { MemoryBuffer buffer(const_cast(reinterpret_cast(gEmbeddedNNUEData)), size_t(gEmbeddedNNUESize)); + (void) gEmbeddedNNUEEnd; // Silence warning on unused variable istream stream(&buffer); if (load_eval(eval_file, stream)) From 27139dedac14af400f5b18e2ab50aca3f8cf0e33 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 19 Feb 2022 18:24:11 +0300 Subject: [PATCH 0792/1766] Adjust usage of LMR for 2nd move in move ordering Current master prohibits usage of LMR for 2nd move at rootNode. This patch also disables LMR for 2nd move not only at rootNode but also at first PvNode that is a reply to rootNode. passed STC: https://tests.stockfishchess.org/tests/view/620e8c9026f5b17ec885143a LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 54096 W: 14305 L: 13996 D: 25795 Ptnml(0-2): 209, 6075, 14192, 6342, 230 passed LTC: https://tests.stockfishchess.org/tests/view/620eb327b1792e8985f81fb8 LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 110864 W: 29602 L: 29156 D: 52106 Ptnml(0-2): 112, 11147, 32455, 11619, 99 closes https://github.com/official-stockfish/Stockfish/pull/3940 bench 6820724 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index e6420931705..6785ba4c6b4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1136,7 +1136,7 @@ namespace { // been searched. In general we would like to reduce them, but there are many // cases where we extend a son if it has good chances to be "interesting". if ( depth >= 2 - && moveCount > 1 + rootNode + && moveCount > 1 + (PvNode && ss->ply <= 1) && ( !ss->ttPv || !captureOrPromotion || (cutNode && (ss-1)->moveCount > 1))) From 5f781d366e0f4369ec12e36c9978ad63ffa32235 Mon Sep 17 00:00:00 2001 From: mstembera Date: Wed, 23 Feb 2022 18:19:36 -0800 Subject: [PATCH 0793/1766] Clean up and simplify some nnue code. Remove some unnecessary code and it's execution during inference. Also the change on line 49 in nnue_architecture.h results in a more efficient SIMD code path through ClippedReLU::propagate(). passed STC: https://tests.stockfishchess.org/tests/view/6217d3bfda649bba32ef25d5 LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 12056 W: 3281 L: 3092 D: 5683 Ptnml(0-2): 55, 1213, 3312, 1384, 64 passed STC SMP: https://tests.stockfishchess.org/tests/view/6217f344da649bba32ef295e LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 27376 W: 7295 L: 7137 D: 12944 Ptnml(0-2): 52, 2859, 7715, 3003, 59 closes https://github.com/official-stockfish/Stockfish/pull/3944 No functional change bench: 6820724 --- src/nnue/evaluate_nnue.cpp | 6 +++--- src/nnue/layers/affine_transform.h | 16 ++++++++-------- src/nnue/layers/clipped_relu.h | 8 -------- src/nnue/nnue_architecture.h | 19 ++++++++++--------- src/nnue/nnue_common.h | 4 ++-- src/nnue/nnue_feature_transformer.h | 6 ++++-- 6 files changed, 27 insertions(+), 32 deletions(-) diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 0fd58462b78..9254e36f9ba 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -109,7 +109,7 @@ namespace Stockfish::Eval::NNUE { { write_little_endian(stream, Version); write_little_endian(stream, hashValue); - write_little_endian(stream, desc.size()); + write_little_endian(stream, (std::uint32_t)desc.size()); stream.write(&desc[0], desc.size()); return !stream.fail(); } @@ -157,7 +157,7 @@ namespace Stockfish::Eval::NNUE { ASSERT_ALIGNED(transformedFeatures, alignment); - const std::size_t bucket = (pos.count() - 1) / 4; + const int bucket = (pos.count() - 1) / 4; const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket); const auto positional = network[bucket]->propagate(transformedFeatures); @@ -197,7 +197,7 @@ namespace Stockfish::Eval::NNUE { NnueEvalTrace t{}; t.correctBucket = (pos.count() - 1) / 4; - for (std::size_t bucket = 0; bucket < LayerStacks; ++bucket) { + for (IndexType bucket = 0; bucket < LayerStacks; ++bucket) { const auto materialist = featureTransformer->transform(pos, transformedFeatures, bucket); const auto positional = network[bucket]->propagate(transformedFeatures); diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 22451915ba1..9a992608cc1 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -235,10 +235,10 @@ namespace Stockfish::Eval::NNUE::Layers { // Read network parameters bool read_parameters(std::istream& stream) { - for (std::size_t i = 0; i < OutputDimensions; ++i) + for (IndexType i = 0; i < OutputDimensions; ++i) biases[i] = read_little_endian(stream); - for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) + for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) weights[get_weight_index(i)] = read_little_endian(stream); return !stream.fail(); @@ -246,10 +246,10 @@ namespace Stockfish::Eval::NNUE::Layers { // Write network parameters bool write_parameters(std::ostream& stream) const { - for (std::size_t i = 0; i < OutputDimensions; ++i) + for (IndexType i = 0; i < OutputDimensions; ++i) write_little_endian(stream, biases[i]); - for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) + for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) write_little_endian(stream, weights[get_weight_index(i)]); return !stream.fail(); @@ -422,9 +422,9 @@ namespace Stockfish::Eval::NNUE::Layers { // Read network parameters bool read_parameters(std::istream& stream) { - for (std::size_t i = 0; i < OutputDimensions; ++i) + for (IndexType i = 0; i < OutputDimensions; ++i) biases[i] = read_little_endian(stream); - for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) + for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) weights[get_weight_index(i)] = read_little_endian(stream); return !stream.fail(); @@ -432,10 +432,10 @@ namespace Stockfish::Eval::NNUE::Layers { // Write network parameters bool write_parameters(std::ostream& stream) const { - for (std::size_t i = 0; i < OutputDimensions; ++i) + for (IndexType i = 0; i < OutputDimensions; ++i) write_little_endian(stream, biases[i]); - for (std::size_t i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) + for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) write_little_endian(stream, weights[get_weight_index(i)]); return !stream.fail(); diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index ffd2e3b76a9..f94d30828d7 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -171,14 +171,6 @@ namespace Stockfish::Eval::NNUE::Layers { std::max(0, std::min(127, input[i] >> WeightScaleBits))); } - // Affine transform layers expect that there is at least - // ceil_to_multiple(OutputDimensions, 32) initialized values. - // We cannot do this in the affine transform because it requires - // preallocating space here. - for (IndexType i = OutputDimensions; i < PaddedOutputDimensions; ++i) { - output[i] = 0; - } - return output; } }; diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index 725b40fb43d..b4f65364c2e 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -46,7 +46,7 @@ struct Network static constexpr int FC_1_OUTPUTS = 32; Layers::AffineTransform fc_0; - Layers::ClippedReLU ac_0; + Layers::ClippedReLU ac_0; Layers::AffineTransform fc_1; Layers::ClippedReLU ac_1; Layers::AffineTransform fc_2; @@ -97,14 +97,19 @@ struct Network alignas(CacheLineSize) decltype(fc_1)::OutputBuffer fc_1_out; alignas(CacheLineSize) decltype(ac_1)::OutputBuffer ac_1_out; alignas(CacheLineSize) decltype(fc_2)::OutputBuffer fc_2_out; + + Buffer() + { + std::memset(this, 0, sizeof(*this)); + } }; #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) - char bufferRaw[sizeof(Buffer) + alignment]; - char* bufferRawAligned = align_ptr_up(&bufferRaw[0]); - Buffer& buffer = *(new (bufferRawAligned) Buffer); + static thread_local char bufferRaw[sizeof(Buffer) + alignment]; + static thread_local char* bufferRawAligned = align_ptr_up(&bufferRaw[0]); + static thread_local Buffer& buffer = *(new (bufferRawAligned) Buffer); #else - alignas(alignment) Buffer buffer; + alignas(alignment) static thread_local Buffer buffer; #endif fc_0.propagate(transformedFeatures, buffer.fc_0_out); @@ -118,10 +123,6 @@ struct Network std::int32_t fwdOut = int(buffer.fc_0_out[FC_0_OUTPUTS]) * (600*OutputScale) / (127*(1<>= 8; } } - u[i] = v; + u[i] = (std::uint8_t)v; stream.write(reinterpret_cast(u), sizeof(IntType)); } diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index fb867421f6c..855980182fc 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -123,8 +123,10 @@ namespace Stockfish::Eval::NNUE { // We use __m* types as template arguments, which causes GCC to emit warnings // about losing some attribute information. This is irrelevant to us as we // only take their size, so the following pragma are harmless. + #if defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wignored-attributes" + #endif template (); static constexpr int NumPsqtRegs = BestRegisterCount(); - + #if defined(__GNUC__) #pragma GCC diagnostic pop - + #endif #endif From 174b038bf3b3b8a0d82422a861a050391a33f34a Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Sun, 27 Feb 2022 17:02:13 +0100 Subject: [PATCH 0794/1766] Use dynamic allocation for evaluation scratch TLS buffer. fixes #3946 an issue related with the toolchain as found in xcode 12 on macOS, related to previous commit 5f781d36. closes https://github.com/official-stockfish/Stockfish/pull/3950 No functional change --- src/nnue/nnue_architecture.h | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index b4f65364c2e..4f9596ae949 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -21,6 +21,8 @@ #ifndef NNUE_ARCHITECTURE_H_INCLUDED #define NNUE_ARCHITECTURE_H_INCLUDED +#include + #include "nnue_common.h" #include "features/half_ka_v2_hm.h" @@ -88,9 +90,7 @@ struct Network std::int32_t propagate(const TransformedFeatureType* transformedFeatures) { - constexpr uint64_t alignment = CacheLineSize; - - struct Buffer + struct alignas(CacheLineSize) Buffer { alignas(CacheLineSize) decltype(fc_0)::OutputBuffer fc_0_out; alignas(CacheLineSize) decltype(ac_0)::OutputBuffer ac_0_out; @@ -104,12 +104,13 @@ struct Network } }; -#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) - static thread_local char bufferRaw[sizeof(Buffer) + alignment]; - static thread_local char* bufferRawAligned = align_ptr_up(&bufferRaw[0]); - static thread_local Buffer& buffer = *(new (bufferRawAligned) Buffer); +#if defined(__clang__) && (__APPLE__) + // workaround for a bug reported with xcode 12 + static thread_local auto tlsBuffer = std::make_unique(); + // Access TLS only once, cache result. + Buffer& buffer = *tlsBuffer; #else - alignas(alignment) static thread_local Buffer buffer; + alignas(CacheLineSize) static thread_local Buffer buffer; #endif fc_0.propagate(transformedFeatures, buffer.fc_0_out); From 4ac7d726ec5ba38ba593bca2ab5cb7589785a957 Mon Sep 17 00:00:00 2001 From: Giacomo Lorenzetti Date: Tue, 1 Mar 2022 09:34:40 +0100 Subject: [PATCH 0795/1766] Sort captures This patch (partially) sort captures in analogy to quiet moves. All three movepickers are affected, hence `depth` is added as an argument in probcut's. Passed STC: https://tests.stockfishchess.org/tests/view/621a4576da649bba32ef6fd4 LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 103848 W: 27884 L: 27473 D: 48491 Ptnml(0-2): 587, 11691, 26974, 12068, 604 Passed LTC: https://tests.stockfishchess.org/tests/view/621aaa5bda649bba32ef7c2d LLR: 2.96 (-2.94,2.94) <0.50,3.00> Total: 212032 W: 56420 L: 55739 D: 99873 Ptnml(0-2): 198, 21310, 62348, 21933, 227 closes https://github.com/official-stockfish/Stockfish/pull/3952 Bench: 6833580 --- src/movepick.cpp | 11 ++++++----- src/movepick.h | 2 +- src/search.cpp | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 694b9222fe5..dc35113f90e 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -87,8 +87,8 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist /// MovePicker constructor for ProbCut: we generate captures with SEE greater /// than or equal to the given threshold. -MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph) - : pos(p), captureHistory(cph), ttMove(ttm), threshold(th) +MovePicker::MovePicker(const Position& p, Move ttm, Value th, Depth d, const CapturePieceToHistory* cph) + : pos(p), captureHistory(cph), ttMove(ttm), threshold(th), depth(d) { assert(!pos.checkers()); @@ -169,11 +169,12 @@ Move MovePicker::next_move(bool skipQuiets) { endMoves = generate(pos, cur); score(); + partial_insertion_sort(cur, endMoves, -3000 * depth); ++stage; goto top; case GOOD_CAPTURE: - if (select([&](){ + if (select([&](){ return pos.see_ge(*cur, Value(-69 * cur->value / 1024)) ? // Move losing capture to endBadCaptures to be tried later true : (*endBadCaptures++ = *cur, false); })) @@ -241,10 +242,10 @@ Move MovePicker::next_move(bool skipQuiets) { return select([](){ return true; }); case PROBCUT: - return select([&](){ return pos.see_ge(*cur, threshold); }); + return select([&](){ return pos.see_ge(*cur, threshold); }); case QCAPTURE: - if (select([&](){ return depth > DEPTH_QS_RECAPTURES + if (select([&](){ return depth > DEPTH_QS_RECAPTURES || to_sq(*cur) == recaptureSquare; })) return *(cur - 1); diff --git a/src/movepick.h b/src/movepick.h index e2cbfcdee33..9a3c279bd2e 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -126,7 +126,7 @@ class MovePicker { const CapturePieceToHistory*, const PieceToHistory**, Square); - MovePicker(const Position&, Move, Value, const CapturePieceToHistory*); + MovePicker(const Position&, Move, Value, Depth, const CapturePieceToHistory*); Move next_move(bool skipQuiets = false); private: diff --git a/src/search.cpp b/src/search.cpp index 6785ba4c6b4..a6552daf2a0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -863,7 +863,7 @@ namespace { { assert(probCutBeta < VALUE_INFINITE); - MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, &captureHistory); + MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, depth - 3, &captureHistory); bool ttPv = ss->ttPv; ss->ttPv = false; From 270a0e737fea1774b409f70f378ca52cbc42dd3d Mon Sep 17 00:00:00 2001 From: Ben Chaney Date: Tue, 1 Mar 2022 17:49:02 -0500 Subject: [PATCH 0796/1766] Generalize the feature transform to use vec_t macros This commit generalizes the feature transform to use vec_t macros that are architecture defined instead of using a seperate code path for each one. It should make some old architectures (MMX, including improvements by Fanael) faster and make further such improvements easier in the future. Includes some corrections to CI for mingw. closes https://github.com/official-stockfish/Stockfish/pull/3955 closes https://github.com/official-stockfish/Stockfish/pull/3928 No functional change --- .github/workflows/stockfish.yml | 12 +- AUTHORS | 1 + src/nnue/nnue_feature_transformer.h | 165 ++++++++++++---------------- 3 files changed, 78 insertions(+), 100 deletions(-) diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index f1741ed8b37..33560d52b60 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -82,20 +82,20 @@ jobs: name: "Windows 2022 Mingw-w64 GCC x86_64", os: windows-2022, compiler: g++, - comp: gcc, + comp: mingw, run_64bit_tests: true, msys_sys: 'mingw64', - msys_env: 'x86_64', + msys_env: 'x86_64-gcc', shell: 'msys2 {0}' } - { name: "Windows 2022 Mingw-w64 GCC i686", os: windows-2022, compiler: g++, - comp: gcc, + comp: mingw, run_32bit_tests: true, msys_sys: 'mingw32', - msys_env: 'i686', + msys_env: 'i686-gcc', shell: 'msys2 {0}' } - { @@ -105,7 +105,7 @@ jobs: comp: clang, run_64bit_tests: true, msys_sys: 'clang64', - msys_env: 'clang-x86_64', + msys_env: 'clang-x86_64-clang', shell: 'msys2 {0}' } @@ -129,7 +129,7 @@ jobs: uses: msys2/setup-msys2@v2 with: msystem: ${{matrix.config.msys_sys}} - install: mingw-w64-${{matrix.config.msys_env}}-${{matrix.config.comp}} make git expect + install: mingw-w64-${{matrix.config.msys_env}} make git expect - name: Download the used network from the fishtest framework run: | diff --git a/AUTHORS b/AUTHORS index f49c1db0ede..65620886dcd 100644 --- a/AUTHORS +++ b/AUTHORS @@ -31,6 +31,7 @@ Arjun Temurnikar Artem Solopiy (EntityFX) Auguste Pop Balint Pfliegel +Ben Chaney (Chaneybenjamini) Ben Koshy (BKSpurgeon) Bill Henry (VoyagerOne) Bojun Guo (noobpwnftw, Nooby) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 855980182fc..c969ac6cee8 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -47,12 +47,22 @@ namespace Stockfish::Eval::NNUE { #define vec_store(a,b) _mm512_store_si512(a,b) #define vec_add_16(a,b) _mm512_add_epi16(a,b) #define vec_sub_16(a,b) _mm512_sub_epi16(a,b) + #define vec_mul_16(a,b) _mm512_mullo_epi16(a,b) + #define vec_zero() _mm512_setzero_epi32() + #define vec_set_16(a) _mm512_set1_epi16(a) + #define vec_max_16(a,b) _mm512_max_epi16(a,b) + #define vec_min_16(a,b) _mm512_min_epi16(a,b) + inline vec_t vec_msb_pack_16(vec_t a, vec_t b){ + vec_t compacted = _mm512_packs_epi16(_mm512_srli_epi16(a,7),_mm512_srli_epi16(b,7)); + return _mm512_permutexvar_epi64(_mm512_setr_epi64(0, 2, 4, 6, 1, 3, 5, 7), compacted); + } #define vec_load_psqt(a) _mm256_load_si256(a) #define vec_store_psqt(a,b) _mm256_store_si256(a,b) #define vec_add_psqt_32(a,b) _mm256_add_epi32(a,b) #define vec_sub_psqt_32(a,b) _mm256_sub_epi32(a,b) #define vec_zero_psqt() _mm256_setzero_si256() #define NumRegistersSIMD 32 + #define MaxChunkSize 64 #elif USE_AVX2 typedef __m256i vec_t; @@ -61,12 +71,22 @@ namespace Stockfish::Eval::NNUE { #define vec_store(a,b) _mm256_store_si256(a,b) #define vec_add_16(a,b) _mm256_add_epi16(a,b) #define vec_sub_16(a,b) _mm256_sub_epi16(a,b) + #define vec_mul_16(a,b) _mm256_mullo_epi16(a,b) + #define vec_zero() _mm256_setzero_si256() + #define vec_set_16(a) _mm256_set1_epi16(a) + #define vec_max_16(a,b) _mm256_max_epi16(a,b) + #define vec_min_16(a,b) _mm256_min_epi16(a,b) + inline vec_t vec_msb_pack_16(vec_t a, vec_t b){ + vec_t compacted = _mm256_packs_epi16(_mm256_srli_epi16(a,7), _mm256_srli_epi16(b,7)); + return _mm256_permute4x64_epi64(compacted, 0b11011000); + } #define vec_load_psqt(a) _mm256_load_si256(a) #define vec_store_psqt(a,b) _mm256_store_si256(a,b) #define vec_add_psqt_32(a,b) _mm256_add_epi32(a,b) #define vec_sub_psqt_32(a,b) _mm256_sub_epi32(a,b) #define vec_zero_psqt() _mm256_setzero_si256() #define NumRegistersSIMD 16 + #define MaxChunkSize 32 #elif USE_SSE2 typedef __m128i vec_t; @@ -75,12 +95,19 @@ namespace Stockfish::Eval::NNUE { #define vec_store(a,b) *(a)=(b) #define vec_add_16(a,b) _mm_add_epi16(a,b) #define vec_sub_16(a,b) _mm_sub_epi16(a,b) + #define vec_mul_16(a,b) _mm_mullo_epi16(a,b) + #define vec_zero() _mm_setzero_si128() + #define vec_set_16(a) _mm_set1_epi16(a) + #define vec_max_16(a,b) _mm_max_epi16(a,b) + #define vec_min_16(a,b) _mm_min_epi16(a,b) + #define vec_msb_pack_16(a,b) _mm_packs_epi16(_mm_srli_epi16(a,7),_mm_srli_epi16(b,7)) #define vec_load_psqt(a) (*(a)) #define vec_store_psqt(a,b) *(a)=(b) #define vec_add_psqt_32(a,b) _mm_add_epi32(a,b) #define vec_sub_psqt_32(a,b) _mm_sub_epi32(a,b) #define vec_zero_psqt() _mm_setzero_si128() #define NumRegistersSIMD (Is64Bit ? 16 : 8) + #define MaxChunkSize 16 #elif USE_MMX typedef __m64 vec_t; @@ -89,12 +116,26 @@ namespace Stockfish::Eval::NNUE { #define vec_store(a,b) *(a)=(b) #define vec_add_16(a,b) _mm_add_pi16(a,b) #define vec_sub_16(a,b) _mm_sub_pi16(a,b) + #define vec_mul_16(a,b) _mm_mullo_pi16(a,b) + #define vec_zero() _mm_setzero_si64() + #define vec_set_16(a) _mm_set1_pi16(a) + inline vec_t vec_max_16(vec_t a,vec_t b){ + vec_t comparison = _mm_cmpgt_pi16(a,b); + return _mm_or_si64(_mm_and_si64(comparison, a), _mm_andnot_si64(comparison, b)); + } + inline vec_t vec_min_16(vec_t a,vec_t b){ + vec_t comparison = _mm_cmpgt_pi16(a,b); + return _mm_or_si64(_mm_and_si64(comparison, b), _mm_andnot_si64(comparison, a)); + } + #define vec_msb_pack_16(a,b) _mm_packs_pi16(_mm_srli_pi16(a,7),_mm_srli_pi16(b,7)) #define vec_load_psqt(a) (*(a)) #define vec_store_psqt(a,b) *(a)=(b) #define vec_add_psqt_32(a,b) _mm_add_pi32(a,b) #define vec_sub_psqt_32(a,b) _mm_sub_pi32(a,b) #define vec_zero_psqt() _mm_setzero_si64() + #define vec_cleanup() _mm_empty() #define NumRegistersSIMD 8 + #define MaxChunkSize 8 #elif USE_NEON typedef int16x8_t vec_t; @@ -103,12 +144,24 @@ namespace Stockfish::Eval::NNUE { #define vec_store(a,b) *(a)=(b) #define vec_add_16(a,b) vaddq_s16(a,b) #define vec_sub_16(a,b) vsubq_s16(a,b) + #define vec_mul_16(a,b) vmulq_s16(a,b) + #define vec_zero() vec_t{0} + #define vec_set_16(a) vdupq_n_s16(a) + #define vec_max_16(a,b) vmaxq_s16(a,b) + #define vec_min_16(a,b) vminq_s16(a,b) + inline vec_t vec_msb_pack_16(vec_t a, vec_t b){ + const int8x8_t shifta = vshrn_n_s16(a, 7); + const int8x8_t shiftb = vshrn_n_s16(b, 7); + const int8x16_t compacted = vcombine_s8(shifta,shiftb); + return *reinterpret_cast (&compacted); + } #define vec_load_psqt(a) (*(a)) #define vec_store_psqt(a,b) *(a)=(b) #define vec_add_psqt_32(a,b) vaddq_s32(a,b) #define vec_sub_psqt_32(a,b) vsubq_s32(a,b) #define vec_zero_psqt() psqt_vec_t{0} #define NumRegistersSIMD 16 + #define MaxChunkSize 16 #else #undef VECTOR @@ -235,110 +288,30 @@ namespace Stockfish::Eval::NNUE { { const IndexType offset = (HalfDimensions / 2) * p; -#if defined(USE_AVX512) +#if defined(VECTOR) - constexpr IndexType OutputChunkSize = 512 / 8; + constexpr IndexType OutputChunkSize = MaxChunkSize; static_assert((HalfDimensions / 2) % OutputChunkSize == 0); constexpr IndexType NumOutputChunks = HalfDimensions / 2 / OutputChunkSize; - const __m512i Zero = _mm512_setzero_si512(); - const __m512i One = _mm512_set1_epi16(127); - const __m512i Control = _mm512_setr_epi64(0, 2, 4, 6, 1, 3, 5, 7); + vec_t Zero = vec_zero(); + vec_t One = vec_set_16(127); - const __m512i* in0 = reinterpret_cast(&(accumulation[perspectives[p]][0])); - const __m512i* in1 = reinterpret_cast(&(accumulation[perspectives[p]][HalfDimensions / 2])); - __m512i* out = reinterpret_cast< __m512i*>(output + offset); + const vec_t* in0 = reinterpret_cast(&(accumulation[perspectives[p]][0])); + const vec_t* in1 = reinterpret_cast(&(accumulation[perspectives[p]][HalfDimensions / 2])); + vec_t* out = reinterpret_cast< vec_t*>(output + offset); for (IndexType j = 0; j < NumOutputChunks; j += 1) { - const __m512i sum0a = _mm512_max_epi16(_mm512_min_epi16(in0[j * 2 + 0], One), Zero); - const __m512i sum0b = _mm512_max_epi16(_mm512_min_epi16(in0[j * 2 + 1], One), Zero); - const __m512i sum1a = _mm512_max_epi16(_mm512_min_epi16(in1[j * 2 + 0], One), Zero); - const __m512i sum1b = _mm512_max_epi16(_mm512_min_epi16(in1[j * 2 + 1], One), Zero); + const vec_t sum0a = vec_max_16(vec_min_16(in0[j * 2 + 0], One), Zero); + const vec_t sum0b = vec_max_16(vec_min_16(in0[j * 2 + 1], One), Zero); + const vec_t sum1a = vec_max_16(vec_min_16(in1[j * 2 + 0], One), Zero); + const vec_t sum1b = vec_max_16(vec_min_16(in1[j * 2 + 1], One), Zero); - const __m512i pa = _mm512_srli_epi16(_mm512_mullo_epi16(sum0a, sum1a), 7); - const __m512i pb = _mm512_srli_epi16(_mm512_mullo_epi16(sum0b, sum1b), 7); + const vec_t pa = vec_mul_16(sum0a, sum1a); + const vec_t pb = vec_mul_16(sum0b, sum1b); - out[j] = _mm512_permutexvar_epi64(Control, _mm512_packs_epi16(pa, pb)); - } - -#elif defined(USE_AVX2) - - constexpr IndexType OutputChunkSize = 256 / 8; - static_assert((HalfDimensions / 2) % OutputChunkSize == 0); - constexpr IndexType NumOutputChunks = HalfDimensions / 2 / OutputChunkSize; - - const __m256i Zero = _mm256_setzero_si256(); - const __m256i One = _mm256_set1_epi16(127); - constexpr int Control = 0b11011000; - - const __m256i* in0 = reinterpret_cast(&(accumulation[perspectives[p]][0])); - const __m256i* in1 = reinterpret_cast(&(accumulation[perspectives[p]][HalfDimensions / 2])); - __m256i* out = reinterpret_cast< __m256i*>(output + offset); - - for (IndexType j = 0; j < NumOutputChunks; j += 1) - { - const __m256i sum0a = _mm256_max_epi16(_mm256_min_epi16(in0[j * 2 + 0], One), Zero); - const __m256i sum0b = _mm256_max_epi16(_mm256_min_epi16(in0[j * 2 + 1], One), Zero); - const __m256i sum1a = _mm256_max_epi16(_mm256_min_epi16(in1[j * 2 + 0], One), Zero); - const __m256i sum1b = _mm256_max_epi16(_mm256_min_epi16(in1[j * 2 + 1], One), Zero); - - const __m256i pa = _mm256_srli_epi16(_mm256_mullo_epi16(sum0a, sum1a), 7); - const __m256i pb = _mm256_srli_epi16(_mm256_mullo_epi16(sum0b, sum1b), 7); - - out[j] = _mm256_permute4x64_epi64(_mm256_packs_epi16(pa, pb), Control); - } - -#elif defined(USE_SSE2) - - constexpr IndexType OutputChunkSize = 128 / 8; - static_assert((HalfDimensions / 2) % OutputChunkSize == 0); - constexpr IndexType NumOutputChunks = HalfDimensions / 2 / OutputChunkSize; - - const __m128i Zero = _mm_setzero_si128(); - const __m128i One = _mm_set1_epi16(127); - - const __m128i* in0 = reinterpret_cast(&(accumulation[perspectives[p]][0])); - const __m128i* in1 = reinterpret_cast(&(accumulation[perspectives[p]][HalfDimensions / 2])); - __m128i* out = reinterpret_cast< __m128i*>(output + offset); - - for (IndexType j = 0; j < NumOutputChunks; j += 1) - { - const __m128i sum0a = _mm_max_epi16(_mm_min_epi16(in0[j * 2 + 0], One), Zero); - const __m128i sum0b = _mm_max_epi16(_mm_min_epi16(in0[j * 2 + 1], One), Zero); - const __m128i sum1a = _mm_max_epi16(_mm_min_epi16(in1[j * 2 + 0], One), Zero); - const __m128i sum1b = _mm_max_epi16(_mm_min_epi16(in1[j * 2 + 1], One), Zero); - - const __m128i pa = _mm_srli_epi16(_mm_mullo_epi16(sum0a, sum1a), 7); - const __m128i pb = _mm_srli_epi16(_mm_mullo_epi16(sum0b, sum1b), 7); - - out[j] = _mm_packs_epi16(pa, pb); - } - -#elif defined(USE_NEON) - - constexpr IndexType OutputChunkSize = 128 / 8; - static_assert((HalfDimensions / 2) % OutputChunkSize == 0); - constexpr IndexType NumOutputChunks = HalfDimensions / 2 / OutputChunkSize; - - const int16x8_t Zero = vdupq_n_s16(0); - const int16x8_t One = vdupq_n_s16(127); - - const int16x8_t* in0 = reinterpret_cast(&(accumulation[perspectives[p]][0])); - const int16x8_t* in1 = reinterpret_cast(&(accumulation[perspectives[p]][HalfDimensions / 2])); - int8x16_t* out = reinterpret_cast< int8x16_t*>(output + offset); - - for (IndexType j = 0; j < NumOutputChunks; j += 1) - { - const int16x8_t sum0a = vmaxq_s16(vminq_s16(in0[j * 2 + 0], One), Zero); - const int16x8_t sum0b = vmaxq_s16(vminq_s16(in0[j * 2 + 1], One), Zero); - const int16x8_t sum1a = vmaxq_s16(vminq_s16(in1[j * 2 + 0], One), Zero); - const int16x8_t sum1b = vmaxq_s16(vminq_s16(in1[j * 2 + 1], One), Zero); - - const int8x8_t pa = vshrn_n_s16(vmulq_s16(sum0a, sum1a), 7); - const int8x8_t pb = vshrn_n_s16(vmulq_s16(sum0b, sum1b), 7); - - out[j] = vcombine_s8(pa, pb); + out[j] = vec_msb_pack_16(pa, pb); } #else @@ -354,6 +327,10 @@ namespace Stockfish::Eval::NNUE { #endif } +#if defined(vec_cleanup) + vec_cleanup(); +#endif + return psqt; } // end of function transform() From eae0f8dd066b31102b6663a60c36fffccf4e1269 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Tue, 8 Mar 2022 10:56:07 +0300 Subject: [PATCH 0797/1766] Decrease reductions in Lmr for some Pv nodes This patch makes us reduce less in Lmr at pv nodes in case of static eval being far away from static evaluation of position. Idea is that if it's the case then probably position is pretty complex so we can't be sure about how reliable LMR is so we need to reduce less. Passed STC: https://tests.stockfishchess.org/tests/view/6226276aa9d47c8160e81220 LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 262696 W: 69944 L: 69239 D: 123513 Ptnml(0-2): 1399, 29702, 68436, 30417, 1394 Passed LTC: https://tests.stockfishchess.org/tests/view/6226b002a9d47c8160e82b91 LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 64008 W: 17320 L: 16982 D: 29706 Ptnml(0-2): 60, 6378, 18811, 6674, 81 closes https://github.com/official-stockfish/Stockfish/pull/3957 bench 6678390 --- src/search.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index a6552daf2a0..3008079e9c4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1166,6 +1166,11 @@ namespace { if (ttCapture) r++; + // Decrease reduction at PvNodes if bestvalue + // is vastly different from static evaluation + if (PvNode && !ss->inCheck && abs(ss->staticEval - bestValue) > 250) + r--; + ss->statScore = thisThread->mainHistory[us][from_to(move)] + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] From 45f2416db4eaba8fbf023f84609d68a60e94b4ff Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Wed, 9 Mar 2022 18:40:54 +0300 Subject: [PATCH 0798/1766] Improvements in Evaluation adjust parameters in classical evaluation and NNUE scaling. STC: LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 37104 W: 9983 L: 9701 D: 17420 Ptnml(0-2): 154, 4187, 9651, 4343, 217 https://tests.stockfishchess.org/tests/view/6228cb13a9d47c8160e885ba LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 266792 W: 71101 L: 70295 D: 125396 Ptnml(0-2): 214, 26928, 78353, 27640, 261 https://tests.stockfishchess.org/tests/view/6228d3c4a9d47c8160e887b0 closes https://github.com/official-stockfish/Stockfish/pull/3958 Bench: 6739741 --- src/evaluate.cpp | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 923564cbfe8..e9376aa670c 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -229,58 +229,58 @@ namespace { // BishopPawns[distance from edge] contains a file-dependent penalty for pawns on // squares of the same color as our bishop. constexpr Score BishopPawns[int(FILE_NB) / 2] = { - S(3, 8), S(3, 9), S(2, 8), S(3, 8) + S(3, 8), S(3, 9), S(2, 7), S(3, 7) }; // KingProtector[knight/bishop] contains penalty for each distance unit to own king - constexpr Score KingProtector[] = { S(8, 9), S(6, 9) }; + constexpr Score KingProtector[] = { S(9, 9), S(7, 9) }; // Outpost[knight/bishop] contains bonuses for each knight or bishop occupying a // pawn protected square on rank 4 to 6 which is also safe from a pawn attack. - constexpr Score Outpost[] = { S(57, 38), S(31, 24) }; + constexpr Score Outpost[] = { S(54, 34), S(31, 25) }; // PassedRank[Rank] contains a bonus according to the rank of a passed pawn constexpr Score PassedRank[RANK_NB] = { - S(0, 0), S(7, 27), S(16, 32), S(17, 40), S(64, 71), S(170, 174), S(278, 262) + S(0, 0), S(2, 38), S(15, 36), S(22, 50), S(64, 81), S(166, 184), S(284, 269) }; constexpr Score RookOnClosedFile = S(10, 5); - constexpr Score RookOnOpenFile[] = { S(19, 6), S(47, 26) }; + constexpr Score RookOnOpenFile[] = { S(18, 8), S(49, 26) }; // 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(5, 32), S(55, 41), S(77, 56), S(89, 119), S(79, 162) + S(0, 0), S(6, 37), S(64, 50), S(82, 57), S(103, 130), S(81, 163) }; constexpr Score ThreatByRook[PIECE_TYPE_NB] = { - S(0, 0), S(3, 44), S(37, 68), S(42, 60), S(0, 39), S(58, 43) + S(0, 0), S(3, 44), S(36, 71), S(44, 59), S(0, 39), S(60, 39) }; constexpr Value CorneredBishop = Value(50); // Assorted bonuses and penalties - constexpr Score UncontestedOutpost = S( 1, 10); + constexpr Score UncontestedOutpost = S( 0, 10); constexpr Score BishopOnKingRing = S( 24, 0); constexpr Score BishopXRayPawns = S( 4, 5); constexpr Score FlankAttacks = S( 8, 0); - constexpr Score Hanging = S( 69, 36); + constexpr Score Hanging = S( 72, 40); constexpr Score KnightOnQueen = S( 16, 11); constexpr Score LongDiagonalBishop = S( 45, 0); constexpr Score MinorBehindPawn = S( 18, 3); - constexpr Score PassedFile = S( 11, 8); - constexpr Score PawnlessFlank = S( 17, 95); - constexpr Score ReachableOutpost = S( 31, 22); - constexpr Score RestrictedPiece = S( 7, 7); + constexpr Score PassedFile = S( 13, 8); + constexpr Score PawnlessFlank = S( 19, 97); + constexpr Score ReachableOutpost = S( 33, 19); + constexpr Score RestrictedPiece = S( 6, 7); constexpr Score RookOnKingRing = S( 16, 0); - constexpr Score SliderOnQueen = S( 60, 18); - constexpr Score ThreatByKing = S( 24, 89); + constexpr Score SliderOnQueen = S( 62, 21); + constexpr Score ThreatByKing = S( 24, 87); constexpr Score ThreatByPawnPush = S( 48, 39); - constexpr Score ThreatBySafePawn = S(173, 94); + constexpr Score ThreatBySafePawn = S(167, 99); constexpr Score TrappedRook = S( 55, 13); constexpr Score WeakQueenProtection = S( 14, 0); - constexpr Score WeakQueen = S( 56, 15); + constexpr Score WeakQueen = S( 57, 19); #undef S @@ -1088,23 +1088,23 @@ Value Eval::evaluate(const Position& pos) { // Deciding between classical and NNUE eval (~10 Elo): for high PSQ imbalance we use classical, // but we switch to NNUE during long shuffling or with high material on the board. if ( !useNNUE - || abs(eg_value(pos.psq_score())) * 5 > (849 + pos.non_pawn_material() / 64) * (5 + pos.rule50_count())) + || abs(eg_value(pos.psq_score())) * 5 > (856 + pos.non_pawn_material() / 64) * (5 + pos.rule50_count())) { v = Evaluation(pos).value(); // classical - useClassical = abs(v) >= 298; + useClassical = abs(v) >= 297; } // If result of a classical evaluation is much lower than threshold fall back to NNUE if (useNNUE && !useClassical) { Value nnue = NNUE::evaluate(pos, true); // NNUE - int scale = 1136 + 20 * pos.non_pawn_material() / 1024; + int scale = 1036 + 20 * pos.non_pawn_material() / 1024; Color stm = pos.side_to_move(); Value optimism = pos.this_thread()->optimism[stm]; Value psq = (stm == WHITE ? 1 : -1) * eg_value(pos.psq_score()); int complexity = 35 * abs(nnue - psq) / 256; - optimism = optimism * (44 + complexity) / 32; + optimism = optimism * (44 + complexity) / 31; v = (nnue + optimism) * scale / 1024 - optimism; if (pos.is_chess960()) @@ -1112,7 +1112,7 @@ Value Eval::evaluate(const Position& pos) { } // Damp down the evaluation linearly when shuffling - v = v * (208 - pos.rule50_count()) / 208; + v = v * (207 - pos.rule50_count()) / 207; // Guarantee evaluation does not hit the tablebase range v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); From 004ea2c25ebe2b900152d0d837089d03f20c8eca Mon Sep 17 00:00:00 2001 From: Giacomo Lorenzetti Date: Sat, 5 Feb 2022 10:18:50 +0100 Subject: [PATCH 0799/1766] Small cleanups Delete cast to int in movepick. update AUTHORS. adjust assert in sigmoid. fix spelling mistakes in README closes https://github.com/official-stockfish/Stockfish/pull/3922 closes https://github.com/official-stockfish/Stockfish/pull/3948 closes https://github.com/official-stockfish/Stockfish/pull/3942 No functional change --- AUTHORS | 1 + README.md | 19 +++++++++---------- src/misc.h | 2 +- src/movepick.cpp | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/AUTHORS b/AUTHORS index 65620886dcd..34b95ba5380 100644 --- a/AUTHORS +++ b/AUTHORS @@ -155,6 +155,7 @@ Pascal Romaret Pasquale Pigazzini (ppigazzini) Patrick Jansen (mibere) pellanda +Peter Schneider (pschneider1968) Peter Zsifkovits (CoffeeOne) Praveen Kumar Tummala (praveentml) Rahul Dsilva (silversolver1) diff --git a/README.md b/README.md index 330d19edd1f..37dae511f07 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,11 @@ Cute Chess, eboard, Arena, Sigma Chess, Shredder, Chess Partner or Fritz) in ord to be used comfortably. Read the documentation for your GUI of choice for information about how to use Stockfish with it. -The Stockfish engine features two evaluation functions for chess, the classical -evaluation based on handcrafted terms, and the NNUE evaluation based on efficiently -updatable neural networks. The classical evaluation runs efficiently on almost all -CPU architectures, while the NNUE evaluation benefits from the vector -intrinsics available on most CPUs (sse2, avx2, neon, or similar). - +The Stockfish engine features two evaluation functions for chess. +The efficiently updatable neural network (NNUE) based evaluation is the default and by far the strongest. +The classical evaluation based on handcrafted terms remains available. +The strongest network is integrated in the binary and downloaded automatically during the build process. +The NNUE evaluation benefits from the vector intrinsics available on most CPUs (sse2, avx2, neon, or similar). ## Files @@ -37,7 +36,7 @@ This distribution of Stockfish consists of the following files: The Universal Chess Interface (UCI) is a standard protocol used to communicate with a chess engine, and is the recommended way to do so for typical graphical user interfaces -(GUI) or chess tools. Stockfish implements the majority of it options as described +(GUI) or chess tools. Stockfish implements the majority of its options as described in [the UCI protocol](https://www.shredderchess.com/download/div/uci.zip). Developers can see the default values for UCI options available in Stockfish by typing @@ -103,7 +102,7 @@ change them via a chess GUI. This is a list of available UCI options in Stockfis Example: `C:\tablebases\wdl345;C:\tablebases\wdl6;D:\tablebases\dtz345;D:\tablebases\dtz6` It is recommended to store .rtbw files on an SSD. There is no loss in storing - the .rtbz files on a regular HD. It is recommended to verify all md5 checksums + the .rtbz files on a regular HDD. It is recommended to verify all md5 checksums of the downloaded tablebase files (`md5sum -c checksum.md5`) as corruption will lead to engine crashes. @@ -322,10 +321,10 @@ it (either by itself or as part of some bigger software package), or using it as the starting point for a software project of your own. The only real limitation is that whenever you distribute Stockfish in -some way, you MUST always include the full source code, or a pointer +some way, you MUST always include the license, the full source code, or a pointer to where the source code can be found, to generate the exact binary you are distributing. If you make any changes to the source code, -these changes must also be made available under the GPL. +these changes must also be made available under the GPL v3. For full details, read the copy of the GPL v3 found in the file named [*Copying.txt*](https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt). diff --git a/src/misc.h b/src/misc.h index b962673384b..b666b6be45b 100644 --- a/src/misc.h +++ b/src/misc.h @@ -163,7 +163,7 @@ inline int64_t sigmoid(int64_t t, int64_t x0, int64_t P, int64_t Q) { - assert(C > 0); + assert(C > 0 && Q != 0); return y0 + P * (t-x0) / (Q * (std::abs(t-x0) + C)) ; } diff --git a/src/movepick.cpp b/src/movepick.cpp index dc35113f90e..c948620b555 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -107,8 +107,8 @@ void MovePicker::score() { for (auto& m : *this) if constexpr (Type == CAPTURES) - m.value = int(PieceValue[MG][pos.piece_on(to_sq(m))]) * 6 - + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]; + m.value = 6 * PieceValue[MG][pos.piece_on(to_sq(m))] + + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]; else if constexpr (Type == QUIETS) m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] From f3a2296e591d09dd50323fc3f96e800f5538d8bb Mon Sep 17 00:00:00 2001 From: mstembera Date: Sat, 12 Mar 2022 07:00:58 -0800 Subject: [PATCH 0800/1766] Small cleanups (2) - fix a small compile error under MSVC - improve sigmoid comment and assert - fix formatting in README.md closes https://github.com/official-stockfish/Stockfish/pull/3960 No functional change --- README.md | 71 ++++++++++++++++++++++++++---------------------- src/misc.h | 5 ++-- src/movepick.cpp | 4 +-- 3 files changed, 44 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 37dae511f07..6e6e762e63e 100644 --- a/README.md +++ b/README.md @@ -10,23 +10,28 @@ Cute Chess, eboard, Arena, Sigma Chess, Shredder, Chess Partner or Fritz) in ord to be used comfortably. Read the documentation for your GUI of choice for information about how to use Stockfish with it. -The Stockfish engine features two evaluation functions for chess. -The efficiently updatable neural network (NNUE) based evaluation is the default and by far the strongest. -The classical evaluation based on handcrafted terms remains available. -The strongest network is integrated in the binary and downloaded automatically during the build process. -The NNUE evaluation benefits from the vector intrinsics available on most CPUs (sse2, avx2, neon, or similar). +The Stockfish engine features two evaluation functions for chess. The efficiently +updatable neural network (NNUE) based evaluation is the default and by far the strongest. +The classical evaluation based on handcrafted terms remains available. The strongest +network is integrated in the binary and downloaded automatically during the build process. +The NNUE evaluation benefits from the vector intrinsics available on most CPUs (sse2, +avx2, neon, or similar). ## Files This distribution of Stockfish consists of the following files: - * [Readme.md](https://github.com/official-stockfish/Stockfish/blob/master/README.md), the file you are currently reading. + * [Readme.md](https://github.com/official-stockfish/Stockfish/blob/master/README.md), + the file you are currently reading. - * [Copying.txt](https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt), a text file containing the GNU General Public License version 3. + * [Copying.txt](https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt), + a text file containing the GNU General Public License version 3. - * [AUTHORS](https://github.com/official-stockfish/Stockfish/blob/master/AUTHORS), a text file with the list of authors for the project + * [AUTHORS](https://github.com/official-stockfish/Stockfish/blob/master/AUTHORS), + a text file with the list of authors for the project - * [src](https://github.com/official-stockfish/Stockfish/tree/master/src), a subdirectory containing the full source code, including a Makefile + * [src](https://github.com/official-stockfish/Stockfish/tree/master/src), + a subdirectory containing the full source code, including a Makefile that can be used to compile Stockfish on Unix-like systems. * a file with the .nnue extension, storing the neural network for the NNUE @@ -67,9 +72,9 @@ change them via a chess GUI. This is a list of available UCI options in Stockfis * #### EvalFile The name of the file of the NNUE evaluation parameters. Depending on the GUI the - filename might have to include the full path to the folder/directory that contains the file. - Other locations, such as the directory that contains the binary and the working directory, - are also searched. + filename might have to include the full path to the folder/directory that contains + the file. Other locations, such as the directory that contains the binary and the + working directory, are also searched. * #### UCI_AnalyseMode An option handled by your GUI. @@ -137,8 +142,9 @@ change them via a chess GUI. This is a list of available UCI options in Stockfis For developers the following non-standard commands might be of interest, mainly useful for debugging: * #### bench *ttSize threads limit fenFile limitType evalType* - Performs a standard benchmark using various options. The signature of a version (standard node - count) is obtained using all defaults. `bench` is currently `bench 16 1 13 default depth mixed`. + Performs a standard benchmark using various options. The signature of a version + (standard node count) is obtained using all defaults. `bench` is currently + `bench 16 1 13 default depth mixed`. * #### compiler Give information about the compiler and environment used for building a binary. @@ -174,26 +180,27 @@ on the evaluations of millions of positions at moderate search depth. The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward. It can be evaluated efficiently on CPUs, and exploits the fact that only parts of the neural network need to be updated after a typical chess move. -[The nodchip repository](https://github.com/nodchip/Stockfish) provided the first version of -the needed tools to train and develop the NNUE networks. Today, more advanced training tools are available -in [the nnue-pytorch repository](https://github.com/glinscott/nnue-pytorch/), while data generation tools -are available in [a dedicated branch](https://github.com/official-stockfish/Stockfish/tree/tools). +[The nodchip repository](https://github.com/nodchip/Stockfish) provided the first +version of the needed tools to train and develop the NNUE networks. Today, more +advanced training tools are available in +[the nnue-pytorch repository](https://github.com/glinscott/nnue-pytorch/), +while data generation tools are available in +[a dedicated branch](https://github.com/official-stockfish/Stockfish/tree/tools). -On CPUs supporting modern vector instructions -(avx2 and similar), the NNUE evaluation results in much stronger playing strength, even -if the nodes per second computed by the engine is somewhat lower (roughly 80% of nps -is typical). +On CPUs supporting modern vector instructions (avx2 and similar), the NNUE evaluation +results in much stronger playing strength, even if the nodes per second computed by +the engine is somewhat lower (roughly 80% of nps is typical). Notes: -1) the NNUE evaluation depends on the Stockfish binary and the network parameter -file (see the EvalFile UCI option). Not every parameter file is compatible with a given -Stockfish binary, but the default value of the EvalFile UCI option is the name of a network -that is guaranteed to be compatible with that binary. +1) the NNUE evaluation depends on the Stockfish binary and the network parameter file +(see the EvalFile UCI option). Not every parameter file is compatible with a given +Stockfish binary, but the default value of the EvalFile UCI option is the name of a +network that is guaranteed to be compatible with that binary. 2) to use the NNUE evaluation, the additional data file with neural network parameters -needs to be available. Normally, this file is already embedded in the binary or it -can be downloaded. The filename for the default (recommended) net can be found as the default +needs to be available. Normally, this file is already embedded in the binary or it can +be downloaded. The filename for the default (recommended) net can be found as the default value of the `EvalFile` UCI option, with the format `nn-[SHA256 first 12 digits].nnue` (for instance, `nn-c157e0a5755b.nnue`). This file can be downloaded from ``` @@ -321,10 +328,10 @@ it (either by itself or as part of some bigger software package), or using it as the starting point for a software project of your own. The only real limitation is that whenever you distribute Stockfish in -some way, you MUST always include the license, the full source code, or a pointer -to where the source code can be found, to generate the exact binary -you are distributing. If you make any changes to the source code, -these changes must also be made available under the GPL v3. +some way, you MUST always include the license and the full source code +(or a pointer to where the source code can be found) to generate the +exact binary you are distributing. If you make any changes to the +source code, these changes must also be made available under the GPL v3. For full details, read the copy of the GPL v3 found in the file named [*Copying.txt*](https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt). diff --git a/src/misc.h b/src/misc.h index b666b6be45b..dcef22a411c 100644 --- a/src/misc.h +++ b/src/misc.h @@ -152,7 +152,7 @@ class ValueList { /// - the slope can be adjusted using C > 0, smaller C giving a steeper sigmoid /// - the slope of the sigmoid when t = x0 is P/(Q*C) /// - sigmoid is increasing with t when P > 0 and Q > 0 -/// - to get a decreasing sigmoid, call with -t, or change sign of P +/// - to get a decreasing sigmoid, change sign of P /// - mean value of the sigmoid is y0 /// /// Use to draw the sigmoid @@ -163,7 +163,8 @@ inline int64_t sigmoid(int64_t t, int64_t x0, int64_t P, int64_t Q) { - assert(C > 0 && Q != 0); + assert(C > 0); + assert(Q != 0); return y0 + P * (t-x0) / (Q * (std::abs(t-x0) + C)) ; } diff --git a/src/movepick.cpp b/src/movepick.cpp index c948620b555..77453a45aed 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -107,8 +107,8 @@ void MovePicker::score() { for (auto& m : *this) if constexpr (Type == CAPTURES) - m.value = 6 * PieceValue[MG][pos.piece_on(to_sq(m))] - + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]; + m.value = 6 * int(PieceValue[MG][pos.piece_on(to_sq(m))]) + + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]; else if constexpr (Type == QUIETS) m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] From e31f97e3baa52042fe60d6f4eeb50fe0d9e61013 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Tue, 15 Mar 2022 16:53:59 +0100 Subject: [PATCH 0801/1766] Remove ttPv tree shrinking. Via the ttPv flag an implicit tree of current and former PV nodes is maintained. In addition this tree is grown or shrinked at the leafs dependant on the search results. But now the shrinking step has been removed. As the frequency of ttPv nodes decreases with depth the shown scaling behavior (STC barely passed but LTC scales well) of the tests was expected. STC: LLR: 2.93 (-2.94,2.94) <-2.25,0.25> Total: 270408 W: 71593 L: 71785 D: 127030 Ptnml(0-2): 1339, 31024, 70630, 30912, 1299 https://tests.stockfishchess.org/tests/view/622fbf9dc9e950cbfc2376d6 LTC: LLR: 2.96 (-2.94,2.94) <-2.25,0.25> Total: 34368 W: 9135 L: 8992 D: 16241 Ptnml(0-2): 28, 3423, 10135, 3574, 24 https://tests.stockfishchess.org/tests/view/62305257c9e950cbfc238964 closes https://github.com/official-stockfish/Stockfish/pull/3963 Bench: 7044203 --- src/search.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 3008079e9c4..672abc05b27 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1359,10 +1359,6 @@ namespace { // opponent move is probably good and the new position is added to the search tree. if (bestValue <= alpha) ss->ttPv = ss->ttPv || ((ss-1)->ttPv && depth > 3); - // Otherwise, a counter move has been found and if the position is the last leaf - // in the search tree, remove the position from the search tree. - else if (depth > 3) - ss->ttPv = ss->ttPv && (ss+1)->ttPv; // Write gathered information in transposition table if (!excludedMove && !(rootNode && thisThread->pvIdx)) From 910cf8b21839eb9f1991934a5436eea112021723 Mon Sep 17 00:00:00 2001 From: Giacomo Lorenzetti Date: Wed, 23 Mar 2022 12:04:10 +0100 Subject: [PATCH 0802/1766] Remove pos.capture_or_promotion() This patch replaces `pos.capture_or_promotion()` with `pos.capture()` and comes after a few attempts with elo-gaining bounds, two of which failed yellow at LTC (https://tests.stockfishchess.org/tests/view/622f8f0cc9e950cbfc237024 and https://tests.stockfishchess.org/tests/view/62319a8bb3b498ba71a6b2dc). Passed non-regression STC: https://tests.stockfishchess.org/tests/view/623aff7eea447151c74828d3 LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 246864 W: 65462 L: 65618 D: 115784 Ptnml(0-2): 1201, 28116, 65001, 27866, 1248 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/623c1fdcea447151c7484fb0 LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 30120 W: 8125 L: 7978 D: 14017 Ptnml(0-2): 22, 2993, 8881, 3144, 20 closes https://github.com/official-stockfish/Stockfish/pull/3968 Bench: 6847732 --- src/position.h | 5 ----- src/search.cpp | 35 ++++++++++++++++++----------------- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/position.h b/src/position.h index 8dbf1493c28..7b6165f37ac 100644 --- a/src/position.h +++ b/src/position.h @@ -352,11 +352,6 @@ inline bool Position::is_chess960() const { return chess960; } -inline bool Position::capture_or_promotion(Move m) const { - assert(is_ok(m)); - return type_of(m) != NORMAL ? type_of(m) != CASTLING : !empty(to_sq(m)); -} - inline bool Position::capture(Move m) const { assert(is_ok(m)); // Castling is encoded as "king captures rook" diff --git a/src/search.cpp b/src/search.cpp index 672abc05b27..2d24c31372b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -554,7 +554,7 @@ namespace { Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue, probCutBeta; bool givesCheck, improving, didLMR, priorCapture; - bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture; + bool capture, doFullDepthSearch, moveCountPruning, ttCapture; Piece movedPiece; int moveCount, captureCount, quietCount, bestMoveCount, improvement, complexity; @@ -624,7 +624,7 @@ namespace { ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] : ss->ttHit ? tte->move() : MOVE_NONE; - ttCapture = ttMove && pos.capture_or_promotion(ttMove); + ttCapture = ttMove && pos.capture(ttMove); if (!excludedMove) ss->ttPv = PvNode || (ss->ttHit && tte->is_pv()); @@ -865,12 +865,13 @@ namespace { MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, depth - 3, &captureHistory); bool ttPv = ss->ttPv; + bool captureOrPromotion; ss->ttPv = false; while ((move = mp.next_move()) != MOVE_NONE) if (move != excludedMove && pos.legal(move)) { - assert(pos.capture_or_promotion(move)); + assert(pos.capture(move) || promotion_type(move) == QUEEN); captureOrPromotion = true; @@ -987,7 +988,7 @@ namespace { (ss+1)->pv = nullptr; extension = 0; - captureOrPromotion = pos.capture_or_promotion(move); + capture = pos.capture(move); movedPiece = pos.moved_piece(move); givesCheck = pos.gives_check(move); @@ -1007,7 +1008,7 @@ namespace { // Reduced depth of the next LMR search int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount, delta, thisThread->rootDelta), 0); - if ( captureOrPromotion + if ( capture || givesCheck) { // Futility pruning for captures (~0 Elo) @@ -1122,7 +1123,7 @@ namespace { // Update the current move (this must be done after singular extension search) ss->currentMove = move; ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck] - [captureOrPromotion] + [capture] [movedPiece] [to_sq(move)]; @@ -1138,7 +1139,7 @@ namespace { if ( depth >= 2 && moveCount > 1 + (PvNode && ss->ply <= 1) && ( !ss->ttPv - || !captureOrPromotion + || !capture || (cutNode && (ss-1)->moveCount > 1))) { Depth r = reduction(improving, depth, moveCount, delta, thisThread->rootDelta); @@ -1215,7 +1216,7 @@ namespace { int bonus = value > alpha ? stat_bonus(newDepth) : -stat_bonus(newDepth); - if (captureOrPromotion) + if (capture) bonus /= 6; update_continuation_histories(ss, movedPiece, to_sq(move), bonus); @@ -1306,10 +1307,10 @@ namespace { // If the move is worse than some previously searched move, remember it to update its stats later if (move != bestMove) { - if (captureOrPromotion && captureCount < 32) + if (capture && captureCount < 32) capturesSearched[captureCount++] = move; - else if (!captureOrPromotion && quietCount < 64) + else if (!capture && quietCount < 64) quietsSearched[quietCount++] = move; } } @@ -1394,7 +1395,7 @@ namespace { Move ttMove, move, bestMove; Depth ttDepth; Value bestValue, value, ttValue, futilityValue, futilityBase; - bool pvHit, givesCheck, captureOrPromotion; + bool pvHit, givesCheck, capture; int moveCount; if (PvNode) @@ -1503,7 +1504,7 @@ namespace { continue; givesCheck = pos.gives_check(move); - captureOrPromotion = pos.capture_or_promotion(move); + capture = pos.capture(move); moveCount++; @@ -1543,12 +1544,12 @@ namespace { ss->currentMove = move; ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck] - [captureOrPromotion] + [capture] [pos.moved_piece(move)] [to_sq(move)]; // Continuation history based pruning (~2 Elo) - if ( !captureOrPromotion + if ( !capture && bestValue > VALUE_TB_LOSS_IN_MAX_PLY && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < CounterMovePruneThreshold && (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < CounterMovePruneThreshold) @@ -1557,11 +1558,11 @@ namespace { // movecount pruning for quiet check evasions if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY && quietCheckEvasions > 1 - && !captureOrPromotion + && !capture && ss->inCheck) continue; - quietCheckEvasions += !captureOrPromotion && ss->inCheck; + quietCheckEvasions += !capture && ss->inCheck; // Make and search the move pos.do_move(move, st, givesCheck); @@ -1680,7 +1681,7 @@ namespace { bonus2 = bestValue > beta + PawnValueMg ? bonus1 // larger bonus : stat_bonus(depth); // smaller bonus - if (!pos.capture_or_promotion(bestMove)) + if (!pos.capture(bestMove)) { // Increase stats for the best move in case it was a quiet move update_quiet_stats(pos, ss, bestMove, bonus2); From 08e0f52b77edb929989c68c49e954b9bc5d7d67e Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Mon, 28 Mar 2022 14:15:56 +0300 Subject: [PATCH 0803/1766] In movepicker increase priority for moves that evade a capture This idea is a mix of koivisto idea of threat history and heuristic that was simplified some time ago in LMR - decreasing reduction for moves that evade a capture. Instead of doing so in LMR this patch does it in movepicker - to do this it calculates squares that are attacked by different piece types and pieces that are located on this squares and boosts up weight of moves that make this pieces land on a square that is not under threat. Boost is greater for pieces with bigger material values. Special thanks to koivisto and seer authors for explaining me ideas behind threat history. Passed STC: https://tests.stockfishchess.org/tests/view/62406e473b32264b9aa1478b LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 19816 W: 5320 L: 5072 D: 9424 Ptnml(0-2): 86, 2165, 5172, 2385, 100 Passed LTC: https://tests.stockfishchess.org/tests/view/62407f2e3b32264b9aa149c8 LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 51200 W: 13805 L: 13500 D: 23895 Ptnml(0-2): 44, 5023, 15164, 5322, 47 closes https://github.com/official-stockfish/Stockfish/pull/3970 bench 7736491 --- src/movepick.cpp | 75 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 77453a45aed..ce82a59ba3d 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -97,6 +97,44 @@ MovePicker::MovePicker(const Position& p, Move ttm, Value th, Depth d, const Cap && pos.see_ge(ttm, threshold)); } +//squares threatened by pawn attacks +template +Bitboard threatsByPawn (const Position& pos) +{ + return pawn_attacks_bb(pos.pieces(Us, PAWN)); +} + +//squares threatened by minor attacks +template +Bitboard threatsByMinor (const Position& pos) +{ + Bitboard our = pos.pieces(Us, KNIGHT, BISHOP); + Bitboard threats = 0; + while (our) + { + Square s = pop_lsb(our); + if (type_of(pos.piece_on(s)) == KNIGHT) + threats |= attacks_bb(s, pos.pieces()); + else + threats |= attacks_bb(s, pos.pieces()); + } + return threats; +} + +//squares threatened by rook attacks +template +Bitboard threatsByRook (const Position& pos) +{ + Bitboard our = pos.pieces(Us, ROOK); + Bitboard threats = 0; + while (our) + { + Square s = pop_lsb(our); + threats |= attacks_bb(s, pos.pieces()); + } + return threats; +} + /// MovePicker::score() assigns a numerical value to each move in a list, used /// for sorting. Captures are ordered by Most Valuable Victim (MVV), preferring /// captures with a good history. Quiets moves are ordered using the histories. @@ -105,6 +143,35 @@ void MovePicker::score() { static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type"); + Bitboard threatened, threatenedByPawn, threatenedByMinor, threatenedByRook; + if constexpr (Type == QUIETS) + { + // squares threatened by pawns + threatenedByPawn = pos.side_to_move() == WHITE ? threatsByPawn(pos) : threatsByPawn(pos); + // squares threatened by minors or pawns + threatenedByMinor = pos.side_to_move() == WHITE ? threatsByMinor(pos) : threatsByMinor(pos); + threatenedByMinor |= threatenedByPawn; + // squares threatened by rooks, minors or pawns + threatenedByRook = pos.side_to_move() == WHITE ? threatsByRook(pos) : threatsByRook(pos); + threatenedByRook |= threatenedByMinor; + + // pieces threatened by pieces of lesser material value + threatened = pos.side_to_move() == WHITE ? ((pos.pieces(WHITE, QUEEN) & threatenedByRook) | + (pos.pieces(WHITE, ROOK) & threatenedByMinor) | + (pos.pieces(WHITE, KNIGHT, BISHOP) & threatenedByPawn)) + : ((pos.pieces(BLACK, QUEEN) & threatenedByRook) | + (pos.pieces(BLACK, ROOK) & threatenedByMinor) | + (pos.pieces(BLACK, KNIGHT, BISHOP) & threatenedByPawn)); + } + else + { + // Silence unused variable warning + (void) threatened; + (void) threatenedByPawn; + (void) threatenedByMinor; + (void) threatenedByRook; + } + for (auto& m : *this) if constexpr (Type == CAPTURES) m.value = 6 * int(PieceValue[MG][pos.piece_on(to_sq(m))]) @@ -115,7 +182,13 @@ void MovePicker::score() { + 2 * (*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)]; + + (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)] + + (threatened & from_sq(m) ? + (type_of(pos.piece_on(from_sq(m))) == QUEEN && !(to_sq(m) & threatenedByRook) ? 50000 + : type_of(pos.piece_on(from_sq(m))) == ROOK && !(to_sq(m) & threatenedByMinor) ? 25000 + : !(to_sq(m) & threatenedByPawn) ? 15000 + : 0) + : 0); else // Type == EVASIONS { From 471d93063a8fc1803a4a34397fe39e2344a05d76 Mon Sep 17 00:00:00 2001 From: Topologist Date: Mon, 28 Mar 2022 11:50:08 +0200 Subject: [PATCH 0804/1766] Play more positional in endgames This patch chooses the delta value (which skews the nnue evaluation between positional and materialistic) depending on the material: If the material is low, delta will be higher and the evaluation is shifted to the positional value. If the material is high, the evaluation will be shifted to the psqt value. I don't think slightly negative values of delta should be a concern. Passed STC: https://tests.stockfishchess.org/tests/view/62418513b3b383e86185766f LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 28808 W: 7832 L: 7564 D: 13412 Ptnml(0-2): 147, 3186, 7505, 3384, 182 Passed LTC: https://tests.stockfishchess.org/tests/view/62419137b3b383e861857842 LLR: 2.96 (-2.94,2.94) <0.50,3.00> Total: 58632 W: 15776 L: 15450 D: 27406 Ptnml(0-2): 42, 5889, 17149, 6173, 63 closes https://github.com/official-stockfish/Stockfish/pull/3971 Bench: 7588855 --- src/nnue/evaluate_nnue.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 9254e36f9ba..9ee599f4aa4 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -143,7 +143,7 @@ namespace Stockfish::Eval::NNUE { // overaligning stack variables with alignas() doesn't work correctly. constexpr uint64_t alignment = CacheLineSize; - int delta = 7; + int delta = 10 - pos.non_pawn_material() / 1515; #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) TransformedFeatureType transformedFeaturesUnaligned[ From 9f6bcb38c032a18c8b1aec318d5c7255827f9c7b Mon Sep 17 00:00:00 2001 From: mstembera Date: Wed, 30 Mar 2022 18:14:27 -0700 Subject: [PATCH 0805/1766] Minor cleanups simplify and relocate to position.cpp some of the recent threat calculations used in the movepicker. passed STC: https://tests.stockfishchess.org/tests/view/62468c301f682ea45ce3b3b9 LLR: 2.96 (-2.94,2.94) <-2.25,0.25> Total: 76544 W: 20247 L: 20152 D: 36145 Ptnml(0-2): 327, 8113, 21317, 8168, 347 closes https://github.com/official-stockfish/Stockfish/pull/3972 No functional change --- src/misc.h | 9 +++---- src/movepick.cpp | 69 ++++++++++-------------------------------------- src/position.h | 18 ++++++++++++- 3 files changed, 34 insertions(+), 62 deletions(-) diff --git a/src/misc.h b/src/misc.h index dcef22a411c..2fd2b408a12 100644 --- a/src/misc.h +++ b/src/misc.h @@ -90,9 +90,6 @@ static inline const bool IsLittleEndian = (Le.c[0] == 4); class RunningAverage { public: - // Constructor - RunningAverage() {} - // Reset the running average to rational value p / q void set(int64_t p, int64_t q) { average = p * PERIOD * RESOLUTION / q; } @@ -102,10 +99,10 @@ class RunningAverage { { average = RESOLUTION * v + (PERIOD - 1) * average / PERIOD; } // Test if average is strictly greater than rational a / b - bool is_greater(int64_t a, int64_t b) - { return b * average > a * PERIOD * RESOLUTION ; } + bool is_greater(int64_t a, int64_t b) const + { return b * average > a * (PERIOD * RESOLUTION); } - int64_t value() + int64_t value() const { return average / (PERIOD * RESOLUTION); } private : diff --git a/src/movepick.cpp b/src/movepick.cpp index ce82a59ba3d..b0166c6e962 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -18,6 +18,7 @@ #include +#include "bitboard.h" #include "movepick.h" namespace Stockfish { @@ -97,44 +98,6 @@ MovePicker::MovePicker(const Position& p, Move ttm, Value th, Depth d, const Cap && pos.see_ge(ttm, threshold)); } -//squares threatened by pawn attacks -template -Bitboard threatsByPawn (const Position& pos) -{ - return pawn_attacks_bb(pos.pieces(Us, PAWN)); -} - -//squares threatened by minor attacks -template -Bitboard threatsByMinor (const Position& pos) -{ - Bitboard our = pos.pieces(Us, KNIGHT, BISHOP); - Bitboard threats = 0; - while (our) - { - Square s = pop_lsb(our); - if (type_of(pos.piece_on(s)) == KNIGHT) - threats |= attacks_bb(s, pos.pieces()); - else - threats |= attacks_bb(s, pos.pieces()); - } - return threats; -} - -//squares threatened by rook attacks -template -Bitboard threatsByRook (const Position& pos) -{ - Bitboard our = pos.pieces(Us, ROOK); - Bitboard threats = 0; - while (our) - { - Square s = pop_lsb(our); - threats |= attacks_bb(s, pos.pieces()); - } - return threats; -} - /// MovePicker::score() assigns a numerical value to each move in a list, used /// for sorting. Captures are ordered by Most Valuable Victim (MVV), preferring /// captures with a good history. Quiets moves are ordered using the histories. @@ -146,26 +109,22 @@ void MovePicker::score() { Bitboard threatened, threatenedByPawn, threatenedByMinor, threatenedByRook; if constexpr (Type == QUIETS) { + Color us = pos.side_to_move(); // squares threatened by pawns - threatenedByPawn = pos.side_to_move() == WHITE ? threatsByPawn(pos) : threatsByPawn(pos); + threatenedByPawn = pos.attacks_by(~us); // squares threatened by minors or pawns - threatenedByMinor = pos.side_to_move() == WHITE ? threatsByMinor(pos) : threatsByMinor(pos); - threatenedByMinor |= threatenedByPawn; + threatenedByMinor = pos.attacks_by(~us) | pos.attacks_by(~us) | threatenedByPawn; // squares threatened by rooks, minors or pawns - threatenedByRook = pos.side_to_move() == WHITE ? threatsByRook(pos) : threatsByRook(pos); - threatenedByRook |= threatenedByMinor; + threatenedByRook = pos.attacks_by(~us) | threatenedByMinor; // pieces threatened by pieces of lesser material value - threatened = pos.side_to_move() == WHITE ? ((pos.pieces(WHITE, QUEEN) & threatenedByRook) | - (pos.pieces(WHITE, ROOK) & threatenedByMinor) | - (pos.pieces(WHITE, KNIGHT, BISHOP) & threatenedByPawn)) - : ((pos.pieces(BLACK, QUEEN) & threatenedByRook) | - (pos.pieces(BLACK, ROOK) & threatenedByMinor) | - (pos.pieces(BLACK, KNIGHT, BISHOP) & threatenedByPawn)); + threatened = (pos.pieces(us, QUEEN) & threatenedByRook) + | (pos.pieces(us, ROOK) & threatenedByMinor) + | (pos.pieces(us, KNIGHT, BISHOP) & threatenedByPawn); } else { - // Silence unused variable warning + // Silence unused variable warnings (void) threatened; (void) threatenedByPawn; (void) threatenedByMinor; @@ -184,11 +143,11 @@ void MovePicker::score() { + (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)] + (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)] + (threatened & from_sq(m) ? - (type_of(pos.piece_on(from_sq(m))) == QUEEN && !(to_sq(m) & threatenedByRook) ? 50000 - : type_of(pos.piece_on(from_sq(m))) == ROOK && !(to_sq(m) & threatenedByMinor) ? 25000 - : !(to_sq(m) & threatenedByPawn) ? 15000 - : 0) - : 0); + (type_of(pos.moved_piece(m)) == QUEEN && !(to_sq(m) & threatenedByRook) ? 50000 + : type_of(pos.moved_piece(m)) == ROOK && !(to_sq(m) & threatenedByMinor) ? 25000 + : !(to_sq(m) & threatenedByPawn) ? 15000 + : 0) + : 0); else // Type == EVASIONS { diff --git a/src/position.h b/src/position.h index 7b6165f37ac..e558581850f 100644 --- a/src/position.h +++ b/src/position.h @@ -120,12 +120,12 @@ class Position { Bitboard attackers_to(Square s) const; Bitboard attackers_to(Square s, Bitboard occupied) const; Bitboard slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const; + template Bitboard attacks_by(Color c) const; // Properties of moves bool legal(Move m) const; bool pseudo_legal(const Move m) const; bool capture(Move m) const; - bool capture_or_promotion(Move m) const; bool gives_check(Move m) const; Piece moved_piece(Move m) const; Piece captured_piece() const; @@ -285,6 +285,22 @@ inline Bitboard Position::attackers_to(Square s) const { return attackers_to(s, pieces()); } +template +inline Bitboard Position::attacks_by(Color c) const { + + if constexpr (Pt == PAWN) + return c == WHITE ? pawn_attacks_bb(pieces(WHITE, PAWN)) + : pawn_attacks_bb(pieces(BLACK, PAWN)); + else + { + Bitboard threats = 0; + Bitboard attackers = pieces(c, Pt); + while (attackers) + threats |= attacks_bb(pop_lsb(attackers), pieces()); + return threats; + } +} + inline Bitboard Position::checkers() const { return st->checkersBB; } From 19a90b45bceb69aa62b5c85366343a7d1cfc695f Mon Sep 17 00:00:00 2001 From: Topologist Date: Sat, 9 Apr 2022 08:55:40 +0200 Subject: [PATCH 0806/1766] Use NNUE in low piece endgames close to the root. This patch enforces that NNUE evaluation is used for endgame positions at shallow depth (depth <= 9). Classic evaluation will still be used for high imbalance positions when the depth is high or there are many pieces. Passed STC: https://tests.stockfishchess.org/tests/view/624c193b3a8a6ac93892dc27 LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 255840 W: 68024 L: 67362 D: 120454 Ptnml(0-2): 1074, 27089, 70926, 27763, 1068 Passed LTC: https://tests.stockfishchess.org/tests/view/624e8675e9e7821808467f77 LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 67088 W: 17784 L: 17454 D: 31850 Ptnml(0-2): 45, 6209, 20715, 6521, 54 closes https://github.com/official-stockfish/Stockfish/pull/3978 bench: 6602222 --- src/evaluate.cpp | 4 +++- src/search.cpp | 1 + src/thread.h | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index e9376aa670c..d9180f2b4c2 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1088,7 +1088,8 @@ Value Eval::evaluate(const Position& pos) { // Deciding between classical and NNUE eval (~10 Elo): for high PSQ imbalance we use classical, // but we switch to NNUE during long shuffling or with high material on the board. if ( !useNNUE - || abs(eg_value(pos.psq_score())) * 5 > (856 + pos.non_pawn_material() / 64) * (5 + pos.rule50_count())) + || ((pos.this_thread()->depth > 9 || pos.count() > 7) && + abs(eg_value(pos.psq_score())) * 5 > (856 + pos.non_pawn_material() / 64) * (5 + pos.rule50_count()))) { v = Evaluation(pos).value(); // classical useClassical = abs(v) >= 297; @@ -1138,6 +1139,7 @@ std::string Eval::trace(Position& pos) { std::memset(scores, 0, sizeof(scores)); // Reset any global variable used in eval + pos.this_thread()->depth = 0; pos.this_thread()->trend = SCORE_ZERO; pos.this_thread()->bestValue = VALUE_ZERO; pos.this_thread()->optimism[WHITE] = VALUE_ZERO; diff --git a/src/search.cpp b/src/search.cpp index 2d24c31372b..fa73dce5da0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -560,6 +560,7 @@ namespace { // Step 1. Initialize node Thread* thisThread = pos.this_thread(); + thisThread->depth = depth; ss->inCheck = pos.checkers(); priorCapture = pos.captured_piece(); Color us = pos.side_to_move(); diff --git a/src/thread.h b/src/thread.h index 594a8ea29c5..8027855a614 100644 --- a/src/thread.h +++ b/src/thread.h @@ -69,7 +69,7 @@ class Thread { Position rootPos; StateInfo rootState; Search::RootMoves rootMoves; - Depth rootDepth, completedDepth; + Depth rootDepth, completedDepth, depth; Value rootDelta; CounterMoveHistory counterMoves; ButterflyHistory mainHistory; From 319af5cf0a77d1057a69cef0cf8885d06475dece Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 14 Apr 2022 23:00:14 +0200 Subject: [PATCH 0807/1766] Update CPU contributors closes https://github.com/official-stockfish/Stockfish/pull/3979 No functional change --- Top CPU Contributors.txt | 121 ++++++++++++++++++++------------------- 1 file changed, 63 insertions(+), 58 deletions(-) diff --git a/Top CPU Contributors.txt b/Top CPU Contributors.txt index 4bc96cde7aa..76aa01e9c34 100644 --- a/Top CPU Contributors.txt +++ b/Top CPU Contributors.txt @@ -1,59 +1,59 @@ -Contributors to Fishtest with >10,000 CPU hours, as of 2022-02-05. +Contributors to Fishtest with >10,000 CPU hours, as of 2022-04-14. Thank you! Username CPU Hours Games played ------------------------------------------------------------------ -noobpwnftw 30730952 2158431735 -mlang 2729669 187335452 -technologov 1696847 74478658 -dew 1635640 97483012 -grandphish2 1062754 64955639 +noobpwnftw 31714850 2267266129 +mlang 2954099 198421098 +technologov 2324150 102449398 +dew 1670874 99276012 +grandphish2 1134273 68070459 +okrout 901194 77738874 +TueRens 821388 50207666 tvijlbrief 795993 51894442 -okrout 773704 63465204 -TueRens 766198 47770388 +pemo 744463 32486677 +JojoM 724378 43660674 mibere 703840 46867607 -JojoM 703005 42689868 -pemo 634102 29868807 linrock 626939 17408017 -gvreuls 517442 33605006 -cw 503905 33850487 -fastgm 482847 29004732 +gvreuls 534079 34352532 +cw 507221 34006775 +fastgm 489749 29344518 crunchy 427035 27344275 -CSU_Dynasty 415864 28116776 -ctoks 403102 26737127 -oz 357710 26490208 -bcross 331095 23165889 +CSU_Dynasty 424643 28525220 +ctoks 415771 27364603 +oz 369200 27017658 +bcross 342642 23671289 Fisherman 327231 21829379 -velislav 321708 20729264 -leszek 303654 19063973 -Dantist 251015 15843226 -mgrabiak 231973 15162494 +velislav 325670 20911076 +leszek 321295 19874113 +Dantist 274747 16910258 +mgrabiak 237604 15418700 +robal 217959 13840386 glinscott 217799 13780820 -robal 213960 13665726 nordlandia 211692 13484886 -drabel 200914 13755384 +drabel 201967 13798360 bking_US 198894 11876016 -mhoram 180229 11610075 +mhoram 194862 12261809 Thanar 179852 12365359 vdv 175544 9904472 spams 157128 10319326 +rpngn 154081 9652139 marrco 150300 9402229 sqrt2 147963 9724586 -vdbergh 137429 8955089 +vdbergh 137430 8955097 CoffeeOne 137100 5024116 malala 136182 8002293 xoto 133759 9159372 -rpngn 131285 8657757 -davar 122661 7996937 +davar 125240 8117121 dsmith 122059 7570238 amicic 119659 7937885 Data 113305 8220352 BrunoBanani 112960 7436849 CypressChess 108321 7759588 +DesolatedDodo 106811 6776980 MaZePallas 102823 6633619 sterni1971 100532 5880772 sunu 100167 7040199 -DesolatedDodo 99038 6414626 ElbertoOne 99028 7023771 skiminki 98123 6478402 brabos 92118 6186135 @@ -61,39 +61,39 @@ cuistot 90358 5351004 psk 89957 5984901 racerschmacer 85712 6119648 Vizvezdenec 83761 5344740 +zeryl 83680 5250995 sschnee 83003 4840890 0x3C33 82614 5271253 BRAVONE 81239 5054681 nssy 76497 5259388 teddybaer 75125 5407666 +jromang 74796 5175825 Pking_cda 73776 5293873 -zeryl 73335 4774257 -jromang 72192 5057715 +Calis007 72477 4088576 solarlight 70517 5028306 dv8silencer 70287 3883992 Bobo1239 68515 4652287 manap 66273 4121774 +yurikvelo 65716 4457300 tinker 64333 4268790 -yurikvelo 63371 4335060 +Wolfgang 62644 3817410 qurashee 61208 3429862 robnjr 57262 4053117 -Wolfgang 57014 3561352 Freja 56938 3733019 ttruscott 56010 3680085 rkl 55132 4164467 renouve 53811 3501516 +megaman7de 52434 3243016 +MaxKlaxxMiner 51977 3153032 finfish 51360 3370515 eva42 51272 3599691 -Calis007 51182 3131552 eastorwest 51058 3451555 rap 49985 3219146 pb00067 49727 3298270 -Spprtr 48260 3141959 +Spprtr 48920 3161711 bigpen0r 47667 3336927 ronaldjerum 47654 3240695 -MaxKlaxxMiner 47584 2972142 biffhero 46564 3111352 -megaman7de 45992 2952006 Fifis 45843 3088497 VoyagerOne 45476 3452465 speedycpu 43842 3003273 @@ -102,25 +102,27 @@ Antihistamine 41788 2761312 mhunt 41735 2691355 homyur 39893 2850481 gri 39871 2515779 +armo9494 39064 2832326 oryx 38867 2976992 SC 37299 2731694 Garf 37213 2986270 +tolkki963 37059 2154330 csnodgrass 36207 2688994 jmdana 36157 2210661 strelock 34716 2074055 +DMBK 34010 2482916 EthanOConnor 33370 2090311 slakovv 32915 2021889 -armo9494 32129 2551682 -tolkki963 32114 1932256 +gopeto 30993 2028106 manapbk 30987 1810399 -DMBK 30675 2383552 Prcuvu 30377 2170122 anst 30301 2190091 jkiiski 30136 1904470 -gopeto 29886 1979118 hyperbolic.tom 29840 2017394 chuckstablers 29659 2093438 Pyafue 29650 1902349 +ncfish1 29105 1704011 +belzedar94 27935 1789106 OuaisBla 27636 1578800 chriswk 26902 1868317 achambord 26582 1767323 @@ -129,15 +131,14 @@ yorkman 26193 1992080 SFTUser 25182 1675689 nabildanial 24942 1519409 Sharaf_DG 24765 1786697 -ncfish1 24411 1520927 rodneyc 24275 1410450 agg177 23890 1395014 -belzedar94 23707 1593860 JanErik 23408 1703875 Isidor 23388 1680691 Norabor 23339 1602636 -Ente 23093 1642458 +Ente 23270 1651432 cisco2015 22897 1762669 +MarcusTullius 22688 1274821 Zirie 22542 1472937 team-oh 22272 1636708 MazeOfGalious 21978 1629593 @@ -146,17 +147,22 @@ ianh2105 21725 1632562 xor12 21628 1680365 dex 21612 1467203 nesoneg 21494 1463031 +Roady 21323 1433822 sphinx 21211 1384728 +user213718 21196 1397710 +spcc 21065 1311338 jjoshua2 21001 1423089 horst.prack 20878 1465656 -user213718 20783 1379584 0xB00B1ES 20590 1208666 j3corre 20405 941444 +kdave 20364 1389254 Adrian.Schmidt123 20316 1281436 +Ulysses 20217 1351500 +markkulix 19976 1115258 wei 19973 1745989 -Roady 19848 1335928 rstoesser 19569 1293588 eudhan 19274 1283717 +fishtester 18995 1238686 vulcan 18871 1729392 jundery 18445 1115855 iisiraider 18247 1101015 @@ -164,21 +170,19 @@ ville 17883 1384026 chris 17698 1487385 purplefishies 17595 1092533 dju 17353 978595 -kdave 17183 1242754 +Wencey 17125 805964 DragonLord 17014 1162790 thirdlife 16996 447356 -spcc 16932 1130940 -fishtester 16644 1123000 -Ulysses 16490 1184400 IgorLeMasson 16064 1147232 ako027ako 15671 1173203 +AndreasKrug 15550 1194497 Nikolay.IT 15154 1068349 Andrew Grant 15114 895539 +scuzzi 14928 953313 OssumOpossum 14857 1007129 Karby 14808 867120 -AndreasKrug 14608 1152093 +jsys14 14652 855642 enedene 14476 905279 -jsys14 14340 844792 bpfliegel 14298 884523 mpx86 14019 759568 jpulman 13982 870599 @@ -188,8 +192,8 @@ Nesa92 13786 1114691 mbeier 13650 1044928 Hjax 13535 915487 Dark_wizzie 13422 1007152 +Jopo12321 13367 678852 Rudolphous 13244 883140 -MarcusTullius 13221 843169 Machariel 13010 863104 mabichito 12903 749391 thijsk 12886 722107 @@ -197,34 +201,35 @@ AdrianSA 12860 804972 infinigon 12807 937332 Flopzee 12698 894821 fatmurphy 12547 853210 -scuzzi 12511 845761 SapphireBrand 12416 969604 modolief 12386 896470 Farseer 12249 694108 pgontarz 12151 848794 +pirt 12008 923149 stocky 11954 699440 mschmidt 11941 803401 dbernier 11609 818636 Maxim 11543 836024 -pirt 11516 894513 infinity 11470 727027 aga 11409 695071 torbjo 11395 729145 Thomas A. Anderson 11372 732094 savage84 11358 670860 -markkulix 11331 739098 -FormazChar 11308 847735 +FormazChar 11349 850327 d64 11263 789184 MooTheCow 11237 720174 snicolet 11106 869170 ali-al-zhrani 11098 768494 whelanh 11067 235676 +Jackfish 10978 720078 +deflectooor 10886 520116 basepi 10637 744851 Cubox 10621 826448 michaelrpg 10509 739239 OIVAS7572 10420 995586 dzjp 10343 732529 -Garruk 10332 703905 +Garruk 10334 704065 ols 10259 570669 lbraesch 10252 647825 -Jackfish 10098 682338 +qoo_charly_cai 10212 620407 +Naven94 10069 503192 From c3b67faf983ac918f313d019a7427d99901fcdb0 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 15 Apr 2022 17:23:51 +0200 Subject: [PATCH 0808/1766] Update WDL model for current SF This updates the WDL model based on the LTC statistics for the last month (8M games). for old results see: https://github.com/official-stockfish/Stockfish/pull/3582 https://github.com/official-stockfish/Stockfish/pull/2778 the model changed a bit from the past, some images to follow in the PR closes https://github.com/official-stockfish/Stockfish/pull/3981 No functional change. --- src/uci.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uci.cpp b/src/uci.cpp index 741241b3271..7b30cc04325 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -207,8 +207,8 @@ namespace { // Coefficients of a 3rd order polynomial fit based on fishtest data // for two parameters needed to transform eval to the argument of a // logistic function. - double as[] = {-3.68389304, 30.07065921, -60.52878723, 149.53378557}; - double bs[] = {-2.0181857, 15.85685038, -29.83452023, 47.59078827}; + double as[] = {-1.17202460e-01, 5.94729104e-01, 1.12065546e+01, 1.22606222e+02}; + double bs[] = {-1.79066759, 11.30759193, -17.43677612, 36.47147479}; double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3]; double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3]; From c25d4c4887dbc23395afef59e24a520c5d12ab52 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Tue, 12 Apr 2022 20:45:25 +0300 Subject: [PATCH 0809/1766] Tuning classical and NNUE scaling terms changes to parameters in both classical and NNUE scaling, following up from an earlier successful #3958 passed STC: LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 23936 W: 6490 L: 6234 D: 11212 Ptnml(0-2): 107, 2610, 6306, 2810, 135 https://tests.stockfishchess.org/tests/view/625820aa33c40bb9d964e6ae passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 50376 W: 13629 L: 13327 D: 23420 Ptnml(0-2): 20, 4979, 14920, 5217, 52 https://tests.stockfishchess.org/tests/view/62584592c1d7f5008a33a4d1 closes https://github.com/official-stockfish/Stockfish/pull/3982 Bench: 6964954 --- src/evaluate.cpp | 10 +++++----- src/pawns.cpp | 38 +++++++++++++++++++------------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d9180f2b4c2..8bb42ce170c 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -198,12 +198,12 @@ namespace { constexpr Value SpaceThreshold = Value(11551); // KingAttackWeights[PieceType] contains king attack weights by piece type - constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 }; + constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 76, 46, 45, 14 }; // SafeCheck[PieceType][single/multiple] contains safe check bonus by piece type, // higher if multiple safe checks are possible for that piece type. constexpr int SafeCheck[][2] = { - {}, {}, {803, 1292}, {639, 974}, {1087, 1878}, {759, 1132} + {}, {}, {805, 1292}, {650, 984}, {1071, 1886}, {730, 1128} }; #define S(mg, eg) make_score(mg, eg) @@ -1089,7 +1089,7 @@ Value Eval::evaluate(const Position& pos) { // but we switch to NNUE during long shuffling or with high material on the board. if ( !useNNUE || ((pos.this_thread()->depth > 9 || pos.count() > 7) && - abs(eg_value(pos.psq_score())) * 5 > (856 + pos.non_pawn_material() / 64) * (5 + pos.rule50_count()))) + abs(eg_value(pos.psq_score())) * 5 > (856 + pos.non_pawn_material() / 64) * (10 + pos.rule50_count()))) { v = Evaluation(pos).value(); // classical useClassical = abs(v) >= 297; @@ -1099,7 +1099,7 @@ Value Eval::evaluate(const Position& pos) { if (useNNUE && !useClassical) { Value nnue = NNUE::evaluate(pos, true); // NNUE - int scale = 1036 + 20 * pos.non_pawn_material() / 1024; + int scale = 1036 + 22 * pos.non_pawn_material() / 1024; Color stm = pos.side_to_move(); Value optimism = pos.this_thread()->optimism[stm]; Value psq = (stm == WHITE ? 1 : -1) * eg_value(pos.psq_score()); @@ -1113,7 +1113,7 @@ Value Eval::evaluate(const Position& pos) { } // Damp down the evaluation linearly when shuffling - v = v * (207 - pos.rule50_count()) / 207; + v = v * (195 - pos.rule50_count()) / 211; // Guarantee evaluation does not hit the tablebase range v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); diff --git a/src/pawns.cpp b/src/pawns.cpp index 6e509133abc..fdcfa0224e9 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -32,30 +32,30 @@ namespace { #define S(mg, eg) make_score(mg, eg) // Pawn penalties - constexpr Score Backward = S( 9, 22); - constexpr Score Doubled = S(13, 51); - constexpr Score DoubledEarly = S(20, 7); - constexpr Score Isolated = S( 3, 15); - constexpr Score WeakLever = S( 4, 58); - constexpr Score WeakUnopposed = S(13, 24); + constexpr Score Backward = S( 6, 19); + constexpr Score Doubled = S(11, 51); + constexpr Score DoubledEarly = S(17, 7); + constexpr Score Isolated = S( 1, 20); + constexpr Score WeakLever = S( 2, 57); + constexpr Score WeakUnopposed = S(15, 18); // Bonus for blocked pawns at 5th or 6th rank - constexpr Score BlockedPawn[2] = { S(-17, -6), S(-9, 2) }; + constexpr Score BlockedPawn[2] = { S(-19, -8), S(-7, 3) }; constexpr Score BlockedStorm[RANK_NB] = { - S(0, 0), S(0, 0), S(75, 78), S(-8, 16), S(-6, 10), S(-6, 6), S(0, 2) + S(0, 0), S(0, 0), S(64, 75), S(-3, 14), S(-12, 19), S(-7, 4), S(-10, 5) }; // Connected pawn bonus - constexpr int Connected[RANK_NB] = { 0, 5, 7, 11, 23, 48, 87 }; + constexpr int Connected[RANK_NB] = { 0, 3, 7, 7, 15, 54, 86 }; // Strength of pawn shelter for our king by [distance from edge][rank]. // RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king. constexpr Value ShelterStrength[int(FILE_NB) / 2][RANK_NB] = { - { V( -5), V( 82), V( 92), V( 54), V( 36), V( 22), V( 28) }, - { V(-44), V( 63), V( 33), V(-50), V(-30), V(-12), V( -62) }, - { V(-11), V( 77), V( 22), V( -6), V( 31), V( 8), V( -45) }, - { V(-39), V(-12), V(-29), V(-50), V(-43), V(-68), V(-164) } + { V(-2), V(85), V(95), V(53), V(39), V(23), V(25) }, + { V(-55), V(64), V(32), V(-55), V(-30), V(-11), V(-61) }, + { V(-11), V(75), V(19), V(-6), V(26), V(9), V(-47) }, + { V(-41), V(-11), V(-27), V(-58), V(-42), V(-66), V(-163) } }; // Danger of enemy pawns moving toward our king by [distance from edge][rank]. @@ -63,17 +63,17 @@ namespace { // 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( 87), V(-288), V(-168), V( 96), V( 47), V( 44), V( 46) }, - { V( 42), V( -25), V( 120), V( 45), V( 34), V( -9), V( 24) }, - { V( -8), V( 51), V( 167), V( 35), V( -4), V(-16), V(-12) }, - { V(-17), V( -13), V( 100), V( 4), V( 9), V(-16), V(-31) } + { V(94), V(-280), V(-170), V(90), V(59), V(47), V(53) }, + { V(43), V(-17), V(128), V(39), V(26), V(-17), V(15) }, + { V(-9), V(62), V(170), V(34), V(-5), V(-20), V(-11) }, + { V(-27), V(-19), V(106), V(10), V(2), V(-13), V(-24) } }; // KingOnFile[semi-open Us][semi-open Them] contains bonuses/penalties // for king when the king is on a semi-open or open file. - constexpr Score KingOnFile[2][2] = {{ S(-21,10), S(-7, 1) }, - { S( 0,-3), S( 9,-4) }}; + constexpr Score KingOnFile[2][2] = {{ S(-18,11), S(-6,-3) }, + { S( 0, 0), S( 5,-4) }}; #undef S #undef V From df2f7e75276cc93a8cb8c70057903ab0edbd92bd Mon Sep 17 00:00:00 2001 From: KJE-98 <> Date: Thu, 14 Apr 2022 22:09:50 -0400 Subject: [PATCH 0810/1766] Decrease LMR at PV nodes with low depth. This patch lessens the Late Move Reduction at PV nodes with low depth. Previously the affect of depth on LMR was independant of nodeType. The idea behind this patch is that at PV nodes, LMR at low depth is will miss out on potential alpha-raising moves. Passed STC: https://tests.stockfishchess.org/tests/view/625aa867d3367522c4b8965c LLR: 2.93 (-2.94,2.94) <0.00,2.50> Total: 19360 W: 5252 L: 5006 D: 9102 Ptnml(0-2): 79, 2113, 5069, 2321, 98 Passed LTC: https://tests.stockfishchess.org/tests/view/625ae844d3367522c4b8a009 LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 39264 W: 10636 L: 10357 D: 18271 Ptnml(0-2): 18, 3928, 11473, 4183, 30 closes https://github.com/official-stockfish/Stockfish/pull/3985 bench: 8129754 --- AUTHORS | 1 + src/search.cpp | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/AUTHORS b/AUTHORS index 34b95ba5380..edf189d8cff 100644 --- a/AUTHORS +++ b/AUTHORS @@ -104,6 +104,7 @@ jundery Justin Blanchard (UncombedCoconut) Kelly Wilson Ken Takusagawa +Kian E (KJE-98) kinderchocolate Kiran Panditrao (Krgp) Kojirion diff --git a/src/search.cpp b/src/search.cpp index fa73dce5da0..49d7c5c9b9d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1173,6 +1173,10 @@ namespace { if (PvNode && !ss->inCheck && abs(ss->staticEval - bestValue) > 250) r--; + // Increase depth based reduction if PvNode + if (PvNode) + r -= 15 / ( 3 + depth ); + ss->statScore = thisThread->mainHistory[us][from_to(move)] + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] From e6e324eb28fd49c1fc44b3b65784f85a773ec61c Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 18 Apr 2022 10:17:57 +0200 Subject: [PATCH 0811/1766] Stockfish 15 Official release version of Stockfish 15 Bench: 8129754 --- A new major release of Stockfish is now available at https://stockfishchess.org Stockfish 15 continues to push the boundaries of chess, providing unrivalled analysis and playing strength. In our testing, Stockfish 15 is ahead of Stockfish 14 by 36 Elo points and wins nine times more game pairs than it loses[1]. Improvements to the engine have made it possible for Stockfish to end up victorious in tournaments at all sorts of time controls ranging from bullet to classical and even at Fischer random chess[2]. At CCC, Stockfish won all of the latest tournaments: CCC 16 Bullet, Blitz and Rapid, CCC 960 championship, and the CCC 17 Rapid. At TCEC, Stockfish won the Season 21, Cup 9, FRC 4 and in the current Season 22 superfinal, at the time of writing, has won 16 game pairs and not yet lost a single one. This progress is the result of a dedicated team of developers that comes up with new ideas and improvements. For Stockfish 15, we tested nearly 13000 different changes and retained the best 200. These include the fourth generation of our NNUE network architecture, as well as various search improvements. To perform these tests, contributors provide CPU time for testing, and in the last year, they have collectively played roughly a billion chess games. In the last few years, our distributed testing framework, Fishtest, has been operated superbly and has been developed and improved extensively. This work by Pasquale Pigazzini, Tom Vijlbrief, Michel Van den Bergh, and various other developers[3] is an essential part of the success of the Stockfish project. Indeed, the Stockfish project builds on a thriving community of enthusiasts to offer a free and open-source chess engine that is robust, widely available, and very strong. We invite our chess fans to join the Fishtest testing framework and programmers to contribute to the project[4]. The Stockfish team [1] https://tests.stockfishchess.org/tests/view/625d156dff677a888877d1be [2] https://en.wikipedia.org/wiki/Stockfish_(chess)#Competition_results [3] https://github.com/glinscott/fishtest/blob/master/AUTHORS [4] https://stockfishchess.org/get-involved/ --- src/misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index 41c59b3fd88..178465c1ad7 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -69,7 +69,7 @@ namespace { /// Version number. If Version is left empty, then compile date in the format /// DD-MM-YY and show in engine_info. -const string Version = ""; +const string Version = "15"; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We From c4db7fd1f941dbd3875e9faaaeb964755d039633 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 18 Apr 2022 23:05:24 +0200 Subject: [PATCH 0812/1766] Restore development version No functional change. --- src/misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index 178465c1ad7..41c59b3fd88 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -69,7 +69,7 @@ namespace { /// Version number. If Version is left empty, then compile date in the format /// DD-MM-YY and show in engine_info. -const string Version = "15"; +const string Version = ""; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We From 6e0680efa0d0653686b8eb8753dc49718b95cb2b Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 18 Apr 2022 07:34:03 +0200 Subject: [PATCH 0813/1766] Update default net to nn-d0b74ce1e5eb.nnue train a net using training data with a heavier weight on positions having 16 pieces on the board. More specifically, with a relative weight of `i * (32-i)/(16 * 16)+1` (where i is the number of pieces on the board). This is done with the trainer branch https://github.com/glinscott/nnue-pytorch/pull/173 The command used is: ``` python train.py $datafile $datafile $restarttype $restartfile --gpus 1 --threads 4 --num-workers 12 --random-fen-skipping=3 --batch-size 16384 --progress_bar_refresh_rate 300 --smart-fen-skipping --features=HalfKAv2_hm^ --lambda=1.00 --max_epochs=$epochs --seed $RANDOM --default_root_dir exp/run_$i ``` The datafile is T60T70wIsRightFarseerT60T74T75T76.binpack, the restart is from the master net. passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 22728 W: 6197 L: 5945 D: 10586 Ptnml(0-2): 105, 2453, 6001, 2695, 110 https://tests.stockfishchess.org/tests/view/625cf944ff677a888877cd90 passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 35664 W: 9535 L: 9264 D: 16865 Ptnml(0-2): 30, 3524, 10455, 3791, 32 https://tests.stockfishchess.org/tests/view/625d3c32ff677a888877d7ca closes https://github.com/official-stockfish/Stockfish/pull/3989 Bench: 7269563 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 1934c9bddf0..e857b7992d8 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-6877cd24400e.nnue" + #define EvalFileDefaultName "nn-d0b74ce1e5eb.nnue" namespace NNUE { From e41f727f0f7041b43997f04c031353be5087856b Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Thu, 21 Apr 2022 15:28:23 +0300 Subject: [PATCH 0814/1766] Simplify away best move count logic the only place where it was used it was true with >99% probability so it seemed to not be doing much any more. Passed STC: https://tests.stockfishchess.org/tests/view/625f4778d00da81c22dd4c93 LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 85152 W: 22487 L: 22406 D: 40259 Ptnml(0-2): 313, 9035, 23818, 9078, 332 Passed LTC: https://tests.stockfishchess.org/tests/view/625ff1f1b03f22647441a215 LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 66776 W: 17768 L: 17673 D: 31335 Ptnml(0-2): 46, 6200, 20792, 6313, 37 close https://github.com/official-stockfish/Stockfish/pull/3993 bench 7280798 --- src/search.cpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 49d7c5c9b9d..167209a315b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -556,7 +556,7 @@ namespace { bool givesCheck, improving, didLMR, priorCapture; bool capture, doFullDepthSearch, moveCountPruning, ttCapture; Piece movedPiece; - int moveCount, captureCount, quietCount, bestMoveCount, improvement, complexity; + int moveCount, captureCount, quietCount, improvement, complexity; // Step 1. Initialize node Thread* thisThread = pos.this_thread(); @@ -564,7 +564,7 @@ namespace { ss->inCheck = pos.checkers(); priorCapture = pos.captured_piece(); Color us = pos.side_to_move(); - moveCount = bestMoveCount = captureCount = quietCount = ss->moveCount = 0; + moveCount = captureCount = quietCount = ss->moveCount = 0; bestValue = -VALUE_INFINITE; maxValue = VALUE_INFINITE; @@ -1145,11 +1145,6 @@ namespace { { Depth r = reduction(improving, depth, moveCount, delta, thisThread->rootDelta); - // Decrease reduction at some PvNodes (~2 Elo) - if ( PvNode - && bestMoveCount <= 3) - r--; - // Decrease reduction if position is or has been on the PV // and node is not likely to fail low. (~3 Elo) if ( ss->ttPv @@ -1173,9 +1168,9 @@ namespace { if (PvNode && !ss->inCheck && abs(ss->staticEval - bestValue) > 250) r--; - // Increase depth based reduction if PvNode + // Decrease reduction for PvNodes based on depth if (PvNode) - r -= 15 / ( 3 + depth ); + r -= 1 + 15 / ( 3 + depth ); ss->statScore = thisThread->mainHistory[us][from_to(move)] + (*contHist[0])[movedPiece][to_sq(move)] @@ -1297,10 +1292,7 @@ namespace { update_pv(ss->pv, move, (ss+1)->pv); if (PvNode && value < beta) // Update alpha! Always alpha < beta - { alpha = value; - bestMoveCount++; - } else { assert(value >= beta); // Fail high From e1f12aa4e61a8bbb772918c405137acdd85e3eec Mon Sep 17 00:00:00 2001 From: candirufish <38038147+candirufish@users.noreply.github.com> Date: Fri, 22 Apr 2022 08:03:50 +0200 Subject: [PATCH 0815/1766] Negative extension for ttMove that is less than alpha and value in the context of singular extensions Passed STC: https://tests.stockfishchess.org/tests/view/626047e8b03f22647441ade0 LLR: 2.97 (-2.94,2.94) <0.00,2.50> Total: 50296 W: 13410 L: 13108 D: 23778 Ptnml(0-2): 196, 5548, 13370, 5826, 208 Passed LTC: https://tests.stockfishchess.org/tests/view/6260a513b03f22647441b970 LLR: 2.96 (-2.94,2.94) <0.50,3.00> Total: 83896 W: 22433 L: 22054 D: 39409 Ptnml(0-2): 49, 8273, 24938, 8626, 62 closes https://github.com/official-stockfish/Stockfish/pull/3995 bench: 7729968 --- src/search.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 167209a315b..ff6bf335d06 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1098,6 +1098,10 @@ namespace { // If the eval of ttMove is greater than beta, we reduce it (negative extension) else if (ttValue >= beta) extension = -2; + + // If the eval of ttMove is less than alpha and value, we reduce it (negative extension) + else if (ttValue <= alpha && ttValue <= value) + extension = -1; } // Check extensions (~1 Elo) From 285a79eaa0b89b342b5e4f217682e083a6fd33f5 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Wed, 27 Apr 2022 13:09:53 +0200 Subject: [PATCH 0816/1766] Simplify time management. Replace the best move instability adjustment factor by a simpler version which doesn't have a dependency on the iteration depth. STC: LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 30800 W: 8232 L: 8073 D: 14495 Ptnml(0-2): 101, 3309, 8444, 3422, 124 https://tests.stockfishchess.org/tests/view/6266c77bc5b924ba22908d30 LTC: LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 61664 W: 16375 L: 16272 D: 29017 Ptnml(0-2): 40, 5869, 18897, 6000, 26 https://tests.stockfishchess.org/tests/view/6266fc39b3d1812808915f23 closes https://github.com/official-stockfish/Stockfish/pull/3999 Bench: 7729968 --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ff6bf335d06..cd38e62920f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -466,8 +466,7 @@ void Thread::search() { // If the bestMove is stable over several iterations, reduce time accordingly timeReduction = lastBestMoveDepth + 10 < completedDepth ? 1.63 : 0.73; double reduction = (1.56 + mainThread->previousTimeReduction) / (2.20 * timeReduction); - double bestMoveInstability = 1.073 + std::max(1.0, 2.25 - 9.9 / rootDepth) - * totBestMoveChanges / Threads.size(); + double bestMoveInstability = 1 + 1.7 * totBestMoveChanges / Threads.size(); int complexity = mainThread->complexityAverage.value(); double complexPosition = std::clamp(1.0 + (complexity - 326) / 1618.1, 0.5, 1.5); From a32d2086bc227f1d76bd04808e10c8e7bd230fe9 Mon Sep 17 00:00:00 2001 From: candirufish <38038147+candirufish@users.noreply.github.com> Date: Tue, 3 May 2022 12:35:21 +0200 Subject: [PATCH 0817/1766] Use fail high count for LMR Increase reduction if next ply has a lot of fail high else reset count to 0 Passed STC: https://tests.stockfishchess.org/tests/view/626ea8299116b52aa83b71f6 LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 144288 W: 38377 L: 37902 D: 68009 Ptnml(0-2): 565, 16298, 38054, 16551, 676 Passed LTC: https://tests.stockfishchess.org/tests/view/626fa0fb79f761bab2e382f0 LLR: 2.98 (-2.94,2.94) <0.50,3.00> Total: 74872 W: 20050 L: 19686 D: 35136 Ptnml(0-2): 51, 7541, 21893, 7895, 56 closes https://github.com/official-stockfish/Stockfish/pull/4006 bench: 7084802 --- src/search.cpp | 9 +++++++++ src/search.h | 1 + 2 files changed, 10 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index cd38e62920f..70b852f35a8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -603,6 +603,7 @@ namespace { (ss+1)->ttPv = false; (ss+1)->excludedMove = bestMove = MOVE_NONE; (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; + (ss+2)->cutoffCnt = 0; ss->doubleExtensions = (ss-1)->doubleExtensions; ss->depth = depth; Square prevSq = to_sq((ss-1)->currentMove); @@ -1175,6 +1176,10 @@ namespace { if (PvNode) r -= 1 + 15 / ( 3 + depth ); + // Increase reduction if next ply has a lot of fail high else reset count to 0 + if ((ss+1)->cutoffCnt > 3 && !PvNode) + r++; + ss->statScore = thisThread->mainHistory[us][from_to(move)] + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] @@ -1298,11 +1303,15 @@ namespace { alpha = value; else { + ss->cutoffCnt++; assert(value >= beta); // Fail high break; } } } + else + ss->cutoffCnt = 0; + // If the move is worse than some previously searched move, remember it to update its stats later if (move != bestMove) diff --git a/src/search.h b/src/search.h index 806295a1100..8bb51832586 100644 --- a/src/search.h +++ b/src/search.h @@ -54,6 +54,7 @@ struct Stack { bool ttPv; bool ttHit; int doubleExtensions; + int cutoffCnt; }; From 9eb7b607cf69f6b613c89ae767b7f934f26e59e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Wed, 4 May 2022 07:39:23 +0200 Subject: [PATCH 0818/1766] Reduce depth after score improvement at PV nodes STC: LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 73760 W: 19590 L: 19244 D: 34926 Ptnml(0-2): 285, 8352, 19292, 8634, 317 https://tests.stockfishchess.org/tests/view/626eb2dc9116b52aa83b73da LTC: LLR: 2.93 (-2.94,2.94) <0.50,3.00> Total: 114400 W: 30561 L: 30111 D: 53728 Ptnml(0-2): 68, 11432, 33785, 11812, 103 https://tests.stockfishchess.org/tests/view/626f730859e9c431e0b10b21 closes https://github.com/official-stockfish/Stockfish/pull/4008 bench: 6174823 --- src/search.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 70b852f35a8..c12b60e6815 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1300,7 +1300,18 @@ namespace { update_pv(ss->pv, move, (ss+1)->pv); if (PvNode && value < beta) // Update alpha! Always alpha < beta + { alpha = value; + + // Reduce other moves if we have found at least one score improvement + if ( depth > 2 + && depth < 7 + && beta < VALUE_KNOWN_WIN + && alpha > -VALUE_KNOWN_WIN) + depth -= 1; + + assert(depth > 0); + } else { ss->cutoffCnt++; From c079acc26f93acc2eda08c7218c60559854f52f0 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Fri, 13 May 2022 17:26:50 +0200 Subject: [PATCH 0819/1766] Update NNUE architecture to SFNNv5. Update network to nn-3c0aa92af1da.nnue. Architecture changes: Duplicated activation after the 1024->15 layer with squared crelu (so 15->15*2). As proposed by vondele. Trainer changes: Added bias to L1 factorization, which was previously missing (no measurable improvement but at least neutral in principle) For retraining linearly reduce lambda parameter from 1.0 at epoch 0 to 0.75 at epoch 800. reduce max_skipping_rate from 15 to 10 (compared to vondele's outstanding PR) Note: This network was trained with a ~0.8% error in quantization regarding the newly added activation function. This will be fixed in the released trainer version. Expect a trainer PR tomorrow. Note: The inference implementation cuts a corner to merge results from two activation functions. This could possibly be resolved nicer in the future. AVX2 implementation likely not necessary, but NEON is missing. First training session invocation: python3 train.py \ ../nnue-pytorch-training/data/nodes5000pv2_UHO.binpack \ ../nnue-pytorch-training/data/nodes5000pv2_UHO.binpack \ --gpus "$3," \ --threads 4 \ --num-workers 8 \ --batch-size 16384 \ --progress_bar_refresh_rate 20 \ --random-fen-skipping 3 \ --features=HalfKAv2_hm^ \ --lambda=1.0 \ --max_epochs=400 \ --default_root_dir ../nnue-pytorch-training/experiment_$1/run_$2 Second training session invocation: python3 train.py \ ../nnue-pytorch-training/data/T60T70wIsRightFarseerT60T74T75T76.binpack \ ../nnue-pytorch-training/data/T60T70wIsRightFarseerT60T74T75T76.binpack \ --gpus "$3," \ --threads 4 \ --num-workers 8 \ --batch-size 16384 \ --progress_bar_refresh_rate 20 \ --random-fen-skipping 3 \ --features=HalfKAv2_hm^ \ --start-lambda=1.0 \ --end-lambda=0.75 \ --gamma=0.995 \ --lr=4.375e-4 \ --max_epochs=800 \ --resume-from-model /data/sopel/nnue/nnue-pytorch-training/data/exp367/nn-exp367-run3-epoch399.pt \ --default_root_dir ../nnue-pytorch-training/experiment_$1/run_$2 Passed STC: LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 27288 W: 7445 L: 7178 D: 12665 Ptnml(0-2): 159, 3002, 7054, 3271, 158 https://tests.stockfishchess.org/tests/view/627e8c001919125939623644 Passed LTC: LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 21792 W: 5969 L: 5727 D: 10096 Ptnml(0-2): 25, 2152, 6294, 2406, 19 https://tests.stockfishchess.org/tests/view/627f2a855734b18b2e2ece47 closes https://github.com/official-stockfish/Stockfish/pull/4020 Bench: 6481017 --- src/evaluate.h | 2 +- src/nnue/layers/sqr_clipped_relu.h | 120 +++++++++++++++++++++++++++++ src/nnue/nnue_architecture.h | 9 ++- 3 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 src/nnue/layers/sqr_clipped_relu.h diff --git a/src/evaluate.h b/src/evaluate.h index e857b7992d8..f67961a90e5 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-d0b74ce1e5eb.nnue" + #define EvalFileDefaultName "nn-3c0aa92af1da.nnue" namespace NNUE { diff --git a/src/nnue/layers/sqr_clipped_relu.h b/src/nnue/layers/sqr_clipped_relu.h new file mode 100644 index 00000000000..b603a277fa2 --- /dev/null +++ b/src/nnue/layers/sqr_clipped_relu.h @@ -0,0 +1,120 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// Definition of layer ClippedReLU of NNUE evaluation function + +#ifndef NNUE_LAYERS_SQR_CLIPPED_RELU_H_INCLUDED +#define NNUE_LAYERS_SQR_CLIPPED_RELU_H_INCLUDED + +#include "../nnue_common.h" + +namespace Stockfish::Eval::NNUE::Layers { + + // Clipped ReLU + template + class SqrClippedReLU { + public: + // Input/output type + using InputType = std::int32_t; + using OutputType = std::uint8_t; + + // Number of input/output dimensions + static constexpr IndexType InputDimensions = InDims; + static constexpr IndexType OutputDimensions = InputDimensions; + static constexpr IndexType PaddedOutputDimensions = + ceil_to_multiple(OutputDimensions, 32); + + using OutputBuffer = OutputType[PaddedOutputDimensions]; + + // Hash value embedded in the evaluation file + static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) { + std::uint32_t hashValue = 0x538D24C7u; + hashValue += prevHash; + return hashValue; + } + + // Read network parameters + bool read_parameters(std::istream&) { + return true; + } + + // Write network parameters + bool write_parameters(std::ostream&) const { + return true; + } + + // Forward propagation + const OutputType* propagate( + const InputType* input, OutputType* output) const { + + #if defined(USE_SSE2) + constexpr IndexType NumChunks = InputDimensions / 16; + + #ifdef USE_SSE41 + const __m128i Zero = _mm_setzero_si128(); + #else + const __m128i k0x80s = _mm_set1_epi8(-128); + #endif + + static_assert(WeightScaleBits == 6); + const auto in = reinterpret_cast(input); + const auto out = reinterpret_cast<__m128i*>(output); + for (IndexType i = 0; i < NumChunks; ++i) { + __m128i words0 = _mm_packs_epi32( + _mm_load_si128(&in[i * 4 + 0]), + _mm_load_si128(&in[i * 4 + 1])); + __m128i words1 = _mm_packs_epi32( + _mm_load_si128(&in[i * 4 + 2]), + _mm_load_si128(&in[i * 4 + 3])); + + // Not sure if + words0 = _mm_srli_epi16(_mm_mulhi_epi16(words0, words0), 3); + words1 = _mm_srli_epi16(_mm_mulhi_epi16(words1, words1), 3); + + const __m128i packedbytes = _mm_packs_epi16(words0, words1); + + _mm_store_si128(&out[i], + + #ifdef USE_SSE41 + _mm_max_epi8(packedbytes, Zero) + #else + _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s) + #endif + + ); + } + constexpr IndexType Start = NumChunks * 16; + + #else + constexpr IndexType Start = 0; + #endif + + for (IndexType i = Start; i < InputDimensions; ++i) { + output[i] = static_cast( + // realy should be /127 but we need to make it fast + // needs to be accounted for in the trainer + std::max(0ll, std::min(127ll, (((long long)input[i] * input[i]) >> (2 * WeightScaleBits)) / 128))); + } + + return output; + } + }; + +} // namespace Stockfish::Eval::NNUE::Layers + +#endif // NNUE_LAYERS_SQR_CLIPPED_RELU_H_INCLUDED diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index 4f9596ae949..cac8373075e 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -29,6 +29,7 @@ #include "layers/affine_transform.h" #include "layers/clipped_relu.h" +#include "layers/sqr_clipped_relu.h" #include "../misc.h" @@ -48,8 +49,9 @@ struct Network static constexpr int FC_1_OUTPUTS = 32; Layers::AffineTransform fc_0; + Layers::SqrClippedReLU ac_sqr_0; Layers::ClippedReLU ac_0; - Layers::AffineTransform fc_1; + Layers::AffineTransform fc_1; Layers::ClippedReLU ac_1; Layers::AffineTransform fc_2; @@ -93,6 +95,7 @@ struct Network struct alignas(CacheLineSize) Buffer { alignas(CacheLineSize) decltype(fc_0)::OutputBuffer fc_0_out; + alignas(CacheLineSize) decltype(ac_sqr_0)::OutputType ac_sqr_0_out[ceil_to_multiple(FC_0_OUTPUTS * 2, 32)]; alignas(CacheLineSize) decltype(ac_0)::OutputBuffer ac_0_out; alignas(CacheLineSize) decltype(fc_1)::OutputBuffer fc_1_out; alignas(CacheLineSize) decltype(ac_1)::OutputBuffer ac_1_out; @@ -114,8 +117,10 @@ struct Network #endif fc_0.propagate(transformedFeatures, buffer.fc_0_out); + ac_sqr_0.propagate(buffer.fc_0_out, buffer.ac_sqr_0_out); ac_0.propagate(buffer.fc_0_out, buffer.ac_0_out); - fc_1.propagate(buffer.ac_0_out, buffer.fc_1_out); + std::memcpy(buffer.ac_sqr_0_out + FC_0_OUTPUTS, buffer.ac_0_out, FC_0_OUTPUTS * sizeof(decltype(ac_0)::OutputType)); + fc_1.propagate(buffer.ac_sqr_0_out, buffer.fc_1_out); ac_1.propagate(buffer.fc_1_out, buffer.ac_1_out); fc_2.propagate(buffer.ac_1_out, buffer.fc_2_out); From 5372f81cc81d5be3040db6f2dbfff108c460baf9 Mon Sep 17 00:00:00 2001 From: disservin <45608332+Disservin@users.noreply.github.com> Date: Sat, 14 May 2022 12:10:13 +0200 Subject: [PATCH 0820/1766] SE depth scaling using the previous depth This patch makes the SE depth condition more robust and allows it to scale with completed depth from a previous search. At long TC this patch is almost equivalent to https://github.com/official-stockfish/Stockfish/pull/4016 which had VLTC: https://tests.stockfishchess.org/tests/view/626abd7e8707aa698c0093a8 Elo: 2.35 +-1.5 (95%) LOS: 99.9% Total: 40000 W: 10991 L: 10720 D: 18289 Ptnml(0-2): 8, 3534, 12648, 3799, 11 nElo: 5.47 +-3.4 (95%) PairsRatio: 1.08 VLTC multicore: https://tests.stockfishchess.org/tests/view/6272a6afc8f14123163c1997 LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 86808 W: 24165 L: 23814 D: 38829 Ptnml(0-2): 11, 7253, 28524, 7606, 10 however, it is now also gaining at LTC: LTC: https://tests.stockfishchess.org/tests/view/627e7cb523c0c72a05b651a9 LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 27064 W: 7285 L: 7046 D: 12733 Ptnml(0-2): 8, 2446, 8390, 2675, 13 and should have nearly no influence at STC as depth 27 is rarely reached. It was noticed that initializing the threshold with MAX_PLY, had an adverse effect, possibly because the first move is sensitive to this. closes https://github.com/official-stockfish/Stockfish/pull/4021 closes https://github.com/official-stockfish/Stockfish/pull/4016 Bench: 6481017 --- AUTHORS | 1 + src/search.cpp | 5 ++++- src/thread.cpp | 3 ++- src/thread.h | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index edf189d8cff..b435ff8f4d8 100644 --- a/AUTHORS +++ b/AUTHORS @@ -125,6 +125,7 @@ marotear Matt Ginsberg (mattginsberg) Matthew Lai (matthewlai) Matthew Sullivan (Matt14916) +Max A. (Disservin) Maxim Molchanov (Maxim) Michael An (man) Michael Byrne (MichaelB7) diff --git a/src/search.cpp b/src/search.cpp index c12b60e6815..4a41a8b0b09 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -238,6 +238,9 @@ void MainThread::search() { bestPreviousScore = bestThread->rootMoves[0].score; bestPreviousAverageScore = bestThread->rootMoves[0].averageScore; + for (Thread* th : Threads) + th->previousDepth = bestThread->completedDepth; + // Send again PV info if we have a new best thread if (bestThread != this) sync_cout << UCI::pv(bestThread->rootPos, bestThread->completedDepth, -VALUE_INFINITE, VALUE_INFINITE) << sync_endl; @@ -1061,7 +1064,7 @@ namespace { // 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 ( !rootNode - && depth >= 4 + 2 * (PvNode && tte->is_pv()) + && depth >= 4 - (thisThread->previousDepth > 27) + 2 * (PvNode && tte->is_pv()) && move == ttMove && !excludedMove // Avoid recursive singular search /* && ttValue != VALUE_NONE Already implicit in the next condition */ diff --git a/src/thread.cpp b/src/thread.cpp index 30177a3915a..08a78db5f31 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -60,7 +60,8 @@ void Thread::clear() { counterMoves.fill(MOVE_NONE); mainHistory.fill(0); captureHistory.fill(0); - + previousDepth = 0; + for (bool inCheck : { false, true }) for (StatsType c : { NoCaptures, Captures }) { diff --git a/src/thread.h b/src/thread.h index 8027855a614..9e9cd488c3c 100644 --- a/src/thread.h +++ b/src/thread.h @@ -69,7 +69,7 @@ class Thread { Position rootPos; StateInfo rootState; Search::RootMoves rootMoves; - Depth rootDepth, completedDepth, depth; + Depth rootDepth, completedDepth, depth, previousDepth; Value rootDelta; CounterMoveHistory counterMoves; ButterflyHistory mainHistory; From 22b7909809c731aea691184dd7c1a2b02c5946af Mon Sep 17 00:00:00 2001 From: xoto10 Date: Sun, 15 May 2022 13:14:28 +0100 Subject: [PATCH 0821/1766] Tune scale and optimism. Tune scale and optimism in effort to make stockfish play more aggressively. STC @ 10+0.1 th 1: LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 27896 W: 7506 L: 7248 D: 13142 Ptnml(0-2): 103, 3047, 7388, 3309, 101 https://tests.stockfishchess.org/tests/live_elo/627fd0cfab44257388ab1f13 LTC @ 60+0.6 th 1: LLR: 2.93 (-2.94,2.94) <0.50,3.00> Total: 65576 W: 17512 L: 17178 D: 30886 Ptnml(0-2): 37, 6397, 19587, 6729, 38 https://tests.stockfishchess.org/tests/live_elo/627ff666ab44257388ab256d closes https://github.com/official-stockfish/Stockfish/pull/4025 Bench 6407734 --- src/evaluate.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 8bb42ce170c..718c7bc035d 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -192,6 +192,7 @@ using namespace Trace; namespace { + // Threshold for lazy and space evaluation constexpr Value LazyThreshold1 = Value(3631); constexpr Value LazyThreshold2 = Value(2084); @@ -1099,14 +1100,14 @@ Value Eval::evaluate(const Position& pos) { if (useNNUE && !useClassical) { Value nnue = NNUE::evaluate(pos, true); // NNUE - int scale = 1036 + 22 * pos.non_pawn_material() / 1024; + int scale = 1014 + 21 * pos.non_pawn_material() / 1024; Color stm = pos.side_to_move(); Value optimism = pos.this_thread()->optimism[stm]; Value psq = (stm == WHITE ? 1 : -1) * eg_value(pos.psq_score()); int complexity = 35 * abs(nnue - psq) / 256; - optimism = optimism * (44 + complexity) / 31; - v = (nnue + optimism) * scale / 1024 - optimism; + optimism = optimism * (32 + complexity) / 32; + v = (nnue * scale + optimism * (scale - 846)) / 1024; if (pos.is_chess960()) v += fix_FRC(pos); From cc7bcd5303a645223a6cd853817d3172754243aa Mon Sep 17 00:00:00 2001 From: candirufish <38038147+candirufish@users.noreply.github.com> Date: Sat, 21 May 2022 04:20:09 +0200 Subject: [PATCH 0822/1766] Simplify a condition Principal variation depth late move reduction extension simplification. stc: https://tests.stockfishchess.org/tests/view/6285a1d19d18a78568e7fa24 LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 428536 W: 113433 L: 113851 D: 201252 Ptnml(0-2): 1671, 48606, 114090, 48272, 1629 ltc: https://tests.stockfishchess.org/tests/view/62871d20375cdc5de8cf5db3 LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 56792 W: 15123 L: 15011 D: 26658 Ptnml(0-2): 42, 5681, 16825, 5819, 29 closes https://github.com/official-stockfish/Stockfish/pull/4028 bench: 6501437 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 4a41a8b0b09..d09ced9a670 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1197,7 +1197,7 @@ namespace { // deeper than the first move (this may lead to hidden double extensions). int deeper = r >= -1 ? 0 : moveCount <= 4 ? 2 - : PvNode && depth > 4 ? 1 + : PvNode ? 1 : cutNode && moveCount <= 8 ? 1 : 0; From f7d1491b3df28bf10faac81e340cb6a22fc5b57b Mon Sep 17 00:00:00 2001 From: Giacomo Lorenzetti Date: Fri, 1 Apr 2022 18:33:25 +0200 Subject: [PATCH 0823/1766] Assorted small cleanups closes https://github.com/official-stockfish/Stockfish/pull/3973 No functional change --- README.md | 3 +- src/Makefile | 50 ++++++++++++++--------------- src/evaluate.cpp | 10 +++--- src/nnue/nnue_feature_transformer.h | 18 +++++------ src/search.cpp | 6 +--- src/search.h | 1 - src/syzygy/tbprobe.cpp | 2 +- src/types.h | 2 +- 8 files changed, 43 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 6e6e762e63e..f84b79d109b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ ## Overview [![Build Status](https://github.com/official-stockfish/Stockfish/actions/workflows/stockfish.yml/badge.svg)](https://github.com/official-stockfish/Stockfish/actions) -[![Build Status](https://ci.appveyor.com/api/projects/status/github/official-stockfish/Stockfish?branch=master&svg=true)](https://ci.appveyor.com/project/mcostalba/stockfish/branch/master) [Stockfish](https://stockfishchess.org) is a free, powerful UCI chess engine derived from Glaurung 2.1. Stockfish is not a complete chess program and requires a @@ -21,7 +20,7 @@ avx2, neon, or similar). This distribution of Stockfish consists of the following files: - * [Readme.md](https://github.com/official-stockfish/Stockfish/blob/master/README.md), + * [README.md](https://github.com/official-stockfish/Stockfish/blob/master/README.md), the file you are currently reading. * [Copying.txt](https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt), diff --git a/src/Makefile b/src/Makefile index eff8bacae0a..cc9b4201555 100644 --- a/src/Makefile +++ b/src/Makefile @@ -542,17 +542,17 @@ ifeq ($(optimize),yes) endif endif - ifeq ($(KERNEL),Darwin) - ifeq ($(comp),$(filter $(comp),clang icc)) - CXXFLAGS += -mdynamic-no-pic - endif - - ifeq ($(comp),gcc) - ifneq ($(arch),arm64) - CXXFLAGS += -mdynamic-no-pic - endif - endif - endif + ifeq ($(KERNEL),Darwin) + ifeq ($(comp),$(filter $(comp),clang icc)) + CXXFLAGS += -mdynamic-no-pic + endif + + ifeq ($(comp),gcc) + ifneq ($(arch),arm64) + CXXFLAGS += -mdynamic-no-pic + endif + endif + endif ifeq ($(comp),clang) CXXFLAGS += -fexperimental-new-pass-manager @@ -824,22 +824,22 @@ net: $(eval nnuedownloadurl := https://tests.stockfishchess.org/api/nn/$(nnuenet)) $(eval curl_or_wget := $(shell if hash curl 2>/dev/null; then echo "curl -skL"; elif hash wget 2>/dev/null; then echo "wget -qO-"; fi)) @if test -f "$(nnuenet)"; then \ - echo "Already available."; \ - else \ - if [ "x$(curl_or_wget)" = "x" ]; then \ - echo "Automatic download failed: neither curl nor wget is installed. Install one of these tools or download the net manually"; exit 1; \ - else \ - echo "Downloading $(nnuedownloadurl)"; $(curl_or_wget) $(nnuedownloadurl) > $(nnuenet);\ - fi; \ - fi; + echo "Already available."; \ + else \ + if [ "x$(curl_or_wget)" = "x" ]; then \ + echo "Automatic download failed: neither curl nor wget is installed. Install one of these tools or download the net manually"; exit 1; \ + else \ + echo "Downloading $(nnuedownloadurl)"; $(curl_or_wget) $(nnuedownloadurl) > $(nnuenet);\ + fi; \ + fi; $(eval shasum_command := $(shell if hash shasum 2>/dev/null; then echo "shasum -a 256 "; elif hash sha256sum 2>/dev/null; then echo "sha256sum "; fi)) @if [ "x$(shasum_command)" != "x" ]; then \ - if [ "$(nnuenet)" != "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then \ - echo "Failed download or $(nnuenet) corrupted, please delete!"; exit 1; \ - fi \ - else \ - echo "shasum / sha256sum not found, skipping net validation"; \ - fi + if [ "$(nnuenet)" != "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then \ + echo "Failed download or $(nnuenet) corrupted, please delete!"; exit 1; \ + fi \ + else \ + echo "shasum / sha256sum not found, skipping net validation"; \ + fi # clean binaries and objects objclean: diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 718c7bc035d..9061a38421f 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -192,7 +192,6 @@ using namespace Trace; namespace { - // Threshold for lazy and space evaluation constexpr Value LazyThreshold1 = Value(3631); constexpr Value LazyThreshold2 = Value(2084); @@ -1084,13 +1083,14 @@ namespace { Value Eval::evaluate(const Position& pos) { Value v; - bool useClassical = false; + // Deciding between classical and NNUE eval (~10 Elo): for high PSQ imbalance we use classical, + // but we switch to NNUE during long shuffling or with high material on the board. + bool useClassical = (pos.this_thread()->depth > 9 || pos.count() > 7) && + abs(eg_value(pos.psq_score())) * 5 > (856 + pos.non_pawn_material() / 64) * (10 + pos.rule50_count()); // Deciding between classical and NNUE eval (~10 Elo): for high PSQ imbalance we use classical, // but we switch to NNUE during long shuffling or with high material on the board. - if ( !useNNUE - || ((pos.this_thread()->depth > 9 || pos.count() > 7) && - abs(eg_value(pos.psq_score())) * 5 > (856 + pos.non_pawn_material() / 64) * (10 + pos.rule50_count()))) + if (!useNNUE || useClassical) { v = Evaluation(pos).value(); // classical useClassical = abs(v) >= 297; diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index c969ac6cee8..34d7292c006 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -120,12 +120,12 @@ namespace Stockfish::Eval::NNUE { #define vec_zero() _mm_setzero_si64() #define vec_set_16(a) _mm_set1_pi16(a) inline vec_t vec_max_16(vec_t a,vec_t b){ - vec_t comparison = _mm_cmpgt_pi16(a,b); - return _mm_or_si64(_mm_and_si64(comparison, a), _mm_andnot_si64(comparison, b)); + vec_t comparison = _mm_cmpgt_pi16(a,b); + return _mm_or_si64(_mm_and_si64(comparison, a), _mm_andnot_si64(comparison, b)); } inline vec_t vec_min_16(vec_t a,vec_t b){ - vec_t comparison = _mm_cmpgt_pi16(a,b); - return _mm_or_si64(_mm_and_si64(comparison, b), _mm_andnot_si64(comparison, a)); + vec_t comparison = _mm_cmpgt_pi16(a,b); + return _mm_or_si64(_mm_and_si64(comparison, b), _mm_andnot_si64(comparison, a)); } #define vec_msb_pack_16(a,b) _mm_packs_pi16(_mm_srli_pi16(a,7),_mm_srli_pi16(b,7)) #define vec_load_psqt(a) (*(a)) @@ -150,10 +150,10 @@ namespace Stockfish::Eval::NNUE { #define vec_max_16(a,b) vmaxq_s16(a,b) #define vec_min_16(a,b) vminq_s16(a,b) inline vec_t vec_msb_pack_16(vec_t a, vec_t b){ - const int8x8_t shifta = vshrn_n_s16(a, 7); - const int8x8_t shiftb = vshrn_n_s16(b, 7); - const int8x16_t compacted = vcombine_s8(shifta,shiftb); - return *reinterpret_cast (&compacted); + const int8x8_t shifta = vshrn_n_s16(a, 7); + const int8x8_t shiftb = vshrn_n_s16(b, 7); + const int8x16_t compacted = vcombine_s8(shifta,shiftb); + return *reinterpret_cast (&compacted); } #define vec_load_psqt(a) (*(a)) #define vec_store_psqt(a,b) *(a)=(b) @@ -290,7 +290,7 @@ namespace Stockfish::Eval::NNUE { #if defined(VECTOR) - constexpr IndexType OutputChunkSize = MaxChunkSize; + constexpr IndexType OutputChunkSize = MaxChunkSize; static_assert((HalfDimensions / 2) % OutputChunkSize == 0); constexpr IndexType NumOutputChunks = HalfDimensions / 2 / OutputChunkSize; diff --git a/src/search.cpp b/src/search.cpp index d09ced9a670..ff7b996be57 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -608,7 +608,6 @@ namespace { (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; (ss+2)->cutoffCnt = 0; ss->doubleExtensions = (ss-1)->doubleExtensions; - ss->depth = depth; Square prevSq = to_sq((ss-1)->currentMove); // Initialize statScore to zero for the grandchildren of the current position. @@ -869,7 +868,6 @@ namespace { MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, depth - 3, &captureHistory); bool ttPv = ss->ttPv; - bool captureOrPromotion; ss->ttPv = false; while ((move = mp.next_move()) != MOVE_NONE) @@ -877,11 +875,9 @@ namespace { { assert(pos.capture(move) || promotion_type(move) == QUEEN); - captureOrPromotion = true; - ss->currentMove = move; ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck] - [captureOrPromotion] + [true] [pos.moved_piece(move)] [to_sq(move)]; diff --git a/src/search.h b/src/search.h index 8bb51832586..4ad5784f8db 100644 --- a/src/search.h +++ b/src/search.h @@ -47,7 +47,6 @@ struct Stack { Move excludedMove; Move killers[2]; Value staticEval; - Depth depth; int statScore; int moveCount; bool inCheck; diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index a1315244299..43af89f0ec9 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1290,7 +1290,7 @@ void Tablebases::init(const std::string& paths) { for (auto s : diagonal) MapA1D1D4[s] = code++; - // MapKK[] encodes all the 461 possible legal positions of two kings where + // MapKK[] encodes all the 462 possible legal positions of two kings where // the first is in the a1-d1-d4 triangle. If the first king is on the a1-d4 // diagonal, the other one shall not to be above the a1-h8 diagonal. std::vector> bothOnDiagonal; diff --git a/src/types.h b/src/types.h index cf42bc9f8ff..c2087c6c07b 100644 --- a/src/types.h +++ b/src/types.h @@ -450,7 +450,7 @@ constexpr Square to_sq(Move m) { } constexpr int from_to(Move m) { - return m & 0xFFF; + return m & 0xFFF; } constexpr MoveType type_of(Move m) { From 48df0754bc87f099c4b29ff1b2f2629ddacd1c95 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 20 May 2022 07:42:33 +0200 Subject: [PATCH 0824/1766] Add command line flags to link to information This patch provides command line flags `--help` and `--license` as well as the corresponding `help` and `license` commands. ``` $ ./stockfish --help Stockfish 200522 by the Stockfish developers (see AUTHORS file) Stockfish is a powerful chess engine and free software licensed under the GNU GPLv3. Stockfish is normally used with a separate graphical user interface (GUI). Stockfish implements the universal chess interface (UCI) to exchange information. For further information see https://github.com/official-stockfish/Stockfish#readme or the corresponding README.md and Copying.txt files distributed with this program. ``` The idea is to provide a minimal help that links to the README.md file, not replicating information that is already available elsewhere. We use this opportunity to explicitly report the license as well. closes https://github.com/official-stockfish/Stockfish/pull/4027 No functional change. --- src/uci.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/uci.cpp b/src/uci.cpp index 7b30cc04325..c28cf6d110f 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -285,8 +285,14 @@ void UCI::loop(int argc, char* argv[]) { filename = f; Eval::NNUE::save_eval(filename); } + else if (token == "--help" || token == "help" || token == "--license" || token == "license") + sync_cout << "\nStockfish is a powerful chess engine and free software licensed under the GNU GPLv3." + "\nStockfish is normally used with a separate graphical user interface (GUI)." + "\nStockfish implements the universal chess interface (UCI) to exchange information." + "\nFor further information see https://github.com/official-stockfish/Stockfish#readme" + "\nor the corresponding README.md and Copying.txt files distributed with this program.\n" << sync_endl; else if (!token.empty() && token[0] != '#') - sync_cout << "Unknown command: " << cmd << sync_endl; + sync_cout << "Unknown command: '" << cmd << "'. Type help for more information." << sync_endl; } while (token != "quit" && argc == 1); // Command line args are one-shot } From 1a168201bd8424da4cea384ee4c03973b2ccf0ca Mon Sep 17 00:00:00 2001 From: Giacomo Lorenzetti Date: Sun, 22 May 2022 12:47:30 +0200 Subject: [PATCH 0825/1766] Small speedup in futility_move_count The speedup is around 0.25% using gcc 11.3.1 (bmi2, nnue bench, depth 16 and 23) while it is neutral using clang (same conditions). According to `perf` that integer division was one of the most time-consuming instructions in search (gcc disassembly). Passed STC: https://tests.stockfishchess.org/tests/view/628a17fe24a074e5cd59b3aa LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 22232 W: 5992 L: 5751 D: 10489 Ptnml(0-2): 88, 2235, 6218, 2498, 77 yellow LTC: https://tests.stockfishchess.org/tests/view/628a35d7ccae0450e35106f7 LLR: -2.95 (-2.94,2.94) <0.50,3.00> Total: 320168 W: 85853 L: 85326 D: 148989 Ptnml(0-2): 185, 29698, 99821, 30165, 215 This patch also suggests that UHO STC is sensible to small speedups (< 0.50%). closes https://github.com/official-stockfish/Stockfish/pull/4032 No functional change --- src/search.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index ff7b996be57..0a4b1a18d67 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -75,7 +75,8 @@ namespace { } constexpr int futility_move_count(bool improving, Depth depth) { - return (3 + depth * depth) / (2 - improving); + return improving ? (3 + depth * depth) + : (3 + depth * depth) / 2; } // History and stats update bonus, based on depth From 6ede1bed89fd9d1c25cc6349722898f084bf5e15 Mon Sep 17 00:00:00 2001 From: proukornew Date: Fri, 17 Dec 2021 00:41:29 +0300 Subject: [PATCH 0826/1766] Improve handling of variables set in the make environment removes duplication on the commandline for example in a profile-build closes https://github.com/official-stockfish/Stockfish/pull/3859 No functional change --- src/Makefile | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Makefile b/src/Makefile index cc9b4201555..ff2452d6ad5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -345,9 +345,15 @@ endif ### ========================================================================== ### 3.1 Selecting compiler (default = gcc) -CXXFLAGS += -Wall -Wcast-qual -fno-exceptions -std=c++17 $(EXTRACXXFLAGS) -DEPENDFLAGS += -std=c++17 -LDFLAGS += $(EXTRALDFLAGS) +ifeq ($(MAKELEVEL),0) + export ENV_CXXFLAGS := $(CXXFLAGS) + export ENV_DEPENDFLAGS := $(DEPENDFLAGS) + export ENV_LDFLAGS := $(LDFLAGS) +endif + +CXXFLAGS = $(ENV_CXXFLAGS) -Wall -Wcast-qual -fno-exceptions -std=c++17 $(EXTRACXXFLAGS) +DEPENDFLAGS = $(ENV_DEPENDFLAGS) -std=c++17 +LDFLAGS = $(ENV_LDFLAGS) $(EXTRALDFLAGS) ifeq ($(COMP),) COMP=gcc From 4c7de9e8abd8d5dc71d0c85dddd75a7b244600d7 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Thu, 19 May 2022 08:51:40 +0100 Subject: [PATCH 0827/1766] Adjust scale param higher xoto10's scaleopt tune resulted in a yellow LTC, but the main parameter shift looked almost exactly like the tune rate reduction schedule, so further increases of that param were tried. Joint work xoto10 and dubslow. passed LTC: https://tests.stockfishchess.org/tests/view/628c709372775f382300f03e LLR: 2.93 (-2.94,2.94) <0.50,3.00> Total: 70112 W: 18932 L: 18584 D: 32596 Ptnml(0-2): 66, 6904, 20757, 7274, 55 failed STC: https://tests.stockfishchess.org/tests/view/6290e4441e7cd5f29966bdc8 LLR: -2.96 (-2.94,2.94) <0.00,2.50> Total: 59976 W: 15919 L: 16018 D: 28039 Ptnml(0-2): 250, 6791, 15974, 6754, 219 similar LTC's were yellow first yellow LTC: https://tests.stockfishchess.org/tests/view/6288a33f817227d3e5c5b05d double exaggerate yellow: https://tests.stockfishchess.org/tests/live_elo/628e140372775f38230129a6 triple exaggerate yellow: https://tests.stockfishchess.org/tests/live_elo/628e2caf72775f3823012d45 closes https://github.com/official-stockfish/Stockfish/pull/4036 bench 6410652 --- AUTHORS | 1 + src/evaluate.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index b435ff8f4d8..715f83b1974 100644 --- a/AUTHORS +++ b/AUTHORS @@ -55,6 +55,7 @@ DiscanX Dominik Schlösser (domschl) double-beep Douglas Matos Gomes (dsmsgms) +Dubslow Eduardo Cáceres (eduherminio) Eelco de Groot (KingDefender) Elvin Liu (solarlight2) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 9061a38421f..9ed9e8e3079 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1100,14 +1100,14 @@ Value Eval::evaluate(const Position& pos) { if (useNNUE && !useClassical) { Value nnue = NNUE::evaluate(pos, true); // NNUE - int scale = 1014 + 21 * pos.non_pawn_material() / 1024; + int scale = 1080 + 110 * pos.non_pawn_material() / 5120; Color stm = pos.side_to_move(); Value optimism = pos.this_thread()->optimism[stm]; Value psq = (stm == WHITE ? 1 : -1) * eg_value(pos.psq_score()); - int complexity = 35 * abs(nnue - psq) / 256; + int complexity = (278 * abs(nnue - psq)) / 256; - optimism = optimism * (32 + complexity) / 32; - v = (nnue * scale + optimism * (scale - 846)) / 1024; + optimism = optimism * (251 + complexity) / 256; + v = (nnue * scale + optimism * (scale - 852)) / 1024; if (pos.is_chess960()) v += fix_FRC(pos); From 8fadbcf1b2c3bc281b2d1efd7992fc9f4c3a38be Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Mon, 30 May 2022 13:30:59 +0300 Subject: [PATCH 0828/1766] Add info about elo gained from some heuristics Add info about qsearch and impact of main and continuation histories. Based on these tests: https://tests.stockfishchess.org/tests/view/62946ffcb0d5a7d1b780fc7e https://tests.stockfishchess.org/tests/view/628facb71e7cd5f299669534 https://tests.stockfishchess.org/tests/view/628eade11e7cd5f299666f2e closes https://github.com/official-stockfish/Stockfish/pull/4041 No functional change. --- src/movepick.h | 2 ++ src/search.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/src/movepick.h b/src/movepick.h index 9a3c279bd2e..6b3c08c7a0d 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -86,6 +86,7 @@ enum StatsType { NoCaptures, Captures }; /// unsuccessful during the current search, and is used for reduction and move /// ordering decisions. It uses 2 tables (one for each color) indexed by /// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards +/// (~11 elo) typedef Stats ButterflyHistory; /// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous @@ -101,6 +102,7 @@ typedef Stats PieceToHistory; /// ContinuationHistory is the combined history of a given pair of moves, usually /// the current one given a previous one. The nested history table is based on /// PieceToHistory instead of ButterflyBoards. +/// (~63 elo) typedef Stats ContinuationHistory; diff --git a/src/search.cpp b/src/search.cpp index 0a4b1a18d67..407cb701863 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1396,6 +1396,7 @@ namespace { // qsearch() is the quiescence search function, which is called by the main search // function with zero depth, or recursively with further decreasing depth per call. + // (~155 elo) template Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { From 653bd0817ceea2980474d76198fe527f8b84bf04 Mon Sep 17 00:00:00 2001 From: candirufish <38038147+candirufish@users.noreply.github.com> Date: Tue, 31 May 2022 21:52:04 +0200 Subject: [PATCH 0829/1766] cutnode and movecount lmr extension simplification Passed STC https://tests.stockfishchess.org/tests/view/6294133cb0d5a7d1b780ece3 LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 41072 W: 11052 L: 10908 D: 19112 Ptnml(0-2): 153, 4324, 11461, 4422, 176 Passed LTC ltc: https://tests.stockfishchess.org/tests/view/62947ae6b0d5a7d1b780fe86 LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 102736 W: 27509 L: 27459 D: 47768 Ptnml(0-2): 98, 9734, 31669, 9754, 113 closes https://github.com/official-stockfish/Stockfish/pull/4045 Bench: 6410652 --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 407cb701863..8ecdbc30905 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1194,8 +1194,7 @@ namespace { // deeper than the first move (this may lead to hidden double extensions). int deeper = r >= -1 ? 0 : moveCount <= 4 ? 2 - : PvNode ? 1 - : cutNode && moveCount <= 8 ? 1 + : PvNode || cutNode ? 1 : 0; Depth d = std::clamp(newDepth - r, 1, newDepth + deeper); From 7f1333ccf89c715933ead72e31233510c6f4d146 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Wed, 1 Jun 2022 01:20:27 +0100 Subject: [PATCH 0830/1766] Blend nnue complexity with classical. Following mstembera's test of the complexity value derived from nnue values, this change blends that idea with the old complexity calculation. STC 10+0.1: LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 42320 W: 11436 L: 11148 D: 19736 Ptnml(0-2): 209, 4585, 11263, 4915, 188 https://tests.stockfishchess.org/tests/live_elo/6295c9239c8c2fcb2bad7fd9 LTC 60+0.6: LLR: 2.98 (-2.94,2.94) <0.50,3.00> Total: 34600 W: 9393 L: 9125 D: 16082 Ptnml(0-2): 32, 3323, 10319, 3597, 29 https://tests.stockfishchess.org/tests/view/6295fd5d9c8c2fcb2bad88cf closes https://github.com/official-stockfish/Stockfish/pull/4046 Bench 6078140 --- src/evaluate.cpp | 11 ++++++----- src/evaluate.h | 2 +- src/nnue/evaluate_nnue.cpp | 7 +++++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 9ed9e8e3079..415c18c5573 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1099,15 +1099,16 @@ Value Eval::evaluate(const Position& pos) { // If result of a classical evaluation is much lower than threshold fall back to NNUE if (useNNUE && !useClassical) { - Value nnue = NNUE::evaluate(pos, true); // NNUE - int scale = 1080 + 110 * pos.non_pawn_material() / 5120; + int complexity; + int scale = 1048 + 109 * pos.non_pawn_material() / 5120; Color stm = pos.side_to_move(); Value optimism = pos.this_thread()->optimism[stm]; Value psq = (stm == WHITE ? 1 : -1) * eg_value(pos.psq_score()); - int complexity = (278 * abs(nnue - psq)) / 256; + Value nnue = NNUE::evaluate(pos, true, &complexity); // NNUE - optimism = optimism * (251 + complexity) / 256; - v = (nnue * scale + optimism * (scale - 852)) / 1024; + complexity = (137 * complexity + 137 * abs(nnue - psq)) / 256; + optimism = optimism * (255 + complexity) / 256; + v = (nnue * scale + optimism * (scale - 848)) / 1024; if (pos.is_chess960()) v += fix_FRC(pos); diff --git a/src/evaluate.h b/src/evaluate.h index f67961a90e5..e79eaea3de3 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -44,7 +44,7 @@ namespace Eval { namespace NNUE { std::string trace(Position& pos); - Value evaluate(const Position& pos, bool adjusted = false); + Value evaluate(const Position& pos, bool adjusted = false, int* complexity = nullptr); void init(); void verify(); diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 9ee599f4aa4..eb6ad71f8d9 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -137,7 +137,7 @@ namespace Stockfish::Eval::NNUE { } // Evaluation function. Perform differential calculation. - Value evaluate(const Position& pos, bool adjusted) { + Value evaluate(const Position& pos, bool adjusted, int* complexity) { // We manually align the arrays on the stack because with gcc < 9.3 // overaligning stack variables with alignas() doesn't work correctly. @@ -161,9 +161,12 @@ namespace Stockfish::Eval::NNUE { const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket); const auto positional = network[bucket]->propagate(transformedFeatures); + if (complexity) + *complexity = abs(psqt - positional) / OutputScale; + // Give more value to positional evaluation when adjusted flag is set if (adjusted) - return static_cast(((128 - delta) * psqt + (128 + delta) * positional) / 128 / OutputScale); + return static_cast(((128 - delta) * psqt + (128 + delta) * positional) / (128 * OutputScale)); else return static_cast((psqt + positional) / OutputScale); } From 90cf8e7d2bde9e480aac4b119ce130e09dd2be39 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Sun, 29 May 2022 19:52:11 -0500 Subject: [PATCH 0831/1766] Remove LMR condition for complex pos Inspired by Kia's similar test: https://tests.stockfishchess.org/tests/view/6292898c1e7cd5f29966fbe0 Passed STC: https://tests.stockfishchess.org/tests/view/62941588b0d5a7d1b780ed4b LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 266872 W: 70850 L: 71033 D: 124989 Ptnml(0-2): 1180, 30114, 70941, 30111, 1090 Passed LTC: https://tests.stockfishchess.org/tests/view/62964a754628d33daa24f062 LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 70160 W: 18756 L: 18662 D: 32742 Ptnml(0-2): 42, 6976, 20950, 7070, 42 closes https://github.com/official-stockfish/Stockfish/pull/4047 Bench 6237567 --- src/search.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 8ecdbc30905..ea1e63fea0a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1167,11 +1167,6 @@ namespace { if (ttCapture) r++; - // Decrease reduction at PvNodes if bestvalue - // is vastly different from static evaluation - if (PvNode && !ss->inCheck && abs(ss->staticEval - bestValue) > 250) - r--; - // Decrease reduction for PvNodes based on depth if (PvNode) r -= 1 + 15 / ( 3 + depth ); From 809849fa275512225d19e280809f227990d97eb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bo=C5=A1tjan=20Mejak?= Date: Tue, 31 May 2022 20:25:57 +0200 Subject: [PATCH 0832/1766] Wording of help output and comments. Improved the output text that is diplayed when executing the 'help' command. Also, some comments were fixed along the way. closes https://github.com/official-stockfish/Stockfish/pull/4048 closes https://github.com/official-stockfish/Stockfish/pull/4044 No functional change --- AUTHORS | 1 + src/uci.cpp | 126 ++++++++++++++++++++++++++-------------------------- src/uci.h | 6 +-- 3 files changed, 67 insertions(+), 66 deletions(-) diff --git a/AUTHORS b/AUTHORS index 715f83b1974..fc885acbf9c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -35,6 +35,7 @@ Ben Chaney (Chaneybenjamini) Ben Koshy (BKSpurgeon) Bill Henry (VoyagerOne) Bojun Guo (noobpwnftw, Nooby) +Boštjan Mejak (PedanticHacker) braich Brian Sheppard (SapphireBrand, briansheppard-toast) Bruno de Melo Costa (BM123499) diff --git a/src/uci.cpp b/src/uci.cpp index c28cf6d110f..c0bacfafefe 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -40,14 +40,14 @@ extern vector setup_bench(const Position&, istream&); namespace { - // FEN string of the initial position, normal chess + // FEN string for the initial position in standard chess const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; - // position() is called when engine receives the "position" UCI command. - // The function sets up the position described in the given FEN string ("fen") - // or the starting position ("startpos") and then makes the moves given in the - // following move list ("moves"). + // position() is called when the engine receives the "position" UCI command. + // It sets up the position that is described in the given FEN string ("fen") or + // the initial position ("startpos") and then makes the moves given in the following + // move list ("moves"). void position(Position& pos, istringstream& is, StateListPtr& states) { @@ -59,7 +59,7 @@ namespace { if (token == "startpos") { fen = StartFEN; - is >> token; // Consume "moves" token if any + is >> token; // Consume the "moves" token, if any } else if (token == "fen") while (is >> token && token != "moves") @@ -67,10 +67,10 @@ namespace { else return; - states = StateListPtr(new std::deque(1)); // Drop old and create a new one + states = StateListPtr(new std::deque(1)); // Drop the old state and create a new one pos.set(fen, Options["UCI_Chess960"], &states->back(), Threads.main()); - // Parse move list (if any) + // Parse the move list, if any while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE) { states->emplace_back(); @@ -78,8 +78,8 @@ namespace { } } - // trace_eval() prints the evaluation for the current position, consistent with the UCI - // options set so far. + // trace_eval() prints the evaluation of the current position, consistent with + // the UCI options set so far. void trace_eval(Position& pos) { @@ -93,20 +93,20 @@ namespace { } - // setoption() is called when engine receives the "setoption" UCI command. The - // function updates the UCI option ("name") to the given value ("value"). + // setoption() is called when the engine receives the "setoption" UCI command. + // The function updates the UCI option ("name") to the given value ("value"). void setoption(istringstream& is) { string token, name, value; - is >> token; // Consume "name" token + is >> token; // Consume the "name" token - // Read option name (can contain spaces) + // Read the option name (can contain spaces) while (is >> token && token != "value") name += (name.empty() ? "" : " ") + token; - // Read option value (can contain spaces) + // Read the option value (can contain spaces) while (is >> token) value += (value.empty() ? "" : " ") + token; @@ -117,9 +117,9 @@ namespace { } - // go() is called when engine receives the "go" UCI command. The function sets - // the thinking time and other parameters from the input string, then starts - // the search. + // go() is called when the engine receives the "go" UCI command. The function + // sets the thinking time and other parameters from the input string, then starts + // with a search. void go(Position& pos, istringstream& is, StateListPtr& states) { @@ -127,7 +127,7 @@ namespace { string token; bool ponderMode = false; - limits.startTime = now(); // As early as possible! + limits.startTime = now(); // The search starts as early as possible while (is >> token) if (token == "searchmoves") // Needs to be the last command on the line @@ -151,9 +151,9 @@ namespace { } - // bench() is called when engine receives the "bench" command. Firstly - // a list of UCI commands is setup according to bench parameters, then - // it is run one by one printing a summary at the end. + // bench() is called when the engine receives the "bench" command. + // Firstly, a list of UCI commands is set up according to the bench + // parameters, then it is run one by one, printing a summary at the end. void bench(Position& pos, istream& args, StateListPtr& states) { @@ -184,12 +184,12 @@ namespace { } else if (token == "setoption") setoption(is); else if (token == "position") position(pos, is, states); - else if (token == "ucinewgame") { Search::clear(); elapsed = now(); } // Search::clear() may take some while + else if (token == "ucinewgame") { Search::clear(); elapsed = now(); } // Search::clear() may take a while } elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero' - dbg_print(); // Just before exiting + dbg_print(); cerr << "\n===========================" << "\nTotal time (ms) : " << elapsed @@ -197,36 +197,36 @@ namespace { << "\nNodes/second : " << 1000 * nodes / elapsed << endl; } - // The win rate model returns the probability (per mille) of winning given an eval - // and a game-ply. The model fits rather accurately the LTC fishtest statistics. + // The win rate model returns the probability of winning (in per mille units) given an + // eval and a game ply. It fits the LTC fishtest statistics rather accurately. int win_rate_model(Value v, int ply) { - // The model captures only up to 240 plies, so limit input (and rescale) + // The model only captures up to 240 plies, so limit the input and then rescale double m = std::min(240, ply) / 64.0; - // Coefficients of a 3rd order polynomial fit based on fishtest data - // for two parameters needed to transform eval to the argument of a - // logistic function. + // The coefficients of a third-order polynomial fit is based on the fishtest data + // for two parameters that need to transform eval to the argument of a logistic + // function. double as[] = {-1.17202460e-01, 5.94729104e-01, 1.12065546e+01, 1.22606222e+02}; double bs[] = {-1.79066759, 11.30759193, -17.43677612, 36.47147479}; double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3]; double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3]; - // Transform eval to centipawns with limited range + // Transform the eval to centipawns with limited range double x = std::clamp(double(100 * v) / PawnValueEg, -2000.0, 2000.0); - // Return win rate in per mille (rounded to nearest) + // Return the win rate in per mille units rounded to the nearest value return int(0.5 + 1000 / (1 + std::exp((a - x) / b))); } } // namespace -/// UCI::loop() waits for a command from stdin, parses it and calls the appropriate -/// function. Also intercepts EOF from stdin to ensure gracefully exiting if the -/// GUI dies unexpectedly. When called with some command line arguments, e.g. to -/// run 'bench', once the command is executed the function returns immediately. -/// In addition to the UCI ones, also some additional debug commands are supported. +/// UCI::loop() waits for a command from the stdin, parses it and then calls the appropriate +/// function. It also intercepts an end-of-file (EOF) indication from the stdin to ensure a +/// graceful exit if the GUI dies unexpectedly. When called with some command-line arguments, +/// like running 'bench', the function returns immediately after the command is executed. +/// In addition to the UCI ones, some additional debug commands are also supported. void UCI::loop(int argc, char* argv[]) { @@ -240,24 +240,24 @@ void UCI::loop(int argc, char* argv[]) { cmd += std::string(argv[i]) + " "; do { - if (argc == 1 && !getline(cin, cmd)) // Block here waiting for input or EOF + if (argc == 1 && !getline(cin, cmd)) // Wait for an input or an end-of-file (EOF) indication cmd = "quit"; istringstream is(cmd); - token.clear(); // Avoid a stale if getline() returns empty or blank line + token.clear(); // Avoid a stale if getline() returns nothing or a blank line is >> skipws >> token; if ( token == "quit" || token == "stop") Threads.stop = true; - // The GUI sends 'ponderhit' to tell us the user has played the expected move. - // So 'ponderhit' will be sent if we were told to ponder on the same move the - // user has played. We should continue searching but switch from pondering to - // normal search. + // The GUI sends 'ponderhit' to tell that the user has played the expected move. + // So, 'ponderhit' is sent if pondering was done on the same move that the user + // has played. The search should continue, but should also switch from pondering + // to the normal search. else if (token == "ponderhit") - Threads.main()->ponder = false; // Switch to normal search + Threads.main()->ponder = false; // Switch to the normal search else if (token == "uci") sync_cout << "id name " << engine_info(true) @@ -270,8 +270,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. - // Do not use these commands during a search! + // Add custom non-UCI commands, mainly for debugging purposes. + // These commands must not be used 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; @@ -286,24 +286,24 @@ void UCI::loop(int argc, char* argv[]) { Eval::NNUE::save_eval(filename); } else if (token == "--help" || token == "help" || token == "--license" || token == "license") - sync_cout << "\nStockfish is a powerful chess engine and free software licensed under the GNU GPLv3." - "\nStockfish is normally used with a separate graphical user interface (GUI)." - "\nStockfish implements the universal chess interface (UCI) to exchange information." - "\nFor further information see https://github.com/official-stockfish/Stockfish#readme" - "\nor the corresponding README.md and Copying.txt files distributed with this program.\n" << sync_endl; + sync_cout << "\nStockfish is a powerful chess engine for playing and analyzing." + "\nIt is released as free software licensed under the GNU GPLv3 License." + "\nStockfish is normally used with a graphical user interface (GUI) and implements" + "\nthe Universal Chess Interface (UCI) protocol to communicate with a GUI, an API, etc." + "\nFor any further information, visit https://github.com/official-stockfish/Stockfish#readme" + "\nor read the corresponding README.md and Copying.txt files distributed along with this program.\n" << sync_endl; else if (!token.empty() && token[0] != '#') sync_cout << "Unknown command: '" << cmd << "'. Type help for more information." << sync_endl; - } while (token != "quit" && argc == 1); // Command line args are one-shot + } while (token != "quit" && argc == 1); // The command-line arguments are one-shot } -/// UCI::value() converts a Value to a string suitable for use with the UCI -/// protocol specification: +/// UCI::value() converts a Value to a string by adhering to the UCI protocol specification: /// /// cp The score from the engine's point of view in centipawns. -/// mate Mate in y moves, not plies. If the engine is getting mated -/// use negative values for y. +/// mate Mate in 'y' moves (not plies). If the engine is getting mated, +/// uses negative values for 'y'. string UCI::value(Value v) { @@ -320,8 +320,8 @@ string UCI::value(Value v) { } -/// UCI::wdl() report WDL statistics given an evaluation and a game ply, based on -/// data gathered for fishtest LTC games. +/// UCI::wdl() reports the win-draw-loss (WDL) statistics given an evaluation +/// and a game ply based on the data gathered for fishtest LTC games. string UCI::wdl(Value v, int ply) { @@ -344,9 +344,9 @@ std::string UCI::square(Square s) { /// UCI::move() converts a Move to a string in coordinate notation (g1f3, a7a8q). -/// The only special case is castling, where we print in the e1g1 notation in -/// normal chess mode, and in e1h1 notation in chess960 mode. Internally all -/// castling moves are always encoded as 'king captures rook'. +/// The only special case is castling where the e1g1 notation is printed in +/// standard chess mode and in e1h1 notation it is printed in Chess960 mode. +/// Internally, all castling moves are always encoded as 'king captures rook'. string UCI::move(Move m, bool chess960) { @@ -376,8 +376,8 @@ string UCI::move(Move m, bool chess960) { Move UCI::to_move(const Position& pos, string& str) { - if (str.length() == 5) // Junior could send promotion piece in uppercase - str[4] = char(tolower(str[4])); + if (str.length() == 5) + str[4] = char(tolower(str[4])); // The promotion piece character must be lowercased for (const auto& m : MoveList(pos)) if (str == UCI::move(m, pos.is_chess960())) diff --git a/src/uci.h b/src/uci.h index 5bb24a4ec65..76a893f90c4 100644 --- a/src/uci.h +++ b/src/uci.h @@ -32,15 +32,15 @@ namespace UCI { class Option; -/// Custom comparator because UCI options should be case insensitive +/// Define a custom comparator, because the UCI options should be case-insensitive struct CaseInsensitiveLess { bool operator() (const std::string&, const std::string&) const; }; -/// Our options container is actually a std::map +/// The options container is defined as a std::map typedef std::map OptionsMap; -/// Option class implements an option as defined by UCI protocol +/// The Option class implements each option as specified by the UCI protocol class Option { typedef void (*OnChange)(const Option&); From 00297cfef0ad604a61aaff2747dea029fa6aa733 Mon Sep 17 00:00:00 2001 From: candirufish <38038147+candirufish@users.noreply.github.com> Date: Tue, 7 Jun 2022 07:21:43 +0200 Subject: [PATCH 0833/1766] Use qsearch on step 11 if depth is equal to or below 0 larger reduction of depth if no TT entry is found, and go in qsearch as needed. stc: https://tests.stockfishchess.org/tests/view/629dfacd593a4a9b6482db72 LLR: 2.93 (-2.94,2.94) <0.00,2.50> Total: 31920 W: 8591 L: 8322 D: 15007 Ptnml(0-2): 127, 3551, 8376, 3738, 168 ltc: https://tests.stockfishchess.org/tests/view/629e304e593a4a9b6482e451 LLR: 2.95 (-2.94,2.94) <0.50,3.00> Total: 17488 W: 4842 L: 4614 D: 8032 Ptnml(0-2): 13, 1670, 5151, 1896, 14 closes https://github.com/official-stockfish/Stockfish/pull/4056 Bench: 5870283 --- src/search.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ea1e63fea0a..17ac05c4c9a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -908,14 +908,17 @@ namespace { ss->ttPv = ttPv; } - // Step 11. If the position is not in TT, decrease depth by 2 or 1 depending on node type (~3 Elo) - if ( PvNode - && depth >= 3 + // Step 11. If the position is not in TT, decrease depth by 3. + // Use qsearch if depth is equal or below zero (~4 Elo) + if ( PvNode && !ttMove) - depth -= 2; + depth -= 3; - if ( cutNode - && depth >= 8 + if (depth <= 0) + return qsearch(pos, ss, alpha, beta); + + if ( cutNode + && depth >= 8 && !ttMove) depth--; From d54b85b4bdcd2354903a31f4f35cca6b3742a7d9 Mon Sep 17 00:00:00 2001 From: ppigazzini Date: Mon, 13 Jun 2022 22:08:01 +0200 Subject: [PATCH 0834/1766] Restore NDKv21 for GitHub Actions GitHub updated the versions of NDK installed on the Actions runners breaking the ARM tests. Restore the NDKv21 using the GitHub suggested mitigation, see: https://github.com/actions/virtual-environments/issues/5595 closes https://github.com/official-stockfish/Stockfish/pull/4077 No functional change --- .github/workflows/stockfish.yml | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index 33560d52b60..782e3f2b442 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -5,7 +5,6 @@ on: - master - tools - github_ci - - github_ci_armv7 pull_request: branches: - master @@ -269,6 +268,12 @@ jobs: - name: Test armv8 build if: ${{ matrix.config.run_armv8_tests }} run: | + ANDROID_ROOT=/usr/local/lib/android + ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk + SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager + echo "y" | $SDKMANAGER "ndk;21.4.7075529" + ANDROID_NDK_ROOT=${ANDROID_SDK_ROOT}/ndk-bundle + ln -sfn $ANDROID_SDK_ROOT/ndk/21.4.7075529 $ANDROID_NDK_ROOT export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH export LDFLAGS="-static -Wno-unused-command-line-argument" make clean @@ -280,6 +285,12 @@ jobs: - name: Test armv7 build if: ${{ matrix.config.run_armv7_tests }} run: | + ANDROID_ROOT=/usr/local/lib/android + ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk + SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager + echo "y" | $SDKMANAGER "ndk;21.4.7075529" + ANDROID_NDK_ROOT=${ANDROID_SDK_ROOT}/ndk-bundle + ln -sfn $ANDROID_SDK_ROOT/ndk/21.4.7075529 $ANDROID_NDK_ROOT export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH export LDFLAGS="-static -Wno-unused-command-line-argument" make clean @@ -289,6 +300,12 @@ jobs: - name: Test armv7-neon build if: ${{ matrix.config.run_armv7_tests }} run: | + ANDROID_ROOT=/usr/local/lib/android + ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk + SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager + echo "y" | $SDKMANAGER "ndk;21.4.7075529" + ANDROID_NDK_ROOT=${ANDROID_SDK_ROOT}/ndk-bundle + ln -sfn $ANDROID_SDK_ROOT/ndk/21.4.7075529 $ANDROID_NDK_ROOT export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH export LDFLAGS="-static -Wno-unused-command-line-argument" make clean From 2d5dcf3d18842dc2cfb17e3dd64e0812bf01ab65 Mon Sep 17 00:00:00 2001 From: mstembera Date: Sun, 5 Jun 2022 19:32:05 -0700 Subject: [PATCH 0835/1766] Minor simplifications and cleanup in search STC: https://tests.stockfishchess.org/tests/view/629d6775593a4a9b6482c1ec LLR: 2.93 (-2.94,2.94) <-2.25,0.25> Total: 77416 W: 20683 L: 20589 D: 36144 Ptnml(0-2): 317, 8690, 20620, 8744, 337 LTC: https://tests.stockfishchess.org/tests/view/629db4be593a4a9b6482ceef LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 106544 W: 28752 L: 28705 D: 49087 Ptnml(0-2): 97, 10692, 31641, 10751, 91 closes https://github.com/official-stockfish/Stockfish/pull/4059 Bench: 5913510 --- src/search.cpp | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 17ac05c4c9a..1ab71a5620a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -85,8 +85,8 @@ namespace { } // Add a small random component to draw evaluations to avoid 3-fold blindness - Value value_draw(Thread* thisThread) { - return VALUE_DRAW + Value(2 * (thisThread->nodes & 1) - 1); + Value value_draw(const Thread* thisThread) { + return VALUE_DRAW - 1 + Value(thisThread->nodes & 0x2); } // Skill structure is used to implement strength limit. If we have an uci_elo then @@ -116,7 +116,7 @@ namespace { Value value_to_tt(Value v, int ply); Value value_from_tt(Value v, int ply, int r50c); - void update_pv(Move* pv, Move move, Move* childPv); + void update_pv(Move* pv, Move move, const Move* childPv); void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus); void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus); void update_all_stats(const Position& pos, Stack* ss, Move bestMove, Value bestValue, Value beta, Square prevSq, @@ -635,10 +635,9 @@ namespace { // At non-PV nodes we check for an early TT cutoff if ( !PvNode && ss->ttHit - && tte->depth() > depth - (thisThread->id() % 2 == 1) + && tte->depth() > depth - ((int)thisThread->id() & 0x1) && ttValue != VALUE_NONE // Possible in case of TT access race - && (ttValue >= beta ? (tte->bound() & BOUND_LOWER) - : (tte->bound() & BOUND_UPPER))) + && (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER))) { // If ttMove is quiet, update move sorting heuristics on TT hit (~1 Elo) if (ttMove) @@ -896,12 +895,11 @@ namespace { if (value >= probCutBeta) { // if transposition table doesn't have equal or more deep info write probCut data into it - if ( !(ss->ttHit - && tte->depth() >= depth - 3 - && ttValue != VALUE_NONE)) + if (!( ss->ttHit + && tte->depth() >= depth - 3 + && ttValue != VALUE_NONE)) tte->save(posKey, value_to_tt(value, ss->ply), ttPv, - BOUND_LOWER, - depth - 3, move, ss->staticEval); + BOUND_LOWER, depth - 3, move, ss->staticEval); return value; } } @@ -1172,7 +1170,7 @@ namespace { // Decrease reduction for PvNodes based on depth if (PvNode) - r -= 1 + 15 / ( 3 + depth ); + r -= 1 + 15 / (3 + depth); // Increase reduction if next ply has a lot of fail high else reset count to 0 if ((ss+1)->cutoffCnt > 3 && !PvNode) @@ -1450,8 +1448,7 @@ namespace { && ss->ttHit && tte->depth() >= ttDepth && ttValue != VALUE_NONE // Only in case of TT access race - && (ttValue >= beta ? (tte->bound() & BOUND_LOWER) - : (tte->bound() & BOUND_UPPER))) + && (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER))) return ttValue; // Evaluate the position statically @@ -1567,14 +1564,14 @@ namespace { [to_sq(move)]; // Continuation history based pruning (~2 Elo) - if ( !capture + if ( !capture && bestValue > VALUE_TB_LOSS_IN_MAX_PLY && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < CounterMovePruneThreshold && (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < CounterMovePruneThreshold) continue; // movecount pruning for quiet check evasions - if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY + if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY && quietCheckEvasions > 1 && !capture && ss->inCheck) @@ -1675,7 +1672,7 @@ namespace { // update_pv() adds current move and appends child pv[] - void update_pv(Move* pv, Move move, Move* childPv) { + void update_pv(Move* pv, Move move, const Move* childPv) { for (*pv++ = move; childPv && *childPv != MOVE_NONE; ) *pv++ = *childPv++; @@ -1688,19 +1685,18 @@ namespace { void update_all_stats(const Position& pos, Stack* ss, Move bestMove, Value bestValue, Value beta, Square prevSq, Move* quietsSearched, int quietCount, Move* capturesSearched, int captureCount, Depth depth) { - int bonus1, bonus2; Color us = pos.side_to_move(); Thread* thisThread = pos.this_thread(); CapturePieceToHistory& captureHistory = thisThread->captureHistory; Piece moved_piece = pos.moved_piece(bestMove); PieceType captured = type_of(pos.piece_on(to_sq(bestMove))); - - bonus1 = stat_bonus(depth + 1); - bonus2 = bestValue > beta + PawnValueMg ? bonus1 // larger bonus - : stat_bonus(depth); // smaller bonus + int bonus1 = stat_bonus(depth + 1); if (!pos.capture(bestMove)) { + int bonus2 = bestValue > beta + PawnValueMg ? bonus1 // larger bonus + : stat_bonus(depth); // smaller bonus + // Increase stats for the best move in case it was a quiet move update_quiet_stats(pos, ss, bestMove, bonus2); From 6edc29d720b43383a04bd2208e9666a6f3173a64 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Thu, 9 Jun 2022 10:37:24 -0300 Subject: [PATCH 0836/1766] Simplify away condition in ttSave in probCut Remove condition for tte->save in probCut so it always saves on probCut cutoff. STC: LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 47848 W: 12921 L: 12782 D: 22145 Ptnml(0-2): 207, 5340, 12715, 5431, 231 https://tests.stockfishchess.org/tests/view/62a1f7c87bd8e641e44436f7 LTC: LLR: 2.97 (-2.94,2.94) <-2.25,0.25> Total: 132736 W: 35895 L: 35881 D: 60960 Ptnml(0-2): 109, 13384, 39360, 13414, 101 https://tests.stockfishchess.org/tests/view/62a2421a7bd8e641e444434f closes https://github.com/official-stockfish/Stockfish/pull/4069 bench: 5845802 --- src/search.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 1ab71a5620a..466c9ec10eb 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -894,12 +894,8 @@ namespace { if (value >= probCutBeta) { - // if transposition table doesn't have equal or more deep info write probCut data into it - if (!( ss->ttHit - && tte->depth() >= depth - 3 - && ttValue != VALUE_NONE)) - tte->save(posKey, value_to_tt(value, ss->ply), ttPv, - BOUND_LOWER, depth - 3, move, ss->staticEval); + // Save ProbCut data into transposition table + tte->save(posKey, value_to_tt(value, ss->ply), ttPv, BOUND_LOWER, depth - 3, move, ss->staticEval); return value; } } From 4d6a11a04cd8447b56447a9433c0cfb547a00643 Mon Sep 17 00:00:00 2001 From: bmc4 Date: Thu, 16 Jun 2022 07:19:39 -0300 Subject: [PATCH 0837/1766] Don't change ttPv at probCut STC: LLR: 2.96 (-2.94,2.94) <-2.25,0.25> Total: 35672 W: 9618 L: 9462 D: 16592 Ptnml(0-2): 151, 3890, 9601, 4040, 154 https://tests.stockfishchess.org/tests/view/62ab03f750949cfc241b1965 LTC: LLR: 2.93 (-2.94,2.94) <-2.25,0.25> Total: 54160 W: 14626 L: 14511 D: 25023 Ptnml(0-2): 42, 5414, 16056, 5523, 45 https://tests.stockfishchess.org/tests/view/62ab5e6fd89eb6cf1e071b87 closes https://github.com/official-stockfish/Stockfish/pull/4088 bench: 5798229 --- src/search.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 466c9ec10eb..6169e0601a0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -867,8 +867,6 @@ namespace { assert(probCutBeta < VALUE_INFINITE); MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, depth - 3, &captureHistory); - bool ttPv = ss->ttPv; - ss->ttPv = false; while ((move = mp.next_move()) != MOVE_NONE) if (move != excludedMove && pos.legal(move)) @@ -895,11 +893,10 @@ namespace { if (value >= probCutBeta) { // Save ProbCut data into transposition table - tte->save(posKey, value_to_tt(value, ss->ply), ttPv, BOUND_LOWER, depth - 3, move, ss->staticEval); + tte->save(posKey, value_to_tt(value, ss->ply), ss->ttPv, BOUND_LOWER, depth - 3, move, ss->staticEval); return value; } } - ss->ttPv = ttPv; } // Step 11. If the position is not in TT, decrease depth by 3. From 5304b561ab96ae1c025d98cf4a6d138daa11374d Mon Sep 17 00:00:00 2001 From: Dubslow Date: Wed, 15 Jun 2022 17:30:32 -0500 Subject: [PATCH 0838/1766] LMR: remove `deeper` ...apparently it wasn't doing much anymore. inspired by rufish's recent attempts to improve this. passed STC: https://tests.stockfishchess.org/tests/view/62abca2cd89eb6cf1e072c04 LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 85576 W: 22766 L: 22683 D: 40127 Ptnml(0-2): 362, 9607, 22741, 9742, 336 passed LTC: https://tests.stockfishchess.org/tests/view/62ac90ffd89eb6cf1e07488f LLR: 2.93 (-2.94,2.94) <-2.25,0.25> Total: 48248 W: 13018 L: 12896 D: 22334 Ptnml(0-2): 32, 4773, 14400, 4879, 40 closes https://github.com/official-stockfish/Stockfish/pull/4088 bench 5578988 --- src/search.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 6169e0601a0..37277ec418b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1178,15 +1178,10 @@ namespace { // Decrease/increase reduction for moves with a good/bad history (~30 Elo) r -= ss->statScore / 15914; - // In general we want to cap the LMR depth search at newDepth. But if reductions - // are really negative and movecount is low, we allow this move to be searched - // deeper than the first move (this may lead to hidden double extensions). - int deeper = r >= -1 ? 0 - : moveCount <= 4 ? 2 - : PvNode || cutNode ? 1 - : 0; - - Depth d = std::clamp(newDepth - r, 1, newDepth + deeper); + // In general we want to cap the LMR depth search at newDepth, but when + // reduction is negative, we allow this move a limited search extension + // beyond the first move depth. This may lead to hidden double extensions. + Depth d = std::clamp(newDepth - r, 1, newDepth + 1); value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); From 442c40b43de8ede1e424efa674c8d45322e3b43c Mon Sep 17 00:00:00 2001 From: Dubslow Date: Fri, 10 Jun 2022 08:11:02 -0500 Subject: [PATCH 0839/1766] Use NNUE complexity in search, retune related parameters This builds on ideas of xoto10 and mstembera to use more output from NNUE in the search algorithm. passed STC: https://tests.stockfishchess.org/tests/view/62ae454fe7ee5525ef88a957 LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 89208 W: 24127 L: 23753 D: 41328 Ptnml(0-2): 400, 9886, 23642, 10292, 384 passed LTC: https://tests.stockfishchess.org/tests/view/62acc6ddd89eb6cf1e0750a1 LLR: 2.93 (-2.94,2.94) <0.50,3.00> Total: 56352 W: 15430 L: 15115 D: 25807 Ptnml(0-2): 44, 5501, 16782, 5794, 55 closes https://github.com/official-stockfish/Stockfish/pull/4088 bench 5332964 --- src/evaluate.cpp | 32 ++++++++++++++++++++------------ src/evaluate.h | 2 +- src/nnue/evaluate_nnue.cpp | 4 ++-- src/position.h | 5 +++++ src/search.cpp | 20 ++++++++++---------- 5 files changed, 38 insertions(+), 25 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 415c18c5573..6d2e8da8d40 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1080,35 +1080,39 @@ namespace { /// evaluate() is the evaluator for the outer world. It returns a static /// evaluation of the position from the point of view of the side to move. -Value Eval::evaluate(const Position& pos) { +Value Eval::evaluate(const Position& pos, int* complexity) { Value v; + Color stm = pos.side_to_move(); + Value psq = pos.psq_eg_stm(); // Deciding between classical and NNUE eval (~10 Elo): for high PSQ imbalance we use classical, // but we switch to NNUE during long shuffling or with high material on the board. - bool useClassical = (pos.this_thread()->depth > 9 || pos.count() > 7) && - abs(eg_value(pos.psq_score())) * 5 > (856 + pos.non_pawn_material() / 64) * (10 + pos.rule50_count()); + bool useClassical = (pos.this_thread()->depth > 9 || pos.count() > 7) + && abs(psq) * 5 > (856 + pos.non_pawn_material() / 64) * (10 + pos.rule50_count()); // Deciding between classical and NNUE eval (~10 Elo): for high PSQ imbalance we use classical, // but we switch to NNUE during long shuffling or with high material on the board. if (!useNNUE || useClassical) { - v = Evaluation(pos).value(); // classical + v = Evaluation(pos).value(); useClassical = abs(v) >= 297; } // If result of a classical evaluation is much lower than threshold fall back to NNUE if (useNNUE && !useClassical) { - int complexity; - int scale = 1048 + 109 * pos.non_pawn_material() / 5120; - Color stm = pos.side_to_move(); + int nnueComplexity; + int scale = 1092 + 106 * pos.non_pawn_material() / 5120; Value optimism = pos.this_thread()->optimism[stm]; - Value psq = (stm == WHITE ? 1 : -1) * eg_value(pos.psq_score()); - Value nnue = NNUE::evaluate(pos, true, &complexity); // NNUE - complexity = (137 * complexity + 137 * abs(nnue - psq)) / 256; - optimism = optimism * (255 + complexity) / 256; - v = (nnue * scale + optimism * (scale - 848)) / 1024; + Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); + // Blend nnue complexity with (semi)classical complexity + nnueComplexity = (104 * nnueComplexity + 131 * abs(nnue - psq)) / 256; + if (complexity) // Return hybrid NNUE complexity to caller + *complexity = nnueComplexity; + + optimism = optimism * (269 + nnueComplexity) / 256; + v = (nnue * scale + optimism * (scale - 754)) / 1024; if (pos.is_chess960()) v += fix_FRC(pos); @@ -1120,6 +1124,10 @@ Value Eval::evaluate(const Position& pos) { // Guarantee evaluation does not hit the tablebase range v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); + // When not using NNUE, return classical complexity to caller + if (complexity && (!useNNUE || useClassical)) + *complexity = abs(v - psq); + return v; } diff --git a/src/evaluate.h b/src/evaluate.h index e79eaea3de3..e25bd52e849 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -31,7 +31,7 @@ class Position; namespace Eval { std::string trace(Position& pos); - Value evaluate(const Position& pos); + Value evaluate(const Position& pos, int* complexity = nullptr); extern bool useNNUE; extern std::string currentEvalFileName; diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index eb6ad71f8d9..ba2ed36746c 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -143,7 +143,7 @@ namespace Stockfish::Eval::NNUE { // overaligning stack variables with alignas() doesn't work correctly. constexpr uint64_t alignment = CacheLineSize; - int delta = 10 - pos.non_pawn_material() / 1515; + int delta = 24 - pos.non_pawn_material() / 9560; #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) TransformedFeatureType transformedFeaturesUnaligned[ @@ -166,7 +166,7 @@ namespace Stockfish::Eval::NNUE { // Give more value to positional evaluation when adjusted flag is set if (adjusted) - return static_cast(((128 - delta) * psqt + (128 + delta) * positional) / (128 * OutputScale)); + return static_cast(((1024 - delta) * psqt + (1024 + delta) * positional) / (1024 * OutputScale)); else return static_cast((psqt + positional) / OutputScale); } diff --git a/src/position.h b/src/position.h index e558581850f..510875d86d3 100644 --- a/src/position.h +++ b/src/position.h @@ -161,6 +161,7 @@ class Position { bool has_repeated() const; int rule50_count() const; Score psq_score() const; + Value psq_eg_stm() const; Value non_pawn_material(Color c) const; Value non_pawn_material() const; @@ -342,6 +343,10 @@ inline Score Position::psq_score() const { return psq; } +inline Value Position::psq_eg_stm() const { + return (sideToMove == WHITE ? 1 : -1) * eg_value(psq); +} + inline Value Position::non_pawn_material(Color c) const { return st->nonPawnMaterial[c]; } diff --git a/src/search.cpp b/src/search.cpp index 37277ec418b..f11d76be715 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -307,7 +307,7 @@ void Thread::search() { multiPV = std::min(multiPV, rootMoves.size()); - complexityAverage.set(202, 1); + complexityAverage.set(174, 1); trend = SCORE_ZERO; optimism[ us] = Value(39); @@ -472,7 +472,7 @@ void Thread::search() { double reduction = (1.56 + mainThread->previousTimeReduction) / (2.20 * timeReduction); double bestMoveInstability = 1 + 1.7 * totBestMoveChanges / Threads.size(); int complexity = mainThread->complexityAverage.value(); - double complexPosition = std::clamp(1.0 + (complexity - 326) / 1618.1, 0.5, 1.5); + double complexPosition = std::clamp(1.0 + (complexity - 277) / 1819, 0.5, 1.5); double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability * complexPosition; @@ -736,7 +736,9 @@ namespace { // Never assume anything about values stored in TT ss->staticEval = eval = tte->eval(); if (eval == VALUE_NONE) - ss->staticEval = eval = evaluate(pos); + ss->staticEval = eval = evaluate(pos, &complexity); + else // Fall back to (semi)classical complexity for TT hits, the NNUE complexity is lost + complexity = abs(ss->staticEval - pos.psq_eg_stm()); // Randomize draw evaluation if (eval == VALUE_DRAW) @@ -749,13 +751,15 @@ namespace { } else { - ss->staticEval = eval = evaluate(pos); + ss->staticEval = eval = evaluate(pos, &complexity); // Save static evaluation into transposition table if (!excludedMove) tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); } + thisThread->complexityAverage.update(complexity); + // Use static evaluation difference to improve quiet move ordering (~3 Elo) if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture) { @@ -770,11 +774,7 @@ namespace { improvement = (ss-2)->staticEval != VALUE_NONE ? ss->staticEval - (ss-2)->staticEval : (ss-4)->staticEval != VALUE_NONE ? ss->staticEval - (ss-4)->staticEval : 175; - improving = improvement > 0; - complexity = abs(ss->staticEval - (us == WHITE ? eg_value(pos.psq_score()) : -eg_value(pos.psq_score()))); - - thisThread->complexityAverage.update(complexity); // Step 7. Razoring. // If eval is really low check with qsearch if it can exceed alpha, if it can't, @@ -803,7 +803,7 @@ namespace { && (ss-1)->statScore < 14695 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 15 * depth - improvement / 15 + 198 + complexity / 28 + && ss->staticEval >= beta - 15 * depth - improvement / 15 + 201 + complexity / 24 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -811,7 +811,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth, eval and complexity of position - Depth R = std::min(int(eval - beta) / 147, 5) + depth / 3 + 4 - (complexity > 753); + Depth R = std::min(int(eval - beta) / 147, 5) + depth / 3 + 4 - (complexity > 650); ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; From 85f8ee6199f8578fbc082fc0f37e1985813e637a Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 1 Jul 2022 15:07:49 +0200 Subject: [PATCH 0840/1766] Update default net to nn-3c0054ea9860.nnu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First things first... this PR is being made from court. Today, Tord and Stéphane, with broad support of the developer community are defending their complaint, filed in Munich, against ChessBase. With their products Houdini 6 and Fat Fritz 2, both Stockfish derivatives, ChessBase violated repeatedly the Stockfish GPLv3 license. Tord and Stéphane have terminated their license with ChessBase permanently. Today we have the opportunity to present our evidence to the judge and enforce that termination. To read up, have a look at our blog post https://stockfishchess.org/blog/2022/public-court-hearing-soon/ and https://stockfishchess.org/blog/2021/our-lawsuit-against-chessbase/ This PR introduces a net trained with an enhanced data set and a modified loss function in the trainer. A slight adjustment for the scaling was needed to get a pass on standard chess. passed STC: https://tests.stockfishchess.org/tests/view/62c0527a49b62510394bd610 LLR: 2.94 (-2.94,2.94) <0.00,2.50> Total: 135008 W: 36614 L: 36152 D: 62242 Ptnml(0-2): 640, 15184, 35407, 15620, 653 passed LTC: https://tests.stockfishchess.org/tests/view/62c17e459e7d9997a12d458e LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 28864 W: 8007 L: 7749 D: 13108 Ptnml(0-2): 47, 2810, 8466, 3056, 53 Local testing at a fixed 25k nodes resulted in Test run1026/easy_train_data/experiments/experiment_2/training/run_0/nn-epoch799.nnue localElo: 4.2 +- 1.6 The real strength of the net is in FRC and DFRC chess where it gains significantly. Tested at STC with slightly different scaling: FRC: https://tests.stockfishchess.org/tests/view/62c13a4002ba5d0a774d20d4 Elo: 29.78 +-3.4 (95%) LOS: 100.0% Total: 10000 W: 2007 L: 1152 D: 6841 Ptnml(0-2): 31, 686, 2804, 1355, 124 nElo: 59.24 +-6.9 (95%) PairsRatio: 2.06 DFRC: https://tests.stockfishchess.org/tests/view/62c13a5702ba5d0a774d20d9 Elo: 55.25 +-3.9 (95%) LOS: 100.0% Total: 10000 W: 2984 L: 1407 D: 5609 Ptnml(0-2): 51, 636, 2266, 1779, 268 nElo: 96.95 +-7.2 (95%) PairsRatio: 2.98 Tested at LTC with identical scaling: FRC: https://tests.stockfishchess.org/tests/view/62c26a3c9e7d9997a12d6caf Elo: 16.20 +-2.5 (95%) LOS: 100.0% Total: 10000 W: 1192 L: 726 D: 8082 Ptnml(0-2): 10, 403, 3727, 831, 29 nElo: 44.12 +-6.7 (95%) PairsRatio: 2.08 DFRC: https://tests.stockfishchess.org/tests/view/62c26a539e7d9997a12d6cb2 Elo: 40.94 +-3.0 (95%) LOS: 100.0% Total: 10000 W: 2215 L: 1042 D: 6743 Ptnml(0-2): 10, 410, 3053, 1451, 76 nElo: 92.77 +-6.9 (95%) PairsRatio: 3.64 This is due to the mixing in a significant fraction of DFRC training data in the final training round. The net is trained using the easy_train.py script in the following way: ``` python easy_train.py \ --training-dataset=../Leela-dfrc_n5000.binpack \ --experiment-name=2 \ --nnue-pytorch-branch=vondele/nnue-pytorch/lossScan4 \ --additional-training-arg=--param-index=2 \ --start-lambda=1.0 \ --end-lambda=0.75 \ --gamma=0.995 \ --lr=4.375e-4 \ --start-from-engine-test-net True \ --tui=False \ --seed=$RANDOM \ --max_epoch=800 \ --auto-exit-timeout-on-training-finished=900 \ --network-testing-threads 8 \ --num-workers 12 ``` where the data set used (Leela-dfrc_n5000.binpack) is a combination of our previous best data set (mix of Leela and some SF data) and DFRC data, interleaved to form: The data is available in https://drive.google.com/drive/folders/1S9-ZiQa_3ApmjBtl2e8SyHxj4zG4V8gG?usp=sharing Leela mix: https://drive.google.com/file/d/1JUkMhHSfgIYCjfDNKZUMYZt6L5I7Ra6G/view?usp=sharing DFRC: https://drive.google.com/file/d/17vDaff9LAsVo_1OfsgWAIYqJtqR8aHlm/view?usp=sharing The training branch used is https://github.com/vondele/nnue-pytorch/commits/lossScan4 A PR to the main trainer repo will be made later. This contains a revised loss function, now computing the loss from the score based on the win rate model, which is a more accurate representation than what we had before. Scaling constants are tweaked there as well. closes https://github.com/official-stockfish/Stockfish/pull/4100 Bench: 5186781 --- src/evaluate.cpp | 2 +- src/evaluate.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 6d2e8da8d40..53613794857 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1102,7 +1102,7 @@ Value Eval::evaluate(const Position& pos, int* complexity) { if (useNNUE && !useClassical) { int nnueComplexity; - int scale = 1092 + 106 * pos.non_pawn_material() / 5120; + int scale = 1064 + 106 * pos.non_pawn_material() / 5120; Value optimism = pos.this_thread()->optimism[stm]; Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); diff --git a/src/evaluate.h b/src/evaluate.h index e25bd52e849..36d3b2b282d 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-3c0aa92af1da.nnue" + #define EvalFileDefaultName "nn-3c0054ea9860.nnue" namespace NNUE { From c2aaaa65f97d4cd5fc06f19ce8d158d85dcd7a7b Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Tue, 5 Jul 2022 14:15:34 +0300 Subject: [PATCH 0841/1766] Simplify away FRC correction term Since new net is trained partially using FRC data this part of adjustment that penalises bishops that are locked in the corner is no longer needed - net should "know" this things itself much better. STC on FRC book : https://tests.stockfishchess.org/tests/view/62c3031b9e7d9997a12d852f LLR: 2.96 (-2.94,2.94) <-2.25,0.25> Total: 22048 W: 3003 L: 2845 D: 16200 Ptnml(0-2): 96, 1778, 7149, 1874, 127 LTC on FRC book : https://tests.stockfishchess.org/tests/view/62c32e939e7d9997a12d8c5e LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 36784 W: 3138 L: 3037 D: 30609 Ptnml(0-2): 36, 1842, 14537, 1939, 38 STC on DFRC book : https://tests.stockfishchess.org/tests/view/62c32efb9e7d9997a12d8c6f LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 20424 W: 3903 L: 3721 D: 12800 Ptnml(0-2): 172, 1984, 5724, 2154, 178 LTC on DFRC book : https://tests.stockfishchess.org/tests/view/62c358c79e7d9997a12d9319 LLR: 2.93 (-2.94,2.94) <-2.25,0.25> Total: 53784 W: 7581 L: 7480 D: 38723 Ptnml(0-2): 87, 3887, 18856, 3962, 100 closes https://github.com/official-stockfish/Stockfish/pull/4101 bench 5182295 --- src/evaluate.cpp | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 53613794857..d340d3d5439 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1042,38 +1042,6 @@ namespace { return v; } - - /// Fisher Random Chess: correction for cornered bishops, to fix chess960 play with NNUE - - Value fix_FRC(const Position& pos) { - - constexpr Bitboard Corners = 1ULL << SQ_A1 | 1ULL << SQ_H1 | 1ULL << SQ_A8 | 1ULL << SQ_H8; - - if (!(pos.pieces(BISHOP) & Corners)) - return VALUE_ZERO; - - int correction = 0; - - if ( pos.piece_on(SQ_A1) == W_BISHOP - && pos.piece_on(SQ_B2) == W_PAWN) - correction -= CorneredBishop; - - if ( pos.piece_on(SQ_H1) == W_BISHOP - && pos.piece_on(SQ_G2) == W_PAWN) - correction -= CorneredBishop; - - if ( pos.piece_on(SQ_A8) == B_BISHOP - && pos.piece_on(SQ_B7) == B_PAWN) - correction += CorneredBishop; - - if ( pos.piece_on(SQ_H8) == B_BISHOP - && pos.piece_on(SQ_G7) == B_PAWN) - correction += CorneredBishop; - - return pos.side_to_move() == WHITE ? Value(3 * correction) - : -Value(3 * correction); - } - } // namespace Eval @@ -1113,9 +1081,6 @@ Value Eval::evaluate(const Position& pos, int* complexity) { optimism = optimism * (269 + nnueComplexity) / 256; v = (nnue * scale + optimism * (scale - 754)) / 1024; - - if (pos.is_chess960()) - v += fix_FRC(pos); } // Damp down the evaluation linearly when shuffling From aa18b68033403ee2d8d49df783cab32e6bccf59c Mon Sep 17 00:00:00 2001 From: Dubslow Date: Fri, 1 Jul 2022 11:53:16 -0500 Subject: [PATCH 0842/1766] Time mgmt fix division. oversight changed the corresponding float division to integer division in a previous tune https://github.com/official-stockfish/Stockfish/commit/442c40b43de8ede1e424efa674c8d45322e3b43c it is stronger to keep the original float division. green LTC: https://tests.stockfishchess.org/tests/view/62bf34bc0340fb1e0cc934e7 LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 38952 W: 10738 L: 10467 D: 17747 Ptnml(0-2): 46, 3576, 11968, 3833, 53 yellow STC: https://tests.stockfishchess.org/tests/view/62bff6506178ffe6394ba1d1 LLR: -2.95 (-2.94,2.94) <0.00,2.50> Total: 226960 W: 61265 L: 61062 D: 104633 Ptnml(0-2): 938, 24398, 62582, 24647, 915 further slightly tweaked tests confirm this Elo gain. closes https://github.com/official-stockfish/Stockfish/pull/4097 No functional change --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index f11d76be715..07f18a34fe3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -472,7 +472,7 @@ void Thread::search() { double reduction = (1.56 + mainThread->previousTimeReduction) / (2.20 * timeReduction); double bestMoveInstability = 1 + 1.7 * totBestMoveChanges / Threads.size(); int complexity = mainThread->complexityAverage.value(); - double complexPosition = std::clamp(1.0 + (complexity - 277) / 1819, 0.5, 1.5); + double complexPosition = std::clamp(1.0 + (complexity - 277) / 1819.1, 0.5, 1.5); double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability * complexPosition; From 2e02dd79366e6f17df5b0599048937289fd5819e Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 2 Jul 2022 08:55:05 +0200 Subject: [PATCH 0843/1766] Limit the researching at same depth. If the elapsed time is close to the available time, the time management thread can signal that the next iterations should be searched at the same depth (Threads.increaseDepth = false). While the rootDepth increases, the adjustedDepth is kept constant with the searchAgainCounter. In exceptional cases, when threading is used and the master thread, which controls the time management, signals to not increaseDepth, but by itself takes a long time to finish the iteration, the helper threads can search repeatedly at the same depth. This search finishes more and more quickly, leading to helper threads that report a rootDepth of MAX_DEPTH (245). The latter is not optimal as it is confusing for the user, stops search on these threads, and leads to an incorrect bias in the thread voting scheme. Probably with only a small impact on strength. This behavior was observed almost two years ago, see https://github.com/official-stockfish/Stockfish/issues/2717 This patch fixes #2717 by ensuring the effective depth increases at once every four iterations, even in increaseDepth is false. Depth 245 searches (for non-trivial positions) were indeed absent with this patch, but frequent with master in the tests below: https://discord.com/channels/435943710472011776/813919248455827515/994872720800088095 Total pgns: 2173 Base: 2867 Patch: 0 it passed non-regression testing in various setups: SMP STC: https://tests.stockfishchess.org/tests/view/62bfecc96178ffe6394ba036 LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 37288 W: 10171 L: 10029 D: 17088 Ptnml(0-2): 75, 3777, 10793, 3929, 70 SMP LTC: https://tests.stockfishchess.org/tests/view/62c08f6f49b62510394be066 LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 190568 W: 52125 L: 52186 D: 86257 Ptnml(0-2): 70, 17854, 59504, 17779, 77 LTC: https://tests.stockfishchess.org/tests/view/62c08b6049b62510394bdfb6 LLR: 2.96 (-2.94,2.94) <-2.25,0.25> Total: 48120 W: 13204 L: 13083 D: 21833 Ptnml(0-2): 54, 4458, 14919, 4571, 58 Special thanks to miguel-I, Disservin, ruicoelhopedro and others for analysing the problem, the data, and coming up with the key insight, needed to fix this longstanding issue. closes https://github.com/official-stockfish/Stockfish/pull/4104 Bench: 5182295 --- src/search.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 07f18a34fe3..ca1d2632b53 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -373,7 +373,9 @@ void Thread::search() { int failedHighCnt = 0; while (true) { - Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt - searchAgainCounter); + // Adjust the effective depth searched, but ensuring at least one effective increment for every + // four searchAgain steps (see issue #2717). + Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt - 3 * (searchAgainCounter + 1) / 4); bestValue = Stockfish::search(rootPos, ss, alpha, beta, adjustedDepth, false); // Bring the best move to the front. It is critical that sorting From 95d24b77dfe8a147100d9753c5580563c3e642d2 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Wed, 13 Jul 2022 11:59:54 +0300 Subject: [PATCH 0844/1766] Simplify away some unneeded code in time management The lower bound of the clamp is never used since complexity can't be negative and thus is unneeded. closes https://github.com/official-stockfish/Stockfish/pull/4105 No functional change --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index ca1d2632b53..c5c7f111e88 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -474,7 +474,7 @@ void Thread::search() { double reduction = (1.56 + mainThread->previousTimeReduction) / (2.20 * timeReduction); double bestMoveInstability = 1 + 1.7 * totBestMoveChanges / Threads.size(); int complexity = mainThread->complexityAverage.value(); - double complexPosition = std::clamp(1.0 + (complexity - 277) / 1819.1, 0.5, 1.5); + double complexPosition = std::min(1.0 + (complexity - 277) / 1819.1, 1.5); double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability * complexPosition; From 4b4b7d1209259811537634c68555d2a8f4af197c Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 11 Jul 2022 17:32:39 +0200 Subject: [PATCH 0845/1766] Update default net to nn-ad9b42354671.nnue using trainer branch https://github.com/glinscott/nnue-pytorch/pull/208 with a slightly tweaked loss function (power 2.5 instead of 2.6), otherwise same training as in the previous net update https://github.com/official-stockfish/Stockfish/pull/4100 passed STC: LLR: 2.97 (-2.94,2.94) <0.00,2.50> Total: 367536 W: 99465 L: 98573 D: 169498 Ptnml(0-2): 1820, 40994, 97117, 42148, 1689 https://tests.stockfishchess.org/tests/view/62cc43fe50dcbecf5fc1c5b8 passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 25032 W: 6802 L: 6553 D: 11677 Ptnml(0-2): 40, 2424, 7341, 2669, 42 https://tests.stockfishchess.org/tests/view/62ce5f421dacb46e4d5fd277 closes https://github.com/official-stockfish/Stockfish/pull/4107 Bench: 5905619 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 36d3b2b282d..f5ac3263c33 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-3c0054ea9860.nnue" + #define EvalFileDefaultName "nn-ad9b42354671.nnue" namespace NNUE { From c4a644922dc219aade86ea07c85e95899850fac8 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Tue, 19 Jul 2022 01:09:30 -0500 Subject: [PATCH 0846/1766] Simplify reduction condition for cutNodes LMR: for cutNodes, dont exclude killer moves. This was a prelude to reducing allNodes, altho that's failed so far. STC https://tests.stockfishchess.org/tests/view/62d64ad147ae1768b34a27c3 LLR: 2.95 (-2.94,2.94) <-2.25,0.25> Total: 37064 W: 10044 L: 9889 D: 17131 Ptnml(0-2): 162, 4115, 9828, 4260, 167 LTC https://tests.stockfishchess.org/tests/view/62d66cc047ae1768b34a2b14 LLR: 2.94 (-2.94,2.94) <-2.25,0.25> Total: 39832 W: 10796 L: 10659 D: 18377 Ptnml(0-2): 69, 3969, 11706, 4100, 72 closes https://github.com/official-stockfish/Stockfish/pull/4109 bench: 5697891 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index c5c7f111e88..96b1d0f1a0c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1156,7 +1156,7 @@ namespace { r--; // Increase reduction for cut nodes (~3 Elo) - if (cutNode && move != ss->killers[0]) + if (cutNode) r += 2; // Increase reduction if ttMove is a capture (~3 Elo) From 18389e269d43af69c96521f3fe9d85e6b7ed073c Mon Sep 17 00:00:00 2001 From: Dubslow Date: Tue, 26 Jul 2022 23:45:19 -0500 Subject: [PATCH 0847/1766] remove useClassical depth condition passed STC: https://tests.stockfishchess.org/tests/view/62e0c3e98e4fa6ae472695ed LLR: 2.96 (-2.94,2.94) <-2.25,0.25> Total: 293568 W: 78934 L: 79151 D: 135483 Ptnml(0-2): 1344, 31488, 81366, 31213, 1373 passed LTC: https://tests.stockfishchess.org/tests/view/62e190aa8e4fa6ae4726b5b5 LLR: 2.98 (-2.94,2.94) <-2.25,0.25> Total: 187392 W: 50971 L: 51028 D: 85393 Ptnml(0-2): 384, 17801, 57369, 17772, 370 other attempts to otherwise tune this parameter failed, bounds 6,7,10,11 failed STC, 8 passed STC but failed LTC closes https://github.com/official-stockfish/Stockfish/pull/4112 bench 5796377 --- src/evaluate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d340d3d5439..9e3eaba512c 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1055,7 +1055,7 @@ Value Eval::evaluate(const Position& pos, int* complexity) { Value psq = pos.psq_eg_stm(); // Deciding between classical and NNUE eval (~10 Elo): for high PSQ imbalance we use classical, // but we switch to NNUE during long shuffling or with high material on the board. - bool useClassical = (pos.this_thread()->depth > 9 || pos.count() > 7) + bool useClassical = (pos.count() > 7) && abs(psq) * 5 > (856 + pos.non_pawn_material() / 64) * (10 + pos.rule50_count()); // Deciding between classical and NNUE eval (~10 Elo): for high PSQ imbalance we use classical, From 582c88ee94c24b4352477ffacf8de2afc521bcce Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 31 Jul 2022 02:04:23 +0300 Subject: [PATCH 0848/1766] Do more TT cutoffs in case of exact bound The idea is that these TT entries are considered move valuable in TT replacement scheme - they are always overwriting other entries. So it makes sence for them to produce more aggressive cutoffs. passed STC https://tests.stockfishchess.org/tests/view/62e4d407b383a712b1385410 LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 96632 W: 26045 L: 25659 D: 44928 Ptnml(0-2): 434, 10635, 25770, 11065, 412 passed LTC https://tests.stockfishchess.org/tests/view/62e523e2b383a712b1386193 LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 77960 W: 21363 L: 20989 D: 35608 Ptnml(0-2): 190, 7591, 23009, 8035, 155 closes https://github.com/official-stockfish/Stockfish/pull/4114 bench 5820568 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 96b1d0f1a0c..004d061b7b6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -637,7 +637,7 @@ namespace { // At non-PV nodes we check for an early TT cutoff if ( !PvNode && ss->ttHit - && tte->depth() > depth - ((int)thisThread->id() & 0x1) + && tte->depth() > depth - ((int)thisThread->id() & 0x1) - (tte->bound() == BOUND_EXACT) && ttValue != VALUE_NONE // Possible in case of TT access race && (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER))) { From 675f6a038ba98b6b906a4767f009cf6fe91b0c52 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Tue, 2 Aug 2022 23:14:14 +0200 Subject: [PATCH 0849/1766] Tweak history updates In general the history update bonus is slightly decreased by 11% which gives a slower saturation speed. In addition only for main history the divisor is halfed (used history values are doubled to maintain same maximum) which have an effect in the opposite direction on saturation speed. STC: LLR: 2.95 (-2.94,2.94) <0.00,2.50> Total: 157088 W: 42673 L: 42168 D: 72247 Ptnml(0-2): 857, 17346, 41642, 17833, 866 https://tests.stockfishchess.org/tests/view/62e5517ab383a712b13867c5 LTC: LLR: 2.94 (-2.94,2.94) <0.50,3.00> Total: 325592 W: 88705 L: 87753 D: 149134 Ptnml(0-2): 594, 32288, 96076, 33248, 590 https://tests.stockfishchess.org/tests/view/62e5e4f4b383a712b1387d53 closes https://github.com/official-stockfish/Stockfish/pull/4119 Bench: 5518728 --- src/movepick.cpp | 4 ++-- src/movepick.h | 2 +- src/search.cpp | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index b0166c6e962..60d041ab9c7 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -137,7 +137,7 @@ void MovePicker::score() { + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]; else if constexpr (Type == QUIETS) - m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] + m.value = 2 * (*mainHistory)[pos.side_to_move()][from_to(m)] + 2 * (*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)] @@ -155,7 +155,7 @@ void MovePicker::score() { m.value = PieceValue[MG][pos.piece_on(to_sq(m))] - Value(type_of(pos.moved_piece(m))); else - m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] + m.value = 2 * (*mainHistory)[pos.side_to_move()][from_to(m)] + 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] - (1 << 28); } diff --git a/src/movepick.h b/src/movepick.h index 6b3c08c7a0d..979709ae682 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -87,7 +87,7 @@ enum StatsType { NoCaptures, Captures }; /// ordering decisions. It uses 2 tables (one for each color) indexed by /// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards /// (~11 elo) -typedef Stats ButterflyHistory; +typedef Stats ButterflyHistory; /// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous /// move, see www.chessprogramming.org/Countermove_Heuristic diff --git a/src/search.cpp b/src/search.cpp index 004d061b7b6..9b747e78e41 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -81,7 +81,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return std::min((9 * d + 270) * d - 311 , 2145); + return std::min((8 * d + 240) * d - 276 , 1907); } // Add a small random component to draw evaluations to avoid 3-fold blindness @@ -1033,7 +1033,7 @@ namespace { && history < -3875 * (depth - 1)) continue; - history += thisThread->mainHistory[us][from_to(move)]; + history += 2 * thisThread->mainHistory[us][from_to(move)]; // Futility pruning: parent node (~9 Elo) if ( !ss->inCheck @@ -1171,7 +1171,7 @@ namespace { if ((ss+1)->cutoffCnt > 3 && !PvNode) r++; - ss->statScore = thisThread->mainHistory[us][from_to(move)] + ss->statScore = 2 * thisThread->mainHistory[us][from_to(move)] + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] From b8f4903fbb8ac9be8d26cef30848b3a9527c8725 Mon Sep 17 00:00:00 2001 From: lonfom169 Date: Wed, 3 Aug 2022 12:43:21 -0300 Subject: [PATCH 0850/1766] Reintroduce singularQuietLMR STC: LLR: 2.96 (-2.94,2.94) <0.00,2.00> Total: 88912 W: 23972 L: 23580 D: 41360 Ptnml(0-2): 365, 9820, 23712, 10176, 383 https://tests.stockfishchess.org/tests/view/62e9537a400addce2c13399b LTC: LLR: 2.97 (-2.94,2.94) <0.50,2.50> Total: 85672 W: 23607 L: 23192 D: 38873 Ptnml(0-2): 219, 8316, 25365, 8703, 233 https://tests.stockfishchess.org/tests/view/62e9a174400addce2c1346e4 closes https://github.com/official-stockfish/Stockfish/pull/4122 Bench: 5921315 --- src/search.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 9b747e78e41..7c0601e60e2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -558,7 +558,7 @@ namespace { Move ttMove, move, excludedMove, bestMove; Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue, probCutBeta; - bool givesCheck, improving, didLMR, priorCapture; + bool givesCheck, improving, didLMR, priorCapture, singularQuietLMR; bool capture, doFullDepthSearch, moveCountPruning, ttCapture; Piece movedPiece; int moveCount, captureCount, quietCount, improvement, complexity; @@ -945,7 +945,7 @@ namespace { ss->killers); value = bestValue; - moveCountPruning = false; + moveCountPruning = singularQuietLMR = false; // Indicate PvNodes that will probably fail low if the node was searched // at a depth equal or greater than the current depth, and the result of this search was a fail low. @@ -1075,6 +1075,7 @@ namespace { if (value < singularBeta) { extension = 1; + singularQuietLMR = !ttCapture; // Avoid search explosion by limiting the number of double extensions if ( !PvNode @@ -1167,6 +1168,10 @@ namespace { if (PvNode) r -= 1 + 15 / (3 + depth); + // Decrease reduction if ttMove has been singularly extended (~1 Elo) + if (singularQuietLMR) + r--; + // Increase reduction if next ply has a lot of fail high else reset count to 0 if ((ss+1)->cutoffCnt > 3 && !PvNode) r++; From 7cc929f437f56396ce151c4d8095e24f3d957bd9 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 31 Jul 2022 15:04:23 +0200 Subject: [PATCH 0851/1766] Update CPU contributors list Thanks for your contributions! closes https://github.com/official-stockfish/Stockfish/pull/4116 No functional change --- Top CPU Contributors.txt | 148 +++++++++++++++++++++------------------ 1 file changed, 79 insertions(+), 69 deletions(-) diff --git a/Top CPU Contributors.txt b/Top CPU Contributors.txt index 76aa01e9c34..23a5d7f97e2 100644 --- a/Top CPU Contributors.txt +++ b/Top CPU Contributors.txt @@ -1,96 +1,102 @@ -Contributors to Fishtest with >10,000 CPU hours, as of 2022-04-14. +Contributors to Fishtest with >10,000 CPU hours, as of 2022-07-31. Thank you! Username CPU Hours Games played ------------------------------------------------------------------ -noobpwnftw 31714850 2267266129 -mlang 2954099 198421098 -technologov 2324150 102449398 -dew 1670874 99276012 -grandphish2 1134273 68070459 -okrout 901194 77738874 -TueRens 821388 50207666 +noobpwnftw 33202707 2423743815 +technologov 5064327 270208248 +mlang 2963357 198937430 +dew 1677196 99717674 +grandphish2 1231326 74551309 +okrout 1102747 98977462 +TueRens 925904 57404676 +pemo 911980 35581261 tvijlbrief 795993 51894442 -pemo 744463 32486677 -JojoM 724378 43660674 +JojoM 774270 47311084 mibere 703840 46867607 -linrock 626939 17408017 -gvreuls 534079 34352532 -cw 507221 34006775 -fastgm 489749 29344518 +linrock 697283 18804969 +gvreuls 564284 36392236 +cw 515739 34775505 +fastgm 500949 30101898 +oz 439015 31794460 +CSU_Dynasty 438017 29369136 crunchy 427035 27344275 -CSU_Dynasty 424643 28525220 -ctoks 415771 27364603 -oz 369200 27017658 -bcross 342642 23671289 +ctoks 422671 27812261 +bcross 363335 25108521 +leszek 360149 22674005 +velislav 333325 21444360 Fisherman 327231 21829379 -velislav 325670 20911076 -leszek 321295 19874113 -Dantist 274747 16910258 -mgrabiak 237604 15418700 -robal 217959 13840386 +Dantist 292327 17951982 +mgrabiak 247220 16137378 +nordlandia 226543 14601042 +robal 224740 14314972 glinscott 217799 13780820 -nordlandia 211692 13484886 -drabel 201967 13798360 +ncfish1 207751 13909639 +drabel 203884 13922680 +mhoram 200022 12533963 bking_US 198894 11876016 -mhoram 194862 12261809 +rpngn 191764 12236583 Thanar 179852 12365359 vdv 175544 9904472 spams 157128 10319326 -rpngn 154081 9652139 marrco 150300 9402229 sqrt2 147963 9724586 -vdbergh 137430 8955097 +vdbergh 137480 8958795 CoffeeOne 137100 5024116 malala 136182 8002293 xoto 133759 9159372 -davar 125240 8117121 +davar 128645 8367253 +DesolatedDodo 124877 8056482 dsmith 122059 7570238 -amicic 119659 7937885 +amicic 119661 7938029 Data 113305 8220352 BrunoBanani 112960 7436849 CypressChess 108321 7759588 -DesolatedDodo 106811 6776980 MaZePallas 102823 6633619 +skiminki 102168 6778440 sterni1971 100532 5880772 sunu 100167 7040199 ElbertoOne 99028 7023771 -skiminki 98123 6478402 +zeryl 96984 6162287 brabos 92118 6186135 -cuistot 90358 5351004 +cuistot 91738 5447070 psk 89957 5984901 racerschmacer 85712 6119648 Vizvezdenec 83761 5344740 -zeryl 83680 5250995 sschnee 83003 4840890 0x3C33 82614 5271253 +armo9494 82501 5806056 BRAVONE 81239 5054681 nssy 76497 5259388 +thirdlife 76478 1544524 +Calis007 76457 4281018 +jromang 75885 5230523 teddybaer 75125 5407666 -jromang 74796 5175825 Pking_cda 73776 5293873 -Calis007 72477 4088576 +Wolfgang 72750 4538670 +sebastronomy 70784 1329428 solarlight 70517 5028306 dv8silencer 70287 3883992 Bobo1239 68515 4652287 +yurikvelo 67651 4578970 manap 66273 4121774 -yurikvelo 65716 4457300 tinker 64333 4268790 -Wolfgang 62644 3817410 qurashee 61208 3429862 robnjr 57262 4053117 +megaman7de 57023 3525850 Freja 56938 3733019 +MaxKlaxxMiner 56279 3410158 ttruscott 56010 3680085 rkl 55132 4164467 renouve 53811 3501516 -megaman7de 52434 3243016 -MaxKlaxxMiner 51977 3153032 +tolkki963 53294 3354682 +DMBK 52963 3933332 finfish 51360 3370515 eva42 51272 3599691 +Spprtr 51139 3299983 eastorwest 51058 3451555 rap 49985 3219146 pb00067 49727 3298270 -Spprtr 48920 3161711 bigpen0r 47667 3336927 ronaldjerum 47654 3240695 biffhero 46564 3111352 @@ -102,18 +108,17 @@ Antihistamine 41788 2761312 mhunt 41735 2691355 homyur 39893 2850481 gri 39871 2515779 -armo9494 39064 2832326 -oryx 38867 2976992 +oryx 39602 3024830 SC 37299 2731694 Garf 37213 2986270 -tolkki963 37059 2154330 +Dubslow 36714 2409254 csnodgrass 36207 2688994 jmdana 36157 2210661 +markkulix 35994 2226860 strelock 34716 2074055 -DMBK 34010 2482916 EthanOConnor 33370 2090311 slakovv 32915 2021889 -gopeto 30993 2028106 +gopeto 31078 2033362 manapbk 30987 1810399 Prcuvu 30377 2170122 anst 30301 2190091 @@ -121,7 +126,8 @@ jkiiski 30136 1904470 hyperbolic.tom 29840 2017394 chuckstablers 29659 2093438 Pyafue 29650 1902349 -ncfish1 29105 1704011 +MarcusTullius 28611 1646671 +spcc 28241 1821198 belzedar94 27935 1789106 OuaisBla 27636 1578800 chriswk 26902 1868317 @@ -131,14 +137,16 @@ yorkman 26193 1992080 SFTUser 25182 1675689 nabildanial 24942 1519409 Sharaf_DG 24765 1786697 -rodneyc 24275 1410450 +rodneyc 24375 1416258 +Ulysses 24017 1626140 agg177 23890 1395014 JanErik 23408 1703875 +Ente 23403 1660988 +kdave 23392 1630462 Isidor 23388 1680691 Norabor 23339 1602636 -Ente 23270 1651432 cisco2015 22897 1762669 -MarcusTullius 22688 1274821 +Wencey 22573 1121406 Zirie 22542 1472937 team-oh 22272 1636708 MazeOfGalious 21978 1629593 @@ -150,19 +158,16 @@ nesoneg 21494 1463031 Roady 21323 1433822 sphinx 21211 1384728 user213718 21196 1397710 -spcc 21065 1311338 jjoshua2 21001 1423089 horst.prack 20878 1465656 0xB00B1ES 20590 1208666 j3corre 20405 941444 -kdave 20364 1389254 Adrian.Schmidt123 20316 1281436 -Ulysses 20217 1351500 -markkulix 19976 1115258 +jcAEie 20221 1504162 wei 19973 1745989 rstoesser 19569 1293588 eudhan 19274 1283717 -fishtester 18995 1238686 +fishtester 19145 1242668 vulcan 18871 1729392 jundery 18445 1115855 iisiraider 18247 1101015 @@ -170,29 +175,31 @@ ville 17883 1384026 chris 17698 1487385 purplefishies 17595 1092533 dju 17353 978595 -Wencey 17125 805964 +AndreasKrug 17191 1317997 DragonLord 17014 1162790 -thirdlife 16996 447356 +Jopo12321 16966 944924 +GPUex 16744 1077826 +xwziegtm 16608 1276372 IgorLeMasson 16064 1147232 ako027ako 15671 1173203 -AndreasKrug 15550 1194497 +jsys14 15474 917092 Nikolay.IT 15154 1068349 Andrew Grant 15114 895539 -scuzzi 14928 953313 +scuzzi 15112 960373 OssumOpossum 14857 1007129 Karby 14808 867120 -jsys14 14652 855642 enedene 14476 905279 bpfliegel 14298 884523 mpx86 14019 759568 jpulman 13982 870599 +Naven94 13879 811552 +Karpovbot 13808 734276 crocogoat 13803 1117422 joster 13794 950160 Nesa92 13786 1114691 mbeier 13650 1044928 Hjax 13535 915487 Dark_wizzie 13422 1007152 -Jopo12321 13367 678852 Rudolphous 13244 883140 Machariel 13010 863104 mabichito 12903 749391 @@ -200,36 +207,39 @@ thijsk 12886 722107 AdrianSA 12860 804972 infinigon 12807 937332 Flopzee 12698 894821 +pirt 12551 965597 fatmurphy 12547 853210 SapphireBrand 12416 969604 modolief 12386 896470 Farseer 12249 694108 pgontarz 12151 848794 -pirt 12008 923149 stocky 11954 699440 mschmidt 11941 803401 +Oakwen 11925 818865 +MooTheCow 11851 772628 +deflectooor 11642 565132 dbernier 11609 818636 +Skiff84 11604 602786 Maxim 11543 836024 infinity 11470 727027 +FormazChar 11430 856559 aga 11409 695071 +Jackfish 11403 750526 torbjo 11395 729145 Thomas A. Anderson 11372 732094 savage84 11358 670860 -FormazChar 11349 850327 d64 11263 789184 -MooTheCow 11237 720174 +qoo_charly_cai 11127 671959 snicolet 11106 869170 ali-al-zhrani 11098 768494 whelanh 11067 235676 -Jackfish 10978 720078 -deflectooor 10886 520116 basepi 10637 744851 Cubox 10621 826448 +Alb11747 10558 689794 michaelrpg 10509 739239 OIVAS7572 10420 995586 +Garruk 10343 704723 dzjp 10343 732529 -Garruk 10334 704065 ols 10259 570669 lbraesch 10252 647825 -qoo_charly_cai 10212 620407 -Naven94 10069 503192 +Karmatron 10195 661432 From e639c4557786513018f625ff96f04b63157a85c1 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 31 Jul 2022 14:58:13 +0200 Subject: [PATCH 0852/1766] Update WDL model for current SF This updates the WDL model based on the LTC statistics for the two weeks (3M games). for old results see: https://github.com/official-stockfish/Stockfish/pull/3981 https://github.com/official-stockfish/Stockfish/pull/3582 https://github.com/official-stockfish/Stockfish/pull/2778 closes https://github.com/official-stockfish/Stockfish/pull/4115 No functional change. --- src/uci.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uci.cpp b/src/uci.cpp index c0bacfafefe..ec106ee9f40 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -207,8 +207,8 @@ namespace { // The coefficients of a third-order polynomial fit is based on the fishtest data // for two parameters that need to transform eval to the argument of a logistic // function. - double as[] = {-1.17202460e-01, 5.94729104e-01, 1.12065546e+01, 1.22606222e+02}; - double bs[] = {-1.79066759, 11.30759193, -17.43677612, 36.47147479}; + double as[] = { 0.50379905, -4.12755858, 18.95487051, 152.00733652}; + double bs[] = {-1.71790378, 10.71543602, -17.05515898, 41.15680404}; double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3]; double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3]; From 0a01dd044f8a8f291127ebf60462773bf12c90c7 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Tue, 9 Aug 2022 20:56:13 +0200 Subject: [PATCH 0853/1766] Cleanup code This PR includes following cleanups: - Remove the unused depth variable in the thread class. - cleanup ValueList (added from mstembera) closes https://github.com/official-stockfish/Stockfish/pull/4127 No functional change. --- src/evaluate.cpp | 1 - src/misc.h | 13 ------------- src/position.cpp | 6 ++++-- src/search.cpp | 1 - src/thread.h | 2 +- 5 files changed, 5 insertions(+), 18 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 9e3eaba512c..7d5876758c0 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1114,7 +1114,6 @@ std::string Eval::trace(Position& pos) { std::memset(scores, 0, sizeof(scores)); // Reset any global variable used in eval - pos.this_thread()->depth = 0; pos.this_thread()->trend = SCORE_ZERO; pos.this_thread()->bestValue = VALUE_ZERO; pos.this_thread()->optimism[WHITE] = VALUE_ZERO; diff --git a/src/misc.h b/src/misc.h index 2fd2b408a12..fe1143def0b 100644 --- a/src/misc.h +++ b/src/misc.h @@ -116,23 +116,10 @@ class ValueList { public: std::size_t size() const { return size_; } - void resize(std::size_t newSize) { size_ = newSize; } void push_back(const T& value) { values_[size_++] = value; } - T& operator[](std::size_t index) { return values_[index]; } - T* begin() { return values_; } - T* end() { return values_ + size_; } - const T& operator[](std::size_t index) const { return values_[index]; } const T* begin() const { return values_; } const T* end() const { return values_ + size_; } - void swap(ValueList& other) { - const std::size_t maxSize = std::max(size_, other.size_); - for (std::size_t i = 0; i < maxSize; ++i) { - std::swap(values_[i], other.values_[i]); - } - std::swap(size_, other.size_); - } - private: T values_[MaxSize]; std::size_t size_ = 0; diff --git a/src/position.cpp b/src/position.cpp index ec9229ea337..08ed1a89e15 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1099,10 +1099,12 @@ bool Position::see_ge(Move m, Value threshold) const { // Don't allow pinned pieces to attack as long as there are // pinners on their original square. if (pinners(~stm) & occupied) + { stmAttackers &= ~blockers_for_king(stm); - if (!stmAttackers) - break; + if (!stmAttackers) + break; + } res ^= 1; diff --git a/src/search.cpp b/src/search.cpp index 7c0601e60e2..565fba0f89f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -565,7 +565,6 @@ namespace { // Step 1. Initialize node Thread* thisThread = pos.this_thread(); - thisThread->depth = depth; ss->inCheck = pos.checkers(); priorCapture = pos.captured_piece(); Color us = pos.side_to_move(); diff --git a/src/thread.h b/src/thread.h index 9e9cd488c3c..c430a8184e6 100644 --- a/src/thread.h +++ b/src/thread.h @@ -69,7 +69,7 @@ class Thread { Position rootPos; StateInfo rootState; Search::RootMoves rootMoves; - Depth rootDepth, completedDepth, depth, previousDepth; + Depth rootDepth, completedDepth, previousDepth; Value rootDelta; CounterMoveHistory counterMoves; ButterflyHistory mainHistory; From 1054a483ca0c560d30fb61617c0192cb4cd31528 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 31 Jul 2022 16:55:07 +0200 Subject: [PATCH 0854/1766] Remove an unneeded randomization of evals. most of the effect comes from the randomization of 3-folds. passed STC: https://tests.stockfishchess.org/tests/view/62e697e97e84186e5d19af6f LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 572976 W: 153168 L: 153539 D: 266269 Ptnml(0-2): 2505, 64783, 152364, 64250, 2586 passed LTC: https://tests.stockfishchess.org/tests/view/62ee5977523c86dcd6957154 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 704808 W: 191212 L: 191680 D: 321916 Ptnml(0-2): 1340, 70579, 208972, 70235, 1278 closes https://github.com/official-stockfish/Stockfish/pull/4128 Bench: 5868987 --- src/search.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 565fba0f89f..4d56d14d1ec 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -741,10 +741,6 @@ namespace { else // Fall back to (semi)classical complexity for TT hits, the NNUE complexity is lost complexity = abs(ss->staticEval - pos.psq_eg_stm()); - // Randomize draw evaluation - if (eval == VALUE_DRAW) - eval = value_draw(thisThread); - // ttValue can be used as a better position evaluation (~4 Elo) if ( ttValue != VALUE_NONE && (tte->bound() & (ttValue > eval ? BOUND_LOWER : BOUND_UPPER))) From 4568f6369bfb94d8a105a8074e43e8d79c57eaee Mon Sep 17 00:00:00 2001 From: mstembera Date: Mon, 8 Aug 2022 21:33:59 -0700 Subject: [PATCH 0855/1766] Report longest PV lines for multithreaded search In case several threads find the same bestmove, report the longest PV line found. closes https://github.com/official-stockfish/Stockfish/pull/4126 No functional change. --- src/thread.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/thread.cpp b/src/thread.cpp index 08a78db5f31..c834fa9f9df 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -237,7 +237,9 @@ Thread* ThreadPool::get_best_thread() const { } else if ( th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY || ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY - && votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]])) + && ( votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]] + || ( votes[th->rootMoves[0].pv[0]] == votes[bestThread->rootMoves[0].pv[0]] + && th->rootMoves[0].pv.size() > bestThread->rootMoves[0].pv.size())))) bestThread = th; } From 3370f69881564025d933668ea5d407af0c5dcea2 Mon Sep 17 00:00:00 2001 From: mckx00 Date: Sat, 13 Aug 2022 05:32:30 -0700 Subject: [PATCH 0856/1766] Make LMR code easier to follow Remove flags doFullDepthSearch and didLMR, and reorder instruction. Small measured speedup. Closes https://github.com/official-stockfish/Stockfish/pull/4129 No functional change. --- src/search.cpp | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 4d56d14d1ec..7244b8d661e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -558,8 +558,8 @@ namespace { Move ttMove, move, excludedMove, bestMove; Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue, probCutBeta; - bool givesCheck, improving, didLMR, priorCapture, singularQuietLMR; - bool capture, doFullDepthSearch, moveCountPruning, ttCapture; + bool givesCheck, improving, priorCapture, singularQuietLMR; + bool capture, moveCountPruning, ttCapture; Piece movedPiece; int moveCount, captureCount, quietCount, improvement, complexity; @@ -1127,8 +1127,6 @@ namespace { // Step 16. Make the move pos.do_move(move, st, givesCheck); - bool doDeeperSearch = false; - // Step 17. Late moves reduction / extension (LMR, ~98 Elo) // We use various heuristics for the sons of a node after the first son has // been searched. In general we would like to reduce them, but there are many @@ -1187,25 +1185,12 @@ namespace { value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); - // If the son is reduced and fails high it will be re-searched at full depth - doFullDepthSearch = value > alpha && d < newDepth; - doDeeperSearch = value > (alpha + 78 + 11 * (newDepth - d)); - didLMR = true; - } - else - { - doFullDepthSearch = !PvNode || moveCount > 1; - didLMR = false; - } - - // Step 18. Full depth search when LMR is skipped or fails high - if (doFullDepthSearch) - { - value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth + doDeeperSearch, !cutNode); - - // If the move passed LMR update its stats - if (didLMR) + // Do full depth search when reduced LMR search fails high + if (value > alpha && d < newDepth) { + const bool doDeeperSearch = value > (alpha + 78 + 11 * (newDepth - d)); + value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth + doDeeperSearch, !cutNode); + int bonus = value > alpha ? stat_bonus(newDepth) : -stat_bonus(newDepth); @@ -1216,6 +1201,12 @@ namespace { } } + // Step 18. Full depth search when LMR is skipped + else if (!PvNode || moveCount > 1) + { + value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode); + } + // 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 5f290352cd91beb9374671f6ae489165bd8ac2c0 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Mon, 15 Aug 2022 08:52:55 +0300 Subject: [PATCH 0857/1766] Simplify away smp adjustment in TT use Passed STC https://tests.stockfishchess.org/tests/view/62f7d81f23d42b50a8dab568 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 98160 W: 26307 L: 26165 D: 45688 Ptnml(0-2): 201, 10282, 27960, 10448, 189 Passed LTC https://tests.stockfishchess.org/tests/view/62f8d1a623d42b50a8dad4fb LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 81544 W: 22346 L: 22200 D: 36998 Ptnml(0-2): 44, 7542, 25446, 7704, 36 closes https://github.com/official-stockfish/Stockfish/pull/4131 No functional change (single threaded). --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 7244b8d661e..6d78403ffd9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -636,7 +636,7 @@ namespace { // At non-PV nodes we check for an early TT cutoff if ( !PvNode && ss->ttHit - && tte->depth() > depth - ((int)thisThread->id() & 0x1) - (tte->bound() == BOUND_EXACT) + && tte->depth() > depth - (tte->bound() == BOUND_EXACT) && ttValue != VALUE_NONE // Possible in case of TT access race && (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER))) { From 15ac117ac492e3147b391aa0ee3665fe8876be63 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 12 Aug 2022 12:31:49 +0200 Subject: [PATCH 0858/1766] Simplify the use of classical eval no benefit of the fallback term (exercised rarely). Cleanup the associated code. passed STC https://tests.stockfishchess.org/tests/view/62f62c2b6f0a08af9f776367 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 67832 W: 18334 L: 18148 D: 31350 Ptnml(0-2): 369, 7171, 18609, 7439, 328 passed LTC https://tests.stockfishchess.org/tests/view/62f68beb6f0a08af9f77710e LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 104664 W: 28363 L: 28233 D: 48068 Ptnml(0-2): 169, 10162, 31511, 10350, 140 closes https://github.com/official-stockfish/Stockfish/pull/4132 Bench: 6079565 --- src/evaluate.cpp | 45 ++++++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 7d5876758c0..eaad4d55672 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1053,34 +1053,29 @@ Value Eval::evaluate(const Position& pos, int* complexity) { Value v; Color stm = pos.side_to_move(); Value psq = pos.psq_eg_stm(); - // Deciding between classical and NNUE eval (~10 Elo): for high PSQ imbalance we use classical, - // but we switch to NNUE during long shuffling or with high material on the board. - bool useClassical = (pos.count() > 7) - && abs(psq) * 5 > (856 + pos.non_pawn_material() / 64) * (10 + pos.rule50_count()); - // Deciding between classical and NNUE eval (~10 Elo): for high PSQ imbalance we use classical, + // Deciding between classical and NNUE eval: for high PSQ imbalance we use classical, // but we switch to NNUE during long shuffling or with high material on the board. - if (!useNNUE || useClassical) - { - v = Evaluation(pos).value(); - useClassical = abs(v) >= 297; - } + bool useClassical = !useNNUE || + ((pos.count() > 7) + && abs(psq) * 5 > (856 + pos.non_pawn_material() / 64) * (10 + pos.rule50_count())); - // If result of a classical evaluation is much lower than threshold fall back to NNUE - if (useNNUE && !useClassical) + if (useClassical) + v = Evaluation(pos).value(); + else { - int nnueComplexity; - int scale = 1064 + 106 * pos.non_pawn_material() / 5120; - Value optimism = pos.this_thread()->optimism[stm]; - - Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); - // Blend nnue complexity with (semi)classical complexity - nnueComplexity = (104 * nnueComplexity + 131 * abs(nnue - psq)) / 256; - if (complexity) // Return hybrid NNUE complexity to caller - *complexity = nnueComplexity; - - optimism = optimism * (269 + nnueComplexity) / 256; - v = (nnue * scale + optimism * (scale - 754)) / 1024; + int nnueComplexity; + int scale = 1064 + 106 * pos.non_pawn_material() / 5120; + Value optimism = pos.this_thread()->optimism[stm]; + + Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); + // Blend nnue complexity with (semi)classical complexity + nnueComplexity = (104 * nnueComplexity + 131 * abs(nnue - psq)) / 256; + if (complexity) // Return hybrid NNUE complexity to caller + *complexity = nnueComplexity; + + optimism = optimism * (269 + nnueComplexity) / 256; + v = (nnue * scale + optimism * (scale - 754)) / 1024; } // Damp down the evaluation linearly when shuffling @@ -1091,7 +1086,7 @@ Value Eval::evaluate(const Position& pos, int* complexity) { // When not using NNUE, return classical complexity to caller if (complexity && (!useNNUE || useClassical)) - *complexity = abs(v - psq); + *complexity = abs(v - psq); return v; } From 02ef1f4496965b5ad8c26ac6bc18245eaffae2ea Mon Sep 17 00:00:00 2001 From: mstembera Date: Sat, 13 Aug 2022 17:01:11 -0700 Subject: [PATCH 0859/1766] Make key_after() more consistent with key() STC: https://tests.stockfishchess.org/tests/view/62f8547123d42b50a8dac674 LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 176640 W: 47699 L: 47189 D: 81752 Ptnml(0-2): 776, 18599, 49129, 18971, 845 A bug fix plus non functional speed optimization. Position::key_after(Move m) is now consistent with Position::key() thus prefetching correct TT entries which speeds things up. Related PR #3759 closes https://github.com/official-stockfish/Stockfish/pull/4130 No functional change --- src/position.cpp | 5 ++++- src/position.h | 12 ++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index 08ed1a89e15..62e6e2387cb 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1054,7 +1054,10 @@ Key Position::key_after(Move m) const { if (captured) k ^= Zobrist::psq[captured][to]; - return k ^ Zobrist::psq[pc][to] ^ Zobrist::psq[pc][from]; + k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[pc][from]; + + return (captured || type_of(pc) == PAWN) + ? k : adjust_key50(k); } diff --git a/src/position.h b/src/position.h index 510875d86d3..078ff5b79f2 100644 --- a/src/position.h +++ b/src/position.h @@ -185,6 +185,8 @@ class Position { void move_piece(Square from, Square to); template void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto); + template + Key adjust_key50(Key k) const; // Data members Piece board[SQUARE_NB]; @@ -327,8 +329,14 @@ inline int Position::pawns_on_same_color_squares(Color c, Square s) const { } inline Key Position::key() const { - return st->rule50 < 14 ? st->key - : st->key ^ make_key((st->rule50 - 14) / 8); + return adjust_key50(st->key); +} + +template +inline Key Position::adjust_key50(Key k) const +{ + return st->rule50 < 14 - AfterMove + ? k : k ^ make_key((st->rule50 - (14 - AfterMove)) / 8); } inline Key Position::pawn_key() const { From 97860cb575e3e71791b9a99b94f9f7d7a0dbf25e Mon Sep 17 00:00:00 2001 From: dav1312 <63931154+dav1312@users.noreply.github.com> Date: Fri, 26 Aug 2022 11:26:31 +0200 Subject: [PATCH 0860/1766] Disable ARM CI tests Temporarily disable ARM CI tests until a mitigation is implemented closes https://github.com/official-stockfish/Stockfish/pull/4148 No functional change. --- .github/workflows/stockfish.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index 782e3f2b442..b007ec78937 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -50,7 +50,7 @@ jobs: os: ubuntu-20.04, compiler: aarch64-linux-android21-clang++, comp: ndk, - run_armv8_tests: true, + run_armv8_tests: false, shell: 'bash {0}' } - { @@ -58,7 +58,7 @@ jobs: os: ubuntu-20.04, compiler: armv7a-linux-androideabi21-clang++, comp: ndk, - run_armv7_tests: true, + run_armv7_tests: false, shell: 'bash {0}' } - { From dddf8fc2b4867bad8b01fe3a18266c3677f3f726 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 3 Sep 2022 11:15:40 +0200 Subject: [PATCH 0861/1766] Increase the maximum number of threads to 1024 relatively soon servers with 512 threads will be available 'quite commonly', anticipate even more threads, and increase our current maximum from 512 to 1024. closes https://github.com/official-stockfish/Stockfish/pull/4152 No functional change. --- src/ucioption.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 922fa34fe93..9fb48345a06 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -61,7 +61,7 @@ void init(OptionsMap& o) { constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048; o["Debug Log File"] << Option("", on_logger); - o["Threads"] << Option(1, 1, 512, on_threads); + o["Threads"] << Option(1, 1, 1024, on_threads); o["Hash"] << Option(16, 1, MaxHashMB, on_hash_size); o["Clear Hash"] << Option(on_clear_hash); o["Ponder"] << Option(false); From a4d18d23a9f7626234fcfbb6b23b3d0b8d1a9441 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 3 Sep 2022 11:03:09 +0200 Subject: [PATCH 0862/1766] Provide network download fallback in case the base infrastructure for providing the networks https://tests.stockfishchess.org/nns is down, use an alternate github repo for downloading networks during the build. fixes #4149 fixes #4140 closes https://github.com/official-stockfish/Stockfish/pull/4151 No functional change --- src/Makefile | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/src/Makefile b/src/Makefile index ff2452d6ad5..8315f33d6b0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -827,24 +827,34 @@ clean: objclean profileclean net: $(eval nnuenet := $(shell grep EvalFileDefaultName evaluate.h | grep define | sed 's/.*\(nn-[a-z0-9]\{12\}.nnue\).*/\1/')) @echo "Default net: $(nnuenet)" - $(eval nnuedownloadurl := https://tests.stockfishchess.org/api/nn/$(nnuenet)) + $(eval nnuedownloadurl1 := https://tests.stockfishchess.org/api/nn/$(nnuenet)) + $(eval nnuedownloadurl2 := https://github.com/official-stockfish/networks/raw/master/$(nnuenet)) $(eval curl_or_wget := $(shell if hash curl 2>/dev/null; then echo "curl -skL"; elif hash wget 2>/dev/null; then echo "wget -qO-"; fi)) - @if test -f "$(nnuenet)"; then \ - echo "Already available."; \ - else \ - if [ "x$(curl_or_wget)" = "x" ]; then \ - echo "Automatic download failed: neither curl nor wget is installed. Install one of these tools or download the net manually"; exit 1; \ - else \ - echo "Downloading $(nnuedownloadurl)"; $(curl_or_wget) $(nnuedownloadurl) > $(nnuenet);\ - fi; \ - fi; + @if [ "x$(curl_or_wget)" = "x" ]; then \ + echo "Automatic download failed: neither curl nor wget is installed. Install one of these tools or download the net manually"; exit 1; \ + fi $(eval shasum_command := $(shell if hash shasum 2>/dev/null; then echo "shasum -a 256 "; elif hash sha256sum 2>/dev/null; then echo "sha256sum "; fi)) - @if [ "x$(shasum_command)" != "x" ]; then \ - if [ "$(nnuenet)" != "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then \ - echo "Failed download or $(nnuenet) corrupted, please delete!"; exit 1; \ - fi \ - else \ - echo "shasum / sha256sum not found, skipping net validation"; \ + @if [ "x$(shasum_command)" = "x" ]; then \ + echo "shasum / sha256sum not found, skipping net validation"; \ + fi + @for nnuedownloadurl in "$(nnuedownloadurl1)" "$(nnuedownloadurl2)"; do \ + if test -f "$(nnuenet)"; then \ + echo "$(nnuenet) available."; \ + else \ + if [ "x$(curl_or_wget)" != "x" ]; then \ + echo "Downloading $${nnuedownloadurl}"; $(curl_or_wget) $${nnuedownloadurl} > $(nnuenet);\ + fi; \ + fi; \ + if [ "x$(shasum_command)" != "x" ]; then \ + if [ "$(nnuenet)" != "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then \ + echo "Removing failed download"; rm -f $(nnuenet); \ + else \ + echo "Network validated"; break; \ + fi; \ + fi; \ + done + @if ! test -f "$(nnuenet)"; then \ + echo "Failed to download $(nnuenet)."; \ fi # clean binaries and objects From 5eeb96d0e7d54686376305029c477c42478aa1d5 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Wed, 31 Aug 2022 14:45:39 +0300 Subject: [PATCH 0863/1766] VLTC tuning Tuning some parameters that scale well with longer time control: Failed STC: https://tests.stockfishchess.org/tests/view/6313424d8202a039920e130a LLR: -2.94 (-2.94,2.94) <-1.75,0.25> Total: 42680 W: 11231 L: 11540 D: 19909 Ptnml(0-2): 191, 5008, 11232, 4737, 172 Passed LTC: https://tests.stockfishchess.org/tests/view/6311e2cd874169ca52ae7933 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 53448 W: 14782 L: 14437 D: 24229 Ptnml(0-2): 101, 5214, 15740, 5577, 92 Passed VLTC: https://tests.stockfishchess.org/tests/view/6312530cfa99a92e3002c927 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 123336 W: 33465 L: 33007 D: 56864 Ptnml(0-2): 38, 11466, 38204, 11920, 40 closes https://github.com/official-stockfish/Stockfish/pull/4154 Bench: 5609606 --- src/search.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 6d78403ffd9..cd7369c8623 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -63,7 +63,7 @@ namespace { // Futility margin Value futility_margin(Depth d, bool improving) { - return Value(168 * (d - improving)); + return Value(174 * (d - improving)); } // Reductions lookup table, initialized at startup @@ -362,7 +362,7 @@ void Thread::search() { trend = (us == WHITE ? make_score(tr, tr / 2) : -make_score(tr, tr / 2)); - int opt = sigmoid(prev, 8, 17, 144, 13966, 183); + int opt = sigmoid(prev, 8, 17, 144, 15012, 183); optimism[ us] = Value(opt); optimism[~us] = -optimism[us]; } @@ -778,7 +778,7 @@ namespace { // return a fail low. if ( !PvNode && depth <= 7 - && eval < alpha - 348 - 258 * depth * depth) + && eval < alpha - 341 - 267 * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -797,7 +797,7 @@ namespace { // Step 9. Null move search with verification search (~22 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 14695 + && (ss-1)->statScore < 15344 && eval >= beta && eval >= ss->staticEval && ss->staticEval >= beta - 15 * depth - improvement / 15 + 201 + complexity / 24 @@ -808,7 +808,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth, eval and complexity of position - Depth R = std::min(int(eval - beta) / 147, 5) + depth / 3 + 4 - (complexity > 650); + Depth R = std::min(int(eval - beta) / 152, 5) + depth / 3 + 4 - (complexity > 650); ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -844,7 +844,7 @@ namespace { } } - probCutBeta = beta + 179 - 46 * improving; + probCutBeta = beta + 173 - 46 * improving; // Step 10. ProbCut (~4 Elo) // If we have a good enough capture and a reduced search returns a value @@ -906,9 +906,9 @@ namespace { return qsearch(pos, ss, alpha, beta); if ( cutNode - && depth >= 8 + && depth >= 9 && !ttMove) - depth--; + depth -= 2; moves_loop: // When in check, search starts here @@ -1009,7 +1009,7 @@ namespace { && !PvNode && lmrDepth < 6 && !ss->inCheck - && ss->staticEval + 281 + 179 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] + && ss->staticEval + 277 + 187 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 6 < alpha) continue; @@ -1173,7 +1173,7 @@ namespace { + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 4334; + - 4560; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) r -= ss->statScore / 15914; @@ -1188,7 +1188,7 @@ namespace { // Do full depth search when reduced LMR search fails high if (value > alpha && d < newDepth) { - const bool doDeeperSearch = value > (alpha + 78 + 11 * (newDepth - d)); + const bool doDeeperSearch = value > (alpha + 73 + 12 * (newDepth - d)); value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth + doDeeperSearch, !cutNode); int bonus = value > alpha ? stat_bonus(newDepth) @@ -1337,14 +1337,14 @@ namespace { quietsSearched, quietCount, capturesSearched, captureCount, depth); // Bonus for prior countermove that caused the fail low - else if ( (depth >= 4 || PvNode) + else if ( (depth >= 5 || PvNode) && !priorCapture) { //Assign extra bonus if current node is PvNode or cutNode //or fail low was really bad bool extraBonus = PvNode || cutNode - || bestValue < alpha - 70 * depth; + || bestValue < alpha - 66 * depth; update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * (1 + extraBonus)); } From eaf2c8207fff9699201085fec123c114f454f798 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 11 Sep 2022 01:45:37 +0300 Subject: [PATCH 0864/1766] Further LTC tuning of search parameters Tuning done by bigpenor with some hand adjustments on top by Viz. Had a good performance at fixed games 180+1.8: https://tests.stockfishchess.org/tests/view/631836b437f41b13973d7da1 Elo: 1.35 +-1.2 (95%) LOS: 98.6% Total: 60000 W: 16422 L: 16189 D: 27389 Ptnml(0-2): 39, 5335, 18992, 5622, 12 nElo: 3.13 +-2.8 (95%) PairsRatio: 1.05 Passed 60+0.6 8 threads SPRT: https://tests.stockfishchess.org/tests/view/631ba0ff74bc4fe483a99db3 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 29712 W: 8301 L: 8039 D: 13372 Ptnml(0-2): 12, 2318, 9925, 2598, 3 closes https://github.com/official-stockfish/Stockfish/pull/4160 bench 3938073 --- src/search.cpp | 92 +++++++++++++++++++++++++------------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index cd7369c8623..162a0994513 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -63,7 +63,7 @@ namespace { // Futility margin Value futility_margin(Depth d, bool improving) { - return Value(174 * (d - improving)); + return Value(165 * (d - improving)); } // Reductions lookup table, initialized at startup @@ -71,7 +71,7 @@ namespace { Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) { int r = Reductions[d] * Reductions[mn]; - return (r + 1463 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 1010); + return (r + 1642 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 916); } constexpr int futility_move_count(bool improving, Depth depth) { @@ -81,7 +81,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return std::min((8 * d + 240) * d - 276 , 1907); + return std::min((12 * d + 282) * d - 349 , 1594); } // Add a small random component to draw evaluations to avoid 3-fold blindness @@ -158,7 +158,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int((20.81 + std::log(Threads.size()) / 2) * std::log(i)); + Reductions[i] = int((20.26 + std::log(Threads.size()) / 2) * std::log(i)); } @@ -307,10 +307,10 @@ void Thread::search() { multiPV = std::min(multiPV, rootMoves.size()); - complexityAverage.set(174, 1); + complexityAverage.set(155, 1); trend = SCORE_ZERO; - optimism[ us] = Value(39); + optimism[ us] = Value(37); optimism[~us] = -optimism[us]; int searchAgainCounter = 0; @@ -353,16 +353,16 @@ void Thread::search() { if (rootDepth >= 4) { Value prev = rootMoves[pvIdx].averageScore; - delta = Value(16) + int(prev) * prev / 19178; + delta = Value(10) + int(prev) * prev / 15620; alpha = std::max(prev - delta,-VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE); // Adjust trend and optimism based on root move's previousScore - int tr = sigmoid(prev, 3, 8, 90, 125, 1); + int tr = sigmoid(prev, 3, 10, 89, 116, 1); trend = (us == WHITE ? make_score(tr, tr / 2) : -make_score(tr, tr / 2)); - int opt = sigmoid(prev, 8, 17, 144, 15012, 183); + int opt = sigmoid(prev, 7, 20, 169, 19350, 164); optimism[ us] = Value(opt); optimism[~us] = -optimism[us]; } @@ -465,16 +465,16 @@ void Thread::search() { && !Threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (69 + 12 * (mainThread->bestPreviousAverageScore - bestValue) - + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 781.4; + double fallingEval = (71 + 12 * (mainThread->bestPreviousAverageScore - bestValue) + + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 656.7; fallingEval = std::clamp(fallingEval, 0.5, 1.5); // If the bestMove is stable over several iterations, reduce time accordingly - timeReduction = lastBestMoveDepth + 10 < completedDepth ? 1.63 : 0.73; - double reduction = (1.56 + mainThread->previousTimeReduction) / (2.20 * timeReduction); + timeReduction = lastBestMoveDepth + 9 < completedDepth ? 1.37 : 0.65; + double reduction = (1.4 + mainThread->previousTimeReduction) / (2.15 * timeReduction); double bestMoveInstability = 1 + 1.7 * totBestMoveChanges / Threads.size(); int complexity = mainThread->complexityAverage.value(); - double complexPosition = std::min(1.0 + (complexity - 277) / 1819.1, 1.5); + double complexPosition = std::min(1.0 + (complexity - 261) / 1738.7, 1.5); double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability * complexPosition; @@ -495,7 +495,7 @@ void Thread::search() { } else if ( Threads.increaseDepth && !mainThread->ponder - && Time.elapsed() > totalTime * 0.43) + && Time.elapsed() > totalTime * 0.53) Threads.increaseDepth = false; else Threads.increaseDepth = true; @@ -760,7 +760,7 @@ namespace { // Use static evaluation difference to improve quiet move ordering (~3 Elo) if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture) { - int bonus = std::clamp(-16 * int((ss-1)->staticEval + ss->staticEval), -2000, 2000); + int bonus = std::clamp(-19 * int((ss-1)->staticEval + ss->staticEval), -1914, 1914); thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus; } @@ -770,7 +770,7 @@ namespace { // margin and the improving flag are used in various pruning heuristics. improvement = (ss-2)->staticEval != VALUE_NONE ? ss->staticEval - (ss-2)->staticEval : (ss-4)->staticEval != VALUE_NONE ? ss->staticEval - (ss-4)->staticEval - : 175; + : 168; improving = improvement > 0; // Step 7. Razoring. @@ -778,7 +778,7 @@ namespace { // return a fail low. if ( !PvNode && depth <= 7 - && eval < alpha - 341 - 267 * depth * depth) + && eval < alpha - 369 - 254 * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -789,18 +789,18 @@ namespace { // The depth condition is important for mate finding. if ( !ss->ttPv && depth < 8 - && eval - futility_margin(depth, improving) - (ss-1)->statScore / 256 >= beta + && eval - futility_margin(depth, improving) - (ss-1)->statScore / 303 >= beta && eval >= beta - && eval < 26305) // larger than VALUE_KNOWN_WIN, but smaller than TB wins. + && eval < 28031) // larger than VALUE_KNOWN_WIN, but smaller than TB wins. return eval; // Step 9. Null move search with verification search (~22 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 15344 + && (ss-1)->statScore < 17139 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 15 * depth - improvement / 15 + 201 + complexity / 24 + && ss->staticEval >= beta - 20 * depth - improvement / 13 + 233 + complexity / 25 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -808,7 +808,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth, eval and complexity of position - Depth R = std::min(int(eval - beta) / 152, 5) + depth / 3 + 4 - (complexity > 650); + Depth R = std::min(int(eval - beta) / 168, 7) + depth / 3 + 4 - (complexity > 861); ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -844,7 +844,7 @@ namespace { } } - probCutBeta = beta + 173 - 46 * improving; + probCutBeta = beta + 191 - 54 * improving; // Step 10. ProbCut (~4 Elo) // If we have a good enough capture and a reduced search returns a value @@ -913,7 +913,7 @@ namespace { moves_loop: // When in check, search starts here // Step 12. A small Probcut idea, when we are in check (~0 Elo) - probCutBeta = beta + 481; + probCutBeta = beta + 417; if ( ss->inCheck && !PvNode && depth >= 2 @@ -1007,14 +1007,14 @@ namespace { if ( !pos.empty(to_sq(move)) && !givesCheck && !PvNode - && lmrDepth < 6 + && lmrDepth < 7 && !ss->inCheck - && ss->staticEval + 277 + 187 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] + && ss->staticEval + 180 + 201 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 6 < alpha) continue; // SEE based pruning (~9 Elo) - if (!pos.see_ge(move, Value(-203) * depth)) + if (!pos.see_ge(move, Value(-222) * depth)) continue; } else @@ -1032,12 +1032,12 @@ namespace { // Futility pruning: parent node (~9 Elo) if ( !ss->inCheck - && lmrDepth < 11 - && ss->staticEval + 122 + 138 * lmrDepth + history / 60 <= alpha) + && lmrDepth < 13 + && ss->staticEval + 106 + 145 * lmrDepth + history / 52 <= alpha) continue; // Prune moves with negative SEE (~3 Elo) - if (!pos.see_ge(move, Value(-25 * lmrDepth * lmrDepth - 20 * lmrDepth))) + if (!pos.see_ge(move, Value(-24 * lmrDepth * lmrDepth - 15 * lmrDepth))) continue; } } @@ -1052,7 +1052,7 @@ namespace { // 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 ( !rootNode - && depth >= 4 - (thisThread->previousDepth > 27) + 2 * (PvNode && tte->is_pv()) + && depth >= 4 - (thisThread->previousDepth > 24) + 2 * (PvNode && tte->is_pv()) && move == ttMove && !excludedMove // Avoid recursive singular search /* && ttValue != VALUE_NONE Already implicit in the next condition */ @@ -1074,8 +1074,8 @@ namespace { // Avoid search explosion by limiting the number of double extensions if ( !PvNode - && value < singularBeta - 26 - && ss->doubleExtensions <= 8) + && value < singularBeta - 25 + && ss->doubleExtensions <= 9) extension = 2; } @@ -1099,14 +1099,14 @@ namespace { // Check extensions (~1 Elo) else if ( givesCheck && depth > 9 - && abs(ss->staticEval) > 71) + && abs(ss->staticEval) > 82) extension = 1; // Quiet ttMove extensions (~0 Elo) else if ( PvNode && move == ttMove && move == ss->killers[0] - && (*contHist[0])[movedPiece][to_sq(move)] >= 5491) + && (*contHist[0])[movedPiece][to_sq(move)] >= 5177) extension = 1; } @@ -1159,7 +1159,7 @@ namespace { // Decrease reduction for PvNodes based on depth if (PvNode) - r -= 1 + 15 / (3 + depth); + r -= 1 + 11 / (3 + depth); // Decrease reduction if ttMove has been singularly extended (~1 Elo) if (singularQuietLMR) @@ -1173,10 +1173,10 @@ namespace { + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 4560; + - 4433; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - r -= ss->statScore / 15914; + r -= ss->statScore / 13628; // In general we want to cap the LMR depth search at newDepth, but when // reduction is negative, we allow this move a limited search extension @@ -1188,7 +1188,7 @@ namespace { // Do full depth search when reduced LMR search fails high if (value > alpha && d < newDepth) { - const bool doDeeperSearch = value > (alpha + 73 + 12 * (newDepth - d)); + const bool doDeeperSearch = value > (alpha + 64 + 11 * (newDepth - d)); value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth + doDeeperSearch, !cutNode); int bonus = value > alpha ? stat_bonus(newDepth) @@ -1280,8 +1280,8 @@ namespace { alpha = value; // Reduce other moves if we have found at least one score improvement - if ( depth > 2 - && depth < 7 + if ( depth > 1 + && depth < 6 && beta < VALUE_KNOWN_WIN && alpha > -VALUE_KNOWN_WIN) depth -= 1; @@ -1344,7 +1344,7 @@ namespace { //or fail low was really bad bool extraBonus = PvNode || cutNode - || bestValue < alpha - 66 * depth; + || bestValue < alpha - 62 * depth; update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * (1 + extraBonus)); } @@ -1471,7 +1471,7 @@ namespace { if (PvNode && bestValue > alpha) alpha = bestValue; - futilityBase = bestValue + 118; + futilityBase = bestValue + 153; } const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, @@ -1675,8 +1675,8 @@ namespace { if (!pos.capture(bestMove)) { - int bonus2 = bestValue > beta + PawnValueMg ? bonus1 // larger bonus - : stat_bonus(depth); // smaller bonus + int bonus2 = bestValue > beta + 137 ? bonus1 // larger bonus + : stat_bonus(depth); // smaller bonus // Increase stats for the best move in case it was a quiet move update_quiet_stats(pos, ss, bestMove, bonus2); From 9fa258ee1d36d5a432a7a3e857e0723d439528b0 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Fri, 19 Aug 2022 22:49:29 -0500 Subject: [PATCH 0865/1766] Razor also on PV nodes Simplification introduced by xoto10 blue LTC vs new master: https://tests.stockfishchess.org/tests/view/631ad4ef9cfa5e9b648d1b4e LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 59184 W: 16002 L: 15828 D: 27354 Ptnml(0-2): 65, 5777, 17747, 5925, 78 blue STC vs old master: https://tests.stockfishchess.org/tests/view/6306b87b902a848543334c25 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 213944 W: 57184 L: 57159 D: 99601 Ptnml(0-2): 877, 23448, 58331, 23405, 911 blue LTC vs old master: https://tests.stockfishchess.org/tests/view/63070e6b902a8485433357e7 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 192080 W: 52050 L: 52006 D: 88024 Ptnml(0-2): 232, 18981, 57611, 18943, 273 closes https://github.com/official-stockfish/Stockfish/pull/4147 bench 4208975 --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 162a0994513..1257637895f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -776,8 +776,7 @@ namespace { // Step 7. Razoring. // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. - if ( !PvNode - && depth <= 7 + if ( depth <= 7 && eval < alpha - 369 - 254 * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); From 1591e5ac3b24f068f965471f17d7aae33ceaab9f Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 10 Sep 2022 12:30:25 +0300 Subject: [PATCH 0866/1766] Do less singular extensions for former PVnode Patch is a reintroduction of logic what was simplified a while ago in a slightly different form. Do bigger extension offset in case of non-pv node having a pv. passed STC https://tests.stockfishchess.org/tests/view/631977c048f27688a06e66d5 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 23296 W: 6404 L: 6108 D: 10784 Ptnml(0-2): 88, 2539, 6118, 2795, 108 passed LTC https://tests.stockfishchess.org/tests/view/631989cb48f27688a06e696c LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 235592 W: 63890 L: 63188 D: 108514 Ptnml(0-2): 275, 23392, 69804, 24006, 319 closes https://github.com/official-stockfish/Stockfish/pull/4159 Bench: 3993611 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 1257637895f..c998f20d156 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1059,7 +1059,7 @@ namespace { && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - 3 * depth; + Value singularBeta = ttValue - (3 + (ss->ttPv && !PvNode)) * depth; Depth singularDepth = (depth - 1) / 2; ss->excludedMove = move; From 82bb21dc7a198609589ef0cc78d185f00f619a90 Mon Sep 17 00:00:00 2001 From: mstembera Date: Tue, 6 Sep 2022 15:02:35 -0700 Subject: [PATCH 0867/1766] Optimize AVX2 path in NNUE evaluation always selecting AffineTransform specialization for small inputs. A related patch was tested as Initially tested as a simplification STC https://tests.stockfishchess.org/tests/view/6317c3f437f41b13973d6dff LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 58072 W: 15619 L: 15425 D: 27028 Ptnml(0-2): 241, 6191, 15992, 6357, 255 Elo gain speedup test STC https://tests.stockfishchess.org/tests/view/63181c1b37f41b13973d79dc LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 184496 W: 49922 L: 49401 D: 85173 Ptnml(0-2): 851, 19397, 51208, 19964, 828 and this patch gained in testing speedup = +0.0071 P(speedup > 0) = 1.0000 on CPU: 16 x AMD Ryzen 9 3950X closes https://github.com/official-stockfish/Stockfish/pull/4158 No functional change --- src/nnue/layers/affine_transform.h | 16 +++++++++++----- src/{ => nnue/layers}/simd.h | 0 2 files changed, 11 insertions(+), 5 deletions(-) rename src/{ => nnue/layers}/simd.h (100%) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 9a992608cc1..461a7b83eca 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -25,7 +25,7 @@ #include #include #include "../nnue_common.h" -#include "../../simd.h" +#include "simd.h" /* This file contains the definition for a fully connected layer (aka affine transform). @@ -151,9 +151,15 @@ namespace Stockfish::Eval::NNUE::Layers { template class AffineTransform; +#if defined (USE_AVX512) + constexpr IndexType LargeInputSize = 2 * 64; +#else + constexpr IndexType LargeInputSize = std::numeric_limits::max(); +#endif + // A specialization for large inputs. template - class AffineTransform(InDims, MaxSimdWidth) >= 2*64)>> { + class AffineTransform(InDims, MaxSimdWidth) >= LargeInputSize)>> { public: // Input/output type using InputType = std::uint8_t; @@ -170,7 +176,7 @@ namespace Stockfish::Eval::NNUE::Layers { using OutputBuffer = OutputType[PaddedOutputDimensions]; - static_assert(PaddedInputDimensions >= 128, "Something went wrong. This specialization should not have been chosen."); + static_assert(PaddedInputDimensions >= LargeInputSize, "Something went wrong. This specialization should not have been chosen."); #if defined (USE_AVX512) static constexpr const IndexType InputSimdWidth = 64; @@ -369,7 +375,7 @@ namespace Stockfish::Eval::NNUE::Layers { }; template - class AffineTransform(InDims, MaxSimdWidth) < 2*64)>> { + class AffineTransform(InDims, MaxSimdWidth) < LargeInputSize)>> { public: // Input/output type // Input/output type @@ -387,7 +393,7 @@ namespace Stockfish::Eval::NNUE::Layers { using OutputBuffer = OutputType[PaddedOutputDimensions]; - static_assert(PaddedInputDimensions < 128, "Something went wrong. This specialization should not have been chosen."); + static_assert(PaddedInputDimensions < LargeInputSize, "Something went wrong. This specialization should not have been chosen."); #if defined (USE_SSSE3) static constexpr const IndexType OutputSimdWidth = SimdWidth / 4; diff --git a/src/simd.h b/src/nnue/layers/simd.h similarity index 100% rename from src/simd.h rename to src/nnue/layers/simd.h From 5a871e174f22894837c2363b5c215854ee155113 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 11 Sep 2022 21:28:12 +0200 Subject: [PATCH 0868/1766] Explicitly annotate a few variables as [[maybe_unused]], avoiding the (void)foo trick. closes https://github.com/official-stockfish/Stockfish/pull/4162 No functional change --- src/misc.cpp | 6 ++---- src/movepick.cpp | 10 +--------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index 41c59b3fd88..d19cd840bba 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -378,10 +378,9 @@ void std_aligned_free(void* ptr) { #if defined(_WIN32) -static void* aligned_large_pages_alloc_windows(size_t allocSize) { +static void* aligned_large_pages_alloc_windows([[maybe_unused]] size_t allocSize) { #if !defined(_WIN64) - (void)allocSize; // suppress unused-parameter compiler warning return nullptr; #else @@ -626,8 +625,7 @@ string argv0; // path+name of the executable binary, as given by argv string binaryDirectory; // path of the executable directory string workingDirectory; // path of the working directory -void init(int argc, char* argv[]) { - (void)argc; +void init([[maybe_unused]] int argc, char* argv[]) { string pathSeparator; // extract the path+name of the executable binary diff --git a/src/movepick.cpp b/src/movepick.cpp index 60d041ab9c7..d8d0612a585 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -106,7 +106,7 @@ void MovePicker::score() { static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type"); - Bitboard threatened, threatenedByPawn, threatenedByMinor, threatenedByRook; + [[maybe_unused]] Bitboard threatened, threatenedByPawn, threatenedByMinor, threatenedByRook; if constexpr (Type == QUIETS) { Color us = pos.side_to_move(); @@ -122,14 +122,6 @@ void MovePicker::score() { | (pos.pieces(us, ROOK) & threatenedByMinor) | (pos.pieces(us, KNIGHT, BISHOP) & threatenedByPawn); } - else - { - // Silence unused variable warnings - (void) threatened; - (void) threatenedByPawn; - (void) threatenedByMinor; - (void) threatenedByRook; - } for (auto& m : *this) if constexpr (Type == CAPTURES) From 154e7afed0fe9c6f45a2aee8ef6f38d44076cb19 Mon Sep 17 00:00:00 2001 From: atumanian Date: Mon, 12 Sep 2022 20:16:54 +0300 Subject: [PATCH 0869/1766] Simplify trend and optimism. This patch simplifies the formulas used to compute the trend and optimism values before each search iteration. As a side effect, this removes the parameters which make the relationship between the displayed evaluation value and the expected game result asymmetric. I've also provided links to the results of isotonic regression analysis of the relationship between the evaluation and game result (statistical data and a graph) for both tests, which demonstrate that the new version has a more symmetric relationship: STC: [Data and graph](https://github.com/official-stockfish/Stockfish/discussions/4150#discussioncomment-3548954) LTC: [Data and graph](https://github.com/official-stockfish/Stockfish/discussions/4150#discussioncomment-3626311) See also https://github.com/official-stockfish/Stockfish/issues/4142 passed STC: https://tests.stockfishchess.org/tests/view/6313f44b8202a039920e27e6 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 108016 W: 28903 L: 28760 D: 50353 Ptnml(0-2): 461, 12075, 28850, 12104, 518 passed LTC: https://tests.stockfishchess.org/tests/view/631de45db85daa436625dfe6 LLR: 3.01 (-2.94,2.94) <-1.75,0.25> Total: 34792 W: 9412 L: 9209 D: 16171 Ptnml(0-2): 24, 3374, 10397, 3577, 24 Furthermore, this does not measurably impact Elo strength against weaker engines, as demonstrated in a match of master and patch vs SF13: This patch vs SF 13: https://tests.stockfishchess.org/tests/view/631fa34ae1612778c344c6eb Elo: 141.66 +-1.2 (95%) LOS: 100.0% Total: 100000 W: 48182 L: 9528 D: 42290 Ptnml(0-2): 96, 1426, 13277, 30130, 5071 nElo: 284.13 +-3.3 (95%) PairsRatio: 23.13 Master vs SF 13: https://tests.stockfishchess.org/tests/view/631fa3ece1612778c344c6ff Elo: 143.26 +-1.2 (95%) LOS: 100.0% Total: 100000 W: 48525 L: 9479 D: 41996 Ptnml(0-2): 94, 1537, 13098, 29771, 5500 nElo: 281.70 +-3.3 (95%) PairsRatio: 21.63 closes: https://github.com/official-stockfish/Stockfish/pull/4163 Bench: 4425574 --- src/misc.h | 27 --------------------------- src/search.cpp | 9 ++++----- 2 files changed, 4 insertions(+), 32 deletions(-) diff --git a/src/misc.h b/src/misc.h index fe1143def0b..77b81d50fc0 100644 --- a/src/misc.h +++ b/src/misc.h @@ -126,33 +126,6 @@ class ValueList { }; -/// sigmoid(t, x0, y0, C, P, Q) implements a sigmoid-like function using only integers, -/// with the following properties: -/// -/// - sigmoid is centered in (x0, y0) -/// - sigmoid has amplitude [-P/Q , P/Q] instead of [-1 , +1] -/// - limit is (y0 - P/Q) when t tends to -infinity -/// - limit is (y0 + P/Q) when t tends to +infinity -/// - the slope can be adjusted using C > 0, smaller C giving a steeper sigmoid -/// - the slope of the sigmoid when t = x0 is P/(Q*C) -/// - sigmoid is increasing with t when P > 0 and Q > 0 -/// - to get a decreasing sigmoid, change sign of P -/// - mean value of the sigmoid is y0 -/// -/// Use to draw the sigmoid - -inline int64_t sigmoid(int64_t t, int64_t x0, - int64_t y0, - int64_t C, - int64_t P, - int64_t Q) -{ - assert(C > 0); - assert(Q != 0); - return y0 + P * (t-x0) / (Q * (std::abs(t-x0) + C)) ; -} - - /// xorshift64star Pseudo-Random Number Generator /// This class is based on original code written and dedicated /// to the public domain by Sebastiano Vigna (2014). diff --git a/src/search.cpp b/src/search.cpp index c998f20d156..0f524093c2d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -309,9 +309,8 @@ void Thread::search() { complexityAverage.set(155, 1); - trend = SCORE_ZERO; - optimism[ us] = Value(37); - optimism[~us] = -optimism[us]; + trend = SCORE_ZERO; + optimism[us] = optimism[~us] = VALUE_ZERO; int searchAgainCounter = 0; @@ -358,11 +357,11 @@ void Thread::search() { beta = std::min(prev + delta, VALUE_INFINITE); // Adjust trend and optimism based on root move's previousScore - int tr = sigmoid(prev, 3, 10, 89, 116, 1); + int tr = 116 * prev / (std::abs(prev) + 89); trend = (us == WHITE ? make_score(tr, tr / 2) : -make_score(tr, tr / 2)); - int opt = sigmoid(prev, 7, 20, 169, 19350, 164); + int opt = 118 * prev / (std::abs(prev) + 169); optimism[ us] = Value(opt); optimism[~us] = -optimism[us]; } From dc0c441b7c5178baa1bb0cc51b2d0294f58759a4 Mon Sep 17 00:00:00 2001 From: mstembera Date: Wed, 14 Sep 2022 12:11:52 -0700 Subject: [PATCH 0870/1766] Prioritize checks in movepicker give a little bonus for moving pieces to squares where they give check STC: https://tests.stockfishchess.org/tests/view/631da742162491686d2e40b5 LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 80072 W: 21753 L: 21368 D: 36951 Ptnml(0-2): 421, 8876, 21075, 9225, 439 LTC: https://tests.stockfishchess.org/tests/view/631dd9e6b85daa436625de1d LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 263480 W: 70916 L: 70158 D: 122406 Ptnml(0-2): 322, 26156, 78029, 26908, 325 similar ideas have been tested by Viz and Guenther closes https://github.com/official-stockfish/Stockfish/pull/4165 bench: 4326572 --- src/movepick.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index d8d0612a585..636f4ba7987 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -139,8 +139,8 @@ void MovePicker::score() { : type_of(pos.moved_piece(m)) == ROOK && !(to_sq(m) & threatenedByMinor) ? 25000 : !(to_sq(m) & threatenedByPawn) ? 15000 : 0) - : 0); - + : 0) + + bool(pos.check_squares(type_of(pos.moved_piece(m))) & to_sq(m)) * 16384; else // Type == EVASIONS { if (pos.capture(m)) From 29295ecfd357b5421eb4f273761dce8d9661f130 Mon Sep 17 00:00:00 2001 From: mstembera Date: Sat, 17 Sep 2022 05:45:54 -0700 Subject: [PATCH 0871/1766] Simplify EVASIONS scoring remove some multipliers & adjust, doesn't change the move ordering STC https://tests.stockfishchess.org/tests/view/6325c1c9b9c0caa5f4a759ae LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 192760 W: 51528 L: 51482 D: 89750 Ptnml(0-2): 642, 20490, 54148, 20380, 720 Credit to locutus2 closes https://github.com/official-stockfish/Stockfish/pull/4171 No functional change --- src/movepick.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 636f4ba7987..e10454b0d98 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -145,11 +145,11 @@ void MovePicker::score() { { if (pos.capture(m)) m.value = PieceValue[MG][pos.piece_on(to_sq(m))] - - Value(type_of(pos.moved_piece(m))); + - Value(type_of(pos.moved_piece(m))) + + (1 << 28); else - m.value = 2 * (*mainHistory)[pos.side_to_move()][from_to(m)] - + 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] - - (1 << 28); + m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] + + (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]; } } From 70e51a5bc81bd15a73d2cb0be5008c7c02b1b0d5 Mon Sep 17 00:00:00 2001 From: Torsten Hellwig Date: Mon, 19 Sep 2022 18:22:56 +0200 Subject: [PATCH 0872/1766] Always output hashfull This removes the restriction that no hashfull information is printed within the first second of a search. On modern systems, a non-zero value is returned within 6 ms with default settings. passed STC: https://tests.stockfishchess.org/tests/view/63277b08b9c0caa5f4a798e4 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 290096 W: 77505 L: 77561 D: 135030 Ptnml(0-2): 1008, 30713, 81592, 30797, 938 closes https://github.com/official-stockfish/Stockfish/pull/4174 No functional change --- src/search.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 0f524093c2d..be0f3451635 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1860,12 +1860,9 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) { ss << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : ""); ss << " nodes " << nodesSearched - << " nps " << nodesSearched * 1000 / elapsed; - - if (elapsed > 1000) // Earlier makes little sense - ss << " hashfull " << TT.hashfull(); - - ss << " tbhits " << tbHits + << " nps " << nodesSearched * 1000 / elapsed + << " hashfull " << TT.hashfull() + << " tbhits " << tbHits << " time " << elapsed << " pv"; From 4339a756ac0a97563442ee4fb67694a5dfc66da4 Mon Sep 17 00:00:00 2001 From: Brad Knox <64992190+bknox83@users.noreply.github.com> Date: Tue, 20 Sep 2022 16:15:15 -0500 Subject: [PATCH 0873/1766] Update README.md Adding some svg icons and additional information, insert links as references closes https://github.com/official-stockfish/Stockfish/pull/4176 No functional change --- README.md | 114 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 77 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index f84b79d109b..b076ab6bc02 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,27 @@ -## Overview +
+ + [![Stockfish][stockfish128-logo]][website-link] + + [![Build][build-badge]][build-link] + [![License][license-badge]][license-link] +
+ [![Release][release-badge]][release-link] + [![Commits][commits-badge]][commits-link] +
+ [![Website][website-badge]][website-link] + [![Fishtest][fishtest-badge]][fishtest-link] + [![Discord][discord-badge]][discord-link] + +
-[![Build Status](https://github.com/official-stockfish/Stockfish/actions/workflows/stockfish.yml/badge.svg)](https://github.com/official-stockfish/Stockfish/actions) +## Overview -[Stockfish](https://stockfishchess.org) is a free, powerful UCI chess engine -derived from Glaurung 2.1. Stockfish is not a complete chess program and requires a -UCI-compatible graphical user interface (GUI) (e.g. XBoard with PolyGlot, Scid, -Cute Chess, eboard, Arena, Sigma Chess, Shredder, Chess Partner or Fritz) in order -to be used comfortably. Read the documentation for your GUI of choice for information -about how to use Stockfish with it. +[Stockfish][website-link] is a free, powerful UCI chess engine derived from +Glaurung 2.1. Stockfish is not a complete chess program and requires a UCI-compatible +graphical user interface (GUI) (e.g. XBoard with PolyGlot, Scid, Cute Chess, eboard, +Arena, Sigma Chess, Shredder, Chess Partner or Fritz) in order to be used comfortably. +Read the documentation for your GUI of choice for informationabout how to use +Stockfish with it. The Stockfish engine features two evaluation functions for chess. The efficiently updatable neural network (NNUE) based evaluation is the default and by far the strongest. @@ -20,28 +34,25 @@ avx2, neon, or similar). This distribution of Stockfish consists of the following files: - * [README.md](https://github.com/official-stockfish/Stockfish/blob/master/README.md), - the file you are currently reading. + * [README.md][readme-link], the file you are currently reading. - * [Copying.txt](https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt), - a text file containing the GNU General Public License version 3. + * [Copying.txt][license-link], a text file containing the GNU General Public License + version 3. - * [AUTHORS](https://github.com/official-stockfish/Stockfish/blob/master/AUTHORS), - a text file with the list of authors for the project + * [AUTHORS][authors-link], a text file with the list of authors for the project. - * [src](https://github.com/official-stockfish/Stockfish/tree/master/src), - a subdirectory containing the full source code, including a Makefile + * [src][src-link], a subdirectory containing the full source code, including a Makefile that can be used to compile Stockfish on Unix-like systems. - * a file with the .nnue extension, storing the neural network for the NNUE - evaluation. Binary distributions will have this file embedded. + * a file with the .nnue extension, storing the neural network for the NNUE evaluation. + Binary distributions will have this file embedded. ## The UCI protocol and available options The Universal Chess Interface (UCI) is a standard protocol used to communicate with a chess engine, and is the recommended way to do so for typical graphical user interfaces (GUI) or chess tools. Stockfish implements the majority of its options as described -in [the UCI protocol](https://www.shredderchess.com/download/div/uci.zip). +in [the UCI protocol][uci-link]. Developers can see the default values for UCI options available in Stockfish by typing `./stockfish uci` in a terminal, but the majority of users will typically see them and @@ -179,12 +190,10 @@ on the evaluations of millions of positions at moderate search depth. The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward. It can be evaluated efficiently on CPUs, and exploits the fact that only parts of the neural network need to be updated after a typical chess move. -[The nodchip repository](https://github.com/nodchip/Stockfish) provided the first -version of the needed tools to train and develop the NNUE networks. Today, more -advanced training tools are available in -[the nnue-pytorch repository](https://github.com/glinscott/nnue-pytorch/), -while data generation tools are available in -[a dedicated branch](https://github.com/official-stockfish/Stockfish/tree/tools). +[The nodchip repository][nodchip-link] provided the first version of the needed tools +to train and develop the NNUE networks. Today, more advanced training tools are +available in [the nnue-pytorch repository][pytorch-link], while data generation tools +are available in [a dedicated branch][tools-link]. On CPUs supporting modern vector instructions (avx2 and similar), the NNUE evaluation results in much stronger playing strength, even if the nodes per second computed by @@ -250,8 +259,8 @@ are already enabled, and no configuration is needed. ### Support on Windows The use of large pages requires "Lock Pages in Memory" privilege. See -[Enable the Lock Pages in Memory Option (Windows)](https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/enable-the-lock-pages-in-memory-option-windows) -on how to enable this privilege, then run [RAMMap](https://docs.microsoft.com/en-us/sysinternals/downloads/rammap) +[Enable the Lock Pages in Memory Option (Windows)][lockpages-link] +on how to enable this privilege, then run [RAMMap][rammap-link] to double-check that large pages are used. We suggest that you reboot your computer after you have enabled large pages, because long Windows sessions suffer from memory fragmentation, which may prevent Stockfish @@ -294,26 +303,26 @@ effort. There are a few ways to help contribute to its growth. ### Donating hardware Improving Stockfish requires a massive amount of testing. You can donate -your hardware resources by installing the [Fishtest Worker](https://github.com/glinscott/fishtest/wiki/Running-the-worker:-overview) -and view the current tests on [Fishtest](https://tests.stockfishchess.org/tests). +your hardware resources by installing the [Fishtest Worker][worker-link] +and view the current tests on [Fishtest][fishtest-link]. ### Improving the code If you want to help improve the code, there are several valuable resources: -* [In this wiki,](https://www.chessprogramming.org) many techniques used in +* [In this wiki,][programming-link] many techniques used in Stockfish are explained with a lot of background information. -* [The section on Stockfish](https://www.chessprogramming.org/Stockfish) +* [The section on Stockfish][programmingsf-link] describes many features and techniques used by Stockfish. However, it is generic rather than being focused on Stockfish's precise implementation. Nevertheless, a helpful resource. -* The latest source can always be found on [GitHub](https://github.com/official-stockfish/Stockfish). -Discussions about Stockfish take place these days mainly in the [FishCooking](https://groups.google.com/forum/#!forum/fishcooking) -group and on the [Stockfish Discord channel](https://discord.gg/nv8gDtt). -The engine testing is done on [Fishtest](https://tests.stockfishchess.org/tests). -If you want to help improve Stockfish, please read this [guideline](https://github.com/glinscott/fishtest/wiki/Creating-my-first-test) +* The latest source can always be found on [GitHub][github-link]. +Discussions about Stockfish take place these days mainly in the [FishCooking][fishcooking-link] +group and on the [Stockfish Discord channel][discord-link]. +The engine testing is done on [Fishtest][fishtest-link]. +If you want to help improve Stockfish, please read this [guideline][guideline-link] first, where the basics of Stockfish development are explained. @@ -333,4 +342,35 @@ exact binary you are distributing. If you make any changes to the source code, these changes must also be made available under the GPL v3. For full details, read the copy of the GPL v3 found in the file named -[*Copying.txt*](https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt). +[*Copying.txt*][license-link]. + +[authors-link]:https://github.com/official-stockfish/Stockfish/blob/master/AUTHORS +[build-badge]:https://img.shields.io/github/workflow/status/official-stockfish/Stockfish/Stockfish?style=for-the-badge&label=stockfish&logo=github +[build-link]:https://github.com/official-stockfish/Stockfish/actions/workflows/stockfish.yml +[commits-badge]:https://img.shields.io/github/commits-since/official-stockfish/Stockfish/latest?style=for-the-badge +[commits-link]:https://github.com/official-stockfish/Stockfish/commits/master +[discord-badge]:https://img.shields.io/discord/435943710472011776?style=for-the-badge&label=discord&logo=Discord +[discord-link]:https://discord.com/invite/aefaxmq +[fishcooking-link]:https://groups.google.com/g/fishcooking +[fishtest-badge]:https://img.shields.io/website?style=for-the-badge&down_color=red&down_message=Offline&label=Fishtest&up_color=success&up_message=Online&url=https%3A%2F%2Ftests.stockfishchess.org%2Ftests +[fishtest-link]:https://tests.stockfishchess.org/tests +[github-link]:https://github.com/official-stockfish/Stockfish +[guideline-link]:https://github.com/glinscott/fishtest/wiki/Creating-my-first-test +[license-badge]:https://img.shields.io/github/license/official-stockfish/Stockfish?style=for-the-badge&label=license&color=success +[license-link]:https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt +[lockpages-link]:https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/enable-the-lock-pages-in-memory-option-windows +[nodchip-link]:https://github.com/nodchip/Stockfish +[programming-link]:https://www.chessprogramming.org/Main_Page +[programmingsf-link]:https://www.chessprogramming.org/Stockfish +[pytorch-link]:https://github.com/glinscott/nnue-pytorch +[rammap-link]:https://docs.microsoft.com/en-us/sysinternals/downloads/rammap +[readme-link]:https://github.com/official-stockfish/Stockfish/blob/master/README.md +[release-badge]:https://img.shields.io/github/v/release/official-stockfish/Stockfish?style=for-the-badge&label=official%20release +[release-link]:https://github.com/official-stockfish/Stockfish/releases/latest +[src-link]:https://github.com/official-stockfish/Stockfish/tree/master/src +[stockfish128-logo]:https://stockfishchess.org/images/logo/icon_128x128.png +[tools-link]:https://github.com/official-stockfish/Stockfish/tree/tools +[uci-link]:https://www.shredderchess.com/download/div/uci.zip +[website-badge]:https://img.shields.io/website?style=for-the-badge&down_color=red&down_message=Offline&label=website&up_color=success&up_message=Online&url=https%3A%2F%2Fstockfishchess.org +[website-link]:https://stockfishchess.org +[worker-link]:https://github.com/glinscott/fishtest/wiki/Running-the-worker:-overview From 232bf19be43117cdecea054c9a825735f0b47842 Mon Sep 17 00:00:00 2001 From: peregrineshahin Date: Thu, 22 Sep 2022 08:49:21 +0300 Subject: [PATCH 0874/1766] Simplify both position calls in useClassical Simplify the use of classical evaluation when using default settings to only be dependent on piece count and decisive PSQ passed STC: https://tests.stockfishchess.org/tests/view/632d32a7006ef9eb96d86ce9 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 108048 W: 28904 L: 28763 D: 50381 Ptnml(0-2): 383, 12060, 29006, 12183, 392 passed LTC: https://tests.stockfishchess.org/tests/view/632d705a006ef9eb96d87649 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 76600 W: 20671 L: 20516 D: 35413 Ptnml(0-2): 34, 7533, 23023, 7664, 46 Inspired by sorais, credit to him. closes https://github.com/official-stockfish/Stockfish/pull/4177 bench 4173163 --- src/evaluate.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index eaad4d55672..0657088f19c 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1054,12 +1054,10 @@ Value Eval::evaluate(const Position& pos, int* complexity) { Color stm = pos.side_to_move(); Value psq = pos.psq_eg_stm(); - // Deciding between classical and NNUE eval: for high PSQ imbalance we use classical, - // but we switch to NNUE during long shuffling or with high material on the board. - bool useClassical = !useNNUE || - ((pos.count() > 7) - && abs(psq) * 5 > (856 + pos.non_pawn_material() / 64) * (10 + pos.rule50_count())); - + // We use the much less accurate but faster Classical eval when the NNUE + // option is set to false. Otherwise we use the NNUE eval unless the + // PSQ advantage is decisive and several pieces remain (~3 Elo) + bool useClassical = !useNNUE || (pos.count() > 7 && abs(psq) > 1760); if (useClassical) v = Evaluation(pos).value(); else From f436bf77ad2eb42228747d9aa58eeb7403e23d49 Mon Sep 17 00:00:00 2001 From: disservin <45608332+Disservin@users.noreply.github.com> Date: Sun, 18 Sep 2022 11:16:54 +0200 Subject: [PATCH 0875/1766] Use less reduction for escaping moves This patch reuses the threatenedPieces variable (which is calculated in movepicker) to reduce less in the search tree the moves which escape a capture. passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 314352 W: 84042 L: 83328 D: 146982 Ptnml(0-2): 1105, 35084, 84207, 35552, 1228 https://tests.stockfishchess.org/tests/view/63355f37a004bed9a2e4a17f passed LTC: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 90752 W: 24556 L: 24147 D: 42049 Ptnml(0-2): 59, 8855, 27123, 9296, 43 https://tests.stockfishchess.org/tests/view/63383a7735f43d649ff5fa8b closes https://github.com/official-stockfish/Stockfish/pull/4181 bench: 4114228 --- src/evaluate.cpp | 2 +- src/movepick.cpp | 17 ++++++++--------- src/movepick.h | 2 ++ src/search.cpp | 9 +++++++-- src/thread.cpp | 2 +- src/uci.cpp | 4 ++-- 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 0657088f19c..85700bcc60d 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1054,7 +1054,7 @@ Value Eval::evaluate(const Position& pos, int* complexity) { Color stm = pos.side_to_move(); Value psq = pos.psq_eg_stm(); - // We use the much less accurate but faster Classical eval when the NNUE + // We use the much less accurate but faster Classical eval when the NNUE // option is set to false. Otherwise we use the NNUE eval unless the // PSQ advantage is decisive and several pieces remain (~3 Elo) bool useClassical = !useNNUE || (pos.count() > 7 && abs(psq) > 1760); diff --git a/src/movepick.cpp b/src/movepick.cpp index e10454b0d98..3428a764f1a 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -69,6 +69,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist stage = (pos.checkers() ? EVASION_TT : MAIN_TT) + !(ttm && pos.pseudo_legal(ttm)); + threatenedPieces = 0; } /// MovePicker constructor for quiescence search @@ -106,21 +107,19 @@ void MovePicker::score() { static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type"); - [[maybe_unused]] Bitboard threatened, threatenedByPawn, threatenedByMinor, threatenedByRook; + [[maybe_unused]] Bitboard threatenedByPawn, threatenedByMinor, threatenedByRook; if constexpr (Type == QUIETS) { Color us = pos.side_to_move(); - // squares threatened by pawns + threatenedByPawn = pos.attacks_by(~us); - // squares threatened by minors or pawns threatenedByMinor = pos.attacks_by(~us) | pos.attacks_by(~us) | threatenedByPawn; - // squares threatened by rooks, minors or pawns threatenedByRook = pos.attacks_by(~us) | threatenedByMinor; - // pieces threatened by pieces of lesser material value - threatened = (pos.pieces(us, QUEEN) & threatenedByRook) - | (pos.pieces(us, ROOK) & threatenedByMinor) - | (pos.pieces(us, KNIGHT, BISHOP) & threatenedByPawn); + // Pieces threatened by pieces of lesser material value + threatenedPieces = (pos.pieces(us, QUEEN) & threatenedByRook) + | (pos.pieces(us, ROOK) & threatenedByMinor) + | (pos.pieces(us, KNIGHT, BISHOP) & threatenedByPawn); } for (auto& m : *this) @@ -134,7 +133,7 @@ void MovePicker::score() { + (*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)] - + (threatened & from_sq(m) ? + + (threatenedPieces & from_sq(m) ? (type_of(pos.moved_piece(m)) == QUEEN && !(to_sq(m) & threatenedByRook) ? 50000 : type_of(pos.moved_piece(m)) == ROOK && !(to_sq(m) & threatenedByMinor) ? 25000 : !(to_sq(m) & threatenedByPawn) ? 15000 diff --git a/src/movepick.h b/src/movepick.h index 979709ae682..55fcc6442dc 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -131,6 +131,8 @@ class MovePicker { MovePicker(const Position&, Move, Value, Depth, const CapturePieceToHistory*); Move next_move(bool skipQuiets = false); + Bitboard threatenedPieces; + private: template Move select(Pred); template void score(); diff --git a/src/search.cpp b/src/search.cpp index be0f3451635..4b6b497a5b4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -789,7 +789,7 @@ namespace { && depth < 8 && eval - futility_margin(depth, improving) - (ss-1)->statScore / 303 >= beta && eval >= beta - && eval < 28031) // larger than VALUE_KNOWN_WIN, but smaller than TB wins. + && eval < 28031) // larger than VALUE_KNOWN_WIN, but smaller than TB wins return eval; // Step 9. Null move search with verification search (~22 Elo) @@ -1163,7 +1163,12 @@ namespace { if (singularQuietLMR) r--; - // Increase reduction if next ply has a lot of fail high else reset count to 0 + // Dicrease reduction if we move a threatened piece (~1 Elo) + if ( depth > 9 + && (mp.threatenedPieces & from_sq(move))) + r--; + + // Increase reduction if next ply has a lot of fail high if ((ss+1)->cutoffCnt > 3 && !PvNode) r++; diff --git a/src/thread.cpp b/src/thread.cpp index c834fa9f9df..288588b0ee8 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -61,7 +61,7 @@ void Thread::clear() { mainHistory.fill(0); captureHistory.fill(0); previousDepth = 0; - + for (bool inCheck : { false, true }) for (StatsType c : { NoCaptures, Captures }) { diff --git a/src/uci.cpp b/src/uci.cpp index ec106ee9f40..d5e2c2c3586 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -224,7 +224,7 @@ namespace { /// UCI::loop() waits for a command from the stdin, parses it and then calls the appropriate /// function. It also intercepts an end-of-file (EOF) indication from the stdin to ensure a -/// graceful exit if the GUI dies unexpectedly. When called with some command-line arguments, +/// graceful exit if the GUI dies unexpectedly. When called with some command-line arguments, /// like running 'bench', the function returns immediately after the command is executed. /// In addition to the UCI ones, some additional debug commands are also supported. @@ -240,7 +240,7 @@ void UCI::loop(int argc, char* argv[]) { cmd += std::string(argv[i]) + " "; do { - if (argc == 1 && !getline(cin, cmd)) // Wait for an input or an end-of-file (EOF) indication + if (argc == 1 && !getline(cin, cmd)) // Wait for an input or an end-of-file (EOF) indication cmd = "quit"; istringstream is(cmd); From 8bab09749dd00951bfa9c5f89f6e35bded76c8a9 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Mon, 3 Oct 2022 17:45:05 +0300 Subject: [PATCH 0876/1766] Mix alpha and statScore for reduction Idea by @xoto10, and tuning by @FauziAkram. Passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 57832 W: 15540 L: 15199 D: 27093 Ptnml(0-2): 207, 6343, 15477, 6680, 209 https://tests.stockfishchess.org/tests/view/6338db6f35f43d649ff60fdc passed LTC: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 50968 W: 13770 L: 13440 D: 23758 Ptnml(0-2): 25, 4905, 15306, 5211, 37 https://tests.stockfishchess.org/tests/view/6339777035f43d649ff62686 Links to the tuning sessions: https://tests.stockfishchess.org/tests/view/63345725a004bed9a2e47b28 https://tests.stockfishchess.org/tests/view/63345728a004bed9a2e47b2a closes https://github.com/official-stockfish/Stockfish/pull/4183 Bench: 4426602 --- src/search.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 4b6b497a5b4..46463b327e1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -787,9 +787,9 @@ namespace { // The depth condition is important for mate finding. if ( !ss->ttPv && depth < 8 - && eval - futility_margin(depth, improving) - (ss-1)->statScore / 303 >= beta + && eval - futility_margin(depth, improving) - (ss-1)->statScore / 301 >= beta && eval >= beta - && eval < 28031) // larger than VALUE_KNOWN_WIN, but smaller than TB wins + && eval < 28692) // larger than VALUE_KNOWN_WIN, but smaller than TB wins return eval; // Step 9. Null move search with verification search (~22 Elo) @@ -1179,7 +1179,7 @@ namespace { - 4433; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - r -= ss->statScore / 13628; + r -= (ss->statScore + 5 * alpha) / 15448; // In general we want to cap the LMR depth search at newDepth, but when // reduction is negative, we allow this move a limited search extension From da937e219ee7981966ac29fc11c43470a505ff18 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 5 Oct 2022 22:59:05 +0200 Subject: [PATCH 0877/1766] Revert "Mix alpha and statScore for reduction" This reverts commit 8bab09749dd00951bfa9c5f89f6e35bded76c8a9. In this form the patch reduces mate finding effectiveness, as the large alpha value has negative influence on the reductions. see also https://github.com/official-stockfish/Stockfish/pull/4183 Bench: 4114228 --- src/search.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 46463b327e1..4b6b497a5b4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -787,9 +787,9 @@ namespace { // The depth condition is important for mate finding. if ( !ss->ttPv && depth < 8 - && eval - futility_margin(depth, improving) - (ss-1)->statScore / 301 >= beta + && eval - futility_margin(depth, improving) - (ss-1)->statScore / 303 >= beta && eval >= beta - && eval < 28692) // larger than VALUE_KNOWN_WIN, but smaller than TB wins + && eval < 28031) // larger than VALUE_KNOWN_WIN, but smaller than TB wins return eval; // Step 9. Null move search with verification search (~22 Elo) @@ -1179,7 +1179,7 @@ namespace { - 4433; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - r -= (ss->statScore + 5 * alpha) / 15448; + r -= ss->statScore / 13628; // In general we want to cap the LMR depth search at newDepth, but when // reduction is negative, we allow this move a limited search extension From d5271af0ee28cb52e1033f183f3b34754aa5efa0 Mon Sep 17 00:00:00 2001 From: Giacomo Lorenzetti Date: Wed, 5 Oct 2022 13:08:00 +0200 Subject: [PATCH 0878/1766] Remove old line in "Futility pruning for captures" The line is no longer needed after https://github.com/official-stockfish/Stockfish/commit/910cf8b21839eb9f1991934a5436eea112021723. This patch incidentally applies "Futility Pruning for Captures" also in case of en-passant, changing the bench signature. Passed STC: https://tests.stockfishchess.org/tests/view/6332c1f1208c26088697b731 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 68760 W: 18440 L: 18256 D: 32064 Ptnml(0-2): 267, 7530, 18595, 7728, 260 Passed LTC: https://tests.stockfishchess.org/tests/view/633312e9208c26088697c59b LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 455552 W: 121910 L: 122123 D: 211519 Ptnml(0-2): 253, 45439, 136600, 45236, 248 closes https://github.com/official-stockfish/Stockfish/pull/4185 Bench: 4374521 --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 4b6b497a5b4..7019635dd34 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1002,8 +1002,7 @@ namespace { || givesCheck) { // Futility pruning for captures (~0 Elo) - if ( !pos.empty(to_sq(move)) - && !givesCheck + if ( !givesCheck && !PvNode && lmrDepth < 7 && !ss->inCheck From e90341f9c9cd24e75aba2485b2ab1e445f4a5b70 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Tue, 4 Oct 2022 18:16:20 +0200 Subject: [PATCH 0879/1766] Tweak history initialization Simplify initialization of continuation history by using everywhere the same starting value. STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 90952 W: 24312 L: 24153 D: 42487 Ptnml(0-2): 356, 10168, 24290, 10285, 377 https://tests.stockfishchess.org/tests/view/633948f235f43d649ff61fd0 LTC: LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 162416 W: 43540 L: 43466 D: 75410 Ptnml(0-2): 77, 16289, 48417, 16333, 92 https://tests.stockfishchess.org/tests/view/6339ee8a35f43d649ff63986 closes https://github.com/official-stockfish/Stockfish/pull/4186 Bench: 4156027 --- src/search.cpp | 4 ++-- src/search.h | 3 --- src/thread.cpp | 7 ++----- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 7019635dd34..3c2ae3e52df 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1549,8 +1549,8 @@ namespace { // Continuation history based pruning (~2 Elo) if ( !capture && bestValue > VALUE_TB_LOSS_IN_MAX_PLY - && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < CounterMovePruneThreshold - && (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < CounterMovePruneThreshold) + && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < 0 + && (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < 0) continue; // movecount pruning for quiet check evasions diff --git a/src/search.h b/src/search.h index 4ad5784f8db..f264bcae840 100644 --- a/src/search.h +++ b/src/search.h @@ -31,9 +31,6 @@ class Position; namespace Search { -/// Threshold used for countermoves based pruning -constexpr int CounterMovePruneThreshold = 0; - /// Stack struct keeps track of the information we need to remember from nodes /// shallower and deeper in the tree during the search. Each search thread has diff --git a/src/thread.cpp b/src/thread.cpp index 288588b0ee8..9ce408e059e 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -64,12 +64,9 @@ void Thread::clear() { for (bool inCheck : { false, true }) for (StatsType c : { NoCaptures, Captures }) - { for (auto& to : continuationHistory[inCheck][c]) - for (auto& h : to) - h->fill(-71); - continuationHistory[inCheck][c][NO_PIECE][0]->fill(Search::CounterMovePruneThreshold - 1); - } + for (auto& h : to) + h->fill(-71); } From 93f71ecfe1d26e5ccc813318f420b8363cd26003 Mon Sep 17 00:00:00 2001 From: mstembera Date: Fri, 14 Oct 2022 14:41:08 -0700 Subject: [PATCH 0880/1766] Optimize make_index() using templates and lookup tables. https://tests.stockfishchess.org/tests/view/634517e54bc7650f07542f99 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 642672 W: 171819 L: 170658 D: 300195 Ptnml(0-2): 2278, 68077, 179416, 69336, 2229 this also introduces `-flto-partition=one` as suggested by MinetaS (Syine Mineta) to avoid linking errors due to LTO on 32 bit mingw. This change was tested in isolation as well https://tests.stockfishchess.org/tests/view/634aacf84bc7650f0755188b LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 119352 W: 31986 L: 31862 D: 55504 Ptnml(0-2): 439, 12624, 33400, 12800, 413 closes https://github.com/official-stockfish/Stockfish/pull/4199 No functional change --- src/Makefile | 4 +- src/nnue/features/half_ka_v2_hm.cpp | 33 ++++++++------- src/nnue/features/half_ka_v2_hm.h | 62 +++++++++++++++++++-------- src/nnue/nnue_feature_transformer.h | 65 +++++++++++++++-------------- 4 files changed, 96 insertions(+), 68 deletions(-) diff --git a/src/Makefile b/src/Makefile index 8315f33d6b0..880710fe8a7 100644 --- a/src/Makefile +++ b/src/Makefile @@ -698,11 +698,9 @@ ifeq ($(debug), no) # To use LTO and static linking on Windows, # the tool chain requires gcc version 10.1 or later. else ifeq ($(comp),mingw) - ifneq ($(arch),i386) - CXXFLAGS += -flto + CXXFLAGS += -flto -flto-partition=one LDFLAGS += $(CXXFLAGS) -save-temps endif - endif endif endif diff --git a/src/nnue/features/half_ka_v2_hm.cpp b/src/nnue/features/half_ka_v2_hm.cpp index 07a1d7a154d..11e05c9410a 100644 --- a/src/nnue/features/half_ka_v2_hm.cpp +++ b/src/nnue/features/half_ka_v2_hm.cpp @@ -24,50 +24,51 @@ namespace Stockfish::Eval::NNUE::Features { - // Orient a square according to perspective (rotates by 180 for black) - inline Square HalfKAv2_hm::orient(Color perspective, Square s, Square ksq) { - return Square(int(s) ^ (bool(perspective) * SQ_A8) ^ ((file_of(ksq) < FILE_E) * SQ_H1)); - } - // Index of a feature for a given king position and another piece on some square - inline IndexType HalfKAv2_hm::make_index(Color perspective, Square s, Piece pc, Square ksq) { - Square o_ksq = orient(perspective, ksq, ksq); - return IndexType(orient(perspective, s, ksq) + PieceSquareIndex[perspective][pc] + PS_NB * KingBuckets[o_ksq]); + template + inline IndexType HalfKAv2_hm::make_index(Square s, Piece pc, Square ksq) { + return IndexType((int(s) ^ OrientTBL[Perspective][ksq]) + PieceSquareIndex[Perspective][pc] + KingBuckets[Perspective][ksq]); } // Get a list of indices for active features + template void HalfKAv2_hm::append_active_indices( const Position& pos, - Color perspective, IndexList& active ) { - Square ksq = pos.square(perspective); + Square ksq = pos.square(Perspective); Bitboard bb = pos.pieces(); while (bb) { Square s = pop_lsb(bb); - active.push_back(make_index(perspective, s, pos.piece_on(s), ksq)); + active.push_back(make_index(s, pos.piece_on(s), ksq)); } } - + // Explicit template instantiations + template void HalfKAv2_hm::append_active_indices(const Position& pos, IndexList& active); + template void HalfKAv2_hm::append_active_indices(const Position& pos, IndexList& active); + // append_changed_indices() : get a list of indices for recently changed features - + template void HalfKAv2_hm::append_changed_indices( Square ksq, const DirtyPiece& dp, - Color perspective, IndexList& removed, IndexList& added ) { for (int i = 0; i < dp.dirty_num; ++i) { if (dp.from[i] != SQ_NONE) - removed.push_back(make_index(perspective, dp.from[i], dp.piece[i], ksq)); + removed.push_back(make_index(dp.from[i], dp.piece[i], ksq)); if (dp.to[i] != SQ_NONE) - added.push_back(make_index(perspective, dp.to[i], dp.piece[i], ksq)); + added.push_back(make_index(dp.to[i], dp.piece[i], ksq)); } } + // Explicit template instantiations + template void HalfKAv2_hm::append_changed_indices(Square ksq, const DirtyPiece& dp, IndexList& removed, IndexList& added); + template void HalfKAv2_hm::append_changed_indices(Square ksq, const DirtyPiece& dp, IndexList& removed, IndexList& added); + int HalfKAv2_hm::update_cost(const StateInfo* st) { return st->dirtyPiece.dirty_num; } diff --git a/src/nnue/features/half_ka_v2_hm.h b/src/nnue/features/half_ka_v2_hm.h index 1e6da0bfe2b..a95d4328bb4 100644 --- a/src/nnue/features/half_ka_v2_hm.h +++ b/src/nnue/features/half_ka_v2_hm.h @@ -49,8 +49,8 @@ namespace Stockfish::Eval::NNUE::Features { PS_B_ROOK = 7 * SQUARE_NB, PS_W_QUEEN = 8 * SQUARE_NB, PS_B_QUEEN = 9 * SQUARE_NB, - PS_KING = 10 * SQUARE_NB, - PS_NB = 11 * SQUARE_NB + PS_KING = 10 * SQUARE_NB, + PS_NB = 11 * SQUARE_NB }; static constexpr IndexType PieceSquareIndex[COLOR_NB][PIECE_NB] = { @@ -62,11 +62,9 @@ namespace Stockfish::Eval::NNUE::Features { PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE } }; - // Orient a square according to perspective (rotates by 180 for black) - static Square orient(Color perspective, Square s, Square ksq); - // Index of a feature for a given king position and another piece on some square - static IndexType make_index(Color perspective, Square s, Piece pc, Square ksq); + template + static IndexType make_index(Square s, Piece pc, Square ksq); public: // Feature name @@ -79,15 +77,45 @@ namespace Stockfish::Eval::NNUE::Features { static constexpr IndexType Dimensions = static_cast(SQUARE_NB) * static_cast(PS_NB) / 2; - static constexpr int KingBuckets[64] = { - -1, -1, -1, -1, 31, 30, 29, 28, - -1, -1, -1, -1, 27, 26, 25, 24, - -1, -1, -1, -1, 23, 22, 21, 20, - -1, -1, -1, -1, 19, 18, 17, 16, - -1, -1, -1, -1, 15, 14, 13, 12, - -1, -1, -1, -1, 11, 10, 9, 8, - -1, -1, -1, -1, 7, 6, 5, 4, - -1, -1, -1, -1, 3, 2, 1, 0 +#define B(v) (v * PS_NB) + static constexpr int KingBuckets[COLOR_NB][SQUARE_NB] = { + { B(28), B(29), B(30), B(31), B(31), B(30), B(29), B(28), + B(24), B(25), B(26), B(27), B(27), B(26), B(25), B(24), + B(20), B(21), B(22), B(23), B(23), B(22), B(21), B(20), + B(16), B(17), B(18), B(19), B(19), B(18), B(17), B(16), + B(12), B(13), B(14), B(15), B(15), B(14), B(13), B(12), + B( 8), B( 9), B(10), B(11), B(11), B(10), B( 9), B( 8), + B( 4), B( 5), B( 6), B( 7), B( 7), B( 6), B( 5), B( 4), + B( 0), B( 1), B( 2), B( 3), B( 3), B( 2), B( 1), B( 0) }, + { B( 0), B( 1), B( 2), B( 3), B( 3), B( 2), B( 1), B( 0), + B( 4), B( 5), B( 6), B( 7), B( 7), B( 6), B( 5), B( 4), + B( 8), B( 9), B(10), B(11), B(11), B(10), B( 9), B( 8), + B(12), B(13), B(14), B(15), B(15), B(14), B(13), B(12), + B(16), B(17), B(18), B(19), B(19), B(18), B(17), B(16), + B(20), B(21), B(22), B(23), B(23), B(22), B(21), B(20), + B(24), B(25), B(26), B(27), B(27), B(26), B(25), B(24), + B(28), B(29), B(30), B(31), B(31), B(30), B(29), B(28) } + }; +#undef B + + // Orient a square according to perspective (rotates by 180 for black) + static constexpr int OrientTBL[COLOR_NB][SQUARE_NB] = { + { SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1, + SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1, + SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1, + SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1, + SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1, + SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1, + SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1, + SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1 }, + { SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8, + SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8, + SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8, + SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8, + SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8, + SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8, + SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8, + SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8 } }; // Maximum number of simultaneously active features. @@ -95,16 +123,16 @@ namespace Stockfish::Eval::NNUE::Features { using IndexList = ValueList; // Get a list of indices for active features + template static void append_active_indices( const Position& pos, - Color perspective, IndexList& active); // Get a list of indices for recently changed features + template static void append_changed_indices( Square ksq, const DirtyPiece& dp, - Color perspective, IndexList& removed, IndexList& added ); diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 34d7292c006..b6dd54d3378 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -271,8 +271,8 @@ namespace Stockfish::Eval::NNUE { // Convert input features std::int32_t transform(const Position& pos, OutputType* output, int bucket) const { - update_accumulator(pos, WHITE); - update_accumulator(pos, BLACK); + update_accumulator(pos); + update_accumulator(pos); const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()}; const auto& accumulation = pos.state()->accumulator.accumulation; @@ -338,7 +338,8 @@ namespace Stockfish::Eval::NNUE { private: - void update_accumulator(const Position& pos, const Color perspective) const { + template + void update_accumulator(const Position& pos) const { // The size must be enough to contain the largest possible update. // That might depend on the feature set and generally relies on the @@ -356,18 +357,18 @@ namespace Stockfish::Eval::NNUE { // of the estimated gain in terms of features to be added/subtracted. StateInfo *st = pos.state(), *next = nullptr; int gain = FeatureSet::refresh_cost(pos); - while (st->previous && !st->accumulator.computed[perspective]) + while (st->previous && !st->accumulator.computed[Perspective]) { // This governs when a full feature refresh is needed and how many // updates are better than just one full refresh. - if ( FeatureSet::requires_refresh(st, perspective) + if ( FeatureSet::requires_refresh(st, Perspective) || (gain -= FeatureSet::update_cost(st) + 1) < 0) break; next = st; st = st->previous; } - if (st->accumulator.computed[perspective]) + if (st->accumulator.computed[Perspective]) { if (next == nullptr) return; @@ -376,17 +377,17 @@ namespace Stockfish::Eval::NNUE { // accumulator. Then, we update the current accumulator (pos.state()). // Gather all features to be updated. - const Square ksq = pos.square(perspective); + const Square ksq = pos.square(Perspective); FeatureSet::IndexList removed[2], added[2]; - FeatureSet::append_changed_indices( - ksq, next->dirtyPiece, perspective, removed[0], added[0]); + FeatureSet::append_changed_indices( + ksq, next->dirtyPiece, removed[0], added[0]); for (StateInfo *st2 = pos.state(); st2 != next; st2 = st2->previous) - FeatureSet::append_changed_indices( - ksq, st2->dirtyPiece, perspective, removed[1], added[1]); + FeatureSet::append_changed_indices( + ksq, st2->dirtyPiece, removed[1], added[1]); // Mark the accumulators as computed. - next->accumulator.computed[perspective] = true; - pos.state()->accumulator.computed[perspective] = true; + next->accumulator.computed[Perspective] = true; + pos.state()->accumulator.computed[Perspective] = true; // Now update the accumulators listed in states_to_update[], where the last element is a sentinel. StateInfo *states_to_update[3] = @@ -396,7 +397,7 @@ namespace Stockfish::Eval::NNUE { { // Load accumulator auto accTile = reinterpret_cast( - &st->accumulator.accumulation[perspective][j * TileHeight]); + &st->accumulator.accumulation[Perspective][j * TileHeight]); for (IndexType k = 0; k < NumRegs; ++k) acc[k] = vec_load(&accTile[k]); @@ -422,7 +423,7 @@ namespace Stockfish::Eval::NNUE { // Store accumulator accTile = reinterpret_cast( - &states_to_update[i]->accumulator.accumulation[perspective][j * TileHeight]); + &states_to_update[i]->accumulator.accumulation[Perspective][j * TileHeight]); for (IndexType k = 0; k < NumRegs; ++k) vec_store(&accTile[k], acc[k]); } @@ -432,7 +433,7 @@ namespace Stockfish::Eval::NNUE { { // Load accumulator auto accTilePsqt = reinterpret_cast( - &st->accumulator.psqtAccumulation[perspective][j * PsqtTileHeight]); + &st->accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); for (std::size_t k = 0; k < NumPsqtRegs; ++k) psqt[k] = vec_load_psqt(&accTilePsqt[k]); @@ -458,7 +459,7 @@ namespace Stockfish::Eval::NNUE { // Store accumulator accTilePsqt = reinterpret_cast( - &states_to_update[i]->accumulator.psqtAccumulation[perspective][j * PsqtTileHeight]); + &states_to_update[i]->accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); for (std::size_t k = 0; k < NumPsqtRegs; ++k) vec_store_psqt(&accTilePsqt[k], psqt[k]); } @@ -467,12 +468,12 @@ namespace Stockfish::Eval::NNUE { #else for (IndexType i = 0; states_to_update[i]; ++i) { - std::memcpy(states_to_update[i]->accumulator.accumulation[perspective], - st->accumulator.accumulation[perspective], + std::memcpy(states_to_update[i]->accumulator.accumulation[Perspective], + st->accumulator.accumulation[Perspective], HalfDimensions * sizeof(BiasType)); for (std::size_t k = 0; k < PSQTBuckets; ++k) - states_to_update[i]->accumulator.psqtAccumulation[perspective][k] = st->accumulator.psqtAccumulation[perspective][k]; + states_to_update[i]->accumulator.psqtAccumulation[Perspective][k] = st->accumulator.psqtAccumulation[Perspective][k]; st = states_to_update[i]; @@ -482,10 +483,10 @@ namespace Stockfish::Eval::NNUE { const IndexType offset = HalfDimensions * index; for (IndexType j = 0; j < HalfDimensions; ++j) - st->accumulator.accumulation[perspective][j] -= weights[offset + j]; + st->accumulator.accumulation[Perspective][j] -= weights[offset + j]; for (std::size_t k = 0; k < PSQTBuckets; ++k) - st->accumulator.psqtAccumulation[perspective][k] -= psqtWeights[index * PSQTBuckets + k]; + st->accumulator.psqtAccumulation[Perspective][k] -= psqtWeights[index * PSQTBuckets + k]; } // Difference calculation for the activated features @@ -494,10 +495,10 @@ namespace Stockfish::Eval::NNUE { const IndexType offset = HalfDimensions * index; for (IndexType j = 0; j < HalfDimensions; ++j) - st->accumulator.accumulation[perspective][j] += weights[offset + j]; + st->accumulator.accumulation[Perspective][j] += weights[offset + j]; for (std::size_t k = 0; k < PSQTBuckets; ++k) - st->accumulator.psqtAccumulation[perspective][k] += psqtWeights[index * PSQTBuckets + k]; + st->accumulator.psqtAccumulation[Perspective][k] += psqtWeights[index * PSQTBuckets + k]; } } #endif @@ -506,9 +507,9 @@ namespace Stockfish::Eval::NNUE { { // Refresh the accumulator auto& accumulator = pos.state()->accumulator; - accumulator.computed[perspective] = true; + accumulator.computed[Perspective] = true; FeatureSet::IndexList active; - FeatureSet::append_active_indices(pos, perspective, active); + FeatureSet::append_active_indices(pos, active); #ifdef VECTOR for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) @@ -528,7 +529,7 @@ namespace Stockfish::Eval::NNUE { } auto accTile = reinterpret_cast( - &accumulator.accumulation[perspective][j * TileHeight]); + &accumulator.accumulation[Perspective][j * TileHeight]); for (unsigned k = 0; k < NumRegs; k++) vec_store(&accTile[k], acc[k]); } @@ -548,27 +549,27 @@ namespace Stockfish::Eval::NNUE { } auto accTilePsqt = reinterpret_cast( - &accumulator.psqtAccumulation[perspective][j * PsqtTileHeight]); + &accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); for (std::size_t k = 0; k < NumPsqtRegs; ++k) vec_store_psqt(&accTilePsqt[k], psqt[k]); } #else - std::memcpy(accumulator.accumulation[perspective], biases, + std::memcpy(accumulator.accumulation[Perspective], biases, HalfDimensions * sizeof(BiasType)); for (std::size_t k = 0; k < PSQTBuckets; ++k) - accumulator.psqtAccumulation[perspective][k] = 0; + accumulator.psqtAccumulation[Perspective][k] = 0; for (const auto index : active) { const IndexType offset = HalfDimensions * index; for (IndexType j = 0; j < HalfDimensions; ++j) - accumulator.accumulation[perspective][j] += weights[offset + j]; + accumulator.accumulation[Perspective][j] += weights[offset + j]; for (std::size_t k = 0; k < PSQTBuckets; ++k) - accumulator.psqtAccumulation[perspective][k] += psqtWeights[index * PSQTBuckets + k]; + accumulator.psqtAccumulation[Perspective][k] += psqtWeights[index * PSQTBuckets + k]; } #endif } From f97a86e213fd352a1a7017e5c98608cefc3ef1fa Mon Sep 17 00:00:00 2001 From: Dubslow Date: Fri, 7 Oct 2022 05:44:29 -0500 Subject: [PATCH 0881/1766] Remove depth condition from razoring The eval condition depends on depth anyways, so this patch is nearly (not quite) non-functional passed STC: https://tests.stockfishchess.org/tests/view/63428169fb7ccb2ea9be2629 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 185992 W: 49612 L: 49558 D: 86822 Ptnml(0-2): 618, 19956, 51842, 19914, 666 passed LTC: https://tests.stockfishchess.org/tests/view/634418b14bc7650f07540760 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 126816 W: 34147 L: 34043 D: 58626 Ptnml(0-2): 74, 11941, 39281, 12031, 81 closes https://github.com/official-stockfish/Stockfish/pull/4196 bench 4148700 --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 3c2ae3e52df..898de87523f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -775,8 +775,7 @@ namespace { // Step 7. Razoring. // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. - if ( depth <= 7 - && eval < alpha - 369 - 254 * depth * depth) + if (eval < alpha - 369 - 254 * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) From d6b6360ff5dcdffa141f50d0a81d0be7c6324c71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sun, 9 Oct 2022 00:27:26 +0200 Subject: [PATCH 0882/1766] Tweak the formula for NNUE complexity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Joint work by Ofek Shochat and Stéphane Nicolet. passed STC: LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 93288 W: 24996 L: 24601 D: 43691 Ptnml(0-2): 371, 10263, 24989, 10642, 379 https://tests.stockfishchess.org/tests/view/63448f4f4bc7650f07541987 passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 84168 W: 22771 L: 22377 D: 39020 Ptnml(0-2): 47, 8181, 25234, 8575, 47 https://tests.stockfishchess.org/tests/view/6345186d4bc7650f07542fbd ================ It seems there are two effects with this patch: effect A : If Stockfish is winning at root, we have optimism > 0 for all leaves in the search tree where Stockfish is to move. There, if (psq - nnue) > 0 (ie if the advantage is more materialistic than positional), then the product D = optimism * (psq - nnue) will be positive, nnueComplexity will increase, and the eval will increase from SF point of view. So the effect A is that if Stockfish is winning at root, she will slightly favor in the search tree (in other words, search more) the positions where she can convert her advantage via materialist means. effect B : If Stockfish is losing at root, we have optimism > 0 for all leaves in the search tree where the opponent is to move. There, if (psq - nnue) < 0 (ie if the opponent advantage is more positional than materialistic), then the product D = optimism * (psq-nnue) will be negative, nnueComplexity will decrease, and the eval will decrease from the opponent point of view. So the effect B is that Stockfish will slightly favor in the search tree (search more) the branches where she can defend by slowly reducing the opponent positional advantage. ================= closes https://github.com/official-stockfish/Stockfish/pull/4195 bench: 4673898 --- src/evaluate.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 85700bcc60d..d5844593fe4 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1051,25 +1051,33 @@ namespace { Value Eval::evaluate(const Position& pos, int* complexity) { Value v; - Color stm = pos.side_to_move(); Value psq = pos.psq_eg_stm(); // We use the much less accurate but faster Classical eval when the NNUE // option is set to false. Otherwise we use the NNUE eval unless the - // PSQ advantage is decisive and several pieces remain (~3 Elo) + // PSQ advantage is decisive and several pieces remain. (~3 Elo) bool useClassical = !useNNUE || (pos.count() > 7 && abs(psq) > 1760); + if (useClassical) v = Evaluation(pos).value(); else { int nnueComplexity; int scale = 1064 + 106 * pos.non_pawn_material() / 5120; + + Color stm = pos.side_to_move(); Value optimism = pos.this_thread()->optimism[stm]; Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); + // Blend nnue complexity with (semi)classical complexity - nnueComplexity = (104 * nnueComplexity + 131 * abs(nnue - psq)) / 256; - if (complexity) // Return hybrid NNUE complexity to caller + nnueComplexity = ( 416 * nnueComplexity + + 424 * abs(psq - nnue) + + (optimism > 0 ? int(optimism) * int(psq - nnue) : 0) + ) / 1024; + + // Return hybrid NNUE complexity to caller + if (complexity) *complexity = nnueComplexity; optimism = optimism * (269 + nnueComplexity) / 256; From 9be2977da7921aedfd3215f1f4b5f522359effa0 Mon Sep 17 00:00:00 2001 From: xoto10 Date: Sat, 8 Oct 2022 14:53:14 +0100 Subject: [PATCH 0883/1766] Adjust timeman constants Adjust timeman constants to use more time in early part of game. STC @ 10+0.1 th 1 : LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 93984 W: 25177 L: 24787 D: 44020 Ptnml(0-2): 350, 10096, 25729, 10448, 369 https://tests.stockfishchess.org/tests/live_elo/6339077135f43d649ff6162a LTC @ 60+0.6 th 1 : LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 329368 W: 88953 L: 88093 D: 152322 Ptnml(0-2): 170, 31457, 100594, 32269, 194 https://tests.stockfishchess.org/tests/live_elo/6339baed35f43d649ff63142 Sudden death 10+0 : LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 20400 W: 5908 L: 5588 D: 8904 Ptnml(0-2): 177, 2252, 5128, 2360, 283 https://tests.stockfishchess.org/tests/live_elo/6347c9384bc7650f07549ba7 Sudden death 10+0, no adjudication : LLR: 2.96 (-2.94,2.94) <0.00,2.00> Total: 17920 W: 4755 L: 4442 D: 8723 Ptnml(0-2): 137, 1985, 4466, 2172, 200 https://tests.stockfishchess.org/tests/live_elo/634806e84bc7650f0754a639 closes https://github.com/official-stockfish/Stockfish/pull/4188 No functional change --- src/timeman.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/timeman.cpp b/src/timeman.cpp index 0400401e779..cab0d7671a2 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -80,7 +80,7 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { // game time for the current move, so also cap to 20% of available game time. if (limits.movestogo == 0) { - optScale = std::min(0.0084 + std::pow(ply + 3.0, 0.5) * 0.0042, + optScale = std::min(0.0120 + std::pow(ply + 3.0, 0.45) * 0.0039, 0.2 * limits.time[us] / double(timeLeft)) * optExtra; maxScale = std::min(7.0, 4.0 + ply / 12.0); From 79c5f3a69247b44ee6362a3d9236cd9bc048c5f5 Mon Sep 17 00:00:00 2001 From: Rodrigo Roim Date: Fri, 7 Oct 2022 16:55:07 -0700 Subject: [PATCH 0884/1766] Fix tablebase probe for dtz >1000 w/o 50 move rule For qn4N1/6R1/3K4/8/B2k4/8/8/8 w - - 0 1, white loses with DTZ 1034. See https://syzygy-tables.info/?fen=qn4N1/6R1/3K4/8/B2k4/8/8/8_w_-_-_0_1 Prior to this fix, due to a too small hard-coded value, Stockfish interpreted this as winning. The new value picked (1<<18) is large enough to deal with the largest DTZ values that can be stored in the current syzygy format. closes https://github.com/official-stockfish/Stockfish/pull/4187 No functional change. --- AUTHORS | 1 + src/syzygy/tbprobe.cpp | 13 +++++++------ src/syzygy/tbprobe.h | 2 -- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/AUTHORS b/AUTHORS index fc885acbf9c..804232f1772 100644 --- a/AUTHORS +++ b/AUTHORS @@ -169,6 +169,7 @@ renouve Reuven Peleg Richard Lloyd Rodrigo Exterckötter Tjäder +Rodrigo Roim (roim) Ron Britvich (Britvich) Ronald de Man (syzygy1, syzygy) rqs diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 43af89f0ec9..f2de036df4a 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -59,6 +59,7 @@ namespace Stockfish { namespace { constexpr int TBPIECES = 7; // Max number of supported pieces +constexpr int MAX_DTZ = 1 << 18; // Max DTZ supported, large enough to deal with the syzygy TB limit. enum { BigEndian, LittleEndian }; enum TBType { WDL, DTZ }; // Used as template parameter @@ -1522,7 +1523,7 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves) { // Check whether a position was repeated since the last zeroing move. bool rep = pos.has_repeated(); - int dtz, bound = Options["Syzygy50MoveRule"] ? 900 : 1; + int dtz, bound = Options["Syzygy50MoveRule"] ? (MAX_DTZ - 100) : 1; // Probe and rank each move for (auto& m : rootMoves) @@ -1565,8 +1566,8 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves) { // Better moves are ranked higher. Certain wins are ranked equally. // Losing moves are ranked equally unless a 50-move draw is in sight. - int r = dtz > 0 ? (dtz + cnt50 <= 99 && !rep ? 1000 : 1000 - (dtz + cnt50)) - : dtz < 0 ? (-dtz * 2 + cnt50 < 100 ? -1000 : -1000 + (-dtz + cnt50)) + int r = dtz > 0 ? (dtz + cnt50 <= 99 && !rep ? MAX_DTZ : MAX_DTZ - (dtz + cnt50)) + : dtz < 0 ? (-dtz * 2 + cnt50 < 100 ? -MAX_DTZ : -MAX_DTZ + (-dtz + cnt50)) : 0; m.tbRank = r; @@ -1574,9 +1575,9 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves) { // 1 cp to cursed wins and let it grow to 49 cp as the positions gets // closer to a real win. m.tbScore = r >= bound ? VALUE_MATE - MAX_PLY - 1 - : r > 0 ? Value((std::max( 3, r - 800) * int(PawnValueEg)) / 200) + : r > 0 ? Value((std::max( 3, r - (MAX_DTZ - 200)) * int(PawnValueEg)) / 200) : r == 0 ? VALUE_DRAW - : r > -bound ? Value((std::min(-3, r + 800) * int(PawnValueEg)) / 200) + : r > -bound ? Value((std::min(-3, r + (MAX_DTZ - 200)) * int(PawnValueEg)) / 200) : -VALUE_MATE + MAX_PLY + 1; } @@ -1590,7 +1591,7 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves) { // A return value false indicates that not all probes were successful. bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves) { - static const int WDL_to_rank[] = { -1000, -899, 0, 899, 1000 }; + static const int WDL_to_rank[] = { -MAX_DTZ, -MAX_DTZ + 101, 0, MAX_DTZ - 101, MAX_DTZ }; ProbeState result; StateInfo st; diff --git a/src/syzygy/tbprobe.h b/src/syzygy/tbprobe.h index c2917fef636..179c75721c2 100644 --- a/src/syzygy/tbprobe.h +++ b/src/syzygy/tbprobe.h @@ -31,8 +31,6 @@ enum WDLScore { WDLDraw = 0, // Draw WDLCursedWin = 1, // Win, but draw under 50-move rule WDLWin = 2, // Win - - WDLScoreNone = -1000 }; // Possible states after a probing operation From 234d2156fdf011a7bb850e3d6172ed2290ab3ad2 Mon Sep 17 00:00:00 2001 From: MinetaS Date: Mon, 17 Oct 2022 00:03:08 +0900 Subject: [PATCH 0885/1766] Apply -flto-partition=one / -flto=full This patch fixes a potential bug derived from an incompatibility between LTO and top-level assembly code (INCBIN). Passed non-regression STC (master e90341f): LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 119352 W: 31986 L: 31862 D: 55504 Ptnml(0-2): 439, 12624, 33400, 12800, 413 https://tests.stockfishchess.org/tests/view/634aacf84bc7650f0755188b closes https://github.com/official-stockfish/Stockfish/pull/4201 No functional change --- AUTHORS | 1 + src/Makefile | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index 804232f1772..89e7c9edcb9 100644 --- a/AUTHORS +++ b/AUTHORS @@ -189,6 +189,7 @@ Stefan Geschwentner (locutus2) Stefano Cardanobile (Stefano80) Steinar Gunderson (sesse) Stéphane Nicolet (snicolet) +Syine Mineta (MinetaS) Prokop Randáček (ProkopRandacek) Thanar2 thaspel diff --git a/src/Makefile b/src/Makefile index 880710fe8a7..1d5137d1c50 100644 --- a/src/Makefile +++ b/src/Makefile @@ -678,7 +678,7 @@ endif ifeq ($(optimize),yes) ifeq ($(debug), no) ifeq ($(comp),clang) - CXXFLAGS += -flto + CXXFLAGS += -flto=full ifeq ($(target_windows),yes) CXXFLAGS += -fuse-ld=lld endif @@ -688,10 +688,10 @@ ifeq ($(debug), no) # GCC on some systems. else ifeq ($(comp),gcc) ifeq ($(gccisclang),) - CXXFLAGS += -flto + CXXFLAGS += -flto -flto-partition=one LDFLAGS += $(CXXFLAGS) -flto=jobserver else - CXXFLAGS += -flto + CXXFLAGS += -flto=full LDFLAGS += $(CXXFLAGS) endif From 804394b939614ca6ac99e31be236c019db365bb7 Mon Sep 17 00:00:00 2001 From: disservin Date: Sun, 16 Oct 2022 14:37:01 +0200 Subject: [PATCH 0886/1766] enable bit manipulation instruction set 1 bmi1 enables the use of _blsr_u64 for pop_lsb, and is availabe when avx2 is. verified a small speedup (0.2 - 0.6%) closes https://github.com/official-stockfish/Stockfish/pull/4202 No functional change --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 1d5137d1c50..727466f9b57 100644 --- a/src/Makefile +++ b/src/Makefile @@ -593,7 +593,7 @@ endif ifeq ($(avx2),yes) CXXFLAGS += -DUSE_AVX2 ifeq ($(comp),$(filter $(comp),gcc clang mingw)) - CXXFLAGS += -mavx2 + CXXFLAGS += -mavx2 -mbmi endif endif From 5604b255e6012ed44eb03e2e93949d904fc7279b Mon Sep 17 00:00:00 2001 From: Clement Date: Fri, 21 Oct 2022 02:29:57 +0000 Subject: [PATCH 0887/1766] Add RISC-V 64-bit support adds a riscv64 target architecture to the Makefile to support RISC-V 64-bit. Compiled and tested on VisionFive 2 board. closes https://github.com/official-stockfish/Stockfish/pull/4205 No functional change. --- AUTHORS | 1 + src/Makefile | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index 89e7c9edcb9..d4b37e7abb2 100644 --- a/AUTHORS +++ b/AUTHORS @@ -43,6 +43,7 @@ Bryan Cross (crossbr) candirufish Chess13234 Chris Cain (ceebo) +clefrks Dale Weiler (graphitemaster) Dan Schmidt (dfannius) Daniel Axtens (daxtens) diff --git a/src/Makefile b/src/Makefile index 727466f9b57..e481aca5141 100644 --- a/src/Makefile +++ b/src/Makefile @@ -116,7 +116,7 @@ ifeq ($(ARCH), $(filter $(ARCH), \ x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-avxvnni x86-64-bmi2 \ x86-64-avx2 x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \ x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-32 e2k \ - armv7 armv7-neon armv8 apple-silicon general-64 general-32)) + armv7 armv7-neon armv8 apple-silicon general-64 general-32 riscv64)) SUPPORTED_ARCH=true else SUPPORTED_ARCH=false @@ -338,7 +338,11 @@ ifeq ($(findstring e2k,$(ARCH)),e2k) popcnt = yes endif +ifeq ($(ARCH),riscv64) + arch = riscv64 endif +endif + ### ========================================================================== ### Section 3. Low-level Configuration @@ -364,11 +368,14 @@ ifeq ($(COMP),gcc) CXX=g++ CXXFLAGS += -pedantic -Wextra -Wshadow - ifeq ($(arch),$(filter $(arch),armv7 armv8)) + ifeq ($(arch),$(filter $(arch),armv7 armv8 riscv64)) ifeq ($(OS),Android) CXXFLAGS += -m$(bits) LDFLAGS += -m$(bits) endif + ifeq ($(ARCH),riscv64) + CXXFLAGS += -latomic + endif else CXXFLAGS += -m$(bits) LDFLAGS += -m$(bits) @@ -429,11 +436,14 @@ ifeq ($(COMP),clang) endif endif - ifeq ($(arch),$(filter $(arch),armv7 armv8)) + ifeq ($(arch),$(filter $(arch),armv7 armv8 riscv64)) ifeq ($(OS),Android) CXXFLAGS += -m$(bits) LDFLAGS += -m$(bits) endif + ifeq ($(ARCH),riscv64) + CXXFLAGS += -latomic + endif else CXXFLAGS += -m$(bits) LDFLAGS += -m$(bits) @@ -757,6 +767,7 @@ help: @echo "apple-silicon > Apple silicon ARM64" @echo "general-64 > unspecified 64-bit" @echo "general-32 > unspecified 32-bit" + @echo "riscv64 > RISC-V 64-bit" @echo "" @echo "Supported compilers:" @echo "" @@ -916,7 +927,7 @@ config-sanity: net @test "$(SUPPORTED_ARCH)" = "true" @test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \ test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || test "$(arch)" = "e2k" || \ - test "$(arch)" = "armv7" || test "$(arch)" = "armv8" || test "$(arch)" = "arm64" + test "$(arch)" = "armv7" || test "$(arch)" = "armv8" || test "$(arch)" = "arm64" || test "$(arch)" = "riscv64" @test "$(bits)" = "32" || test "$(bits)" = "64" @test "$(prefetch)" = "yes" || test "$(prefetch)" = "no" @test "$(popcnt)" = "yes" || test "$(popcnt)" = "no" From 4ec8945eafc5b271d1c9d276fab590fa26c24901 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Fri, 21 Oct 2022 17:10:45 +0300 Subject: [PATCH 0888/1766] Use TT moves more often in qsearch During the recapture phase of quiescence search (where we limit the generated moves to recaptures on the last seen capture square), the move picker will now emit the tt move, even if the tt move is not a recapture. Passed STC : https://tests.stockfishchess.org/tests/view/6350df2928d3a71cb1eef838 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 90280 W: 24001 L: 23845 D: 42434 Ptnml(0-2): 273, 9779, 24941, 9813, 334 Passed LTC : https://tests.stockfishchess.org/tests/view/6351308b28d3a71cb1ef06ce LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 104504 W: 27937 L: 27807 D: 48760 Ptnml(0-2): 54, 10378, 31260, 10504, 56 closes https://github.com/official-stockfish/Stockfish/pull/4206 Bench: 4540268 --- src/movepick.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 3428a764f1a..587c6d79c0e 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -83,7 +83,6 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist stage = (pos.checkers() ? EVASION_TT : QSEARCH_TT) + !( ttm - && (pos.checkers() || depth > DEPTH_QS_RECAPTURES || to_sq(ttm) == recaptureSquare) && pos.pseudo_legal(ttm)); } From a5500edc555f6d4429b3b697392f5f27215cb1db Mon Sep 17 00:00:00 2001 From: dav1312 <63931154+dav1312@users.noreply.github.com> Date: Tue, 25 Oct 2022 11:15:00 +0200 Subject: [PATCH 0889/1766] Add issue template Add an issue template using GitHub's form schema https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema closes https://github.com/official-stockfish/Stockfish/pull/4210 No functional change. --- .github/ISSUE_TEMPLATE/BUG-REPORT.yml | 65 +++++++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 8 ++++ README.md | 63 +++++++++++++------------- 3 files changed, 106 insertions(+), 30 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/BUG-REPORT.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/BUG-REPORT.yml b/.github/ISSUE_TEMPLATE/BUG-REPORT.yml new file mode 100644 index 00000000000..e46d2bf822a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/BUG-REPORT.yml @@ -0,0 +1,65 @@ +name: Report issue +description: Create a report to help us fix issues with the engine +body: +- type: textarea + attributes: + label: Describe the issue + description: A clear and concise description of what you're experiencing. + validations: + required: true + +- type: textarea + attributes: + label: Expected behavior + description: A clear and concise description of what you expected to happen. + validations: + required: true + +- type: textarea + attributes: + label: Steps to reproduce + description: | + Steps to reproduce the behavior. + You can also use this section to paste the command line output. + placeholder: | + ``` + position startpos moves g2g4 e7e5 f2f3 + go mate 1 + info string NNUE evaluation using nn-6877cd24400e.nnue enabled + info depth 1 seldepth 1 multipv 1 score mate 1 nodes 33 nps 11000 tbhits 0 time 3 pv d8h4 + bestmove d8h4 + ``` + validations: + required: true + +- type: textarea + attributes: + label: Anything else? + description: | + Anything that will give us more context about the issue you are encountering. + You can also use this section to propose ideas on how to solve the issue. + validations: + required: false + +- type: dropdown + attributes: + label: Operating system + options: + - All + - Windows + - Linux + - MacOS + - Android + - Other or N/A + validations: + required: true + +- type: input + attributes: + label: Stockfish version + description: | + This can be found by running the engine. + You can also use the commit ID. + placeholder: Stockfish 15 / e6e324e + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000..1f8694d2e6f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Discord server + url: https://discord.gg/GWDRS3kU6R + about: Feel free to ask for support or have a chat with us in our Discord server! + - name: Discussions, Q&A, ideas, show us something... + url: https://github.com/official-stockfish/Stockfish/discussions/new + about: Do you have an idea for Stockfish? Do you want to show something that you made? Please open a discussion about it! diff --git a/README.md b/README.md index b076ab6bc02..7066db4bad9 100644 --- a/README.md +++ b/README.md @@ -344,33 +344,36 @@ source code, these changes must also be made available under the GPL v3. For full details, read the copy of the GPL v3 found in the file named [*Copying.txt*][license-link]. -[authors-link]:https://github.com/official-stockfish/Stockfish/blob/master/AUTHORS -[build-badge]:https://img.shields.io/github/workflow/status/official-stockfish/Stockfish/Stockfish?style=for-the-badge&label=stockfish&logo=github -[build-link]:https://github.com/official-stockfish/Stockfish/actions/workflows/stockfish.yml -[commits-badge]:https://img.shields.io/github/commits-since/official-stockfish/Stockfish/latest?style=for-the-badge -[commits-link]:https://github.com/official-stockfish/Stockfish/commits/master -[discord-badge]:https://img.shields.io/discord/435943710472011776?style=for-the-badge&label=discord&logo=Discord -[discord-link]:https://discord.com/invite/aefaxmq -[fishcooking-link]:https://groups.google.com/g/fishcooking -[fishtest-badge]:https://img.shields.io/website?style=for-the-badge&down_color=red&down_message=Offline&label=Fishtest&up_color=success&up_message=Online&url=https%3A%2F%2Ftests.stockfishchess.org%2Ftests -[fishtest-link]:https://tests.stockfishchess.org/tests -[github-link]:https://github.com/official-stockfish/Stockfish -[guideline-link]:https://github.com/glinscott/fishtest/wiki/Creating-my-first-test -[license-badge]:https://img.shields.io/github/license/official-stockfish/Stockfish?style=for-the-badge&label=license&color=success -[license-link]:https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt -[lockpages-link]:https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/enable-the-lock-pages-in-memory-option-windows -[nodchip-link]:https://github.com/nodchip/Stockfish -[programming-link]:https://www.chessprogramming.org/Main_Page -[programmingsf-link]:https://www.chessprogramming.org/Stockfish -[pytorch-link]:https://github.com/glinscott/nnue-pytorch -[rammap-link]:https://docs.microsoft.com/en-us/sysinternals/downloads/rammap -[readme-link]:https://github.com/official-stockfish/Stockfish/blob/master/README.md -[release-badge]:https://img.shields.io/github/v/release/official-stockfish/Stockfish?style=for-the-badge&label=official%20release -[release-link]:https://github.com/official-stockfish/Stockfish/releases/latest -[src-link]:https://github.com/official-stockfish/Stockfish/tree/master/src -[stockfish128-logo]:https://stockfishchess.org/images/logo/icon_128x128.png -[tools-link]:https://github.com/official-stockfish/Stockfish/tree/tools -[uci-link]:https://www.shredderchess.com/download/div/uci.zip -[website-badge]:https://img.shields.io/website?style=for-the-badge&down_color=red&down_message=Offline&label=website&up_color=success&up_message=Online&url=https%3A%2F%2Fstockfishchess.org -[website-link]:https://stockfishchess.org -[worker-link]:https://github.com/glinscott/fishtest/wiki/Running-the-worker:-overview + +[authors-link]: https://github.com/official-stockfish/Stockfish/blob/master/AUTHORS +[build-link]: https://github.com/official-stockfish/Stockfish/actions/workflows/stockfish.yml +[commits-link]: https://github.com/official-stockfish/Stockfish/commits/master +[discord-link]: https://discord.gg/GWDRS3kU6R +[fishcooking-link]: https://groups.google.com/g/fishcooking +[fishtest-link]: https://tests.stockfishchess.org/tests +[github-link]: https://github.com/official-stockfish/Stockfish +[guideline-link]: https://github.com/glinscott/fishtest/wiki/Creating-my-first-test +[license-link]: https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt +[lockpages-link]: https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/enable-the-lock-pages-in-memory-option-windows +[nodchip-link]: https://github.com/nodchip/Stockfish +[programming-link]: https://www.chessprogramming.org/Main_Page +[programmingsf-link]: https://www.chessprogramming.org/Stockfish +[pytorch-link]: https://github.com/glinscott/nnue-pytorch +[rammap-link]: https://docs.microsoft.com/en-us/sysinternals/downloads/rammap +[readme-link]: https://github.com/official-stockfish/Stockfish/blob/master/README.md +[release-link]: https://github.com/official-stockfish/Stockfish/releases/latest +[src-link]: https://github.com/official-stockfish/Stockfish/tree/master/src +[stockfish128-logo]: https://stockfishchess.org/images/logo/icon_128x128.png +[tools-link]: https://github.com/official-stockfish/Stockfish/tree/tools +[uci-link]: https://www.shredderchess.com/download/div/uci.zip +[website-link]: https://stockfishchess.org +[worker-link]: https://github.com/glinscott/fishtest/wiki/Running-the-worker:-overview + +[build-badge]: https://img.shields.io/github/workflow/status/official-stockfish/Stockfish/Stockfish?style=for-the-badge&label=stockfish&logo=github +[commits-badge]: https://img.shields.io/github/commits-since/official-stockfish/Stockfish/latest?style=for-the-badge +[discord-badge]: https://img.shields.io/discord/435943710472011776?style=for-the-badge&label=discord&logo=Discord +[fishtest-badge]: https://img.shields.io/website?style=for-the-badge&down_color=red&down_message=Offline&label=Fishtest&up_color=success&up_message=Online&url=https%3A%2F%2Ftests.stockfishchess.org%2Ftests +[license-badge]: https://img.shields.io/github/license/official-stockfish/Stockfish?style=for-the-badge&label=license&color=success +[release-badge]: https://img.shields.io/github/v/release/official-stockfish/Stockfish?style=for-the-badge&label=official%20release + +[website-badge]: https://img.shields.io/website?style=for-the-badge&down_color=red&down_message=Offline&label=website&up_color=success&up_message=Online&url=https%3A%2F%2Fstockfishchess.org From 8333b2a94c7c7e6e13822153f35899b64a690ac2 Mon Sep 17 00:00:00 2001 From: Clausable <97650056+Clausable@users.noreply.github.com> Date: Mon, 24 Oct 2022 16:00:31 -0400 Subject: [PATCH 0890/1766] Fix README typos, update AUTHORS closes https://github.com/official-stockfish/Stockfish/pull/4208 No functional change --- AUTHORS | 7 ++++--- README.md | 27 +++++++++++++-------------- src/nnue/features/half_ka_v2_hm.cpp | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/AUTHORS b/AUTHORS index d4b37e7abb2..173669d4d0a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -39,6 +39,7 @@ Boštjan Mejak (PedanticHacker) braich Brian Sheppard (SapphireBrand, briansheppard-toast) Bruno de Melo Costa (BM123499) +Bruno Pellanda (pellanda) Bryan Cross (crossbr) candirufish Chess13234 @@ -50,6 +51,7 @@ Daniel Axtens (daxtens) Daniel Dugovic (ddugovic) Dariusz Orzechowski (dorzechowski) David Zar +David (dav1312) Daylen Yang (daylen) Deshawn Mohan-Smith (GoldenRare) Dieter Dobbelaere (ddobbelaere) @@ -159,7 +161,6 @@ Panthee Pascal Romaret Pasquale Pigazzini (ppigazzini) Patrick Jansen (mibere) -pellanda Peter Schneider (pschneider1968) Peter Zsifkovits (CoffeeOne) Praveen Kumar Tummala (praveentml) @@ -167,8 +168,8 @@ Rahul Dsilva (silversolver1) Ralph Stößer (Ralph Stoesser) Raminder Singh renouve -Reuven Peleg -Richard Lloyd +Reuven Peleg (R-Peleg) +Richard Lloyd (Richard-Lloyd) Rodrigo Exterckötter Tjäder Rodrigo Roim (roim) Ron Britvich (Britvich) diff --git a/README.md b/README.md index 7066db4bad9..ac15fbfafbe 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@
- + [![Stockfish][stockfish128-logo]][website-link] - + [![Build][build-badge]][build-link] [![License][license-badge]][license-link]
@@ -11,7 +11,7 @@ [![Website][website-badge]][website-link] [![Fishtest][fishtest-badge]][fishtest-link] [![Discord][discord-badge]][discord-link] - +
## Overview @@ -20,14 +20,14 @@ Glaurung 2.1. Stockfish is not a complete chess program and requires a UCI-compatible graphical user interface (GUI) (e.g. XBoard with PolyGlot, Scid, Cute Chess, eboard, Arena, Sigma Chess, Shredder, Chess Partner or Fritz) in order to be used comfortably. -Read the documentation for your GUI of choice for informationabout how to use +Read the documentation for your GUI of choice for information about how to use Stockfish with it. -The Stockfish engine features two evaluation functions for chess. The efficiently +The Stockfish engine features two evaluation functions for chess. The efficiently updatable neural network (NNUE) based evaluation is the default and by far the strongest. -The classical evaluation based on handcrafted terms remains available. The strongest +The classical evaluation based on handcrafted terms remains available. The strongest network is integrated in the binary and downloaded automatically during the build process. -The NNUE evaluation benefits from the vector intrinsics available on most CPUs (sse2, +The NNUE evaluation benefits from the vector intrinsics available on most CPUs (sse2, avx2, neon, or similar). ## Files @@ -152,8 +152,8 @@ change them via a chess GUI. This is a list of available UCI options in Stockfis For developers the following non-standard commands might be of interest, mainly useful for debugging: * #### bench *ttSize threads limit fenFile limitType evalType* - Performs a standard benchmark using various options. The signature of a version - (standard node count) is obtained using all defaults. `bench` is currently + Performs a standard benchmark using various options. The signature of a version + (standard node count) is obtained using all defaults. `bench` is currently `bench 16 1 13 default depth mixed`. * #### compiler @@ -201,9 +201,9 @@ the engine is somewhat lower (roughly 80% of nps is typical). Notes: -1) the NNUE evaluation depends on the Stockfish binary and the network parameter file +1) the NNUE evaluation depends on the Stockfish binary and the network parameter file (see the EvalFile UCI option). Not every parameter file is compatible with a given -Stockfish binary, but the default value of the EvalFile UCI option is the name of a +Stockfish binary, but the default value of the EvalFile UCI option is the name of a network that is guaranteed to be compatible with that binary. 2) to use the NNUE evaluation, the additional data file with neural network parameters @@ -337,7 +337,7 @@ using it as the starting point for a software project of your own. The only real limitation is that whenever you distribute Stockfish in some way, you MUST always include the license and the full source code -(or a pointer to where the source code can be found) to generate the +(or a pointer to where the source code can be found) to generate the exact binary you are distributing. If you make any changes to the source code, these changes must also be made available under the GPL v3. @@ -372,8 +372,7 @@ For full details, read the copy of the GPL v3 found in the file named [build-badge]: https://img.shields.io/github/workflow/status/official-stockfish/Stockfish/Stockfish?style=for-the-badge&label=stockfish&logo=github [commits-badge]: https://img.shields.io/github/commits-since/official-stockfish/Stockfish/latest?style=for-the-badge [discord-badge]: https://img.shields.io/discord/435943710472011776?style=for-the-badge&label=discord&logo=Discord -[fishtest-badge]: https://img.shields.io/website?style=for-the-badge&down_color=red&down_message=Offline&label=Fishtest&up_color=success&up_message=Online&url=https%3A%2F%2Ftests.stockfishchess.org%2Ftests +[fishtest-badge]: https://img.shields.io/website?style=for-the-badge&down_color=red&down_message=Offline&label=Fishtest&up_color=success&up_message=Online&url=https%3A%2F%2Ftests.stockfishchess.org%2Ftests%2Ffinished [license-badge]: https://img.shields.io/github/license/official-stockfish/Stockfish?style=for-the-badge&label=license&color=success [release-badge]: https://img.shields.io/github/v/release/official-stockfish/Stockfish?style=for-the-badge&label=official%20release - [website-badge]: https://img.shields.io/website?style=for-the-badge&down_color=red&down_message=Offline&label=website&up_color=success&up_message=Online&url=https%3A%2F%2Fstockfishchess.org diff --git a/src/nnue/features/half_ka_v2_hm.cpp b/src/nnue/features/half_ka_v2_hm.cpp index 11e05c9410a..7dbd3415624 100644 --- a/src/nnue/features/half_ka_v2_hm.cpp +++ b/src/nnue/features/half_ka_v2_hm.cpp @@ -48,7 +48,7 @@ namespace Stockfish::Eval::NNUE::Features { // Explicit template instantiations template void HalfKAv2_hm::append_active_indices(const Position& pos, IndexList& active); template void HalfKAv2_hm::append_active_indices(const Position& pos, IndexList& active); - + // append_changed_indices() : get a list of indices for recently changed features template void HalfKAv2_hm::append_changed_indices( From f154ed7a2d4176670cc971611b1b32e8d3d18b4b Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 29 Oct 2022 08:23:11 +0200 Subject: [PATCH 0891/1766] Update MacOS CI move to 12 following actions runner update deprecation (see https://github.com/actions/runner-images/issues/5583) closes https://github.com/official-stockfish/Stockfish/pull/4212 No functional change --- .github/workflows/stockfish.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index b007ec78937..6ff735228bd 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -62,17 +62,17 @@ jobs: shell: 'bash {0}' } - { - name: "MacOS 10.15 Apple Clang", - os: macos-10.15, + name: "MacOS 12 Apple Clang", + os: macos-12, compiler: clang++, comp: clang, run_64bit_tests: true, shell: 'bash {0}' } - { - name: "MacOS 10.15 GCC 10", - os: macos-10.15, - compiler: g++-10, + name: "MacOS 12 GCC 11", + os: macos-12, + compiler: g++-11, comp: gcc, run_64bit_tests: true, shell: 'bash {0}' From d09653df0d1bfec0af05ab2e8975e0d8e5cccba8 Mon Sep 17 00:00:00 2001 From: kurt22i Date: Sun, 30 Oct 2022 09:02:48 -0400 Subject: [PATCH 0892/1766] Adjust reduction less at medium depths This patch dampens the reduction increase/decrease from statScore at mid-range depths. Inspired by patterns noticed in this tune: https://tests.stockfishchess.org/tests/view/635188930e5f47a8d0ffe8f5 Passed STC: https://tests.stockfishchess.org/tests/view/63599dfd6b27ef94d9ec04af LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 87464 W: 23519 L: 23134 D: 40811 Ptnml(0-2): 319, 9599, 23524, 9958, 332 Passed LTC: https://tests.stockfishchess.org/tests/view/635a73046b27ef94d9ec2313 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 154792 W: 41746 L: 41214 D: 71832 Ptnml(0-2): 79, 15181, 46349, 15703, 84 closes https://github.com/official-stockfish/Stockfish/pull/4213 Bench 4271738 --- AUTHORS | 2 +- src/search.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index 173669d4d0a..432d9b90d6d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -91,7 +91,7 @@ Hongzhi Cheng Ivan Ivec (IIvec) Jacques B. (Timshel) Jan Ondruš (hxim) -Jared Kish (Kurtbusch) +Jared Kish (Kurtbusch, kurt22i) Jarrod Torriero (DU-jdto) Jean Gauthier (OuaisBla) Jean-Francois Romang (jromang) diff --git a/src/search.cpp b/src/search.cpp index 898de87523f..422d4b43a82 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1177,7 +1177,7 @@ namespace { - 4433; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - r -= ss->statScore / 13628; + r -= ss->statScore / (13628 + 4000 * (depth > 7 && depth < 19)); // In general we want to cap the LMR depth search at newDepth, but when // reduction is negative, we allow this move a limited search extension From 61a2cb84a60b8a48a0e1d34955d8a4d1acdcf497 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 5 Nov 2022 09:14:11 +0100 Subject: [PATCH 0893/1766] Mark variable as potentially unused fixes CI when compiled with -Werror closes https://github.com/official-stockfish/Stockfish/pull/4221 No functional change --- src/position.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/position.cpp b/src/position.cpp index 62e6e2387cb..5befcaf2cfc 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -129,7 +129,7 @@ void Position::init() { // Prepare the cuckoo tables std::memset(cuckoo, 0, sizeof(cuckoo)); std::memset(cuckooMove, 0, sizeof(cuckooMove)); - int count = 0; + [[maybe_unused]] int count = 0; for (Piece pc : Pieces) for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) for (Square s2 = Square(s1 + 1); s2 <= SQ_H8; ++s2) From ad2aa8c06f438de8b8bb7b7c8726430e3f2a5685 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 31 Oct 2022 20:36:43 +0100 Subject: [PATCH 0894/1766] Normalize evaluation Normalizes the internal value as reported by evaluate or search to the UCI centipawn result used in output. This value is derived from the win_rate_model() such that Stockfish outputs an advantage of "100 centipawns" for a position if the engine has a 50% probability to win from this position in selfplay at fishtest LTC time control. The reason to introduce this normalization is that our evaluation is, since NNUE, no longer related to the classical parameter PawnValueEg (=208). This leads to the current evaluation changing quite a bit from release to release, for example, the eval needed to have 50% win probability at fishtest LTC (in cp and internal Value): June 2020 : 113cp (237) June 2021 : 115cp (240) April 2022 : 134cp (279) July 2022 : 167cp (348) With this patch, a 100cp advantage will have a fixed interpretation, i.e. a 50% win chance. To keep this value steady, it will be needed to update the win_rate_model() from time to time, based on fishtest data. This analysis can be performed with a set of scripts currently available at https://github.com/vondele/WLD_model fixes https://github.com/official-stockfish/Stockfish/issues/4155 closes https://github.com/official-stockfish/Stockfish/pull/4216 No functional change --- src/nnue/evaluate_nnue.cpp | 4 ++-- src/uci.cpp | 12 ++++++++---- src/uci.h | 7 +++++++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index ba2ed36746c..4715fed07d6 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -220,7 +220,7 @@ namespace Stockfish::Eval::NNUE { buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' '); - int cp = std::abs(100 * v / PawnValueEg); + int cp = std::abs(100 * v / UCI::NormalizeToPawnValue); if (cp >= 10000) { buffer[1] = '0' + cp / 10000; cp %= 10000; @@ -251,7 +251,7 @@ namespace Stockfish::Eval::NNUE { buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' '); - double cp = 1.0 * std::abs(int(v)) / PawnValueEg; + double cp = 1.0 * std::abs(int(v)) / UCI::NormalizeToPawnValue; sprintf(&buffer[1], "%6.2f", cp); } diff --git a/src/uci.cpp b/src/uci.cpp index d5e2c2c3586..19e2b0cb920 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -207,13 +207,17 @@ namespace { // The coefficients of a third-order polynomial fit is based on the fishtest data // for two parameters that need to transform eval to the argument of a logistic // function. - double as[] = { 0.50379905, -4.12755858, 18.95487051, 152.00733652}; - double bs[] = {-1.71790378, 10.71543602, -17.05515898, 41.15680404}; + constexpr double as[] = { 1.04790516, -8.58534089, 39.42615625, 316.17524816}; + constexpr double bs[] = { -3.57324784, 22.28816201, -35.47480551, 85.60617701 }; + + // Enforce that NormalizeToPawnValue corresponds to a 50% win rate at ply 64 + static_assert(UCI::NormalizeToPawnValue == int(as[0] + as[1] + as[2] + as[3])); + double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3]; double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3]; // Transform the eval to centipawns with limited range - double x = std::clamp(double(100 * v) / PawnValueEg, -2000.0, 2000.0); + double x = std::clamp(double(v), -4000.0, 4000.0); // Return the win rate in per mille units rounded to the nearest value return int(0.5 + 1000 / (1 + std::exp((a - x) / b))); @@ -312,7 +316,7 @@ string UCI::value(Value v) { stringstream ss; if (abs(v) < VALUE_MATE_IN_MAX_PLY) - ss << "cp " << v * 100 / PawnValueEg; + ss << "cp " << v * 100 / NormalizeToPawnValue; else ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; diff --git a/src/uci.h b/src/uci.h index 76a893f90c4..f5f2c385c6c 100644 --- a/src/uci.h +++ b/src/uci.h @@ -30,6 +30,13 @@ class Position; namespace UCI { +// Normalizes the internal value as reported by evaluate or search +// to the UCI centipawn result used in output. This value is derived from +// the win_rate_model() such that Stockfish outputs an advantage of +// "100 centipawns" for a position if the engine has a 50% probability to win +// from this position in selfplay at fishtest LTC time control. +const int NormalizeToPawnValue = 348; + class Option; /// Define a custom comparator, because the UCI options should be case-insensitive From e048d1182562eb7ce1eae45f626681206da446b8 Mon Sep 17 00:00:00 2001 From: disservin Date: Sun, 6 Nov 2022 16:17:17 +0100 Subject: [PATCH 0895/1766] Change versioning and save binaries as CI artifacts For development versions of Stockfish, the version will now look like dev-20221107-dca9a0533 indicating a development version, the date of the last commit, and the git SHA of that commit. If git is not available, the fallback is the date of compilation. Releases will continue to be versioned as before. Additionally, this PR extends the CI to create binary artifacts, i.e. pushes to master will automatically build Stockfish and upload the binaries to github. closes https://github.com/official-stockfish/Stockfish/pull/4220 No functional change --- .github/workflows/stockfish.yml | 5 +- .github/workflows/stockfish_binaries.yml | 110 +++++++++++++++++++++++ src/Makefile | 18 +++- src/misc.cpp | 47 ++++++---- 4 files changed, 163 insertions(+), 17 deletions(-) create mode 100644 .github/workflows/stockfish_binaries.yml diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index 6ff735228bd..288301332b8 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -10,6 +10,9 @@ on: - master - tools jobs: + Binaries: + if: github.ref == 'refs/heads/master' + uses: ./.github/workflows/stockfish_binaries.yml Stockfish: name: ${{ matrix.config.name }} runs-on: ${{ matrix.config.os }} @@ -113,7 +116,7 @@ jobs: working-directory: src shell: ${{ matrix.config.shell }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 0 diff --git a/.github/workflows/stockfish_binaries.yml b/.github/workflows/stockfish_binaries.yml new file mode 100644 index 00000000000..0b205ded061 --- /dev/null +++ b/.github/workflows/stockfish_binaries.yml @@ -0,0 +1,110 @@ +name: Stockfish +on: + workflow_call: +jobs: + Stockfish: + name: ${{ matrix.config.name }} ${{ matrix.binaries }} + runs-on: ${{ matrix.config.os }} + env: + COMPILER: ${{ matrix.config.compiler }} + COMP: ${{ matrix.config.comp }} + EXT: ${{ matrix.config.ext }} + OS: ${{ matrix.config.os }} + BINARY: ${{ matrix.binaries }} + strategy: + matrix: + config: + - { + name: "Ubuntu 20.04 GCC", + os: ubuntu-20.04, + compiler: g++, + comp: gcc, + shell: 'bash {0}' + } + - { + name: "MacOS 12 Apple Clang", + os: macos-12, + compiler: clang++, + comp: clang, + shell: 'bash {0}' + } + - { + name: "Windows 2022 Mingw-w64 GCC x86_64", + os: windows-2022, + compiler: g++, + comp: mingw, + msys_sys: 'mingw64', + msys_env: 'x86_64-gcc', + shell: 'msys2 {0}', + ext: .exe + } + binaries: + - x86-64 + - x86-64-modern + - x86-64-avx2 + exclude: + - binaries: x86-64-avx2 + config: {os: macos-12} + defaults: + run: + working-directory: src + shell: ${{ matrix.config.shell }} + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Download required linux packages + if: runner.os == 'Linux' + run: | + sudo apt update + + - name: Setup msys and install required packages + if: runner.os == 'Windows' + uses: msys2/setup-msys2@v2 + with: + msystem: ${{matrix.config.msys_sys}} + install: mingw-w64-${{matrix.config.msys_env}} make git expect + + - name: Download the used network from the fishtest framework + run: | + make net + + - name: Check compiler + run: | + export PATH=$PATH:$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin + $COMPILER -v + + - name: Test help target + run: | + make help + + # Compile profile guided builds + + - name: Compile ${{ matrix.binaries }} build + run: | + make clean + make -j2 profile-build ARCH=$BINARY COMP=$COMP + strip ./stockfish$EXT + mv ./stockfish$EXT ../stockfish-$OS-$BINARY$EXT + + - name: Remove non src files + run: rm -f *.o .depend *.nnue + + - name: Create tar archive. + run: | + cd .. + mkdir stockfish + cp -r src stockfish/ + cp stockfish-$OS-$BINARY$EXT stockfish/ + cp "Top CPU Contributors.txt" stockfish/ + cp Copying.txt stockfish/ + cp AUTHORS stockfish/ + tar -cvf stockfish-$OS-$BINARY.tar stockfish + + - name: Upload binaries + uses: actions/upload-artifact@v3 + with: + name: stockfish-${{ matrix.config.os }}-${{ matrix.binaries }} + path: | + stockfish-${{ matrix.config.os }}-${{ matrix.binaries }}.tar diff --git a/src/Makefile b/src/Makefile index e481aca5141..917bd5c06c7 100644 --- a/src/Makefile +++ b/src/Makefile @@ -682,6 +682,18 @@ ifeq ($(pext),yes) endif endif +### 3.7.1 Try to include git commit sha for versioning +GIT_SHA = $(shell git rev-parse --short HEAD 2>/dev/null) +ifneq ($(GIT_SHA), ) + CXXFLAGS += -DGIT_SHA=\"$(GIT_SHA)\" +endif + +### 3.7.2 Try to include git commit date for versioning +GIT_DATE = $(shell git show -s --date=format:'%Y%m%d' --format=%cd HEAD 2>/dev/null) +ifneq ($(GIT_DATE), ) + CXXFLAGS += -DGIT_DATE=\"$(GIT_DATE)\" +endif + ### 3.8 Link Time Optimization ### This is a mix of compile and link time options because the lto link phase ### needs access to the optimization flags. @@ -800,7 +812,7 @@ endif .PHONY: help build profile-build strip install clean net objclean profileclean \ config-sanity icc-profile-use icc-profile-make gcc-profile-use gcc-profile-make \ - clang-profile-use clang-profile-make + clang-profile-use clang-profile-make FORCE build: net config-sanity $(MAKE) ARCH=$(ARCH) COMP=$(COMP) all @@ -948,6 +960,10 @@ config-sanity: net $(EXE): $(OBJS) +$(CXX) -o $@ $(OBJS) $(LDFLAGS) +# Force recompilation to ensure version info is up-to-date +misc.o: FORCE +FORCE: + clang-profile-make: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ EXTRACXXFLAGS='-fprofile-instr-generate ' \ diff --git a/src/misc.cpp b/src/misc.cpp index d19cd840bba..c7fa0e9ae10 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -67,9 +67,8 @@ namespace Stockfish { namespace { -/// Version number. If Version is left empty, then compile date in the format -/// DD-MM-YY and show in engine_info. -const string Version = ""; +/// Version number or dev. +const string version = "dev"; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We @@ -138,23 +137,41 @@ class Logger { } // namespace -/// engine_info() returns the full name of the current Stockfish version. This -/// will be either "Stockfish DD-MM-YY" (where DD-MM-YY is the date when -/// the program was compiled) or "Stockfish ", depending on whether -/// Version is empty. +/// engine_info() returns the full name of the current Stockfish version. +/// For local dev compiles we try to append the commit sha and commit date +/// from git if that fails only the local compilation date is set and "nogit" is specified: +/// Stockfish dev-YYYYMMDD-SHA +/// or +/// Stockfish dev-YYYYMMDD-nogit +/// +/// For releases (non dev builds) we only include the version number: +/// Stockfish version string engine_info(bool to_uci) { + stringstream ss; + ss << "Stockfish " << version << setfill('0'); - const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); - string month, day, year; - stringstream ss, date(__DATE__); // From compiler, format is "Sep 21 2008" - - ss << "Stockfish " << Version << setfill('0'); - - if (Version.empty()) + if (version == "dev") { + ss << "-"; + #ifdef GIT_DATE + ss << GIT_DATE; + #else + const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); + string month, day, year; + stringstream date(__DATE__); // From compiler, format is "Sep 21 2008" + date >> month >> day >> year; - ss << setw(2) << day << setw(2) << (1 + months.find(month) / 4) << year.substr(2); + ss << year << setw(2) << setfill('0') << (1 + months.find(month) / 4) << setw(2) << setfill('0') << day; + #endif + + ss << "-"; + + #ifdef GIT_SHA + ss << GIT_SHA; + #else + ss << "nogit"; + #endif } ss << (to_uci ? "\nid author ": " by ") From a41390079147aea89cb5222089a45e1958ebec8e Mon Sep 17 00:00:00 2001 From: disservin Date: Thu, 3 Nov 2022 21:53:02 +0100 Subject: [PATCH 0896/1766] Remove trend Simplify trend away. passed Non-regression STC: https://tests.stockfishchess.org/tests/view/63642a63a90afcecbd1cb887 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 130000 W: 34683 L: 34567 D: 60750 Ptnml(0-2): 455, 14424, 35135, 14522, 464 passed Non-regression LTC: https://tests.stockfishchess.org/tests/view/636566fda90afcecbd1cded9 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 81592 W: 21938 L: 21787 D: 37867 Ptnml(0-2): 42, 8035, 24490, 8188, 41 closes https://github.com/official-stockfish/Stockfish/pull/4222 Bench: 4239512 --- src/evaluate.cpp | 3 +-- src/search.cpp | 7 +------ src/thread.h | 1 - 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d5844593fe4..d18c2c9328d 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -981,7 +981,7 @@ namespace { // Initialize score by reading the incrementally updated scores included in // the position object (material + piece square tables) and the material // imbalance. Score is computed internally from the white point of view. - Score score = pos.psq_score() + me->imbalance() + pos.this_thread()->trend; + Score score = pos.psq_score() + me->imbalance(); // Probe the pawn hash table pe = Pawns::probe(pos); @@ -1115,7 +1115,6 @@ std::string Eval::trace(Position& pos) { std::memset(scores, 0, sizeof(scores)); // Reset any global variable used in eval - pos.this_thread()->trend = SCORE_ZERO; pos.this_thread()->bestValue = VALUE_ZERO; pos.this_thread()->optimism[WHITE] = VALUE_ZERO; pos.this_thread()->optimism[BLACK] = VALUE_ZERO; diff --git a/src/search.cpp b/src/search.cpp index 422d4b43a82..77619345c8f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -309,7 +309,6 @@ void Thread::search() { complexityAverage.set(155, 1); - trend = SCORE_ZERO; optimism[us] = optimism[~us] = VALUE_ZERO; int searchAgainCounter = 0; @@ -356,11 +355,7 @@ void Thread::search() { alpha = std::max(prev - delta,-VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE); - // Adjust trend and optimism based on root move's previousScore - int tr = 116 * prev / (std::abs(prev) + 89); - trend = (us == WHITE ? make_score(tr, tr / 2) - : -make_score(tr, tr / 2)); - + // Adjust optimism based on root move's previousScore int opt = 118 * prev / (std::abs(prev) + 169); optimism[ us] = Value(opt); optimism[~us] = -optimism[us]; diff --git a/src/thread.h b/src/thread.h index c430a8184e6..5f0b2c3ed9c 100644 --- a/src/thread.h +++ b/src/thread.h @@ -75,7 +75,6 @@ class Thread { ButterflyHistory mainHistory; CapturePieceToHistory captureHistory; ContinuationHistory continuationHistory[2][2]; - Score trend; }; From 6c1df553fa7a94551de7b07515a29098a2f23c16 Mon Sep 17 00:00:00 2001 From: disservin Date: Mon, 7 Nov 2022 18:15:42 +0100 Subject: [PATCH 0897/1766] speedup CI Github Actions allows us to use up to 20 workers. This way we can launch multiple different checks at the same time and optimize the overall time the CI takes a bit. closes https://github.com/official-stockfish/Stockfish/pull/4223 No functional change --- .github/workflows/stockfish.yml | 344 +------------------ .github/workflows/stockfish_compile_test.yml | 115 +++++++ .github/workflows/stockfish_sanitizers.yml | 77 +++++ .github/workflows/stockfish_test.yml | 284 +++++++++++++++ 4 files changed, 482 insertions(+), 338 deletions(-) create mode 100644 .github/workflows/stockfish_compile_test.yml create mode 100644 .github/workflows/stockfish_sanitizers.yml create mode 100644 .github/workflows/stockfish_test.yml diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index 288301332b8..07ecfc07b5c 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -10,344 +10,12 @@ on: - master - tools jobs: + Sanitizers: + uses: ./.github/workflows/stockfish_sanitizers.yml + Tests: + uses: ./.github/workflows/stockfish_test.yml + Compiles: + uses: ./.github/workflows/stockfish_compile_test.yml Binaries: if: github.ref == 'refs/heads/master' uses: ./.github/workflows/stockfish_binaries.yml - Stockfish: - name: ${{ matrix.config.name }} - runs-on: ${{ matrix.config.os }} - env: - COMPILER: ${{ matrix.config.compiler }} - COMP: ${{ matrix.config.comp }} - CXXFLAGS: "-Werror" - strategy: - matrix: - config: - # set the variable for the required tests: - # run_expensive_tests: true - # run_32bit_tests: true - # run_64bit_tests: true - # run_armv8_tests: true - # run_armv7_tests: true - - { - name: "Ubuntu 20.04 GCC", - os: ubuntu-20.04, - compiler: g++, - comp: gcc, - run_expensive_tests: true, - run_32bit_tests: true, - run_64bit_tests: true, - shell: 'bash {0}' - } - - { - name: "Ubuntu 20.04 Clang", - os: ubuntu-20.04, - compiler: clang++, - comp: clang, - run_32bit_tests: true, - run_64bit_tests: true, - shell: 'bash {0}' - } - - { - name: "Ubuntu 20.04 NDK armv8", - os: ubuntu-20.04, - compiler: aarch64-linux-android21-clang++, - comp: ndk, - run_armv8_tests: false, - shell: 'bash {0}' - } - - { - name: "Ubuntu 20.04 NDK armv7", - os: ubuntu-20.04, - compiler: armv7a-linux-androideabi21-clang++, - comp: ndk, - run_armv7_tests: false, - shell: 'bash {0}' - } - - { - name: "MacOS 12 Apple Clang", - os: macos-12, - compiler: clang++, - comp: clang, - run_64bit_tests: true, - shell: 'bash {0}' - } - - { - name: "MacOS 12 GCC 11", - os: macos-12, - compiler: g++-11, - comp: gcc, - run_64bit_tests: true, - shell: 'bash {0}' - } - - { - name: "Windows 2022 Mingw-w64 GCC x86_64", - os: windows-2022, - compiler: g++, - comp: mingw, - run_64bit_tests: true, - msys_sys: 'mingw64', - msys_env: 'x86_64-gcc', - shell: 'msys2 {0}' - } - - { - name: "Windows 2022 Mingw-w64 GCC i686", - os: windows-2022, - compiler: g++, - comp: mingw, - run_32bit_tests: true, - msys_sys: 'mingw32', - msys_env: 'i686-gcc', - shell: 'msys2 {0}' - } - - { - name: "Windows 2022 Mingw-w64 Clang x86_64", - os: windows-2022, - compiler: clang++, - comp: clang, - run_64bit_tests: true, - msys_sys: 'clang64', - msys_env: 'clang-x86_64-clang', - shell: 'msys2 {0}' - } - - defaults: - run: - working-directory: src - shell: ${{ matrix.config.shell }} - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Download required linux packages - if: runner.os == 'Linux' - run: | - sudo apt update - sudo apt install expect valgrind g++-multilib qemu-user - - - name: Setup msys and install required packages - if: runner.os == 'Windows' - uses: msys2/setup-msys2@v2 - with: - msystem: ${{matrix.config.msys_sys}} - install: mingw-w64-${{matrix.config.msys_env}} make git expect - - - name: Download the used network from the fishtest framework - run: | - make net - - - name: Extract the bench number from the commit history - run: | - git log HEAD | grep "\b[Bb]ench[ :]\+[0-9]\{7\}" | head -n 1 | sed "s/[^0-9]*\([0-9]*\).*/\1/g" > git_sig - [ -s git_sig ] && echo "benchref=$(cat git_sig)" >> $GITHUB_ENV && echo "Reference bench:" $(cat git_sig) || echo "No bench found" - - - name: Check compiler - run: | - export PATH=$PATH:$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin - $COMPILER -v - - - name: Test help target - run: | - make help - - # x86-32 tests - - - name: Test debug x86-32 build - if: ${{ matrix.config.run_32bit_tests }} - run: | - export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" - make clean - make -j2 ARCH=x86-32 optimize=no debug=yes build - ../tests/signature.sh $benchref - - - name: Test x86-32 build - if: ${{ matrix.config.run_32bit_tests }} - run: | - make clean - make -j2 ARCH=x86-32 build - ../tests/signature.sh $benchref - - - name: Test x86-32-sse41-popcnt build - if: ${{ matrix.config.run_32bit_tests }} - run: | - make clean - make -j2 ARCH=x86-32-sse41-popcnt build - ../tests/signature.sh $benchref - - - name: Test x86-32-sse2 build - if: ${{ matrix.config.run_32bit_tests }} - run: | - make clean - make -j2 ARCH=x86-32-sse2 build - ../tests/signature.sh $benchref - - - name: Test general-32 build - if: ${{ matrix.config.run_32bit_tests }} - run: | - make clean - make -j2 ARCH=general-32 build - ../tests/signature.sh $benchref - - # x86-64 tests - - - name: Test debug x86-64-modern build - if: ${{ matrix.config.run_64bit_tests }} - run: | - export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" - make clean - make -j2 ARCH=x86-64-modern optimize=no debug=yes build - ../tests/signature.sh $benchref - - - name: Test x86-64-modern build - if: ${{ matrix.config.run_64bit_tests }} - run: | - make clean - make -j2 ARCH=x86-64-modern build - ../tests/signature.sh $benchref - - - name: Test x86-64-ssse3 build - if: ${{ matrix.config.run_64bit_tests }} - run: | - make clean - make -j2 ARCH=x86-64-ssse3 build - ../tests/signature.sh $benchref - - - name: Test x86-64-sse3-popcnt build - if: ${{ matrix.config.run_64bit_tests }} - run: | - make clean - make -j2 ARCH=x86-64-sse3-popcnt build - ../tests/signature.sh $benchref - - - name: Test x86-64 build - if: ${{ matrix.config.run_64bit_tests }} - run: | - make clean - make -j2 ARCH=x86-64 build - ../tests/signature.sh $benchref - - - name: Test general-64 build - if: matrix.config.run_64bit_tests - run: | - make clean - make -j2 ARCH=general-64 build - ../tests/signature.sh $benchref - - # x86-64 with newer extensions tests - - - name: Compile x86-64-avx2 build - if: ${{ matrix.config.run_64bit_tests }} - run: | - make clean - make -j2 ARCH=x86-64-avx2 build - - - name: Compile x86-64-bmi2 build - if: ${{ matrix.config.run_64bit_tests }} - run: | - make clean - make -j2 ARCH=x86-64-bmi2 build - - - name: Compile x86-64-avx512 build - if: ${{ matrix.config.run_64bit_tests }} - run: | - make clean - make -j2 ARCH=x86-64-avx512 build - - - name: Compile x86-64-vnni512 build - if: ${{ matrix.config.run_64bit_tests }} - run: | - make clean - make -j2 ARCH=x86-64-vnni512 build - - - name: Compile x86-64-vnni256 build - if: ${{ matrix.config.run_64bit_tests }} - run: | - make clean - make -j2 ARCH=x86-64-vnni256 build - - # armv8 tests - - - name: Test armv8 build - if: ${{ matrix.config.run_armv8_tests }} - run: | - ANDROID_ROOT=/usr/local/lib/android - ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk - SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager - echo "y" | $SDKMANAGER "ndk;21.4.7075529" - ANDROID_NDK_ROOT=${ANDROID_SDK_ROOT}/ndk-bundle - ln -sfn $ANDROID_SDK_ROOT/ndk/21.4.7075529 $ANDROID_NDK_ROOT - export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH - export LDFLAGS="-static -Wno-unused-command-line-argument" - make clean - make -j2 ARCH=armv8 build - ../tests/signature.sh $benchref - - # armv7 tests - - - name: Test armv7 build - if: ${{ matrix.config.run_armv7_tests }} - run: | - ANDROID_ROOT=/usr/local/lib/android - ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk - SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager - echo "y" | $SDKMANAGER "ndk;21.4.7075529" - ANDROID_NDK_ROOT=${ANDROID_SDK_ROOT}/ndk-bundle - ln -sfn $ANDROID_SDK_ROOT/ndk/21.4.7075529 $ANDROID_NDK_ROOT - export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH - export LDFLAGS="-static -Wno-unused-command-line-argument" - make clean - make -j2 ARCH=armv7 build - ../tests/signature.sh $benchref - - - name: Test armv7-neon build - if: ${{ matrix.config.run_armv7_tests }} - run: | - ANDROID_ROOT=/usr/local/lib/android - ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk - SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager - echo "y" | $SDKMANAGER "ndk;21.4.7075529" - ANDROID_NDK_ROOT=${ANDROID_SDK_ROOT}/ndk-bundle - ln -sfn $ANDROID_SDK_ROOT/ndk/21.4.7075529 $ANDROID_NDK_ROOT - export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH - export LDFLAGS="-static -Wno-unused-command-line-argument" - make clean - make -j2 ARCH=armv7-neon build - ../tests/signature.sh $benchref - - # Other tests - - - name: Check perft and search reproducibility - if: ${{ matrix.config.run_64bit_tests }} - run: | - make clean - make -j2 ARCH=x86-64-modern build - ../tests/perft.sh - ../tests/reprosearch.sh - - # Sanitizers - - - name: Run under valgrind - if: ${{ matrix.config.run_expensive_tests }} - run: | - export CXXFLAGS="-O1 -fno-inline" - make clean - make -j2 ARCH=x86-64-modern debug=yes optimize=no build > /dev/null - ../tests/instrumented.sh --valgrind - ../tests/instrumented.sh --valgrind-thread - - - name: Run with UB sanitizer - if: ${{ matrix.config.run_expensive_tests }} - run: | - export CXXFLAGS="-O1 -fno-inline" - make clean - make -j2 ARCH=x86-64-modern sanitize=undefined optimize=no debug=yes build > /dev/null - ../tests/instrumented.sh --sanitizer-undefined - - - name: Run with thread sanitizer - if: ${{ matrix.config.run_expensive_tests }} - run: | - export CXXFLAGS="-O1 -fno-inline" - make clean - make -j2 ARCH=x86-64-modern sanitize=thread optimize=no debug=yes build > /dev/null - ../tests/instrumented.sh --sanitizer-thread diff --git a/.github/workflows/stockfish_compile_test.yml b/.github/workflows/stockfish_compile_test.yml new file mode 100644 index 00000000000..63136737070 --- /dev/null +++ b/.github/workflows/stockfish_compile_test.yml @@ -0,0 +1,115 @@ +name: Stockfish +on: + workflow_call: +jobs: + Stockfish: + name: ${{ matrix.config.name }} + runs-on: ${{ matrix.config.os }} + env: + COMPILER: ${{ matrix.config.compiler }} + COMP: ${{ matrix.config.comp }} + strategy: + matrix: + config: + - { + name: "Ubuntu 20.04 GCC", + os: ubuntu-20.04, + compiler: g++, + comp: gcc, + shell: 'bash {0}' + } + - { + name: "Ubuntu 20.04 Clang", + os: ubuntu-20.04, + compiler: clang++, + comp: clang, + shell: 'bash {0}' + } + - { + name: "MacOS 12 Apple Clang", + os: macos-12, + compiler: clang++, + comp: clang, + shell: 'bash {0}' + } + - { + name: "MacOS 12 GCC 11", + os: macos-12, + compiler: g++-11, + comp: gcc, + shell: 'bash {0}' + } + - { + name: "Windows 2022 Mingw-w64 GCC x86_64", + os: windows-2022, + compiler: g++, + comp: mingw, + msys_sys: 'mingw64', + msys_env: 'x86_64-gcc', + shell: 'msys2 {0}' + } + - { + name: "Windows 2022 Mingw-w64 Clang x86_64", + os: windows-2022, + compiler: clang++, + comp: clang, + msys_sys: 'clang64', + msys_env: 'clang-x86_64-clang', + shell: 'msys2 {0}' + } + + defaults: + run: + working-directory: src + shell: ${{ matrix.config.shell }} + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Setup msys and install required packages + if: runner.os == 'Windows' + uses: msys2/setup-msys2@v2 + with: + msystem: ${{matrix.config.msys_sys}} + install: mingw-w64-${{matrix.config.msys_env}} make git expect + + - name: Download the used network from the fishtest framework + run: | + make net + + - name: Check compiler + run: | + export PATH=$PATH:$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin + $COMPILER -v + + - name: Test help target + run: | + make help + + # x86-64 with newer extensions tests + + - name: Compile x86-64-avx2 build + run: | + make clean + make -j2 ARCH=x86-64-avx2 build + + - name: Compile x86-64-bmi2 build + run: | + make clean + make -j2 ARCH=x86-64-bmi2 build + + - name: Compile x86-64-avx512 build + run: | + make clean + make -j2 ARCH=x86-64-avx512 build + + - name: Compile x86-64-vnni512 build + run: | + make clean + make -j2 ARCH=x86-64-vnni512 build + + - name: Compile x86-64-vnni256 build + run: | + make clean + make -j2 ARCH=x86-64-vnni256 build \ No newline at end of file diff --git a/.github/workflows/stockfish_sanitizers.yml b/.github/workflows/stockfish_sanitizers.yml new file mode 100644 index 00000000000..61eaf0c9f07 --- /dev/null +++ b/.github/workflows/stockfish_sanitizers.yml @@ -0,0 +1,77 @@ +name: Stockfish +on: + workflow_call: +jobs: + Stockfish: + name: ${{ matrix.sanitizers.name }} + runs-on: ${{ matrix.config.os }} + env: + COMPILER: ${{ matrix.config.compiler }} + COMP: ${{ matrix.config.comp }} + CXXFLAGS: "-Werror" + strategy: + matrix: + config: + - { + name: "Ubuntu 20.04 GCC", + os: ubuntu-20.04, + compiler: g++, + comp: gcc, + shell: 'bash {0}' + } + sanitizers: + - { + name: Run with thread sanitizer, + make_option: sanitize=thread, + instrumented_option: sanitizer-thread + } + - { + name: Run with UB sanitizer, + make_option: sanitize=undefined, + instrumented_option: sanitizer-undefined + } + - { + name: Run under valgrind, + make_option: "", + instrumented_option: valgrind + } + - { + name: Run under valgrind-thread, + make_option: "", + instrumented_option: valgrind-thread + } + defaults: + run: + working-directory: src + shell: ${{ matrix.config.shell }} + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Download required linux packages + run: | + sudo apt update + sudo apt install expect valgrind g++-multilib qemu-user + + - name: Download the used network from the fishtest framework + run: | + make net + + - name: Check compiler + run: | + export PATH=$PATH:$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin + $COMPILER -v + + - name: Test help target + run: | + make help + + # Sanitizers + + - name: ${{ matrix.sanitizers.name }} + run: | + export CXXFLAGS="-O1 -fno-inline" + make clean + make -j2 ARCH=x86-64-modern ${{ matrix.sanitizers.make_option }} debug=yes optimize=no build > /dev/null + ../tests/instrumented.sh --${{ matrix.sanitizers.instrumented_option }} \ No newline at end of file diff --git a/.github/workflows/stockfish_test.yml b/.github/workflows/stockfish_test.yml new file mode 100644 index 00000000000..46b4e26f6dd --- /dev/null +++ b/.github/workflows/stockfish_test.yml @@ -0,0 +1,284 @@ +name: Stockfish +on: + workflow_call: +jobs: + Stockfish: + name: ${{ matrix.config.name }} + runs-on: ${{ matrix.config.os }} + env: + COMPILER: ${{ matrix.config.compiler }} + COMP: ${{ matrix.config.comp }} + CXXFLAGS: "-Werror" + strategy: + matrix: + config: + - { + name: "Ubuntu 20.04 GCC", + os: ubuntu-20.04, + compiler: g++, + comp: gcc, + run_32bit_tests: true, + run_64bit_tests: true, + shell: 'bash {0}' + } + - { + name: "Ubuntu 20.04 Clang", + os: ubuntu-20.04, + compiler: clang++, + comp: clang, + run_32bit_tests: true, + run_64bit_tests: true, + shell: 'bash {0}' + } + - { + name: "Ubuntu 20.04 NDK armv8", + os: ubuntu-20.04, + compiler: aarch64-linux-android21-clang++, + comp: ndk, + run_armv8_tests: false, + shell: 'bash {0}' + } + - { + name: "Ubuntu 20.04 NDK armv7", + os: ubuntu-20.04, + compiler: armv7a-linux-androideabi21-clang++, + comp: ndk, + run_armv7_tests: false, + shell: 'bash {0}' + } + - { + name: "MacOS 12 Apple Clang", + os: macos-12, + compiler: clang++, + comp: clang, + run_64bit_tests: true, + shell: 'bash {0}' + } + - { + name: "MacOS 12 GCC 11", + os: macos-12, + compiler: g++-11, + comp: gcc, + run_64bit_tests: true, + shell: 'bash {0}' + } + - { + name: "Windows 2022 Mingw-w64 GCC x86_64", + os: windows-2022, + compiler: g++, + comp: mingw, + run_64bit_tests: true, + msys_sys: 'mingw64', + msys_env: 'x86_64-gcc', + shell: 'msys2 {0}' + } + - { + name: "Windows 2022 Mingw-w64 GCC i686", + os: windows-2022, + compiler: g++, + comp: mingw, + run_32bit_tests: true, + msys_sys: 'mingw32', + msys_env: 'i686-gcc', + shell: 'msys2 {0}' + } + - { + name: "Windows 2022 Mingw-w64 Clang x86_64", + os: windows-2022, + compiler: clang++, + comp: clang, + run_64bit_tests: true, + msys_sys: 'clang64', + msys_env: 'clang-x86_64-clang', + shell: 'msys2 {0}' + } + exclude: + - config: + { + name: "Ubuntu 20.04 NDK armv7" + } + - config: + { + name: "Ubuntu 20.04 NDK armv8" + } + defaults: + run: + working-directory: src + shell: ${{ matrix.config.shell }} + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Download required linux packages + if: runner.os == 'Linux' + run: | + sudo apt update + sudo apt install expect valgrind g++-multilib qemu-user + + - name: Setup msys and install required packages + if: runner.os == 'Windows' + uses: msys2/setup-msys2@v2 + with: + msystem: ${{matrix.config.msys_sys}} + install: mingw-w64-${{matrix.config.msys_env}} make git expect + + - name: Download the used network from the fishtest framework + run: | + make net + + - name: Extract the bench number from the commit history + run: | + git log HEAD | grep "\b[Bb]ench[ :]\+[0-9]\{7\}" | head -n 1 | sed "s/[^0-9]*\([0-9]*\).*/\1/g" > git_sig + [ -s git_sig ] && echo "benchref=$(cat git_sig)" >> $GITHUB_ENV && echo "Reference bench:" $(cat git_sig) || echo "No bench found" + + - name: Check compiler + run: | + export PATH=$PATH:$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin + $COMPILER -v + + - name: Test help target + run: | + make help + + # x86-32 tests + + - name: Test debug x86-32 build + if: ${{ matrix.config.run_32bit_tests }} + run: | + export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" + make clean + make -j2 ARCH=x86-32 optimize=no debug=yes build + ../tests/signature.sh $benchref + + - name: Test x86-32 build + if: ${{ matrix.config.run_32bit_tests }} + run: | + make clean + make -j2 ARCH=x86-32 build + ../tests/signature.sh $benchref + + - name: Test x86-32-sse41-popcnt build + if: ${{ matrix.config.run_32bit_tests }} + run: | + make clean + make -j2 ARCH=x86-32-sse41-popcnt build + ../tests/signature.sh $benchref + + - name: Test x86-32-sse2 build + if: ${{ matrix.config.run_32bit_tests }} + run: | + make clean + make -j2 ARCH=x86-32-sse2 build + ../tests/signature.sh $benchref + + - name: Test general-32 build + if: ${{ matrix.config.run_32bit_tests }} + run: | + make clean + make -j2 ARCH=general-32 build + ../tests/signature.sh $benchref + + # x86-64 tests + + - name: Test debug x86-64-modern build + if: ${{ matrix.config.run_64bit_tests }} + run: | + export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" + make clean + make -j2 ARCH=x86-64-modern optimize=no debug=yes build + ../tests/signature.sh $benchref + + - name: Test x86-64-modern build + if: ${{ matrix.config.run_64bit_tests }} + run: | + make clean + make -j2 ARCH=x86-64-modern build + ../tests/signature.sh $benchref + + - name: Test x86-64-ssse3 build + if: ${{ matrix.config.run_64bit_tests }} + run: | + make clean + make -j2 ARCH=x86-64-ssse3 build + ../tests/signature.sh $benchref + + - name: Test x86-64-sse3-popcnt build + if: ${{ matrix.config.run_64bit_tests }} + run: | + make clean + make -j2 ARCH=x86-64-sse3-popcnt build + ../tests/signature.sh $benchref + + - name: Test x86-64 build + if: ${{ matrix.config.run_64bit_tests }} + run: | + make clean + make -j2 ARCH=x86-64 build + ../tests/signature.sh $benchref + + - name: Test general-64 build + if: matrix.config.run_64bit_tests + run: | + make clean + make -j2 ARCH=general-64 build + ../tests/signature.sh $benchref + + # armv8 tests + + - name: Test armv8 build + if: ${{ matrix.config.run_armv8_tests }} + run: | + ANDROID_ROOT=/usr/local/lib/android + ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk + SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager + echo "y" | $SDKMANAGER "ndk;21.4.7075529" + ANDROID_NDK_ROOT=${ANDROID_SDK_ROOT}/ndk-bundle + ln -sfn $ANDROID_SDK_ROOT/ndk/21.4.7075529 $ANDROID_NDK_ROOT + export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH + export LDFLAGS="-static -Wno-unused-command-line-argument" + make clean + make -j2 ARCH=armv8 build + ../tests/signature.sh $benchref + + # armv7 tests + + - name: Test armv7 build + if: ${{ matrix.config.run_armv7_tests }} + run: | + ANDROID_ROOT=/usr/local/lib/android + ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk + SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager + echo "y" | $SDKMANAGER "ndk;21.4.7075529" + ANDROID_NDK_ROOT=${ANDROID_SDK_ROOT}/ndk-bundle + ln -sfn $ANDROID_SDK_ROOT/ndk/21.4.7075529 $ANDROID_NDK_ROOT + export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH + export LDFLAGS="-static -Wno-unused-command-line-argument" + make clean + make -j2 ARCH=armv7 build + ../tests/signature.sh $benchref + + - name: Test armv7-neon build + if: ${{ matrix.config.run_armv7_tests }} + run: | + ANDROID_ROOT=/usr/local/lib/android + ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk + SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager + echo "y" | $SDKMANAGER "ndk;21.4.7075529" + ANDROID_NDK_ROOT=${ANDROID_SDK_ROOT}/ndk-bundle + ln -sfn $ANDROID_SDK_ROOT/ndk/21.4.7075529 $ANDROID_NDK_ROOT + export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH + export LDFLAGS="-static -Wno-unused-command-line-argument" + make clean + make -j2 ARCH=armv7-neon build + ../tests/signature.sh $benchref + + # Other tests + + - name: Check perft and search reproducibility + if: ${{ matrix.config.run_64bit_tests }} + run: | + make clean + make -j2 ARCH=x86-64-modern build + ../tests/perft.sh + ../tests/reprosearch.sh \ No newline at end of file From 219fa2f0a79381d35d9eb1240781cc598e74f647 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Thu, 17 Nov 2022 18:31:59 +0300 Subject: [PATCH 0898/1766] Do shallower search in case of lmr being not successful enough In case of a move passing LMR but it results being not too far from the current best search result produce a full depth search with reduced depth. Original idea by lonfom169 . Passed STC: https://tests.stockfishchess.org/tests/view/6373409b54d69a2f33913fbd LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 169504 W: 45351 L: 44848 D: 79305 Ptnml(0-2): 598, 18853, 45353, 19344, 604 Passed LTC: https://tests.stockfishchess.org/tests/view/6374c58528e3405283eb8d2d LLR: 2.96 (-2.94,2.94) <0.50,2.50> Total: 51144 W: 13802 L: 13471 D: 23871 Ptnml(0-2): 19, 4928, 15362, 5229, 34 closes https://github.com/official-stockfish/Stockfish/pull/4230 bench 4277005 --- src/search.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 77619345c8f..5839763dc7f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1184,8 +1184,11 @@ namespace { // Do full depth search when reduced LMR search fails high if (value > alpha && d < newDepth) { + // Adjust full depth search based on LMR results - if result + // was good enough search deeper, if it was bad enough search shallower const bool doDeeperSearch = value > (alpha + 64 + 11 * (newDepth - d)); - value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth + doDeeperSearch, !cutNode); + const bool doShallowerSearch = value < bestValue + newDepth; + value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth + doDeeperSearch - doShallowerSearch, !cutNode); int bonus = value > alpha ? stat_bonus(newDepth) : -stat_bonus(newDepth); From 41c6a74d372799c6ed94b842db66f956d080e0b0 Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Mon, 14 Nov 2022 08:11:27 -0500 Subject: [PATCH 0899/1766] Simplification away Cutoff Reset STC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 150184 W: 39913 L: 39819 D: 70452 Ptnml(0-2): 493, 16796, 40474, 16782, 547 https://tests.stockfishchess.org/tests/view/63723e9e54d69a2f33911d3c LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 58880 W: 15890 L: 15717 D: 27273 Ptnml(0-2): 35, 5765, 17659, 5954, 27 https://tests.stockfishchess.org/tests/view/6373baf49849fa7a36a65427 closes https://github.com/official-stockfish/Stockfish/pull/4231 Bench: 4035152 --- src/search.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5839763dc7f..fa87f1c3f0f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1295,8 +1295,6 @@ namespace { } } } - else - ss->cutoffCnt = 0; // If the move is worse than some previously searched move, remember it to update its stats later From d756d97a66cb18de182e018446b9149a5ff8ef18 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 19 Nov 2022 10:57:08 +0100 Subject: [PATCH 0900/1766] Fix a missing conversion This conversion to cp was overlooked. closes https://github.com/official-stockfish/Stockfish/pull/4235 No functional change --- src/evaluate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d18c2c9328d..87412b81b0b 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -159,7 +159,7 @@ namespace Trace { Score scores[TERM_NB][COLOR_NB]; - double to_cp(Value v) { return double(v) / PawnValueEg; } + double to_cp(Value v) { return double(v) / UCI::NormalizeToPawnValue; } void add(int idx, Color c, Score s) { scores[idx][c] = s; From 341163116233682bf150d20cddedcb23e6d09431 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 19 Nov 2022 13:03:14 +0100 Subject: [PATCH 0901/1766] Update WDL model for current SF This updates the WDL model based on the LTC statistics (2M games). Relatively small change, note that this also adjusts the NormalizeToPawnValue (now 361), to keep win prob at 50% for 100cp. closes https://github.com/official-stockfish/Stockfish/pull/4236 No functional change. --- src/uci.cpp | 4 ++-- src/uci.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/uci.cpp b/src/uci.cpp index 19e2b0cb920..5d842d25e73 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -207,8 +207,8 @@ namespace { // The coefficients of a third-order polynomial fit is based on the fishtest data // for two parameters that need to transform eval to the argument of a logistic // function. - constexpr double as[] = { 1.04790516, -8.58534089, 39.42615625, 316.17524816}; - constexpr double bs[] = { -3.57324784, 22.28816201, -35.47480551, 85.60617701 }; + constexpr double as[] = { -0.58270499, 2.68512549, 15.24638015, 344.49745382}; + constexpr double bs[] = { -2.65734562, 15.96509799, -20.69040836, 73.61029937 }; // Enforce that NormalizeToPawnValue corresponds to a 50% win rate at ply 64 static_assert(UCI::NormalizeToPawnValue == int(as[0] + as[1] + as[2] + as[3])); diff --git a/src/uci.h b/src/uci.h index f5f2c385c6c..7a2ef255818 100644 --- a/src/uci.h +++ b/src/uci.h @@ -35,7 +35,7 @@ namespace UCI { // the win_rate_model() such that Stockfish outputs an advantage of // "100 centipawns" for a position if the engine has a 50% probability to win // from this position in selfplay at fishtest LTC time control. -const int NormalizeToPawnValue = 348; +const int NormalizeToPawnValue = 361; class Option; From d8f3209fb4053bec406645e6df0f8a4d71f5a749 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 19 Nov 2022 10:18:04 +0100 Subject: [PATCH 0902/1766] Update Top CPU Contributors list as of 2022-11-19. Thanks! closes https://github.com/official-stockfish/Stockfish/pull/4234 No functional change --- Top CPU Contributors.txt | 225 +++++++++++++++++++++------------------ 1 file changed, 120 insertions(+), 105 deletions(-) diff --git a/Top CPU Contributors.txt b/Top CPU Contributors.txt index 23a5d7f97e2..30c963d7c7e 100644 --- a/Top CPU Contributors.txt +++ b/Top CPU Contributors.txt @@ -1,245 +1,260 @@ -Contributors to Fishtest with >10,000 CPU hours, as of 2022-07-31. +Contributors to Fishtest with >10,000 CPU hours, as of 2022-11-19. Thank you! Username CPU Hours Games played ------------------------------------------------------------------ -noobpwnftw 33202707 2423743815 -technologov 5064327 270208248 -mlang 2963357 198937430 -dew 1677196 99717674 -grandphish2 1231326 74551309 -okrout 1102747 98977462 -TueRens 925904 57404676 -pemo 911980 35581261 -tvijlbrief 795993 51894442 -JojoM 774270 47311084 +noobpwnftw 36475307 2748033975 +technologov 14570711 760073590 +mlang 3026000 200065824 +dew 1689222 100034318 +grandphish2 1442171 86798057 +okrout 1439985 133471766 +pemo 1405374 44189811 +linrock 1299003 28382783 +TueRens 1163420 71159522 +JojoM 897158 55177114 +tvijlbrief 796125 51897690 mibere 703840 46867607 -linrock 697283 18804969 -gvreuls 564284 36392236 -cw 515739 34775505 -fastgm 500949 30101898 -oz 439015 31794460 -CSU_Dynasty 438017 29369136 +gvreuls 635982 40652394 +oz 590763 41201352 +sebastronomy 581517 23307132 +cw 517915 34865769 +fastgm 504266 30264740 +CSU_Dynasty 479901 31846710 +ctoks 433503 28180725 crunchy 427035 27344275 -ctoks 422671 27812261 -bcross 363335 25108521 -leszek 360149 22674005 -velislav 333325 21444360 +leszek 416883 27493447 +bcross 409982 28062127 +velislav 345954 22232274 Fisherman 327231 21829379 -Dantist 292327 17951982 -mgrabiak 247220 16137378 -nordlandia 226543 14601042 -robal 224740 14314972 -glinscott 217799 13780820 -ncfish1 207751 13909639 -drabel 203884 13922680 -mhoram 200022 12533963 +Dantist 296386 18031762 +mgrabiak 288928 18869896 +rpngn 259965 16281463 +robal 237653 15148350 +ncfish1 231764 15275003 +nordlandia 226923 14624832 +glinscott 208125 13277240 +drabel 204167 13930674 +mhoram 202894 12601997 bking_US 198894 11876016 -rpngn 191764 12236583 +thirdlife 198844 5453268 Thanar 179852 12365359 vdv 175544 9904472 +armo9494 168201 11136452 spams 157128 10319326 -marrco 150300 9402229 +marrco 151599 9551115 sqrt2 147963 9724586 -vdbergh 137480 8958795 +vdbergh 137690 8971569 CoffeeOne 137100 5024116 malala 136182 8002293 +DesolatedDodo 135276 8657464 xoto 133759 9159372 -davar 128645 8367253 -DesolatedDodo 124877 8056482 +davar 129023 8376525 dsmith 122059 7570238 amicic 119661 7938029 Data 113305 8220352 BrunoBanani 112960 7436849 -CypressChess 108321 7759588 +CypressChess 108331 7759788 +skiminki 106518 7062598 MaZePallas 102823 6633619 -skiminki 102168 6778440 sterni1971 100532 5880772 sunu 100167 7040199 +zeryl 99331 6221261 ElbertoOne 99028 7023771 -zeryl 96984 6162287 +DMBK 97572 6950312 +Calis007 96779 5611552 +cuistot 93111 5536500 brabos 92118 6186135 -cuistot 91738 5447070 +Wolfgang 91769 5720158 psk 89957 5984901 -racerschmacer 85712 6119648 +racerschmacer 85805 6122790 +jcAEie 85527 5630616 Vizvezdenec 83761 5344740 -sschnee 83003 4840890 +sschnee 83557 4853690 0x3C33 82614 5271253 -armo9494 82501 5806056 BRAVONE 81239 5054681 +Dubslow 78461 5042980 nssy 76497 5259388 -thirdlife 76478 1544524 -Calis007 76457 4281018 -jromang 75885 5230523 +jromang 76106 5236025 teddybaer 75125 5407666 +yurikvelo 73933 5031096 +tolkki963 73885 4721430 Pking_cda 73776 5293873 -Wolfgang 72750 4538670 -sebastronomy 70784 1329428 +Bobo1239 71675 4860987 solarlight 70517 5028306 dv8silencer 70287 3883992 -Bobo1239 68515 4652287 -yurikvelo 67651 4578970 +Gelma 69304 3980932 manap 66273 4121774 +megaman7de 65419 4120200 +markkulix 65331 4114860 +bigpen0r 64932 4683883 tinker 64333 4268790 qurashee 61208 3429862 +AGI 58325 4258646 robnjr 57262 4053117 -megaman7de 57023 3525850 Freja 56938 3733019 -MaxKlaxxMiner 56279 3410158 +MaxKlaxxMiner 56879 3423958 ttruscott 56010 3680085 rkl 55132 4164467 renouve 53811 3501516 -tolkki963 53294 3354682 -DMBK 52963 3933332 +Spprtr 52736 3410019 finfish 51360 3370515 eva42 51272 3599691 -Spprtr 51139 3299983 -eastorwest 51058 3451555 +eastorwest 51117 3454811 rap 49985 3219146 +unixwizard 49734 2536230 pb00067 49727 3298270 -bigpen0r 47667 3336927 ronaldjerum 47654 3240695 biffhero 46564 3111352 +GPUex 45861 2926502 Fifis 45843 3088497 +oryx 45578 3493978 VoyagerOne 45476 3452465 +Wencey 44943 2654490 speedycpu 43842 3003273 jbwiebe 43305 2805433 Antihistamine 41788 2761312 mhunt 41735 2691355 +olafm 41277 3284344 homyur 39893 2850481 gri 39871 2515779 -oryx 39602 3024830 +MarcusTullius 38303 2251097 +Garf 37741 2999686 +kdave 37424 2557406 SC 37299 2731694 -Garf 37213 2986270 -Dubslow 36714 2409254 csnodgrass 36207 2688994 jmdana 36157 2210661 -markkulix 35994 2226860 strelock 34716 2074055 EthanOConnor 33370 2090311 slakovv 32915 2021889 -gopeto 31078 2033362 +gopeto 31669 2060958 manapbk 30987 1810399 Prcuvu 30377 2170122 anst 30301 2190091 jkiiski 30136 1904470 +spcc 30135 1903728 hyperbolic.tom 29840 2017394 +xwziegtm 29763 2347412 chuckstablers 29659 2093438 Pyafue 29650 1902349 -MarcusTullius 28611 1646671 -spcc 28241 1821198 -belzedar94 27935 1789106 +belzedar94 28846 1811530 OuaisBla 27636 1578800 chriswk 26902 1868317 achambord 26582 1767323 Patrick_G 26276 1801617 yorkman 26193 1992080 +Ulysses 25289 1674274 SFTUser 25182 1675689 nabildanial 24942 1519409 Sharaf_DG 24765 1786697 -rodneyc 24375 1416258 -Ulysses 24017 1626140 +rodneyc 24376 1416402 agg177 23890 1395014 +Ente 23747 1674582 +Karpovbot 23629 1313186 JanErik 23408 1703875 -Ente 23403 1660988 -kdave 23392 1630462 Isidor 23388 1680691 -Norabor 23339 1602636 -cisco2015 22897 1762669 -Wencey 22573 1121406 +Norabor 23371 1603244 +cisco2015 22934 1763773 Zirie 22542 1472937 team-oh 22272 1636708 +Roady 22220 1465606 MazeOfGalious 21978 1629593 -sg4032 21947 1643265 +sg4032 21947 1643353 ianh2105 21725 1632562 xor12 21628 1680365 dex 21612 1467203 nesoneg 21494 1463031 -Roady 21323 1433822 +user213718 21454 1404128 +AndreasKrug 21227 1577833 sphinx 21211 1384728 -user213718 21196 1397710 jjoshua2 21001 1423089 horst.prack 20878 1465656 +jsys14 20729 1221010 0xB00B1ES 20590 1208666 j3corre 20405 941444 Adrian.Schmidt123 20316 1281436 -jcAEie 20221 1504162 +bonsi 20022 1300682 wei 19973 1745989 +dapper 19754 1167758 +Zake9298 19745 1458416 +fishtester 19617 1257388 rstoesser 19569 1293588 eudhan 19274 1283717 -fishtester 19145 1242668 vulcan 18871 1729392 +Jopo12321 18803 1036284 jundery 18445 1115855 -iisiraider 18247 1101015 ville 17883 1384026 +5t0ckf15hTr4in3r 17809 1105858 chris 17698 1487385 +dju 17697 994333 purplefishies 17595 1092533 -dju 17353 978595 -AndreasKrug 17191 1317997 +iisiraider 17275 1049015 DragonLord 17014 1162790 -Jopo12321 16966 944924 -GPUex 16744 1077826 -xwziegtm 16608 1276372 +Karby 16457 1010138 +Goatminola 16278 1145026 IgorLeMasson 16064 1147232 +Gaster319 16056 1109070 +redstone59 15953 1161664 +scuzzi 15757 968735 ako027ako 15671 1173203 -jsys14 15474 917092 Nikolay.IT 15154 1068349 Andrew Grant 15114 895539 -scuzzi 15112 960373 +Naven94 15054 834762 OssumOpossum 14857 1007129 -Karby 14808 867120 +qoo_charly_cai 14490 847865 enedene 14476 905279 -bpfliegel 14298 884523 +szupaw 14252 929130 +bpfliegel 14233 882523 mpx86 14019 759568 jpulman 13982 870599 -Naven94 13879 811552 -Karpovbot 13808 734276 crocogoat 13803 1117422 -joster 13794 950160 Nesa92 13786 1114691 +joster 13710 946160 mbeier 13650 1044928 Hjax 13535 915487 Dark_wizzie 13422 1007152 Rudolphous 13244 883140 Machariel 13010 863104 +infinigon 12991 943216 +pirt 12925 985437 +Skiff84 12923 649994 mabichito 12903 749391 thijsk 12886 722107 AdrianSA 12860 804972 -infinigon 12807 937332 Flopzee 12698 894821 -pirt 12551 965597 fatmurphy 12547 853210 +woutboat 12419 836696 SapphireBrand 12416 969604 +Oakwen 12406 840961 +deflectooor 12386 579392 modolief 12386 896470 Farseer 12249 694108 pgontarz 12151 848794 stocky 11954 699440 mschmidt 11941 803401 -Oakwen 11925 818865 -MooTheCow 11851 772628 -deflectooor 11642 565132 -dbernier 11609 818636 -Skiff84 11604 602786 +MooTheCow 11871 773654 +Jackfish 11867 773550 +dbernier 11705 821780 +whelanh 11557 245188 Maxim 11543 836024 +Nullvalue 11534 731410 +icewulf 11528 650470 +FormazChar 11523 861599 infinity 11470 727027 -FormazChar 11430 856559 -aga 11409 695071 -Jackfish 11403 750526 +aga 11412 695127 torbjo 11395 729145 Thomas A. Anderson 11372 732094 savage84 11358 670860 +ali-al-zhrani 11272 781310 d64 11263 789184 -qoo_charly_cai 11127 671959 +Bourbaki 11108 709144 snicolet 11106 869170 -ali-al-zhrani 11098 768494 -whelanh 11067 235676 +Alb11747 10855 696920 basepi 10637 744851 Cubox 10621 826448 -Alb11747 10558 689794 +Karmatron 10616 674818 michaelrpg 10509 739239 OIVAS7572 10420 995586 -Garruk 10343 704723 +Garruk 10348 704905 dzjp 10343 732529 ols 10259 570669 -lbraesch 10252 647825 -Karmatron 10195 661432 From 85ae65db1dd315ea500f30226781c4c471d30c5d Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Tue, 22 Nov 2022 20:07:33 +0300 Subject: [PATCH 0903/1766] Skip full depth search in LMR depending on depth dynamically adjust newDepth, and skip full depth search if newDepth doesn't exceed the previous search depth. This affects the used newDepth for future searches, and influences the stat bonus for the move. Passed STC: https://tests.stockfishchess.org/tests/view/63795500aa34433735bc1cfe LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 112776 W: 30082 L: 29663 D: 53031 Ptnml(0-2): 352, 12453, 30423, 12744, 416 Passed LTC: https://tests.stockfishchess.org/tests/view/6379ea39aa34433735bc2f9b LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 83576 W: 22559 L: 22169 D: 38848 Ptnml(0-2): 38, 8011, 25303, 8395, 41 closes https://github.com/official-stockfish/Stockfish/pull/4240 Bench: 4390318 --- src/search.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index fa87f1c3f0f..5cef5c40c23 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1188,7 +1188,11 @@ namespace { // was good enough search deeper, if it was bad enough search shallower const bool doDeeperSearch = value > (alpha + 64 + 11 * (newDepth - d)); const bool doShallowerSearch = value < bestValue + newDepth; - value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth + doDeeperSearch - doShallowerSearch, !cutNode); + + newDepth += doDeeperSearch - doShallowerSearch; + + if (newDepth > d) + value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode); int bonus = value > alpha ? stat_bonus(newDepth) : -stat_bonus(newDepth); From 1370127fcd72b5c6646ff03a4a779b81ad0bcf3d Mon Sep 17 00:00:00 2001 From: peregrineshahin Date: Sun, 13 Nov 2022 11:08:17 +0200 Subject: [PATCH 0904/1766] Simplify both quiet check evasions' conditions passed Non-regression STC: https://tests.stockfishchess.org/tests/view/6370b647f1b748d4819e0b64 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 162904 W: 43249 L: 43171 D: 76484 Ptnml(0-2): 491, 17089, 46220, 17155, 497 closes https://github.com/official-stockfish/Stockfish/pull/4228 No functional change --- src/search.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5cef5c40c23..e73f68ffb0a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1552,12 +1552,11 @@ namespace { && (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < 0) continue; - // movecount pruning for quiet check evasions + // We prune after 2nd quiet check evasion where being 'in check' is implicitly checked through the counter + // and being a 'quiet' apart from being a tt move is assumed after an increment because captures are pushed ahead. if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY - && quietCheckEvasions > 1 - && !capture - && ss->inCheck) - continue; + && quietCheckEvasions > 1) + break; quietCheckEvasions += !capture && ss->inCheck; From f5a31b7e576e2e56825fcfdff75c739ed545e852 Mon Sep 17 00:00:00 2001 From: Guenther Demetz Date: Tue, 22 Nov 2022 11:07:18 +0100 Subject: [PATCH 0905/1766] Correctly output lowerbound/upperbound in threaded searches fixes the lowerbound/upperbound output by taking the alpha,beta bracket into account also if a bestThread is selected that is different from the master thread. Instead of keeping track which bounds where used in the specific search, in this version we simply store the quality (exact, upperbound, lowerbound) of the score along with the actual score as information on rootMove. closes https://github.com/official-stockfish/Stockfish/pull/4239 No functional change --- src/search.cpp | 14 ++++++++------ src/search.h | 2 ++ src/uci.h | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index e73f68ffb0a..b3f60f3d15b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -244,7 +244,7 @@ void MainThread::search() { // Send again PV info if we have a new best thread if (bestThread != this) - sync_cout << UCI::pv(bestThread->rootPos, bestThread->completedDepth, -VALUE_INFINITE, VALUE_INFINITE) << sync_endl; + sync_cout << UCI::pv(bestThread->rootPos, bestThread->completedDepth) << sync_endl; sync_cout << "bestmove " << UCI::move(bestThread->rootMoves[0].pv[0], rootPos.is_chess960()); @@ -392,7 +392,7 @@ void Thread::search() { && multiPV == 1 && (bestValue <= alpha || bestValue >= beta) && Time.elapsed() > 3000) - sync_cout << UCI::pv(rootPos, rootDepth, alpha, beta) << sync_endl; + sync_cout << UCI::pv(rootPos, rootDepth) << sync_endl; // In case of failing low/high increase aspiration window and // re-search, otherwise exit the loop. @@ -423,7 +423,7 @@ void Thread::search() { if ( mainThread && (Threads.stop || pvIdx + 1 == multiPV || Time.elapsed() > 3000)) - sync_cout << UCI::pv(rootPos, rootDepth, alpha, beta) << sync_endl; + sync_cout << UCI::pv(rootPos, rootDepth) << sync_endl; } if (!Threads.stop) @@ -1246,6 +1246,8 @@ namespace { { rm.score = value; rm.selDepth = thisThread->selDepth; + rm.scoreLowerbound = value >= beta; + rm.scoreUpperbound = value <= alpha; rm.pv.resize(1); assert((ss+1)->pv); @@ -1820,7 +1822,7 @@ void MainThread::check_time() { /// UCI::pv() formats PV information according to the UCI protocol. UCI requires /// that all (if any) unsearched PV lines are sent using a previous search score. -string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) { +string UCI::pv(const Position& pos, Depth depth) { std::stringstream ss; TimePoint elapsed = Time.elapsed() + 1; @@ -1858,8 +1860,8 @@ string UCI::pv(const Position& pos, Depth depth, Value alpha, Value beta) { if (Options["UCI_ShowWDL"]) ss << UCI::wdl(v, pos.game_ply()); - if (!tb && i == pvIdx) - ss << (v >= beta ? " lowerbound" : v <= alpha ? " upperbound" : ""); + if (i == pvIdx && !tb && updated) // tablebase- and previous-scores are exact + ss << (rootMoves[i].scoreLowerbound ? " lowerbound" : (rootMoves[i].scoreUpperbound ? " upperbound" : "")); ss << " nodes " << nodesSearched << " nps " << nodesSearched * 1000 / elapsed diff --git a/src/search.h b/src/search.h index f264bcae840..60f2762a6f4 100644 --- a/src/search.h +++ b/src/search.h @@ -71,6 +71,8 @@ struct RootMove { Value score = -VALUE_INFINITE; Value previousScore = -VALUE_INFINITE; Value averageScore = -VALUE_INFINITE; + bool scoreLowerbound = false; + bool scoreUpperbound = false; int selDepth = 0; int tbRank = 0; Value tbScore; diff --git a/src/uci.h b/src/uci.h index 7a2ef255818..3b5a6764b48 100644 --- a/src/uci.h +++ b/src/uci.h @@ -79,7 +79,7 @@ void loop(int argc, char* argv[]); std::string value(Value v); std::string square(Square s); std::string move(Move m, bool chess960); -std::string pv(const Position& pos, Depth depth, Value alpha, Value beta); +std::string pv(const Position& pos, Depth depth); std::string wdl(Value v, int ply); Move to_move(const Position& pos, std::string& str); From 6a6faac04db26daae6a77b6df6529e22d549531b Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Sun, 27 Nov 2022 12:00:16 -0500 Subject: [PATCH 0906/1766] Remove PvNode Parameter for cutoff LMR STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 198520 W: 52673 L: 52632 D: 93215 Ptnml(0-2): 645, 22241, 53499, 22178, 697 https://tests.stockfishchess.org/tests/view/63746e8f9849fa7a36a6698f LTC: LLR: 2.97 (-2.94,2.94) <-1.75,0.25> Total: 253568 W: 67487 L: 67501 D: 118580 Ptnml(0-2): 109, 25222, 76141, 25198, 114 https://tests.stockfishchess.org/tests/view/63839859d2b9c924c4c4feb7 closes https://github.com/official-stockfish/Stockfish/pull/4248 Bench: 3733322 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index b3f60f3d15b..abb51190485 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1156,13 +1156,13 @@ namespace { if (singularQuietLMR) r--; - // Dicrease reduction if we move a threatened piece (~1 Elo) + // Decrease reduction if we move a threatened piece (~1 Elo) if ( depth > 9 && (mp.threatenedPieces & from_sq(move))) r--; // Increase reduction if next ply has a lot of fail high - if ((ss+1)->cutoffCnt > 3 && !PvNode) + if ((ss+1)->cutoffCnt > 3) r++; ss->statScore = 2 * thisThread->mainHistory[us][from_to(move)] From c7118fb46dc71d87d3f2c925b24e67184693da4f Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Thu, 1 Dec 2022 16:31:35 -0500 Subject: [PATCH 0907/1766] Simply do full sort on captures. STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 42712 W: 11413 L: 11203 D: 20096 Ptnml(0-2): 145, 4661, 11544, 4851, 155 https://tests.stockfishchess.org/tests/view/6384df57d2b9c924c4c53900 LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 239072 W: 64065 L: 64067 D: 110940 Ptnml(0-2): 106, 23735, 71859, 23727, 109 https://tests.stockfishchess.org/tests/view/63851120d2b9c924c4c541ee closes https://github.com/official-stockfish/Stockfish/pull/4249 Bench: 3467381 --- src/movepick.cpp | 6 +++--- src/movepick.h | 2 +- src/search.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 587c6d79c0e..188d6bd8172 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -88,8 +88,8 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist /// MovePicker constructor for ProbCut: we generate captures with SEE greater /// than or equal to the given threshold. -MovePicker::MovePicker(const Position& p, Move ttm, Value th, Depth d, const CapturePieceToHistory* cph) - : pos(p), captureHistory(cph), ttMove(ttm), threshold(th), depth(d) +MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph) + : pos(p), captureHistory(cph), ttMove(ttm), threshold(th) { assert(!pos.checkers()); @@ -191,7 +191,7 @@ Move MovePicker::next_move(bool skipQuiets) { endMoves = generate(pos, cur); score(); - partial_insertion_sort(cur, endMoves, -3000 * depth); + partial_insertion_sort(cur, endMoves, std::numeric_limits::min()); ++stage; goto top; diff --git a/src/movepick.h b/src/movepick.h index 55fcc6442dc..e4c4a5bfdea 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -128,7 +128,7 @@ class MovePicker { const CapturePieceToHistory*, const PieceToHistory**, Square); - MovePicker(const Position&, Move, Value, Depth, const CapturePieceToHistory*); + MovePicker(const Position&, Move, Value, const CapturePieceToHistory*); Move next_move(bool skipQuiets = false); Bitboard threatenedPieces; diff --git a/src/search.cpp b/src/search.cpp index abb51190485..c8163d1fa21 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -855,7 +855,7 @@ namespace { { assert(probCutBeta < VALUE_INFINITE); - MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, depth - 3, &captureHistory); + MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, &captureHistory); while ((move = mp.next_move()) != MOVE_NONE) if (move != excludedMove && pos.legal(move)) From d60f5de9670cc84ba7940b5815bc3e128da9423f Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 28 Nov 2022 20:59:38 +0100 Subject: [PATCH 0908/1766] Fix bestThread selection If multiple threads have the same best move, pick the thread with the largest contribution to the confidence vote. This thread will later be used to display PV, so this patch is about user-friendliness and/or least surprises, it non-functional for playing strenght. closes https://github.com/official-stockfish/Stockfish/pull/4246 No functional change --- src/thread.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/thread.cpp b/src/thread.cpp index 9ce408e059e..b7471f60267 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -221,11 +221,14 @@ Thread* ThreadPool::get_best_thread() const { minScore = std::min(minScore, th->rootMoves[0].score); // Vote according to score and depth, and select the best thread + auto thread_value = [minScore](Thread* th) { + return (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth); + }; + for (Thread* th : *this) - { - votes[th->rootMoves[0].pv[0]] += - (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth); + votes[th->rootMoves[0].pv[0]] += thread_value(th); + for (Thread* th : *this) if (abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY) { // Make sure we pick the shortest mate / TB conversion or stave off mate the longest @@ -236,9 +239,8 @@ Thread* ThreadPool::get_best_thread() const { || ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY && ( votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]] || ( votes[th->rootMoves[0].pv[0]] == votes[bestThread->rootMoves[0].pv[0]] - && th->rootMoves[0].pv.size() > bestThread->rootMoves[0].pv.size())))) + && thread_value(th) > thread_value(bestThread))))) bestThread = th; - } return bestThread; } From 758f9c9350abee36a5865ec701560db8ea62004d Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 3 Dec 2022 08:50:46 +0100 Subject: [PATCH 0909/1766] Stockfish 15.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Official release version of Stockfish 15.1 Bench: 3467381 --- Today, we have the pleasure to announce Stockfish 15.1. As usual, downloads will be freely available at stockfishchess.org/download *Elo gain and competition results* With this release, version 5 of the NNUE neural net architecture has been introduced, and the training data has been extended to include Fischer random chess (FRC) positions. As a result, Elo gains are largest for FRC, reaching up to 50 Elo for doubly randomized FRC[1] (DFRC). More importantly, also for standard chess this release progressed and will win two times more game pairs than it loses[2] against Stockfish 15. Stockfish continues to win in a dominating way[3] all chess engine tournaments, including the TCEC Superfinal, Cup, FRC, DFRC, and Swiss as well as the CCC Bullet, Blitz, and Rapid events. *New evaluation* This release also introduces a new convention for the evaluation that is reported by search. An evaluation of +1 is now no longer tied to the value of one pawn, but to the likelihood of winning the game. With a +1 evaluation, Stockfish has now a 50% chance of winning the game against an equally strong opponent. This convention scales down evaluations a bit compared to Stockfish 15 and allows for consistent evaluations in the future. *ChessBase settlement* In this release period, the Stockfish team has successfully enforced its GPL license against ChessBase. This has been an intense process that included filing a lawsuit[4], a court hearing[5], and finally negotiating a settlement[6] that established that ChessBase infringed on the license by not distributing the Stockfish derivatives Fat Fritz 2 and Houdini 6 as free software, and that ensures ChessBase will respect the Free Software principles in the future. This settlement has been covered by major chess sites (see e.g. lichess.org[7] and chess.com[8]), and we are proud that it has been hailed as a ‘historic violation settlement[9]’ by the Software Freedom Conservancy. *Thank you* The Stockfish project builds on a thriving community of enthusiasts (thanks everybody!) that contribute their expertise, time, and resources to build a free and open-source chess engine that is robust, widely available, and very strong. We invite our chess fans to join the fishtest testing framework and programmers to contribute to the project[10]. The Stockfish team [1] https://tests.stockfishchess.org/tests/view/638a6170d2b9c924c4c62cb4 [2] https://tests.stockfishchess.org/tests/view/638a4dd7d2b9c924c4c6297b [3] https://en.wikipedia.org/wiki/Stockfish_(chess)#Competition_results [4] https://stockfishchess.org/blog/2021/our-lawsuit-against-chessbase/ [5] https://stockfishchess.org/blog/2022/public-court-hearing-soon/ [6] https://stockfishchess.org/blog/2022/chessbase-stockfish-agreement/ [7] https://lichess.org/blog/Y3u1mRAAACIApBVn/settlement-reached-in-stockfish-v-chessbase [8] https://www.chess.com/news/view/chessbase-stockfish-reach-settlement [9] https://sfconservancy.org/news/2022/nov/28/sfc-named-trusted-party-in-gpl-case/ [10] https://stockfishchess.org/get-involved/ --- src/misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index c7fa0e9ae10..2d86969f536 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -68,7 +68,7 @@ namespace Stockfish { namespace { /// Version number or dev. -const string version = "dev"; +const string version = "15.1"; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We From e8caa6640df15b1823d5d4b94e759d52923769e6 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 8 Dec 2022 20:33:32 +0100 Subject: [PATCH 0910/1766] Restore development version No functional change --- src/misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index 2d86969f536..c7fa0e9ae10 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -68,7 +68,7 @@ namespace Stockfish { namespace { /// Version number or dev. -const string version = "15.1"; +const string version = "dev"; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We From 9fc203a3d05262ac74bfca8b4618155e18d76003 Mon Sep 17 00:00:00 2001 From: ppigazzini Date: Thu, 8 Dec 2022 18:32:30 +0100 Subject: [PATCH 0911/1766] Set the right PATH for ARM compiler and build tests in CI Fix for the GitHub upgrade: https://github.com/actions/runner-images/issues/5879 that broke our ARM workflows because it changed the value of the ANDROID_NDK_HOME variable referenced in our PATH. closes https://github.com/official-stockfish/Stockfish/pull/4267 No functional change --- .github/workflows/stockfish_test.yml | 43 ++++++++++++---------------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/.github/workflows/stockfish_test.yml b/.github/workflows/stockfish_test.yml index 46b4e26f6dd..e4e6205f08a 100644 --- a/.github/workflows/stockfish_test.yml +++ b/.github/workflows/stockfish_test.yml @@ -35,7 +35,7 @@ jobs: os: ubuntu-20.04, compiler: aarch64-linux-android21-clang++, comp: ndk, - run_armv8_tests: false, + run_armv8_tests: true, shell: 'bash {0}' } - { @@ -43,7 +43,7 @@ jobs: os: ubuntu-20.04, compiler: armv7a-linux-androideabi21-clang++, comp: ndk, - run_armv7_tests: false, + run_armv7_tests: true, shell: 'bash {0}' } - { @@ -92,15 +92,6 @@ jobs: msys_env: 'clang-x86_64-clang', shell: 'msys2 {0}' } - exclude: - - config: - { - name: "Ubuntu 20.04 NDK armv7" - } - - config: - { - name: "Ubuntu 20.04 NDK armv8" - } defaults: run: working-directory: src @@ -120,8 +111,8 @@ jobs: if: runner.os == 'Windows' uses: msys2/setup-msys2@v2 with: - msystem: ${{matrix.config.msys_sys}} - install: mingw-w64-${{matrix.config.msys_env}} make git expect + msystem: ${{ matrix.config.msys_sys }} + install: mingw-w64-${{ matrix.config.msys_env }} make git expect - name: Download the used network from the fishtest framework run: | @@ -134,7 +125,14 @@ jobs: - name: Check compiler run: | - export PATH=$PATH:$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin + if [ $COMP == ndk ]; then + ANDROID_ROOT=/usr/local/lib/android + ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk + SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager + echo "y" | $SDKMANAGER "ndk;21.4.7075529" + ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/21.4.7075529 + export PATH=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH + fi $COMPILER -v - name: Test help target @@ -233,9 +231,8 @@ jobs: ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager echo "y" | $SDKMANAGER "ndk;21.4.7075529" - ANDROID_NDK_ROOT=${ANDROID_SDK_ROOT}/ndk-bundle - ln -sfn $ANDROID_SDK_ROOT/ndk/21.4.7075529 $ANDROID_NDK_ROOT - export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH + ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/21.4.7075529 + export PATH=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH export LDFLAGS="-static -Wno-unused-command-line-argument" make clean make -j2 ARCH=armv8 build @@ -250,9 +247,8 @@ jobs: ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager echo "y" | $SDKMANAGER "ndk;21.4.7075529" - ANDROID_NDK_ROOT=${ANDROID_SDK_ROOT}/ndk-bundle - ln -sfn $ANDROID_SDK_ROOT/ndk/21.4.7075529 $ANDROID_NDK_ROOT - export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH + ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/21.4.7075529 + export PATH=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH export LDFLAGS="-static -Wno-unused-command-line-argument" make clean make -j2 ARCH=armv7 build @@ -265,9 +261,8 @@ jobs: ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager echo "y" | $SDKMANAGER "ndk;21.4.7075529" - ANDROID_NDK_ROOT=${ANDROID_SDK_ROOT}/ndk-bundle - ln -sfn $ANDROID_SDK_ROOT/ndk/21.4.7075529 $ANDROID_NDK_ROOT - export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH + ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/21.4.7075529 + export PATH=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH export LDFLAGS="-static -Wno-unused-command-line-argument" make clean make -j2 ARCH=armv7-neon build @@ -281,4 +276,4 @@ jobs: make clean make -j2 ARCH=x86-64-modern build ../tests/perft.sh - ../tests/reprosearch.sh \ No newline at end of file + ../tests/reprosearch.sh From 98965c139df1483a3d684ee8bc7a60dc4b95efa1 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Fri, 2 Dec 2022 18:23:28 +0300 Subject: [PATCH 0912/1766] doEvenDeeperSearch + tuning Credit for the main idea of doEvenDeeperSearch goes to Vizvezdenec, tuning by FauziAkram: Expansion of existing logic of doDeeperSearch - if value from LMR is really really good do full depth search not 1 ply deeper but rather 2 instead. Passed STC: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 330048 W: 87672 L: 86942 D: 155434 Ptnml(0-2): 1012, 36739, 88912, 37229, 1132 https://tests.stockfishchess.org/tests/view/638a1cadd2b9c924c4c621d2 Passed LTC: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 216696 W: 57891 L: 57240 D: 101565 Ptnml(0-2): 72, 21221, 65152, 21790, 113 https://tests.stockfishchess.org/tests/view/638c7d52a971f1f096c68fe2 closes https://github.com/official-stockfish/Stockfish/pull/4256 Bench: 3461830 --- src/evaluate.cpp | 14 +++++++------- src/search.cpp | 7 ++++--- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 87412b81b0b..93e665dfe85 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1063,7 +1063,7 @@ Value Eval::evaluate(const Position& pos, int* complexity) { else { int nnueComplexity; - int scale = 1064 + 106 * pos.non_pawn_material() / 5120; + int scale = 1076 + 96 * pos.non_pawn_material() / 5120; Color stm = pos.side_to_move(); Value optimism = pos.this_thread()->optimism[stm]; @@ -1071,21 +1071,21 @@ Value Eval::evaluate(const Position& pos, int* complexity) { Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); // Blend nnue complexity with (semi)classical complexity - nnueComplexity = ( 416 * nnueComplexity - + 424 * abs(psq - nnue) + nnueComplexity = ( 412 * nnueComplexity + + 428 * abs(psq - nnue) + (optimism > 0 ? int(optimism) * int(psq - nnue) : 0) - ) / 1024; + ) / 1026; // Return hybrid NNUE complexity to caller if (complexity) *complexity = nnueComplexity; - optimism = optimism * (269 + nnueComplexity) / 256; - v = (nnue * scale + optimism * (scale - 754)) / 1024; + optimism = optimism * (278 + nnueComplexity) / 256; + v = (nnue * scale + optimism * (scale - 755)) / 1024; } // Damp down the evaluation linearly when shuffling - v = v * (195 - pos.rule50_count()) / 211; + v = v * (197 - pos.rule50_count()) / 214; // Guarantee evaluation does not hit the tablebase range v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); diff --git a/src/search.cpp b/src/search.cpp index c8163d1fa21..343c098a587 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -81,7 +81,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return std::min((12 * d + 282) * d - 349 , 1594); + return std::min((12 * d + 282) * d - 349 , 1480); } // Add a small random component to draw evaluations to avoid 3-fold blindness @@ -1172,7 +1172,7 @@ namespace { - 4433; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - r -= ss->statScore / (13628 + 4000 * (depth > 7 && depth < 19)); + r -= ss->statScore / (13000 + 4152 * (depth > 7 && depth < 19)); // In general we want to cap the LMR depth search at newDepth, but when // reduction is negative, we allow this move a limited search extension @@ -1187,9 +1187,10 @@ namespace { // Adjust full depth search based on LMR results - if result // was good enough search deeper, if it was bad enough search shallower const bool doDeeperSearch = value > (alpha + 64 + 11 * (newDepth - d)); + const bool doEvenDeeperSearch = value > alpha + 582; const bool doShallowerSearch = value < bestValue + newDepth; - newDepth += doDeeperSearch - doShallowerSearch; + newDepth += doDeeperSearch - doShallowerSearch + doEvenDeeperSearch; if (newDepth > d) value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode); From cb0c7a98485fbef4e5d6ed5f5b08201113ce0b4e Mon Sep 17 00:00:00 2001 From: Guenther Demetz Date: Tue, 6 Dec 2022 19:09:33 +0100 Subject: [PATCH 0913/1766] Correctly output lowerbound/upperbound scores fixes the lowerbound/upperbound output by avoiding scores outside the alpha,beta bracket. Since SF search uses fail-soft we can't simply take the returned value as score. closes https://github.com/official-stockfish/Stockfish/pull/4259 No functional change --- src/search.cpp | 14 ++++++++++---- src/search.h | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 343c098a587..04f73e1c0a2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1245,10 +1245,16 @@ namespace { // PV move or new best move? if (moveCount == 1 || value > alpha) { - rm.score = value; + rm.score = rm.uciScore = value; rm.selDepth = thisThread->selDepth; - rm.scoreLowerbound = value >= beta; - rm.scoreUpperbound = value <= alpha; + if (value >= beta) { + rm.scoreLowerbound = true; + rm.uciScore = beta; + } + else if (value <= alpha) { + rm.scoreUpperbound = true; + rm.uciScore = alpha; + } rm.pv.resize(1); assert((ss+1)->pv); @@ -1841,7 +1847,7 @@ string UCI::pv(const Position& pos, Depth depth) { continue; Depth d = updated ? depth : std::max(1, depth - 1); - Value v = updated ? rootMoves[i].score : rootMoves[i].previousScore; + Value v = updated ? rootMoves[i].uciScore : rootMoves[i].previousScore; if (v == -VALUE_INFINITE) v = VALUE_ZERO; diff --git a/src/search.h b/src/search.h index 60f2762a6f4..b620202d917 100644 --- a/src/search.h +++ b/src/search.h @@ -71,6 +71,7 @@ struct RootMove { Value score = -VALUE_INFINITE; Value previousScore = -VALUE_INFINITE; Value averageScore = -VALUE_INFINITE; + Value uciScore = -VALUE_INFINITE; bool scoreLowerbound = false; bool scoreUpperbound = false; int selDepth = 0; From 74fb936dbd49c61bf3352febd1c57a68888100d0 Mon Sep 17 00:00:00 2001 From: MinetaS Date: Tue, 6 Dec 2022 21:32:42 +0900 Subject: [PATCH 0914/1766] Invoke .depend only on build targets Add a constraint so that the dependency build only occurs when users actually run build tasks. This fixes a bug on some systems where gcc/g++ is not available. closes https://github.com/official-stockfish/Stockfish/pull/4255 No functional change --- src/Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 917bd5c06c7..0c98391bb84 100644 --- a/src/Makefile +++ b/src/Makefile @@ -512,7 +512,7 @@ endif ### Sometimes gcc is really clang ifeq ($(COMP),gcc) - gccversion = $(shell $(CXX) --version) + gccversion = $(shell $(CXX) --version 2>/dev/null) gccisclang = $(findstring clang,$(gccversion)) ifneq ($(gccisclang),) profile_make = clang-profile-make @@ -1006,4 +1006,6 @@ icc-profile-use: .depend: $(SRCS) -@$(CXX) $(DEPENDFLAGS) -MM $(SRCS) > $@ 2> /dev/null +ifneq (, $(filter $(MAKECMDGOALS), build profile-build)) -include .depend +endif From aa603cfeeb6e902bcf996758515170b996ec1fb6 Mon Sep 17 00:00:00 2001 From: ppigazzini Date: Thu, 8 Dec 2022 20:50:32 +0100 Subject: [PATCH 0915/1766] GitHub Action: upload ARM artifacts And some clean up in other files. closes https://github.com/official-stockfish/Stockfish/pull/4269 No functional change --- .github/workflows/stockfish.yml | 3 + .github/workflows/stockfish_arm_binaries.yml | 117 +++++++++++++++++++ .github/workflows/stockfish_binaries.yml | 7 +- .github/workflows/stockfish_compile_test.yml | 5 +- .github/workflows/stockfish_sanitizers.yml | 5 +- .github/workflows/stockfish_test.yml | 20 ++-- 6 files changed, 137 insertions(+), 20 deletions(-) create mode 100644 .github/workflows/stockfish_arm_binaries.yml diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index 07ecfc07b5c..6345b27cb74 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -19,3 +19,6 @@ jobs: Binaries: if: github.ref == 'refs/heads/master' uses: ./.github/workflows/stockfish_binaries.yml + ARM_Binaries: + if: github.ref == 'refs/heads/master' + uses: ./.github/workflows/stockfish_arm_binaries.yml diff --git a/.github/workflows/stockfish_arm_binaries.yml b/.github/workflows/stockfish_arm_binaries.yml new file mode 100644 index 00000000000..cf3ae710091 --- /dev/null +++ b/.github/workflows/stockfish_arm_binaries.yml @@ -0,0 +1,117 @@ +name: Stockfish +on: + workflow_call: +jobs: + Stockfish: + name: ${{ matrix.config.name }} ${{ matrix.binaries }} + runs-on: ${{ matrix.config.os }} + env: + COMPILER: ${{ matrix.config.compiler }} + COMP: ${{ matrix.config.comp }} + EMU: ${{ matrix.config.emu }} + EXT: ${{ matrix.config.ext }} + OS: ${{ matrix.config.os }} + BINARY: ${{ matrix.binaries }} + strategy: + matrix: + config: + - { + name: "Android NDK aarch64", + os: ubuntu-20.04, + compiler: aarch64-linux-android21-clang++, + emu: qemu-aarch64, + comp: ndk, + shell: 'bash {0}' + } + - { + name: "Android NDK arm", + os: ubuntu-20.04, + compiler: armv7a-linux-androideabi21-clang++, + emu: qemu-arm, + comp: ndk, + shell: 'bash {0}' + } + binaries: + - armv8 + - armv7 + - armv7-neon + exclude: + - binaries: armv8 + config: {compiler: armv7a-linux-androideabi21-clang++} + - binaries: armv7 + config: {compiler: aarch64-linux-android21-clang++} + - binaries: armv7-neon + config: {compiler: aarch64-linux-android21-clang++} + defaults: + run: + working-directory: src + shell: ${{ matrix.config.shell }} + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Download required linux packages + if: runner.os == 'Linux' + run: | + sudo apt update + sudo apt install qemu-user + + - name: Download the used network from the fishtest framework + run: | + make net + + - name: Check compiler + run: | + if [ $COMP == ndk ]; then + ANDROID_ROOT=/usr/local/lib/android + ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk + SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager + echo "y" | $SDKMANAGER "ndk;21.4.7075529" + ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/21.4.7075529 + export PATH=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH + fi + $COMPILER -v + + - name: Test help target + run: | + make help + + # Compile profile guided builds + + - name: Compile ${{ matrix.binaries }} build + run: | + if [ $COMP == ndk ]; then + ANDROID_ROOT=/usr/local/lib/android + ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk + SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager + echo "y" | $SDKMANAGER "ndk;21.4.7075529" + ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/21.4.7075529 + export PATH=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH + export LDFLAGS="-static -Wno-unused-command-line-argument" + fi + make clean + make -j2 profile-build ARCH=$BINARY COMP=$COMP WINE_PATH=$EMU + make strip ARCH=$BINARY COMP=$COMP + mv ./stockfish$EXT ../stockfish-android-$BINARY$EXT + + - name: Remove non src files + run: rm -f *.o .depend *.nnue + + - name: Create tar archive. + run: | + cd .. + mkdir stockfish + cp -r src stockfish/ + cp stockfish-android-$BINARY$EXT stockfish/ + cp "Top CPU Contributors.txt" stockfish/ + cp Copying.txt stockfish/ + cp AUTHORS stockfish/ + tar -cvf stockfish-android-$BINARY.tar stockfish + + - name: Upload binaries + uses: actions/upload-artifact@v3 + with: + name: stockfish-android-${{ matrix.binaries }} + path: | + stockfish-android-${{ matrix.binaries }}.tar diff --git a/.github/workflows/stockfish_binaries.yml b/.github/workflows/stockfish_binaries.yml index 0b205ded061..1fa123fac21 100644 --- a/.github/workflows/stockfish_binaries.yml +++ b/.github/workflows/stockfish_binaries.yml @@ -63,8 +63,8 @@ jobs: if: runner.os == 'Windows' uses: msys2/setup-msys2@v2 with: - msystem: ${{matrix.config.msys_sys}} - install: mingw-w64-${{matrix.config.msys_env}} make git expect + msystem: ${{ matrix.config.msys_sys }} + install: mingw-w64-${{ matrix.config.msys_env }} make - name: Download the used network from the fishtest framework run: | @@ -72,7 +72,6 @@ jobs: - name: Check compiler run: | - export PATH=$PATH:$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin $COMPILER -v - name: Test help target @@ -85,7 +84,7 @@ jobs: run: | make clean make -j2 profile-build ARCH=$BINARY COMP=$COMP - strip ./stockfish$EXT + make strip ARCH=$BINARY COMP=$COMP mv ./stockfish$EXT ../stockfish-$OS-$BINARY$EXT - name: Remove non src files diff --git a/.github/workflows/stockfish_compile_test.yml b/.github/workflows/stockfish_compile_test.yml index 63136737070..8467f52d964 100644 --- a/.github/workflows/stockfish_compile_test.yml +++ b/.github/workflows/stockfish_compile_test.yml @@ -72,7 +72,7 @@ jobs: uses: msys2/setup-msys2@v2 with: msystem: ${{matrix.config.msys_sys}} - install: mingw-w64-${{matrix.config.msys_env}} make git expect + install: mingw-w64-${{matrix.config.msys_env}} make - name: Download the used network from the fishtest framework run: | @@ -80,7 +80,6 @@ jobs: - name: Check compiler run: | - export PATH=$PATH:$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin $COMPILER -v - name: Test help target @@ -112,4 +111,4 @@ jobs: - name: Compile x86-64-vnni256 build run: | make clean - make -j2 ARCH=x86-64-vnni256 build \ No newline at end of file + make -j2 ARCH=x86-64-vnni256 build diff --git a/.github/workflows/stockfish_sanitizers.yml b/.github/workflows/stockfish_sanitizers.yml index 61eaf0c9f07..b74c2f970e0 100644 --- a/.github/workflows/stockfish_sanitizers.yml +++ b/.github/workflows/stockfish_sanitizers.yml @@ -52,7 +52,7 @@ jobs: - name: Download required linux packages run: | sudo apt update - sudo apt install expect valgrind g++-multilib qemu-user + sudo apt install expect valgrind g++-multilib - name: Download the used network from the fishtest framework run: | @@ -60,7 +60,6 @@ jobs: - name: Check compiler run: | - export PATH=$PATH:$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin $COMPILER -v - name: Test help target @@ -74,4 +73,4 @@ jobs: export CXXFLAGS="-O1 -fno-inline" make clean make -j2 ARCH=x86-64-modern ${{ matrix.sanitizers.make_option }} debug=yes optimize=no build > /dev/null - ../tests/instrumented.sh --${{ matrix.sanitizers.instrumented_option }} \ No newline at end of file + ../tests/instrumented.sh --${{ matrix.sanitizers.instrumented_option }} diff --git a/.github/workflows/stockfish_test.yml b/.github/workflows/stockfish_test.yml index e4e6205f08a..3cb89d39f33 100644 --- a/.github/workflows/stockfish_test.yml +++ b/.github/workflows/stockfish_test.yml @@ -31,7 +31,7 @@ jobs: shell: 'bash {0}' } - { - name: "Ubuntu 20.04 NDK armv8", + name: "Android NDK aarch64", os: ubuntu-20.04, compiler: aarch64-linux-android21-clang++, comp: ndk, @@ -39,7 +39,7 @@ jobs: shell: 'bash {0}' } - { - name: "Ubuntu 20.04 NDK armv7", + name: "Android NDK arm", os: ubuntu-20.04, compiler: armv7a-linux-androideabi21-clang++, comp: ndk, @@ -127,8 +127,8 @@ jobs: run: | if [ $COMP == ndk ]; then ANDROID_ROOT=/usr/local/lib/android - ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk - SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager + ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk + SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager echo "y" | $SDKMANAGER "ndk;21.4.7075529" ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/21.4.7075529 export PATH=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH @@ -228,8 +228,8 @@ jobs: if: ${{ matrix.config.run_armv8_tests }} run: | ANDROID_ROOT=/usr/local/lib/android - ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk - SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager + ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk + SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager echo "y" | $SDKMANAGER "ndk;21.4.7075529" ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/21.4.7075529 export PATH=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH @@ -244,8 +244,8 @@ jobs: if: ${{ matrix.config.run_armv7_tests }} run: | ANDROID_ROOT=/usr/local/lib/android - ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk - SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager + ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk + SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager echo "y" | $SDKMANAGER "ndk;21.4.7075529" ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/21.4.7075529 export PATH=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH @@ -258,8 +258,8 @@ jobs: if: ${{ matrix.config.run_armv7_tests }} run: | ANDROID_ROOT=/usr/local/lib/android - ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk - SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager + ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk + SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager echo "y" | $SDKMANAGER "ndk;21.4.7075529" ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/21.4.7075529 export PATH=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH From 9d3fd011f1bc9ef6f3a3091aae10634b31e0032c Mon Sep 17 00:00:00 2001 From: Alfredo Menezes Date: Fri, 9 Dec 2022 12:11:43 -0300 Subject: [PATCH 0916/1766] Extend all moves at low depth if ttMove is doubly extended If ttMove is doubly extended, we allow a depth growth of the remaining moves. The idea is to get a more realistic score comparison, because of the depth difference. We take some care to avoid this extension for high depths, in order to avoid the cost, since the search result is supposed to be more accurate in this case. This pull request includes some small cleanups. STC: LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 60256 W: 16189 L: 15848 D: 28219 Ptnml(0-2): 182, 6546, 16330, 6889, 181 https://tests.stockfishchess.org/tests/view/639109a1792a529ae8f27777 LTC: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 106232 W: 28487 L: 28053 D: 49692 Ptnml(0-2): 46, 10224, 32145, 10652, 49 https://tests.stockfishchess.org/tests/view/63914cba792a529ae8f282ee closes https://github.com/official-stockfish/Stockfish/pull/4271 Bench: 3622368 --- src/search.cpp | 6 ++++-- src/types.h | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 04f73e1c0a2..ec7cff5413e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1067,7 +1067,10 @@ namespace { if ( !PvNode && value < singularBeta - 25 && ss->doubleExtensions <= 9) + { extension = 2; + depth += depth < 12; + } } // Multi-cut pruning @@ -1296,7 +1299,7 @@ namespace { && depth < 6 && beta < VALUE_KNOWN_WIN && alpha > -VALUE_KNOWN_WIN) - depth -= 1; + depth -= 1; assert(depth > 0); } @@ -1521,7 +1524,6 @@ namespace { && futilityBase > -VALUE_KNOWN_WIN && type_of(move) != PROMOTION) { - if (moveCount > 2) continue; diff --git a/src/types.h b/src/types.h index c2087c6c07b..29c16ce7dbb 100644 --- a/src/types.h +++ b/src/types.h @@ -186,6 +186,9 @@ enum Value : int { VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY, VALUE_MATED_IN_MAX_PLY = -VALUE_MATE_IN_MAX_PLY, + // In the code, we make the assumption that these values + // are such that non_pawn_material() can be used to uniquely + // identify the material on the board. PawnValueMg = 126, PawnValueEg = 208, KnightValueMg = 781, KnightValueEg = 854, BishopValueMg = 825, BishopValueEg = 915, From 44ecadee10111f028e28f47df6dfc9accd908293 Mon Sep 17 00:00:00 2001 From: Douglas Matos Gomes Date: Thu, 8 Dec 2022 21:40:07 -0300 Subject: [PATCH 0917/1766] Simplify redundant condition. closes https://github.com/official-stockfish/Stockfish/pull/4270 No functional change --- src/evaluate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 93e665dfe85..71c4e8d15a9 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1091,7 +1091,7 @@ Value Eval::evaluate(const Position& pos, int* complexity) { v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); // When not using NNUE, return classical complexity to caller - if (complexity && (!useNNUE || useClassical)) + if (complexity && useClassical) *complexity = abs(v - psq); return v; From aedf0251e6170a631b56a24dff31e83b9656933d Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 9 Dec 2022 17:56:55 +0100 Subject: [PATCH 0918/1766] CI workflows, install git on windows ensures the SF dev version is reported correctly No functional change --- .github/workflows/stockfish_binaries.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stockfish_binaries.yml b/.github/workflows/stockfish_binaries.yml index 1fa123fac21..5ba4784e318 100644 --- a/.github/workflows/stockfish_binaries.yml +++ b/.github/workflows/stockfish_binaries.yml @@ -64,7 +64,7 @@ jobs: uses: msys2/setup-msys2@v2 with: msystem: ${{ matrix.config.msys_sys }} - install: mingw-w64-${{ matrix.config.msys_env }} make + install: mingw-w64-${{ matrix.config.msys_env }} make git - name: Download the used network from the fishtest framework run: | From 3a30b478d20c16a357d1e538f1ce9428e7285736 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 9 Dec 2022 18:24:54 +0100 Subject: [PATCH 0919/1766] CI workflows, install git on windows ensures the SF dev version is reported correctly closes https://github.com/official-stockfish/Stockfish/pull/4272 No functional change --- .github/workflows/stockfish_arm_binaries.yml | 12 ++++++------ .github/workflows/stockfish_binaries.yml | 18 ++++++++---------- .github/workflows/stockfish_compile_test.yml | 14 +++++++------- .github/workflows/stockfish_sanitizers.yml | 12 ++++++------ .github/workflows/stockfish_test.yml | 9 +++++---- 5 files changed, 32 insertions(+), 33 deletions(-) diff --git a/.github/workflows/stockfish_arm_binaries.yml b/.github/workflows/stockfish_arm_binaries.yml index cf3ae710091..ea738cef66f 100644 --- a/.github/workflows/stockfish_arm_binaries.yml +++ b/.github/workflows/stockfish_arm_binaries.yml @@ -58,8 +58,7 @@ jobs: sudo apt install qemu-user - name: Download the used network from the fishtest framework - run: | - make net + run: make net - name: Check compiler run: | @@ -74,8 +73,10 @@ jobs: $COMPILER -v - name: Test help target - run: | - make help + run: make help + + - name: Check git + run: git --version # Compile profile guided builds @@ -113,5 +114,4 @@ jobs: uses: actions/upload-artifact@v3 with: name: stockfish-android-${{ matrix.binaries }} - path: | - stockfish-android-${{ matrix.binaries }}.tar + path: stockfish-android-${{ matrix.binaries }}.tar diff --git a/.github/workflows/stockfish_binaries.yml b/.github/workflows/stockfish_binaries.yml index 5ba4784e318..57535296687 100644 --- a/.github/workflows/stockfish_binaries.yml +++ b/.github/workflows/stockfish_binaries.yml @@ -56,8 +56,7 @@ jobs: - name: Download required linux packages if: runner.os == 'Linux' - run: | - sudo apt update + run: sudo apt update - name: Setup msys and install required packages if: runner.os == 'Windows' @@ -67,16 +66,16 @@ jobs: install: mingw-w64-${{ matrix.config.msys_env }} make git - name: Download the used network from the fishtest framework - run: | - make net + run: make net - name: Check compiler - run: | - $COMPILER -v + run: $COMPILER -v - name: Test help target - run: | - make help + run: make help + + - name: Check git + run: git --version # Compile profile guided builds @@ -105,5 +104,4 @@ jobs: uses: actions/upload-artifact@v3 with: name: stockfish-${{ matrix.config.os }}-${{ matrix.binaries }} - path: | - stockfish-${{ matrix.config.os }}-${{ matrix.binaries }}.tar + path: stockfish-${{ matrix.config.os }}-${{ matrix.binaries }}.tar diff --git a/.github/workflows/stockfish_compile_test.yml b/.github/workflows/stockfish_compile_test.yml index 8467f52d964..eeb4229cbd3 100644 --- a/.github/workflows/stockfish_compile_test.yml +++ b/.github/workflows/stockfish_compile_test.yml @@ -72,19 +72,19 @@ jobs: uses: msys2/setup-msys2@v2 with: msystem: ${{matrix.config.msys_sys}} - install: mingw-w64-${{matrix.config.msys_env}} make + install: mingw-w64-${{matrix.config.msys_env}} make git - name: Download the used network from the fishtest framework - run: | - make net + run: make net - name: Check compiler - run: | - $COMPILER -v + run: $COMPILER -v - name: Test help target - run: | - make help + run: make help + + - name: Check git + run: git --version # x86-64 with newer extensions tests diff --git a/.github/workflows/stockfish_sanitizers.yml b/.github/workflows/stockfish_sanitizers.yml index b74c2f970e0..fa679330d8e 100644 --- a/.github/workflows/stockfish_sanitizers.yml +++ b/.github/workflows/stockfish_sanitizers.yml @@ -55,16 +55,16 @@ jobs: sudo apt install expect valgrind g++-multilib - name: Download the used network from the fishtest framework - run: | - make net + run: make net - name: Check compiler - run: | - $COMPILER -v + run: $COMPILER -v - name: Test help target - run: | - make help + run: make help + + - name: Check git + run: git --version # Sanitizers diff --git a/.github/workflows/stockfish_test.yml b/.github/workflows/stockfish_test.yml index 3cb89d39f33..953f68205bc 100644 --- a/.github/workflows/stockfish_test.yml +++ b/.github/workflows/stockfish_test.yml @@ -115,8 +115,7 @@ jobs: install: mingw-w64-${{ matrix.config.msys_env }} make git expect - name: Download the used network from the fishtest framework - run: | - make net + run: make net - name: Extract the bench number from the commit history run: | @@ -136,8 +135,10 @@ jobs: $COMPILER -v - name: Test help target - run: | - make help + run: make help + + - name: Check git + run: git --version # x86-32 tests From 8f817ef0824e4d940128f5701573f74819f50da5 Mon Sep 17 00:00:00 2001 From: disservin Date: Fri, 9 Dec 2022 21:48:03 +0100 Subject: [PATCH 0920/1766] Fix lower/upper bounds output Commit cb0c7a98485fbef4e5d6ed5f5b08201113ce0b4e doesnt reset the lower/upper bounds back to false. fixes #4273 closes https://github.com/official-stockfish/Stockfish/pull/4274 No functional change --- src/search.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index ec7cff5413e..b2a5b9401be 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1250,6 +1250,8 @@ namespace { { rm.score = rm.uciScore = value; rm.selDepth = thisThread->selDepth; + rm.scoreLowerbound = rm.scoreUpperbound = false; + if (value >= beta) { rm.scoreLowerbound = true; rm.uciScore = beta; From 955edf1d1d4f5643b450b1ee1e95dc3f094e1884 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 12 Dec 2022 08:12:10 +0100 Subject: [PATCH 0921/1766] Revert "doEvenDeeperSearch + tuning" This reverts commit 98965c139df1483a3d684ee8bc7a60dc4b95efa1. The increase of depth could lead to search explosions, most visible with TB. fixes https://github.com/official-stockfish/Stockfish/issues/4276 closes https://github.com/official-stockfish/Stockfish/pull/4256 Bench: 3872306 --- src/evaluate.cpp | 14 +++++++------- src/search.cpp | 7 +++---- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 71c4e8d15a9..6a0fae9e9ba 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1063,7 +1063,7 @@ Value Eval::evaluate(const Position& pos, int* complexity) { else { int nnueComplexity; - int scale = 1076 + 96 * pos.non_pawn_material() / 5120; + int scale = 1064 + 106 * pos.non_pawn_material() / 5120; Color stm = pos.side_to_move(); Value optimism = pos.this_thread()->optimism[stm]; @@ -1071,21 +1071,21 @@ Value Eval::evaluate(const Position& pos, int* complexity) { Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); // Blend nnue complexity with (semi)classical complexity - nnueComplexity = ( 412 * nnueComplexity - + 428 * abs(psq - nnue) + nnueComplexity = ( 416 * nnueComplexity + + 424 * abs(psq - nnue) + (optimism > 0 ? int(optimism) * int(psq - nnue) : 0) - ) / 1026; + ) / 1024; // Return hybrid NNUE complexity to caller if (complexity) *complexity = nnueComplexity; - optimism = optimism * (278 + nnueComplexity) / 256; - v = (nnue * scale + optimism * (scale - 755)) / 1024; + optimism = optimism * (269 + nnueComplexity) / 256; + v = (nnue * scale + optimism * (scale - 754)) / 1024; } // Damp down the evaluation linearly when shuffling - v = v * (197 - pos.rule50_count()) / 214; + v = v * (195 - pos.rule50_count()) / 211; // Guarantee evaluation does not hit the tablebase range v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); diff --git a/src/search.cpp b/src/search.cpp index b2a5b9401be..b58a344a597 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -81,7 +81,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return std::min((12 * d + 282) * d - 349 , 1480); + return std::min((12 * d + 282) * d - 349 , 1594); } // Add a small random component to draw evaluations to avoid 3-fold blindness @@ -1175,7 +1175,7 @@ namespace { - 4433; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - r -= ss->statScore / (13000 + 4152 * (depth > 7 && depth < 19)); + r -= ss->statScore / (13628 + 4000 * (depth > 7 && depth < 19)); // In general we want to cap the LMR depth search at newDepth, but when // reduction is negative, we allow this move a limited search extension @@ -1190,10 +1190,9 @@ namespace { // Adjust full depth search based on LMR results - if result // was good enough search deeper, if it was bad enough search shallower const bool doDeeperSearch = value > (alpha + 64 + 11 * (newDepth - d)); - const bool doEvenDeeperSearch = value > alpha + 582; const bool doShallowerSearch = value < bestValue + newDepth; - newDepth += doDeeperSearch - doShallowerSearch + doEvenDeeperSearch; + newDepth += doDeeperSearch - doShallowerSearch; if (newDepth > d) value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode); From 310928e985a6d87bdd73542e2109f93c31e2cc41 Mon Sep 17 00:00:00 2001 From: mstembera Date: Fri, 9 Dec 2022 18:50:06 -0800 Subject: [PATCH 0922/1766] Avoid truncated PV in the threaded case strongly prefer to pick as bestThread those threads with a longer PV, among those threads that all found the same bestmove. extended discussion in #4244 closes https://github.com/official-stockfish/Stockfish/pull/4278 No functional change --- src/thread.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/thread.cpp b/src/thread.cpp index b7471f60267..e8723eb7411 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -239,7 +239,8 @@ Thread* ThreadPool::get_best_thread() const { || ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY && ( votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]] || ( votes[th->rootMoves[0].pv[0]] == votes[bestThread->rootMoves[0].pv[0]] - && thread_value(th) > thread_value(bestThread))))) + && thread_value(th) * int(th->rootMoves[0].pv.size() > 2) + > thread_value(bestThread) * int(bestThread->rootMoves[0].pv.size() > 2))))) bestThread = th; return bestThread; From 5fe1fa0210e25cecc9de5520da56173ef94b8e59 Mon Sep 17 00:00:00 2001 From: ppigazzini Date: Sun, 11 Dec 2022 12:06:22 +0100 Subject: [PATCH 0923/1766] GitHub Actions: install NDK once and clean up yaml Use Ubuntu 22.04 as runner for NDK to avoid a qemu bug with `profile-build` closes https://github.com/official-stockfish/Stockfish/pull/4280 No functional change --- .github/workflows/stockfish_arm_binaries.yml | 56 +++--- .github/workflows/stockfish_binaries.yml | 42 ++--- .github/workflows/stockfish_compile_test.yml | 80 ++++---- .github/workflows/stockfish_sanitizers.yml | 44 ++--- .github/workflows/stockfish_test.yml | 184 ++++++++----------- 5 files changed, 177 insertions(+), 229 deletions(-) diff --git a/.github/workflows/stockfish_arm_binaries.yml b/.github/workflows/stockfish_arm_binaries.yml index ea738cef66f..a1b3cdab0ea 100644 --- a/.github/workflows/stockfish_arm_binaries.yml +++ b/.github/workflows/stockfish_arm_binaries.yml @@ -15,22 +15,18 @@ jobs: strategy: matrix: config: - - { - name: "Android NDK aarch64", - os: ubuntu-20.04, - compiler: aarch64-linux-android21-clang++, - emu: qemu-aarch64, - comp: ndk, - shell: 'bash {0}' - } - - { - name: "Android NDK arm", - os: ubuntu-20.04, - compiler: armv7a-linux-androideabi21-clang++, - emu: qemu-arm, - comp: ndk, - shell: 'bash {0}' - } + - name: Android NDK aarch64 + os: ubuntu-22.04 + compiler: aarch64-linux-android21-clang++ + emu: qemu-aarch64 + comp: ndk + shell: bash {0} + - name: Android NDK arm + os: ubuntu-22.04 + compiler: armv7a-linux-androideabi21-clang++ + emu: qemu-arm + comp: ndk + shell: bash {0} binaries: - armv8 - armv7 @@ -57,18 +53,27 @@ jobs: sudo apt update sudo apt install qemu-user + - name: Install NDK + if: runner.os == 'Linux' + run: | + if [ $COMP == ndk ]; then + NDKV="21.4.7075529" + ANDROID_ROOT=/usr/local/lib/android + ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk + SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager + echo "y" | $SDKMANAGER "ndk;$NDKV" + ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/$NDKV + ANDROID_NDK_BIN=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin + echo "ANDROID_NDK_BIN=$ANDROID_NDK_BIN" >> $GITHUB_ENV + fi + - name: Download the used network from the fishtest framework run: make net - name: Check compiler run: | if [ $COMP == ndk ]; then - ANDROID_ROOT=/usr/local/lib/android - ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk - SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager - echo "y" | $SDKMANAGER "ndk;21.4.7075529" - ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/21.4.7075529 - export PATH=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH + export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH fi $COMPILER -v @@ -83,12 +88,7 @@ jobs: - name: Compile ${{ matrix.binaries }} build run: | if [ $COMP == ndk ]; then - ANDROID_ROOT=/usr/local/lib/android - ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk - SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager - echo "y" | $SDKMANAGER "ndk;21.4.7075529" - ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/21.4.7075529 - export PATH=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH + export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH export LDFLAGS="-static -Wno-unused-command-line-argument" fi make clean diff --git a/.github/workflows/stockfish_binaries.yml b/.github/workflows/stockfish_binaries.yml index 57535296687..06b13a9e095 100644 --- a/.github/workflows/stockfish_binaries.yml +++ b/.github/workflows/stockfish_binaries.yml @@ -14,30 +14,24 @@ jobs: strategy: matrix: config: - - { - name: "Ubuntu 20.04 GCC", - os: ubuntu-20.04, - compiler: g++, - comp: gcc, - shell: 'bash {0}' - } - - { - name: "MacOS 12 Apple Clang", - os: macos-12, - compiler: clang++, - comp: clang, - shell: 'bash {0}' - } - - { - name: "Windows 2022 Mingw-w64 GCC x86_64", - os: windows-2022, - compiler: g++, - comp: mingw, - msys_sys: 'mingw64', - msys_env: 'x86_64-gcc', - shell: 'msys2 {0}', - ext: .exe - } + - name: Ubuntu 20.04 GCC + os: ubuntu-20.04 + compiler: g++ + comp: gcc + shell: bash {0} + - name: MacOS 12 Apple Clang + os: macos-12 + compiler: clang++ + comp: clang + shell: bash {0} + - name: Windows 2022 Mingw-w64 GCC x86_64 + os: windows-2022 + compiler: g++ + comp: mingw + msys_sys: mingw64 + msys_env: x86_64-gcc + shell: msys2 {0} + ext: .exe binaries: - x86-64 - x86-64-modern diff --git a/.github/workflows/stockfish_compile_test.yml b/.github/workflows/stockfish_compile_test.yml index eeb4229cbd3..c7280a85537 100644 --- a/.github/workflows/stockfish_compile_test.yml +++ b/.github/workflows/stockfish_compile_test.yml @@ -11,52 +11,40 @@ jobs: strategy: matrix: config: - - { - name: "Ubuntu 20.04 GCC", - os: ubuntu-20.04, - compiler: g++, - comp: gcc, - shell: 'bash {0}' - } - - { - name: "Ubuntu 20.04 Clang", - os: ubuntu-20.04, - compiler: clang++, - comp: clang, - shell: 'bash {0}' - } - - { - name: "MacOS 12 Apple Clang", - os: macos-12, - compiler: clang++, - comp: clang, - shell: 'bash {0}' - } - - { - name: "MacOS 12 GCC 11", - os: macos-12, - compiler: g++-11, - comp: gcc, - shell: 'bash {0}' - } - - { - name: "Windows 2022 Mingw-w64 GCC x86_64", - os: windows-2022, - compiler: g++, - comp: mingw, - msys_sys: 'mingw64', - msys_env: 'x86_64-gcc', - shell: 'msys2 {0}' - } - - { - name: "Windows 2022 Mingw-w64 Clang x86_64", - os: windows-2022, - compiler: clang++, - comp: clang, - msys_sys: 'clang64', - msys_env: 'clang-x86_64-clang', - shell: 'msys2 {0}' - } + - name: Ubuntu 20.04 GCC + os: ubuntu-20.04 + compiler: g++ + comp: gcc + shell: bash {0} + - name: Ubuntu 20.04 Clang + os: ubuntu-20.04 + compiler: clang++ + comp: clang + shell: bash {0} + - name: MacOS 12 Apple Clang + os: macos-12 + compiler: clang++ + comp: clang + shell: bash {0} + - name: MacOS 12 GCC 11 + os: macos-12 + compiler: g++-11 + comp: gcc + shell: bash {0} + - name: Windows 2022 Mingw-w64 GCC x86_64 + os: windows-2022 + compiler: g++ + comp: mingw + msys_sys: mingw64 + msys_env: x86_64-gcc + shell: msys2 {0} + - name: Windows 2022 Mingw-w64 Clang x86_64 + os: windows-2022 + compiler: clang++ + comp: clang + msys_sys: clang64 + msys_env: clang-x86_64-clang + shell: msys2 {0} defaults: run: diff --git a/.github/workflows/stockfish_sanitizers.yml b/.github/workflows/stockfish_sanitizers.yml index fa679330d8e..708c92275bf 100644 --- a/.github/workflows/stockfish_sanitizers.yml +++ b/.github/workflows/stockfish_sanitizers.yml @@ -12,34 +12,24 @@ jobs: strategy: matrix: config: - - { - name: "Ubuntu 20.04 GCC", - os: ubuntu-20.04, - compiler: g++, - comp: gcc, - shell: 'bash {0}' - } + - name: Ubuntu 20.04 GCC + os: ubuntu-20.04 + compiler: g++ + comp: gcc + shell: bash {0} sanitizers: - - { - name: Run with thread sanitizer, - make_option: sanitize=thread, - instrumented_option: sanitizer-thread - } - - { - name: Run with UB sanitizer, - make_option: sanitize=undefined, - instrumented_option: sanitizer-undefined - } - - { - name: Run under valgrind, - make_option: "", - instrumented_option: valgrind - } - - { - name: Run under valgrind-thread, - make_option: "", - instrumented_option: valgrind-thread - } + - name: Run with thread sanitizer + make_option: sanitize=thread + instrumented_option: sanitizer-thread + - name: Run with UB sanitizer + make_option: sanitize=undefined + instrumented_option: sanitizer-undefined + - name: Run under valgrind + make_option: "" + instrumented_option: valgrind + - name: Run under valgrind-thread + make_option: "" + instrumented_option: valgrind-thread defaults: run: working-directory: src diff --git a/.github/workflows/stockfish_test.yml b/.github/workflows/stockfish_test.yml index 953f68205bc..8c383fe7cf8 100644 --- a/.github/workflows/stockfish_test.yml +++ b/.github/workflows/stockfish_test.yml @@ -12,86 +12,68 @@ jobs: strategy: matrix: config: - - { - name: "Ubuntu 20.04 GCC", - os: ubuntu-20.04, - compiler: g++, - comp: gcc, - run_32bit_tests: true, - run_64bit_tests: true, - shell: 'bash {0}' - } - - { - name: "Ubuntu 20.04 Clang", - os: ubuntu-20.04, - compiler: clang++, - comp: clang, - run_32bit_tests: true, - run_64bit_tests: true, - shell: 'bash {0}' - } - - { - name: "Android NDK aarch64", - os: ubuntu-20.04, - compiler: aarch64-linux-android21-clang++, - comp: ndk, - run_armv8_tests: true, - shell: 'bash {0}' - } - - { - name: "Android NDK arm", - os: ubuntu-20.04, - compiler: armv7a-linux-androideabi21-clang++, - comp: ndk, - run_armv7_tests: true, - shell: 'bash {0}' - } - - { - name: "MacOS 12 Apple Clang", - os: macos-12, - compiler: clang++, - comp: clang, - run_64bit_tests: true, - shell: 'bash {0}' - } - - { - name: "MacOS 12 GCC 11", - os: macos-12, - compiler: g++-11, - comp: gcc, - run_64bit_tests: true, - shell: 'bash {0}' - } - - { - name: "Windows 2022 Mingw-w64 GCC x86_64", - os: windows-2022, - compiler: g++, - comp: mingw, - run_64bit_tests: true, - msys_sys: 'mingw64', - msys_env: 'x86_64-gcc', - shell: 'msys2 {0}' - } - - { - name: "Windows 2022 Mingw-w64 GCC i686", - os: windows-2022, - compiler: g++, - comp: mingw, - run_32bit_tests: true, - msys_sys: 'mingw32', - msys_env: 'i686-gcc', - shell: 'msys2 {0}' - } - - { - name: "Windows 2022 Mingw-w64 Clang x86_64", - os: windows-2022, - compiler: clang++, - comp: clang, - run_64bit_tests: true, - msys_sys: 'clang64', - msys_env: 'clang-x86_64-clang', - shell: 'msys2 {0}' - } + - name: Ubuntu 20.04 GCC + os: ubuntu-20.04 + compiler: g++ + comp: gcc + run_32bit_tests: true + run_64bit_tests: true + shell: bash {0} + - name: Ubuntu 20.04 Clang + os: ubuntu-20.04 + compiler: clang++ + comp: clang + run_32bit_tests: true + run_64bit_tests: true + shell: bash {0} + - name: Android NDK aarch64 + os: ubuntu-22.04 + compiler: aarch64-linux-android21-clang++ + comp: ndk + run_armv8_tests: true + shell: bash {0} + - name: Android NDK arm + os: ubuntu-22.04 + compiler: armv7a-linux-androideabi21-clang++ + comp: ndk + run_armv7_tests: true + shell: bash {0} + - name: MacOS 12 Apple Clang + os: macos-12 + compiler: clang++ + comp: clang + run_64bit_tests: true + shell: bash {0} + - name: MacOS 12 GCC 11 + os: macos-12 + compiler: g++-11 + comp: gcc + run_64bit_tests: true + shell: bash {0} + - name: Windows 2022 Mingw-w64 GCC x86_64 + os: windows-2022 + compiler: g++ + comp: mingw + run_64bit_tests: true + msys_sys: mingw64 + msys_env: x86_64-gcc + shell: msys2 {0} + - name: Windows 2022 Mingw-w64 GCC i686 + os: windows-2022 + compiler: g++ + comp: mingw + run_32bit_tests: true + msys_sys: mingw32 + msys_env: i686-gcc + shell: msys2 {0} + - name: Windows 2022 Mingw-w64 Clang x86_64 + os: windows-2022 + compiler: clang++ + comp: clang + run_64bit_tests: true + msys_sys: clang64 + msys_env: clang-x86_64-clang + shell: msys2 {0} defaults: run: working-directory: src @@ -107,6 +89,20 @@ jobs: sudo apt update sudo apt install expect valgrind g++-multilib qemu-user + - name: Install NDK + if: runner.os == 'Linux' + run: | + if [ $COMP == ndk ]; then + NDKV="21.4.7075529" + ANDROID_ROOT=/usr/local/lib/android + ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk + SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager + echo "y" | $SDKMANAGER "ndk;$NDKV" + ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/$NDKV + ANDROID_NDK_BIN=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin + echo "ANDROID_NDK_BIN=$ANDROID_NDK_BIN" >> $GITHUB_ENV + fi + - name: Setup msys and install required packages if: runner.os == 'Windows' uses: msys2/setup-msys2@v2 @@ -125,12 +121,7 @@ jobs: - name: Check compiler run: | if [ $COMP == ndk ]; then - ANDROID_ROOT=/usr/local/lib/android - ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk - SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager - echo "y" | $SDKMANAGER "ndk;21.4.7075529" - ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/21.4.7075529 - export PATH=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH + export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH fi $COMPILER -v @@ -228,12 +219,7 @@ jobs: - name: Test armv8 build if: ${{ matrix.config.run_armv8_tests }} run: | - ANDROID_ROOT=/usr/local/lib/android - ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk - SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager - echo "y" | $SDKMANAGER "ndk;21.4.7075529" - ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/21.4.7075529 - export PATH=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH + export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH export LDFLAGS="-static -Wno-unused-command-line-argument" make clean make -j2 ARCH=armv8 build @@ -244,12 +230,7 @@ jobs: - name: Test armv7 build if: ${{ matrix.config.run_armv7_tests }} run: | - ANDROID_ROOT=/usr/local/lib/android - ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk - SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager - echo "y" | $SDKMANAGER "ndk;21.4.7075529" - ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/21.4.7075529 - export PATH=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH + export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH export LDFLAGS="-static -Wno-unused-command-line-argument" make clean make -j2 ARCH=armv7 build @@ -258,12 +239,7 @@ jobs: - name: Test armv7-neon build if: ${{ matrix.config.run_armv7_tests }} run: | - ANDROID_ROOT=/usr/local/lib/android - ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk - SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager - echo "y" | $SDKMANAGER "ndk;21.4.7075529" - ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/21.4.7075529 - export PATH=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH + export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH export LDFLAGS="-static -Wno-unused-command-line-argument" make clean make -j2 ARCH=armv7-neon build From 7cf93f8b7101e5cf5df3f7921861f27b3beb9525 Mon Sep 17 00:00:00 2001 From: VoyagerOne Date: Sun, 11 Dec 2022 12:36:13 -0500 Subject: [PATCH 0924/1766] Simplify Capture Scoring The parameters are now in one place for easier tuning. New formula is very similar to current. STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 48176 W: 12819 L: 12616 D: 22741 Ptnml(0-2): 139, 5316, 13001, 5467, 165 LTC: LLR: 2.97 (-2.94,2.94) <-1.75,0.25> Total: 176752 W: 47364 L: 47304 D: 82084 Ptnml(0-2): 83, 17302, 53536, 17382, 73 https://tests.stockfishchess.org/tests/view/638ec7d068532fcbf79dfa15 closes https://github.com/official-stockfish/Stockfish/pull/4281 Bench: 3410998 --- src/movepick.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 188d6bd8172..564adc286ea 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -100,7 +100,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePiece /// MovePicker::score() assigns a numerical value to each move in a list, used /// for sorting. Captures are ordered by Most Valuable Victim (MVV), preferring -/// captures with a good history. Quiets moves are ordered using the histories. +/// captures with a good history. Quiets moves are ordered using the history tables. template void MovePicker::score() { @@ -123,8 +123,8 @@ void MovePicker::score() { for (auto& m : *this) if constexpr (Type == CAPTURES) - m.value = 6 * int(PieceValue[MG][pos.piece_on(to_sq(m))]) - + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]; + m.value = (7 * int(PieceValue[MG][pos.piece_on(to_sq(m))]) + + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]) / 16; else if constexpr (Type == QUIETS) m.value = 2 * (*mainHistory)[pos.side_to_move()][from_to(m)] @@ -197,7 +197,7 @@ Move MovePicker::next_move(bool skipQuiets) { case GOOD_CAPTURE: if (select([&](){ - return pos.see_ge(*cur, Value(-69 * cur->value / 1024)) ? + return pos.see_ge(*cur, Value(-cur->value)) ? // Move losing capture to endBadCaptures to be tried later true : (*endBadCaptures++ = *cur, false); })) return *(cur - 1); From 3659a9fda0096cac091458df2c259e5636bc9106 Mon Sep 17 00:00:00 2001 From: NguyenPham Date: Thu, 15 Dec 2022 17:29:23 +1100 Subject: [PATCH 0925/1766] Fixed the help of Makefile make profile-build more prominent, adjust comments closes https://github.com/official-stockfish/Stockfish/pull/4284 No functional change --- src/Makefile | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Makefile b/src/Makefile index 0c98391bb84..da81ceb4a0e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -747,9 +747,9 @@ help: @echo "Supported targets:" @echo "" @echo "help > Display architecture details" - @echo "build > Standard build" + @echo "profile-build > standard build with profile-guided optimization" + @echo "build > skip profile-guided optimization" @echo "net > Download the default nnue net" - @echo "profile-build > Faster build (with profile-guided optimization)" @echo "strip > Strip executable" @echo "install > Install executable" @echo "clean > Clean up" @@ -789,14 +789,15 @@ help: @echo "icc > Intel compiler" @echo "ndk > Google NDK to cross-compile for Android" @echo "" - @echo "Simple examples. If you don't know what to do, you likely want to run: " + @echo "Simple examples. If you don't know what to do, you likely want to run one of: " @echo "" - @echo "make -j build ARCH=x86-64 (A portable, slow compile for 64-bit systems)" - @echo "make -j build ARCH=x86-32 (A portable, slow compile for 32-bit systems)" + @echo "make -j profile-build ARCH=x86-64-avx2 # typically a fast compile for common systems " + @echo "make -j profile-build ARCH=x86-64-modern # A more portable compile for 64-bit systems " + @echo "make -j profile-build ARCH=x86-64 # A portable compile for 64-bit systems " @echo "" - @echo "Advanced examples, for experienced users looking for performance: " + @echo "Advanced examples, for experienced users: " @echo "" - @echo "make help ARCH=x86-64-bmi2" + @echo "make -j profile-build ARCH=x86-64-bmi2" @echo "make -j profile-build ARCH=x86-64-bmi2 COMP=gcc COMPCXX=g++-9.0" @echo "make -j build ARCH=x86-64-ssse3 COMP=clang" @echo "" From 726e90ccfaea85c5831ad4834a6a08f5bec3f2f2 Mon Sep 17 00:00:00 2001 From: PikaCat <73384062+PikaCat-OuO@users.noreply.github.com> Date: Fri, 16 Dec 2022 10:28:31 +0800 Subject: [PATCH 0926/1766] Badge link fix Fix the badge link issue mentioned in https://github.com/badges/shields/issues/8671 closes https://github.com/official-stockfish/Stockfish/pull/4285 No functional change --- AUTHORS | 1 + README.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 432d9b90d6d..0c1c552930f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -163,6 +163,7 @@ Pasquale Pigazzini (ppigazzini) Patrick Jansen (mibere) Peter Schneider (pschneider1968) Peter Zsifkovits (CoffeeOne) +PikaCat Praveen Kumar Tummala (praveentml) Rahul Dsilva (silversolver1) Ralph Stößer (Ralph Stoesser) diff --git a/README.md b/README.md index ac15fbfafbe..4cd5968e4be 100644 --- a/README.md +++ b/README.md @@ -369,7 +369,7 @@ For full details, read the copy of the GPL v3 found in the file named [website-link]: https://stockfishchess.org [worker-link]: https://github.com/glinscott/fishtest/wiki/Running-the-worker:-overview -[build-badge]: https://img.shields.io/github/workflow/status/official-stockfish/Stockfish/Stockfish?style=for-the-badge&label=stockfish&logo=github +[build-badge]: https://img.shields.io/github/actions/workflow/status/official-stockfish/Stockfish/stockfish.yml?branch=master&style=for-the-badge&label=stockfish&logo=github [commits-badge]: https://img.shields.io/github/commits-since/official-stockfish/Stockfish/latest?style=for-the-badge [discord-badge]: https://img.shields.io/discord/435943710472011776?style=for-the-badge&label=discord&logo=Discord [fishtest-badge]: https://img.shields.io/website?style=for-the-badge&down_color=red&down_message=Offline&label=Fishtest&up_color=success&up_message=Online&url=https%3A%2F%2Ftests.stockfishchess.org%2Ftests%2Ffinished From 39af98c807d236b6511b6e399caf40102398900c Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 17 Dec 2022 12:48:03 +0300 Subject: [PATCH 0927/1766] Reintroduce doEvenDeeperSearch This patch is basically the same as a reverted patch but now has some guarding against search being stuck - the same way as we do with double extensions. This should help with search explosions - albeit slowly but they eventually should be resolved. passed STC: https://tests.stockfishchess.org/tests/view/639733d0b4e52c95053f3485 LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 514048 W: 136423 L: 135435 D: 242190 Ptnml(0-2): 1425, 56945, 139420, 57685, 1549 passed LTC: https://tests.stockfishchess.org/tests/view/639ab79b93ed41c57eded5c3 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 113800 W: 30642 L: 30190 D: 52968 Ptnml(0-2): 53, 11092, 34178, 11504, 73 closes https://github.com/official-stockfish/Stockfish/pull/4287 bench 3611278 --- src/evaluate.cpp | 12 ++++++------ src/search.cpp | 11 +++++++---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 6a0fae9e9ba..7619398e01f 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1063,7 +1063,7 @@ Value Eval::evaluate(const Position& pos, int* complexity) { else { int nnueComplexity; - int scale = 1064 + 106 * pos.non_pawn_material() / 5120; + int scale = 1076 + 96 * pos.non_pawn_material() / 5120; Color stm = pos.side_to_move(); Value optimism = pos.this_thread()->optimism[stm]; @@ -1071,8 +1071,8 @@ Value Eval::evaluate(const Position& pos, int* complexity) { Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); // Blend nnue complexity with (semi)classical complexity - nnueComplexity = ( 416 * nnueComplexity - + 424 * abs(psq - nnue) + nnueComplexity = ( 412 * nnueComplexity + + 428 * abs(psq - nnue) + (optimism > 0 ? int(optimism) * int(psq - nnue) : 0) ) / 1024; @@ -1080,12 +1080,12 @@ Value Eval::evaluate(const Position& pos, int* complexity) { if (complexity) *complexity = nnueComplexity; - optimism = optimism * (269 + nnueComplexity) / 256; - v = (nnue * scale + optimism * (scale - 754)) / 1024; + optimism = optimism * (278 + nnueComplexity) / 256; + v = (nnue * scale + optimism * (scale - 755)) / 1024; } // Damp down the evaluation linearly when shuffling - v = v * (195 - pos.rule50_count()) / 211; + v = v * (197 - pos.rule50_count()) / 214; // Guarantee evaluation does not hit the tablebase range v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); diff --git a/src/search.cpp b/src/search.cpp index b58a344a597..4507460a5b6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -81,7 +81,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return std::min((12 * d + 282) * d - 349 , 1594); + return std::min((12 * d + 282) * d - 349 , 1480); } // Add a small random component to draw evaluations to avoid 3-fold blindness @@ -1066,7 +1066,7 @@ namespace { // Avoid search explosion by limiting the number of double extensions if ( !PvNode && value < singularBeta - 25 - && ss->doubleExtensions <= 9) + && ss->doubleExtensions <= 10) { extension = 2; depth += depth < 12; @@ -1175,7 +1175,7 @@ namespace { - 4433; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - r -= ss->statScore / (13628 + 4000 * (depth > 7 && depth < 19)); + r -= ss->statScore / (13000 + 4152 * (depth > 7 && depth < 19)); // In general we want to cap the LMR depth search at newDepth, but when // reduction is negative, we allow this move a limited search extension @@ -1190,9 +1190,12 @@ namespace { // Adjust full depth search based on LMR results - if result // was good enough search deeper, if it was bad enough search shallower const bool doDeeperSearch = value > (alpha + 64 + 11 * (newDepth - d)); + const bool doEvenDeeperSearch = value > alpha + 582 && ss->doubleExtensions <= 5; const bool doShallowerSearch = value < bestValue + newDepth; - newDepth += doDeeperSearch - doShallowerSearch; + ss->doubleExtensions = ss->doubleExtensions + doEvenDeeperSearch; + + newDepth += doDeeperSearch - doShallowerSearch + doEvenDeeperSearch; if (newDepth > d) value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode); From 3a32d3e00cca23648f10a40c5b322f2fd56dc0ae Mon Sep 17 00:00:00 2001 From: mstembera Date: Mon, 12 Dec 2022 23:22:02 -0800 Subject: [PATCH 0928/1766] Don't reset increaseDepth back to true after it has been set to false Resetting increaseDepth back to true each time on the very next iteration was not intended so this is a bug fix and a simplification. See more discussion here #2482 (comment) Thanks to xoto10 STC: https://tests.stockfishchess.org/tests/view/6398c74693ed41c57ede7bfd LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 51128 W: 13543 L: 13220 D: 24365 Ptnml(0-2): 165, 5363, 14174, 5708, 154 LTC: https://tests.stockfishchess.org/tests/view/6399bcd393ed41c57edea750 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 290864 W: 77282 L: 77334 D: 136248 Ptnml(0-2): 107, 28127, 89029, 28049, 120 closes https://github.com/official-stockfish/Stockfish/pull/4288 bench: 3611278 --- src/search.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 4507460a5b6..d39a8ad275c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -487,12 +487,11 @@ void Thread::search() { else Threads.stop = true; } - else if ( Threads.increaseDepth - && !mainThread->ponder + else if ( !mainThread->ponder && Time.elapsed() > totalTime * 0.53) - Threads.increaseDepth = false; + Threads.increaseDepth = false; else - Threads.increaseDepth = true; + Threads.increaseDepth = true; } mainThread->iterValue[iterIdx] = bestValue; From 61ea1534ff7026009a3435575c7beee91534db83 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 19 Dec 2022 17:54:36 +0100 Subject: [PATCH 0929/1766] No error if net available but wget/curl missing do not error out on missing wget/curl if these tools are not needed later on, i.e. if the net is available already. closes https://github.com/official-stockfish/Stockfish/pull/4291 closes https://github.com/official-stockfish/Stockfish/pull/4253 No functional change --- src/Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index da81ceb4a0e..bcf0abdf5ee 100644 --- a/src/Makefile +++ b/src/Makefile @@ -853,7 +853,7 @@ net: $(eval nnuedownloadurl2 := https://github.com/official-stockfish/networks/raw/master/$(nnuenet)) $(eval curl_or_wget := $(shell if hash curl 2>/dev/null; then echo "curl -skL"; elif hash wget 2>/dev/null; then echo "wget -qO-"; fi)) @if [ "x$(curl_or_wget)" = "x" ]; then \ - echo "Automatic download failed: neither curl nor wget is installed. Install one of these tools or download the net manually"; exit 1; \ + echo "Neither curl nor wget is installed. Install one of these tools unless the net has been downloaded manually"; \ fi $(eval shasum_command := $(shell if hash shasum 2>/dev/null; then echo "shasum -a 256 "; elif hash sha256sum 2>/dev/null; then echo "sha256sum "; fi)) @if [ "x$(shasum_command)" = "x" ]; then \ @@ -864,7 +864,9 @@ net: echo "$(nnuenet) available."; \ else \ if [ "x$(curl_or_wget)" != "x" ]; then \ - echo "Downloading $${nnuedownloadurl}"; $(curl_or_wget) $${nnuedownloadurl} > $(nnuenet);\ + echo "Downloading $${nnuedownloadurl}"; $(curl_or_wget) $${nnuedownloadurl} > $(nnuenet);\ + else \ + echo "No net found and download not possible"; exit 1;\ fi; \ fi; \ if [ "x$(shasum_command)" != "x" ]; then \ From c2d507005c51145211a7963af7c41026bd759d08 Mon Sep 17 00:00:00 2001 From: Alfredo Menezes Date: Mon, 19 Dec 2022 16:07:09 -0300 Subject: [PATCH 0930/1766] Sometimes do a reduced search if LMR is skipped If the node doesn't go through LMR and r is too big, reduce search depth by one ply. STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 664888 W: 176375 L: 175169 D: 313344 Ptnml(0-2): 1965, 73754, 179851, 74858, 2016 https://tests.stockfishchess.org/tests/view/6399414c93ed41c57ede8fb8 LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 150784 W: 40553 L: 40031 D: 70200 Ptnml(0-2): 76, 14668, 45387, 15180, 81 https://tests.stockfishchess.org/tests/view/639dee6e11c576d919dc2b38 closes https://github.com/official-stockfish/Stockfish/pull/4290 Bench: 3727508 --- src/search.cpp | 94 +++++++++++++++++++++++++------------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index d39a8ad275c..7f2f5284728 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1120,62 +1120,62 @@ namespace { // Step 16. Make the move pos.do_move(move, st, givesCheck); - // Step 17. Late moves reduction / extension (LMR, ~98 Elo) - // We use various heuristics for the sons of a node after the first son has - // been searched. In general we would like to reduce them, but there are many - // cases where we extend a son if it has good chances to be "interesting". - if ( depth >= 2 - && moveCount > 1 + (PvNode && ss->ply <= 1) - && ( !ss->ttPv - || !capture - || (cutNode && (ss-1)->moveCount > 1))) - { - Depth r = reduction(improving, depth, moveCount, delta, thisThread->rootDelta); + Depth r = reduction(improving, depth, moveCount, delta, thisThread->rootDelta); - // Decrease reduction if position is or has been on the PV - // and node is not likely to fail low. (~3 Elo) - if ( ss->ttPv - && !likelyFailLow) - r -= 2; + // Decrease reduction if position is or has been on the PV + // and node is not likely to fail low. (~3 Elo) + if ( ss->ttPv + && !likelyFailLow) + r -= 2; - // Decrease reduction if opponent's move count is high (~1 Elo) - if ((ss-1)->moveCount > 7) - r--; + // Decrease reduction if opponent's move count is high (~1 Elo) + if ((ss-1)->moveCount > 7) + r--; - // Increase reduction for cut nodes (~3 Elo) - if (cutNode) - r += 2; + // Increase reduction for cut nodes (~3 Elo) + if (cutNode) + r += 2; - // Increase reduction if ttMove is a capture (~3 Elo) - if (ttCapture) - r++; + // Increase reduction if ttMove is a capture (~3 Elo) + if (ttCapture) + r++; - // Decrease reduction for PvNodes based on depth - if (PvNode) - r -= 1 + 11 / (3 + depth); + // Decrease reduction for PvNodes based on depth + if (PvNode) + r -= 1 + 11 / (3 + depth); - // Decrease reduction if ttMove has been singularly extended (~1 Elo) - if (singularQuietLMR) - r--; + // Decrease reduction if ttMove has been singularly extended (~1 Elo) + if (singularQuietLMR) + r--; - // Decrease reduction if we move a threatened piece (~1 Elo) - if ( depth > 9 - && (mp.threatenedPieces & from_sq(move))) - r--; + // Decrease reduction if we move a threatened piece (~1 Elo) + if ( depth > 9 + && (mp.threatenedPieces & from_sq(move))) + r--; - // Increase reduction if next ply has a lot of fail high - if ((ss+1)->cutoffCnt > 3) - r++; + // Increase reduction if next ply has a lot of fail high + if ((ss+1)->cutoffCnt > 3) + r++; - ss->statScore = 2 * thisThread->mainHistory[us][from_to(move)] - + (*contHist[0])[movedPiece][to_sq(move)] - + (*contHist[1])[movedPiece][to_sq(move)] - + (*contHist[3])[movedPiece][to_sq(move)] - - 4433; + ss->statScore = 2 * thisThread->mainHistory[us][from_to(move)] + + (*contHist[0])[movedPiece][to_sq(move)] + + (*contHist[1])[movedPiece][to_sq(move)] + + (*contHist[3])[movedPiece][to_sq(move)] + - 4433; - // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - r -= ss->statScore / (13000 + 4152 * (depth > 7 && depth < 19)); + // Decrease/increase reduction for moves with a good/bad history (~30 Elo) + r -= ss->statScore / (13000 + 4152 * (depth > 7 && depth < 19)); + // Step 17. Late moves reduction / extension (LMR, ~98 Elo) + // We use various heuristics for the sons of a node after the first son has + // been searched. In general we would like to reduce them, but there are many + // cases where we extend a son if it has good chances to be "interesting". + if ( depth >= 2 + && moveCount > 1 + (PvNode && ss->ply <= 1) + && ( !ss->ttPv + || !capture + || (cutNode && (ss-1)->moveCount > 1))) + { // In general we want to cap the LMR depth search at newDepth, but when // reduction is negative, we allow this move a limited search extension // beyond the first move depth. This may lead to hidden double extensions. @@ -1209,10 +1209,10 @@ namespace { } } - // Step 18. Full depth search when LMR is skipped + // Step 18. Full depth search when LMR is skipped. If expected reduction is high, reduce its depth by 1. else if (!PvNode || moveCount > 1) { - value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode); + value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth - (r > 4), !cutNode); } // For PV nodes only, do a full PV search on the first move or after a fail From 20b02264620397eacf4e43736ec7f7c48a9a7068 Mon Sep 17 00:00:00 2001 From: MinetaS Date: Tue, 20 Dec 2022 16:01:05 +0900 Subject: [PATCH 0931/1766] Fix a dependency bug Instead of allowing .depend for specific build-related targets, filter non-build-related targets (i.e. help, clean) so that other targets can normally execute .depend target. closes https://github.com/official-stockfish/Stockfish/pull/4293 No functional change --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index bcf0abdf5ee..500c100675d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1009,6 +1009,6 @@ icc-profile-use: .depend: $(SRCS) -@$(CXX) $(DEPENDFLAGS) -MM $(SRCS) > $@ 2> /dev/null -ifneq (, $(filter $(MAKECMDGOALS), build profile-build)) +ifeq (, $(filter $(MAKECMDGOALS), help strip install clean net objclean profileclean config-sanity)) -include .depend endif From c620886181b5c0fb4a0c067b4173143a81819199 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Mon, 19 Dec 2022 16:10:22 -0500 Subject: [PATCH 0932/1766] Update default net to nn-335a9b2d8a80.nnue Created by retraining the master net with a combination of: the previous best dataset (Leela-dfrc_n5000.binpack), with about half the dataset filtered using depth6 multipv2 search to throw away positions where either of the 2 best moves are captures Leela T80 Oct and Nov training data rescored with best moves, adding ~9.5 billion positions Trained effectively the same way as the previous master net: python3 easy_train.py \ --experiment-name=leela-dfrc-filtered-T80-oct-nov \ --training-dataset=/data/leela-dfrc-filtered-T80-oct-nov.binpack \ --start-from-engine-test-net True \ --gpus="0," \ --start-lambda=1.0 \ --end-lambda=0.75 \ --gamma=0.995 \ --lr=4.375e-4 \ --tui=False \ --seed=$RANDOM \ --max_epoch=800 \ --auto-exit-timeout-on-training-finished=900 \ --network-testing-threads 20 \ --num-workers 6 Local testing at a fixed 25k nodes: experiments/experiment_leela-dfrc-filtered-T80-oct-nov/training/run_0/nn-epoch779.nnue localElo: run_0/nn-epoch779.nnue : 4.7 +/- 3.1 The new Leela T80 part of the dataset was prepared by downloading test80 training data from all of Oct 2022 and Nov 2022, rescoring with syzygy 6-piece tablebases and ~600 GB of 7-piece tablebases, saving best moves to exported .plain files, removing all positions with castling flags, then converting to binpacks and using interleave_binpacks.py to merge them together. Scripts used in this data conversion process are available at: https://github.com/linrock/lc0-data-converter Filtering binpack data using depth6 multipv2 search was done by modifying transform.cpp in the tools branch: https://github.com/linrock/Stockfish/tree/tools-filter-multipv2-no-rescore Links for downloading the training data (total size: 338 GB) are available at: https://robotmoon.com/nnue-training-data/ Passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 30544 W: 8244 L: 7947 D: 14353 Ptnml(0-2): 93, 3243, 8302, 3542, 92 https://tests.stockfishchess.org/tests/view/63a0d377264a0cf18f86f82b Passed LTC: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 32464 W: 8866 L: 8573 D: 15025 Ptnml(0-2): 19, 3054, 9794, 3345, 20 https://tests.stockfishchess.org/tests/view/63a10bc9fb452d3c44b1e016 closes https://github.com/official-stockfish/Stockfish/pull/4295 Bench 3554904 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index f5ac3263c33..d1398dd5ba8 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-ad9b42354671.nnue" + #define EvalFileDefaultName "nn-335a9b2d8a80.nnue" namespace NNUE { From b2bd8699eccc149cdda1f7bf6e1eed42088ec827 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Tue, 20 Dec 2022 22:54:12 +0300 Subject: [PATCH 0933/1766] Update Elo estimates for terms in search based on 25k games per term, using the UHO_XXL_+0.90_+1.19.epd book, at STC. More detailed information in the PR. closes https://github.com/official-stockfish/Stockfish/pull/4294 No functional change --- src/search.cpp | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 7f2f5284728..77d23ac2f41 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -633,16 +633,16 @@ namespace { && ttValue != VALUE_NONE // Possible in case of TT access race && (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER))) { - // If ttMove is quiet, update move sorting heuristics on TT hit (~1 Elo) + // If ttMove is quiet, update move sorting heuristics on TT hit (~2 Elo) if (ttMove) { if (ttValue >= beta) { - // Bonus for a quiet ttMove that fails high (~3 Elo) + // Bonus for a quiet ttMove that fails high (~2 Elo) if (!ttCapture) update_quiet_stats(pos, ss, ttMove, stat_bonus(depth)); - // Extra penalty for early quiet moves of the previous ply (~0 Elo) + // Extra penalty for early quiet moves of the previous ply (~0 Elo on STC, ~2 Elo on LTC) if ((ss-1)->moveCount <= 2 && !priorCapture) update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + 1)); } @@ -734,7 +734,7 @@ namespace { else // Fall back to (semi)classical complexity for TT hits, the NNUE complexity is lost complexity = abs(ss->staticEval - pos.psq_eg_stm()); - // ttValue can be used as a better position evaluation (~4 Elo) + // ttValue can be used as a better position evaluation (~7 Elo) if ( ttValue != VALUE_NONE && (tte->bound() & (ttValue > eval ? BOUND_LOWER : BOUND_UPPER))) eval = ttValue; @@ -750,7 +750,7 @@ namespace { thisThread->complexityAverage.update(complexity); - // Use static evaluation difference to improve quiet move ordering (~3 Elo) + // Use static evaluation difference to improve quiet move ordering (~4 Elo) if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture) { int bonus = std::clamp(-19 * int((ss-1)->staticEval + ss->staticEval), -1914, 1914); @@ -766,7 +766,7 @@ namespace { : 168; improving = improvement > 0; - // Step 7. Razoring. + // Step 7. Razoring (~1 Elo). // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. if (eval < alpha - 369 - 254 * depth * depth) @@ -776,7 +776,7 @@ namespace { return value; } - // Step 8. Futility pruning: child node (~25 Elo). + // Step 8. Futility pruning: child node (~40 Elo). // The depth condition is important for mate finding. if ( !ss->ttPv && depth < 8 @@ -785,7 +785,7 @@ namespace { && eval < 28031) // larger than VALUE_KNOWN_WIN, but smaller than TB wins return eval; - // Step 9. Null move search with verification search (~22 Elo) + // Step 9. Null move search with verification search (~35 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL && (ss-1)->statScore < 17139 @@ -837,7 +837,7 @@ namespace { probCutBeta = beta + 191 - 54 * improving; - // Step 10. ProbCut (~4 Elo) + // Step 10. ProbCut (~10 Elo) // 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 @@ -888,7 +888,7 @@ namespace { } // Step 11. If the position is not in TT, decrease depth by 3. - // Use qsearch if depth is equal or below zero (~4 Elo) + // Use qsearch if depth is equal or below zero (~9 Elo) if ( PvNode && !ttMove) depth -= 3; @@ -903,7 +903,7 @@ namespace { moves_loop: // When in check, search starts here - // Step 12. A small Probcut idea, when we are in check (~0 Elo) + // Step 12. A small Probcut idea, when we are in check (~4 Elo) probCutBeta = beta + 417; if ( ss->inCheck && !PvNode @@ -980,12 +980,12 @@ namespace { Value delta = beta - alpha; - // Step 14. Pruning at shallow depth (~98 Elo). Depth conditions are important for mate finding. + // Step 14. Pruning at shallow depth (~120 Elo). Depth conditions are important for mate finding. if ( !rootNode && pos.non_pawn_material(us) && bestValue > VALUE_TB_LOSS_IN_MAX_PLY) { - // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold (~7 Elo) + // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold (~8 Elo) moveCountPruning = moveCount >= futility_move_count(improving, depth); // Reduced depth of the next LMR search @@ -994,7 +994,7 @@ namespace { if ( capture || givesCheck) { - // Futility pruning for captures (~0 Elo) + // Futility pruning for captures (~2 Elo) if ( !givesCheck && !PvNode && lmrDepth < 7 @@ -1003,7 +1003,7 @@ namespace { + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 6 < alpha) continue; - // SEE based pruning (~9 Elo) + // SEE based pruning (~11 Elo) if (!pos.see_ge(move, Value(-222) * depth)) continue; } @@ -1020,23 +1020,23 @@ namespace { history += 2 * thisThread->mainHistory[us][from_to(move)]; - // Futility pruning: parent node (~9 Elo) + // Futility pruning: parent node (~13 Elo) if ( !ss->inCheck && lmrDepth < 13 && ss->staticEval + 106 + 145 * lmrDepth + history / 52 <= alpha) continue; - // Prune moves with negative SEE (~3 Elo) + // Prune moves with negative SEE (~4 Elo) if (!pos.see_ge(move, Value(-24 * lmrDepth * lmrDepth - 15 * lmrDepth))) continue; } } - // Step 15. Extensions (~66 Elo) + // Step 15. Extensions (~100 Elo) // We take care to not overdo to avoid search getting stuck. if (ss->ply < thisThread->rootDepth * 2) { - // Singular extension search (~58 Elo). If all moves but one fail low on a + // Singular extension search (~94 Elo). If all moves but one fail low on a // search of (alpha-s, beta-s), and just one fails high on (alpha, beta), // 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 @@ -1095,7 +1095,7 @@ namespace { && abs(ss->staticEval) > 82) extension = 1; - // Quiet ttMove extensions (~0 Elo) + // Quiet ttMove extensions (~1 Elo) else if ( PvNode && move == ttMove && move == ss->killers[0] @@ -1166,7 +1166,7 @@ namespace { // Decrease/increase reduction for moves with a good/bad history (~30 Elo) r -= ss->statScore / (13000 + 4152 * (depth > 7 && depth < 19)); - // Step 17. Late moves reduction / extension (LMR, ~98 Elo) + // Step 17. Late moves reduction / extension (LMR, ~117 Elo) // We use various heuristics for the sons of a node after the first son has // been searched. In general we would like to reduce them, but there are many // cases where we extend a son if it has good chances to be "interesting". @@ -1462,7 +1462,7 @@ namespace { if ((ss->staticEval = bestValue = tte->eval()) == VALUE_NONE) ss->staticEval = bestValue = evaluate(pos); - // ttValue can be used as a better position evaluation (~7 Elo) + // ttValue can be used as a better position evaluation (~13 Elo) if ( ttValue != VALUE_NONE && (tte->bound() & (ttValue > bestValue ? BOUND_LOWER : BOUND_UPPER))) bestValue = ttValue; @@ -1520,7 +1520,7 @@ namespace { moveCount++; - // Futility pruning and moveCount pruning (~5 Elo) + // Futility pruning and moveCount pruning (~10 Elo) if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY && !givesCheck && to_sq(move) != prevSq @@ -1559,7 +1559,7 @@ namespace { [pos.moved_piece(move)] [to_sq(move)]; - // Continuation history based pruning (~2 Elo) + // Continuation history based pruning (~3 Elo) if ( !capture && bestValue > VALUE_TB_LOSS_IN_MAX_PLY && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < 0 From 64656f8583dde88d558d5367b4cf8fc136c3214a Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Sat, 24 Dec 2022 13:05:24 +0800 Subject: [PATCH 0934/1766] Add double bonus for prior countermove fail low Add a double extra bonus for particularly bad fail low cases. Original idea by Yoshie2000. STC: https://tests.stockfishchess.org/tests/view/63a2f0d86b5bf07ac7fad543 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 146488 W: 38992 L: 38532 D: 68964 Ptnml(0-2): 385, 16036, 39965, 16450, 408 LTC: https://tests.stockfishchess.org/tests/view/63a3eaeb6b5bf07ac7fafdec LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 103992 W: 27853 L: 27423 D: 48716 Ptnml(0-2): 41, 10029, 31435, 10441, 50 closes https://github.com/official-stockfish/Stockfish/pull/4302 Bench: 3801857 --- AUTHORS | 1 + src/search.cpp | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index 0c1c552930f..70b500ea79e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -145,6 +145,7 @@ Mira Miroslav Fontán (Hexik) Moez Jellouli (MJZ1977) Mohammed Li (tthsqe12) +Muzhen J (XInTheDark) Nathan Rugg (nmrugg) Nick Pelling (nickpelling) Nicklas Persson (NicklasPersson) diff --git a/src/search.cpp b/src/search.cpp index 77d23ac2f41..0c8377d01ed 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1353,16 +1353,17 @@ namespace { quietsSearched, quietCount, capturesSearched, captureCount, depth); // Bonus for prior countermove that caused the fail low - else if ( (depth >= 5 || PvNode) + else if ( (depth >= 5 || PvNode || bestValue < alpha - 62 * depth) && !priorCapture) { //Assign extra bonus if current node is PvNode or cutNode //or fail low was really bad bool extraBonus = PvNode - || cutNode - || bestValue < alpha - 62 * depth; + || cutNode; - update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * (1 + extraBonus)); + bool doubleExtraBonus = extraBonus && bestValue < alpha - 85 * depth; + + update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * (1 + extraBonus + doubleExtraBonus)); } if (PvNode) From f09b391ceb5ea282538b721b069db67e3b6e098f Mon Sep 17 00:00:00 2001 From: Sebastian Buchwald Date: Tue, 27 Dec 2022 13:33:26 +0100 Subject: [PATCH 0935/1766] Fix comparison with uninitialized variable In both modified methods, the variable 'result' is checked to detect whether the probe operation failed. However, the variable is not initialized on all paths, so the check might test an uninitialized value. A test position (with TB) is given by: position fen 3K1k2/R7/8/8/8/8/8/R6Q w - - 0 1 moves a1b1 f8g8 b1a1 g8f8 a1b1 f8g8 b1a1 This is now fixed by always initializing the variable. closes https://github.com/official-stockfish/Stockfish/pull/4309 No functional change --- src/syzygy/tbprobe.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index f2de036df4a..4df495a84f5 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1514,7 +1514,7 @@ int Tablebases::probe_dtz(Position& pos, ProbeState* result) { // A return value false indicates that not all probes were successful. bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves) { - ProbeState result; + ProbeState result = OK; StateInfo st; // Obtain 50-move counter for the root position @@ -1593,7 +1593,7 @@ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves) { static const int WDL_to_rank[] = { -MAX_DTZ, -MAX_DTZ + 101, 0, MAX_DTZ - 101, MAX_DTZ }; - ProbeState result; + ProbeState result = OK; StateInfo st; WDLScore wdl; From 258c13ba8cc8d054b803a04c433a766d0c5a1e8f Mon Sep 17 00:00:00 2001 From: Sebastian Buchwald Date: Tue, 27 Dec 2022 10:31:24 +0100 Subject: [PATCH 0936/1766] Remove redundant extern modifier for function declarations Functions have external linkage by default, so there's no need to declare them extern. closes https://github.com/official-stockfish/Stockfish/pull/4308 No functional change --- src/position.h | 2 +- src/psqt.h | 2 +- src/uci.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/position.h b/src/position.h index 078ff5b79f2..90247cb1823 100644 --- a/src/position.h +++ b/src/position.h @@ -204,7 +204,7 @@ class Position { bool chess960; }; -extern std::ostream& operator<<(std::ostream& os, const Position& pos); +std::ostream& operator<<(std::ostream& os, const Position& pos); inline Color Position::side_to_move() const { return sideToMove; diff --git a/src/psqt.h b/src/psqt.h index 4ee0e379b14..bd78be90f23 100644 --- a/src/psqt.h +++ b/src/psqt.h @@ -30,7 +30,7 @@ namespace Stockfish::PSQT extern Score psq[PIECE_NB][SQUARE_NB]; // Fill psqt array from a set of internally linked parameters -extern void init(); +void init(); } // namespace Stockfish::PSQT diff --git a/src/uci.cpp b/src/uci.cpp index 5d842d25e73..dc5f10e12f2 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -36,7 +36,7 @@ using namespace std; namespace Stockfish { -extern vector setup_bench(const Position&, istream&); +vector setup_bench(const Position&, istream&); namespace { From be9bc420afa11314c886c0aede4c4ae3d76f8a50 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Thu, 29 Dec 2022 10:34:28 -0500 Subject: [PATCH 0937/1766] Update default net to nn-60fa44e376d9.nnue Created by retraining the master net on the previous best dataset with additional filtering. No new data was added. More of the Leela-dfrc_n5000.binpack part of the dataset was pre-filtered with depth6 multipv2 search to remove bestmove captures. About 93% of the previous Leela/SF data and 99% of the SF dfrc data was filtered. Unfiltered parts of the dataset were left out. The new Leela T80 oct+nov data is the same as before. All early game positions with ply count <= 28 were skipped during training by modifying the training data loader in nnue-pytorch. Trained in a similar way as recent master nets, with a different nnue-pytorch branch for early ply skipping: python3 easy_train.py \ --experiment-name=leela93-dfrc99-filt-only-T80-oct-nov-skip28 \ --training-dataset=/data/leela93-dfrc99-filt-only-T80-oct-nov.binpack \ --start-from-engine-test-net True \ --nnue-pytorch-branch=linrock/nnue-pytorch/misc-fixes-skip-ply-lteq-28 \ --gpus="0," \ --start-lambda=1.0 \ --end-lambda=0.75 \ --gamma=0.995 \ --lr=4.375e-4 \ --tui=False \ --seed=$RANDOM \ --max_epoch=800 \ --network-testing-threads 20 \ --num-workers 6 For the exact training data used: https://robotmoon.com/nnue-training-data/ Details about the previous best dataset: #4295 Local testing at a fixed 25k nodes: experiment_leela93-dfrc99-filt-only-T80-oct-nov-skip28 Local Elo: run_0/nn-epoch779.nnue : 5.1 +/- 1.5 Passed STC https://tests.stockfishchess.org/tests/view/63adb3acae97a464904fd4e8 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 36504 W: 9847 L: 9538 D: 17119 Ptnml(0-2): 108, 3981, 9784, 4252, 127 Passed LTC https://tests.stockfishchess.org/tests/view/63ae0ae25bd1e5f27f13d884 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 36592 W: 10017 L: 9717 D: 16858 Ptnml(0-2): 17, 3461, 11037, 3767, 14 closes https://github.com/official-stockfish/Stockfish/pull/4314 bench 4015511 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index d1398dd5ba8..4001166b153 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-335a9b2d8a80.nnue" + #define EvalFileDefaultName "nn-60fa44e376d9.nnue" namespace NNUE { From b60f9cc4515cdfb657a0166abb29a60257cc59e1 Mon Sep 17 00:00:00 2001 From: Sebastian Buchwald Date: Sun, 1 Jan 2023 14:45:08 +0100 Subject: [PATCH 0938/1766] Update copyright years Happy New Year! closes https://github.com/official-stockfish/Stockfish/pull/4315 No functional change --- src/Makefile | 2 +- src/benchmark.cpp | 2 +- src/bitbase.cpp | 2 +- src/bitboard.cpp | 2 +- src/bitboard.h | 2 +- src/endgame.cpp | 2 +- src/endgame.h | 2 +- src/evaluate.cpp | 2 +- src/evaluate.h | 2 +- src/main.cpp | 2 +- src/material.cpp | 2 +- src/material.h | 2 +- src/misc.cpp | 2 +- src/misc.h | 2 +- src/movegen.cpp | 2 +- src/movegen.h | 2 +- src/movepick.cpp | 2 +- src/movepick.h | 2 +- src/nnue/evaluate_nnue.cpp | 2 +- src/nnue/evaluate_nnue.h | 2 +- src/nnue/features/half_ka_v2_hm.cpp | 2 +- src/nnue/features/half_ka_v2_hm.h | 2 +- src/nnue/layers/affine_transform.h | 2 +- src/nnue/layers/clipped_relu.h | 2 +- src/nnue/layers/simd.h | 2 +- src/nnue/layers/sqr_clipped_relu.h | 2 +- src/nnue/nnue_accumulator.h | 2 +- src/nnue/nnue_architecture.h | 2 +- src/nnue/nnue_common.h | 2 +- src/nnue/nnue_feature_transformer.h | 2 +- src/pawns.cpp | 2 +- src/pawns.h | 2 +- src/position.cpp | 2 +- src/position.h | 2 +- src/psqt.cpp | 2 +- src/psqt.h | 2 +- src/search.cpp | 2 +- src/search.h | 2 +- src/syzygy/tbprobe.cpp | 2 +- src/syzygy/tbprobe.h | 2 +- src/thread.cpp | 2 +- src/thread.h | 2 +- src/thread_win32_osx.h | 2 +- src/timeman.cpp | 2 +- src/timeman.h | 2 +- src/tt.cpp | 2 +- src/tt.h | 2 +- src/tune.cpp | 2 +- src/tune.h | 2 +- src/types.h | 2 +- src/uci.cpp | 2 +- src/uci.h | 2 +- src/ucioption.cpp | 2 +- 53 files changed, 53 insertions(+), 53 deletions(-) diff --git a/src/Makefile b/src/Makefile index 500c100675d..d4f089ee423 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,5 +1,5 @@ # Stockfish, a UCI chess playing engine derived from Glaurung 2.1 -# Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) +# Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) # # Stockfish is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/src/benchmark.cpp b/src/benchmark.cpp index e1c025adb9a..2abb9c8fbb5 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/bitbase.cpp b/src/bitbase.cpp index 84300baf92a..e21d1fe9536 100644 --- a/src/bitbase.cpp +++ b/src/bitbase.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/bitboard.cpp b/src/bitboard.cpp index fd0ba235cb4..0ed13fd0d24 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/bitboard.h b/src/bitboard.h index 2b6e2a6920c..d4485fcbefc 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/endgame.cpp b/src/endgame.cpp index e773e7a9119..9021f242313 100644 --- a/src/endgame.cpp +++ b/src/endgame.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/endgame.h b/src/endgame.h index e79f696fa3a..c184cb3fd50 100644 --- a/src/endgame.h +++ b/src/endgame.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 7619398e01f..3b6cbbcd8a6 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/evaluate.h b/src/evaluate.h index 4001166b153..db9e6424ec2 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/main.cpp b/src/main.cpp index fad0ef8449b..c40e0fa3492 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/material.cpp b/src/material.cpp index 1567358af48..7102f8799ea 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/material.h b/src/material.h index 3ca169ce0b9..f6db85c4226 100644 --- a/src/material.h +++ b/src/material.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/misc.cpp b/src/misc.cpp index c7fa0e9ae10..5bb8da697a0 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/misc.h b/src/misc.h index 77b81d50fc0..4c1150f867b 100644 --- a/src/misc.h +++ b/src/misc.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/movegen.cpp b/src/movegen.cpp index c7a3c29bc04..a960f8630e5 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/movegen.h b/src/movegen.h index bbb35b39159..b8df3e65d5c 100644 --- a/src/movegen.h +++ b/src/movegen.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/movepick.cpp b/src/movepick.cpp index 564adc286ea..dbe67357d60 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/movepick.h b/src/movepick.h index e4c4a5bfdea..90f60b8a961 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 4715fed07d6..8d720ccbcf7 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/evaluate_nnue.h b/src/nnue/evaluate_nnue.h index 2e4f1f5098d..9499f7d99ab 100644 --- a/src/nnue/evaluate_nnue.h +++ b/src/nnue/evaluate_nnue.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/features/half_ka_v2_hm.cpp b/src/nnue/features/half_ka_v2_hm.cpp index 7dbd3415624..19ebb15fca4 100644 --- a/src/nnue/features/half_ka_v2_hm.cpp +++ b/src/nnue/features/half_ka_v2_hm.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/features/half_ka_v2_hm.h b/src/nnue/features/half_ka_v2_hm.h index a95d4328bb4..78063c3684c 100644 --- a/src/nnue/features/half_ka_v2_hm.h +++ b/src/nnue/features/half_ka_v2_hm.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 461a7b83eca..710ab8a7d9d 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index f94d30828d7..51e562dad34 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/layers/simd.h b/src/nnue/layers/simd.h index 7b9e8fb23b7..aeab39c4fc3 100644 --- a/src/nnue/layers/simd.h +++ b/src/nnue/layers/simd.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/layers/sqr_clipped_relu.h b/src/nnue/layers/sqr_clipped_relu.h index b603a277fa2..df539b39164 100644 --- a/src/nnue/layers/sqr_clipped_relu.h +++ b/src/nnue/layers/sqr_clipped_relu.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index 600483b5cfd..8eba4497464 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index cac8373075e..c43a23c3f69 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index 1795618946f..12309d262b1 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index b6dd54d3378..62f1615d5fe 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/pawns.cpp b/src/pawns.cpp index fdcfa0224e9..0ccafd9e9c8 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/pawns.h b/src/pawns.h index af0370fc41a..95c9ea3ce20 100644 --- a/src/pawns.h +++ b/src/pawns.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/position.cpp b/src/position.cpp index 5befcaf2cfc..e82425af7c4 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/position.h b/src/position.h index 90247cb1823..3de24f22888 100644 --- a/src/position.h +++ b/src/position.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/psqt.cpp b/src/psqt.cpp index ca5664c259f..d3ebb20da1b 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/psqt.h b/src/psqt.h index bd78be90f23..9630f446bc3 100644 --- a/src/psqt.h +++ b/src/psqt.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/search.cpp b/src/search.cpp index 0c8377d01ed..dd24036604e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/search.h b/src/search.h index b620202d917..48a0f7ce710 100644 --- a/src/search.h +++ b/src/search.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 4df495a84f5..b7ba3240745 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/syzygy/tbprobe.h b/src/syzygy/tbprobe.h index 179c75721c2..159c68652d1 100644 --- a/src/syzygy/tbprobe.h +++ b/src/syzygy/tbprobe.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/thread.cpp b/src/thread.cpp index e8723eb7411..7e71edf10a3 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/thread.h b/src/thread.h index 5f0b2c3ed9c..680da2090d9 100644 --- a/src/thread.h +++ b/src/thread.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/thread_win32_osx.h b/src/thread_win32_osx.h index 77d1c3c7ef3..01ff1c77afa 100644 --- a/src/thread_win32_osx.h +++ b/src/thread_win32_osx.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/timeman.cpp b/src/timeman.cpp index cab0d7671a2..5c826b4f0c5 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/timeman.h b/src/timeman.h index a86f07693a0..3462b8236a7 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/tt.cpp b/src/tt.cpp index c7118aea3fc..39f18d3d9c4 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/tt.h b/src/tt.h index 03fe3e143d1..3e335b44696 100644 --- a/src/tt.h +++ b/src/tt.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/tune.cpp b/src/tune.cpp index a885845f750..41f6664d9ca 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/tune.h b/src/tune.h index 75ab484acf0..f5b787afce5 100644 --- a/src/tune.h +++ b/src/tune.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/types.h b/src/types.h index 29c16ce7dbb..8a021342e25 100644 --- a/src/types.h +++ b/src/types.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/uci.cpp b/src/uci.cpp index dc5f10e12f2..c49b9b78a49 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/uci.h b/src/uci.h index 3b5a6764b48..bd3df323f0a 100644 --- a/src/uci.h +++ b/src/uci.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 9fb48345a06..78711c1816c 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2022 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by From a6fa683418c6a74491035601e16497851eea6be6 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Thu, 29 Dec 2022 10:34:28 -0500 Subject: [PATCH 0939/1766] Update default net to nn-a3dc078bafc7.nnue This is a later epoch (epoch 859) from the same experiment run that trained yesterday's master net nn-60fa44e376d9.nnue (epoch 779). The experiment was manually paused around epoch 790 and unpaused with max epoch increased to 900 mainly to get more local elo data without letting the GPU idle. nn-60fa44e376d9.nnue is from #4314 nn-335a9b2d8a80.nnue is from #4295 Local elo vs. nn-335a9b2d8a80.nnue at 25k nodes per move: experiment_leela93-dfrc99-filt-only-T80-oct-nov-skip28 run_0/nn-epoch779.nnue (nn-60fa44e376d9.nnue) : 5.0 +/- 1.2 run_0/nn-epoch859.nnue (nn-a3dc078bafc7.nnue) : 5.6 +/- 1.6 Passed STC vs. nn-335a9b2d8a80.nnue https://tests.stockfishchess.org/tests/view/63ae10495bd1e5f27f13d94f LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 37536 W: 10088 L: 9781 D: 17667 Ptnml(0-2): 110, 4006, 10223, 4325, 104 An LTC test vs. nn-335a9b2d8a80.nnue was paused due to nn-60fa44e376d9.nnue passing LTC first: https://tests.stockfishchess.org/tests/view/63ae5d34331d5fca5113703b Passed LTC vs. nn-60fa44e376d9.nnue https://tests.stockfishchess.org/tests/view/63af1e41465d2b022dbce4e7 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 148704 W: 39672 L: 39155 D: 69877 Ptnml(0-2): 59, 14443, 44843, 14936, 71 closes https://github.com/official-stockfish/Stockfish/pull/4319 bench 3984365 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index db9e6424ec2..0a00faa7846 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-60fa44e376d9.nnue" + #define EvalFileDefaultName "nn-a3dc078bafc7.nnue" namespace NNUE { From fc5b59b88bae00b7e74bbad0de7c3c33136937cf Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Mon, 2 Jan 2023 17:19:41 +0300 Subject: [PATCH 0940/1766] Parameter Tweaks This patch is a parameter tweak that passed both STC and LTC tests. STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 80944 W: 21557 L: 21189 D: 38198 Ptnml(0-2): 192, 8883, 22028, 9103, 266 https://tests.stockfishchess.org/tests/view/63b07fe2d421d8f75795a03b LTC: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 30440 W: 8296 L: 8007 D: 14137 Ptnml(0-2): 6, 2893, 9143, 3162, 16 https://tests.stockfishchess.org/tests/view/63b167d02ab1290f961644db closes https://github.com/official-stockfish/Stockfish/pull/4318 Bench: 4182223 --- src/evaluate.cpp | 12 +++++----- src/search.cpp | 60 ++++++++++++++++++++++++------------------------ 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 3b6cbbcd8a6..d5cda3d80e8 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1056,7 +1056,7 @@ Value Eval::evaluate(const Position& pos, int* complexity) { // We use the much less accurate but faster Classical eval when the NNUE // option is set to false. Otherwise we use the NNUE eval unless the // PSQ advantage is decisive and several pieces remain. (~3 Elo) - bool useClassical = !useNNUE || (pos.count() > 7 && abs(psq) > 1760); + bool useClassical = !useNNUE || (pos.count() > 7 && abs(psq) > 1781); if (useClassical) v = Evaluation(pos).value(); @@ -1071,8 +1071,8 @@ Value Eval::evaluate(const Position& pos, int* complexity) { Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); // Blend nnue complexity with (semi)classical complexity - nnueComplexity = ( 412 * nnueComplexity - + 428 * abs(psq - nnue) + nnueComplexity = ( 406 * nnueComplexity + + 424 * abs(psq - nnue) + (optimism > 0 ? int(optimism) * int(psq - nnue) : 0) ) / 1024; @@ -1080,12 +1080,12 @@ Value Eval::evaluate(const Position& pos, int* complexity) { if (complexity) *complexity = nnueComplexity; - optimism = optimism * (278 + nnueComplexity) / 256; - v = (nnue * scale + optimism * (scale - 755)) / 1024; + optimism = optimism * (272 + nnueComplexity) / 256; + v = (nnue * scale + optimism * (scale - 748)) / 1024; } // Damp down the evaluation linearly when shuffling - v = v * (197 - pos.rule50_count()) / 214; + v = v * (200 - pos.rule50_count()) / 214; // Guarantee evaluation does not hit the tablebase range v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); diff --git a/src/search.cpp b/src/search.cpp index dd24036604e..d0ed44fa16a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -63,7 +63,7 @@ namespace { // Futility margin Value futility_margin(Depth d, bool improving) { - return Value(165 * (d - improving)); + return Value(158 * (d - improving)); } // Reductions lookup table, initialized at startup @@ -71,7 +71,7 @@ namespace { Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) { int r = Reductions[d] * Reductions[mn]; - return (r + 1642 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 916); + return (r + 1460 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 937); } constexpr int futility_move_count(bool improving, Depth depth) { @@ -81,7 +81,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return std::min((12 * d + 282) * d - 349 , 1480); + return std::min((11 * d + 284) * d - 363 , 1650); } // Add a small random component to draw evaluations to avoid 3-fold blindness @@ -307,7 +307,7 @@ void Thread::search() { multiPV = std::min(multiPV, rootMoves.size()); - complexityAverage.set(155, 1); + complexityAverage.set(153, 1); optimism[us] = optimism[~us] = VALUE_ZERO; @@ -351,12 +351,12 @@ void Thread::search() { if (rootDepth >= 4) { Value prev = rootMoves[pvIdx].averageScore; - delta = Value(10) + int(prev) * prev / 15620; + delta = Value(10) + int(prev) * prev / 15400; alpha = std::max(prev - delta,-VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE); // Adjust optimism based on root move's previousScore - int opt = 118 * prev / (std::abs(prev) + 169); + int opt = 116 * prev / (std::abs(prev) + 170); optimism[ us] = Value(opt); optimism[~us] = -optimism[us]; } @@ -753,7 +753,7 @@ namespace { // Use static evaluation difference to improve quiet move ordering (~4 Elo) if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture) { - int bonus = std::clamp(-19 * int((ss-1)->staticEval + ss->staticEval), -1914, 1914); + int bonus = std::clamp(-19 * int((ss-1)->staticEval + ss->staticEval), -1940, 1940); thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus; } @@ -763,13 +763,13 @@ namespace { // margin and the improving flag are used in various pruning heuristics. improvement = (ss-2)->staticEval != VALUE_NONE ? ss->staticEval - (ss-2)->staticEval : (ss-4)->staticEval != VALUE_NONE ? ss->staticEval - (ss-4)->staticEval - : 168; + : 172; improving = improvement > 0; // Step 7. Razoring (~1 Elo). // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. - if (eval < alpha - 369 - 254 * depth * depth) + if (eval < alpha - 394 - 255 * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -780,18 +780,18 @@ namespace { // The depth condition is important for mate finding. if ( !ss->ttPv && depth < 8 - && eval - futility_margin(depth, improving) - (ss-1)->statScore / 303 >= beta + && eval - futility_margin(depth, improving) - (ss-1)->statScore / 304 >= beta && eval >= beta - && eval < 28031) // larger than VALUE_KNOWN_WIN, but smaller than TB wins + && eval < 28580) // larger than VALUE_KNOWN_WIN, but smaller than TB wins return eval; // Step 9. Null move search with verification search (~35 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 17139 + && (ss-1)->statScore < 18200 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 20 * depth - improvement / 13 + 233 + complexity / 25 + && ss->staticEval >= beta - 20 * depth - improvement / 14 + 235 + complexity / 24 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -799,7 +799,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth, eval and complexity of position - Depth R = std::min(int(eval - beta) / 168, 7) + depth / 3 + 4 - (complexity > 861); + Depth R = std::min(int(eval - beta) / 165, 6) + depth / 3 + 4 - (complexity > 800); ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -835,7 +835,7 @@ namespace { } } - probCutBeta = beta + 191 - 54 * improving; + probCutBeta = beta + 180 - 54 * improving; // Step 10. ProbCut (~10 Elo) // If we have a good enough capture and a reduced search returns a value @@ -904,7 +904,7 @@ namespace { moves_loop: // When in check, search starts here // Step 12. A small Probcut idea, when we are in check (~4 Elo) - probCutBeta = beta + 417; + probCutBeta = beta + 402; if ( ss->inCheck && !PvNode && depth >= 2 @@ -999,12 +999,12 @@ namespace { && !PvNode && lmrDepth < 7 && !ss->inCheck - && ss->staticEval + 180 + 201 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] + && ss->staticEval + 185 + 203 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 6 < alpha) continue; // SEE based pruning (~11 Elo) - if (!pos.see_ge(move, Value(-222) * depth)) + if (!pos.see_ge(move, Value(-220) * depth)) continue; } else @@ -1015,7 +1015,7 @@ namespace { // Continuation history based pruning (~2 Elo) if ( lmrDepth < 5 - && history < -3875 * (depth - 1)) + && history < -4180 * (depth - 1)) continue; history += 2 * thisThread->mainHistory[us][from_to(move)]; @@ -1023,11 +1023,11 @@ namespace { // Futility pruning: parent node (~13 Elo) if ( !ss->inCheck && lmrDepth < 13 - && ss->staticEval + 106 + 145 * lmrDepth + history / 52 <= alpha) + && ss->staticEval + 103 + 136 * lmrDepth + history / 53 <= alpha) continue; // Prune moves with negative SEE (~4 Elo) - if (!pos.see_ge(move, Value(-24 * lmrDepth * lmrDepth - 15 * lmrDepth))) + if (!pos.see_ge(move, Value(-25 * lmrDepth * lmrDepth - 16 * lmrDepth))) continue; } } @@ -1092,14 +1092,14 @@ namespace { // Check extensions (~1 Elo) else if ( givesCheck && depth > 9 - && abs(ss->staticEval) > 82) + && abs(ss->staticEval) > 78) extension = 1; // Quiet ttMove extensions (~1 Elo) else if ( PvNode && move == ttMove && move == ss->killers[0] - && (*contHist[0])[movedPiece][to_sq(move)] >= 5177) + && (*contHist[0])[movedPiece][to_sq(move)] >= 5600) extension = 1; } @@ -1161,10 +1161,10 @@ namespace { + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 4433; + - 4467; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - r -= ss->statScore / (13000 + 4152 * (depth > 7 && depth < 19)); + r -= ss->statScore / (12800 + 4410 * (depth > 7 && depth < 19)); // Step 17. Late moves reduction / extension (LMR, ~117 Elo) // We use various heuristics for the sons of a node after the first son has @@ -1188,7 +1188,7 @@ namespace { { // Adjust full depth search based on LMR results - if result // was good enough search deeper, if it was bad enough search shallower - const bool doDeeperSearch = value > (alpha + 64 + 11 * (newDepth - d)); + const bool doDeeperSearch = value > (alpha + 66 + 11 * (newDepth - d)); const bool doEvenDeeperSearch = value > alpha + 582 && ss->doubleExtensions <= 5; const bool doShallowerSearch = value < bestValue + newDepth; @@ -1353,7 +1353,7 @@ namespace { quietsSearched, quietCount, capturesSearched, captureCount, depth); // Bonus for prior countermove that caused the fail low - else if ( (depth >= 5 || PvNode || bestValue < alpha - 62 * depth) + else if ( (depth >= 5 || PvNode || bestValue < alpha - 65 * depth) && !priorCapture) { //Assign extra bonus if current node is PvNode or cutNode @@ -1361,7 +1361,7 @@ namespace { bool extraBonus = PvNode || cutNode; - bool doubleExtraBonus = extraBonus && bestValue < alpha - 85 * depth; + bool doubleExtraBonus = extraBonus && bestValue < alpha - 88 * depth; update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * (1 + extraBonus + doubleExtraBonus)); } @@ -1488,7 +1488,7 @@ namespace { if (PvNode && bestValue > alpha) alpha = bestValue; - futilityBase = bestValue + 153; + futilityBase = bestValue + 158; } const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, @@ -1690,7 +1690,7 @@ namespace { if (!pos.capture(bestMove)) { - int bonus2 = bestValue > beta + 137 ? bonus1 // larger bonus + int bonus2 = bestValue > beta + 146 ? bonus1 // larger bonus : stat_bonus(depth); // smaller bonus // Increase stats for the best move in case it was a quiet move From 9fe9ff00823a0d1e989fcfc45410396e94345987 Mon Sep 17 00:00:00 2001 From: mstembera Date: Tue, 27 Dec 2022 16:44:32 -0800 Subject: [PATCH 0941/1766] Fix stack initialization This fixes a bug where on line 278 the Stack::staticEvals are initialized to 0. However VALUE_NONE is defined to be 32002 so this is a bug in master. It is probably due to the calculation of improvement, where staticEval prior to rootPos can be accessed. https://tests.stockfishchess.org/tests/view/63ab91cf39af998100ce1666 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 53736 W: 14285 L: 13955 D: 25496 Ptnml(0-2): 121, 5921, 14500, 6159, 167 https://tests.stockfishchess.org/tests/view/63b2af5ee28ed36c814bed52 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 33776 W: 9130 L: 8934 D: 15712 Ptnml(0-2): 14, 3240, 10185, 3434, 15 closes https://github.com/official-stockfish/Stockfish/pull/4320 Bench: 4068510 --- src/search.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index d0ed44fa16a..d27b21d2cb8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -276,8 +276,11 @@ void Thread::search() { int iterIdx = 0; std::memset(ss-7, 0, 10 * sizeof(Stack)); - for (int i = 7; i > 0; i--) + for (int i = 7; i > 0; --i) + { (ss-i)->continuationHistory = &this->continuationHistory[0][0][NO_PIECE][0]; // Use as a sentinel + (ss-i)->staticEval = VALUE_NONE; + } for (int i = 0; i <= MAX_PLY + 2; ++i) (ss+i)->ply = i; From ea0f34120fbc1ab7aa8047a1319714b87a1f3b87 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Sat, 24 Dec 2022 09:43:22 -0600 Subject: [PATCH 0942/1766] Late countermove bonus: remove "extraBonus &&" passed stc: https://tests.stockfishchess.org/tests/view/63a71e409c0589b83751dc25 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 432480 W: 113846 L: 114055 D: 204579 Ptnml(0-2): 1164, 48205, 117701, 48016, 1154 passed ltc: https://tests.stockfishchess.org/tests/view/63aba66639af998100ce1aa9 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 245344 W: 65309 L: 65317 D: 114718 Ptnml(0-2): 117, 24257, 73903, 24307, 88 closes https://github.com/official-stockfish/Stockfish/pull/4322 bench 4379218 --- src/search.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index d27b21d2cb8..55af6abab1c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1359,14 +1359,9 @@ namespace { else if ( (depth >= 5 || PvNode || bestValue < alpha - 65 * depth) && !priorCapture) { - //Assign extra bonus if current node is PvNode or cutNode - //or fail low was really bad - bool extraBonus = PvNode - || cutNode; - - bool doubleExtraBonus = extraBonus && bestValue < alpha - 88 * depth; - - update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * (1 + extraBonus + doubleExtraBonus)); + // Extra bonuses for PV/Cut nodes or bad fail lows + int bonus = 1 + (PvNode || cutNode) + (bestValue < alpha - 88 * depth); + update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); } if (PvNode) From 4101893a28b4d34262ed1fba10f2365e964b6537 Mon Sep 17 00:00:00 2001 From: candirufish <38038147+candirufish@users.noreply.github.com> Date: Mon, 9 Jan 2023 14:26:51 +0100 Subject: [PATCH 0943/1766] On step 18 increase reduction by 2 if not ttmove and cutnode stc: https://tests.stockfishchess.org/tests/view/63babc9fcd3db0c8d399f723 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 43104 W: 11711 L: 11389 D: 20004 Ptnml(0-2): 211, 4518, 11793, 4798, 232 ltc: https://tests.stockfishchess.org/tests/view/63bb1857cd3db0c8d39a0661 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 127104 W: 33810 L: 33339 D: 59955 Ptnml(0-2): 39, 12155, 38702, 12608, 48 closes https://github.com/official-stockfish/Stockfish/pull/4334 Bench: 4035725 --- src/search.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 55af6abab1c..f5eeb23b43f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1215,6 +1215,10 @@ namespace { // Step 18. Full depth search when LMR is skipped. If expected reduction is high, reduce its depth by 1. else if (!PvNode || moveCount > 1) { + // Increase reduction for cut nodes and not ttMove (~1 Elo) + if (!ttMove && cutNode) + r += 2; + value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth - (r > 4), !cutNode); } From fcee83810a5bcf774d3e13cf9e65ccf3b29e3319 Mon Sep 17 00:00:00 2001 From: Jake Senne <26696846+w1wwwwww@users.noreply.github.com> Date: Tue, 3 Jan 2023 15:16:45 -0600 Subject: [PATCH 0944/1766] Only close file if already open Ensures that the tablebase file is only closed if already open. Fixes #4268 Closes https://github.com/official-stockfish/Stockfish/pull/4321 No functional change --- AUTHORS | 1 + src/syzygy/tbprobe.cpp | 9 +++------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/AUTHORS b/AUTHORS index 70b500ea79e..998399b969d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -92,6 +92,7 @@ Ivan Ivec (IIvec) Jacques B. (Timshel) Jan Ondruš (hxim) Jared Kish (Kurtbusch, kurt22i) +Jake Senne (w1wwwwww) Jarrod Torriero (DU-jdto) Jean Gauthier (OuaisBla) Jean-Francois Romang (jromang) diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index b7ba3240745..cc5e2852a09 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -199,13 +199,10 @@ class TBFile : public std::ifstream { } } - // Memory map the file and check it. File should be already open and will be - // closed after mapping. + // Memory map the file and check it. uint8_t* map(void** baseAddress, uint64_t* mapping, TBType type) { - - assert(is_open()); - - close(); // Need to re-open to get native file descriptor + if (is_open()) + close(); // Need to re-open to get native file descriptor #ifndef _WIN32 struct stat statbuf; From 31acd6bab70f4661316986c2c93163d39736fd61 Mon Sep 17 00:00:00 2001 From: Sebastian Buchwald Date: Tue, 27 Dec 2022 21:06:10 +0100 Subject: [PATCH 0945/1766] Warn if a global function has no previous declaration If a global function has no previous declaration, either the declaration is missing in the corresponding header file or the function should be declared static. Static functions are local to the translation unit, which allows the compiler to apply some optimizations earlier (when compiling the translation unit rather than during link-time optimization). The commit enables the warning for gcc, clang, and mingw. It also fixes the reported warnings by declaring the functions static or by adding a header file (benchmark.h). closes https://github.com/official-stockfish/Stockfish/pull/4325 No functional change --- src/Makefile | 6 +++--- src/benchmark.cpp | 2 ++ src/benchmark.h | 34 ++++++++++++++++++++++++++++++++++ src/evaluate.cpp | 10 +++++----- src/misc.cpp | 2 +- src/nnue/evaluate_nnue.cpp | 10 +++++----- src/uci.cpp | 3 +-- src/ucioption.cpp | 14 +++++++------- 8 files changed, 58 insertions(+), 23 deletions(-) create mode 100644 src/benchmark.h diff --git a/src/Makefile b/src/Makefile index d4f089ee423..30c1be5ee52 100644 --- a/src/Makefile +++ b/src/Makefile @@ -366,7 +366,7 @@ endif ifeq ($(COMP),gcc) comp=gcc CXX=g++ - CXXFLAGS += -pedantic -Wextra -Wshadow + CXXFLAGS += -pedantic -Wextra -Wshadow -Wmissing-declarations ifeq ($(arch),$(filter $(arch),armv7 armv8 riscv64)) ifeq ($(OS),Android) @@ -410,7 +410,7 @@ ifeq ($(COMP),mingw) CXX=i686-w64-mingw32-c++-posix endif endif - CXXFLAGS += -pedantic -Wextra -Wshadow + CXXFLAGS += -pedantic -Wextra -Wshadow -Wmissing-declarations endif ifeq ($(COMP),icc) @@ -426,7 +426,7 @@ ifeq ($(COMP),clang) CXX=x86_64-w64-mingw32-clang++ endif - CXXFLAGS += -pedantic -Wextra -Wshadow + CXXFLAGS += -pedantic -Wextra -Wshadow -Wmissing-prototypes ifeq ($(filter $(KERNEL),Darwin OpenBSD FreeBSD),) ifeq ($(target_windows),) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 2abb9c8fbb5..a1ad055057b 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -16,6 +16,8 @@ along with this program. If not, see . */ +#include "benchmark.h" + #include #include #include diff --git a/src/benchmark.h b/src/benchmark.h new file mode 100644 index 00000000000..64acf833ac0 --- /dev/null +++ b/src/benchmark.h @@ -0,0 +1,34 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef BENCHMARK_H_INCLUDED +#define BENCHMARK_H_INCLUDED + +#include +#include +#include + +namespace Stockfish { + +class Position; + +std::vector setup_bench(const Position&, std::istream&); + +} // namespace Stockfish + +#endif // #ifndef BENCHMARK_H_INCLUDED diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d5cda3d80e8..3aae9f1c6ec 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -159,24 +159,24 @@ namespace Trace { Score scores[TERM_NB][COLOR_NB]; - double to_cp(Value v) { return double(v) / UCI::NormalizeToPawnValue; } + static double to_cp(Value v) { return double(v) / UCI::NormalizeToPawnValue; } - void add(int idx, Color c, Score s) { + static void add(int idx, Color c, Score s) { scores[idx][c] = s; } - void add(int idx, Score w, Score b = SCORE_ZERO) { + static void add(int idx, Score w, Score b = SCORE_ZERO) { scores[idx][WHITE] = w; scores[idx][BLACK] = b; } - std::ostream& operator<<(std::ostream& os, Score s) { + static std::ostream& operator<<(std::ostream& os, Score s) { os << std::setw(5) << to_cp(mg_value(s)) << " " << std::setw(5) << to_cp(eg_value(s)); return os; } - std::ostream& operator<<(std::ostream& os, Term t) { + static std::ostream& operator<<(std::ostream& os, Term t) { if (t == MATERIAL || t == IMBALANCE || t == WINNABLE || t == TOTAL) os << " ---- ----" << " | " << " ---- ----"; diff --git a/src/misc.cpp b/src/misc.cpp index 5bb8da697a0..1cb515c3e41 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -517,7 +517,7 @@ void bindThisThread(size_t) {} /// API and returns the best node id for the thread with index idx. Original /// code from Texel by Peter Österlund. -int best_node(size_t idx) { +static int best_node(size_t idx) { int threads = 0; int nodes = 0; diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 8d720ccbcf7..a06c1978517 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -83,7 +83,7 @@ namespace Stockfish::Eval::NNUE { } // namespace Detail // Initialize the evaluation function parameters - void initialize() { + static void initialize() { Detail::initialize(featureTransformer); for (std::size_t i = 0; i < LayerStacks; ++i) @@ -91,7 +91,7 @@ namespace Stockfish::Eval::NNUE { } // Read network header - bool read_header(std::istream& stream, std::uint32_t* hashValue, std::string* desc) + static bool read_header(std::istream& stream, std::uint32_t* hashValue, std::string* desc) { std::uint32_t version, size; @@ -105,7 +105,7 @@ namespace Stockfish::Eval::NNUE { } // Write network header - bool write_header(std::ostream& stream, std::uint32_t hashValue, const std::string& desc) + static bool write_header(std::ostream& stream, std::uint32_t hashValue, const std::string& desc) { write_little_endian(stream, Version); write_little_endian(stream, hashValue); @@ -115,7 +115,7 @@ namespace Stockfish::Eval::NNUE { } // Read network parameters - bool read_parameters(std::istream& stream) { + static bool read_parameters(std::istream& stream) { std::uint32_t hashValue; if (!read_header(stream, &hashValue, &netDescription)) return false; @@ -127,7 +127,7 @@ namespace Stockfish::Eval::NNUE { } // Write network parameters - bool write_parameters(std::ostream& stream) { + static bool write_parameters(std::ostream& stream) { if (!write_header(stream, HashValue, netDescription)) return false; if (!Detail::write_parameters(stream, *featureTransformer)) return false; diff --git a/src/uci.cpp b/src/uci.cpp index c49b9b78a49..eb158e723e4 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -22,6 +22,7 @@ #include #include +#include "benchmark.h" #include "evaluate.h" #include "movegen.h" #include "position.h" @@ -36,8 +37,6 @@ using namespace std; namespace Stockfish { -vector setup_bench(const Position&, istream&); - namespace { // FEN string for the initial position in standard chess diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 78711c1816c..b4ce70b4ed2 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -38,13 +38,13 @@ UCI::OptionsMap Options; // Global object namespace UCI { /// 'On change' actions, triggered by an option's value change -void on_clear_hash(const Option&) { Search::clear(); } -void on_hash_size(const Option& o) { TT.resize(size_t(o)); } -void on_logger(const Option& o) { start_logger(o); } -void on_threads(const Option& o) { Threads.set(size_t(o)); } -void on_tb_path(const Option& o) { Tablebases::init(o); } -void on_use_NNUE(const Option& ) { Eval::NNUE::init(); } -void on_eval_file(const Option& ) { Eval::NNUE::init(); } +static void on_clear_hash(const Option&) { Search::clear(); } +static void on_hash_size(const Option& o) { TT.resize(size_t(o)); } +static void on_logger(const Option& o) { start_logger(o); } +static void on_threads(const Option& o) { Threads.set(size_t(o)); } +static void on_tb_path(const Option& o) { Tablebases::init(o); } +static void on_use_NNUE(const Option&) { Eval::NNUE::init(); } +static void on_eval_file(const Option&) { Eval::NNUE::init(); } /// Our case insensitive less() function as required by UCI protocol bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const { From 5a88c5bb9b3e5ee431ac85abb8981b1571b68b2d Mon Sep 17 00:00:00 2001 From: Stefano Di Martino Date: Sat, 7 Jan 2023 01:08:30 +0100 Subject: [PATCH 0946/1766] Modernize code base a little bit Removed sprintf() which generated a warning, because of security reasons. Replace NULL with nullptr Replace typedef with using Do not inherit from std::vector. Use composition instead. optimize mutex-unlocking closes https://github.com/official-stockfish/Stockfish/pull/4327 No functional change --- AUTHORS | 1 + src/material.h | 2 +- src/misc.cpp | 8 ++++---- src/nnue/evaluate_nnue.cpp | 31 ++++++++++++------------------- src/thread.cpp | 38 +++++++++++++++++++------------------- src/thread.h | 14 +++++++++++--- src/thread_win32_osx.h | 6 +++--- 7 files changed, 51 insertions(+), 49 deletions(-) diff --git a/AUTHORS b/AUTHORS index 998399b969d..87fed7b80c7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -192,6 +192,7 @@ Shawn Varghese (xXH4CKST3RXx) Siad Daboul (Topologist) Stefan Geschwentner (locutus2) Stefano Cardanobile (Stefano80) +Stefano Di Martino (StefanoD) Steinar Gunderson (sesse) Stéphane Nicolet (snicolet) Syine Mineta (MinetaS) diff --git a/src/material.h b/src/material.h index f6db85c4226..73c831006bf 100644 --- a/src/material.h +++ b/src/material.h @@ -28,7 +28,7 @@ namespace Stockfish::Material { /// Material::Entry contains various information about a material configuration. /// It contains a material imbalance evaluation, a function pointer to a special -/// endgame evaluation function (which in most cases is NULL, meaning that the +/// endgame evaluation function (which in most cases is nullptr, meaning that the /// standard evaluation function will be used), and scale factors. /// /// The scale factors are used to scale the evaluation score up or down. For diff --git a/src/misc.cpp b/src/misc.cpp index 1cb515c3e41..be7abba5d01 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -413,7 +413,7 @@ static void* aligned_large_pages_alloc_windows([[maybe_unused]] size_t allocSize if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken)) return nullptr; - if (LookupPrivilegeValue(NULL, SE_LOCK_MEMORY_NAME, &luid)) + if (LookupPrivilegeValue(nullptr, SE_LOCK_MEMORY_NAME, &luid)) { TOKEN_PRIVILEGES tp { }; TOKEN_PRIVILEGES prevTp { }; @@ -432,10 +432,10 @@ static void* aligned_large_pages_alloc_windows([[maybe_unused]] size_t allocSize // Round up size to full pages and allocate allocSize = (allocSize + largePageSize - 1) & ~size_t(largePageSize - 1); mem = VirtualAlloc( - NULL, allocSize, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); + nullptr, allocSize, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); // Privilege no longer needed, restore previous state - AdjustTokenPrivileges(hProcessToken, FALSE, &prevTp, 0, NULL, NULL); + AdjustTokenPrivileges(hProcessToken, FALSE, &prevTp, 0, nullptr, nullptr); } } @@ -453,7 +453,7 @@ void* aligned_large_pages_alloc(size_t allocSize) { // Fall back to regular, page aligned, allocation if necessary if (!mem) - mem = VirtualAlloc(NULL, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + mem = VirtualAlloc(nullptr, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); return mem; } diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index a06c1978517..06281da06a5 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -26,7 +26,6 @@ #include "../evaluate.h" #include "../position.h" -#include "../misc.h" #include "../uci.h" #include "../types.h" @@ -245,14 +244,15 @@ namespace Stockfish::Eval::NNUE { } - // format_cp_aligned_dot() converts a Value into (centi)pawns and writes it in a buffer, - // always keeping two decimals. The buffer must have capacity for at least 7 chars. - static void format_cp_aligned_dot(Value v, char* buffer) { + // format_cp_aligned_dot() converts a Value into (centi)pawns, always keeping two decimals. + static void format_cp_aligned_dot(Value v, std::stringstream &stream) { + const double cp = 1.0 * std::abs(int(v)) / UCI::NormalizeToPawnValue; - buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' '); - - double cp = 1.0 * std::abs(int(v)) / UCI::NormalizeToPawnValue; - sprintf(&buffer[1], "%6.2f", cp); + stream << (v < 0 ? '-' : v > 0 ? '+' : ' ') + << std::setiosflags(std::ios::fixed) + << std::setw(6) + << std::setprecision(2) + << cp; } @@ -332,17 +332,10 @@ namespace Stockfish::Eval::NNUE { for (std::size_t bucket = 0; bucket < LayerStacks; ++bucket) { - char buffer[3][8]; - std::memset(buffer, '\0', sizeof(buffer)); - - format_cp_aligned_dot(t.psqt[bucket], buffer[0]); - format_cp_aligned_dot(t.positional[bucket], buffer[1]); - format_cp_aligned_dot(t.psqt[bucket] + t.positional[bucket], buffer[2]); - - ss << "| " << bucket << " " - << " | " << buffer[0] << " " - << " | " << buffer[1] << " " - << " | " << buffer[2] << " " + ss << "| " << bucket << " "; + ss << " | "; format_cp_aligned_dot(t.psqt[bucket], ss); ss << " " + << " | "; format_cp_aligned_dot(t.positional[bucket], ss); ss << " " + << " | "; format_cp_aligned_dot(t.psqt[bucket] + t.positional[bucket], ss); ss << " " << " |"; if (bucket == t.correctBucket) ss << " <-- this bucket is used"; diff --git a/src/thread.cpp b/src/thread.cpp index 7e71edf10a3..ca1a7c85273 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -73,9 +73,9 @@ void Thread::clear() { /// Thread::start_searching() wakes up the thread that will start the search void Thread::start_searching() { - - std::lock_guard lk(mutex); + mutex.lock(); searching = true; + mutex.unlock(); // Unlock before notifying saves a few CPU-cycles cv.notify_one(); // Wake up the thread in idle_loop() } @@ -125,20 +125,20 @@ void Thread::idle_loop() { void ThreadPool::set(size_t requested) { - if (size() > 0) // destroy any existing thread(s) + if (threads.size() > 0) // destroy any existing thread(s) { main()->wait_for_search_finished(); - while (size() > 0) - delete back(), pop_back(); + while (threads.size() > 0) + delete threads.back(), threads.pop_back(); } if (requested > 0) // create new thread(s) { - push_back(new MainThread(0)); + threads.push_back(new MainThread(0)); - while (size() < requested) - push_back(new Thread(size())); + while (threads.size() < requested) + threads.push_back(new Thread(threads.size())); clear(); // Reallocate the hash with the new threadpool size @@ -154,7 +154,7 @@ void ThreadPool::set(size_t requested) { void ThreadPool::clear() { - for (Thread* th : *this) + for (Thread* th : threads) th->clear(); main()->callsCnt = 0; @@ -187,7 +187,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states, Tablebases::rank_root_moves(pos, rootMoves); // After ownership transfer 'states' becomes empty, so if we stop the search - // and call 'go' again without setting a new position states.get() == NULL. + // and call 'go' again without setting a new position states.get() == nullptr. assert(states.get() || setupStates.get()); if (states.get()) @@ -198,7 +198,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states, // be deduced from a fen string, so set() clears them and they are set from // setupStates->back() later. The rootState is per thread, earlier states are shared // since they are read-only. - for (Thread* th : *this) + for (Thread* th : threads) { th->nodes = th->tbHits = th->nmpMinPly = th->bestMoveChanges = 0; th->rootDepth = th->completedDepth = 0; @@ -212,12 +212,12 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states, Thread* ThreadPool::get_best_thread() const { - Thread* bestThread = front(); + Thread* bestThread = threads.front(); std::map votes; Value minScore = VALUE_NONE; // Find minimum score of all threads - for (Thread* th: *this) + for (Thread* th: threads) minScore = std::min(minScore, th->rootMoves[0].score); // Vote according to score and depth, and select the best thread @@ -225,10 +225,10 @@ Thread* ThreadPool::get_best_thread() const { return (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth); }; - for (Thread* th : *this) + for (Thread* th : threads) votes[th->rootMoves[0].pv[0]] += thread_value(th); - for (Thread* th : *this) + for (Thread* th : threads) if (abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY) { // Make sure we pick the shortest mate / TB conversion or stave off mate the longest @@ -251,8 +251,8 @@ Thread* ThreadPool::get_best_thread() const { void ThreadPool::start_searching() { - for (Thread* th : *this) - if (th != front()) + for (Thread* th : threads) + if (th != threads.front()) th->start_searching(); } @@ -261,8 +261,8 @@ void ThreadPool::start_searching() { void ThreadPool::wait_for_search_finished() const { - for (Thread* th : *this) - if (th != front()) + for (Thread* th : threads) + if (th != threads.front()) th->wait_for_search_finished(); } diff --git a/src/thread.h b/src/thread.h index 680da2090d9..7566322c54c 100644 --- a/src/thread.h +++ b/src/thread.h @@ -101,13 +101,13 @@ struct MainThread : public Thread { /// parking and, most importantly, launching a thread. All the access to threads /// is done through this class. -struct ThreadPool : public std::vector { +struct ThreadPool { void start_thinking(Position&, StateListPtr&, const Search::LimitsType&, bool = false); void clear(); void set(size_t); - MainThread* main() const { return static_cast(front()); } + MainThread* main() const { return static_cast(threads.front()); } uint64_t nodes_searched() const { return accumulate(&Thread::nodes); } uint64_t tb_hits() const { return accumulate(&Thread::tbHits); } Thread* get_best_thread() const; @@ -116,13 +116,21 @@ struct ThreadPool : public std::vector { std::atomic_bool stop, increaseDepth; + auto cbegin() const noexcept { return threads.cbegin(); } + auto begin() noexcept { return threads.begin(); } + auto end() noexcept { return threads.end(); } + auto cend() const noexcept { return threads.cend(); } + auto size() const noexcept { return threads.size(); } + auto empty() const noexcept { return threads.empty(); } + private: StateListPtr setupStates; + std::vector threads; uint64_t accumulate(std::atomic Thread::* member) const { uint64_t sum = 0; - for (Thread* th : *this) + for (Thread* th : threads) sum += (th->*member).load(std::memory_order_relaxed); return sum; } diff --git a/src/thread_win32_osx.h b/src/thread_win32_osx.h index 01ff1c77afa..330a8341dd8 100644 --- a/src/thread_win32_osx.h +++ b/src/thread_win32_osx.h @@ -41,7 +41,7 @@ void* start_routine(void* ptr) P* p = reinterpret_cast(ptr); (p->first->*(p->second))(); // Call member function pointer delete p; - return NULL; + return nullptr; } class NativeThread { @@ -56,7 +56,7 @@ class NativeThread { pthread_attr_setstacksize(attr, TH_STACK_SIZE); pthread_create(&thread, attr, start_routine, new P(obj, fun)); } - void join() { pthread_join(thread, NULL); } + void join() { pthread_join(thread, nullptr); } }; } // namespace Stockfish @@ -65,7 +65,7 @@ class NativeThread { namespace Stockfish { -typedef std::thread NativeThread; +using NativeThread = std::thread; } // namespace Stockfish From e9e7a7b83f78b5c7d29f69083589b449d9b52390 Mon Sep 17 00:00:00 2001 From: Sebastian Buchwald Date: Sat, 7 Jan 2023 14:53:59 +0100 Subject: [PATCH 0947/1766] Replace some std::string occurrences with std::string_view std::string_view is more lightweight than std::string. Furthermore, std::string_view variables can be declared constexpr. closes https://github.com/official-stockfish/Stockfish/pull/4328 No functional change --- src/misc.cpp | 9 +++++---- src/nnue/evaluate_nnue.cpp | 7 ++++--- src/position.cpp | 3 ++- src/syzygy/tbprobe.cpp | 5 +++-- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index be7abba5d01..b651972bba1 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -41,12 +41,13 @@ typedef WORD(*fun5_t)(); } #endif +#include #include #include #include #include +#include #include -#include #if defined(__linux__) && !defined(__ANDROID__) #include @@ -68,7 +69,7 @@ namespace Stockfish { namespace { /// Version number or dev. -const string version = "dev"; +constexpr string_view version = "dev"; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We @@ -151,13 +152,13 @@ string engine_info(bool to_uci) { stringstream ss; ss << "Stockfish " << version << setfill('0'); - if (version == "dev") + if constexpr (version == "dev") { ss << "-"; #ifdef GIT_DATE ss << GIT_DATE; #else - const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); + constexpr string_view months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); string month, day, year; stringstream date(__DATE__); // From compiler, format is "Sep 21 2008" diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 06281da06a5..f132de71385 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -18,11 +18,12 @@ // Code for calculating NNUE evaluation function +#include +#include #include #include #include -#include -#include +#include #include "../evaluate.h" #include "../position.h" @@ -210,7 +211,7 @@ namespace Stockfish::Eval::NNUE { return t; } - static const std::string PieceToChar(" PNBRQK pnbrqk"); + constexpr std::string_view PieceToChar(" PNBRQK pnbrqk"); // format_cp_compact() converts a Value into (centi)pawns and writes it in a buffer. diff --git a/src/position.cpp b/src/position.cpp index e82425af7c4..cfd98f686e0 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -22,6 +22,7 @@ #include // For std::memset, std::memcmp #include #include +#include #include "bitboard.h" #include "misc.h" @@ -46,7 +47,7 @@ namespace Zobrist { namespace { -const string PieceToChar(" PNBRQK pnbrqk"); +constexpr std::string_view PieceToChar(" PNBRQK pnbrqk"); constexpr Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING }; diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index cc5e2852a09..bbfd819d3fa 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -24,9 +24,10 @@ #include #include #include +#include #include +#include #include -#include #include "../bitboard.h" #include "../movegen.h" @@ -70,7 +71,7 @@ enum TBFlag { STM = 1, Mapped = 2, WinPlies = 4, LossPlies = 8, Wide = 16, Singl inline WDLScore operator-(WDLScore d) { return WDLScore(-int(d)); } inline Square operator^(Square s, int i) { return Square(int(s) ^ i); } -const std::string PieceToChar = " PNBRQK pnbrqk"; +constexpr std::string_view PieceToChar = " PNBRQK pnbrqk"; int MapPawns[SQUARE_NB]; int MapB1H1H7[SQUARE_NB]; From 4f4e652ecaf8d42a9bd1092f72c3704435ddba12 Mon Sep 17 00:00:00 2001 From: Sebastian Buchwald Date: Fri, 6 Jan 2023 18:13:21 +0100 Subject: [PATCH 0948/1766] Avoid unnecessary string copies closes https://github.com/official-stockfish/Stockfish/pull/4326 also fixes typo, closes https://github.com/official-stockfish/Stockfish/pull/4332 No functional change --- src/evaluate.cpp | 2 +- src/nnue/layers/sqr_clipped_relu.h | 2 +- src/uci.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 3aae9f1c6ec..6db977a4c7e 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -89,7 +89,7 @@ namespace Eval { vector dirs = { "" , "" , CommandLine::binaryDirectory }; #endif - for (string directory : dirs) + for (const string& directory : dirs) if (currentEvalFileName != eval_file) { if (directory != "") diff --git a/src/nnue/layers/sqr_clipped_relu.h b/src/nnue/layers/sqr_clipped_relu.h index df539b39164..3fbb243cfd6 100644 --- a/src/nnue/layers/sqr_clipped_relu.h +++ b/src/nnue/layers/sqr_clipped_relu.h @@ -106,7 +106,7 @@ namespace Stockfish::Eval::NNUE::Layers { for (IndexType i = Start; i < InputDimensions; ++i) { output[i] = static_cast( - // realy should be /127 but we need to make it fast + // really should be /127 but we need to make it fast // needs to be accounted for in the trainer std::max(0ll, std::min(127ll, (((long long)input[i] * input[i]) >> (2 * WeightScaleBits)) / 128))); } diff --git a/src/uci.cpp b/src/uci.cpp index eb158e723e4..30c1fa0cce0 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -160,7 +160,7 @@ namespace { uint64_t num, nodes = 0, cnt = 1; vector list = setup_bench(pos, args); - num = count_if(list.begin(), list.end(), [](string s) { return s.find("go ") == 0 || s.find("eval") == 0; }); + num = count_if(list.begin(), list.end(), [](const string& s) { return s.find("go ") == 0 || s.find("eval") == 0; }); TimePoint elapsed = now(); From 3d2381d76d7bf9686ef0e0671f60c3b885a7058a Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Wed, 11 Jan 2023 10:07:56 -0500 Subject: [PATCH 0949/1766] Update default net to nn-1e7ca356472e.nnue Created by retraining the master net on a dataset composed of: * The Leela-dfrc_n5000.binpack dataset filtered with depth6 multipv2 search to remove positions with only one good move, in addition to removing positions where either of the two best moves are captures * The same Leela T80 oct+nov 2022 training data used in recent best datasets * Additional Leela training data from T60 nov+dec 2021 and T79 apr+may 2022 Trained with end lambda 0.7 and started with max epoch 800. All positions with ply <= 28 were skipped: ``` python easy_train.py \ --experiment-name leela95-dfrc96-mpv-eval-fonly-T80octnov-T79aprmayT60novdec-12tb7p-sk28-lambda7 \ --training-dataset /data/leela95-dfrc96-mpv-eval-fonly-T80octnov-T79aprmayT60novdec-12tb7p.binpack \ --nnue-pytorch-branch linrock/nnue-pytorch/misc-fixes-skip-ply-lteq-28 \ --start-from-engine-test-net True \ --gpus "0," \ --start-lambda 1.0 \ --end-lambda 0.7 \ --gamma 0.995 \ --lr 4.375e-4 \ --tui False \ --seed $RANDOM \ --max_epoch 800 ``` Around epoch 780, training was manually paused and max epoch increased to 920 before resuming. During depth6 multipv2 data filtering, positions were considered to have only one good move if the score of the best move was significantly better than the 2nd best move in a way that changes the outcome of the game: * the best move leads to a significant advantage while the 2nd best move equalizes or loses * the best move is about equal while the 2nd best move loses The modified stockfish branch and exact score thresholds used for filtering are at: https://github.com/linrock/Stockfish/tree/tools-filter-multipv2-eval-diff/src/filter About 95% of the Leela portion and 96% of the DFRC portion of the Leela-dfrc_n5000.binpack dataset was filtered. Unfiltered parts of the dataset were left out. The additional Leela training data from T60 nov+dec 2021 and T79 apr+may 2022 was WDL-rescored with about 12TB of syzygy 7-piece tablebases where the material difference is less than around 6 pawns. Best moves were exported to .plain data files during data conversion with the lc0 rescorer. The exact training data can be found at: https://robotmoon.com/nnue-training-data/ Local elo at 25k nodes per move experiment_leela95-dfrc96-mpv-eval-fonly-T80octnov-T79aprmayT60novdec-12tb7p-sk28-lambda7 run_0/nn-epoch899.nnue : 3.8 +/- 1.6 Passed STC https://tests.stockfishchess.org/tests/view/63bed1f540aa064159b9c89b LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 103344 W: 27392 L: 26991 D: 48961 Ptnml(0-2): 333, 11223, 28099, 11744, 273 Passed LTC https://tests.stockfishchess.org/tests/view/63c010415705810de2deb3ec LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 21712 W: 5891 L: 5619 D: 10202 Ptnml(0-2): 12, 2022, 6511, 2304, 7 closes https://github.com/official-stockfish/Stockfish/pull/4338 bench 4106793 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 0a00faa7846..643e85403a2 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-a3dc078bafc7.nnue" + #define EvalFileDefaultName "nn-1e7ca356472e.nnue" namespace NNUE { From da5bcec481dc22ad2de2e2c8e5c6e8304a373445 Mon Sep 17 00:00:00 2001 From: Sebastian Buchwald Date: Sat, 14 Jan 2023 16:09:31 +0100 Subject: [PATCH 0950/1766] Fix asm modifiers in add_dpbusd_epi32x2 implementations The accumulator should be an earlyclobber because it is written before all input operands are read. Otherwise, the asm code computes a wrong result if the accumulator shares a register with one of the other input operands (which happens if we pass in the same expression for the accumulator and the operand). Closes https://github.com/official-stockfish/Stockfish/pull/4339 No functional change --- src/nnue/layers/simd.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nnue/layers/simd.h b/src/nnue/layers/simd.h index aeab39c4fc3..231f7891216 100644 --- a/src/nnue/layers/simd.h +++ b/src/nnue/layers/simd.h @@ -153,7 +153,7 @@ namespace Stockfish::Simd { asm( "vpdpbusd %[b0], %[a0], %[acc]\n\t" "vpdpbusd %[b1], %[a1], %[acc]\n\t" - : [acc]"+v"(acc) + : [acc]"+&v"(acc) : [a0]"v"(a0), [b0]"vm"(b0), [a1]"v"(a1), [b1]"vm"(b1) ); # else @@ -249,7 +249,7 @@ namespace Stockfish::Simd { asm( VNNI_PREFIX "vpdpbusd %[b0], %[a0], %[acc]\n\t" VNNI_PREFIX "vpdpbusd %[b1], %[a1], %[acc]\n\t" - : [acc]"+v"(acc) + : [acc]"+&v"(acc) : [a0]"v"(a0), [b0]"vm"(b0), [a1]"v"(a1), [b1]"vm"(b1) ); # else From a08b8d4e9711c20acedbfe17d618c3c384b339ec Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 15 Jan 2023 00:23:24 +0100 Subject: [PATCH 0951/1766] Update UCI_Elo parameterization The old parameterization (https://github.com/official-stockfish/Stockfish/pull/2225/files) has now become quite inaccurate. This updates the formula based on updated results with master. The formula is based on a fit of the Elo results for games played between master at various skill levels, and various versions of the Stash engine, which have been ranked at CCRL. ``` # PLAYER : RATING ERROR POINTS PLAYED (%) 1 master-skill-19 : 3191.1 40.4 940.0 1707 55 2 master-skill-18 : 3170.3 39.3 1343.0 2519 53 3 master-skill-17 : 3141.3 37.8 2282.0 4422 52 4 master-skill-16 : 3111.2 37.1 2773.0 5423 51 5 master-skill-15 : 3069.5 37.2 2728.5 5386 51 6 master-skill-14 : 3024.8 36.1 2702.0 5339 51 7 master-skill-13 : 2972.9 35.4 2645.5 5263 50 8 master-skill-12 : 2923.1 35.0 2653.5 5165 51 9 master-skill-11 : 2855.5 33.6 2524.0 5081 50 10 master-skill-10 : 2788.3 32.0 2724.5 5511 49 11 stash-bot-v25.0 : 2744.0 31.5 1952.5 3840 51 12 master-skill-9 : 2702.8 30.5 2670.0 5018 53 13 master-skill-8 : 2596.2 28.5 2669.5 4975 54 14 stash-bot-v21.0 : 2561.2 30.0 1338.0 3366 40 15 master-skill-7 : 2499.5 28.5 1934.0 4178 46 16 stash-bot-v20.0 : 2452.6 27.7 1606.5 3378 48 17 stash-bot-v19.0 : 2425.3 26.7 1787.0 3365 53 18 master-skill-6 : 2363.2 26.4 2510.5 4379 57 19 stash-bot-v17.0 : 2280.7 25.4 2209.0 4378 50 20 master-skill-5 : 2203.7 25.3 2859.5 5422 53 21 stash-bot-v15.3 : 2200.0 25.4 1757.0 4383 40 22 stash-bot-v14 : 2145.9 25.5 2890.0 5167 56 23 stash-bot-v13 : 2042.7 25.8 2263.5 4363 52 24 stash-bot-v12 : 1963.4 25.8 1769.5 4210 42 25 master-skill-4 : 1922.9 25.9 2690.0 5399 50 26 stash-bot-v11 : 1873.0 26.3 2203.5 4335 51 27 stash-bot-v10 : 1783.8 27.8 2568.5 4301 60 28 master-skill-3 : 1742.3 27.8 1909.5 4439 43 29 master-skill-2 : 1608.4 29.4 2064.5 4389 47 30 stash-bot-v9 : 1582.6 30.2 2130.0 4230 50 31 master-skill-1 : 1467.6 31.3 2015.5 4244 47 32 stash-bot-v8 : 1452.8 31.5 1953.5 3780 52 33 master-skill-0 : 1320.1 32.9 651.5 2083 31 ``` Skill 0 .. 19, now covers CCRL Blitz Elo from 1320 to 3190, approximately. Indeed, the Elo of stash in this analysis is only to within +- 100 Elo of CCRL, probably because it depends quite a bit on the opponent pool. To obtain a skill level for a given Elo number, the above data is fit as a 3rd degree polynomial Skill(Elo). A quick test confirms the correspondence to the above table: ``` Score of master-elo-2721 vs stash-bot-v21.0: 51 - 16 - 19 [0.703] 86 Elo difference: 150.1 +/- 70.2, LOS: 100.0 %, DrawRatio: 22.1 % ``` closes https://github.com/official-stockfish/Stockfish/pull/4341 No functional change. --- README.md | 2 +- src/search.cpp | 5 ++++- src/ucioption.cpp | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4cd5968e4be..ca90d5d4402 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ change them via a chess GUI. This is a list of available UCI options in Stockfis * #### 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. + This Elo rating has been calibrated at a time control of 120s+1.0s and anchored to +- 100 Elo to CCRL Blitz. * #### Skill Level Lower the Skill Level in order to make Stockfish play weaker (see also UCI_LimitStrength). diff --git a/src/search.cpp b/src/search.cpp index f5eeb23b43f..346fd6c3a7a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -96,7 +96,10 @@ namespace { struct Skill { Skill(int skill_level, int uci_elo) { if (uci_elo) - level = std::clamp(std::pow((uci_elo - 1346.6) / 143.4, 1 / 0.806), 0.0, 20.0); + { + double e = double(uci_elo - 1320) / (3190 - 1320); + level = std::clamp((((37.2473 * e - 40.8525) * e + 22.2943) * e - 0.311438), 0.0, 19.0); + } else level = double(skill_level); } diff --git a/src/ucioption.cpp b/src/ucioption.cpp index b4ce70b4ed2..39933ea5e8d 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -73,7 +73,7 @@ void init(OptionsMap& o) { o["UCI_Chess960"] << Option(false); o["UCI_AnalyseMode"] << Option(false); o["UCI_LimitStrength"] << Option(false); - o["UCI_Elo"] << Option(1350, 1350, 2850); + o["UCI_Elo"] << Option(1320, 1320, 3190); o["UCI_ShowWDL"] << Option(false); o["SyzygyPath"] << Option("", on_tb_path); o["SyzygyProbeDepth"] << Option(1, 1, 100); From 734315ff3099564c80737ad2be8121008870d28b Mon Sep 17 00:00:00 2001 From: Stephen Touset Date: Mon, 16 Jan 2023 14:25:47 -0800 Subject: [PATCH 0952/1766] Remove precomputed SquareBB Bit-shifting is a single instruction, and should be faster than an array lookup on supported architectures. Besides (ever so slightly) speeding up the conversion of a square into a bitboard, we may see minor general performance improvements due to preserving more of the CPU's existing cache. passed STC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 47280 W: 12469 L: 12271 D: 22540 Ptnml(0-2): 128, 4893, 13402, 5087, 130 https://tests.stockfishchess.org/tests/view/63c5cfe618c20f4929c5fe46 Small speedup locally: ``` Result of 20 runs ================== base (./stockfish.master ) = 1752135 +/- 10943 test (./stockfish.patch ) = 1763939 +/- 10818 diff = +11804 +/- 4731 speedup = +0.0067 P(speedup > 0) = 1.0000 CPU: 16 x AMD Ryzen 9 3950X 16-Core Processor ``` Closes https://github.com/official-stockfish/Stockfish/pull/4343 Bench: 4106793 --- src/bitboard.cpp | 4 ---- src/bitboard.h | 3 +-- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 0ed13fd0d24..fd5c3c22536 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -27,7 +27,6 @@ namespace Stockfish { uint8_t PopCnt16[1 << 16]; uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; -Bitboard SquareBB[SQUARE_NB]; Bitboard LineBB[SQUARE_NB][SQUARE_NB]; Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; @@ -82,9 +81,6 @@ void Bitboards::init() { for (unsigned i = 0; i < (1 << 16); ++i) PopCnt16[i] = uint8_t(std::bitset<16>(i).count()); - for (Square s = SQ_A1; s <= SQ_H8; ++s) - SquareBB[s] = (1ULL << s); - for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) SquareDistance[s1][s2] = std::max(distance(s1, s2), distance(s1, s2)); diff --git a/src/bitboard.h b/src/bitboard.h index d4485fcbefc..42fd0e97ec6 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -74,7 +74,6 @@ constexpr Bitboard KingFlank[FILE_NB] = { extern uint8_t PopCnt16[1 << 16]; extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; -extern Bitboard SquareBB[SQUARE_NB]; extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; extern Bitboard LineBB[SQUARE_NB][SQUARE_NB]; extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; @@ -108,7 +107,7 @@ extern Magic BishopMagics[SQUARE_NB]; inline Bitboard square_bb(Square s) { assert(is_ok(s)); - return SquareBB[s]; + return (1ULL << s); } From a2038c1a011552c3d56d3b318780f7f5eadaf05d Mon Sep 17 00:00:00 2001 From: Jonathan Date: Tue, 17 Jan 2023 21:30:50 -0700 Subject: [PATCH 0953/1766] apply if constexpr to additional instances as a form of documentation, and a hint to the compiler. closes https://github.com/official-stockfish/Stockfish/pull/4345 No functional change --- AUTHORS | 1 + src/evaluate.cpp | 10 +++++----- src/movegen.cpp | 16 ++++++++-------- src/movepick.cpp | 2 +- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/AUTHORS b/AUTHORS index 87fed7b80c7..42ba5930343 100644 --- a/AUTHORS +++ b/AUTHORS @@ -101,6 +101,7 @@ Jerry Donald Watson (jerrydonaldwatson) jjoshua2 Jonathan Calovski (Mysseno) Jonathan Buladas Dumale (SFisGOD) +Jonathan McDermid (jonathanmcdermid) Joost VandeVondele (vondele) Jörg Oster (joergoster) Joseph Ellis (jhellis3) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 6db977a4c7e..8683182c71d 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -388,10 +388,10 @@ namespace { template template Score Evaluation::pieces() { - constexpr Color Them = ~Us; - constexpr Direction Down = -pawn_push(Us); - constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB - : Rank5BB | Rank4BB | Rank3BB); + constexpr Color Them = ~Us; + [[maybe_unused]] constexpr Direction Down = -pawn_push(Us); + [[maybe_unused]] constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB + : Rank5BB | Rank4BB | Rank3BB); Bitboard b1 = pos.pieces(Us, Pt); Bitboard b, bb; Score score = SCORE_ZERO; @@ -430,7 +430,7 @@ namespace { int mob = popcount(b & mobilityArea[Us]); mobility[Us] += MobilityBonus[Pt - 2][mob]; - if (Pt == BISHOP || Pt == KNIGHT) + if constexpr (Pt == BISHOP || Pt == KNIGHT) { // Bonus if the piece is on an outpost square or can reach one // Bonus for knights (UncontestedOutpost) if few relevant targets diff --git a/src/movegen.cpp b/src/movegen.cpp index a960f8630e5..471541644a0 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -26,12 +26,12 @@ namespace Stockfish { namespace { template - ExtMove* make_promotions(ExtMove* moveList, Square to) { + ExtMove* make_promotions(ExtMove* moveList, [[maybe_unused]] Square to) { - if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) + if constexpr (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) *moveList++ = make(to - D, to, QUEEN); - if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS) + if constexpr (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS) { *moveList++ = make(to - D, to, ROOK); *moveList++ = make(to - D, to, BISHOP); @@ -60,18 +60,18 @@ namespace { Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB; // Single and double pawn pushes, no promotions - if (Type != CAPTURES) + if constexpr (Type != CAPTURES) { Bitboard b1 = shift(pawnsNotOn7) & emptySquares; Bitboard b2 = shift(b1 & TRank3BB) & emptySquares; - if (Type == EVASIONS) // Consider only blocking squares + if constexpr (Type == EVASIONS) // Consider only blocking squares { b1 &= target; b2 &= target; } - if (Type == QUIET_CHECKS) + if constexpr (Type == QUIET_CHECKS) { // To make a quiet check, you either make a direct check by pushing a pawn // or push a blocker pawn that is not on the same file as the enemy king. @@ -102,7 +102,7 @@ namespace { Bitboard b2 = shift(pawnsOn7) & enemies; Bitboard b3 = shift(pawnsOn7) & emptySquares; - if (Type == EVASIONS) + if constexpr (Type == EVASIONS) b3 &= target; while (b1) @@ -116,7 +116,7 @@ namespace { } // Standard and en passant captures - if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) + if constexpr (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) { Bitboard b1 = shift(pawnsNotOn7) & enemies; Bitboard b2 = shift(pawnsNotOn7) & enemies; diff --git a/src/movepick.cpp b/src/movepick.cpp index dbe67357d60..65155a73f79 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -158,7 +158,7 @@ Move MovePicker::select(Pred filter) { while (cur < endMoves) { - if (T == Best) + if constexpr (T == Best) std::swap(*cur, *std::max_element(cur, endMoves)); if (*cur != ttMove && filter()) From 596a528c6a9ace6fb1a8407c86d972d96653418d Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Fri, 20 Jan 2023 10:41:46 -0500 Subject: [PATCH 0954/1766] Update default net to nn-bc24c101ada0.nnue Created by retraining the master net with Leela T78 data from Aug+Sep 2022 added to the previous best dataset. Trained with end lambda 0.7 and started with max epoch 800. All positions with ply <= 28 were skipped: ``` python easy_train.py \ --experiment-name leela95-dfrc96-filt-only-T80octnov-T60novdecT78augsepT79aprmay-12tb7p-sk28-lambda7 \ --training-dataset /data/leela95-dfrc96-filt-only-T80octnov-T60novdecT78augsepT79aprmay-12tb7p.binpack \ --nnue-pytorch-branch linrock/nnue-pytorch/misc-fixes-skip-ply-lteq-28 \ --start-from-engine-test-net True \ --gpus "0," \ --start-lambda 1.0 \ --end-lambda 0.7 \ --gamma 0.995 \ --lr 4.375e-4 \ --tui False \ --seed $RANDOM \ --max_epoch 800 ``` Around epoch 750, training was manually paused and max epoch increased to 950 before resuming. The additional Leela training data from T78 was prepared in the same way as the previous best dataset. The exact training data used can be found at: https://robotmoon.com/nnue-training-data/ While the local elo ratings during this experiment were much lower than in recent master nets, several later epochs had a consistent elo above zero, and this was hypothesized to represent potential strength at slower time controls. Local elo at 25k nodes per move leela95-dfrc96-filt-only-T80octnov-T60novdecT78augsepT79aprmay-12tb7p-sk28-lambda7 nn-epoch819.nnue : 0.4 +/- 1.1 (nn-bc24c101ada0.nnue) nn-epoch799.nnue : 0.3 +/- 1.2 nn-epoch759.nnue : 0.3 +/- 1.1 nn-epoch839.nnue : 0.2 +/- 1.4 Passed STC https://tests.stockfishchess.org/tests/view/63cabf6f0eefe8694a0c6013 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 41608 W: 11161 L: 10848 D: 19599 Ptnml(0-2): 116, 4496, 11281, 4781, 130 Passed LTC https://tests.stockfishchess.org/tests/view/63cb1856344bb01c191af263 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 76760 W: 20517 L: 20137 D: 36106 Ptnml(0-2): 34, 7435, 23070, 7799, 42 closes https://github.com/official-stockfish/Stockfish/pull/4351 bench 3941848 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 643e85403a2..f7ecaac93de 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-1e7ca356472e.nnue" + #define EvalFileDefaultName "nn-bc24c101ada0.nnue" namespace NNUE { From 3dd0a7a7cd9daf306cffc795c343459de6d6f54b Mon Sep 17 00:00:00 2001 From: Dubslow Date: Fri, 20 Jan 2023 03:02:25 -0600 Subject: [PATCH 0955/1766] `stat_bonus`: replace quadratic with nearly identical line passed stc: https://tests.stockfishchess.org/tests/view/63ca58c90eefe8694a0c4eac LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 287960 W: 76146 L: 76201 D: 135613 Ptnml(0-2): 947, 31890, 78307, 31943, 893 passed ltc: https://tests.stockfishchess.org/tests/view/63cc8a51344bb01c191b30f0 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 73784 W: 19559 L: 19402 D: 34823 Ptnml(0-2): 33, 7171, 22327, 7328, 33 closes https://github.com/official-stockfish/Stockfish/pull/4352 bench 3990490 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 346fd6c3a7a..b9ba3811e83 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -81,7 +81,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return std::min((11 * d + 284) * d - 363 , 1650); + return std::min(350 * d - 400, 1650); } // Add a small random component to draw evaluations to avoid 3-fold blindness From d3860f8d5efa4d9df726fa605b24689d6a829c7e Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Wed, 25 Jan 2023 08:12:40 +0300 Subject: [PATCH 0956/1766] Rebalance usage of history heuristics in pruning This patch has multiple effects: * history heuristics sum in futility pruning now can't exceed some negative value so futility pruning for moves with negative histories should become slightly less aggressive; * history heuristics are now used in SEE pruning for quiet moves; Passed STC: https://tests.stockfishchess.org/tests/view/63cde339c93e8828d0f02e3a LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 88424 W: 23681 L: 23303 D: 41440 Ptnml(0-2): 258, 9559, 24219, 9899, 277 Passed LTC: https://tests.stockfishchess.org/tests/view/63ce9009c93e8828d0f04e4f LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 79536 W: 21223 L: 20843 D: 37470 Ptnml(0-2): 22, 7599, 24146, 7979, 22 closes https://github.com/official-stockfish/Stockfish/pull/4355 Bench: 4208265 --- src/search.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index b9ba3811e83..4b2deaddd49 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1026,12 +1026,17 @@ namespace { history += 2 * thisThread->mainHistory[us][from_to(move)]; + lmrDepth += history / 7208; + lmrDepth = std::max(lmrDepth, -2); + // Futility pruning: parent node (~13 Elo) if ( !ss->inCheck && lmrDepth < 13 - && ss->staticEval + 103 + 136 * lmrDepth + history / 53 <= alpha) + && ss->staticEval + 103 + 136 * lmrDepth <= alpha) continue; + lmrDepth = std::max(lmrDepth, 0); + // Prune moves with negative SEE (~4 Elo) if (!pos.see_ge(move, Value(-25 * lmrDepth * lmrDepth - 16 * lmrDepth))) continue; From def296670dbde6cfad446a735f37f25cfe9df6a2 Mon Sep 17 00:00:00 2001 From: disservin Date: Mon, 23 Jan 2023 19:32:26 +0100 Subject: [PATCH 0957/1766] Fixed UCI TB win values This patch results in search values for a TB win/loss to be reported in a way that does not change with normalization, i.e. will be consistent over time. A value of 200.00 pawns is now reported upon entering a TB won position. Values smaller than 200.00 relate to the distance in plies from the root to the probed position position, with 1 cp being 1 ply distance. closes https://github.com/official-stockfish/Stockfish/pull/4353 No functional change --- src/uci.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/uci.cpp b/src/uci.cpp index 30c1fa0cce0..2afd2de7300 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -314,8 +314,13 @@ string UCI::value(Value v) { stringstream ss; - if (abs(v) < VALUE_MATE_IN_MAX_PLY) + if (abs(v) < VALUE_TB_WIN_IN_MAX_PLY) ss << "cp " << v * 100 / NormalizeToPawnValue; + else if (abs(v) < VALUE_MATE_IN_MAX_PLY) + { + const int ply = VALUE_MATE_IN_MAX_PLY - 1 - std::abs(v); // recompute ss->ply + ss << "cp " << (v > 0 ? 20000 - ply : -20000 + ply); + } else ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; From 2167942b6eab54bafb6aed96d7360c74fec95358 Mon Sep 17 00:00:00 2001 From: Sebastian Buchwald Date: Wed, 25 Jan 2023 22:25:18 +0100 Subject: [PATCH 0958/1766] Simplify functions to read/write network parameters closes https://github.com/official-stockfish/Stockfish/pull/4358 No functional change --- src/nnue/nnue_architecture.h | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index c43a23c3f69..508f3aae0a7 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -72,22 +72,20 @@ struct Network // Read network parameters bool read_parameters(std::istream& stream) { - if (!fc_0.read_parameters(stream)) return false; - if (!ac_0.read_parameters(stream)) return false; - if (!fc_1.read_parameters(stream)) return false; - if (!ac_1.read_parameters(stream)) return false; - if (!fc_2.read_parameters(stream)) return false; - return true; + return fc_0.read_parameters(stream) + && ac_0.read_parameters(stream) + && fc_1.read_parameters(stream) + && ac_1.read_parameters(stream) + && fc_2.read_parameters(stream); } - // Read network parameters + // Write network parameters bool write_parameters(std::ostream& stream) const { - if (!fc_0.write_parameters(stream)) return false; - if (!ac_0.write_parameters(stream)) return false; - if (!fc_1.write_parameters(stream)) return false; - if (!ac_1.write_parameters(stream)) return false; - if (!fc_2.write_parameters(stream)) return false; - return true; + return fc_0.write_parameters(stream) + && ac_0.write_parameters(stream) + && fc_1.write_parameters(stream) + && ac_1.write_parameters(stream) + && fc_2.write_parameters(stream); } std::int32_t propagate(const TransformedFeatureType* transformedFeatures) From 2f67409506e65a47f038055de834462b4a707ccd Mon Sep 17 00:00:00 2001 From: Sebastian Buchwald Date: Wed, 25 Jan 2023 22:04:02 +0100 Subject: [PATCH 0959/1766] Remove redundant const qualifiers The const qualifiers are already implied by the constexpr qualifiers. closes https://github.com/official-stockfish/Stockfish/pull/4359 No functional change --- src/nnue/layers/affine_transform.h | 36 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 710ab8a7d9d..363b4916e37 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -179,33 +179,33 @@ namespace Stockfish::Eval::NNUE::Layers { static_assert(PaddedInputDimensions >= LargeInputSize, "Something went wrong. This specialization should not have been chosen."); #if defined (USE_AVX512) - static constexpr const IndexType InputSimdWidth = 64; - static constexpr const IndexType MaxNumOutputRegs = 16; + static constexpr IndexType InputSimdWidth = 64; + static constexpr IndexType MaxNumOutputRegs = 16; #elif defined (USE_AVX2) - static constexpr const IndexType InputSimdWidth = 32; - static constexpr const IndexType MaxNumOutputRegs = 8; + static constexpr IndexType InputSimdWidth = 32; + static constexpr IndexType MaxNumOutputRegs = 8; #elif defined (USE_SSSE3) - static constexpr const IndexType InputSimdWidth = 16; - static constexpr const IndexType MaxNumOutputRegs = 8; + static constexpr IndexType InputSimdWidth = 16; + static constexpr IndexType MaxNumOutputRegs = 8; #elif defined (USE_NEON) - static constexpr const IndexType InputSimdWidth = 8; - static constexpr const IndexType MaxNumOutputRegs = 8; + static constexpr IndexType InputSimdWidth = 8; + static constexpr IndexType MaxNumOutputRegs = 8; #else // The fallback implementation will not have permuted weights. // We define these to avoid a lot of ifdefs later. - static constexpr const IndexType InputSimdWidth = 1; - static constexpr const IndexType MaxNumOutputRegs = 1; + static constexpr IndexType InputSimdWidth = 1; + static constexpr IndexType MaxNumOutputRegs = 1; #endif // A big block is a region in the weight matrix of the size [PaddedInputDimensions, NumOutputRegs]. // A small block is a region of size [InputSimdWidth, 1] - static constexpr const IndexType NumOutputRegs = std::min(MaxNumOutputRegs, OutputDimensions); - static constexpr const IndexType SmallBlockSize = InputSimdWidth; - static constexpr const IndexType BigBlockSize = NumOutputRegs * PaddedInputDimensions; - static constexpr const IndexType NumSmallBlocksInBigBlock = BigBlockSize / SmallBlockSize; - static constexpr const IndexType NumSmallBlocksPerOutput = PaddedInputDimensions / SmallBlockSize; - static constexpr const IndexType NumBigBlocks = OutputDimensions / NumOutputRegs; + static constexpr IndexType NumOutputRegs = std::min(MaxNumOutputRegs, OutputDimensions); + static constexpr IndexType SmallBlockSize = InputSimdWidth; + static constexpr IndexType BigBlockSize = NumOutputRegs * PaddedInputDimensions; + static constexpr IndexType NumSmallBlocksInBigBlock = BigBlockSize / SmallBlockSize; + static constexpr IndexType NumSmallBlocksPerOutput = PaddedInputDimensions / SmallBlockSize; + static constexpr IndexType NumBigBlocks = OutputDimensions / NumOutputRegs; static_assert(OutputDimensions % NumOutputRegs == 0); @@ -396,8 +396,8 @@ namespace Stockfish::Eval::NNUE::Layers { static_assert(PaddedInputDimensions < LargeInputSize, "Something went wrong. This specialization should not have been chosen."); #if defined (USE_SSSE3) - static constexpr const IndexType OutputSimdWidth = SimdWidth / 4; - static constexpr const IndexType InputSimdWidth = SimdWidth; + static constexpr IndexType OutputSimdWidth = SimdWidth / 4; + static constexpr IndexType InputSimdWidth = SimdWidth; #endif // Hash value embedded in the evaluation file From d4d1cec29631f041adeec98adc5893b5c6a54969 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Sun, 15 Jan 2023 04:08:33 -0600 Subject: [PATCH 0960/1766] Remove `previousDepth` in favor of `completedDepth + 2` Beyond the simplification, this could be considered a bugfix from a certain point of view. However, the effect is very subtle and essentially impossible for users to notice. 5372f81cc8 added about 2 Elo at LTC, but only for second and later `go` commands; now, with this patch, the first `go` command will also benefit from that gain. Games under time controls are unaffected (as per the tests). STC: https://tests.stockfishchess.org/tests/view/63c3d291330c0d3d051d48a8 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 473792 W: 124858 L: 125104 D: 223830 Ptnml(0-2): 1338, 49653, 135063, 49601, 1241 LTC: https://tests.stockfishchess.org/tests/view/63c8cd56a83c702aac083bc9 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 290728 W: 76926 L: 76978 D: 136824 Ptnml(0-2): 106, 27987, 89221, 27953, 97 closes https://github.com/official-stockfish/Stockfish/pull/4361 bench 4208265 --- src/search.cpp | 5 +---- src/thread.cpp | 1 - src/thread.h | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 4b2deaddd49..c748f1ff425 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -242,9 +242,6 @@ void MainThread::search() { bestPreviousScore = bestThread->rootMoves[0].score; bestPreviousAverageScore = bestThread->rootMoves[0].averageScore; - for (Thread* th : Threads) - th->previousDepth = bestThread->completedDepth; - // Send again PV info if we have a new best thread if (bestThread != this) sync_cout << UCI::pv(bestThread->rootPos, bestThread->completedDepth) << sync_endl; @@ -1053,7 +1050,7 @@ namespace { // 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 ( !rootNode - && depth >= 4 - (thisThread->previousDepth > 24) + 2 * (PvNode && tte->is_pv()) + && depth >= 4 - (thisThread->completedDepth > 22) + 2 * (PvNode && tte->is_pv()) && move == ttMove && !excludedMove // Avoid recursive singular search /* && ttValue != VALUE_NONE Already implicit in the next condition */ diff --git a/src/thread.cpp b/src/thread.cpp index ca1a7c85273..c680393e277 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -60,7 +60,6 @@ void Thread::clear() { counterMoves.fill(MOVE_NONE); mainHistory.fill(0); captureHistory.fill(0); - previousDepth = 0; for (bool inCheck : { false, true }) for (StatsType c : { NoCaptures, Captures }) diff --git a/src/thread.h b/src/thread.h index 7566322c54c..46cdb11c36a 100644 --- a/src/thread.h +++ b/src/thread.h @@ -69,7 +69,7 @@ class Thread { Position rootPos; StateInfo rootState; Search::RootMoves rootMoves; - Depth rootDepth, completedDepth, previousDepth; + Depth rootDepth, completedDepth; Value rootDelta; CounterMoveHistory counterMoves; ButterflyHistory mainHistory; From e4e61cd9cc953e6bc17070da84639d53b7514709 Mon Sep 17 00:00:00 2001 From: MinetaS Date: Thu, 19 Jan 2023 09:49:42 +0900 Subject: [PATCH 0961/1766] Remove maxNextDepth This patch allows full PV search to have double extensions as well when extension == 1 && doDeeperSearch && doEvenDeeperSearch && !doShallowerSearch is true, which is extremely rare to occur. Passed non-regression STC (master 3d2381d): LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 370824 W: 97835 L: 97974 D: 175015 Ptnml(0-2): 1073, 38814, 105731, 38767, 1027 https://tests.stockfishchess.org/tests/view/63c89416a83c702aac08314c Passed non-regression LTC (master 3d2381d): LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 306048 W: 81173 L: 81237 D: 143638 Ptnml(0-2): 117, 27977, 96901, 27911, 118 https://tests.stockfishchess.org/tests/view/63cc4e84344bb01c191b2658 Bench: 4208265 --- src/search.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c748f1ff425..41096c9c7d2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -522,7 +522,6 @@ namespace { constexpr bool PvNode = nodeType != NonPV; constexpr bool rootNode = nodeType == Root; - const Depth maxNextDepth = rootNode ? depth : depth + 1; // Check if we have an upcoming move which draws by repetition, or // if the opponent had an alternative move earlier to this position. @@ -1235,8 +1234,7 @@ namespace { (ss+1)->pv = pv; (ss+1)->pv[0] = MOVE_NONE; - value = -search(pos, ss+1, -beta, -alpha, - std::min(maxNextDepth, newDepth), false); + value = -search(pos, ss+1, -beta, -alpha, newDepth, false); } // Step 19. Undo move From 0827e00f10709a4475ece44f0588277fc8cdcd9d Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Wed, 1 Feb 2023 15:44:53 +0800 Subject: [PATCH 0962/1766] Decrease reduction for killer moves with good history If move is a main killer and we have a good history, decrease reduction. STC: https://tests.stockfishchess.org/tests/view/63d38b37721fe2bff693069a LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 46688 W: 12542 L: 12222 D: 21924 Ptnml(0-2): 126, 5013, 12769, 5287, 149 LTC: https://tests.stockfishchess.org/tests/view/63d471e2bde6e5f3cb4be5d3 LLR: 2.93 (-2.94,2.94) <0.50,2.50> Total: 130976 W: 35033 L: 34555 D: 61388 Ptnml(0-2): 38, 12551, 39833, 13027, 39 closes https://github.com/official-stockfish/Stockfish/pull/4369 Bench: 4069938 --- src/search.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 41096c9c7d2..7d618a65b56 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1164,6 +1164,11 @@ namespace { if ((ss+1)->cutoffCnt > 3) r++; + // Decrease reduction if move is a killer and we have a good history + if (move == ss->killers[0] + && (*contHist[0])[movedPiece][to_sq(move)] >= 3600) + r--; + ss->statScore = 2 * thisThread->mainHistory[us][from_to(move)] + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] From 7fc0f589d601cb013f995ff44a49b5d2ae6bb253 Mon Sep 17 00:00:00 2001 From: MinetaS Date: Thu, 2 Feb 2023 00:08:23 +0900 Subject: [PATCH 0963/1766] Add -Wconditional-uninitialized when using Clang Add -Wconditional-uninitialized as it is not controlled by -Wall. closes https://github.com/official-stockfish/Stockfish/pull/4371 No functional change --- src/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 30c1be5ee52..775c72c36e9 100644 --- a/src/Makefile +++ b/src/Makefile @@ -426,7 +426,8 @@ ifeq ($(COMP),clang) CXX=x86_64-w64-mingw32-clang++ endif - CXXFLAGS += -pedantic -Wextra -Wshadow -Wmissing-prototypes + CXXFLAGS += -pedantic -Wextra -Wshadow -Wmissing-prototypes \ + -Wconditional-uninitialized ifeq ($(filter $(KERNEL),Darwin OpenBSD FreeBSD),) ifeq ($(target_windows),) From 3589bd008a3470336b3587e3a292a4cd6b02bf6b Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 2 Feb 2023 08:41:32 +0100 Subject: [PATCH 0964/1766] Update WLD model update the WLD model with about 400M positions extracted from recent LTC games after the net updates. This ensures that the 50% win rate is again at 1.0 eval. closes https://github.com/official-stockfish/Stockfish/pull/4373 No functional change. --- src/uci.cpp | 4 ++-- src/uci.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/uci.cpp b/src/uci.cpp index 2afd2de7300..3883b3d3707 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -206,8 +206,8 @@ namespace { // The coefficients of a third-order polynomial fit is based on the fishtest data // for two parameters that need to transform eval to the argument of a logistic // function. - constexpr double as[] = { -0.58270499, 2.68512549, 15.24638015, 344.49745382}; - constexpr double bs[] = { -2.65734562, 15.96509799, -20.69040836, 73.61029937 }; + constexpr double as[] = { 0.33677609, -4.30175627, 33.08810557, 365.60223431}; + constexpr double bs[] = { -2.50471102, 14.23235405, -14.33066859, 71.42705250 }; // Enforce that NormalizeToPawnValue corresponds to a 50% win rate at ply 64 static_assert(UCI::NormalizeToPawnValue == int(as[0] + as[1] + as[2] + as[3])); diff --git a/src/uci.h b/src/uci.h index bd3df323f0a..5d8ccf1afff 100644 --- a/src/uci.h +++ b/src/uci.h @@ -35,7 +35,7 @@ namespace UCI { // the win_rate_model() such that Stockfish outputs an advantage of // "100 centipawns" for a position if the engine has a 50% probability to win // from this position in selfplay at fishtest LTC time control. -const int NormalizeToPawnValue = 361; +const int NormalizeToPawnValue = 394; class Option; From da8513f0eae439f526ab0a905a3433bdb44bcf66 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Thu, 2 Feb 2023 13:05:54 +0300 Subject: [PATCH 0965/1766] Do less SEE pruning in qsearch Current master prunes all moves with negative SEE values in qsearch. This patch sets constant negative threshold thus allowing some moves with negative SEE values to be searched. Value of threshold is completely arbitrary and can be tweaked - also it as function of depth can be tried. Original idea by author of Alexandria engine. Passed STC https://tests.stockfishchess.org/tests/view/63d79a59a67dd929a5564976 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 34864 W: 9392 L: 9086 D: 16386 Ptnml(0-2): 113, 3742, 9429, 4022, 126 Passed LTC https://tests.stockfishchess.org/tests/view/63d8074aa67dd929a5565bc2 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 91616 W: 24532 L: 24126 D: 42958 Ptnml(0-2): 32, 8840, 27662, 9238, 36 closes https://github.com/official-stockfish/Stockfish/pull/4376 Bench: 4010877 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 7d618a65b56..b9ca3961053 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1556,9 +1556,9 @@ namespace { } } - // Do not search moves with negative SEE values (~5 Elo) + // Do not search moves with bad enough SEE values (~5 Elo) if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY - && !pos.see_ge(move)) + && !pos.see_ge(move, Value(-108))) continue; // Speculative prefetch as early as possible From 5a30b087c3a3c4435e680e6c61082d5432fa0ace Mon Sep 17 00:00:00 2001 From: MinetaS Date: Wed, 25 Jan 2023 07:32:02 +0900 Subject: [PATCH 0966/1766] Expand statistics tools for engine development This patch adds more debugging slots up to 32 per type and provide tools to calculate standard deviation and Pearson's correlation coefficient. However, due to slot being 0 at default, dbg_hit_on(c, b) has to be removed. Initial idea from snicolet/Stockfish@d8ab604 closes https://github.com/official-stockfish/Stockfish/pull/4354 No functional change --- src/misc.cpp | 94 ++++++++++++++++++++++++++++++++++++++++++++++------ src/misc.h | 7 ++-- 2 files changed, 88 insertions(+), 13 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index b651972bba1..7d848d32693 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -41,6 +41,7 @@ typedef WORD(*fun5_t)(); } #endif +#include #include #include #include @@ -299,21 +300,94 @@ std::string compiler_info() { /// Debug functions used mainly to collect run-time statistics -static std::atomic hits[2], means[2]; +constexpr int MaxDebugSlots = 32; -void dbg_hit_on(bool b) { ++hits[0]; if (b) ++hits[1]; } -void dbg_hit_on(bool c, bool b) { if (c) dbg_hit_on(b); } -void dbg_mean_of(int v) { ++means[0]; means[1] += v; } +namespace { + +template +struct DebugInfo { + std::atomic data[N] = { 0 }; + + constexpr inline std::atomic& operator[](int index) { return data[index]; } +}; + +DebugInfo<2> hit[MaxDebugSlots]; +DebugInfo<2> mean[MaxDebugSlots]; +DebugInfo<3> stdev[MaxDebugSlots]; +DebugInfo<6> correl[MaxDebugSlots]; + +} // namespace + +void dbg_hit_on(bool cond, int slot) { + + ++hit[slot][0]; + if (cond) + ++hit[slot][1]; +} + +void dbg_mean_of(int64_t value, int slot) { + + ++mean[slot][0]; + mean[slot][1] += value; +} + +void dbg_stdev_of(int64_t value, int slot) { + + ++stdev[slot][0]; + stdev[slot][1] += value; + stdev[slot][2] += value * value; +} + +void dbg_correl_of(int64_t value1, int64_t value2, int slot) { + + ++correl[slot][0]; + correl[slot][1] += value1; + correl[slot][2] += value1 * value1; + correl[slot][3] += value2; + correl[slot][4] += value2 * value2; + correl[slot][5] += value1 * value2; +} void dbg_print() { - if (hits[0]) - cerr << "Total " << hits[0] << " Hits " << hits[1] - << " hit rate (%) " << 100 * hits[1] / hits[0] << endl; + int64_t n; + auto E = [&n](int64_t x) { return double(x) / n; }; + auto sqr = [](double x) { return x * x; }; - if (means[0]) - cerr << "Total " << means[0] << " Mean " - << (double)means[1] / means[0] << endl; + for (int i = 0; i < MaxDebugSlots; ++i) + if ((n = hit[i][0])) + std::cerr << "Hit #" << i + << ": Total " << n << " Hits " << hit[i][1] + << " Hit Rate (%) " << 100.0 * E(hit[i][1]) + << std::endl; + + for (int i = 0; i < MaxDebugSlots; ++i) + if ((n = mean[i][0])) + { + std::cerr << "Mean #" << i + << ": Total " << n << " Mean " << E(mean[i][1]) + << std::endl; + } + + for (int i = 0; i < MaxDebugSlots; ++i) + if ((n = stdev[i][0])) + { + double r = sqrtl(E(stdev[i][2]) - sqr(E(stdev[i][1]))); + std::cerr << "Stdev #" << i + << ": Total " << n << " Stdev " << r + << std::endl; + } + + for (int i = 0; i < MaxDebugSlots; ++i) + if ((n = correl[i][0])) + { + double r = (E(correl[i][5]) - E(correl[i][1]) * E(correl[i][3])) + / ( sqrtl(E(correl[i][2]) - sqr(E(correl[i][1]))) + * sqrtl(E(correl[i][4]) - sqr(E(correl[i][3])))); + std::cerr << "Correl. #" << i + << ": Total " << n << " Coefficient " << r + << std::endl; + } } diff --git a/src/misc.h b/src/misc.h index 4c1150f867b..9761da8addd 100644 --- a/src/misc.h +++ b/src/misc.h @@ -39,9 +39,10 @@ void std_aligned_free(void* ptr); void* aligned_large_pages_alloc(size_t size); // memory aligned by page size, min alignment: 4096 bytes void aligned_large_pages_free(void* mem); // nop if mem == nullptr -void dbg_hit_on(bool b); -void dbg_hit_on(bool c, bool b); -void dbg_mean_of(int v); +void dbg_hit_on(bool cond, int slot = 0); +void dbg_mean_of(int64_t value, int slot = 0); +void dbg_stdev_of(int64_t value, int slot = 0); +void dbg_correl_of(int64_t value1, int64_t value2, int slot = 0); void dbg_print(); typedef std::chrono::milliseconds::rep TimePoint; // A value in milliseconds From 1cdc0f78bd937637128ad10f5168cdb80390f6fb Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Fri, 3 Feb 2023 10:52:14 +0300 Subject: [PATCH 0967/1766] Simplify usage of optimism in complexity This patch removes one condition in optimism usage in complexity, now negative optimism also impacts it. Passed STC: https://tests.stockfishchess.org/tests/view/63d34f43721fe2bff692fb12 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 204920 W: 54343 L: 54309 D: 96268 Ptnml(0-2): 598, 22648, 55897, 22756, 561 Passed LTC: https://tests.stockfishchess.org/tests/view/63d612a2a67dd929a556075c LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 200712 W: 53207 L: 53172 D: 94333 Ptnml(0-2): 58, 19664, 60901, 19651, 82 closes https://github.com/official-stockfish/Stockfish/pull/4377 bench 4204964 --- src/evaluate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 8683182c71d..6d5a8a0ce15 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1073,7 +1073,7 @@ Value Eval::evaluate(const Position& pos, int* complexity) { // Blend nnue complexity with (semi)classical complexity nnueComplexity = ( 406 * nnueComplexity + 424 * abs(psq - nnue) - + (optimism > 0 ? int(optimism) * int(psq - nnue) : 0) + + int(optimism) * int(psq - nnue) ) / 1024; // Return hybrid NNUE complexity to caller From d2f79ff0e0efc33797120f59355c2a5571b4ab80 Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Fri, 3 Feb 2023 18:54:30 +0800 Subject: [PATCH 0968/1766] Remove reduced LMR capture bonus In LMR, simplify away the reduced capture bonus (i.e. if (capture) bonus /= 6). Non-regression STC: https://tests.stockfishchess.org/tests/view/63da1da9bbadd17b3787dced LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 28152 W: 7521 L: 7296 D: 13335 Ptnml(0-2): 76, 3069, 7568, 3280, 83 Non-regression LTC: https://tests.stockfishchess.org/tests/view/63da6ad4bbadd17b3787e98c LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 52472 W: 14120 L: 13941 D: 24411 Ptnml(0-2): 16, 5071, 15887, 5242, 20 closes https://github.com/official-stockfish/Stockfish/pull/4378 Bench: 4034016 --- src/search.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index b9ca3961053..f6bc0aa9a80 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1214,9 +1214,6 @@ namespace { int bonus = value > alpha ? stat_bonus(newDepth) : -stat_bonus(newDepth); - if (capture) - bonus /= 6; - update_continuation_histories(ss, movedPiece, to_sq(move), bonus); } } From 8d3457a9966f8c744ab7f8536be408196ccd8af9 Mon Sep 17 00:00:00 2001 From: pb00067 Date: Fri, 3 Feb 2023 17:57:19 +0100 Subject: [PATCH 0969/1766] Improve excluded move logic PR consists of 2 improvements on nodes with excludeMove: 1. Remove xoring the posKey with make_key(excludedMove) Since we never call tte->save anymore with excludedMove, the unique left purpose of the xoring was to avoid a TT hit. Nevertheless on a normal bench run this produced ~25 false positives (key collisions) To avoid that we now forbid early TT cutoff's with excludeMove Maybe these accesses to TT with xored key caused useless misses in the CPU caches (L1, L2 ...) Now doing the probe with the same key as the enclosing search does, should hit the CPU cache. 2. Don't probe Tablebases with excludedMove. This can't be tested on fishtest, but it's obvious that tablebases don't deliver any information about suboptimal moves. Side note: Very surprisingly it looks like we cannot use static eval's from TT since they slightly differ over time due to changing optimism. Attempts to use static eval's from TT did loose about 13 ELO. This is something about to investigate. LTC: https://tests.stockfishchess.org/tests/view/63dc0f8de9d4cdfbe672d0c6 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 44736 W: 12046 L: 11733 D: 20957 Ptnml(0-2): 12, 4212, 13617, 4505, 22 An analogue of this passed STC & LTC see PR #4374 (thanks Dubslow for reviewing!) closes https://github.com/official-stockfish/Stockfish/pull/4380 Bench: 4758694 --- src/search.cpp | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index f6bc0aa9a80..30a08cb7729 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -615,22 +615,24 @@ namespace { if (!rootNode) (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 - // position key in case of an excluded move. + // Step 4. Transposition table lookup. excludedMove = ss->excludedMove; - posKey = excludedMove == MOVE_NONE ? pos.key() : pos.key() ^ make_key(excludedMove); + posKey = pos.key(); tte = TT.probe(posKey, ss->ttHit); ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] : ss->ttHit ? tte->move() : MOVE_NONE; ttCapture = ttMove && pos.capture(ttMove); + + // At this point, if excluded, skip straight to step 6, static eval. However, + // to save indentation, we list the condition in all code between here and there. if (!excludedMove) ss->ttPv = PvNode || (ss->ttHit && tte->is_pv()); // At non-PV nodes we check for an early TT cutoff if ( !PvNode && ss->ttHit + && !excludedMove && tte->depth() > depth - (tte->bound() == BOUND_EXACT) && ttValue != VALUE_NONE // Possible in case of TT access race && (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER))) @@ -664,7 +666,7 @@ namespace { } // Step 5. Tablebases probe - if (!rootNode && TB::Cardinality) + if (!rootNode && !excludedMove && TB::Cardinality) { int piecesCount = pos.count(); @@ -727,6 +729,12 @@ namespace { complexity = 0; goto moves_loop; } + else if (excludedMove) { + // excludeMove implies that we had a ttHit on the containing non-excluded search with ss->staticEval filled from TT + // However static evals from the TT aren't good enough (-13 elo), presumably due to changing optimism context + // Recalculate value with current optimism (without updating thread avgComplexity) + ss->staticEval = eval = evaluate(pos, &complexity); + } else if (ss->ttHit) { // Never assume anything about values stored in TT @@ -735,6 +743,7 @@ namespace { ss->staticEval = eval = evaluate(pos, &complexity); else // Fall back to (semi)classical complexity for TT hits, the NNUE complexity is lost complexity = abs(ss->staticEval - pos.psq_eg_stm()); + thisThread->complexityAverage.update(complexity); // ttValue can be used as a better position evaluation (~7 Elo) if ( ttValue != VALUE_NONE @@ -744,14 +753,12 @@ namespace { else { ss->staticEval = eval = evaluate(pos, &complexity); + thisThread->complexityAverage.update(complexity); // Save static evaluation into transposition table - if (!excludedMove) - tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); + tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); } - thisThread->complexityAverage.update(complexity); - // Use static evaluation difference to improve quiet move ordering (~4 Elo) if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture) { @@ -1061,6 +1068,7 @@ namespace { Depth singularDepth = (depth - 1) / 2; ss->excludedMove = move; + // the search with excludedMove will update ss->staticEval value = search(pos, ss, singularBeta - 1, singularBeta, singularDepth, cutNode); ss->excludedMove = MOVE_NONE; From d5817a5896a8d93c4560b405da576a02aaa0c08a Mon Sep 17 00:00:00 2001 From: Disservin Date: Sat, 4 Feb 2023 14:16:58 +0100 Subject: [PATCH 0970/1766] remove unnecessary variable pinned already has to be true for the bitwise & closes https://github.com/official-stockfish/Stockfish/pull/4381 No functional change --- src/movegen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index 471541644a0..255dce04c3c 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -264,7 +264,7 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { moveList = pos.checkers() ? generate(pos, moveList) : generate(pos, moveList); while (cur != moveList) - if ( ((pinned && pinned & from_sq(*cur)) || from_sq(*cur) == ksq || type_of(*cur) == EN_PASSANT) + if ( ((pinned & from_sq(*cur)) || from_sq(*cur) == ksq || type_of(*cur) == EN_PASSANT) && !pos.legal(*cur)) *cur = (--moveList)->move; else From 8f843633db3faaf447cc191cbaed9f5ddfd374bd Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 4 Feb 2023 23:46:44 +0300 Subject: [PATCH 0971/1766] Cleanup and reorder in qsearch This patch is a simplification / code normalisation in qsearch. Adds steps in comments the same way we have in search; Makes a separate "pruning" stage instead of heuristics randomly being spread over qsearch code; Reorders pruning heuristics from least taxing ones to more taxing ones; Removes repeated check for best value not being mated, instead uses 1 check - thus removes some lines of code. Moves prefetch and move setup after pruning - makes no sense to do them if move will actually get pruned. Passed non-regression test: https://tests.stockfishchess.org/tests/view/63dd2c5ff9a50a69252c1413 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 113504 W: 29898 L: 29770 D: 53836 Ptnml(0-2): 287, 11861, 32327, 11991, 286 https://github.com/official-stockfish/Stockfish/pull/4382 Non-functional change. --- src/search.cpp | 52 ++++++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 30a08cb7729..aa87948b804 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1427,6 +1427,7 @@ namespace { bool pvHit, givesCheck, capture; int moveCount; + // Step 1. Initialize node if (PvNode) { (ss+1)->pv = pv; @@ -1438,7 +1439,7 @@ namespace { ss->inCheck = pos.checkers(); moveCount = 0; - // Check for an immediate draw or maximum ply reached + // Step 2. Check for an immediate draw or maximum ply reached if ( pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos) : VALUE_DRAW; @@ -1450,13 +1451,14 @@ namespace { // only two types of depth in TT: DEPTH_QS_CHECKS or DEPTH_QS_NO_CHECKS. ttDepth = ss->inCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS : DEPTH_QS_NO_CHECKS; - // Transposition table lookup + // Step 3. Transposition table lookup posKey = pos.key(); tte = TT.probe(posKey, ss->ttHit); ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttMove = ss->ttHit ? tte->move() : MOVE_NONE; pvHit = ss->ttHit && tte->is_pv(); + // At non-PV nodes we check for an early TT cutoff if ( !PvNode && ss->ttHit && tte->depth() >= ttDepth @@ -1464,7 +1466,7 @@ namespace { && (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER))) return ttValue; - // Evaluate the position statically + // Step 4. Static evaluation of the position if (ss->inCheck) { ss->staticEval = VALUE_NONE; @@ -1522,7 +1524,8 @@ namespace { int quietCheckEvasions = 0; - // Loop through the moves until no moves remain or a beta cutoff occurs + // Step 5. Loop through all pseudo-legal moves until no moves remain + // or a beta cutoff occurs. while ((move = mp.next_move()) != MOVE_NONE) { assert(is_ok(move)); @@ -1536,9 +1539,11 @@ namespace { moveCount++; + // Step 6. Pruning. + if (bestValue > VALUE_TB_LOSS_IN_MAX_PLY) + { // Futility pruning and moveCount pruning (~10 Elo) - if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY - && !givesCheck + if ( !givesCheck && to_sq(move) != prevSq && futilityBase > -VALUE_KNOWN_WIN && type_of(move) != PROMOTION) @@ -1561,43 +1566,43 @@ namespace { } } + // We prune after 2nd quiet check evasion where being 'in check' is implicitly checked through the counter + // and being a 'quiet' apart from being a tt move is assumed after an increment because captures are pushed ahead. + if (quietCheckEvasions > 1) + break; + + // Continuation history based pruning (~3 Elo) + if ( !capture + && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < 0 + && (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < 0) + continue; + // Do not search moves with bad enough SEE values (~5 Elo) - if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY - && !pos.see_ge(move, Value(-108))) + if (!pos.see_ge(move, Value(-108))) continue; + } + // Speculative prefetch as early as possible prefetch(TT.first_entry(pos.key_after(move))); + // Update the current move ss->currentMove = move; ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck] [capture] [pos.moved_piece(move)] [to_sq(move)]; - // Continuation history based pruning (~3 Elo) - if ( !capture - && bestValue > VALUE_TB_LOSS_IN_MAX_PLY - && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < 0 - && (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < 0) - continue; - - // We prune after 2nd quiet check evasion where being 'in check' is implicitly checked through the counter - // and being a 'quiet' apart from being a tt move is assumed after an increment because captures are pushed ahead. - if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY - && quietCheckEvasions > 1) - break; - quietCheckEvasions += !capture && ss->inCheck; - // Make and search the move + // Step 7. Make and search the move pos.do_move(move, st, givesCheck); value = -qsearch(pos, ss+1, -beta, -alpha, depth - 1); pos.undo_move(move); assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); - // Check for a new best move + // Step 8. Check for a new best move if (value > bestValue) { bestValue = value; @@ -1617,6 +1622,7 @@ namespace { } } + // Step 9. Check for mate // All legal moves have been searched. A special case: if we're in check // and no legal moves were found, it is checkmate. if (ss->inCheck && bestValue == -VALUE_INFINITE) From e25bcaed3cb69406cec4cd3d212cdf5232234949 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Fri, 3 Feb 2023 18:18:55 -0600 Subject: [PATCH 0972/1766] Update `complexityAverage` in all branches of static eval STC: https://tests.stockfishchess.org/tests/view/63dda49573223e7f52ad0f8c LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 117416 W: 31173 L: 31049 D: 55194 Ptnml(0-2): 290, 12246, 33533, 12328, 311 LTC: https://tests.stockfishchess.org/tests/view/63dfa90873223e7f52ad69b8 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 44416 W: 11924 L: 11744 D: 20748 Ptnml(0-2): 5, 4036, 13968, 4172, 27 closes https://github.com/official-stockfish/Stockfish/pull/4385 bench 4758694 --- src/search.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index aa87948b804..2eed74b8759 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -743,7 +743,6 @@ namespace { ss->staticEval = eval = evaluate(pos, &complexity); else // Fall back to (semi)classical complexity for TT hits, the NNUE complexity is lost complexity = abs(ss->staticEval - pos.psq_eg_stm()); - thisThread->complexityAverage.update(complexity); // ttValue can be used as a better position evaluation (~7 Elo) if ( ttValue != VALUE_NONE @@ -753,11 +752,10 @@ namespace { else { ss->staticEval = eval = evaluate(pos, &complexity); - thisThread->complexityAverage.update(complexity); - // Save static evaluation into transposition table tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); } + thisThread->complexityAverage.update(complexity); // Use static evaluation difference to improve quiet move ordering (~4 Elo) if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture) From 05dea2ca4657dec10637bb53c4ad583f680e0677 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Sun, 5 Feb 2023 13:00:30 -0500 Subject: [PATCH 0973/1766] Update default net to nn-1337b1adec5b.nnue Created by retraining the master net on a dataset composed of: * Most of the previous best dataset filtered to remove positions likely having only one good move * Adding training data from Leela T77 dec2021 rescored with 16tb of 7-piece tablebases Trained with end lambda 0.7 and max epoch 900. Positions with ply <= 28 were removed from most of the previous best dataset before training began. A new nnue-pytorch trainer param for skipping early plies was used to skip plies <= 24 in the unfiltered and additional Leela T77 parts of the dataset. ``` python easy_train.py \ --experiment-name leela96-dfrc99-T80octnovT79aprmayT60novdec-eval-filt-v2-T78augsep-12tb-T77dec-16tb-lambda7-sk24 \ --training-dataset /data/leela96-dfrc99-T80octnovT79aprmayT60novdec-eval-filt-v2-T78augsep-12tb-T77dec-16tb.binpack \ --nnue-pytorch-branch linrock/nnue-pytorch/easy-train-early-fen-skipping \ --early-fen-skipping 24 \ --gpus "0," \ --start-from-engine-test-net True \ --start-lambda 1.0 \ --end-lambda 0.7 \ --gamma 0.995 \ --lr 4.375e-4 \ --tui False \ --seed $RANDOM \ --max_epoch 900 ``` The depth6 multipv2 search filtering method is the same as the one used for filtering recent best datasets, with a lower eval difference threshold to remove slightly more positions than before. These parts of the dataset were filtered: * 96% of T60T70wIsRightFarseerT60T74T75T76.binpack * 99% of dfrc_n5000.binpack * T80 oct + nov 2022 data, no positions with castling flags, rescored with ~600gb 7p tablebases * T79 apr + may 2022 data, rescored with 12tb 7p tablebases * T60 nov + dec 2021 data, rescored with 12tb 7p tablebases These parts of the dataset were not filtered. Positions with ply <= 24 were skipped during training: * T78 aug + sep 2022 data, rescored with 12tb 7p tablebases * 84% of T77 dec 2021 data, rescored with 16tb 7p tablebases The code and exact evaluation thresholds used for data filtering can be found at: https://github.com/linrock/Stockfish/tree/tools-filter-multipv2-eval-diff-t2/src/filter The exact training data used can be found at: https://robotmoon.com/nnue-training-data/ Local elo at 25k nodes per move: nn-epoch859.nnue : 3.5 +/ 1.2 Passed STC: LLR: 2.95 (-2.94,2.94) <0.00,2.00> https://tests.stockfishchess.org/tests/view/63dfeefc73223e7f52ad769f Total: 219744 W: 58572 L: 58002 D: 103170 Ptnml(0-2): 609, 24446, 59284, 24832, 701 Passed LTC: https://tests.stockfishchess.org/tests/view/63e268fc73223e7f52ade7b6 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 91256 W: 24528 L: 24121 D: 42607 Ptnml(0-2): 48, 8863, 27390, 9288, 39 closes https://github.com/official-stockfish/Stockfish/pull/4387 bench 3841998 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index f7ecaac93de..cdea2ab23f3 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-bc24c101ada0.nnue" + #define EvalFileDefaultName "nn-1337b1adec5b.nnue" namespace NNUE { From e5f6d71b96b5149e5e1df30721e1870abdb218ce Mon Sep 17 00:00:00 2001 From: borg323 Date: Thu, 9 Feb 2023 21:14:59 +0200 Subject: [PATCH 0974/1766] Fix build on arm windows avoids the use of _mm_malloc on arm windows. fixes #4379 closes https://github.com/official-stockfish/Stockfish/pull/4388 No functional change --- AUTHORS | 1 + src/misc.cpp | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index 42ba5930343..634de4a3d90 100644 --- a/AUTHORS +++ b/AUTHORS @@ -35,6 +35,7 @@ Ben Chaney (Chaneybenjamini) Ben Koshy (BKSpurgeon) Bill Henry (VoyagerOne) Bojun Guo (noobpwnftw, Nooby) +borg323 Boštjan Mejak (PedanticHacker) braich Brian Sheppard (SapphireBrand, briansheppard-toast) diff --git a/src/misc.cpp b/src/misc.cpp index 7d848d32693..e65faab9422 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -448,8 +448,10 @@ void* std_aligned_alloc(size_t alignment, size_t size) { #if defined(POSIXALIGNEDALLOC) void *mem; return posix_memalign(&mem, alignment, size) ? nullptr : mem; -#elif defined(_WIN32) +#elif defined(_WIN32) && !defined(_M_ARM) && !defined(_M_ARM64) return _mm_malloc(size, alignment); +#elif defined(_WIN32) + return _aligned_malloc(size, alignment); #else return std::aligned_alloc(alignment, size); #endif @@ -459,8 +461,10 @@ void std_aligned_free(void* ptr) { #if defined(POSIXALIGNEDALLOC) free(ptr); -#elif defined(_WIN32) +#elif defined(_WIN32) && !defined(_M_ARM) && !defined(_M_ARM64) _mm_free(ptr); +#elif defined(_WIN32) + _aligned_free(ptr); #else free(ptr); #endif From 852330ee5060e42a42c1cddd85b82e28d09f4229 Mon Sep 17 00:00:00 2001 From: disservin Date: Sat, 11 Feb 2023 17:14:16 +0100 Subject: [PATCH 0975/1766] update cuckoo link use webarchive to link to the cycle detection paper by Kervinck. closes https://github.com/official-stockfish/Stockfish/pull/4389 No functional change --- src/position.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/position.cpp b/src/position.cpp index cfd98f686e0..37aa2e9edec 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -97,7 +97,7 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) { // Marcel van Kervinck's cuckoo algorithm for fast detection of "upcoming repetition" // situations. Description of the algorithm in the following paper: -// https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf +// http://web.archive.org/web/20201107002606/https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf // First and second hash functions for indexing the cuckoo tables inline int H1(Key h) { return h & 0x1fff; } From 2c36d1e7e7374b8babb3cc503c0bc07ceb83dbf8 Mon Sep 17 00:00:00 2001 From: MinetaS Date: Mon, 13 Feb 2023 11:54:59 +0900 Subject: [PATCH 0976/1766] Fix overflow in add_dpbusd_epi32x2 This patch fixes 16bit overflow in *_add_dpbusd_epi32x2 functions, that can be triggered in rare cases depending on the NNUE weights. While the code leads to some slowdown on affected architectures (most notably avx2), the fix is simpler than some of the other options discussed in https://github.com/official-stockfish/Stockfish/pull/4394 Code suggested by Sopel97. Result of "bench 4096 1 30 default depth nnue": | Architecture | master | patch (gcc) | patch (clang) | |---------------------|-----------|-------------|---------------| | x86-64-vnni512 | 762122798 | 762122798 | 762122798 | | x86-64-avx512 | 769723503 | 762122798 | 762122798 | | x86-64-bmi2 | 769723503 | 762122798 | 762122798 | | x86-64-ssse3 | 769723503 | 762122798 | 762122798 | | x86-64 | 762122798 | 762122798 | 762122798 | Following architectures will experience ~4% slowdown due to an additional instruction in the middle of hot path: * x86-64-avx512 * x86-64-bmi2 * x86-64-avx2 * x86-64-sse41-popcnt (x86-64-modern) * x86-64-ssse3 * x86-32-sse41-popcnt This patch clearly loses Elo against master with both STC and LTC. Failed non-regression STC (256bit fix only): LLR: -2.95 (-2.94,2.94) <-1.75,0.25> Total: 33528 W: 8769 L: 9049 D: 15710 Ptnml(0-2): 96, 3616, 9600, 3376, 76 https://tests.stockfishchess.org/tests/view/63e6a5b44299542b1e26a485 60+0.6 @ 30000 games: Elo: -1.67 +-1.7 (95%) LOS: 2.8% Total: 30000 W: 7848 L: 7992 D: 14160 Ptnml(0-2): 12, 2847, 9436, 2683, 22 nElo: -3.84 +-3.9 (95%) PairsRatio: 0.95 https://tests.stockfishchess.org/tests/view/63e7ac716d0e1db55f35a660 However, a test against nn-a3dc078bafc7.nnue, which is the latest "safe" network not causing the bug, passed with regular bounds. Passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 160456 W: 42658 L: 42175 D: 75623 Ptnml(0-2): 487, 17638, 43469, 18173, 461 https://tests.stockfishchess.org/tests/view/63e89836d62a5d02b0fa82c8 closes https://github.com/official-stockfish/Stockfish/pull/4391 closes https://github.com/official-stockfish/Stockfish/pull/4394 No functional change --- src/nnue/layers/simd.h | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/nnue/layers/simd.h b/src/nnue/layers/simd.h index 231f7891216..381e7a68f8e 100644 --- a/src/nnue/layers/simd.h +++ b/src/nnue/layers/simd.h @@ -165,18 +165,19 @@ namespace Stockfish::Simd { __m512i tmp0 = _mm512_maddubs_epi16(a0, b0); __m512i tmp1 = _mm512_maddubs_epi16(a1, b1); asm( - "vpaddsw %[tmp0], %[tmp1], %[tmp0]\n\t" "vpmaddwd %[tmp0], %[ones], %[tmp0]\n\t" + "vpmaddwd %[tmp1], %[ones], %[tmp1]\n\t" + "vpaddd %[tmp0], %[tmp1], %[tmp0]\n\t" "vpaddd %[acc], %[tmp0], %[acc]\n\t" - : [acc]"+v"(acc), [tmp0]"+&v"(tmp0) - : [tmp1]"v"(tmp1), [ones]"v"(_mm512_set1_epi16(1)) + : [acc]"+v"(acc), [tmp0]"+&v"(tmp0), [tmp1]"+&v"(tmp1) + : [ones]"v"(_mm512_set1_epi16(1)) ); # else __m512i product0 = _mm512_maddubs_epi16(a0, b0); __m512i product1 = _mm512_maddubs_epi16(a1, b1); - product0 = _mm512_adds_epi16(product0, product1); product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1)); - acc = _mm512_add_epi32(acc, product0); + product1 = _mm512_madd_epi16(product1, _mm512_set1_epi16(1)); + acc = _mm512_add_epi32(acc, _mm512_add_epi32(product0, product1)); # endif # endif } @@ -261,18 +262,19 @@ namespace Stockfish::Simd { __m256i tmp0 = _mm256_maddubs_epi16(a0, b0); __m256i tmp1 = _mm256_maddubs_epi16(a1, b1); asm( - "vpaddsw %[tmp0], %[tmp1], %[tmp0]\n\t" "vpmaddwd %[tmp0], %[ones], %[tmp0]\n\t" + "vpmaddwd %[tmp1], %[ones], %[tmp1]\n\t" + "vpaddd %[tmp0], %[tmp1], %[tmp0]\n\t" "vpaddd %[acc], %[tmp0], %[acc]\n\t" - : [acc]"+v"(acc), [tmp0]"+&v"(tmp0) - : [tmp1]"v"(tmp1), [ones]"v"(_mm256_set1_epi16(1)) + : [acc]"+v"(acc), [tmp0]"+&v"(tmp0), [tmp1]"+&v"(tmp1) + : [ones]"v"(_mm256_set1_epi16(1)) ); # else __m256i product0 = _mm256_maddubs_epi16(a0, b0); __m256i product1 = _mm256_maddubs_epi16(a1, b1); - product0 = _mm256_adds_epi16(product0, product1); product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1)); - acc = _mm256_add_epi32(acc, product0); + product1 = _mm256_madd_epi16(product1, _mm256_set1_epi16(1)); + acc = _mm256_add_epi32(acc, _mm256_add_epi32(product0, product1)); # endif # endif } @@ -326,18 +328,19 @@ namespace Stockfish::Simd { __m128i tmp0 = _mm_maddubs_epi16(a0, b0); __m128i tmp1 = _mm_maddubs_epi16(a1, b1); asm( - "paddsw %[tmp1], %[tmp0]\n\t" "pmaddwd %[ones], %[tmp0]\n\t" + "pmaddwd %[ones], %[tmp1]\n\t" + "paddd %[tmp1], %[tmp0]\n\t" "paddd %[tmp0], %[acc]\n\t" - : [acc]"+v"(acc), [tmp0]"+&v"(tmp0) - : [tmp1]"v"(tmp1), [ones]"v"(_mm_set1_epi16(1)) + : [acc]"+v"(acc), [tmp0]"+&v"(tmp0), [tmp1]"+&v"(tmp1) + : [ones]"v"(_mm_set1_epi16(1)) ); # else __m128i product0 = _mm_maddubs_epi16(a0, b0); __m128i product1 = _mm_maddubs_epi16(a1, b1); - product0 = _mm_adds_epi16(product0, product1); product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1)); - acc = _mm_add_epi32(acc, product0); + product1 = _mm_madd_epi16(product1, _mm_set1_epi16(1)); + acc = _mm_add_epi32(acc, _mm_add_epi32(product0, product1)); # endif } From 29c1e072b669c2257e4b48094391e7dc39fb31a5 Mon Sep 17 00:00:00 2001 From: mstembera Date: Sun, 12 Feb 2023 17:33:19 -0800 Subject: [PATCH 0977/1766] Simplify nnueComplexity calculation. further simplification after https://github.com/official-stockfish/Stockfish/pull/4377 STC https://tests.stockfishchess.org/tests/view/63e02a3773223e7f52ad8190 LLR: 2.97 (-2.94,2.94) <-1.75,0.25> Total: 359072 W: 94605 L: 94733 D: 169734 Ptnml(0-2): 994, 39874, 97958, 39686, 1024 LTC https://tests.stockfishchess.org/tests/view/63e3fd12b5f425d71f77002a LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 248424 W: 66020 L: 66030 D: 116374 Ptnml(0-2): 113, 24653, 74689, 24645, 112 closes https://github.com/official-stockfish/Stockfish/pull/4390 bench: 4098325 --- src/evaluate.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 6d5a8a0ce15..080d412b7d9 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1072,8 +1072,7 @@ Value Eval::evaluate(const Position& pos, int* complexity) { // Blend nnue complexity with (semi)classical complexity nnueComplexity = ( 406 * nnueComplexity - + 424 * abs(psq - nnue) - + int(optimism) * int(psq - nnue) + + (424 + optimism) * abs(psq - nnue) ) / 1024; // Return hybrid NNUE complexity to caller From 085cace4574bf561472d8d3d3afe50c2c536b4e3 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Sun, 22 Jan 2023 19:45:46 -0600 Subject: [PATCH 0978/1766] Simplify late countermove bonus condition STC: https://tests.stockfishchess.org/tests/view/63d53ac6a67dd929a555e1e2 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 216096 W: 56862 L: 56839 D: 102395 Ptnml(0-2): 648, 24033, 58650, 24082, 635 LTC: https://tests.stockfishchess.org/tests/view/63d7f9a6a67dd929a5565991 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 808512 W: 214060 L: 214610 D: 379842 Ptnml(0-2): 301, 79448, 245293, 78928, 286 closes https://github.com/official-stockfish/Stockfish/pull/4392 Bench: 4283297 --- src/search.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 2eed74b8759..eccb97fd12c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1371,11 +1371,10 @@ namespace { quietsSearched, quietCount, capturesSearched, captureCount, depth); // Bonus for prior countermove that caused the fail low - else if ( (depth >= 5 || PvNode || bestValue < alpha - 65 * depth) - && !priorCapture) + else if (!priorCapture) { // Extra bonuses for PV/Cut nodes or bad fail lows - int bonus = 1 + (PvNode || cutNode) + (bestValue < alpha - 88 * depth); + int bonus = (depth > 4) + (PvNode || cutNode) + (bestValue < alpha - 88 * depth); update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); } From 037ef3e18dc7f5455cc671995ae38d5b4d1fce4a Mon Sep 17 00:00:00 2001 From: Dubslow Date: Tue, 14 Feb 2023 18:04:17 -0600 Subject: [PATCH 0979/1766] Remove one `reduction` call even though bench is unchanged to depth 28, due to adjusting depth in singular extensions this might be functional. STC: https://tests.stockfishchess.org/tests/view/63ec21affe833123fef34153 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 195712 W: 51625 L: 51581 D: 92506 Ptnml(0-2): 504, 20527, 55779, 20513, 533 LTC: https://tests.stockfishchess.org/tests/view/63ed3487fe833123fef375ed LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 32176 W: 8631 L: 8442 D: 15103 Ptnml(0-2): 5, 2794, 10309, 2967, 13 closes https://github.com/official-stockfish/Stockfish/pull/4395 Bench 4283297 --- src/search.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index eccb97fd12c..6ca2cfa5adb 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -987,6 +987,8 @@ namespace { Value delta = beta - alpha; + Depth r = reduction(improving, depth, moveCount, delta, thisThread->rootDelta); + // Step 14. Pruning at shallow depth (~120 Elo). Depth conditions are important for mate finding. if ( !rootNode && pos.non_pawn_material(us) @@ -996,7 +998,7 @@ namespace { moveCountPruning = moveCount >= futility_move_count(improving, depth); // Reduced depth of the next LMR search - int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount, delta, thisThread->rootDelta), 0); + int lmrDepth = std::max(newDepth - r, 0); if ( capture || givesCheck) @@ -1133,8 +1135,6 @@ namespace { // Step 16. Make the move pos.do_move(move, st, givesCheck); - Depth r = reduction(improving, depth, moveCount, delta, thisThread->rootDelta); - // Decrease reduction if position is or has been on the PV // and node is not likely to fail low. (~3 Elo) if ( ss->ttPv From b4ad3a3c4b68f9c8736f444aeb3364f833247fdc Mon Sep 17 00:00:00 2001 From: Sebastian Buchwald Date: Tue, 21 Feb 2023 22:18:17 +0100 Subject: [PATCH 0980/1766] Add support for ARM dot product instructions The sdot instruction computes (and accumulates) a signed dot product, which is quite handy for Stockfish's NNUE code. The instruction is optional for Armv8.2 and Armv8.3, and mandatory for Armv8.4 and above. The commit adds a new 'arm-dotprod' architecture with enabled dot product support. It also enables dot product support for the existing 'apple-silicon' architecture, which is at least Armv8.5. The following local speed test was performed on an Apple M1 with ARCH=apple-silicon. I had to remove CPU pinning from the benchmark script. However, the results were still consistent: Checking both binaries against themselves reported a speedup of +0.0000 and +0.0005, respectively. ``` Result of 100 runs ================== base (...ish.037ef3e1) = 1917997 +/- 7152 test (...fish.dotprod) = 2159682 +/- 9066 diff = +241684 +/- 2923 speedup = +0.1260 P(speedup > 0) = 1.0000 CPU: 10 x arm Hyperthreading: off ``` Fixes #4193 closes https://github.com/official-stockfish/Stockfish/pull/4400 No functional change --- src/Makefile | 65 +++++++++++++++++++----------- src/nnue/layers/affine_transform.h | 24 +++++++++++ src/nnue/layers/simd.h | 13 ++++++ 3 files changed, 78 insertions(+), 24 deletions(-) diff --git a/src/Makefile b/src/Makefile index 775c72c36e9..3d6432fd96b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -69,32 +69,33 @@ VPATH = syzygy:nnue:nnue/features ### Section 2. High-level Configuration ### ========================================================================== # -# flag --- Comp switch --- Description +# flag --- Comp switch --- Description # ---------------------------------------------------------------------------- # -# debug = yes/no --- -DNDEBUG --- Enable/Disable debug mode +# debug = yes/no --- -DNDEBUG --- Enable/Disable debug mode # sanitize = none/ ... (-fsanitize ) -# --- ( undefined ) --- enable undefined behavior checks -# --- ( thread ) --- enable threading error checks -# --- ( address ) --- enable memory access checks -# --- ...etc... --- see compiler documentation for supported sanitizers -# optimize = yes/no --- (-O3/-fast etc.) --- Enable/Disable optimizations -# arch = (name) --- (-arch) --- Target architecture -# bits = 64/32 --- -DIS_64BIT --- 64-/32-bit operating system -# prefetch = yes/no --- -DUSE_PREFETCH --- Use prefetch asm-instruction -# popcnt = yes/no --- -DUSE_POPCNT --- Use popcnt asm-instruction -# pext = yes/no --- -DUSE_PEXT --- Use pext x86_64 asm-instruction -# sse = yes/no --- -msse --- Use Intel Streaming SIMD Extensions -# mmx = yes/no --- -mmmx --- Use Intel MMX instructions -# sse2 = yes/no --- -msse2 --- Use Intel Streaming SIMD Extensions 2 -# ssse3 = yes/no --- -mssse3 --- Use Intel Supplemental Streaming SIMD Extensions 3 -# sse41 = yes/no --- -msse4.1 --- Use Intel Streaming SIMD Extensions 4.1 -# avx2 = yes/no --- -mavx2 --- Use Intel Advanced Vector Extensions 2 -# avxvnni = yes/no --- -mavxvnni --- Use Intel Vector Neural Network Instructions AVX -# avx512 = yes/no --- -mavx512bw --- Use Intel Advanced Vector Extensions 512 -# vnni256 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 256 -# vnni512 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 512 -# neon = yes/no --- -DUSE_NEON --- Use ARM SIMD architecture +# --- ( undefined ) --- enable undefined behavior checks +# --- ( thread ) --- enable threading error checks +# --- ( address ) --- enable memory access checks +# --- ...etc... --- see compiler documentation for supported sanitizers +# optimize = yes/no --- (-O3/-fast etc.) --- Enable/Disable optimizations +# arch = (name) --- (-arch) --- Target architecture +# bits = 64/32 --- -DIS_64BIT --- 64-/32-bit operating system +# prefetch = yes/no --- -DUSE_PREFETCH --- Use prefetch asm-instruction +# popcnt = yes/no --- -DUSE_POPCNT --- Use popcnt asm-instruction +# pext = yes/no --- -DUSE_PEXT --- Use pext x86_64 asm-instruction +# sse = yes/no --- -msse --- Use Intel Streaming SIMD Extensions +# mmx = yes/no --- -mmmx --- Use Intel MMX instructions +# sse2 = yes/no --- -msse2 --- Use Intel Streaming SIMD Extensions 2 +# ssse3 = yes/no --- -mssse3 --- Use Intel Supplemental Streaming SIMD Extensions 3 +# sse41 = yes/no --- -msse4.1 --- Use Intel Streaming SIMD Extensions 4.1 +# avx2 = yes/no --- -mavx2 --- Use Intel Advanced Vector Extensions 2 +# avxvnni = yes/no --- -mavxvnni --- Use Intel Vector Neural Network Instructions AVX +# avx512 = yes/no --- -mavx512bw --- Use Intel Advanced Vector Extensions 512 +# vnni256 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 256 +# vnni512 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 512 +# neon = yes/no --- -DUSE_NEON --- Use ARM SIMD architecture +# dotprod = yes/no --- -DUSE_NEON_DOTPROD --- Use ARM advanced SIMD Int8 dot product instructions # # Note that Makefile is space sensitive, so when adding new architectures # or modifying existing flags, you have to make sure there are no extra spaces @@ -116,7 +117,7 @@ ifeq ($(ARCH), $(filter $(ARCH), \ x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-avxvnni x86-64-bmi2 \ x86-64-avx2 x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \ x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-32 e2k \ - armv7 armv7-neon armv8 apple-silicon general-64 general-32 riscv64)) + armv7 armv7-neon armv8 armv8-dotprod apple-silicon general-64 general-32 riscv64)) SUPPORTED_ARCH=true else SUPPORTED_ARCH=false @@ -140,6 +141,7 @@ avx512 = no vnni256 = no vnni512 = no neon = no +dotprod = no arm_version = 0 STRIP = strip @@ -308,11 +310,21 @@ ifeq ($(ARCH),armv8) arm_version = 8 endif +ifeq ($(ARCH),armv8-dotprod) + arch = armv8 + prefetch = yes + popcnt = yes + neon = yes + dotprod = yes + arm_version = 8 +endif + ifeq ($(ARCH),apple-silicon) arch = arm64 prefetch = yes popcnt = yes neon = yes + dotprod = yes arm_version = 8 endif @@ -675,6 +687,10 @@ ifeq ($(neon),yes) endif endif +ifeq ($(dotprod),yes) + CXXFLAGS += -march=armv8.2-a+dotprod -DUSE_NEON_DOTPROD +endif + ### 3.7 pext ifeq ($(pext),yes) CXXFLAGS += -DUSE_PEXT @@ -776,6 +792,7 @@ help: @echo "armv7 > ARMv7 32-bit" @echo "armv7-neon > ARMv7 32-bit with popcnt and neon" @echo "armv8 > ARMv8 64-bit with popcnt and neon" + @echo "armv8-dotprod > ARMv8 64-bit with popcnt, neon and dot product support" @echo "e2k > Elbrus 2000" @echo "apple-silicon > Apple silicon ARM64" @echo "general-64 > unspecified 64-bit" diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 363b4916e37..63b58af33c3 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -72,6 +72,10 @@ namespace Stockfish::Eval::NNUE::Layers { const __m64 Zeros = _mm_setzero_si64(); const auto inputVector = reinterpret_cast(input); +# elif defined(USE_NEON_DOTPROD) + constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 16) / 16; + const auto inputVector = reinterpret_cast(input); + # elif defined(USE_NEON) constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 16) / 16; const auto inputVector = reinterpret_cast(input); @@ -123,6 +127,14 @@ namespace Stockfish::Eval::NNUE::Layers { sum = _mm_add_pi32(sum, _mm_unpackhi_pi32(sum, sum)); output[i] = _mm_cvtsi64_si32(sum); +# elif defined(USE_NEON_DOTPROD) + int32x4_t sum = {biases[i]}; + const auto row = reinterpret_cast(&weights[offset]); + for (IndexType j = 0; j < NumChunks; ++j) { + sum = vdotq_s32(sum, inputVector[j], row[j]); + } + output[i] = vaddvq_s32(sum); + # elif defined(USE_NEON) int32x4_t sum = {biases[i]}; const auto row = reinterpret_cast(&weights[offset]); @@ -187,6 +199,9 @@ namespace Stockfish::Eval::NNUE::Layers { #elif defined (USE_SSSE3) static constexpr IndexType InputSimdWidth = 16; static constexpr IndexType MaxNumOutputRegs = 8; +#elif defined (USE_NEON_DOTPROD) + static constexpr IndexType InputSimdWidth = 16; + static constexpr IndexType MaxNumOutputRegs = 8; #elif defined (USE_NEON) static constexpr IndexType InputSimdWidth = 8; static constexpr IndexType MaxNumOutputRegs = 8; @@ -292,6 +307,15 @@ namespace Stockfish::Eval::NNUE::Layers { #define vec_add_dpbusd_32x2 Simd::m128_add_dpbusd_epi32x2 #define vec_hadd Simd::m128_hadd #define vec_haddx4 Simd::m128_haddx4 +#elif defined (USE_NEON_DOTPROD) + using acc_vec_t = int32x4_t; + using bias_vec_t = int32x4_t; + using weight_vec_t = int8x16_t; + using in_vec_t = int8x16_t; + #define vec_zero {0} + #define vec_add_dpbusd_32x2 Simd::dotprod_m128_add_dpbusd_epi32x2 + #define vec_hadd Simd::neon_m128_hadd + #define vec_haddx4 Simd::neon_m128_haddx4 #elif defined (USE_NEON) using acc_vec_t = int32x4_t; using bias_vec_t = int32x4_t; diff --git a/src/nnue/layers/simd.h b/src/nnue/layers/simd.h index 381e7a68f8e..22c51980ecc 100644 --- a/src/nnue/layers/simd.h +++ b/src/nnue/layers/simd.h @@ -346,6 +346,19 @@ namespace Stockfish::Simd { #endif +#if defined (USE_NEON_DOTPROD) + + [[maybe_unused]] static void dotprod_m128_add_dpbusd_epi32x2( + int32x4_t& acc, + int8x16_t a0, int8x16_t b0, + int8x16_t a1, int8x16_t b1) { + + acc = vdotq_s32(acc, a0, b0); + acc = vdotq_s32(acc, a1, b1); + } + +#endif + #if defined (USE_NEON) [[maybe_unused]] static int neon_m128_reduce_add_epi32(int32x4_t s) { From 77dfcbedce2861b2c6c5056d49e7a8731fea4256 Mon Sep 17 00:00:00 2001 From: Sebastian Buchwald Date: Sun, 19 Feb 2023 11:25:10 +0100 Subject: [PATCH 0981/1766] Remove unused macros closes https://github.com/official-stockfish/Stockfish/pull/4397 No functional change --- src/nnue/layers/affine_transform.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 63b58af33c3..313b1568393 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -480,18 +480,14 @@ namespace Stockfish::Eval::NNUE::Layers { #define vec_set_32 _mm256_set1_epi32 #define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32 #define vec_add_dpbusd_32x2 Simd::m256_add_dpbusd_epi32x2 - #define vec_add_dpbusd_32x4 Simd::m256_add_dpbusd_epi32x4 #define vec_hadd Simd::m256_hadd - #define vec_haddx4 Simd::m256_haddx4 #elif defined (USE_SSSE3) using vec_t = __m128i; #define vec_setzero _mm_setzero_si128 #define vec_set_32 _mm_set1_epi32 #define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32 #define vec_add_dpbusd_32x2 Simd::m128_add_dpbusd_epi32x2 - #define vec_add_dpbusd_32x4 Simd::m128_add_dpbusd_epi32x4 #define vec_hadd Simd::m128_hadd - #define vec_haddx4 Simd::m128_haddx4 #endif #if defined (USE_SSSE3) @@ -542,9 +538,7 @@ namespace Stockfish::Eval::NNUE::Layers { # undef vec_set_32 # undef vec_add_dpbusd_32 # undef vec_add_dpbusd_32x2 -# undef vec_add_dpbusd_32x4 # undef vec_hadd -# undef vec_haddx4 #else // Use old implementation for the other architectures. affine_transform_non_ssse3< From 08385527dd470ece814ac85013802995a0e7f6ca Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 20 Feb 2023 20:02:55 +0100 Subject: [PATCH 0982/1766] Introduce a function to compute NNUE accumulator This patch introduces `hint_common_parent_position()` to signal that potentially several child nodes will require an NNUE eval. By populating explicitly the accumulator, these subsequent evaluations can be performed more efficiently. This was based on the observation that calculating the evaluation in an excluded move position yielded a significant Elo gain, even though the evaluation itself was already available (work by pb00067). Sopel wrote the code to perform just the accumulator update. This PR is based on cleaned up code that passed STC: https://tests.stockfishchess.org/tests/view/63f62f9be74a12625bcd4aa0 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 110368 W: 29607 L: 29167 D: 51594 Ptnml(0-2): 41, 10551, 33572, 10967, 53 and in an the earlier (equivalent) version passed STC: https://tests.stockfishchess.org/tests/view/63f3c3fee74a12625bcce2a6 LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 47552 W: 12786 L: 12467 D: 22299 Ptnml(0-2): 120, 5107, 12997, 5438, 114 passed LTC: https://tests.stockfishchess.org/tests/view/63f45cc2e74a12625bccfa63 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 110368 W: 29607 L: 29167 D: 51594 Ptnml(0-2): 41, 10551, 33572, 10967, 53 closes https://github.com/official-stockfish/Stockfish/pull/4402 Bench: 3726250 --- src/evaluate.h | 1 + src/nnue/evaluate_nnue.cpp | 5 + src/nnue/evaluate_nnue.h | 1 + src/nnue/nnue_feature_transformer.h | 421 +++++++++++++++++----------- src/search.cpp | 8 +- 5 files changed, 264 insertions(+), 172 deletions(-) diff --git a/src/evaluate.h b/src/evaluate.h index cdea2ab23f3..46f202594e6 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -45,6 +45,7 @@ namespace Eval { std::string trace(Position& pos); Value evaluate(const Position& pos, bool adjusted = false, int* complexity = nullptr); + void hint_common_parent_position(const Position& pos); void init(); void verify(); diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index f132de71385..f33aa3b889b 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -136,6 +136,11 @@ namespace Stockfish::Eval::NNUE { return (bool)stream; } + void hint_common_parent_position(const Position& pos) { + if (Eval::useNNUE) + featureTransformer->hint_common_access(pos); + } + // Evaluation function. Perform differential calculation. Value evaluate(const Position& pos, bool adjusted, int* complexity) { diff --git a/src/nnue/evaluate_nnue.h b/src/nnue/evaluate_nnue.h index 9499f7d99ab..15638caeeaf 100644 --- a/src/nnue/evaluate_nnue.h +++ b/src/nnue/evaluate_nnue.h @@ -31,6 +31,7 @@ namespace Stockfish::Eval::NNUE { constexpr std::uint32_t HashValue = FeatureTransformer::get_hash_value() ^ Network::get_hash_value(); + // Deleter for automating release of memory area template struct AlignedDeleter { diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 62f1615d5fe..13f1604fe13 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -25,6 +25,7 @@ #include "nnue_architecture.h" #include // std::memset() +#include // std::pair namespace Stockfish::Eval::NNUE { @@ -332,27 +333,16 @@ namespace Stockfish::Eval::NNUE { #endif return psqt; + } // end of function transform() - } // end of function transform() - - + void hint_common_access(const Position& pos) const { + hint_common_access_for_perspective(pos); + hint_common_access_for_perspective(pos); + } private: template - void update_accumulator(const Position& pos) const { - - // The size must be enough to contain the largest possible update. - // That might depend on the feature set and generally relies on the - // feature set's update cost calculation to be correct and never - // allow updates with more added/removed features than MaxActiveDimensions. - - #ifdef VECTOR - // Gcc-10.2 unnecessarily spills AVX2 registers if this array - // is defined in the VECTOR code below, once in each branch - vec_t acc[NumRegs]; - psqt_vec_t psqt[NumPsqtRegs]; - #endif - + [[nodiscard]] std::pair try_find_computed_accumulator(const Position& pos) const { // Look for a usable accumulator of an earlier position. We keep track // of the estimated gain in terms of features to be added/subtracted. StateInfo *st = pos.state(), *next = nullptr; @@ -367,218 +357,313 @@ namespace Stockfish::Eval::NNUE { next = st; st = st->previous; } + return { st, next }; + } - if (st->accumulator.computed[Perspective]) - { - if (next == nullptr) - return; + // NOTE: The parameter states_to_update is an array of position states, ending with nullptr. + // All states must be sequential, that is states_to_update[i] must either be reachable + // by repeatedly applying ->previous from states_to_update[i+1] or states_to_update[i] == nullptr. + // computed_st must be reachable by repeatadly applying ->previous on states_to_update[0], if not nullptr. + template + void update_accumulator_incremetal(const Position& pos, StateInfo* computed_st, StateInfo* states_to_update[N]) const { + static_assert(N > 0); + assert(states_to_update[N-1] == nullptr); - // Update incrementally in two steps. First, we update the "next" - // accumulator. Then, we update the current accumulator (pos.state()). + #ifdef VECTOR + // Gcc-10.2 unnecessarily spills AVX2 registers if this array + // is defined in the VECTOR code below, once in each branch + vec_t acc[NumRegs]; + psqt_vec_t psqt[NumPsqtRegs]; + #endif - // Gather all features to be updated. - const Square ksq = pos.square(Perspective); - FeatureSet::IndexList removed[2], added[2]; - FeatureSet::append_changed_indices( - ksq, next->dirtyPiece, removed[0], added[0]); - for (StateInfo *st2 = pos.state(); st2 != next; st2 = st2->previous) - FeatureSet::append_changed_indices( - ksq, st2->dirtyPiece, removed[1], added[1]); + if (states_to_update[0] == nullptr) + return; - // Mark the accumulators as computed. - next->accumulator.computed[Perspective] = true; - pos.state()->accumulator.computed[Perspective] = true; + // Update incrementally going back through states_to_update. - // Now update the accumulators listed in states_to_update[], where the last element is a sentinel. - StateInfo *states_to_update[3] = - { next, next == pos.state() ? nullptr : pos.state(), nullptr }; - #ifdef VECTOR - for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) - { - // Load accumulator - auto accTile = reinterpret_cast( - &st->accumulator.accumulation[Perspective][j * TileHeight]); - for (IndexType k = 0; k < NumRegs; ++k) - acc[k] = vec_load(&accTile[k]); + // Gather all features to be updated. + const Square ksq = pos.square(Perspective); - for (IndexType i = 0; states_to_update[i]; ++i) - { - // Difference calculation for the deactivated features - for (const auto index : removed[i]) - { - const IndexType offset = HalfDimensions * index + j * TileHeight; - auto column = reinterpret_cast(&weights[offset]); - for (IndexType k = 0; k < NumRegs; ++k) - acc[k] = vec_sub_16(acc[k], column[k]); - } - - // Difference calculation for the activated features - for (const auto index : added[i]) - { - const IndexType offset = HalfDimensions * index + j * TileHeight; - auto column = reinterpret_cast(&weights[offset]); - for (IndexType k = 0; k < NumRegs; ++k) - acc[k] = vec_add_16(acc[k], column[k]); - } - - // Store accumulator - accTile = reinterpret_cast( - &states_to_update[i]->accumulator.accumulation[Perspective][j * TileHeight]); - for (IndexType k = 0; k < NumRegs; ++k) - vec_store(&accTile[k], acc[k]); - } - } + // The size must be enough to contain the largest possible update. + // That might depend on the feature set and generally relies on the + // feature set's update cost calculation to be correct and never + // allow updates with more added/removed features than MaxActiveDimensions. + FeatureSet::IndexList removed[N-1], added[N-1]; - for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j) - { - // Load accumulator - auto accTilePsqt = reinterpret_cast( - &st->accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); - for (std::size_t k = 0; k < NumPsqtRegs; ++k) - psqt[k] = vec_load_psqt(&accTilePsqt[k]); + { + int i = N-2; // last potential state to update. Skip last element because it must be nullptr. + while (states_to_update[i] == nullptr) + --i; - for (IndexType i = 0; states_to_update[i]; ++i) - { - // Difference calculation for the deactivated features - for (const auto index : removed[i]) - { - const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; - auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); - for (std::size_t k = 0; k < NumPsqtRegs; ++k) - psqt[k] = vec_sub_psqt_32(psqt[k], columnPsqt[k]); - } - - // Difference calculation for the activated features - for (const auto index : added[i]) - { - const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; - auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); - for (std::size_t k = 0; k < NumPsqtRegs; ++k) - psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]); - } - - // Store accumulator - accTilePsqt = reinterpret_cast( - &states_to_update[i]->accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); - for (std::size_t k = 0; k < NumPsqtRegs; ++k) - vec_store_psqt(&accTilePsqt[k], psqt[k]); - } - } + StateInfo *st2 = states_to_update[i]; - #else - for (IndexType i = 0; states_to_update[i]; ++i) + for (; i >= 0; --i) { - std::memcpy(states_to_update[i]->accumulator.accumulation[Perspective], - st->accumulator.accumulation[Perspective], - HalfDimensions * sizeof(BiasType)); + states_to_update[i]->accumulator.computed[Perspective] = true; - for (std::size_t k = 0; k < PSQTBuckets; ++k) - states_to_update[i]->accumulator.psqtAccumulation[Perspective][k] = st->accumulator.psqtAccumulation[Perspective][k]; + StateInfo* end_state = i == 0 ? computed_st : states_to_update[i - 1]; + + for (; st2 != end_state; st2 = st2->previous) + FeatureSet::append_changed_indices( + ksq, st2->dirtyPiece, removed[i], added[i]); + } + } + + StateInfo* st = computed_st; - st = states_to_update[i]; + // Now update the accumulators listed in states_to_update[], where the last element is a sentinel. +#ifdef VECTOR + for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) + { + // Load accumulator + auto accTile = reinterpret_cast( + &st->accumulator.accumulation[Perspective][j * TileHeight]); + for (IndexType k = 0; k < NumRegs; ++k) + acc[k] = vec_load(&accTile[k]); + for (IndexType i = 0; states_to_update[i]; ++i) + { // Difference calculation for the deactivated features for (const auto index : removed[i]) { - const IndexType offset = HalfDimensions * index; - - for (IndexType j = 0; j < HalfDimensions; ++j) - st->accumulator.accumulation[Perspective][j] -= weights[offset + j]; - - for (std::size_t k = 0; k < PSQTBuckets; ++k) - st->accumulator.psqtAccumulation[Perspective][k] -= psqtWeights[index * PSQTBuckets + k]; + const IndexType offset = HalfDimensions * index + j * TileHeight; + auto column = reinterpret_cast(&weights[offset]); + for (IndexType k = 0; k < NumRegs; ++k) + acc[k] = vec_sub_16(acc[k], column[k]); } // Difference calculation for the activated features for (const auto index : added[i]) - { - const IndexType offset = HalfDimensions * index; - - for (IndexType j = 0; j < HalfDimensions; ++j) - st->accumulator.accumulation[Perspective][j] += weights[offset + j]; - - for (std::size_t k = 0; k < PSQTBuckets; ++k) - st->accumulator.psqtAccumulation[Perspective][k] += psqtWeights[index * PSQTBuckets + k]; - } - } - #endif - } - else - { - // Refresh the accumulator - auto& accumulator = pos.state()->accumulator; - accumulator.computed[Perspective] = true; - FeatureSet::IndexList active; - FeatureSet::append_active_indices(pos, active); - - #ifdef VECTOR - for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) - { - auto biasesTile = reinterpret_cast( - &biases[j * TileHeight]); - for (IndexType k = 0; k < NumRegs; ++k) - acc[k] = biasesTile[k]; - - for (const auto index : active) { const IndexType offset = HalfDimensions * index + j * TileHeight; auto column = reinterpret_cast(&weights[offset]); - - for (unsigned k = 0; k < NumRegs; ++k) + for (IndexType k = 0; k < NumRegs; ++k) acc[k] = vec_add_16(acc[k], column[k]); } - auto accTile = reinterpret_cast( - &accumulator.accumulation[Perspective][j * TileHeight]); - for (unsigned k = 0; k < NumRegs; k++) + // Store accumulator + accTile = reinterpret_cast( + &states_to_update[i]->accumulator.accumulation[Perspective][j * TileHeight]); + for (IndexType k = 0; k < NumRegs; ++k) vec_store(&accTile[k], acc[k]); } + } - for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j) - { - for (std::size_t k = 0; k < NumPsqtRegs; ++k) - psqt[k] = vec_zero_psqt(); + for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j) + { + // Load accumulator + auto accTilePsqt = reinterpret_cast( + &st->accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + psqt[k] = vec_load_psqt(&accTilePsqt[k]); - for (const auto index : active) + for (IndexType i = 0; states_to_update[i]; ++i) + { + // Difference calculation for the deactivated features + for (const auto index : removed[i]) { const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + psqt[k] = vec_sub_psqt_32(psqt[k], columnPsqt[k]); + } + // Difference calculation for the activated features + for (const auto index : added[i]) + { + const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; + auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); for (std::size_t k = 0; k < NumPsqtRegs; ++k) psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]); } - auto accTilePsqt = reinterpret_cast( - &accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); + // Store accumulator + accTilePsqt = reinterpret_cast( + &states_to_update[i]->accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); for (std::size_t k = 0; k < NumPsqtRegs; ++k) vec_store_psqt(&accTilePsqt[k], psqt[k]); } + } - #else - std::memcpy(accumulator.accumulation[Perspective], biases, +#else + for (IndexType i = 0; states_to_update[i]; ++i) + { + std::memcpy(states_to_update[i]->accumulator.accumulation[Perspective], + st->accumulator.accumulation[Perspective], HalfDimensions * sizeof(BiasType)); for (std::size_t k = 0; k < PSQTBuckets; ++k) - accumulator.psqtAccumulation[Perspective][k] = 0; + states_to_update[i]->accumulator.psqtAccumulation[Perspective][k] = st->accumulator.psqtAccumulation[Perspective][k]; - for (const auto index : active) + st = states_to_update[i]; + + // Difference calculation for the deactivated features + for (const auto index : removed[i]) { const IndexType offset = HalfDimensions * index; for (IndexType j = 0; j < HalfDimensions; ++j) - accumulator.accumulation[Perspective][j] += weights[offset + j]; + st->accumulator.accumulation[Perspective][j] -= weights[offset + j]; for (std::size_t k = 0; k < PSQTBuckets; ++k) - accumulator.psqtAccumulation[Perspective][k] += psqtWeights[index * PSQTBuckets + k]; + st->accumulator.psqtAccumulation[Perspective][k] -= psqtWeights[index * PSQTBuckets + k]; } + + // Difference calculation for the activated features + for (const auto index : added[i]) + { + const IndexType offset = HalfDimensions * index; + + for (IndexType j = 0; j < HalfDimensions; ++j) + st->accumulator.accumulation[Perspective][j] += weights[offset + j]; + + for (std::size_t k = 0; k < PSQTBuckets; ++k) + st->accumulator.psqtAccumulation[Perspective][k] += psqtWeights[index * PSQTBuckets + k]; + } + } +#endif + + #if defined(USE_MMX) + _mm_empty(); + #endif + } + + template + void update_accumulator_refresh(const Position& pos) const { + #ifdef VECTOR + // Gcc-10.2 unnecessarily spills AVX2 registers if this array + // is defined in the VECTOR code below, once in each branch + vec_t acc[NumRegs]; + psqt_vec_t psqt[NumPsqtRegs]; #endif + + // Refresh the accumulator + // Could be extracted to a separate function because it's done in 2 places, + // but it's unclear if compilers would correctly handle register allocation. + auto& accumulator = pos.state()->accumulator; + accumulator.computed[Perspective] = true; + FeatureSet::IndexList active; + FeatureSet::append_active_indices(pos, active); + +#ifdef VECTOR + for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) + { + auto biasesTile = reinterpret_cast( + &biases[j * TileHeight]); + for (IndexType k = 0; k < NumRegs; ++k) + acc[k] = biasesTile[k]; + + for (const auto index : active) + { + const IndexType offset = HalfDimensions * index + j * TileHeight; + auto column = reinterpret_cast(&weights[offset]); + + for (unsigned k = 0; k < NumRegs; ++k) + acc[k] = vec_add_16(acc[k], column[k]); + } + + auto accTile = reinterpret_cast( + &accumulator.accumulation[Perspective][j * TileHeight]); + for (unsigned k = 0; k < NumRegs; k++) + vec_store(&accTile[k], acc[k]); + } + + for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j) + { + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + psqt[k] = vec_zero_psqt(); + + for (const auto index : active) + { + const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; + auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); + + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]); + } + + auto accTilePsqt = reinterpret_cast( + &accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + vec_store_psqt(&accTilePsqt[k], psqt[k]); } +#else + std::memcpy(accumulator.accumulation[Perspective], biases, + HalfDimensions * sizeof(BiasType)); + + for (std::size_t k = 0; k < PSQTBuckets; ++k) + accumulator.psqtAccumulation[Perspective][k] = 0; + + for (const auto index : active) + { + const IndexType offset = HalfDimensions * index; + + for (IndexType j = 0; j < HalfDimensions; ++j) + accumulator.accumulation[Perspective][j] += weights[offset + j]; + + for (std::size_t k = 0; k < PSQTBuckets; ++k) + accumulator.psqtAccumulation[Perspective][k] += psqtWeights[index * PSQTBuckets + k]; + } +#endif + #if defined(USE_MMX) _mm_empty(); #endif } + template + void hint_common_access_for_perspective(const Position& pos) const { + + // Works like update_accumulator, but performs less work. + // Updates ONLY the accumulator for pos. + + // Look for a usable accumulator of an earlier position. We keep track + // of the estimated gain in terms of features to be added/subtracted. + // Fast early exit. + if (pos.state()->accumulator.computed[Perspective]) + return; + + auto [oldest_st, _] = try_find_computed_accumulator(pos); + + if (oldest_st->accumulator.computed[Perspective]) + { + // Only update current position accumulator to minimize work. + StateInfo* states_to_update[2] = { pos.state(), nullptr }; + update_accumulator_incremetal(pos, oldest_st, states_to_update); + } + else + { + update_accumulator_refresh(pos); + } + } + + template + void update_accumulator(const Position& pos) const { + + auto [oldest_st, next] = try_find_computed_accumulator(pos); + + if (oldest_st->accumulator.computed[Perspective]) + { + if (next == nullptr) + return; + + // Now update the accumulators listed in states_to_update[], where the last element is a sentinel. + // Currently we update 2 accumulators. + // 1. for the current position + // 2. the next accumulator after the computed one + // The heuristic may change in the future. + StateInfo *states_to_update[3] = + { next, next == pos.state() ? nullptr : pos.state(), nullptr }; + + update_accumulator_incremetal(pos, oldest_st, states_to_update); + } + else + { + update_accumulator_refresh(pos); + } + } + alignas(CacheLineSize) BiasType biases[HalfDimensions]; alignas(CacheLineSize) WeightType weights[HalfDimensions * InputDimensions]; alignas(CacheLineSize) PSQTWeightType psqtWeights[InputDimensions * PSQTBuckets]; diff --git a/src/search.cpp b/src/search.cpp index 6ca2cfa5adb..5cb9750c323 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -730,10 +730,10 @@ namespace { goto moves_loop; } else if (excludedMove) { - // excludeMove implies that we had a ttHit on the containing non-excluded search with ss->staticEval filled from TT - // However static evals from the TT aren't good enough (-13 elo), presumably due to changing optimism context - // Recalculate value with current optimism (without updating thread avgComplexity) - ss->staticEval = eval = evaluate(pos, &complexity); + // Providing the hint that this node's accumulator will be used often brings significant Elo gain (13 elo) + Eval::NNUE::hint_common_parent_position(pos); + eval = ss->staticEval; + complexity = abs(ss->staticEval - pos.psq_eg_stm()); } else if (ss->ttHit) { From 69639d764bde566e524b8c2566119bf677cb2622 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Tue, 21 Feb 2023 11:17:59 -0500 Subject: [PATCH 0983/1766] Reintroduce nnue pawn scaling with lower lazy thresholds Params found with the nevergrad TBPSA optimizer via nevergrad4sf modified to: * use SPRT LLR with fishtest STC elo gainer bounds [0, 2] as the objective function * increase the game batch size after each new optimal point is found The params were the optimal point after TBPSA iteration 7 and 160 nevergrad evaluations with: * initial batch size of 96 games per evaluation * batch size increase of 64 games after each iteration * a budget of 512 evaluations * TC: fixed 1.5 million nodes per move, no time limit nevergrad4sf enables optimizing stockfish params with TBPSA: https://github.com/vondele/nevergrad4sf Using pentanomial game results with smaller game batch sizes was inspired by: Use of SPRT LLR calculated from pentanomial game results as the objective function was an experiment at maximizing the information from game batches to reduce the computational cost for TBPSA to converge on good parameters. For the exact code used to find the params: https://github.com/linrock/tuning-fork Passed STC: https://tests.stockfishchess.org/tests/view/63f4ef5ee74a12625bcd114a LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 66552 W: 17736 L: 17390 D: 31426 Ptnml(0-2): 164, 7229, 18166, 7531, 186 Passed LTC: https://tests.stockfishchess.org/tests/view/63f56028e74a12625bcd2550 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 71264 W: 19150 L: 18787 D: 33327 Ptnml(0-2): 23, 6728, 21771, 7083, 27 closes https://github.com/official-stockfish/Stockfish/pull/4401 bench 3687580 --- src/evaluate.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 080d412b7d9..cf6f23eac55 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -193,8 +193,8 @@ using namespace Trace; namespace { // Threshold for lazy and space evaluation - constexpr Value LazyThreshold1 = Value(3631); - constexpr Value LazyThreshold2 = Value(2084); + constexpr Value LazyThreshold1 = Value(3622); + constexpr Value LazyThreshold2 = Value(1962); constexpr Value SpaceThreshold = Value(11551); // KingAttackWeights[PieceType] contains king attack weights by piece type @@ -1063,7 +1063,7 @@ Value Eval::evaluate(const Position& pos, int* complexity) { else { int nnueComplexity; - int scale = 1076 + 96 * pos.non_pawn_material() / 5120; + int scale = 1001 + 5 * pos.count() + 61 * pos.non_pawn_material() / 4096; Color stm = pos.side_to_move(); Value optimism = pos.this_thread()->optimism[stm]; From 29b5ad5deaf323f43019443b322090caec13f847 Mon Sep 17 00:00:00 2001 From: Sebastian Buchwald Date: Thu, 23 Feb 2023 19:01:51 +0100 Subject: [PATCH 0984/1766] Fix typo in method name closes https://github.com/official-stockfish/Stockfish/pull/4404 No functional change --- src/nnue/nnue_feature_transformer.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 13f1604fe13..b0d5743e669 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -365,7 +365,7 @@ namespace Stockfish::Eval::NNUE { // by repeatedly applying ->previous from states_to_update[i+1] or states_to_update[i] == nullptr. // computed_st must be reachable by repeatadly applying ->previous on states_to_update[0], if not nullptr. template - void update_accumulator_incremetal(const Position& pos, StateInfo* computed_st, StateInfo* states_to_update[N]) const { + void update_accumulator_incremental(const Position& pos, StateInfo* computed_st, StateInfo* states_to_update[N]) const { static_assert(N > 0); assert(states_to_update[N-1] == nullptr); @@ -630,7 +630,7 @@ namespace Stockfish::Eval::NNUE { { // Only update current position accumulator to minimize work. StateInfo* states_to_update[2] = { pos.state(), nullptr }; - update_accumulator_incremetal(pos, oldest_st, states_to_update); + update_accumulator_incremental(pos, oldest_st, states_to_update); } else { @@ -656,7 +656,7 @@ namespace Stockfish::Eval::NNUE { StateInfo *states_to_update[3] = { next, next == pos.state() ? nullptr : pos.state(), nullptr }; - update_accumulator_incremetal(pos, oldest_st, states_to_update); + update_accumulator_incremental(pos, oldest_st, states_to_update); } else { From 472e726bff0d0e496dc8359cc071726a76317a72 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Fri, 24 Feb 2023 18:25:24 +0300 Subject: [PATCH 0985/1766] Search tuning at very long time control This patch is a result of tuning session of approximately 100k games at 120+1.2. Biggest changes are in extensions, stat bonus and depth reduction for nodes without a tt move. Failed STC: https://tests.stockfishchess.org/tests/view/63f72c72e74a12625bcd7938 LLR: -2.94 (-2.94,2.94) <0.00,2.00> Total: 13872 W: 3535 L: 3769 D: 6568 Ptnml(0-2): 56, 1621, 3800, 1419, 40 Close to neutral at LTC: https://tests.stockfishchess.org/tests/view/63f738f5e74a12625bcd7b8a Elo: 0.80 +-1.2 (95%) LOS: 90.0% Total: 60000 W: 16213 L: 16074 D: 27713 Ptnml(0-2): 24, 5718, 18379, 5853, 26 nElo: 1.82 +-2.8 (95%) PairsRatio: 1.02 Passed 180+1.8 VLTC: https://tests.stockfishchess.org/tests/view/63f868f3e74a12625bcdb33e LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 15864 W: 4449 L: 4202 D: 7213 Ptnml(0-2): 1, 1301, 5083, 1544, 3 Passed 60+0.6 8 threads SMP VLTC: https://tests.stockfishchess.org/tests/view/63f8a5d6e74a12625bcdbdb3 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 6288 W: 1821 L: 1604 D: 2863 Ptnml(0-2): 0, 402, 2123, 619, 0 closes https://github.com/official-stockfish/Stockfish/pull/4406 bench 4705194 --- src/search.cpp | 102 ++++++++++++++++++++++++------------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5cb9750c323..a41ea4fd269 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -63,7 +63,7 @@ namespace { // Futility margin Value futility_margin(Depth d, bool improving) { - return Value(158 * (d - improving)); + return Value(154 * (d - improving)); } // Reductions lookup table, initialized at startup @@ -71,7 +71,7 @@ namespace { Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) { int r = Reductions[d] * Reductions[mn]; - return (r + 1460 - int(delta) * 1024 / int(rootDelta)) / 1024 + (!i && r > 937); + return (r + 1449 - int(delta) * 1032 / int(rootDelta)) / 1024 + (!i && r > 941); } constexpr int futility_move_count(bool improving, Depth depth) { @@ -81,7 +81,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return std::min(350 * d - 400, 1650); + return std::min(340 * d - 470, 1855); } // Add a small random component to draw evaluations to avoid 3-fold blindness @@ -161,7 +161,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int((20.26 + std::log(Threads.size()) / 2) * std::log(i)); + Reductions[i] = int((19.47 + std::log(Threads.size()) / 2) * std::log(i)); } @@ -354,12 +354,12 @@ void Thread::search() { if (rootDepth >= 4) { Value prev = rootMoves[pvIdx].averageScore; - delta = Value(10) + int(prev) * prev / 15400; + delta = Value(10) + int(prev) * prev / 16502; alpha = std::max(prev - delta,-VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE); // Adjust optimism based on root move's previousScore - int opt = 116 * prev / (std::abs(prev) + 170); + int opt = 120 * prev / (std::abs(prev) + 161); optimism[ us] = Value(opt); optimism[~us] = -optimism[us]; } @@ -462,16 +462,16 @@ void Thread::search() { && !Threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (71 + 12 * (mainThread->bestPreviousAverageScore - bestValue) - + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 656.7; + double fallingEval = (69 + 13 * (mainThread->bestPreviousAverageScore - bestValue) + + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 619.6; fallingEval = std::clamp(fallingEval, 0.5, 1.5); // If the bestMove is stable over several iterations, reduce time accordingly - timeReduction = lastBestMoveDepth + 9 < completedDepth ? 1.37 : 0.65; - double reduction = (1.4 + mainThread->previousTimeReduction) / (2.15 * timeReduction); - double bestMoveInstability = 1 + 1.7 * totBestMoveChanges / Threads.size(); + timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.57 : 0.65; + double reduction = (1.4 + mainThread->previousTimeReduction) / (2.08 * timeReduction); + double bestMoveInstability = 1 + 1.8 * totBestMoveChanges / Threads.size(); int complexity = mainThread->complexityAverage.value(); - double complexPosition = std::min(1.0 + (complexity - 261) / 1738.7, 1.5); + double complexPosition = std::min(1.03 + (complexity - 241) / 1552.0, 1.45); double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability * complexPosition; @@ -491,7 +491,7 @@ void Thread::search() { Threads.stop = true; } else if ( !mainThread->ponder - && Time.elapsed() > totalTime * 0.53) + && Time.elapsed() > totalTime * 0.50) Threads.increaseDepth = false; else Threads.increaseDepth = true; @@ -760,7 +760,7 @@ namespace { // Use static evaluation difference to improve quiet move ordering (~4 Elo) if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture) { - int bonus = std::clamp(-19 * int((ss-1)->staticEval + ss->staticEval), -1940, 1940); + int bonus = std::clamp(-19 * int((ss-1)->staticEval + ss->staticEval), -1920, 1920); thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus; } @@ -770,13 +770,13 @@ namespace { // margin and the improving flag are used in various pruning heuristics. improvement = (ss-2)->staticEval != VALUE_NONE ? ss->staticEval - (ss-2)->staticEval : (ss-4)->staticEval != VALUE_NONE ? ss->staticEval - (ss-4)->staticEval - : 172; + : 156; improving = improvement > 0; // Step 7. Razoring (~1 Elo). // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. - if (eval < alpha - 394 - 255 * depth * depth) + if (eval < alpha - 426 - 252 * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -786,19 +786,19 @@ namespace { // Step 8. Futility pruning: child node (~40 Elo). // The depth condition is important for mate finding. if ( !ss->ttPv - && depth < 8 - && eval - futility_margin(depth, improving) - (ss-1)->statScore / 304 >= beta + && depth < 9 + && eval - futility_margin(depth, improving) - (ss-1)->statScore / 280 >= beta && eval >= beta - && eval < 28580) // larger than VALUE_KNOWN_WIN, but smaller than TB wins + && eval < 25128) // larger than VALUE_KNOWN_WIN, but smaller than TB wins return eval; // Step 9. Null move search with verification search (~35 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 18200 + && (ss-1)->statScore < 18755 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 20 * depth - improvement / 14 + 235 + complexity / 24 + && ss->staticEval >= beta - 19 * depth - improvement / 13 + 253 + complexity / 25 && !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, eval and complexity of position - Depth R = std::min(int(eval - beta) / 165, 6) + depth / 3 + 4 - (complexity > 800); + Depth R = std::min(int(eval - beta) / 168, 6) + depth / 3 + 4 - (complexity > 825); ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -842,7 +842,7 @@ namespace { } } - probCutBeta = beta + 180 - 54 * improving; + probCutBeta = beta + 186 - 54 * improving; // Step 10. ProbCut (~10 Elo) // If we have a good enough capture and a reduced search returns a value @@ -904,14 +904,14 @@ namespace { return qsearch(pos, ss, alpha, beta); if ( cutNode - && depth >= 9 + && depth >= 7 && !ttMove) depth -= 2; moves_loop: // When in check, search starts here // Step 12. A small Probcut idea, when we are in check (~4 Elo) - probCutBeta = beta + 402; + probCutBeta = beta + 391; if ( ss->inCheck && !PvNode && depth >= 2 @@ -1006,14 +1006,14 @@ namespace { // Futility pruning for captures (~2 Elo) if ( !givesCheck && !PvNode - && lmrDepth < 7 + && lmrDepth < 6 && !ss->inCheck - && ss->staticEval + 185 + 203 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] - + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 6 < alpha) + && ss->staticEval + 182 + 230 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] + + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 7 < alpha) continue; // SEE based pruning (~11 Elo) - if (!pos.see_ge(move, Value(-220) * depth)) + if (!pos.see_ge(move, Value(-206) * depth)) continue; } else @@ -1024,24 +1024,24 @@ namespace { // Continuation history based pruning (~2 Elo) if ( lmrDepth < 5 - && history < -4180 * (depth - 1)) + && history < -4405 * (depth - 1)) continue; history += 2 * thisThread->mainHistory[us][from_to(move)]; - lmrDepth += history / 7208; + lmrDepth += history / 7278; lmrDepth = std::max(lmrDepth, -2); // Futility pruning: parent node (~13 Elo) if ( !ss->inCheck && lmrDepth < 13 - && ss->staticEval + 103 + 136 * lmrDepth <= alpha) + && ss->staticEval + 103 + 138 * lmrDepth <= alpha) continue; lmrDepth = std::max(lmrDepth, 0); // Prune moves with negative SEE (~4 Elo) - if (!pos.see_ge(move, Value(-25 * lmrDepth * lmrDepth - 16 * lmrDepth))) + if (!pos.see_ge(move, Value(-24 * lmrDepth * lmrDepth - 15 * lmrDepth))) continue; } } @@ -1056,7 +1056,7 @@ namespace { // 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 ( !rootNode - && depth >= 4 - (thisThread->completedDepth > 22) + 2 * (PvNode && tte->is_pv()) + && depth >= 4 - (thisThread->completedDepth > 21) + 2 * (PvNode && tte->is_pv()) && move == ttMove && !excludedMove // Avoid recursive singular search /* && ttValue != VALUE_NONE Already implicit in the next condition */ @@ -1064,7 +1064,7 @@ namespace { && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - (3 + (ss->ttPv && !PvNode)) * depth; + Value singularBeta = ttValue - (2 + (ss->ttPv && !PvNode)) * depth; Depth singularDepth = (depth - 1) / 2; ss->excludedMove = move; @@ -1083,7 +1083,7 @@ namespace { && ss->doubleExtensions <= 10) { extension = 2; - depth += depth < 12; + depth += depth < 13; } } @@ -1106,15 +1106,15 @@ namespace { // Check extensions (~1 Elo) else if ( givesCheck - && depth > 9 - && abs(ss->staticEval) > 78) + && depth > 10 + && abs(ss->staticEval) > 88) extension = 1; // Quiet ttMove extensions (~1 Elo) else if ( PvNode && move == ttMove && move == ss->killers[0] - && (*contHist[0])[movedPiece][to_sq(move)] >= 5600) + && (*contHist[0])[movedPiece][to_sq(move)] >= 5705) extension = 1; } @@ -1155,7 +1155,7 @@ namespace { // Decrease reduction for PvNodes based on depth if (PvNode) - r -= 1 + 11 / (3 + depth); + r -= 1 + 12 / (3 + depth); // Decrease reduction if ttMove has been singularly extended (~1 Elo) if (singularQuietLMR) @@ -1172,17 +1172,17 @@ namespace { // Decrease reduction if move is a killer and we have a good history if (move == ss->killers[0] - && (*contHist[0])[movedPiece][to_sq(move)] >= 3600) + && (*contHist[0])[movedPiece][to_sq(move)] >= 3722) r--; ss->statScore = 2 * thisThread->mainHistory[us][from_to(move)] + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 4467; + - 4182; // Decrease/increase reduction for moves with a good/bad history (~30 Elo) - r -= ss->statScore / (12800 + 4410 * (depth > 7 && depth < 19)); + r -= ss->statScore / (11791 + 3992 * (depth > 6 && depth < 19)); // Step 17. Late moves reduction / extension (LMR, ~117 Elo) // We use various heuristics for the sons of a node after the first son has @@ -1206,8 +1206,8 @@ namespace { { // Adjust full depth search based on LMR results - if result // was good enough search deeper, if it was bad enough search shallower - const bool doDeeperSearch = value > (alpha + 66 + 11 * (newDepth - d)); - const bool doEvenDeeperSearch = value > alpha + 582 && ss->doubleExtensions <= 5; + const bool doDeeperSearch = value > (alpha + 58 + 12 * (newDepth - d)); + const bool doEvenDeeperSearch = value > alpha + 588 && ss->doubleExtensions <= 5; const bool doShallowerSearch = value < bestValue + newDepth; ss->doubleExtensions = ss->doubleExtensions + doEvenDeeperSearch; @@ -1318,8 +1318,8 @@ namespace { // Reduce other moves if we have found at least one score improvement if ( depth > 1 && depth < 6 - && beta < VALUE_KNOWN_WIN - && alpha > -VALUE_KNOWN_WIN) + && beta < 10534 + && alpha > -10534) depth -= 1; assert(depth > 0); @@ -1374,7 +1374,7 @@ namespace { else if (!priorCapture) { // Extra bonuses for PV/Cut nodes or bad fail lows - int bonus = (depth > 4) + (PvNode || cutNode) + (bestValue < alpha - 88 * depth); + int bonus = (depth > 5) + (PvNode || cutNode) + (bestValue < alpha - 97 * depth); update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); } @@ -1502,7 +1502,7 @@ namespace { if (PvNode && bestValue > alpha) alpha = bestValue; - futilityBase = bestValue + 158; + futilityBase = bestValue + 168; } const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, @@ -1575,7 +1575,7 @@ namespace { continue; // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, Value(-108))) + if (!pos.see_ge(move, Value(-110))) continue; } @@ -1708,7 +1708,7 @@ namespace { if (!pos.capture(bestMove)) { - int bonus2 = bestValue > beta + 146 ? bonus1 // larger bonus + int bonus2 = bestValue > beta + 153 ? bonus1 // larger bonus : stat_bonus(depth); // smaller bonus // Increase stats for the best move in case it was a quiet move From 98dafda6c8d432924821d085fd958c89bc8834c3 Mon Sep 17 00:00:00 2001 From: Alfredo Menezes Date: Mon, 27 Feb 2023 00:39:26 -0300 Subject: [PATCH 0986/1766] Simplify condition in step 15 Remove 'ttValue <= alpha' check for negative extension in singular search. Also apply some small code style changes. STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 127888 W: 33766 L: 33651 D: 60471 Ptnml(0-2): 303, 14082, 35089, 14137, 333 https://tests.stockfishchess.org/tests/view/63f79528e74a12625bcd8c05 LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 89048 W: 23924 L: 23782 D: 41342 Ptnml(0-2): 27, 8635, 27065, 8763, 34 https://tests.stockfishchess.org/tests/view/63f82177e74a12625bcda6f4 LTC (retest): LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 196360 W: 52514 L: 52475 D: 91371 Ptnml(0-2): 103, 19066, 59780, 19151, 80 https://tests.stockfishchess.org/tests/view/63f934bfe74a12625bcdd929 closes https://github.com/official-stockfish/Stockfish/pull/4407 Bench: 5310866 --- src/search.cpp | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index a41ea4fd269..6ccc70cc673 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -334,7 +334,7 @@ void Thread::search() { pvLast = 0; if (!Threads.increaseDepth) - searchAgainCounter++; + searchAgainCounter++; // MultiPV loop. We perform a full root search for each PV line for (pvIdx = 0; pvIdx < multiPV && !Threads.stop; ++pvIdx) @@ -432,9 +432,10 @@ void Thread::search() { if (!Threads.stop) completedDepth = rootDepth; - if (rootMoves[0].pv[0] != lastBestMove) { - lastBestMove = rootMoves[0].pv[0]; - lastBestMoveDepth = rootDepth; + if (rootMoves[0].pv[0] != lastBestMove) + { + lastBestMove = rootMoves[0].pv[0]; + lastBestMoveDepth = rootDepth; } // Have we found a "mate in x"? @@ -729,7 +730,8 @@ namespace { complexity = 0; goto moves_loop; } - else if (excludedMove) { + else if (excludedMove) + { // Providing the hint that this node's accumulator will be used often brings significant Elo gain (13 elo) Eval::NNUE::hint_common_parent_position(pos); eval = ss->staticEval; @@ -755,6 +757,7 @@ namespace { // Save static evaluation into transposition table tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); } + thisThread->complexityAverage.update(complexity); // Use static evaluation difference to improve quiet move ordering (~4 Elo) @@ -920,11 +923,9 @@ namespace { && tte->depth() >= depth - 3 && ttValue >= probCutBeta && abs(ttValue) <= VALUE_KNOWN_WIN - && abs(beta) <= VALUE_KNOWN_WIN - ) + && abs(beta) <= VALUE_KNOWN_WIN) return probCutBeta; - const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, nullptr , (ss-4)->continuationHistory, nullptr , (ss-6)->continuationHistory }; @@ -1099,8 +1100,8 @@ namespace { else if (ttValue >= beta) extension = -2; - // If the eval of ttMove is less than alpha and value, we reduce it (negative extension) - else if (ttValue <= alpha && ttValue <= value) + // If the eval of ttMove is less than value, we reduce it (negative extension) + else if (ttValue <= value) extension = -1; } @@ -1227,11 +1228,11 @@ namespace { // Step 18. Full depth search when LMR is skipped. If expected reduction is high, reduce its depth by 1. else if (!PvNode || moveCount > 1) { - // Increase reduction for cut nodes and not ttMove (~1 Elo) - if (!ttMove && cutNode) - r += 2; + // Increase reduction for cut nodes and not ttMove (~1 Elo) + if (!ttMove && cutNode) + r += 2; - value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth - (r > 4), !cutNode); + value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth - (r > 4), !cutNode); } // For PV nodes only, do a full PV search on the first move or after a fail @@ -1271,14 +1272,17 @@ namespace { rm.selDepth = thisThread->selDepth; rm.scoreLowerbound = rm.scoreUpperbound = false; - if (value >= beta) { - rm.scoreLowerbound = true; - rm.uciScore = beta; + if (value >= beta) + { + rm.scoreLowerbound = true; + rm.uciScore = beta; } - else if (value <= alpha) { - rm.scoreUpperbound = true; - rm.uciScore = alpha; + else if (value <= alpha) + { + rm.scoreUpperbound = true; + rm.uciScore = alpha; } + rm.pv.resize(1); assert((ss+1)->pv); @@ -1447,7 +1451,8 @@ namespace { // TT entry depth that we are going to use. Note that in qsearch we use // only two types of depth in TT: DEPTH_QS_CHECKS or DEPTH_QS_NO_CHECKS. ttDepth = ss->inCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS - : DEPTH_QS_NO_CHECKS; + : DEPTH_QS_NO_CHECKS; + // Step 3. Transposition table lookup posKey = pos.key(); tte = TT.probe(posKey, ss->ttHit); @@ -1577,7 +1582,6 @@ namespace { // Do not search moves with bad enough SEE values (~5 Elo) if (!pos.see_ge(move, Value(-110))) continue; - } // Speculative prefetch as early as possible From 728b963614a765f5cb64c44a078169cca977750f Mon Sep 17 00:00:00 2001 From: pb00067 Date: Sun, 26 Feb 2023 09:59:35 +0100 Subject: [PATCH 0987/1766] Use common_parent_position hint also at PVNodes TT hits. Credits to Stefan Geschwentner (locutus2) showing that the hint is useful on PvNodes. In contrast to his test, this version avoids to use the hint when in check. I believe checking positions aren't good candidates for the hint because: - evasion moves are rather few, so a checking pos. has much less childs than a normal position - if the king has to move the NNUE eval can't use incremental updates, so the child nodes have to do a full refresh anyway. Passed STC: https://tests.stockfishchess.org/tests/view/63f9c5b1e74a12625bcdf585 LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 124472 W: 33268 L: 32846 D: 58358 Ptnml(0-2): 350, 12986, 35170, 13352, 378 closes https://github.com/official-stockfish/Stockfish/pull/4410 no functional change --- src/search.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 6ccc70cc673..206779ed1be 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -744,7 +744,11 @@ namespace { if (eval == VALUE_NONE) ss->staticEval = eval = evaluate(pos, &complexity); else // Fall back to (semi)classical complexity for TT hits, the NNUE complexity is lost + { complexity = abs(ss->staticEval - pos.psq_eg_stm()); + if (PvNode) + Eval::NNUE::hint_common_parent_position(pos); + } // ttValue can be used as a better position evaluation (~7 Elo) if ( ttValue != VALUE_NONE From ff5a6f8df196d61a0d9b1ebe54d84eeb9af20079 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Sun, 26 Feb 2023 09:56:54 +0100 Subject: [PATCH 0988/1766] NNUE accumulator update in probcut. Call the recently added hint function for NNUE accumulator update after a failed probcut search. In this case we already searched at least some captures and tt move which, however, is not sufficient for a cutoff. So it seems we have a greater chance that the full search will also have no cutoff and hence all moves have to be searched. STC: https://tests.stockfishchess.org/tests/view/63fa74a4e74a12625bce1823 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 70096 W: 18770 L: 18423 D: 32903 Ptnml(0-2): 191, 7342, 19654, 7651, 210 To be sure that we have no heavy interaction retest on top of #4410. Rebased STC: https://tests.stockfishchess.org/tests/view/63fb2f62e74a12625bce3b03 LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 137688 W: 36790 L: 36349 D: 64549 Ptnml(0-2): 397, 14373, 38919, 14702, 453 closes https://github.com/official-stockfish/Stockfish/pull/4411 No functional change --- src/search.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 206779ed1be..cfb569b9093 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -899,6 +899,8 @@ namespace { return value; } } + + Eval::NNUE::hint_common_parent_position(pos); } // Step 11. If the position is not in TT, decrease depth by 3. From 564456a6a824bfca26d6d9af5b35a055eb9fc6c2 Mon Sep 17 00:00:00 2001 From: Sebastian Buchwald Date: Sun, 26 Feb 2023 19:42:31 +0100 Subject: [PATCH 0989/1766] Unify type alias declarations The commit unifies the declaration of type aliases by replacing all typedefs with corresponding using statements. closing https://github.com/official-stockfish/Stockfish/pull/4412 No functional change --- src/material.h | 2 +- src/misc.cpp | 12 ++++++------ src/misc.h | 4 ++-- src/movepick.h | 14 +++++++------- src/nnue/nnue_feature_transformer.h | 20 ++++++++++---------- src/pawns.h | 2 +- src/position.h | 2 +- src/search.h | 2 +- src/syzygy/tbprobe.cpp | 4 ++-- src/tune.h | 6 +++--- src/types.h | 6 +++--- src/uci.h | 4 ++-- 12 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/material.h b/src/material.h index 73c831006bf..9acf78f5ab4 100644 --- a/src/material.h +++ b/src/material.h @@ -62,7 +62,7 @@ struct Entry { uint8_t factor[COLOR_NB]; }; -typedef HashTable Table; +using Table = HashTable; Entry* probe(const Position& pos); diff --git a/src/misc.cpp b/src/misc.cpp index e65faab9422..c22126afe2d 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -32,12 +32,12 @@ // the calls at compile time), try to load them at runtime. To do this we need // first to define the corresponding function pointers. extern "C" { -typedef bool(*fun1_t)(LOGICAL_PROCESSOR_RELATIONSHIP, - PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, PDWORD); -typedef bool(*fun2_t)(USHORT, PGROUP_AFFINITY); -typedef bool(*fun3_t)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY); -typedef bool(*fun4_t)(USHORT, PGROUP_AFFINITY, USHORT, PUSHORT); -typedef WORD(*fun5_t)(); +using fun1_t = bool(*)(LOGICAL_PROCESSOR_RELATIONSHIP, + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, PDWORD); +using fun2_t = bool(*)(USHORT, PGROUP_AFFINITY); +using fun3_t = bool(*)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY); +using fun4_t = bool(*)(USHORT, PGROUP_AFFINITY, USHORT, PUSHORT); +using fun5_t = WORD(*)(); } #endif diff --git a/src/misc.h b/src/misc.h index 9761da8addd..c20a816efa0 100644 --- a/src/misc.h +++ b/src/misc.h @@ -45,7 +45,7 @@ void dbg_stdev_of(int64_t value, int slot = 0); void dbg_correl_of(int64_t value1, int64_t value2, int slot = 0); void dbg_print(); -typedef std::chrono::milliseconds::rep TimePoint; // A value in milliseconds +using TimePoint = std::chrono::milliseconds::rep; // A value in milliseconds static_assert(sizeof(TimePoint) == sizeof(int64_t), "TimePoint should be 64 bits"); inline TimePoint now() { return std::chrono::duration_cast @@ -165,7 +165,7 @@ class PRNG { inline uint64_t mul_hi64(uint64_t a, uint64_t b) { #if defined(__GNUC__) && defined(IS_64BIT) - __extension__ typedef unsigned __int128 uint128; + __extension__ using uint128 = unsigned __int128; return ((uint128)a * (uint128)b) >> 64; #else uint64_t aL = (uint32_t)a, aH = a >> 32; diff --git a/src/movepick.h b/src/movepick.h index 90f60b8a961..b6c07378240 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -62,14 +62,14 @@ class StatsEntry { template struct Stats : public std::array, Size> { - typedef Stats stats; + using stats = Stats; void fill(const T& v) { // For standard-layout 'this' points to first struct member assert(std::is_standard_layout::value); - typedef StatsEntry entry; + using entry = StatsEntry; entry* p = reinterpret_cast(this); std::fill(p, p + sizeof(*this) / sizeof(entry), v); } @@ -87,23 +87,23 @@ enum StatsType { NoCaptures, Captures }; /// ordering decisions. It uses 2 tables (one for each color) indexed by /// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards /// (~11 elo) -typedef Stats ButterflyHistory; +using ButterflyHistory = Stats; /// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous /// move, see www.chessprogramming.org/Countermove_Heuristic -typedef Stats CounterMoveHistory; +using CounterMoveHistory = Stats; /// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type] -typedef Stats CapturePieceToHistory; +using CapturePieceToHistory = Stats; /// PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to] -typedef Stats PieceToHistory; +using PieceToHistory = Stats; /// ContinuationHistory is the combined history of a given pair of moves, usually /// the current one given a previous one. The nested history table is based on /// PieceToHistory instead of ButterflyBoards. /// (~63 elo) -typedef Stats ContinuationHistory; +using ContinuationHistory = Stats; /// MovePicker class is used to pick one pseudo-legal move at a time from the diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index b0d5743e669..8087ea55dd0 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -42,8 +42,8 @@ namespace Stockfish::Eval::NNUE { "Per feature PSQT values cannot be processed at granularity lower than 8 at a time."); #ifdef USE_AVX512 - typedef __m512i vec_t; - typedef __m256i psqt_vec_t; + using vec_t = __m512i; + using psqt_vec_t = __m256i; #define vec_load(a) _mm512_load_si512(a) #define vec_store(a,b) _mm512_store_si512(a,b) #define vec_add_16(a,b) _mm512_add_epi16(a,b) @@ -66,8 +66,8 @@ namespace Stockfish::Eval::NNUE { #define MaxChunkSize 64 #elif USE_AVX2 - typedef __m256i vec_t; - typedef __m256i psqt_vec_t; + using vec_t = __m256i; + using psqt_vec_t = __m256i; #define vec_load(a) _mm256_load_si256(a) #define vec_store(a,b) _mm256_store_si256(a,b) #define vec_add_16(a,b) _mm256_add_epi16(a,b) @@ -90,8 +90,8 @@ namespace Stockfish::Eval::NNUE { #define MaxChunkSize 32 #elif USE_SSE2 - typedef __m128i vec_t; - typedef __m128i psqt_vec_t; + using vec_t = __m128i; + using psqt_vec_t = __m128i; #define vec_load(a) (*(a)) #define vec_store(a,b) *(a)=(b) #define vec_add_16(a,b) _mm_add_epi16(a,b) @@ -111,8 +111,8 @@ namespace Stockfish::Eval::NNUE { #define MaxChunkSize 16 #elif USE_MMX - typedef __m64 vec_t; - typedef __m64 psqt_vec_t; + using vec_t = __m64; + using psqt_vec_t = __m64; #define vec_load(a) (*(a)) #define vec_store(a,b) *(a)=(b) #define vec_add_16(a,b) _mm_add_pi16(a,b) @@ -139,8 +139,8 @@ namespace Stockfish::Eval::NNUE { #define MaxChunkSize 8 #elif USE_NEON - typedef int16x8_t vec_t; - typedef int32x4_t psqt_vec_t; + using vec_t = int16x8_t; + using psqt_vec_t = int32x4_t; #define vec_load(a) (*(a)) #define vec_store(a,b) *(a)=(b) #define vec_add_16(a,b) vaddq_s16(a,b) diff --git a/src/pawns.h b/src/pawns.h index 95c9ea3ce20..d20e7c2ebe5 100644 --- a/src/pawns.h +++ b/src/pawns.h @@ -61,7 +61,7 @@ struct Entry { int blockedCount; }; -typedef HashTable Table; +using Table = HashTable; Entry* probe(const Position& pos); diff --git a/src/position.h b/src/position.h index 3de24f22888..c82c7a8bf87 100644 --- a/src/position.h +++ b/src/position.h @@ -68,7 +68,7 @@ struct StateInfo { /// start position to the position just before the search starts). Needed by /// 'draw by repetition' detection. Use a std::deque because pointers to /// elements are not invalidated upon list resizing. -typedef std::unique_ptr> StateListPtr; +using StateListPtr = std::unique_ptr>; /// Position class stores information regarding the board representation as diff --git a/src/search.h b/src/search.h index 48a0f7ce710..806e4be63fa 100644 --- a/src/search.h +++ b/src/search.h @@ -80,7 +80,7 @@ struct RootMove { std::vector pv; }; -typedef std::vector RootMoves; +using RootMoves = std::vector; /// LimitsType struct stores information sent by GUI about available time to diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index bbfd819d3fa..b594ac3714e 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -142,7 +142,7 @@ struct SparseEntry { static_assert(sizeof(SparseEntry) == 6, "SparseEntry must be 6 bytes"); -typedef uint16_t Sym; // Huffman symbol +using Sym = uint16_t; // Huffman symbol struct LR { enum Side { Left, Right }; @@ -327,7 +327,7 @@ struct PairsData { // first access, when the corresponding file is memory mapped. template struct TBTable { - typedef typename std::conditional::type Ret; + using Ret = typename std::conditional::type; static constexpr int Sides = Type == WDL ? 2 : 1; diff --git a/src/tune.h b/src/tune.h index f5b787afce5..440d950a9ec 100644 --- a/src/tune.h +++ b/src/tune.h @@ -26,8 +26,8 @@ namespace Stockfish { -typedef std::pair Range; // Option's min-max values -typedef Range (RangeFun) (int); +using Range = std::pair; // Option's min-max values +using RangeFun = Range (int); // Default Range function, to calculate Option's min-max values inline Range default_range(int v) { @@ -75,7 +75,7 @@ struct SetRange { class Tune { - typedef void (PostUpdate) (); // Post-update function + using PostUpdate = void (); // Post-update function Tune() { read_results(); } Tune(const Tune&) = delete; diff --git a/src/types.h b/src/types.h index 8a021342e25..37ce343a4da 100644 --- a/src/types.h +++ b/src/types.h @@ -103,8 +103,8 @@ constexpr bool Is64Bit = true; constexpr bool Is64Bit = false; #endif -typedef uint64_t Key; -typedef uint64_t Bitboard; +using Key = uint64_t; +using Bitboard = uint64_t; constexpr int MAX_MOVES = 256; constexpr int MAX_PLY = 246; @@ -218,7 +218,7 @@ constexpr Value PieceValue[PHASE_NB][PIECE_NB] = { VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg, VALUE_ZERO, VALUE_ZERO } }; -typedef int Depth; +using Depth = int; enum : int { DEPTH_QS_CHECKS = 0, diff --git a/src/uci.h b/src/uci.h index 5d8ccf1afff..70e45accd1c 100644 --- a/src/uci.h +++ b/src/uci.h @@ -45,12 +45,12 @@ struct CaseInsensitiveLess { }; /// The options container is defined as a std::map -typedef std::map OptionsMap; +using OptionsMap = std::map; /// The Option class implements each option as specified by the UCI protocol class Option { - typedef void (*OnChange)(const Option&); + using OnChange = void (*)(const Option&); public: Option(OnChange = nullptr); From 6adbc6fa05bbd2a574a8c4c6a6ef2307280217ab Mon Sep 17 00:00:00 2001 From: Dubslow Date: Wed, 22 Feb 2023 05:45:43 -0600 Subject: [PATCH 0990/1766] Late counter bonus: boost underestimated moves The idea here is very intuitive: since we've just proven that the move is good, then if it previously had poor stats, boost those stats more than otherwise. Passed STC: https://tests.stockfishchess.org/tests/view/63fb504ce74a12625bce4154 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 21128 W: 5763 L: 5481 D: 9884 Ptnml(0-2): 52, 2212, 5759, 2484, 57 Passed LTC: https://tests.stockfishchess.org/tests/view/63fb7825e74a12625bce491b LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 91904 W: 24764 L: 24359 D: 42781 Ptnml(0-2): 45, 8735, 27984, 9146, 42 closes https://github.com/official-stockfish/Stockfish/pull/4415 bench 4318808 --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index cfb569b9093..6585f7bcabe 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1383,8 +1383,7 @@ namespace { // Bonus for prior countermove that caused the fail low else if (!priorCapture) { - // Extra bonuses for PV/Cut nodes or bad fail lows - int bonus = (depth > 5) + (PvNode || cutNode) + (bestValue < alpha - 97 * depth); + int bonus = (depth > 5) + (PvNode || cutNode) + (bestValue < alpha - 97 * depth) + ((ss-1)->moveCount > 10); update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); } From 876906965b8d552866486c0e6eda1184fdb1d636 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Fri, 24 Feb 2023 09:16:55 -0500 Subject: [PATCH 0991/1766] Update default net to nn-52471d67216a.nnue Created by retraining the master net with modifications to the previous best dataset: * Improving T80 oct+nov 2022 endgame lambda accuracy by rescoring with 12-16tb of syzygy 7p tablebases * Filtering T78 jun+jul+aug 2022 with d6pv2 search to remove positions with bestmove captures or one good move * Adding T80 sep 2022 data, rescored with 16tb of 7p tablebases, unfiltered Trained with max-epoch 900, end-lambda 0.7, and early-fen-skipping 28. ``` python3 easy_train.py \ --experiment-name leela96-dfrc99-T80octnovT79aprmayT78junjulaugT60novdec-filt-v2-T78sep12tb7p-T77decT80sep16tb7p-lambda7-sk28 \ --training-dataset /data/leela96-dfrc99-T80octnovT79aprmayT78junjulaugT60novdec-filt-v2-T78sep12tb7p-T77decT80sep16tb7p.binpack \ --nnue-pytorch-branch linrock/nnue-pytorch/easy-train-early-fen-skipping \ --early-fen-skipping 28 \ --start-from-engine-test-net True \ --gpus "0," \ --max_epoch 900 \ --start-lambda 1.0 \ --end-lambda 0.7 \ --gamma 0.995 \ --lr 4.375e-4 \ --tui False \ --seed $RANDOM ``` Training data was rescored and d6pv2 filtered in the same way as recent best datasets. For preparing the merged training dataset: ``` python3 interleave_binpacks.py \ leela96-eval-filt-v2.binpack \ dfrc99-eval-filt-v2.binpack \ test80-oct2022-16tb7p-eval-filt-v2-d6.binpack \ test80-nov2022-12tb7p-eval-filt-v2-d6.binpack \ T79-apr2022-12tb7p-eval-filt-v2.binpack \ T79-may2022-12tb7p-eval-filt-v2.binpack \ test78-junjulaug2022-16tb7p-eval-filt-v2-d6.binpack \ T60-nov2021-12tb7p-eval-filt-v2.binpack \ T60-dec2021-12tb7p-eval-filt-v2.binpack \ T78-sep2022-12tb7p.binpack \ test77-dec2021-16gb7p.binpack \ test80-sep2022-16tb7p.binpack \ /data/leela96-dfrc99-T80octnovT79aprmayT78junjulaugT60novdec-filt-v2-T78sep12tb7p-T77decT80sep16tb7p.binpack ``` Links for downloading the training data components can be found at: https://robotmoon.com/nnue-training-data/ Local elo at 25k nodes per move: nn-epoch839.nnue : 0.6 +/- 1.4 Passed STC: https://tests.stockfishchess.org/tests/view/63f9ab4be74a12625bcdf02e LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 84656 W: 22681 L: 22302 D: 39673 Ptnml(0-2): 271, 9343, 22734, 9696, 284 Passed LTC: https://tests.stockfishchess.org/tests/view/63fa3833e74a12625bce0c0e LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 184664 W: 49933 L: 49344 D: 85387 Ptnml(0-2): 111, 17977, 55561, 18578, 105 closes https://github.com/official-stockfish/Stockfish/pull/4416 bench: 4814343 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 46f202594e6..5238cb81c54 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-1337b1adec5b.nnue" + #define EvalFileDefaultName "nn-52471d67216a.nnue" namespace NNUE { From 5c75c1c2fbb7bb4f0bf7c44fb855c415b788cbf7 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Fri, 24 Feb 2023 12:09:45 +0300 Subject: [PATCH 0992/1766] Fix duplicated moves generation in movepicker in a some of cases movepicker returned some moves more than once which lead to them being searched more than once. This bug was possible because of how we use queen promotions - they are generated as a captures but are not included in position function which checks if move is a capture. Thus if any refutation (killer or countermove) was a queen promotion it was searched twice - once as a capture and one as a refutation. This patch affects various things, namely stats assignments for queen promotions and other moves if best move is queen promotion, also some heuristics in search and qsearch. With this patch every queen promotion is now considered a capture. After this patch number of found duplicated moves is 0 during normal 13 depth bench run. Passed STC: https://tests.stockfishchess.org/tests/view/63f77e01e74a12625bcd87d7 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 80920 W: 21455 L: 21289 D: 38176 Ptnml(0-2): 198, 8839, 22241, 8963, 219 Passed LTC: https://tests.stockfishchess.org/tests/view/63f7e020e74a12625bcd9a76 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 89712 W: 23674 L: 23533 D: 42505 Ptnml(0-2): 24, 8737, 27202, 8860, 33 closes https://github.com/official-stockfish/Stockfish/pull/4405 bench 4681731 --- src/movepick.cpp | 6 +++--- src/position.h | 12 ++++++++---- src/search.cpp | 12 ++++++------ src/syzygy/tbprobe.cpp | 4 ++-- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 65155a73f79..36ee46b50f0 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -93,7 +93,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePiece { assert(!pos.checkers()); - stage = PROBCUT_TT + !(ttm && pos.capture(ttm) + stage = PROBCUT_TT + !(ttm && pos.capture_stage(ttm) && pos.pseudo_legal(ttm) && pos.see_ge(ttm, threshold)); } @@ -141,7 +141,7 @@ void MovePicker::score() { + bool(pos.check_squares(type_of(pos.moved_piece(m))) & to_sq(m)) * 16384; else // Type == EVASIONS { - if (pos.capture(m)) + if (pos.capture_stage(m)) m.value = PieceValue[MG][pos.piece_on(to_sq(m))] - Value(type_of(pos.moved_piece(m))) + (1 << 28); @@ -216,7 +216,7 @@ Move MovePicker::next_move(bool skipQuiets) { case REFUTATION: if (select([&](){ return *cur != MOVE_NONE - && !pos.capture(*cur) + && !pos.capture_stage(*cur) && pos.pseudo_legal(*cur); })) return *(cur - 1); ++stage; diff --git a/src/position.h b/src/position.h index c82c7a8bf87..485540ef866 100644 --- a/src/position.h +++ b/src/position.h @@ -125,7 +125,7 @@ class Position { // Properties of moves bool legal(Move m) const; bool pseudo_legal(const Move m) const; - bool capture(Move m) const; + bool capture_stage(Move m) const; bool gives_check(Move m) const; Piece moved_piece(Move m) const; Piece captured_piece() const; @@ -381,10 +381,14 @@ inline bool Position::is_chess960() const { return chess960; } -inline bool Position::capture(Move m) const { +// returns true if a move is generated from the capture stage +// having also queen promotions covered, i.e. consistency with the capture stage move generation +// is needed to avoid the generation of duplicate moves. +inline bool Position::capture_stage(Move m) const { assert(is_ok(m)); - // Castling is encoded as "king captures rook" - return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == EN_PASSANT; + return (!empty(to_sq(m)) && type_of(m) != CASTLING) + || (type_of(m) == PROMOTION && promotion_type(m) == QUEEN) + || type_of(m) == EN_PASSANT; } inline Piece Position::captured_piece() const { diff --git a/src/search.cpp b/src/search.cpp index 6585f7bcabe..fcdb8d67545 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -623,7 +623,7 @@ namespace { ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] : ss->ttHit ? tte->move() : MOVE_NONE; - ttCapture = ttMove && pos.capture(ttMove); + ttCapture = ttMove && pos.capture_stage(ttMove); // At this point, if excluded, skip straight to step 6, static eval. However, // to save indentation, we list the condition in all code between here and there. @@ -852,7 +852,7 @@ namespace { probCutBeta = beta + 186 - 54 * improving; // Step 10. ProbCut (~10 Elo) - // If we have a good enough capture and a reduced search returns a value + // If we have a good enough capture (or queen promotion) and a reduced search returns a value // much above beta, we can (almost) safely prune the previous move. if ( !PvNode && depth > 4 @@ -873,7 +873,7 @@ namespace { while ((move = mp.next_move()) != MOVE_NONE) if (move != excludedMove && pos.legal(move)) { - assert(pos.capture(move) || promotion_type(move) == QUEEN); + assert(pos.capture_stage(move)); ss->currentMove = move; ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck] @@ -985,7 +985,7 @@ namespace { (ss+1)->pv = nullptr; extension = 0; - capture = pos.capture(move); + capture = pos.capture_stage(move); movedPiece = pos.moved_piece(move); givesCheck = pos.gives_check(move); @@ -1542,7 +1542,7 @@ namespace { continue; givesCheck = pos.gives_check(move); - capture = pos.capture(move); + capture = pos.capture_stage(move); moveCount++; @@ -1715,7 +1715,7 @@ namespace { PieceType captured = type_of(pos.piece_on(to_sq(bestMove))); int bonus1 = stat_bonus(depth + 1); - if (!pos.capture(bestMove)) + if (!pos.capture_stage(bestMove)) { int bonus2 = bestValue > beta + 153 ? bonus1 // larger bonus : stat_bonus(depth); // smaller bonus diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index b594ac3714e..2a9e1b68d37 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1203,7 +1203,7 @@ WDLScore search(Position& pos, ProbeState* result) { for (const Move move : moveList) { - if ( !pos.capture(move) + if ( !pos.capture_stage(move) && (!CheckZeroingMoves || type_of(pos.moved_piece(move)) != PAWN)) continue; @@ -1472,7 +1472,7 @@ int Tablebases::probe_dtz(Position& pos, ProbeState* result) { for (const Move move : MoveList(pos)) { - bool zeroing = pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN; + bool zeroing = pos.capture_stage(move) || type_of(pos.moved_piece(move)) == PAWN; pos.do_move(move, st); From 5c589142ae5714ecb9ded29bec12c591a1ba8f3f Mon Sep 17 00:00:00 2001 From: disservin Date: Sat, 4 Mar 2023 16:34:34 +0100 Subject: [PATCH 0993/1766] Add wiki to artifacts snapshot the wiki https://github.com/official-stockfish/stockfish/wiki as part of the artifacts generated. This will allow future release to include the wiki pages as a form of documentation closes https://github.com/official-stockfish/Stockfish/pull/4420 No functional change --- .github/workflows/stockfish_arm_binaries.yml | 7 +++++++ .github/workflows/stockfish_binaries.yml | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/.github/workflows/stockfish_arm_binaries.yml b/.github/workflows/stockfish_arm_binaries.yml index a1b3cdab0ea..e088c441925 100644 --- a/.github/workflows/stockfish_arm_binaries.yml +++ b/.github/workflows/stockfish_arm_binaries.yml @@ -99,10 +99,17 @@ jobs: - name: Remove non src files run: rm -f *.o .depend *.nnue + - name: Download wiki + run: | + git clone https://github.com/official-stockfish/Stockfish.wiki.git ../wiki + cd ../wiki + rm -rf .git + - name: Create tar archive. run: | cd .. mkdir stockfish + cp -r wiki stockfish/ cp -r src stockfish/ cp stockfish-android-$BINARY$EXT stockfish/ cp "Top CPU Contributors.txt" stockfish/ diff --git a/.github/workflows/stockfish_binaries.yml b/.github/workflows/stockfish_binaries.yml index 06b13a9e095..b22897cf692 100644 --- a/.github/workflows/stockfish_binaries.yml +++ b/.github/workflows/stockfish_binaries.yml @@ -83,10 +83,17 @@ jobs: - name: Remove non src files run: rm -f *.o .depend *.nnue + - name: Download wiki + run: | + git clone https://github.com/official-stockfish/Stockfish.wiki.git ../wiki + cd ../wiki + rm -rf .git + - name: Create tar archive. run: | cd .. mkdir stockfish + cp -r wiki stockfish/ cp -r src stockfish/ cp stockfish-$OS-$BINARY$EXT stockfish/ cp "Top CPU Contributors.txt" stockfish/ From 3a634f5282700484445c0f66b21b35e96fcb947b Mon Sep 17 00:00:00 2001 From: dav1312 <63931154+dav1312@users.noreply.github.com> Date: Sat, 4 Mar 2023 13:24:35 +0100 Subject: [PATCH 0994/1766] Update README.md Update and simplify the readme, removing duplicated and outdated stuff and pointing to the new wiki closes https://github.com/official-stockfish/Stockfish/pull/4421 No functional change --- README.md | 376 ++++++++++++------------------------------------------ 1 file changed, 81 insertions(+), 295 deletions(-) diff --git a/README.md b/README.md index ca90d5d4402..1f462d315af 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,21 @@ [![Stockfish][stockfish128-logo]][website-link] +

Stockfish

+ + A free and strong UCI chess engine. +
+ [Explore Stockfish docs »][wiki-link] +
+
+ [Report bug][issue-link] + · + [Open a discussion][discussions-link] + · + [Discord][discord-link] + · + [Blog][website-blog-link] + [![Build][build-badge]][build-link] [![License][license-badge]][license-link]
@@ -16,19 +31,16 @@ ## Overview -[Stockfish][website-link] is a free, powerful UCI chess engine derived from -Glaurung 2.1. Stockfish is not a complete chess program and requires a UCI-compatible -graphical user interface (GUI) (e.g. XBoard with PolyGlot, Scid, Cute Chess, eboard, -Arena, Sigma Chess, Shredder, Chess Partner or Fritz) in order to be used comfortably. -Read the documentation for your GUI of choice for information about how to use +[Stockfish][website-link] is a **free and strong UCI chess engine** derived from +Glaurung 2.1 that analyzes chess positions and computes the optimal moves. + +Stockfish **does not include a graphical user interface** (GUI) that is required +to display a chessboard and to make it easy to input moves. These GUIs are +developed independently from Stockfish and are available online. **Read the +documentation for your GUI** of choice for information about how to use Stockfish with it. -The Stockfish engine features two evaluation functions for chess. The efficiently -updatable neural network (NNUE) based evaluation is the default and by far the strongest. -The classical evaluation based on handcrafted terms remains available. The strongest -network is integrated in the binary and downloaded automatically during the build process. -The NNUE evaluation benefits from the vector intrinsics available on most CPUs (sse2, -avx2, neon, or similar). +See also the Stockfish [documentation][wiki-usage-link] for further usage help. ## Files @@ -36,338 +48,112 @@ This distribution of Stockfish consists of the following files: * [README.md][readme-link], the file you are currently reading. - * [Copying.txt][license-link], a text file containing the GNU General Public License - version 3. + * [Copying.txt][license-link], a text file containing the GNU General Public + License version 3. * [AUTHORS][authors-link], a text file with the list of authors for the project. - * [src][src-link], a subdirectory containing the full source code, including a Makefile - that can be used to compile Stockfish on Unix-like systems. - - * a file with the .nnue extension, storing the neural network for the NNUE evaluation. - Binary distributions will have this file embedded. - -## The UCI protocol and available options - -The Universal Chess Interface (UCI) is a standard protocol used to communicate with -a chess engine, and is the recommended way to do so for typical graphical user interfaces -(GUI) or chess tools. Stockfish implements the majority of its options as described -in [the UCI protocol][uci-link]. - -Developers can see the default values for UCI options available in Stockfish by typing -`./stockfish uci` in a terminal, but the majority of users will typically see them and -change them via a chess GUI. This is a list of available UCI options in Stockfish: - - * #### Threads - The number of CPU threads used for searching a position. For best performance, set - this equal to the number of CPU cores available. - - * #### Hash - The size of the hash table in MB. It is recommended to set Hash after setting Threads. - - * #### Clear Hash - Clear the hash table. - - * #### Ponder - Let Stockfish ponder its next move while the opponent is thinking. - - * #### MultiPV - Output the N best lines (principal variations, PVs) when searching. - Leave at 1 for best performance. - - * #### Use NNUE - Toggle between the NNUE and classical evaluation functions. If set to "true", - the network parameters must be available to load from file (see also EvalFile), - if they are not embedded in the binary. - - * #### EvalFile - The name of the file of the NNUE evaluation parameters. Depending on the GUI the - filename might have to include the full path to the folder/directory that contains - the file. Other locations, such as the directory that contains the binary and the - working directory, are also searched. - - * #### UCI_AnalyseMode - An option handled by your GUI. - - * #### UCI_Chess960 - An option handled by your GUI. If true, Stockfish will play Chess960. + * [src][src-link], a subdirectory containing the full source code, including a + Makefile that can be used to compile Stockfish on Unix-like systems. - * #### UCI_ShowWDL - If enabled, show approximate WDL statistics as part of the engine output. - These WDL numbers model expected game outcomes for a given evaluation and - game ply for engine self-play at fishtest LTC conditions (60+0.6s per game). + * a file with the .nnue extension, storing the neural network for the NNUE + evaluation. Binary distributions will have this file embedded. - * #### UCI_LimitStrength - Enable weaker play aiming for an Elo rating as set by UCI_Elo. This option overrides Skill Level. +## The UCI protocol - * #### 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 120s+1.0s and anchored to +- 100 Elo to CCRL Blitz. +The [Universal Chess Interface][uci-link] (UCI) is a standard text-based protocol +used to communicate with a chess engine and is the recommended way to do so for +typical graphical user interfaces (GUI) or chess tools. Stockfish implements the +majority of its options. - * #### Skill Level - 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. +Developers can see the default values for the UCI options available in Stockfish +by typing `./stockfish uci` in a terminal, but most users should typically use a +chess GUI to interact with Stockfish. - * #### SyzygyPath - Path to the folders/directories storing the Syzygy tablebase files. Multiple - directories are to be separated by ";" on Windows and by ":" on Unix-based - operating systems. Do not use spaces around the ";" or ":". +For more information on UCI or debug commands, see our [documentation][wiki-commands-link]. - Example: `C:\tablebases\wdl345;C:\tablebases\wdl6;D:\tablebases\dtz345;D:\tablebases\dtz6` +## Compiling Stockfish - It is recommended to store .rtbw files on an SSD. There is no loss in storing - the .rtbz files on a regular HDD. It is recommended to verify all md5 checksums - of the downloaded tablebase files (`md5sum -c checksum.md5`) as corruption will - lead to engine crashes. +Stockfish has support for 32 or 64-bit CPUs, certain hardware instructions, +big-endian machines such as Power PC, and other platforms. - * #### SyzygyProbeDepth - Minimum remaining search depth for which a position is probed. Set this option - to a higher value to probe less aggressively if you experience too much slowdown - (in terms of nps) due to tablebase probing. +On Unix-like systems, it should be easy to compile Stockfish directly from the +source code with the included Makefile in the folder `src`. In general, it is +recommended to run `make help` to see a list of make targets with corresponding +descriptions. - * #### Syzygy50MoveRule - Disable to let fifty-move rule draws detected by Syzygy tablebase probes count - as wins or losses. This is useful for ICCF correspondence games. - - * #### SyzygyProbeLimit - Limit Syzygy tablebase probing to positions with at most this many pieces left - (including kings and pawns). - - * #### Move Overhead - Assume a time delay of x ms due to network and GUI overheads. This is useful to - avoid losses on time in those cases. - - * #### Slow Mover - Lower values will make Stockfish take less time in games, higher values will - make it think longer. - - * #### nodestime - Tells the engine to use nodes searched instead of wall time to account for - elapsed time. Useful for engine testing. - - * #### Debug Log File - Write all communication to and from the engine into a text file. - -For developers the following non-standard commands might be of interest, mainly useful for debugging: - - * #### bench *ttSize threads limit fenFile limitType evalType* - Performs a standard benchmark using various options. The signature of a version - (standard node count) is obtained using all defaults. `bench` is currently - `bench 16 1 13 default depth mixed`. - - * #### compiler - Give information about the compiler and environment used for building a binary. - - * #### d - Display the current position, with ascii art and fen. - - * #### eval - Return the evaluation of the current position. - - * #### export_net [filename] - Exports the currently loaded network to a file. - If the currently loaded network is the embedded network and the filename - is not specified then the network is saved to the file matching the name - of the embedded network, as defined in evaluate.h. - If the currently loaded network is not the embedded network (some net set - through the UCI setoption) then the filename parameter is required and the - network is saved into that file. - - * #### flip - Flips the side to move. - - -## A note on classical evaluation versus NNUE evaluation - -Both approaches assign a value to a position that is used in alpha-beta (PVS) search -to find the best move. The classical evaluation computes this value as a function -of various chess concepts, handcrafted by experts, tested and tuned using fishtest. -The NNUE evaluation computes this value with a neural network based on basic -inputs (e.g. piece positions only). The network is optimized and trained -on the evaluations of millions of positions at moderate search depth. - -The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward. -It can be evaluated efficiently on CPUs, and exploits the fact that only parts -of the neural network need to be updated after a typical chess move. -[The nodchip repository][nodchip-link] provided the first version of the needed tools -to train and develop the NNUE networks. Today, more advanced training tools are -available in [the nnue-pytorch repository][pytorch-link], while data generation tools -are available in [a dedicated branch][tools-link]. - -On CPUs supporting modern vector instructions (avx2 and similar), the NNUE evaluation -results in much stronger playing strength, even if the nodes per second computed by -the engine is somewhat lower (roughly 80% of nps is typical). - -Notes: - -1) the NNUE evaluation depends on the Stockfish binary and the network parameter file -(see the EvalFile UCI option). Not every parameter file is compatible with a given -Stockfish binary, but the default value of the EvalFile UCI option is the name of a -network that is guaranteed to be compatible with that binary. - -2) to use the NNUE evaluation, the additional data file with neural network parameters -needs to be available. Normally, this file is already embedded in the binary or it can -be downloaded. The filename for the default (recommended) net can be found as the default -value of the `EvalFile` UCI option, with the format `nn-[SHA256 first 12 digits].nnue` -(for instance, `nn-c157e0a5755b.nnue`). This file can be downloaded from ``` -https://tests.stockfishchess.org/api/nn/[filename] +cd src +make -j build ARCH=x86-64-modern ``` -replacing `[filename]` as needed. - -## What to expect from the Syzygy tablebases? - -If the engine is searching a position that is not in the tablebases (e.g. -a position with 8 pieces), it will access the tablebases during the search. -If the engine reports a very large score (typically 153.xx), this means -it has found a winning line into a tablebase position. - -If the engine is given a position to search that is in the tablebases, it -will use the tablebases at the beginning of the search to preselect all -good moves, i.e. all moves that preserve the win or preserve the draw while -taking into account the 50-move rule. -It will then perform a search only on those moves. **The engine will not move -immediately**, unless there is only a single good move. **The engine likely -will not report a mate score, even if the position is known to be won.** - -It is therefore clear that this behaviour is not identical to what one might -be used to with Nalimov tablebases. There are technical reasons for this -difference, the main technical reason being that Nalimov tablebases use the -DTM metric (distance-to-mate), while the Syzygy tablebases use a variation of the -DTZ metric (distance-to-zero, zero meaning any move that resets the 50-move -counter). This special metric is one of the reasons that the Syzygy tablebases are -more compact than Nalimov tablebases, while still storing all information -needed for optimal play and in addition being able to take into account -the 50-move rule. - -## Large Pages - -Stockfish supports large pages on Linux and Windows. Large pages make -the hash access more efficient, improving the engine speed, especially -on large hash sizes. Typical increases are 5..10% in terms of nodes per -second, but speed increases up to 30% have been measured. The support is -automatic. Stockfish attempts to use large pages when available and -will fall back to regular memory allocation when this is not the case. - -### Support on Linux - -Large page support on Linux is obtained by the Linux kernel -transparent huge pages functionality. Typically, transparent huge pages -are already enabled, and no configuration is needed. - -### Support on Windows - -The use of large pages requires "Lock Pages in Memory" privilege. See -[Enable the Lock Pages in Memory Option (Windows)][lockpages-link] -on how to enable this privilege, then run [RAMMap][rammap-link] -to double-check that large pages are used. We suggest that you reboot -your computer after you have enabled large pages, because long Windows -sessions suffer from memory fragmentation, which may prevent Stockfish -from getting large pages: a fresh session is better in this regard. - -## Compiling Stockfish yourself from the sources - -Stockfish has support for 32 or 64-bit CPUs, certain hardware -instructions, big-endian machines such as Power PC, and other platforms. - -On Unix-like systems, it should be easy to compile Stockfish -directly from the source code with the included Makefile in the folder -`src`. In general it is recommended to run `make help` to see a list of make -targets with corresponding descriptions. -``` - cd src - make help - make net - make build ARCH=x86-64-modern -``` +Detailed compilation instructions for all platforms can be found in our +[documentation][wiki-compile-link]. -When not using the Makefile to compile (for instance, with Microsoft MSVC) you -need to manually set/unset some switches in the compiler command line; see -file *types.h* for a quick reference. - -When reporting an issue or a bug, please tell us which Stockfish version -and which compiler you used to create your executable. This information -can be found by typing the following command in a console: - -``` - ./stockfish compiler -``` - -## Understanding the code base and participating in the project - -Stockfish's improvement over the last decade has been a great community -effort. There are a few ways to help contribute to its growth. +## Contributing ### Donating hardware -Improving Stockfish requires a massive amount of testing. You can donate -your hardware resources by installing the [Fishtest Worker][worker-link] -and view the current tests on [Fishtest][fishtest-link]. +Improving Stockfish requires a massive amount of testing. You can donate your +hardware resources by installing the [Fishtest Worker][worker-link] and viewing +the current tests on [Fishtest][fishtest-link]. ### Improving the code -If you want to help improve the code, there are several valuable resources: - -* [In this wiki,][programming-link] many techniques used in +In the [chessprogramming wiki][programming-link], many techniques used in Stockfish are explained with a lot of background information. +The [section on Stockfish][programmingsf-link] describes many features +and techniques used by Stockfish. However, it is generic rather than +focused on Stockfish's precise implementation. -* [The section on Stockfish][programmingsf-link] -describes many features and techniques used by Stockfish. However, it is -generic rather than being focused on Stockfish's precise implementation. -Nevertheless, a helpful resource. - -* The latest source can always be found on [GitHub][github-link]. -Discussions about Stockfish take place these days mainly in the [FishCooking][fishcooking-link] -group and on the [Stockfish Discord channel][discord-link]. The engine testing is done on [Fishtest][fishtest-link]. If you want to help improve Stockfish, please read this [guideline][guideline-link] first, where the basics of Stockfish development are explained. +Discussions about Stockfish take place these days mainly in the Stockfish +[Discord server][discord-link]. This is also the best place to ask questions +about the codebase and how to improve it. ## Terms of use -Stockfish is free, and distributed under the **GNU General Public License version 3** -(GPL v3). Essentially, this means you are free to do almost exactly -what you want with the program, including distributing it among your -friends, making it available for download from your website, selling -it (either by itself or as part of some bigger software package), or -using it as the starting point for a software project of your own. - -The only real limitation is that whenever you distribute Stockfish in -some way, you MUST always include the license and the full source code -(or a pointer to where the source code can be found) to generate the -exact binary you are distributing. If you make any changes to the -source code, these changes must also be made available under the GPL v3. +Stockfish is free and distributed under the +[**GNU General Public License version 3**][license-link] (GPL v3). Essentially, +this means you are free to do almost exactly what you want with the program, +including distributing it among your friends, making it available for download +from your website, selling it (either by itself or as part of some bigger +software package), or using it as the starting point for a software project of +your own. -For full details, read the copy of the GPL v3 found in the file named -[*Copying.txt*][license-link]. +The only real limitation is that whenever you distribute Stockfish in some way, +you MUST always include the license and the full source code (or a pointer to +where the source code can be found) to generate the exact binary you are +distributing. If you make any changes to the source code, these changes must +also be made available under GPL v3. [authors-link]: https://github.com/official-stockfish/Stockfish/blob/master/AUTHORS [build-link]: https://github.com/official-stockfish/Stockfish/actions/workflows/stockfish.yml [commits-link]: https://github.com/official-stockfish/Stockfish/commits/master [discord-link]: https://discord.gg/GWDRS3kU6R -[fishcooking-link]: https://groups.google.com/g/fishcooking +[issue-link]: https://github.com/official-stockfish/Stockfish/issues/new?assignees=&labels=&template=BUG-REPORT.yml +[discussions-link]: https://github.com/official-stockfish/Stockfish/discussions/new [fishtest-link]: https://tests.stockfishchess.org/tests -[github-link]: https://github.com/official-stockfish/Stockfish [guideline-link]: https://github.com/glinscott/fishtest/wiki/Creating-my-first-test [license-link]: https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt -[lockpages-link]: https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/enable-the-lock-pages-in-memory-option-windows -[nodchip-link]: https://github.com/nodchip/Stockfish [programming-link]: https://www.chessprogramming.org/Main_Page [programmingsf-link]: https://www.chessprogramming.org/Stockfish -[pytorch-link]: https://github.com/glinscott/nnue-pytorch -[rammap-link]: https://docs.microsoft.com/en-us/sysinternals/downloads/rammap [readme-link]: https://github.com/official-stockfish/Stockfish/blob/master/README.md [release-link]: https://github.com/official-stockfish/Stockfish/releases/latest [src-link]: https://github.com/official-stockfish/Stockfish/tree/master/src [stockfish128-logo]: https://stockfishchess.org/images/logo/icon_128x128.png -[tools-link]: https://github.com/official-stockfish/Stockfish/tree/tools -[uci-link]: https://www.shredderchess.com/download/div/uci.zip +[uci-link]: https://backscattering.de/chess/uci/ [website-link]: https://stockfishchess.org -[worker-link]: https://github.com/glinscott/fishtest/wiki/Running-the-worker:-overview +[website-blog-link]: https://stockfishchess.org/blog/ +[wiki-link]: https://github.com/official-stockfish/Stockfish/wiki +[wiki-usage-link]: https://github.com/official-stockfish/Stockfish/wiki/Download-and-usage +[wiki-compile-link]: https://github.com/official-stockfish/Stockfish/wiki/Compiling-from-source +[wiki-commands-link]: https://github.com/official-stockfish/Stockfish/wiki/Commands +[worker-link]: https://github.com/glinscott/fishtest/wiki/Running-the-worker [build-badge]: https://img.shields.io/github/actions/workflow/status/official-stockfish/Stockfish/stockfish.yml?branch=master&style=for-the-badge&label=stockfish&logo=github [commits-badge]: https://img.shields.io/github/commits-since/official-stockfish/Stockfish/latest?style=for-the-badge From cdec775a1555ef83cb9c878c42a11b7dde0a627b Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 4 Mar 2023 18:14:23 +0100 Subject: [PATCH 0995/1766] Add CITATION.cff file Make the stockfish software more easily citable, for example in academic papers. fixes https://github.com/official-stockfish/Stockfish/issues/4419 closes https://github.com/official-stockfish/Stockfish/pull/4422 No functional change --- CITATION.cff | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 CITATION.cff diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 00000000000..bc0889a8b69 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,23 @@ +# This CITATION.cff file was generated with cffinit. +# Visit https://bit.ly/cffinit to generate yours today! + +cff-version: 1.2.0 +title: Stockfish +message: >- + Please cite this software using the metadata from this + file. +type: software +authors: + - name: The Stockfish developers (see AUTHORS file) +repository-code: 'https://github.com/official-stockfish/Stockfish' +url: 'https://stockfishchess.org/' +repository-artifact: 'https://stockfishchess.org/download/' +abstract: Stockfish is a free and strong UCI chess engine. +keywords: + - chess + - artificial intelligence (AI) + - tree search + - alpha-beta search + - neural networks (NN) + - efficiently updatable neural networks (NNUE) +license: GPL-3.0 From 70dfa141d560c18cd1aa28884b7cd8ab0f094944 Mon Sep 17 00:00:00 2001 From: Maxim Masiutin Date: Sun, 5 Mar 2023 17:10:52 +0200 Subject: [PATCH 0996/1766] Clarify the description of the x86-64-vnni256 and x86-64-avxvnni architectures Now it is clearly explained that "x86-64-vnni256" requires full support of AVX512-VNNI, but only 256-bit operands are used. closes https://github.com/official-stockfish/Stockfish/pull/4427 No functional change --- AUTHORS | 1 + src/Makefile | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index 634de4a3d90..49f7009f10b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -134,6 +134,7 @@ Matt Ginsberg (mattginsberg) Matthew Lai (matthewlai) Matthew Sullivan (Matt14916) Max A. (Disservin) +Maxim Masiutin (maximmasiutin) Maxim Molchanov (Maxim) Michael An (man) Michael Byrne (MichaelB7) diff --git a/src/Makefile b/src/Makefile index 3d6432fd96b..774ba6ea8c4 100644 --- a/src/Makefile +++ b/src/Makefile @@ -92,7 +92,7 @@ VPATH = syzygy:nnue:nnue/features # avx2 = yes/no --- -mavx2 --- Use Intel Advanced Vector Extensions 2 # avxvnni = yes/no --- -mavxvnni --- Use Intel Vector Neural Network Instructions AVX # avx512 = yes/no --- -mavx512bw --- Use Intel Advanced Vector Extensions 512 -# vnni256 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 256 +# vnni256 = yes/no --- -mavx256vnni --- Use Intel Vector Neural Network Instructions 512 with 256bit operands # vnni512 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 512 # neon = yes/no --- -DUSE_NEON --- Use ARM SIMD architecture # dotprod = yes/no --- -DUSE_NEON_DOTPROD --- Use ARM advanced SIMD Int8 dot product instructions @@ -773,10 +773,10 @@ help: @echo "" @echo "Supported archs:" @echo "" - @echo "x86-64-vnni512 > x86 64-bit with vnni support 512bit wide" - @echo "x86-64-vnni256 > x86 64-bit with vnni support 256bit wide" + @echo "x86-64-vnni512 > x86 64-bit with vnni 512bit support" + @echo "x86-64-vnni256 > x86 64-bit with vnni 512bit support, limit operands to 256bit wide" @echo "x86-64-avx512 > x86 64-bit with avx512 support" - @echo "x86-64-avxvnni > x86 64-bit with avxvnni support" + @echo "x86-64-avxvnni > x86 64-bit with vnni 256bit support" @echo "x86-64-bmi2 > x86 64-bit with bmi2 support" @echo "x86-64-avx2 > x86 64-bit with avx2 support" @echo "x86-64-sse41-popcnt > x86 64-bit with sse41 and popcnt support" From 6ce225bb4c31298b131714eff67b56de3b8ee78d Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 5 Mar 2023 17:26:12 +0100 Subject: [PATCH 0997/1766] Fix TB after capture_stage fix https://github.com/official-stockfish/Stockfish/commit/5c75c1c2fbb7bb4f0bf7c44fb855c415b788cbf7 introduced a capture_stage() function, but TB usage needs a pure capture() function. closes https://github.com/official-stockfish/Stockfish/pull/4428 No functional change. --- src/position.h | 11 ++++++++--- src/syzygy/tbprobe.cpp | 4 ++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/position.h b/src/position.h index 485540ef866..1fc6b3b89c4 100644 --- a/src/position.h +++ b/src/position.h @@ -125,6 +125,7 @@ class Position { // Properties of moves bool legal(Move m) const; bool pseudo_legal(const Move m) const; + bool capture(Move m) const; bool capture_stage(Move m) const; bool gives_check(Move m) const; Piece moved_piece(Move m) const; @@ -381,14 +382,18 @@ inline bool Position::is_chess960() const { return chess960; } +inline bool Position::capture(Move m) const { + assert(is_ok(m)); + return (!empty(to_sq(m)) && type_of(m) != CASTLING) + || type_of(m) == EN_PASSANT; +} + // returns true if a move is generated from the capture stage // having also queen promotions covered, i.e. consistency with the capture stage move generation // is needed to avoid the generation of duplicate moves. inline bool Position::capture_stage(Move m) const { assert(is_ok(m)); - return (!empty(to_sq(m)) && type_of(m) != CASTLING) - || (type_of(m) == PROMOTION && promotion_type(m) == QUEEN) - || type_of(m) == EN_PASSANT; + return capture(m) || (type_of(m) == PROMOTION && promotion_type(m) == QUEEN); } inline Piece Position::captured_piece() const { diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 2a9e1b68d37..b594ac3714e 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1203,7 +1203,7 @@ WDLScore search(Position& pos, ProbeState* result) { for (const Move move : moveList) { - if ( !pos.capture_stage(move) + if ( !pos.capture(move) && (!CheckZeroingMoves || type_of(pos.moved_piece(move)) != PAWN)) continue; @@ -1472,7 +1472,7 @@ int Tablebases::probe_dtz(Position& pos, ProbeState* result) { for (const Move move : MoveList(pos)) { - bool zeroing = pos.capture_stage(move) || type_of(pos.moved_piece(move)) == PAWN; + bool zeroing = pos.capture(move) || type_of(pos.moved_piece(move)) == PAWN; pos.do_move(move, st); From 39da50ed23ee3f1bd32a58c8f02471faa9a9fd63 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Tue, 7 Mar 2023 01:54:20 +0300 Subject: [PATCH 0998/1766] Do more negative extensions This patch does negatively extend transposition table move if singular search failed and tt value is not bigger than alpha. Logic is close to what we had before recent simplification of negative extensions but uses or condition instead of and condition. Passed STC: https://tests.stockfishchess.org/tests/view/6404c8102644b62c33934607 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 119040 W: 31841 L: 31416 D: 55783 Ptnml(0-2): 356, 13070, 32292, 13397, 405 Passed LTC: https://tests.stockfishchess.org/tests/view/6405abda2644b62c33937119 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 47216 W: 12816 L: 12496 D: 21904 Ptnml(0-2): 12, 4500, 14286, 4776, 34 closes https://github.com/official-stockfish/Stockfish/pull/4430 bench 4747020 --- src/search.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index fcdb8d67545..349d43894dd 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1109,6 +1109,10 @@ namespace { // If the eval of ttMove is less than value, we reduce it (negative extension) else if (ttValue <= value) extension = -1; + + // If the eval of ttMove is less than alpha, we reduce it (negative extension) + else if (ttValue <= alpha) + extension = -1; } // Check extensions (~1 Elo) From a48573e15fbd18fecb087c9fc02c38a07cece68b Mon Sep 17 00:00:00 2001 From: Dubslow Date: Thu, 9 Mar 2023 18:33:13 -0600 Subject: [PATCH 0999/1766] More negative extensions on nonsingular nonpv nodes. Following up the previous gainer also in this nonsingular node section of code. Credit shared with @FauziAkram for realizing this nonsingular node stuff had some potential, and @XInTheDark for reminding us that !PvNodes better handle extensions/reductions than Pv. Passed STC: https://tests.stockfishchess.org/tests/view/640a7bb32644b62c339457c3 LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 136776 W: 36598 L: 36149 D: 64029 Ptnml(0-2): 439, 14834, 37384, 15301, 430 Passed LTC: https://tests.stockfishchess.org/tests/view/640c43a02644b62c3394b23c LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 79536 W: 21363 L: 20984 D: 37189 Ptnml(0-2): 28, 7525, 24285, 7900, 30 closes https://github.com/official-stockfish/Stockfish/pull/4441 Bench: 4444953 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 349d43894dd..582e44576b3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1104,7 +1104,7 @@ namespace { // If the eval of ttMove is greater than beta, we reduce it (negative extension) else if (ttValue >= beta) - extension = -2; + extension = -2 - !PvNode; // If the eval of ttMove is less than value, we reduce it (negative extension) else if (ttValue <= value) From 78532af9dc585a81730d5d28763cc9d5273f96d1 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Mon, 13 Mar 2023 15:57:58 +0300 Subject: [PATCH 1000/1766] Do more singular extensions This patch continues trend of last VLTC tuning - as measured by dubslow most of it gains was in lowering marging in calculation of singularBeta. This patch is a manual adjustment on top of it - it lowers multiplier of depth in calculation of singularBeta even further, from 2/3 to 1,5/2,5. Was negative at STC: https://tests.stockfishchess.org/tests/view/64089c632644b62c3393fc12 Elo: -2.49 +-1.7 (95%) LOS: 0.2% Total: 40000 W: 10601 L: 10888 D: 18511 Ptnml(0-2): 123, 4580, 10875, 4305, 117 nElo: -5.03 +-3.4 (95%) PairsRatio: 0.94 Passed 180+1.8 SPRT: https://tests.stockfishchess.org/tests/view/640096dae74a12625bcf3b33 LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 160952 W: 43753 L: 43342 D: 73857 Ptnml(0-2): 25, 13984, 52039, 14411, 17 Passed 60+0.6 8 threads SPRT: https://tests.stockfishchess.org/tests/view/640dca8e65775d3b539cb7f6 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 38824 W: 10825 L: 10554 D: 17445 Ptnml(0-2): 0, 2939, 13268, 3200, 5 closes https://github.com/official-stockfish/Stockfish/pull/4443 bench 4776866 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 582e44576b3..7eb4a0c5c53 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1071,7 +1071,7 @@ namespace { && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - (2 + (ss->ttPv && !PvNode)) * depth; + Value singularBeta = ttValue - (3 + 2 * (ss->ttPv && !PvNode)) * depth / 2; Depth singularDepth = (depth - 1) / 2; ss->excludedMove = move; From 55896a1384b022624a8c33b9f7f51b6b551eed50 Mon Sep 17 00:00:00 2001 From: Alfredo Menezes Date: Sun, 12 Mar 2023 22:29:07 -0300 Subject: [PATCH 1001/1766] Change mode of incbin.h Keep incbin.h with the same mode as the other source files. A mode diff might show up when working with patch files or sending the source code between devices. This patch should fix such behaviour. closes https://github.com/official-stockfish/Stockfish/pull/4442 No functional change --- src/incbin/incbin.h | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 src/incbin/incbin.h diff --git a/src/incbin/incbin.h b/src/incbin/incbin.h old mode 100755 new mode 100644 From d1e17989b51f220629e4e81a4bf413974f4b18e2 Mon Sep 17 00:00:00 2001 From: Sebastian Buchwald Date: Sat, 11 Mar 2023 22:08:35 +0100 Subject: [PATCH 1002/1766] Fix Makefile for clang 16 The clang 16 release will remove the -fexperimental-new-pass-manager flag (see https://github.com/llvm/llvm-project/commit/69b2b7282e92a1b576b7bd26f3b16716a5027e8e). Thus, the commit adapts the Makefile to use this flag only for older clang versions. closes https://github.com/official-stockfish/Stockfish/pull/4437 No functional change --- src/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 774ba6ea8c4..e257bc6347d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -584,7 +584,10 @@ ifeq ($(optimize),yes) endif ifeq ($(comp),clang) - CXXFLAGS += -fexperimental-new-pass-manager + clangmajorversion = $(shell $(CXX) -dumpversion 2>/dev/null | cut -f1 -d.) + ifeq ($(shell expr $(clangmajorversion) \< 16),1) + CXXFLAGS += -fexperimental-new-pass-manager + endif endif endif From 7077fbdd1481829a0a20b6975c4245609118b938 Mon Sep 17 00:00:00 2001 From: mstembera Date: Sat, 11 Mar 2023 11:51:08 -0800 Subject: [PATCH 1003/1766] Remove redundant condition from capture_stage() Change a non functional promotion check to an assert. closes https://github.com/official-stockfish/Stockfish/pull/4436 No functional change --- src/position.cpp | 3 +-- src/position.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index 37aa2e9edec..632a40b5d61 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -569,8 +569,7 @@ bool Position::pseudo_legal(const Move m) const { : MoveList(*this).contains(m); // Is not a promotion, so promotion piece must be empty - if (promotion_type(m) - KNIGHT != NO_PIECE_TYPE) - return false; + assert(promotion_type(m) - KNIGHT == NO_PIECE_TYPE); // If the 'from' square is not occupied by a piece belonging to the side to // move, the move is obviously not legal. diff --git a/src/position.h b/src/position.h index 1fc6b3b89c4..cc606a5ab00 100644 --- a/src/position.h +++ b/src/position.h @@ -393,7 +393,7 @@ inline bool Position::capture(Move m) const { // is needed to avoid the generation of duplicate moves. inline bool Position::capture_stage(Move m) const { assert(is_ok(m)); - return capture(m) || (type_of(m) == PROMOTION && promotion_type(m) == QUEEN); + return capture(m) || promotion_type(m) == QUEEN; } inline Piece Position::captured_piece() const { From f0556dcbe3ba2fc804ab26d4552446602a75f064 Mon Sep 17 00:00:00 2001 From: pb00067 Date: Wed, 1 Mar 2023 10:29:51 +0100 Subject: [PATCH 1004/1766] Small cleanups remove some unneeded assignments, typos, incorrect comments, add authors entry. closes https://github.com/official-stockfish/Stockfish/pull/4417 no functional change --- AUTHORS | 1 + src/nnue/nnue_feature_transformer.h | 2 +- src/position.cpp | 3 --- src/search.cpp | 2 -- 4 files changed, 2 insertions(+), 6 deletions(-) diff --git a/AUTHORS b/AUTHORS index 49f7009f10b..9b36111ea77 100644 --- a/AUTHORS +++ b/AUTHORS @@ -190,6 +190,7 @@ Sergei Antonov (saproj) Sergei Ivanov (svivanov72) Sergio Vieri (sergiovieri) sf-x +Shahin M. Shahin (peregrine) Shane Booth (shane31) Shawn Varghese (xXH4CKST3RXx) Siad Daboul (Topologist) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 8087ea55dd0..a1888c7a365 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -363,7 +363,7 @@ namespace Stockfish::Eval::NNUE { // NOTE: The parameter states_to_update is an array of position states, ending with nullptr. // All states must be sequential, that is states_to_update[i] must either be reachable // by repeatedly applying ->previous from states_to_update[i+1] or states_to_update[i] == nullptr. - // computed_st must be reachable by repeatadly applying ->previous on states_to_update[0], if not nullptr. + // computed_st must be reachable by repeatedly applying ->previous on states_to_update[0], if not nullptr. template void update_accumulator_incremental(const Position& pos, StateInfo* computed_st, StateInfo* states_to_update[N]) const { static_assert(N > 0); diff --git a/src/position.cpp b/src/position.cpp index 632a40b5d61..aeac23a33f4 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -765,9 +765,6 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // Update board and piece lists remove_piece(capsq); - if (type_of(m) == EN_PASSANT) - board[capsq] = NO_PIECE; - // Update material hash key and prefetch access to materialTable k ^= Zobrist::psq[captured][capsq]; st->materialKey ^= Zobrist::psq[captured][pieceCount[captured]]; diff --git a/src/search.cpp b/src/search.cpp index 7eb4a0c5c53..d6571a140f3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -601,7 +601,6 @@ namespace { assert(0 <= ss->ply && ss->ply < MAX_PLY); - (ss+1)->ttPv = false; (ss+1)->excludedMove = bestMove = MOVE_NONE; (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; (ss+2)->cutoffCnt = 0; @@ -1075,7 +1074,6 @@ namespace { Depth singularDepth = (depth - 1) / 2; ss->excludedMove = move; - // the search with excludedMove will update ss->staticEval value = search(pos, ss, singularBeta - 1, singularBeta, singularDepth, cutNode); ss->excludedMove = MOVE_NONE; From 515b66f18833ed87e97313d2ec4dfa4e2329d3df Mon Sep 17 00:00:00 2001 From: peregrineshahin Date: Sun, 12 Mar 2023 01:22:55 +0300 Subject: [PATCH 1005/1766] Fix null move issue Fix altering for stats landing on B1 Square after a null move and fix considering counter-moves on A1 for root node. fixes https://github.com/official-stockfish/Stockfish/issues/4333 by preventing calls to from_sq and to_sq functions over null-moves and none-moves. closes https://github.com/official-stockfish/Stockfish/pull/4448 bench: 4980082 --- src/search.cpp | 19 ++++++++++++------- src/types.h | 10 ++++++---- src/uci.cpp | 6 +++--- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index d6571a140f3..466e0d6f935 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -605,7 +605,7 @@ namespace { (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; (ss+2)->cutoffCnt = 0; ss->doubleExtensions = (ss-1)->doubleExtensions; - Square prevSq = to_sq((ss-1)->currentMove); + Square prevSq = is_ok((ss-1)->currentMove) ? to_sq((ss-1)->currentMove) : SQ_NONE; // Initialize statScore to zero for the grandchildren of the current position. // So statScore is shared between all grandchildren and only the first grandchild @@ -647,7 +647,7 @@ namespace { update_quiet_stats(pos, ss, ttMove, stat_bonus(depth)); // Extra penalty for early quiet moves of the previous ply (~0 Elo on STC, ~2 Elo on LTC) - if ((ss-1)->moveCount <= 2 && !priorCapture) + if (prevSq != SQ_NONE && (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 (~1 Elo) @@ -935,7 +935,7 @@ namespace { nullptr , (ss-4)->continuationHistory, nullptr , (ss-6)->continuationHistory }; - Move countermove = thisThread->counterMoves[pos.piece_on(prevSq)][prevSq]; + Move countermove = prevSq != SQ_NONE ? thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] : MOVE_NONE; MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &captureHistory, @@ -1383,7 +1383,7 @@ namespace { quietsSearched, quietCount, capturesSearched, captureCount, depth); // Bonus for prior countermove that caused the fail low - else if (!priorCapture) + else if (!priorCapture && prevSq != SQ_NONE) { int bonus = (depth > 5) + (PvNode || cutNode) + (bestValue < alpha - 97 * depth) + ((ss-1)->moveCount > 10); update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); @@ -1525,7 +1525,7 @@ namespace { // to search the moves. Because the depth is <= 0 here, only captures, // queen promotions, and other checks (only if depth >= DEPTH_QS_CHECKS) // will be generated. - Square prevSq = to_sq((ss-1)->currentMove); + Square prevSq = (ss-1)->currentMove != MOVE_NULL ? to_sq((ss-1)->currentMove) : SQ_NONE; MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory, contHist, @@ -1714,7 +1714,8 @@ namespace { Thread* thisThread = pos.this_thread(); CapturePieceToHistory& captureHistory = thisThread->captureHistory; Piece moved_piece = pos.moved_piece(bestMove); - PieceType captured = type_of(pos.piece_on(to_sq(bestMove))); + PieceType captured; + int bonus1 = stat_bonus(depth + 1); if (!pos.capture_stage(bestMove)) @@ -1733,12 +1734,16 @@ namespace { } } else + { // Increase stats for the best move in case it was a capture move + captured = type_of(pos.piece_on(to_sq(bestMove))); captureHistory[moved_piece][to_sq(bestMove)][captured] << bonus1; + } // Extra penalty for a quiet early move that was not a TT move or // main killer move in previous ply when it gets refuted. - if ( ((ss-1)->moveCount == 1 + (ss-1)->ttHit || ((ss-1)->currentMove == (ss-1)->killers[0])) + if ( prevSq != SQ_NONE + && ((ss-1)->moveCount == 1 + (ss-1)->ttHit || ((ss-1)->currentMove == (ss-1)->killers[0])) && !pos.captured_piece()) update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -bonus1); diff --git a/src/types.h b/src/types.h index 37ce343a4da..06b0a05985a 100644 --- a/src/types.h +++ b/src/types.h @@ -416,6 +416,10 @@ inline Color color_of(Piece pc) { return Color(pc >> 3); } +constexpr bool is_ok(Move m) { + return m != MOVE_NONE && m != MOVE_NULL; +} + constexpr bool is_ok(Square s) { return s >= SQ_A1 && s <= SQ_H8; } @@ -445,10 +449,12 @@ constexpr Direction pawn_push(Color c) { } constexpr Square from_sq(Move m) { + assert(is_ok(m)); return Square((m >> 6) & 0x3F); } constexpr Square to_sq(Move m) { + assert(is_ok(m)); return Square(m & 0x3F); } @@ -473,10 +479,6 @@ constexpr Move make(Square from, Square to, PieceType pt = KNIGHT) { return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to); } -constexpr bool is_ok(Move m) { - return from_sq(m) != to_sq(m); // Catch MOVE_NULL and MOVE_NONE -} - /// Based on a congruential pseudo random number generator constexpr Key make_key(uint64_t seed) { return seed * 6364136223846793005ULL + 1442695040888963407ULL; diff --git a/src/uci.cpp b/src/uci.cpp index 3883b3d3707..d3d99243b09 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -358,15 +358,15 @@ std::string UCI::square(Square s) { string UCI::move(Move m, bool chess960) { - Square from = from_sq(m); - Square to = to_sq(m); - if (m == MOVE_NONE) return "(none)"; if (m == MOVE_NULL) return "0000"; + Square from = from_sq(m); + Square to = to_sq(m); + if (type_of(m) == CASTLING && !chess960) to = make_square(to > from ? FILE_G : FILE_C, rank_of(from)); From af4b62a593cc4fa6d7d34110c41301028a5c9695 Mon Sep 17 00:00:00 2001 From: disservin Date: Mon, 13 Mar 2023 19:35:27 +0100 Subject: [PATCH 1006/1766] NNUE namespace cleanup This patch moves the nnue namespace in the appropiate header that correspondes with the definition. It also makes navigation a bit easier. closes https://github.com/official-stockfish/Stockfish/pull/4445 No functional change --- src/evaluate.cpp | 6 +++--- src/evaluate.h | 8 -------- src/nnue/evaluate_nnue.h | 8 ++++++++ src/search.cpp | 1 + src/uci.cpp | 1 + 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index cf6f23eac55..99b873004ed 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -36,7 +36,7 @@ #include "timeman.h" #include "uci.h" #include "incbin/incbin.h" - +#include "nnue/evaluate_nnue.h" // Macro to embed the default efficiently updatable neural network (NNUE) file // data in the engine binary (using incbin.h, by Dale Weiler). @@ -95,7 +95,7 @@ namespace Eval { if (directory != "") { ifstream stream(directory + eval_file, ios::binary); - if (load_eval(eval_file, stream)) + if (NNUE::load_eval(eval_file, stream)) currentEvalFileName = eval_file; } @@ -111,7 +111,7 @@ namespace Eval { (void) gEmbeddedNNUEEnd; // Silence warning on unused variable istream stream(&buffer); - if (load_eval(eval_file, stream)) + if (NNUE::load_eval(eval_file, stream)) currentEvalFileName = eval_file; } } diff --git a/src/evaluate.h b/src/evaluate.h index 5238cb81c54..3615fe6d7e6 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -43,17 +43,9 @@ namespace Eval { namespace NNUE { - std::string trace(Position& pos); - Value evaluate(const Position& pos, bool adjusted = false, int* complexity = nullptr); - void hint_common_parent_position(const Position& pos); - void init(); void verify(); - bool load_eval(std::string name, std::istream& stream); - bool save_eval(std::ostream& stream); - bool save_eval(const std::optional& filename); - } // namespace NNUE } // namespace Eval diff --git a/src/nnue/evaluate_nnue.h b/src/nnue/evaluate_nnue.h index 15638caeeaf..b84bed8b90d 100644 --- a/src/nnue/evaluate_nnue.h +++ b/src/nnue/evaluate_nnue.h @@ -55,6 +55,14 @@ namespace Stockfish::Eval::NNUE { template using LargePagePtr = std::unique_ptr>; + std::string trace(Position& pos); + Value evaluate(const Position& pos, bool adjusted = false, int* complexity = nullptr); + void hint_common_parent_position(const Position& pos); + + bool load_eval(std::string name, std::istream& stream); + bool save_eval(std::ostream& stream); + bool save_eval(const std::optional& filename); + } // namespace Stockfish::Eval::NNUE #endif // #ifndef NNUE_EVALUATE_NNUE_H_INCLUDED diff --git a/src/search.cpp b/src/search.cpp index 466e0d6f935..17c1c28b264 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -34,6 +34,7 @@ #include "tt.h" #include "uci.h" #include "syzygy/tbprobe.h" +#include "nnue/evaluate_nnue.h" namespace Stockfish { diff --git a/src/uci.cpp b/src/uci.cpp index d3d99243b09..8f9684ee265 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -32,6 +32,7 @@ #include "tt.h" #include "uci.h" #include "syzygy/tbprobe.h" +#include "nnue/evaluate_nnue.h" using namespace std; From 02e4697055519ed206fa76e4ef9abb9f156cd1a0 Mon Sep 17 00:00:00 2001 From: pb00067 Date: Mon, 13 Mar 2023 18:32:40 +0100 Subject: [PATCH 1007/1766] Remove 'si' StateInfo variable/parameter. Since st is a member of position we don't need to pass it separately as parameter. While being there also remove some line in pos_is_ok, where a copy of StateInfo was made by using default copy constructor and then verified it's correctedness by doing a memcmp. There is no point in doing that. Passed non-regression test https://tests.stockfishchess.org/tests/view/64098d562644b62c33942b35 LLR: 3.24 (-2.94,2.94) <-1.75,0.25> Total: 548960 W: 145834 L: 146134 D: 256992 Ptnml(0-2): 1617, 57652, 156261, 57314, 1636 closes https://github.com/official-stockfish/Stockfish/pull/4444 No functional change --- src/position.cpp | 58 ++++++++++++++++++++++-------------------------- src/position.h | 4 ++-- 2 files changed, 28 insertions(+), 34 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index aeac23a33f4..171193ec131 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -282,7 +282,7 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th chess960 = isChess960; thisThread = th; - set_state(st); + set_state(); assert(pos_is_ok()); @@ -313,19 +313,19 @@ void Position::set_castling_right(Color c, Square rfrom) { /// Position::set_check_info() sets king attacks to detect if a move gives check -void Position::set_check_info(StateInfo* si) const { +void Position::set_check_info() const { - si->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), square(WHITE), si->pinners[BLACK]); - si->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square(BLACK), si->pinners[WHITE]); + st->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), square(WHITE), st->pinners[BLACK]); + st->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square(BLACK), st->pinners[WHITE]); Square ksq = square(~sideToMove); - si->checkSquares[PAWN] = pawn_attacks_bb(~sideToMove, ksq); - si->checkSquares[KNIGHT] = attacks_bb(ksq); - si->checkSquares[BISHOP] = attacks_bb(ksq, pieces()); - si->checkSquares[ROOK] = attacks_bb(ksq, pieces()); - si->checkSquares[QUEEN] = si->checkSquares[BISHOP] | si->checkSquares[ROOK]; - si->checkSquares[KING] = 0; + st->checkSquares[PAWN] = pawn_attacks_bb(~sideToMove, ksq); + st->checkSquares[KNIGHT] = attacks_bb(ksq); + st->checkSquares[BISHOP] = attacks_bb(ksq, pieces()); + st->checkSquares[ROOK] = attacks_bb(ksq, pieces()); + st->checkSquares[QUEEN] = st->checkSquares[BISHOP] | st->checkSquares[ROOK]; + st->checkSquares[KING] = 0; } @@ -334,39 +334,39 @@ void Position::set_check_info(StateInfo* si) const { /// The function is only used when a new position is set up, and to verify /// the correctness of the StateInfo data when running in debug mode. -void Position::set_state(StateInfo* si) const { +void Position::set_state() const { - si->key = si->materialKey = 0; - si->pawnKey = Zobrist::noPawns; - si->nonPawnMaterial[WHITE] = si->nonPawnMaterial[BLACK] = VALUE_ZERO; - si->checkersBB = attackers_to(square(sideToMove)) & pieces(~sideToMove); + st->key = st->materialKey = 0; + st->pawnKey = Zobrist::noPawns; + st->nonPawnMaterial[WHITE] = st->nonPawnMaterial[BLACK] = VALUE_ZERO; + st->checkersBB = attackers_to(square(sideToMove)) & pieces(~sideToMove); - set_check_info(si); + set_check_info(); for (Bitboard b = pieces(); b; ) { Square s = pop_lsb(b); Piece pc = piece_on(s); - si->key ^= Zobrist::psq[pc][s]; + st->key ^= Zobrist::psq[pc][s]; if (type_of(pc) == PAWN) - si->pawnKey ^= Zobrist::psq[pc][s]; + st->pawnKey ^= Zobrist::psq[pc][s]; else if (type_of(pc) != KING) - si->nonPawnMaterial[color_of(pc)] += PieceValue[MG][pc]; + st->nonPawnMaterial[color_of(pc)] += PieceValue[MG][pc]; } - if (si->epSquare != SQ_NONE) - si->key ^= Zobrist::enpassant[file_of(si->epSquare)]; + if (st->epSquare != SQ_NONE) + st->key ^= Zobrist::enpassant[file_of(st->epSquare)]; if (sideToMove == BLACK) - si->key ^= Zobrist::side; + st->key ^= Zobrist::side; - si->key ^= Zobrist::castling[si->castlingRights]; + st->key ^= Zobrist::castling[st->castlingRights]; for (Piece pc : Pieces) for (int cnt = 0; cnt < pieceCount[pc]; ++cnt) - si->materialKey ^= Zobrist::psq[pc][cnt]; + st->materialKey ^= Zobrist::psq[pc][cnt]; } @@ -865,7 +865,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { sideToMove = ~sideToMove; // Update king attacks used for fast check detection - set_check_info(st); + set_check_info(); // Calculate the repetition info. It is the ply distance from the previous // occurrence of the same position, negative in the 3-fold case, or zero @@ -1017,7 +1017,7 @@ void Position::do_null_move(StateInfo& newSt) { sideToMove = ~sideToMove; - set_check_info(st); + set_check_info(); st->repetition = 0; @@ -1320,12 +1320,6 @@ bool Position::pos_is_ok() const { if (p1 != p2 && (pieces(p1) & pieces(p2))) assert(0 && "pos_is_ok: Bitboards"); - StateInfo si = *st; - ASSERT_ALIGNED(&si, Eval::NNUE::CacheLineSize); - - set_state(&si); - if (std::memcmp(&si, st, sizeof(StateInfo))) - assert(0 && "pos_is_ok: State"); for (Piece pc : Pieces) if ( pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc))) diff --git a/src/position.h b/src/position.h index cc606a5ab00..d3483bcf890 100644 --- a/src/position.h +++ b/src/position.h @@ -179,8 +179,8 @@ class Position { private: // Initialization helpers (used while setting up a position) void set_castling_right(Color c, Square rfrom); - void set_state(StateInfo* si) const; - void set_check_info(StateInfo* si) const; + void set_state() const; + void set_check_info() const; // Other helpers void move_piece(Square from, Square to); From 24b37e4586ba610d331048446bd036bec5544c4f Mon Sep 17 00:00:00 2001 From: pb00067 Date: Mon, 20 Mar 2023 08:56:44 +0100 Subject: [PATCH 1008/1766] Verified SEE pruning for capturing and checking moves. Patch analyzes field after SEE exchanges concluded with a recapture by the opponent: if opponent Queen/Rook/King results under attack after the exchanges, we consider the move sharp and don't prune it. Important note: By accident I forgot to adjust 'occupied' when the king takes part in the exchanges. As result of this a move is considered sharp too, when opponent king apparently can evade check by recapturing. Surprisingly this seems contribute to patch's strength. STC: https://tests.stockfishchess.org/tests/view/640b16132644b62c33947397 LLR: 2.96 (-2.94,2.94) <0.00,2.00> Total: 116400 W: 31239 L: 30817 D: 54344 Ptnml(0-2): 350, 12742, 31618, 13116, 374 LTC: https://tests.stockfishchess.org/tests/view/640c88092644b62c3394c1c5 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 177600 W: 47988 L: 47421 D: 82191 Ptnml(0-2): 62, 16905, 54317, 17436, 80 closes https://github.com/official-stockfish/Stockfish/pull/4453 bench: 5012145 --- src/movepick.cpp | 6 +++--- src/movepick.h | 1 + src/position.cpp | 15 +++++++-------- src/position.h | 2 +- src/search.cpp | 30 +++++++++++++++++++++++++----- 5 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 36ee46b50f0..855f2b1d67d 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -95,7 +95,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePiece stage = PROBCUT_TT + !(ttm && pos.capture_stage(ttm) && pos.pseudo_legal(ttm) - && pos.see_ge(ttm, threshold)); + && pos.see_ge(ttm, occupied, threshold)); } /// MovePicker::score() assigns a numerical value to each move in a list, used @@ -197,7 +197,7 @@ Move MovePicker::next_move(bool skipQuiets) { case GOOD_CAPTURE: if (select([&](){ - return pos.see_ge(*cur, Value(-cur->value)) ? + return pos.see_ge(*cur, occupied, Value(-cur->value)) ? // Move losing capture to endBadCaptures to be tried later true : (*endBadCaptures++ = *cur, false); })) return *(cur - 1); @@ -264,7 +264,7 @@ Move MovePicker::next_move(bool skipQuiets) { return select([](){ return true; }); case PROBCUT: - return select([&](){ return pos.see_ge(*cur, threshold); }); + return select([&](){ return pos.see_ge(*cur, occupied, threshold); }); case QCAPTURE: if (select([&](){ return depth > DEPTH_QS_RECAPTURES diff --git a/src/movepick.h b/src/movepick.h index b6c07378240..725607b8a54 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -150,6 +150,7 @@ class MovePicker { Value threshold; Depth depth; ExtMove moves[MAX_MOVES]; + Bitboard occupied; }; } // namespace Stockfish diff --git a/src/position.cpp b/src/position.cpp index 171193ec131..ba6888eb948 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1062,7 +1062,7 @@ Key Position::key_after(Move m) const { /// SEE value of move is greater or equal to the given threshold. We'll use an /// algorithm similar to alpha-beta pruning with a null window. -bool Position::see_ge(Move m, Value threshold) const { +bool Position::see_ge(Move m, Bitboard& occupied, Value threshold) const { assert(is_ok(m)); @@ -1081,7 +1081,7 @@ bool Position::see_ge(Move m, Value threshold) const { return true; assert(color_of(piece_on(from)) == sideToMove); - Bitboard occupied = pieces() ^ from ^ to; + occupied = pieces() ^ from ^ to; // xoring to is important for pinned piece logic Color stm = sideToMove; Bitboard attackers = attackers_to(to, occupied); Bitboard stmAttackers, bb; @@ -1112,45 +1112,44 @@ bool Position::see_ge(Move m, Value threshold) const { // the bitboard 'attackers' any X-ray attackers behind it. if ((bb = stmAttackers & pieces(PAWN))) { + occupied ^= least_significant_square_bb(bb); if ((swap = PawnValueMg - swap) < res) break; - occupied ^= least_significant_square_bb(bb); attackers |= attacks_bb(to, occupied) & pieces(BISHOP, QUEEN); } else if ((bb = stmAttackers & pieces(KNIGHT))) { + occupied ^= least_significant_square_bb(bb); if ((swap = KnightValueMg - swap) < res) break; - - occupied ^= least_significant_square_bb(bb); } else if ((bb = stmAttackers & pieces(BISHOP))) { + occupied ^= least_significant_square_bb(bb); if ((swap = BishopValueMg - swap) < res) break; - occupied ^= least_significant_square_bb(bb); attackers |= attacks_bb(to, occupied) & pieces(BISHOP, QUEEN); } else if ((bb = stmAttackers & pieces(ROOK))) { + occupied ^= least_significant_square_bb(bb); if ((swap = RookValueMg - swap) < res) break; - occupied ^= least_significant_square_bb(bb); attackers |= attacks_bb(to, occupied) & pieces(ROOK, QUEEN); } else if ((bb = stmAttackers & pieces(QUEEN))) { + occupied ^= least_significant_square_bb(bb); if ((swap = QueenValueMg - swap) < res) break; - occupied ^= least_significant_square_bb(bb); attackers |= (attacks_bb(to, occupied) & pieces(BISHOP, QUEEN)) | (attacks_bb(to, occupied) & pieces(ROOK , QUEEN)); } diff --git a/src/position.h b/src/position.h index d3483bcf890..670b621ce74 100644 --- a/src/position.h +++ b/src/position.h @@ -144,7 +144,7 @@ class Position { void undo_null_move(); // Static Exchange Evaluation - bool see_ge(Move m, Value threshold = VALUE_ZERO) const; + bool see_ge(Move m, Bitboard& occupied, Value threshold = VALUE_ZERO) const; // Accessing hash keys Key key() const; diff --git a/src/search.cpp b/src/search.cpp index 17c1c28b264..7564c10983e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1019,9 +1019,27 @@ namespace { + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 7 < alpha) continue; + Bitboard occupied; // SEE based pruning (~11 Elo) - if (!pos.see_ge(move, Value(-206) * depth)) - continue; + if (!pos.see_ge(move, occupied, Value(-206) * depth)) + { + if (depth < 2 - capture) + continue; + // don't prune move if a heavy enemy piece (KQR) is under attack after the exchanges + Bitboard leftEnemies = (pos.pieces(~us, QUEEN, ROOK) | pos.pieces(~us, KING)) & occupied; + Bitboard attacks = 0; + occupied |= to_sq(move); + while (leftEnemies && !attacks) + { + Square sq = pop_lsb(leftEnemies); + attacks |= pos.attackers_to(sq, occupied) & pos.pieces(us) & occupied; + // exclude Queen/Rook(s) which were already threatened before SEE + if (attacks && (sq != pos.square(~us) && (pos.attackers_to(sq, pos.pieces()) & pos.pieces(us)))) + attacks = 0; + } + if (!attacks) + continue; + } } else { @@ -1047,8 +1065,9 @@ namespace { lmrDepth = std::max(lmrDepth, 0); + Bitboard occupied; // Prune moves with negative SEE (~4 Elo) - if (!pos.see_ge(move, Value(-24 * lmrDepth * lmrDepth - 15 * lmrDepth))) + if (!pos.see_ge(move, occupied, Value(-24 * lmrDepth * lmrDepth - 15 * lmrDepth))) continue; } } @@ -1533,6 +1552,7 @@ namespace { prevSq); int quietCheckEvasions = 0; + Bitboard occupied; // Step 5. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. @@ -1569,7 +1589,7 @@ namespace { continue; } - if (futilityBase <= alpha && !pos.see_ge(move, VALUE_ZERO + 1)) + if (futilityBase <= alpha && !pos.see_ge(move, occupied, VALUE_ZERO + 1)) { bestValue = std::max(bestValue, futilityBase); continue; @@ -1588,7 +1608,7 @@ namespace { continue; // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, Value(-110))) + if (!pos.see_ge(move, occupied, Value(-110))) continue; } From b973e40e45d75c3b3391141d149d4186494d4652 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Tue, 21 Mar 2023 23:58:25 +0300 Subject: [PATCH 1009/1766] Update Elo estimates for terms in search Setting the Elo value of some functions which were not set before. All tests run at 10+0.1 (STC), 25000 games (Same as #4294). Book used: UHO_XXL_+0.90_+1.19.epd Values are rounded to the nearest non-negative integer. Test links: https://tests.stockfishchess.org/tests/view/6419ab5b65775d3b539f46c6 https://tests.stockfishchess.org/tests/view/6419adb465775d3b539f4730 https://tests.stockfishchess.org/tests/view/6419ae9c65775d3b539f4756 https://tests.stockfishchess.org/tests/view/6419b03f65775d3b539f47a8 https://tests.stockfishchess.org/tests/view/6419b35d65775d3b539f4860 https://tests.stockfishchess.org/tests/view/6419b6b965775d3b539f48e6 https://tests.stockfishchess.org/tests/view/6419cade65775d3b539f4cd5 https://tests.stockfishchess.org/tests/view/6419cbb565775d3b539f4d01 https://tests.stockfishchess.org/tests/view/6419cc6965775d3b539f4d1e closes https://github.com/official-stockfish/Stockfish/pull/4459 No functional change --- src/search.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 7564c10983e..b2983f6647a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -732,7 +732,7 @@ namespace { } else if (excludedMove) { - // Providing the hint that this node's accumulator will be used often brings significant Elo gain (13 elo) + // Providing the hint that this node's accumulator will be used often brings significant Elo gain (13 Elo) Eval::NNUE::hint_common_parent_position(pos); eval = ss->staticEval; complexity = abs(ss->staticEval - pos.psq_eg_stm()); @@ -1120,15 +1120,15 @@ namespace { else if (singularBeta >= beta) return singularBeta; - // If the eval of ttMove is greater than beta, we reduce it (negative extension) + // If the eval of ttMove is greater than beta, we reduce it (negative extension) (~7 Elo) else if (ttValue >= beta) extension = -2 - !PvNode; - // If the eval of ttMove is less than value, we reduce it (negative extension) + // If the eval of ttMove is less than value, we reduce it (negative extension) (~1 Elo) else if (ttValue <= value) extension = -1; - // If the eval of ttMove is less than alpha, we reduce it (negative extension) + // If the eval of ttMove is less than alpha, we reduce it (negative extension) (~1 Elo) else if (ttValue <= alpha) extension = -1; } @@ -1182,7 +1182,7 @@ namespace { if (ttCapture) r++; - // Decrease reduction for PvNodes based on depth + // Decrease reduction for PvNodes based on depth (~2 Elo) if (PvNode) r -= 1 + 12 / (3 + depth); @@ -1195,11 +1195,11 @@ namespace { && (mp.threatenedPieces & from_sq(move))) r--; - // Increase reduction if next ply has a lot of fail high + // Increase reduction if next ply has a lot of fail high (~5 Elo) if ((ss+1)->cutoffCnt > 3) r++; - // Decrease reduction if move is a killer and we have a good history + // Decrease reduction if move is a killer and we have a good history (~1 Elo) if (move == ss->killers[0] && (*contHist[0])[movedPiece][to_sq(move)] >= 3722) r--; @@ -1210,7 +1210,7 @@ namespace { + (*contHist[3])[movedPiece][to_sq(move)] - 4182; - // Decrease/increase reduction for moves with a good/bad history (~30 Elo) + // Decrease/increase reduction for moves with a good/bad history (~25 Elo) r -= ss->statScore / (11791 + 3992 * (depth > 6 && depth < 19)); // Step 17. Late moves reduction / extension (LMR, ~117 Elo) @@ -1347,7 +1347,7 @@ namespace { { alpha = value; - // Reduce other moves if we have found at least one score improvement + // Reduce other moves if we have found at least one score improvement (~1 Elo) if ( depth > 1 && depth < 6 && beta < 10534 @@ -1413,7 +1413,7 @@ namespace { bestValue = std::min(bestValue, maxValue); // If no good move is found and the previous position was ttPv, then the previous - // opponent move is probably good and the new position is added to the search tree. + // opponent move is probably good and the new position is added to the search tree. (~7 Elo) if (bestValue <= alpha) ss->ttPv = ss->ttPv || ((ss-1)->ttPv && depth > 3); @@ -1432,7 +1432,7 @@ namespace { // qsearch() is the quiescence search function, which is called by the main search // function with zero depth, or recursively with further decreasing depth per call. - // (~155 elo) + // (~155 Elo) template Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { From 1b5738e0c958ac8d3d140a3d182b85f8c0c0cd2c Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Fri, 24 Mar 2023 07:45:22 +0300 Subject: [PATCH 1010/1766] Simplify statScore initialization This patch simplifies initialization of statScore to "always set it up to 0" instead of setting it up to 0 two plies deeper. Reason for why it was done in previous way partially was because of LMR usage of previous statScore which was simplified long time ago so it makes sense to make in more simple there. Passed STC: https://tests.stockfishchess.org/tests/view/641a86d1db43ab2ba6f7b31d LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 115648 W: 30895 L: 30764 D: 53989 Ptnml(0-2): 368, 12741, 31473, 12876, 366 Passed LTC: https://tests.stockfishchess.org/tests/view/641b1c31db43ab2ba6f7d17a LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 175576 W: 47122 L: 47062 D: 81392 Ptnml(0-2): 91, 17077, 53390, 17141, 89 closes https://github.com/official-stockfish/Stockfish/pull/4460 bench 5081969 --- src/search.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index b2983f6647a..d2358ea2e1c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -607,14 +607,7 @@ namespace { (ss+2)->cutoffCnt = 0; ss->doubleExtensions = (ss-1)->doubleExtensions; Square prevSq = is_ok((ss-1)->currentMove) ? to_sq((ss-1)->currentMove) : SQ_NONE; - - // Initialize statScore to zero for the grandchildren of the current position. - // So statScore is shared between all grandchildren and only the first grandchild - // starts with statScore = 0. Later grandchildren start with the last calculated - // statScore of the previous grandchild. This influences the reduction rules in - // LMR which are based on the statScore of parent position. - if (!rootNode) - (ss+2)->statScore = 0; + ss->statScore = 0; // Step 4. Transposition table lookup. excludedMove = ss->excludedMove; From 587bc647d7d14b53d8625c4446006e23a4acd82a Mon Sep 17 00:00:00 2001 From: MinetaS Date: Wed, 22 Mar 2023 16:50:55 +0900 Subject: [PATCH 1011/1766] Remove non_pawn_material in NNUE::evaluate After "Use NNUE complexity in search, retune related parameters" commit, the effect of non-pawn material adjustment has been nearly diminished. This patch removes pos.non_pawn_material as a simplification, which passed non-regression tests with both STC and LTC. Passed non-regression STC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 75152 W: 20030 L: 19856 D: 35266 Ptnml(0-2): 215, 8281, 20459, 8357, 264 https://tests.stockfishchess.org/tests/view/641ab471db43ab2ba6f7bc58 Passed non-regression LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 193864 W: 51870 L: 51829 D: 90165 Ptnml(0-2): 86, 18968, 58794, 18987, 97 https://tests.stockfishchess.org/tests/view/641b4fe6db43ab2ba6f7db96 closes https://github.com/official-stockfish/Stockfish/pull/4461 Bench: 5020718 --- src/nnue/evaluate_nnue.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index f33aa3b889b..329adfdaa9e 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -148,7 +148,7 @@ namespace Stockfish::Eval::NNUE { // overaligning stack variables with alignas() doesn't work correctly. constexpr uint64_t alignment = CacheLineSize; - int delta = 24 - pos.non_pawn_material() / 9560; + constexpr int delta = 24; #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) TransformedFeatureType transformedFeaturesUnaligned[ From 43108a619899af084e45224e8744ca668a9efed2 Mon Sep 17 00:00:00 2001 From: Sebastian Buchwald Date: Tue, 28 Mar 2023 19:53:43 +0200 Subject: [PATCH 1012/1766] Reuse existing functions to read/write array of network parameters closes https://github.com/official-stockfish/Stockfish/pull/4463 No functional change --- src/nnue/layers/affine_transform.h | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 313b1568393..f84f054e7de 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -256,8 +256,7 @@ namespace Stockfish::Eval::NNUE::Layers { // Read network parameters bool read_parameters(std::istream& stream) { - for (IndexType i = 0; i < OutputDimensions; ++i) - biases[i] = read_little_endian(stream); + read_little_endian(stream, biases, OutputDimensions); for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) weights[get_weight_index(i)] = read_little_endian(stream); @@ -267,8 +266,7 @@ namespace Stockfish::Eval::NNUE::Layers { // Write network parameters bool write_parameters(std::ostream& stream) const { - for (IndexType i = 0; i < OutputDimensions; ++i) - write_little_endian(stream, biases[i]); + write_little_endian(stream, biases, OutputDimensions); for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) write_little_endian(stream, weights[get_weight_index(i)]); @@ -452,8 +450,7 @@ namespace Stockfish::Eval::NNUE::Layers { // Read network parameters bool read_parameters(std::istream& stream) { - for (IndexType i = 0; i < OutputDimensions; ++i) - biases[i] = read_little_endian(stream); + read_little_endian(stream, biases, OutputDimensions); for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) weights[get_weight_index(i)] = read_little_endian(stream); @@ -462,8 +459,7 @@ namespace Stockfish::Eval::NNUE::Layers { // Write network parameters bool write_parameters(std::ostream& stream) const { - for (IndexType i = 0; i < OutputDimensions; ++i) - write_little_endian(stream, biases[i]); + write_little_endian(stream, biases, OutputDimensions); for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) write_little_endian(stream, weights[get_weight_index(i)]); From 37160c4b1632245d46d86cec7bd22b76f5a87531 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Mon, 27 Mar 2023 01:00:54 -0400 Subject: [PATCH 1013/1766] Update default net to nn-dabb1ed23026.nnue Created by retraining the master net with these modifications: * New filtering methods for existing data from T80 sep+oct2022, T79 apr2022, T78 jun+jul+aug+sep2022, T77 dec2021 * Adding new filtered data from T80 aug2022 and T78 apr+may2022 * Increasing early-fen-skipping from 28 to 30 ``` python3 easy_train.py \ --experiment-name leela96-dfrc99-T80novT79mayT60novdec-v2-T80augsepoctT79aprT78aprtosep-v6-T77dec-v3-sk30 \ --training-dataset /data/leela96-dfrc99-T80novT79mayT60novdec-v2-T80augsepoctT79aprT78aprtosep-v6-T77dec-v3.binpack \ --nnue-pytorch-branch linrock/nnue-pytorch/misc-fixes \ --start-from-engine-test-net True \ --early-fen-skipping 30 \ --max_epoch 900 \ --start-lambda 1.0 \ --end-lambda 0.7 \ --lr 4.375e-4 \ --gamma 0.995 \ --tui False \ --gpus "0," \ --seed $RANDOM ``` The v3 filtering used for data from T77dec 2021 differs from v2 filtering in that: * To improve binpack compression, positions after ply 28 were skipped during training by setting position scores to VALUE_NONE (32002) instead of removing them entirely * All early-game positions with ply <= 28 were removed to maximize binpack compression * Only bestmove captures at d6pv2 search were skipped, not 2nd bestmove captures * Binpack compression was repaired for the remaining positions by effectively replacing bestmoves with "played moves" to maintain contiguous sequences of positions in the training game data After improving binpack compression, The T77 dec2021 data size was reduced from 95G to 19G. The v6 filtering used for data from T80augsepoctT79aprT78aprtosep 2022 differs from v2 in that: * All positions with only one legal move were removed * Tighter score differences at d6pv2 search were used to remove more positions with only one good move than before * d6pv2 search was not used to remove positions where the best 2 moves were captures ``` python3 interleave_binpacks.py \ nn-547-dataset/leela96-eval-filt-v2.binpack \ nn-547-dataset/dfrc99-eval-filt-v2.binpack \ nn-547-dataset/test80-nov2022-12tb7p-eval-filt-v2-d6.binpack \ nn-547-dataset/T79-may2022-12tb7p-eval-filt-v2.binpack \ nn-547-dataset/T60-nov2021-12tb7p-eval-filt-v2.binpack \ nn-547-dataset/T60-dec2021-12tb7p-eval-filt-v2.binpack \ filt-v6/test80-aug2022-16tb7p-filter-v6.binpack \ filt-v6/test80-sep2022-16tb7p-filter-v6.binpack \ filt-v6/test80-oct2022-16tb7p-filter-v6.binpack \ filt-v6/test79-apr2022-16tb7p-filter-v6.binpack \ filt-v6/test78-aprmay2022-16tb7p-filter-v6.binpack \ filt-v6/test78-junjulaug2022-16tb7p-filter-v6.binpack \ filt-v6/test78-sep2022-16tb7p-filter-v6.binpack \ filt-v3/test77-dec2021-16tb7p-filt-v3.binpack \ /data/leela96-dfrc99-T80novT79mayT60novdec-v2-T80augsepoctT79aprT78aprtosep-v6-T77dec-v3.binpack ``` The code for the new data filtering methods is available at: https://github.com/linrock/Stockfish/tree/nnue-data-v3/nnue-data The code for giving hexword names to .nnue files is at: https://github.com/linrock/nnue-namer Links for downloading the training data components can be found at: https://robotmoon.com/nnue-training-data/ Local elo at 25k nodes per move: nn-epoch779.nnue : 0.6 +/- 3.1 Passed STC: https://tests.stockfishchess.org/tests/view/64212412db43ab2ba6f8efb0 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 82256 W: 22185 L: 21809 D: 38262 Ptnml(0-2): 286, 9065, 22067, 9407, 303 Passed LTC: https://tests.stockfishchess.org/tests/view/64223726db43ab2ba6f91d6c LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 30840 W: 8437 L: 8149 D: 14254 Ptnml(0-2): 14, 2891, 9323, 3177, 15 closes https://github.com/official-stockfish/Stockfish/pull/4465 bench 5101970 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 3615fe6d7e6..61846073300 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-52471d67216a.nnue" + #define EvalFileDefaultName "nn-dabb1ed23026.nnue" namespace NNUE { From a9c26357deb01c764cd16ef4e61acb4f687cbd77 Mon Sep 17 00:00:00 2001 From: Miguel Lahoz Date: Tue, 28 Mar 2023 00:06:24 +0800 Subject: [PATCH 1014/1766] Clean up repetitive declarations for see_ge The occupied bitboard is only used in one place and is otherwise thrown away. To simplify use, see_ge function can instead be overloaded. Repetitive declarations for occupied bitboard can be removed. Passed non-regression test https://tests.stockfishchess.org/tests/view/6421c286db43ab2ba6f908eb LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 48912 W: 13196 L: 13001 D: 22715 Ptnml(0-2): 146, 5003, 13967, 5190, 150 closes https://github.com/official-stockfish/Stockfish/pull/4469 No functional change. --- src/movepick.cpp | 6 +++--- src/movepick.h | 1 - src/position.cpp | 5 +++++ src/position.h | 1 + src/search.cpp | 8 +++----- 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 855f2b1d67d..36ee46b50f0 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -95,7 +95,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePiece stage = PROBCUT_TT + !(ttm && pos.capture_stage(ttm) && pos.pseudo_legal(ttm) - && pos.see_ge(ttm, occupied, threshold)); + && pos.see_ge(ttm, threshold)); } /// MovePicker::score() assigns a numerical value to each move in a list, used @@ -197,7 +197,7 @@ Move MovePicker::next_move(bool skipQuiets) { case GOOD_CAPTURE: if (select([&](){ - return pos.see_ge(*cur, occupied, Value(-cur->value)) ? + return pos.see_ge(*cur, Value(-cur->value)) ? // Move losing capture to endBadCaptures to be tried later true : (*endBadCaptures++ = *cur, false); })) return *(cur - 1); @@ -264,7 +264,7 @@ Move MovePicker::next_move(bool skipQuiets) { return select([](){ return true; }); case PROBCUT: - return select([&](){ return pos.see_ge(*cur, occupied, threshold); }); + return select([&](){ return pos.see_ge(*cur, threshold); }); case QCAPTURE: if (select([&](){ return depth > DEPTH_QS_RECAPTURES diff --git a/src/movepick.h b/src/movepick.h index 725607b8a54..b6c07378240 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -150,7 +150,6 @@ class MovePicker { Value threshold; Depth depth; ExtMove moves[MAX_MOVES]; - Bitboard occupied; }; } // namespace Stockfish diff --git a/src/position.cpp b/src/position.cpp index ba6888eb948..e6fdb511fcc 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1163,6 +1163,11 @@ bool Position::see_ge(Move m, Bitboard& occupied, Value threshold) const { return bool(res); } +bool Position::see_ge(Move m, Value threshold) const { + Bitboard occupied; + return see_ge(m, occupied, threshold); +} + /// Position::is_draw() tests whether the position is drawn by 50-move rule /// or by repetition. It does not detect stalemates. diff --git a/src/position.h b/src/position.h index 670b621ce74..bb45c44a3bf 100644 --- a/src/position.h +++ b/src/position.h @@ -145,6 +145,7 @@ class Position { // Static Exchange Evaluation bool see_ge(Move m, Bitboard& occupied, Value threshold = VALUE_ZERO) const; + bool see_ge(Move m, Value threshold = VALUE_ZERO) const; // Accessing hash keys Key key() const; diff --git a/src/search.cpp b/src/search.cpp index d2358ea2e1c..ac74cdaf885 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1058,9 +1058,8 @@ namespace { lmrDepth = std::max(lmrDepth, 0); - Bitboard occupied; // Prune moves with negative SEE (~4 Elo) - if (!pos.see_ge(move, occupied, Value(-24 * lmrDepth * lmrDepth - 15 * lmrDepth))) + if (!pos.see_ge(move, Value(-24 * lmrDepth * lmrDepth - 15 * lmrDepth))) continue; } } @@ -1545,7 +1544,6 @@ namespace { prevSq); int quietCheckEvasions = 0; - Bitboard occupied; // Step 5. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. @@ -1582,7 +1580,7 @@ namespace { continue; } - if (futilityBase <= alpha && !pos.see_ge(move, occupied, VALUE_ZERO + 1)) + if (futilityBase <= alpha && !pos.see_ge(move, VALUE_ZERO + 1)) { bestValue = std::max(bestValue, futilityBase); continue; @@ -1601,7 +1599,7 @@ namespace { continue; // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, occupied, Value(-110))) + if (!pos.see_ge(move, Value(-110))) continue; } From 3f01e3f41f11aa66befec2307a32ee023c699a2a Mon Sep 17 00:00:00 2001 From: peregrineshahin Date: Thu, 23 Mar 2023 13:35:34 +0300 Subject: [PATCH 1015/1766] Allow PvNode in futility pruning for captures. Passed non-regression STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 148128 W: 39428 L: 39333 D: 69367 Ptnml(0-2): 492, 16326, 40315, 16457, 474 https://tests.stockfishchess.org/tests/view/641c2dbfdb43ab2ba6f804e8 Passed non-regression LTC: LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 376256 W: 100906 L: 101039 D: 174311 Ptnml(0-2): 186, 36697, 114494, 36566, 185 https://tests.stockfishchess.org/tests/view/641d33b2db43ab2ba6f83338 closes https://github.com/official-stockfish/Stockfish/pull/4470 bench: 4935616 --- src/search.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index ac74cdaf885..5f95a1bd718 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1005,7 +1005,6 @@ namespace { { // Futility pruning for captures (~2 Elo) if ( !givesCheck - && !PvNode && lmrDepth < 6 && !ss->inCheck && ss->staticEval + 182 + 230 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] From 7a6fa34f5f9f0f193d9350cd58c82a8f98a6505d Mon Sep 17 00:00:00 2001 From: Maxim Masiutin Date: Sun, 12 Mar 2023 15:16:51 +0200 Subject: [PATCH 1016/1766] Improve compatibility this makes it easier to compile under MSVC, even though we recommend gcc/clang for production compiles at the moment. In Win32 API, by default, most null-terminated character strings arguments are of wchar_t (UTF16, formerly UCS16-LE) type, i.e. 2 bytes (at least) per character. So, src/misc.cpp should have proper type. Respectively, for src/syzygy/tbprobe.cpp, in Widows, file paths should be std::wstring rather than std::string. However, this requires a very big number of changes, since the config files are also keeping the 8-bit-per-character std::string strings. Therefore, just one change of using 8-byte-per-character CreateFileA make it compile under MSVC. closes https://github.com/official-stockfish/Stockfish/pull/4438 No functional change --- src/misc.cpp | 4 ++-- src/syzygy/tbprobe.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index c22126afe2d..6469c5cf9ec 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -605,7 +605,7 @@ static int best_node(size_t idx) { DWORD byteOffset = 0; // Early exit if the needed API is not available at runtime - HMODULE k32 = GetModuleHandle("Kernel32.dll"); + HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll")); auto fun1 = (fun1_t)(void(*)())GetProcAddress(k32, "GetLogicalProcessorInformationEx"); if (!fun1) return -1; @@ -675,7 +675,7 @@ void bindThisThread(size_t idx) { return; // Early exit if the needed API are not available at runtime - HMODULE k32 = GetModuleHandle("Kernel32.dll"); + HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll")); auto fun2 = (fun2_t)(void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMaskEx"); auto fun3 = (fun3_t)(void(*)())GetProcAddress(k32, "SetThreadGroupAffinity"); auto fun4 = (fun4_t)(void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMask2"); diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index b594ac3714e..9cb0bfdbc0f 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -234,7 +234,7 @@ class TBFile : public std::ifstream { } #else // Note FILE_FLAG_RANDOM_ACCESS is only a hint to Windows and as such may get ignored. - HANDLE fd = CreateFile(fname.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, + HANDLE fd = CreateFileA(fname.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, nullptr); if (fd == INVALID_HANDLE_VALUE) From e8742bdab35c63253b5110d1861f27337e18f9fc Mon Sep 17 00:00:00 2001 From: Maxim Masiutin Date: Wed, 29 Mar 2023 12:43:36 +0300 Subject: [PATCH 1017/1766] Made advanced Windows API calls dynamically linked Made advanced Windows API calls (those from Advapi32.dll) dynamically linked to avoid link errors when compiling using Intel icx compiler for Windows. https://github.com/official-stockfish/Stockfish/pull/4467 No functional change --- src/misc.cpp | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index 6469c5cf9ec..cac9dd94996 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -38,6 +38,9 @@ using fun2_t = bool(*)(USHORT, PGROUP_AFFINITY); using fun3_t = bool(*)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY); using fun4_t = bool(*)(USHORT, PGROUP_AFFINITY, USHORT, PUSHORT); using fun5_t = WORD(*)(); +using fun6_t = bool(*)(HANDLE, DWORD, PHANDLE); +using fun7_t = bool(*)(LPCSTR, LPCSTR, PLUID); +using fun8_t = bool(*)(HANDLE, BOOL, PTOKEN_PRIVILEGES, DWORD, PTOKEN_PRIVILEGES, PDWORD); } #endif @@ -488,11 +491,26 @@ static void* aligned_large_pages_alloc_windows([[maybe_unused]] size_t allocSize if (!largePageSize) return nullptr; + // Dynamically link OpenProcessToken, LookupPrivilegeValue and AdjustTokenPrivileges + HMODULE k32 = GetModuleHandle("Advapi32.dll"); + auto fun6 = (fun6_t)(void(*)())GetProcAddress(k32, "OpenProcessToken"); + if (!fun6) + return nullptr; + auto fun7 = (fun7_t)(void(*)())GetProcAddress(k32, "LookupPrivilegeValueA"); + if (!fun7) + return nullptr; + auto fun8 = (fun8_t)(void(*)())GetProcAddress(k32, "AdjustTokenPrivileges"); + if (!fun8) + return nullptr; + + // We need SeLockMemoryPrivilege, so try to enable it for the process - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken)) + // OpenProcessToken() + if (!fun6(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken)) return nullptr; - if (LookupPrivilegeValue(nullptr, SE_LOCK_MEMORY_NAME, &luid)) + // LookupPrivilegeValueA() + if (fun7(nullptr, SE_LOCK_MEMORY_NAME, &luid)) { TOKEN_PRIVILEGES tp { }; TOKEN_PRIVILEGES prevTp { }; @@ -504,7 +522,8 @@ static void* aligned_large_pages_alloc_windows([[maybe_unused]] size_t allocSize // Try to enable SeLockMemoryPrivilege. Note that even if AdjustTokenPrivileges() succeeds, // we still need to query GetLastError() to ensure that the privileges were actually obtained. - if (AdjustTokenPrivileges( + // AdjustTokenPrivileges() + if (fun8( hProcessToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &prevTp, &prevTpLen) && GetLastError() == ERROR_SUCCESS) { @@ -514,7 +533,8 @@ static void* aligned_large_pages_alloc_windows([[maybe_unused]] size_t allocSize nullptr, allocSize, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); // Privilege no longer needed, restore previous state - AdjustTokenPrivileges(hProcessToken, FALSE, &prevTp, 0, nullptr, nullptr); + // AdjustTokenPrivileges () + fun8(hProcessToken, FALSE, &prevTp, 0, nullptr, nullptr); } } From c3c46feebba470dcbaa0a5a6ef83534091dffe6a Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Thu, 30 Mar 2023 17:46:15 +0800 Subject: [PATCH 1018/1766] Remove reduction for moving threatened piece Simplify away "Decrease reduction if we move a threatened piece". Running a dbg_hit_on() shows that this line is only called ~0.12% of the time. Simplification STC: https://tests.stockfishchess.org/tests/view/641ec2dcdb43ab2ba6f88103 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 146128 W: 39168 L: 39070 D: 67890 Ptnml(0-2): 466, 16117, 39830, 16155, 496 Simplification LTC: https://tests.stockfishchess.org/tests/view/64200689db43ab2ba6f8bca8 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 248016 W: 66703 L: 66714 D: 114599 Ptnml(0-2): 105, 24202, 75406, 24189, 106 closes https://github.com/official-stockfish/Stockfish/pull/4471 Bench: 4961236 --- src/search.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5f95a1bd718..2fcbc7df2a5 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1181,11 +1181,6 @@ namespace { if (singularQuietLMR) r--; - // Decrease reduction if we move a threatened piece (~1 Elo) - if ( depth > 9 - && (mp.threatenedPieces & from_sq(move))) - r--; - // Increase reduction if next ply has a lot of fail high (~5 Elo) if ((ss+1)->cutoffCnt > 3) r++; From 66bf45b99e2061c1ba74f9975bc5059ac0121dfd Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 1 Apr 2023 11:56:49 +0200 Subject: [PATCH 1019/1766] Stringify the git info passed avoid escaping the string in the Makefile. Alternative to https://github.com/official-stockfish/Stockfish/pull/4476 closes https://github.com/official-stockfish/Stockfish/pull/4481 No functional change. --- src/Makefile | 4 ++-- src/evaluate.cpp | 2 -- src/misc.cpp | 6 ++---- src/misc.h | 3 +++ 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Makefile b/src/Makefile index e257bc6347d..0b22fb4e6e9 100644 --- a/src/Makefile +++ b/src/Makefile @@ -705,13 +705,13 @@ endif ### 3.7.1 Try to include git commit sha for versioning GIT_SHA = $(shell git rev-parse --short HEAD 2>/dev/null) ifneq ($(GIT_SHA), ) - CXXFLAGS += -DGIT_SHA=\"$(GIT_SHA)\" + CXXFLAGS += -DGIT_SHA=$(GIT_SHA) endif ### 3.7.2 Try to include git commit date for versioning GIT_DATE = $(shell git show -s --date=format:'%Y%m%d' --format=%cd HEAD 2>/dev/null) ifneq ($(GIT_DATE), ) - CXXFLAGS += -DGIT_DATE=\"$(GIT_DATE)\" + CXXFLAGS += -DGIT_DATE=$(GIT_DATE) endif ### 3.8 Link Time Optimization diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 99b873004ed..12883fcc43e 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -82,8 +82,6 @@ namespace Eval { eval_file = EvalFileDefaultName; #if defined(DEFAULT_NNUE_DIRECTORY) - #define stringify2(x) #x - #define stringify(x) stringify2(x) vector dirs = { "" , "" , CommandLine::binaryDirectory , stringify(DEFAULT_NNUE_DIRECTORY) }; #else vector dirs = { "" , "" , CommandLine::binaryDirectory }; diff --git a/src/misc.cpp b/src/misc.cpp index cac9dd94996..e36a04bccfb 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -160,7 +160,7 @@ string engine_info(bool to_uci) { { ss << "-"; #ifdef GIT_DATE - ss << GIT_DATE; + ss << stringify(GIT_DATE); #else constexpr string_view months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); string month, day, year; @@ -173,7 +173,7 @@ string engine_info(bool to_uci) { ss << "-"; #ifdef GIT_SHA - ss << GIT_SHA; + ss << stringify(GIT_SHA); #else ss << "nogit"; #endif @@ -190,8 +190,6 @@ string engine_info(bool to_uci) { std::string compiler_info() { - #define stringify2(x) #x - #define stringify(x) stringify2(x) #define make_version_string(major, minor, patch) stringify(major) "." stringify(minor) "." stringify(patch) /// Predefined macros hell: diff --git a/src/misc.h b/src/misc.h index c20a816efa0..d4965156325 100644 --- a/src/misc.h +++ b/src/misc.h @@ -28,6 +28,9 @@ #include "types.h" +#define stringify2(x) #x +#define stringify(x) stringify2(x) + namespace Stockfish { std::string engine_info(bool to_uci = false); From 38a80c0b47397dbdd9167ec1476dfd3c033020d6 Mon Sep 17 00:00:00 2001 From: MinetaS Date: Thu, 30 Mar 2023 11:14:30 +0000 Subject: [PATCH 1020/1766] Simplify away complexityAverage Instead of tracking the average of complexity values, calculate complexity of root position at the beginning of the search and use it as a scaling factor in time management. Passed non-regression STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 58752 W: 15738 L: 15551 D: 27463 Ptnml(0-2): 164, 6194, 16478, 6371, 169 https://tests.stockfishchess.org/tests/view/6423010edb43ab2ba6f9424a Passed non-regression LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 92872 W: 24865 L: 24729 D: 43278 Ptnml(0-2): 38, 8652, 28929, 8770, 47 https://tests.stockfishchess.org/tests/view/6423c1f0db43ab2ba6f9644f closes https://github.com/official-stockfish/Stockfish/pull/4472 No functional change --- src/misc.h | 26 -------------------------- src/search.cpp | 17 ++++++++++------- src/thread.h | 2 +- 3 files changed, 11 insertions(+), 34 deletions(-) diff --git a/src/misc.h b/src/misc.h index d4965156325..69d470c22f8 100644 --- a/src/misc.h +++ b/src/misc.h @@ -89,32 +89,6 @@ static inline const union { uint32_t i; char c[4]; } Le = { 0x01020304 }; static inline const bool IsLittleEndian = (Le.c[0] == 4); -// RunningAverage : a class to calculate a running average of a series of values. -// For efficiency, all computations are done with integers. -class RunningAverage { - public: - - // Reset the running average to rational value p / q - void set(int64_t p, int64_t q) - { average = p * PERIOD * RESOLUTION / q; } - - // Update average with value v - void update(int64_t v) - { average = RESOLUTION * v + (PERIOD - 1) * average / PERIOD; } - - // Test if average is strictly greater than rational a / b - bool is_greater(int64_t a, int64_t b) const - { return b * average > a * (PERIOD * RESOLUTION); } - - int64_t value() const - { return average / (PERIOD * RESOLUTION); } - - private : - static constexpr int64_t PERIOD = 4096; - static constexpr int64_t RESOLUTION = 1024; - int64_t average; -}; - template class ValueList { diff --git a/src/search.cpp b/src/search.cpp index 2fcbc7df2a5..3136046d2f5 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -293,6 +293,15 @@ void Thread::search() { if (mainThread) { + + int rootComplexity; + if (Eval::useNNUE) + Eval::NNUE::evaluate(rootPos, true, &rootComplexity); + else + Eval::evaluate(rootPos, &rootComplexity); + + mainThread->complexity = std::min(1.03 + (rootComplexity - 241) / 1552.0, 1.45); + if (mainThread->bestPreviousScore == VALUE_INFINITE) for (int i = 0; i < 4; ++i) mainThread->iterValue[i] = VALUE_ZERO; @@ -311,8 +320,6 @@ void Thread::search() { multiPV = std::min(multiPV, rootMoves.size()); - complexityAverage.set(153, 1); - optimism[us] = optimism[~us] = VALUE_ZERO; int searchAgainCounter = 0; @@ -472,10 +479,8 @@ void Thread::search() { timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.57 : 0.65; double reduction = (1.4 + mainThread->previousTimeReduction) / (2.08 * timeReduction); double bestMoveInstability = 1 + 1.8 * totBestMoveChanges / Threads.size(); - int complexity = mainThread->complexityAverage.value(); - double complexPosition = std::min(1.03 + (complexity - 241) / 1552.0, 1.45); - double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability * complexPosition; + double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability * mainThread->complexity; // Cap used time in case of a single legal move for a better viewer experience in tournaments // yielding correct scores and sufficiently fast moves. @@ -755,8 +760,6 @@ namespace { tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); } - thisThread->complexityAverage.update(complexity); - // Use static evaluation difference to improve quiet move ordering (~4 Elo) if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture) { diff --git a/src/thread.h b/src/thread.h index 46cdb11c36a..d6a48eca73d 100644 --- a/src/thread.h +++ b/src/thread.h @@ -60,7 +60,6 @@ class Thread { Pawns::Table pawnsTable; Material::Table materialTable; size_t pvIdx, pvLast; - RunningAverage complexityAverage; std::atomic nodes, tbHits, bestMoveChanges; int selDepth, nmpMinPly; Color nmpColor; @@ -87,6 +86,7 @@ struct MainThread : public Thread { void search() override; void check_time(); + double complexity; double previousTimeReduction; Value bestPreviousScore; Value bestPreviousAverageScore; From bc50378ff1915a8ad6ac3e4946193c65e4cacb56 Mon Sep 17 00:00:00 2001 From: Maxim Masiutin Date: Fri, 31 Mar 2023 18:16:50 +0300 Subject: [PATCH 1021/1766] Replace deprecated icc with icx Replace the deprecated Intel compiler icc with its newer icx variant. This newer compiler is based on clang, and yields good performance. As before, currently only linux is supported. closes https://github.com/official-stockfish/Stockfish/pull/4478 No functional change --- src/Makefile | 70 +++++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/src/Makefile b/src/Makefile index 0b22fb4e6e9..abcf11b0a11 100644 --- a/src/Makefile +++ b/src/Makefile @@ -425,10 +425,11 @@ ifeq ($(COMP),mingw) CXXFLAGS += -pedantic -Wextra -Wshadow -Wmissing-declarations endif -ifeq ($(COMP),icc) - comp=icc - CXX=icpc - CXXFLAGS += -diag-disable 1476,10120 -Wcheck -Wabi -Wdeprecated -strict-ansi +ifeq ($(COMP),icx) + comp=icx + CXX=icpx + CXXFLAGS += --intel -pedantic -Wextra -Wshadow -Wmissing-prototypes \ + -Wconditional-uninitialized -Wabi -Wdeprecated endif ifeq ($(COMP),clang) @@ -499,9 +500,9 @@ ifeq ($(COMP),ndk) LDFLAGS += -static-libstdc++ -pie -lm -latomic endif -ifeq ($(comp),icc) - profile_make = icc-profile-make - profile_use = icc-profile-use +ifeq ($(comp),icx) + profile_make = icx-profile-make + profile_use = icx-profile-use else ifeq ($(comp),clang) profile_make = clang-profile-make profile_use = clang-profile-use @@ -572,7 +573,7 @@ ifeq ($(optimize),yes) endif ifeq ($(KERNEL),Darwin) - ifeq ($(comp),$(filter $(comp),clang icc)) + ifeq ($(comp),$(filter $(comp),clang icx)) CXXFLAGS += -mdynamic-no-pic endif @@ -608,8 +609,6 @@ endif ifeq ($(popcnt),yes) ifeq ($(arch),$(filter $(arch),ppc64 armv7 armv8 arm64)) CXXFLAGS += -DUSE_POPCNT - else ifeq ($(comp),icc) - CXXFLAGS += -msse3 -DUSE_POPCNT else CXXFLAGS += -msse3 -mpopcnt -DUSE_POPCNT endif @@ -618,63 +617,63 @@ endif ### 3.6 SIMD architectures ifeq ($(avx2),yes) CXXFLAGS += -DUSE_AVX2 - ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + ifeq ($(comp),$(filter $(comp),gcc clang mingw icx)) CXXFLAGS += -mavx2 -mbmi endif endif ifeq ($(avxvnni),yes) CXXFLAGS += -DUSE_VNNI -DUSE_AVXVNNI - ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + ifeq ($(comp),$(filter $(comp),gcc clang mingw icx)) CXXFLAGS += -mavxvnni endif endif ifeq ($(avx512),yes) CXXFLAGS += -DUSE_AVX512 - ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + ifeq ($(comp),$(filter $(comp),gcc clang mingw icx)) CXXFLAGS += -mavx512f -mavx512bw endif endif ifeq ($(vnni256),yes) CXXFLAGS += -DUSE_VNNI - ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + ifeq ($(comp),$(filter $(comp),gcc clang mingw icx)) CXXFLAGS += -mavx512f -mavx512bw -mavx512vnni -mavx512dq -mavx512vl -mprefer-vector-width=256 endif endif ifeq ($(vnni512),yes) CXXFLAGS += -DUSE_VNNI - ifeq ($(comp),$(filter $(comp),gcc clang mingw)) - CXXFLAGS += -mavx512vnni -mavx512dq -mavx512vl + ifeq ($(comp),$(filter $(comp),gcc clang mingw icx)) + CXXFLAGS += -mavx512f -mavx512bw -mavx512vnni -mavx512dq -mavx512vl -mprefer-vector-width=512 endif endif ifeq ($(sse41),yes) CXXFLAGS += -DUSE_SSE41 - ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + ifeq ($(comp),$(filter $(comp),gcc clang mingw icx)) CXXFLAGS += -msse4.1 endif endif ifeq ($(ssse3),yes) CXXFLAGS += -DUSE_SSSE3 - ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + ifeq ($(comp),$(filter $(comp),gcc clang mingw icx)) CXXFLAGS += -mssse3 endif endif ifeq ($(sse2),yes) CXXFLAGS += -DUSE_SSE2 - ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + ifeq ($(comp),$(filter $(comp),gcc clang mingw icx)) CXXFLAGS += -msse2 endif endif ifeq ($(mmx),yes) CXXFLAGS += -DUSE_MMX - ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + ifeq ($(comp),$(filter $(comp),gcc clang mingw icx)) CXXFLAGS += -mmmx endif endif @@ -697,7 +696,7 @@ endif ### 3.7 pext ifeq ($(pext),yes) CXXFLAGS += -DUSE_PEXT - ifeq ($(comp),$(filter $(comp),gcc clang mingw)) + ifeq ($(comp),$(filter $(comp),gcc clang mingw icx)) CXXFLAGS += -mbmi2 endif endif @@ -719,8 +718,11 @@ endif ### needs access to the optimization flags. ifeq ($(optimize),yes) ifeq ($(debug), no) - ifeq ($(comp),clang) + ifeq ($(comp),$(filter $(comp),clang icx)) CXXFLAGS += -flto=full + ifeq ($(comp),icx) + CXXFLAGS += -fwhole-program-vtables + endif ifeq ($(target_windows),yes) CXXFLAGS += -fuse-ld=lld endif @@ -807,7 +809,7 @@ help: @echo "gcc > Gnu compiler (default)" @echo "mingw > Gnu compiler with MinGW under Windows" @echo "clang > LLVM Clang compiler" - @echo "icc > Intel compiler" + @echo "icx > Intel oneAPI DPC++/C++ Compiler" @echo "ndk > Google NDK to cross-compile for Android" @echo "" @echo "Simple examples. If you don't know what to do, you likely want to run one of: " @@ -833,8 +835,10 @@ endif .PHONY: help build profile-build strip install clean net objclean profileclean \ - config-sanity icc-profile-use icc-profile-make gcc-profile-use gcc-profile-make \ - clang-profile-use clang-profile-make FORCE + config-sanity \ + icx-profile-use icx-profile-make \ + gcc-profile-use gcc-profile-make \ + clang-profile-use clang-profile-make FORCE build: net config-sanity $(MAKE) ARCH=$(ARCH) COMP=$(COMP) all @@ -949,7 +953,9 @@ config-sanity: net @echo "vnni256: '$(vnni256)'" @echo "vnni512: '$(vnni512)'" @echo "neon: '$(neon)'" + @echo "dotprod: '$(dotprod)'" @echo "arm_version: '$(arm_version)'" + @echo "target_windows: '$(target_windows)'" @echo "" @echo "Flags:" @echo "CXX: $(CXX)" @@ -978,7 +984,7 @@ config-sanity: net @test "$(vnni256)" = "yes" || test "$(vnni256)" = "no" @test "$(vnni512)" = "yes" || test "$(vnni512)" = "no" @test "$(neon)" = "yes" || test "$(neon)" = "no" - @test "$(comp)" = "gcc" || test "$(comp)" = "icc" || test "$(comp)" = "mingw" || test "$(comp)" = "clang" \ + @test "$(comp)" = "gcc" || test "$(comp)" = "icx" || test "$(comp)" = "mingw" || test "$(comp)" = "clang" \ || test "$(comp)" = "armv7a-linux-androideabi16-clang" || test "$(comp)" = "aarch64-linux-android21-clang" $(EXE): $(OBJS) @@ -1016,15 +1022,17 @@ gcc-profile-use: EXTRALDFLAGS='-lgcov' \ all -icc-profile-make: - @mkdir -p profdir +icx-profile-make: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ - EXTRACXXFLAGS='-prof-gen=srcpos -prof_dir ./profdir' \ + EXTRACXXFLAGS='-fprofile-instr-generate ' \ + EXTRALDFLAGS=' -fprofile-instr-generate' \ all -icc-profile-use: +icx-profile-use: + $(XCRUN) llvm-profdata merge -output=stockfish.profdata *.profraw $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ - EXTRACXXFLAGS='-prof_use -prof_dir ./profdir' \ + EXTRACXXFLAGS='-fprofile-instr-use=stockfish.profdata' \ + EXTRALDFLAGS='-fprofile-use ' \ all .depend: $(SRCS) From 6a6e32dfc80488dfdcd6c23e601063b47729e890 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sat, 1 Apr 2023 15:22:53 +0300 Subject: [PATCH 1022/1766] Decrease Depth more for positions not in TT. If the position is not in TT, decrease depth by 2 or by 4 if the TT entry for the current position was hit and the stored depth is greater than or equal to the current depth. Many thanks to Vizvezdenec as the main idea was his. Passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 70664 W: 18995 L: 18639 D: 33030 Ptnml(0-2): 228, 7712, 19090, 8080, 222 https://tests.stockfishchess.org/tests/view/64258a8bdb43ab2ba6f9b682 Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 85040 W: 23227 L: 22836 D: 38977 Ptnml(0-2): 26, 8115, 25867, 8466, 46 https://tests.stockfishchess.org/tests/view/64262057db43ab2ba6f9d0e7 closes https://github.com/official-stockfish/Stockfish/pull/4482 bench: 4380438 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 3136046d2f5..46cca50f4b2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -899,11 +899,11 @@ namespace { Eval::NNUE::hint_common_parent_position(pos); } - // Step 11. If the position is not in TT, decrease depth by 3. + // Step 11. If the position is not in TT, decrease depth by 2 (or by 4 if the TT entry for the current position was hit and the stored depth is greater than or equal to the current depth). // Use qsearch if depth is equal or below zero (~9 Elo) if ( PvNode && !ttMove) - depth -= 3; + depth -= 2 + 2 * (ss->ttHit && tte->depth() >= depth); if (depth <= 0) return qsearch(pos, ss, alpha, beta); From 1fee996999364bedbd9ca4c29649d5c7321947c5 Mon Sep 17 00:00:00 2001 From: Miguel Lahoz Date: Sun, 2 Apr 2023 17:28:39 +0800 Subject: [PATCH 1023/1766] Remove unneeded bitboard from MP Recent simplification has removed the need for an extra bitboard in MP struct. Use a local variable instead. STC: Passed Non-regression test https://tests.stockfishchess.org/tests/view/64294ae677ff3301150cba16 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 64872 W: 17383 L: 17203 D: 30286 Ptnml(0-2): 179, 6675, 18546, 6859, 177 closes https://github.com/official-stockfish/Stockfish/pull/4490 No functional change. --- src/movepick.cpp | 3 +-- src/movepick.h | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 36ee46b50f0..6fbcb2c3d2f 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -69,7 +69,6 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist stage = (pos.checkers() ? EVASION_TT : MAIN_TT) + !(ttm && pos.pseudo_legal(ttm)); - threatenedPieces = 0; } /// MovePicker constructor for quiescence search @@ -106,7 +105,7 @@ void MovePicker::score() { static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type"); - [[maybe_unused]] Bitboard threatenedByPawn, threatenedByMinor, threatenedByRook; + [[maybe_unused]] Bitboard threatenedByPawn, threatenedByMinor, threatenedByRook, threatenedPieces; if constexpr (Type == QUIETS) { Color us = pos.side_to_move(); diff --git a/src/movepick.h b/src/movepick.h index b6c07378240..0b44557f198 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -131,8 +131,6 @@ class MovePicker { MovePicker(const Position&, Move, Value, const CapturePieceToHistory*); Move next_move(bool skipQuiets = false); - Bitboard threatenedPieces; - private: template Move select(Pred); template void score(); From 77e2b915e1e4f2469a414712e52b469633fb3273 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Sat, 1 Apr 2023 18:48:31 -0500 Subject: [PATCH 1024/1766] Simplifiy TM's root complexity Also requires moving optimism initialization, this is a very early `evaluate()` call. STC: https://tests.stockfishchess.org/tests/view/6428c39677ff3301150ca0d7 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 51256 W: 13805 L: 13612 D: 23839 Ptnml(0-2): 145, 5283, 14592, 5450, 158 LTC: https://tests.stockfishchess.org/tests/view/64296ff377ff3301150cc519 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 106968 W: 28951 L: 28830 D: 49187 Ptnml(0-2): 47, 9746, 33789, 9843, 59 closes https://github.com/official-stockfish/Stockfish/pull/4492 no functional change --- src/search.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 46cca50f4b2..becaee3a20a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -290,15 +290,13 @@ void Thread::search() { bestValue = delta = alpha = -VALUE_INFINITE; beta = VALUE_INFINITE; + optimism[us] = optimism[~us] = VALUE_ZERO; if (mainThread) { int rootComplexity; - if (Eval::useNNUE) - Eval::NNUE::evaluate(rootPos, true, &rootComplexity); - else - Eval::evaluate(rootPos, &rootComplexity); + Eval::evaluate(rootPos, &rootComplexity); mainThread->complexity = std::min(1.03 + (rootComplexity - 241) / 1552.0, 1.45); @@ -320,8 +318,6 @@ void Thread::search() { multiPV = std::min(multiPV, rootMoves.size()); - optimism[us] = optimism[~us] = VALUE_ZERO; - int searchAgainCounter = 0; // Iterative deepening loop until requested to stop or the target depth is reached From 9a42bbdf3163222db5e0fa764d48ca0a09a0dec2 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Tue, 4 Apr 2023 14:26:29 +0300 Subject: [PATCH 1025/1766] Parameters Tweak Passed STC LLR: 3.22 (-2.94,2.94) <0.00,2.00> Total: 664048 W: 177526 L: 176301 D: 310221 Ptnml(0-2): 2002, 72968, 180891, 74129, 2034 https://tests.stockfishchess.org/tests/view/64219901db43ab2ba6f901fa Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 77576 W: 21125 L: 20750 D: 35701 Ptnml(0-2): 24, 7350, 23669, 7717, 28 https://tests.stockfishchess.org/tests/view/642abe3377ff3301150d3a16 closes https://github.com/official-stockfish/Stockfish/pull/4493 bench: 4522076 --- src/search.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index becaee3a20a..749de792dd6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -72,7 +72,7 @@ namespace { Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) { int r = Reductions[d] * Reductions[mn]; - return (r + 1449 - int(delta) * 1032 / int(rootDelta)) / 1024 + (!i && r > 941); + return (r + 1449 - int(delta) * 1001 / int(rootDelta)) / 1024 + (!i && r > 941); } constexpr int futility_move_count(bool improving, Depth depth) { @@ -82,7 +82,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return std::min(340 * d - 470, 1855); + return std::min(341 * d - 470, 1855); } // Add a small random component to draw evaluations to avoid 3-fold blindness @@ -1057,7 +1057,7 @@ namespace { lmrDepth = std::max(lmrDepth, 0); // Prune moves with negative SEE (~4 Elo) - if (!pos.see_ge(move, Value(-24 * lmrDepth * lmrDepth - 15 * lmrDepth))) + if (!pos.see_ge(move, Value(-24 * lmrDepth * lmrDepth - 16 * lmrDepth))) continue; } } @@ -1193,10 +1193,10 @@ namespace { + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 4182; + - 4082; // Decrease/increase reduction for moves with a good/bad history (~25 Elo) - r -= ss->statScore / (11791 + 3992 * (depth > 6 && depth < 19)); + r -= ss->statScore / (11079 + 4626 * (depth > 6 && depth < 19)); // Step 17. Late moves reduction / extension (LMR, ~117 Elo) // We use various heuristics for the sons of a node after the first son has From a2737d8bb5e480563823820fb12a8887d61c991e Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Wed, 5 Apr 2023 07:25:00 +0800 Subject: [PATCH 1026/1766] Simplify away piece count condition for useClassical Simplify away the piece count condition for useClassical. In compensation, the psq requirement is increased by 15%. Also updated the Elo estimate for useClassical, based on recent testing. Simplification STC: https://tests.stockfishchess.org/tests/view/642acbb577ff3301150d3ef5 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 51984 W: 13906 L: 13707 D: 24371 Ptnml(0-2): 150, 5638, 14227, 5817, 160 Simplification LTC: https://tests.stockfishchess.org/tests/view/642b9c5777ff3301150d778a LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 119696 W: 32412 L: 32300 D: 54984 Ptnml(0-2): 53, 11529, 36567, 11651, 48 closes https://github.com/official-stockfish/Stockfish/pull/4494 Bench: 5089321 --- src/evaluate.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 12883fcc43e..703cf869cee 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1053,8 +1053,8 @@ Value Eval::evaluate(const Position& pos, int* complexity) { // We use the much less accurate but faster Classical eval when the NNUE // option is set to false. Otherwise we use the NNUE eval unless the - // PSQ advantage is decisive and several pieces remain. (~3 Elo) - bool useClassical = !useNNUE || (pos.count() > 7 && abs(psq) > 1781); + // PSQ advantage is decisive. (~4 Elo at STC, 1 Elo at LTC) + bool useClassical = !useNNUE || abs(psq) > 2048; if (useClassical) v = Evaluation(pos).value(); From 510aca1ef62279dc35941fa45ed61fb9d3796f10 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Wed, 5 Apr 2023 05:48:34 +0300 Subject: [PATCH 1027/1766] Assign negative stat bonuses for quiet moves at Pv nodes This patch assigns negative stat bonuses for quiet moves at pv nodes which are searched at depth greater than this node assumes, so are extended. Passed STC: https://tests.stockfishchess.org/tests/view/6426198bdb43ab2ba6f9cfa2 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 548944 W: 147287 L: 146254 D: 255403 Ptnml(0-2): 1662, 59772, 150671, 60605, 1762 Passed LTC: https://tests.stockfishchess.org/tests/view/642be4f177ff3301150d892d LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 93352 W: 25400 L: 24994 D: 42958 Ptnml(0-2): 44, 8817, 28547, 9225, 43 closes https://github.com/official-stockfish/Stockfish/pull/4495 bench 5044536 --- src/search.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 749de792dd6..aa1a3e8c17f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -290,7 +290,7 @@ void Thread::search() { bestValue = delta = alpha = -VALUE_INFINITE; beta = VALUE_INFINITE; - optimism[us] = optimism[~us] = VALUE_ZERO; + optimism[WHITE] = optimism[BLACK] = VALUE_ZERO; if (mainThread) { @@ -1257,6 +1257,9 @@ namespace { (ss+1)->pv[0] = MOVE_NONE; value = -search(pos, ss+1, -beta, -alpha, newDepth, false); + + if (moveCount > 1 && newDepth >= depth && !capture) + update_continuation_histories(ss, movedPiece, to_sq(move), -stat_bonus(newDepth)); } // Step 19. Undo move From 59f2085469a7dd96146905a5d8d0c1a5d987187d Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Thu, 6 Apr 2023 00:35:05 +0300 Subject: [PATCH 1028/1766] Depth Tweak and tuning tunes reduction related parameters, and introduces more reduction on found good moves. credit for this patch goes also to candirufish Yoshie2000 dubslow peregrineshahin Vizvezdenec Passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 38424 W: 10346 L: 10040 D: 18038 Ptnml(0-2): 103, 4111, 10473, 4427, 98 https://tests.stockfishchess.org/tests/view/642ca74277ff3301150db511 Passed LTC: LLR: 2.97 (-2.94,2.94) <0.50,2.50> Total: 136968 W: 37151 L: 36660 D: 63157 Ptnml(0-2): 43, 13052, 41808, 13533, 48 https://tests.stockfishchess.org/tests/view/642d632377ff3301150dddbe closes https://github.com/official-stockfish/Stockfish/pull/4499 bench: 3672914 --- src/search.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index aa1a3e8c17f..fba9685b2e0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -72,7 +72,7 @@ namespace { Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) { int r = Reductions[d] * Reductions[mn]; - return (r + 1449 - int(delta) * 1001 / int(rootDelta)) / 1024 + (!i && r > 941); + return (r + 1449 - int(delta) * 937 / int(rootDelta)) / 1024 + (!i && r > 941); } constexpr int futility_move_count(bool improving, Depth depth) { @@ -82,7 +82,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return std::min(341 * d - 470, 1855); + return std::min(341 * d - 470, 1710); } // Add a small random component to draw evaluations to avoid 3-fold blindness @@ -775,7 +775,7 @@ namespace { // Step 7. Razoring (~1 Elo). // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. - if (eval < alpha - 426 - 252 * depth * depth) + if (eval < alpha - 426 - 256 * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -797,7 +797,7 @@ namespace { && (ss-1)->statScore < 18755 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 19 * depth - improvement / 13 + 253 + complexity / 25 + && ss->staticEval >= beta - 20 * depth - improvement / 13 + 253 + complexity / 25 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) @@ -805,7 +805,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth, eval and complexity of position - Depth R = std::min(int(eval - beta) / 168, 6) + depth / 3 + 4 - (complexity > 825); + Depth R = std::min(int(eval - beta) / 172, 6) + depth / 3 + 4 - (complexity > 825); ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -1333,16 +1333,18 @@ namespace { if (PvNode && value < beta) // Update alpha! Always alpha < beta { - alpha = value; // Reduce other moves if we have found at least one score improvement (~1 Elo) if ( depth > 1 - && depth < 6 - && beta < 10534 - && alpha > -10534) - depth -= 1; + && ((improving && complexity > 971) || (value < (5 * alpha + 75 * beta) / 87) || depth < 6) + && beta < 12535 + && value > -12535) { + bool extraReduction = depth > 2 && alpha > -12535 && bestValue != -VALUE_INFINITE && (value - bestValue) > (7 * (beta - alpha)) / 8; + depth -= 1 + extraReduction; + } assert(depth > 0); + alpha = value; } else { From b36d39de3d61b8f31c11d85233631aafaf760ee1 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 9 Apr 2023 09:18:29 +0200 Subject: [PATCH 1029/1766] Fix rootComplexity calculation The calculation of rootComplexity can't call eval when in check. Doing so triggers an assert if compiled in debug mode when the rootpos is evaluated using classical eval. Fixes https://github.com/official-stockfish/Stockfish/issues/4512 Passed STC: https://tests.stockfishchess.org/tests/view/6432697431feee5c6d306876 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 41096 W: 11017 L: 10815 D: 19264 Ptnml(0-2): 113, 4172, 11780, 4366, 117 Running LTC: https://tests.stockfishchess.org/tests/view/6432974d31feee5c6d306fc0 LLR: 1.76 (-2.94,2.94) <-1.75,0.25> Total: 73200 W: 19792 L: 19728 D: 33680 Ptnml(0-2): 24, 6659, 23182, 6699, 36 closes https://github.com/official-stockfish/Stockfish/pull/4515 No functional change --- src/evaluate.cpp | 2 ++ src/search.cpp | 10 ++++++---- src/thread.cpp | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 703cf869cee..2d0df89c3e7 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1048,6 +1048,8 @@ namespace { Value Eval::evaluate(const Position& pos, int* complexity) { + assert(!pos.checkers()); + Value v; Value psq = pos.psq_eg_stm(); diff --git a/src/search.cpp b/src/search.cpp index fba9685b2e0..5d54a15d628 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -295,10 +295,12 @@ void Thread::search() { if (mainThread) { - int rootComplexity; - Eval::evaluate(rootPos, &rootComplexity); - - mainThread->complexity = std::min(1.03 + (rootComplexity - 241) / 1552.0, 1.45); + if (!rootPos.checkers()) + { + int rootComplexity; + Eval::evaluate(rootPos, &rootComplexity); + mainThread->complexity = std::min(1.03 + (rootComplexity - 241) / 1552.0, 1.45); + } if (mainThread->bestPreviousScore == VALUE_INFINITE) for (int i = 0; i < 4; ++i) diff --git a/src/thread.cpp b/src/thread.cpp index c680393e277..202768c863c 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -160,6 +160,7 @@ void ThreadPool::clear() { main()->bestPreviousScore = VALUE_INFINITE; main()->bestPreviousAverageScore = VALUE_INFINITE; main()->previousTimeReduction = 1.0; + main()->complexity = 1.0; } From 5d258e168f7ea9019ed640ae2e56f04b26aea6a2 Mon Sep 17 00:00:00 2001 From: Maxim Masiutin Date: Sat, 1 Apr 2023 20:14:41 +0300 Subject: [PATCH 1030/1766] Fix linking / character types of windows API calls ensures large pages can be allocated again. closes https://github.com/official-stockfish/Stockfish/pull/4509 No functional change --- src/misc.cpp | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index e36a04bccfb..f1554060d5e 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -490,25 +490,29 @@ static void* aligned_large_pages_alloc_windows([[maybe_unused]] size_t allocSize return nullptr; // Dynamically link OpenProcessToken, LookupPrivilegeValue and AdjustTokenPrivileges - HMODULE k32 = GetModuleHandle("Advapi32.dll"); - auto fun6 = (fun6_t)(void(*)())GetProcAddress(k32, "OpenProcessToken"); + + HMODULE hAdvapi32 = GetModuleHandle(TEXT("advapi32.dll")); + + if (!hAdvapi32) + hAdvapi32 = LoadLibrary(TEXT("advapi32.dll")); + + auto fun6 = (fun6_t)(void(*)())GetProcAddress(hAdvapi32, "OpenProcessToken"); if (!fun6) return nullptr; - auto fun7 = (fun7_t)(void(*)())GetProcAddress(k32, "LookupPrivilegeValueA"); + auto fun7 = (fun7_t)(void(*)())GetProcAddress(hAdvapi32, "LookupPrivilegeValueA"); if (!fun7) return nullptr; - auto fun8 = (fun8_t)(void(*)())GetProcAddress(k32, "AdjustTokenPrivileges"); + auto fun8 = (fun8_t)(void(*)())GetProcAddress(hAdvapi32, "AdjustTokenPrivileges"); if (!fun8) return nullptr; - // We need SeLockMemoryPrivilege, so try to enable it for the process - // OpenProcessToken() - if (!fun6(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken)) - return nullptr; + if (!fun6( // OpenProcessToken() + GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken)) + return nullptr; - // LookupPrivilegeValueA() - if (fun7(nullptr, SE_LOCK_MEMORY_NAME, &luid)) + if (fun7( // LookupPrivilegeValue(nullptr, SE_LOCK_MEMORY_NAME, &luid) + nullptr, "SeLockMemoryPrivilege", &luid)) { TOKEN_PRIVILEGES tp { }; TOKEN_PRIVILEGES prevTp { }; @@ -520,8 +524,7 @@ static void* aligned_large_pages_alloc_windows([[maybe_unused]] size_t allocSize // Try to enable SeLockMemoryPrivilege. Note that even if AdjustTokenPrivileges() succeeds, // we still need to query GetLastError() to ensure that the privileges were actually obtained. - // AdjustTokenPrivileges() - if (fun8( + if (fun8( // AdjustTokenPrivileges() hProcessToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &prevTp, &prevTpLen) && GetLastError() == ERROR_SUCCESS) { @@ -531,8 +534,8 @@ static void* aligned_large_pages_alloc_windows([[maybe_unused]] size_t allocSize nullptr, allocSize, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); // Privilege no longer needed, restore previous state - // AdjustTokenPrivileges () - fun8(hProcessToken, FALSE, &prevTp, 0, nullptr, nullptr); + fun8( // AdjustTokenPrivileges () + hProcessToken, FALSE, &prevTp, 0, nullptr, nullptr); } } From 6e63dd63a4f1e3074a9f5a8d7f64fdd0eba19c7e Mon Sep 17 00:00:00 2001 From: MinetaS Date: Fri, 7 Apr 2023 15:23:04 +0000 Subject: [PATCH 1031/1766] Use int conversion for Option class The current implementation generates warnings on MSVC. However, we have no real use cases for double-typed UCI option values now. Also parameter tuning only accepts following three types: int, Value, Score closes https://github.com/official-stockfish/Stockfish/pull/4505 No functional change --- src/tt.cpp | 4 ++-- src/uci.h | 2 +- src/ucioption.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tt.cpp b/src/tt.cpp index 39f18d3d9c4..3339c993c41 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -87,7 +87,7 @@ void TranspositionTable::clear() { std::vector threads; - for (size_t idx = 0; idx < Options["Threads"]; ++idx) + for (size_t idx = 0; idx < size_t(Options["Threads"]); ++idx) { threads.emplace_back([this, idx]() { @@ -98,7 +98,7 @@ void TranspositionTable::clear() { // Each thread will zero its part of the hash table const size_t stride = size_t(clusterCount / Options["Threads"]), start = size_t(stride * idx), - len = idx != Options["Threads"] - 1 ? + len = idx != size_t(Options["Threads"]) - 1 ? stride : clusterCount - start; std::memset(&table[start], 0, len * sizeof(Cluster)); diff --git a/src/uci.h b/src/uci.h index 70e45accd1c..9ca0ed36b4d 100644 --- a/src/uci.h +++ b/src/uci.h @@ -61,7 +61,7 @@ class Option { Option& operator=(const std::string&); void operator<<(const Option&); - operator double() const; + operator int() const; operator std::string() const; bool operator==(const char*) const; diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 39933ea5e8d..f6342e5cb57 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -128,9 +128,9 @@ Option::Option(double v, int minv, int maxv, OnChange f) : type("spin"), min(min Option::Option(const char* v, const char* cur, OnChange f) : type("combo"), min(0), max(0), on_change(f) { defaultValue = v; currentValue = cur; } -Option::operator double() const { +Option::operator int() const { assert(type == "check" || type == "spin"); - return (type == "spin" ? stof(currentValue) : currentValue == "true"); + return (type == "spin" ? std::stoi(currentValue) : currentValue == "true"); } Option::operator std::string() const { From a5643b89fda5060d4b40dff54afe02816c899dd4 Mon Sep 17 00:00:00 2001 From: MinetaS Date: Fri, 7 Apr 2023 14:30:59 +0000 Subject: [PATCH 1032/1766] Remove extraReduction Since bestValue becomes value and beta - alpha is always non-negative, extraReduction is always false, hence it has no effect. This patch includes small changes to improve readability. closes https://github.com/official-stockfish/Stockfish/pull/4505 No functional change --- src/search.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5d54a15d628..4463b42aaed 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1335,15 +1335,14 @@ namespace { if (PvNode && value < beta) // Update alpha! Always alpha < beta { - // Reduce other moves if we have found at least one score improvement (~1 Elo) if ( depth > 1 - && ((improving && complexity > 971) || (value < (5 * alpha + 75 * beta) / 87) || depth < 6) + && ( (improving && complexity > 971) + || value < (5 * alpha + 75 * beta) / 87 + || depth < 6) && beta < 12535 - && value > -12535) { - bool extraReduction = depth > 2 && alpha > -12535 && bestValue != -VALUE_INFINITE && (value - bestValue) > (7 * (beta - alpha)) / 8; - depth -= 1 + extraReduction; - } + && value > -12535) + depth -= 1; assert(depth > 0); alpha = value; From 2f2f45f9f47c1212f3229c22304456c9bad8f843 Mon Sep 17 00:00:00 2001 From: Maxim Masiutin Date: Thu, 6 Apr 2023 14:08:50 +0300 Subject: [PATCH 1033/1766] Made two specializations for affine transform easier to understand. Added AVX-512 for the specialization for small inputs closes https://github.com/official-stockfish/Stockfish/pull/4502 No functional change --- src/nnue/layers/affine_transform.h | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index f84f054e7de..9e2f2f97323 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -31,7 +31,7 @@ This file contains the definition for a fully connected layer (aka affine transform). Two approaches are employed, depending on the sizes of the transform. - Approach 1: + Approach 1 (a specialization for large inputs): - used when the PaddedInputDimensions >= 128 - uses AVX512 if possible - processes inputs in batches of 2*InputSimdWidth @@ -42,9 +42,8 @@ depends on the architecture (the amount of registers) - accumulate + hadd is used - Approach 2: + Approach 2 (a specialization for small inputs): - used when the PaddedInputDimensions < 128 - - does not use AVX512 - expected use-case is for when PaddedInputDimensions == 32 and InputDimensions <= 32. - that's why AVX512 is hard to implement - expected use-case is small layers @@ -169,7 +168,7 @@ namespace Stockfish::Eval::NNUE::Layers { constexpr IndexType LargeInputSize = std::numeric_limits::max(); #endif - // A specialization for large inputs. + // A specialization for large inputs template class AffineTransform(InDims, MaxSimdWidth) >= LargeInputSize)>> { public: @@ -188,7 +187,7 @@ namespace Stockfish::Eval::NNUE::Layers { using OutputBuffer = OutputType[PaddedOutputDimensions]; - static_assert(PaddedInputDimensions >= LargeInputSize, "Something went wrong. This specialization should not have been chosen."); + static_assert(PaddedInputDimensions >= LargeInputSize, "Something went wrong. This specialization (for large inputs) should not have been chosen."); #if defined (USE_AVX512) static constexpr IndexType InputSimdWidth = 64; @@ -396,6 +395,7 @@ namespace Stockfish::Eval::NNUE::Layers { alignas(CacheLineSize) WeightType weights[OutputDimensions * PaddedInputDimensions]; }; + // A specialization for small inputs template class AffineTransform(InDims, MaxSimdWidth) < LargeInputSize)>> { public: @@ -415,12 +415,7 @@ namespace Stockfish::Eval::NNUE::Layers { using OutputBuffer = OutputType[PaddedOutputDimensions]; - static_assert(PaddedInputDimensions < LargeInputSize, "Something went wrong. This specialization should not have been chosen."); - -#if defined (USE_SSSE3) - static constexpr IndexType OutputSimdWidth = SimdWidth / 4; - static constexpr IndexType InputSimdWidth = SimdWidth; -#endif + static_assert(PaddedInputDimensions < LargeInputSize, "Something went wrong. This specialization (for small inputs) should not have been chosen."); // Hash value embedded in the evaluation file static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) { @@ -470,7 +465,14 @@ namespace Stockfish::Eval::NNUE::Layers { const OutputType* propagate( const InputType* input, OutputType* output) const { -#if defined (USE_AVX2) +#if defined (USE_AVX512) + using vec_t = __m512i; + #define vec_setzero _mm512_setzero_si512 + #define vec_set_32 _mm512_set1_epi32 + #define vec_add_dpbusd_32 Simd::m512_add_dpbusd_epi32 + #define vec_add_dpbusd_32x2 Simd::m512_add_dpbusd_epi32x2 + #define vec_hadd Simd::m512_hadd +#elif defined (USE_AVX2) using vec_t = __m256i; #define vec_setzero _mm256_setzero_si256 #define vec_set_32 _mm256_set1_epi32 @@ -489,6 +491,8 @@ namespace Stockfish::Eval::NNUE::Layers { #if defined (USE_SSSE3) const auto inputVector = reinterpret_cast(input); + static constexpr IndexType OutputSimdWidth = sizeof(vec_t) / sizeof(OutputType); + static_assert(OutputDimensions % OutputSimdWidth == 0 || OutputDimensions == 1); if constexpr (OutputDimensions % OutputSimdWidth == 0) From 7a9f67747f23e837a8691ba9e6e4f0d1fdafff73 Mon Sep 17 00:00:00 2001 From: mstembera Date: Tue, 4 Apr 2023 19:44:09 -0700 Subject: [PATCH 1034/1766] Reduce Position::pieces() overloads Reduce the number of overloads for pieces() by using a more general template implementation. Secondly simplify some code in search.cpp using the new general functionality. TC https://tests.stockfishchess.org/tests/view/642ce27877ff3301150dc193 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 269640 W: 71775 L: 71809 D: 126056 Ptnml(0-2): 687, 27294, 78885, 27274, 680 closes https://github.com/official-stockfish/Stockfish/pull/4501 No functional change. --- src/position.h | 19 ++++++++----------- src/search.cpp | 12 +++++++----- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/position.h b/src/position.h index bb45c44a3bf..a736f3e677b 100644 --- a/src/position.h +++ b/src/position.h @@ -92,10 +92,9 @@ class Position { // Position representation Bitboard pieces(PieceType pt) const; - Bitboard pieces(PieceType pt1, PieceType pt2) const; + template Bitboard pieces(PieceType pt, PieceTypes... pts) const; Bitboard pieces(Color c) const; - Bitboard pieces(Color c, PieceType pt) const; - Bitboard pieces(Color c, PieceType pt1, PieceType pt2) const; + template Bitboard pieces(Color c, PieceTypes... pts) const; Piece piece_on(Square s) const; Square ep_square() const; bool empty(Square s) const; @@ -229,20 +228,18 @@ inline Bitboard Position::pieces(PieceType pt = ALL_PIECES) const { return byTypeBB[pt]; } -inline Bitboard Position::pieces(PieceType pt1, PieceType pt2) const { - return pieces(pt1) | pieces(pt2); +template +inline Bitboard Position::pieces(PieceType pt, PieceTypes... pts) const { + return pieces(pt) | pieces(pts...); } inline Bitboard Position::pieces(Color c) const { return byColorBB[c]; } -inline Bitboard Position::pieces(Color c, PieceType pt) const { - return pieces(c) & pieces(pt); -} - -inline Bitboard Position::pieces(Color c, PieceType pt1, PieceType pt2) const { - return pieces(c) & (pieces(pt1) | pieces(pt2)); +template +inline Bitboard Position::pieces(Color c, PieceTypes... pts) const { + return pieces(c) & pieces(pts...); } template inline int Position::count(Color c) const { diff --git a/src/search.cpp b/src/search.cpp index 4463b42aaed..4187117b7df 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1018,16 +1018,18 @@ namespace { { if (depth < 2 - capture) continue; - // don't prune move if a heavy enemy piece (KQR) is under attack after the exchanges - Bitboard leftEnemies = (pos.pieces(~us, QUEEN, ROOK) | pos.pieces(~us, KING)) & occupied; + // Don't prune the move if opp. King/Queen/Rook is attacked by a slider after the exchanges. + // Since in see_ge we don't update occupied when the king recaptures, we also don't prune the + // move when the opp. King gets a discovered slider attack DURING the exchanges. + Bitboard leftEnemies = pos.pieces(~us, ROOK, QUEEN, KING) & occupied; Bitboard attacks = 0; occupied |= to_sq(move); while (leftEnemies && !attacks) { Square sq = pop_lsb(leftEnemies); - attacks |= pos.attackers_to(sq, occupied) & pos.pieces(us) & occupied; - // exclude Queen/Rook(s) which were already threatened before SEE - if (attacks && (sq != pos.square(~us) && (pos.attackers_to(sq, pos.pieces()) & pos.pieces(us)))) + attacks = pos.attackers_to(sq, occupied) & pos.pieces(us) & occupied; + // Exclude Queen/Rook(s) which were already threatened before SEE + if (attacks && sq != pos.square(~us) && (pos.attackers_to(sq, pos.pieces()) & pos.pieces(us))) attacks = 0; } if (!attacks) From 1a64afb1c65591ccd374504c791eb27b762f6c8f Mon Sep 17 00:00:00 2001 From: Maxim Masiutin Date: Sat, 1 Apr 2023 23:49:03 +0300 Subject: [PATCH 1035/1766] Do no initialize TM in all cases Avoid doing full TM initialization if it won't be used, avoids division by zero. closes https://github.com/official-stockfish/Stockfish/pull/4484 No functional change --- src/timeman.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/timeman.cpp b/src/timeman.cpp index 5c826b4f0c5..061de0182f7 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -36,6 +36,12 @@ TimeManagement Time; // Our global time management object void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { + // if we have no time, no need to initialize TM, except for the start time, + // which is used by movetime. + startTime = limits.startTime; + if (limits.time[us] == 0) + return; + TimePoint moveOverhead = TimePoint(Options["Move Overhead"]); TimePoint slowMover = TimePoint(Options["Slow Mover"]); TimePoint npmsec = TimePoint(Options["nodestime"]); @@ -59,8 +65,6 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { limits.npmsec = npmsec; } - startTime = limits.startTime; - // Maximum move horizon of 50 moves int mtg = limits.movestogo ? std::min(limits.movestogo, 50) : 50; From 7bd23d4d04d6644b6ccae8ea63cfc6646e4248dd Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Fri, 7 Apr 2023 12:33:28 -0400 Subject: [PATCH 1036/1766] Simplify away nnue scale pawn count multiplier Removes 2x multipliers in nnue scale calculation along with the pawn count term that was recently reintroduced. Passed non-regression STC: https://tests.stockfishchess.org/tests/view/64305bc720eb941419bdf72e LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 38008 W: 10234 L: 10021 D: 17753 Ptnml(0-2): 96, 4151, 10323, 4312, 122 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/6430b76a028b029b01ac9bfd LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 91232 W: 24686 L: 24547 D: 41999 Ptnml(0-2): 30, 8721, 27986, 8838, 41 closes https://github.com/official-stockfish/Stockfish/pull/4510 bench 4017320 --- src/evaluate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 2d0df89c3e7..873dc5d2069 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1063,7 +1063,7 @@ Value Eval::evaluate(const Position& pos, int* complexity) { else { int nnueComplexity; - int scale = 1001 + 5 * pos.count() + 61 * pos.non_pawn_material() / 4096; + int scale = 1001 + pos.non_pawn_material() / 64; Color stm = pos.side_to_move(); Value optimism = pos.this_thread()->optimism[stm]; From 4ad2713e19cbf8db1e588c4d24d1fe4af5c6e917 Mon Sep 17 00:00:00 2001 From: peregrineshahin Date: Fri, 17 Mar 2023 00:04:41 +0300 Subject: [PATCH 1037/1766] Fix capturing underpromotions issue Fix underpromotion captures are generated amongst quiets although dealt with as a capture_stage in search, this makes not skipping them when move count pruning kicks-in consistent with updating their histories amongst captures. Passed STC: https://tests.stockfishchess.org/tests/view/6415579f65775d3b539e7537 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 118896 W: 31678 L: 31553 D: 55665 Ptnml(0-2): 356, 12911, 32793, 13028, 360 Passed LTC: https://tests.stockfishchess.org/tests/view/641633b965775d3b539e9e95 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 126800 W: 34255 L: 34148 D: 58397 Ptnml(0-2): 57, 12216, 38763, 12291, 73 see also discussion in https://github.com/official-stockfish/Stockfish/pull/4436 closes https://github.com/official-stockfish/Stockfish/pull/4452 bench: 3979409 --- src/movegen.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index 255dce04c3c..6b28a52ecf0 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -25,13 +25,21 @@ namespace Stockfish { namespace { - template + template ExtMove* make_promotions(ExtMove* moveList, [[maybe_unused]] Square to) { if constexpr (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) + { *moveList++ = make(to - D, to, QUEEN); + if constexpr (Enemy && Type == CAPTURES) + { + *moveList++ = make(to - D, to, ROOK); + *moveList++ = make(to - D, to, BISHOP); + *moveList++ = make(to - D, to, KNIGHT); + } + } - if constexpr (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS) + if constexpr ((Type == QUIETS && !Enemy) || Type == EVASIONS || Type == NON_EVASIONS) { *moveList++ = make(to - D, to, ROOK); *moveList++ = make(to - D, to, BISHOP); @@ -106,13 +114,13 @@ namespace { b3 &= target; while (b1) - moveList = make_promotions(moveList, pop_lsb(b1)); + moveList = make_promotions(moveList, pop_lsb(b1)); while (b2) - moveList = make_promotions(moveList, pop_lsb(b2)); + moveList = make_promotions(moveList, pop_lsb(b2)); while (b3) - moveList = make_promotions(moveList, pop_lsb(b3)); + moveList = make_promotions(moveList, pop_lsb(b3)); } // Standard and en passant captures From f66c36277fe57d0ce4f10a4aeb5b41eb0cb9ebd1 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Wed, 5 Apr 2023 20:30:01 -0500 Subject: [PATCH 1038/1766] Remove nmpColor no benefit seen, neither in game play nor for zugzwang test positions STC: https://tests.stockfishchess.org/tests/view/642e293977ff3301150e9b55 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 161848 W: 43332 L: 43254 D: 75262 Ptnml(0-2): 418, 16987, 46058, 17021, 440 LTC: https://tests.stockfishchess.org/tests/view/642fea9420eb941419bde296 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 120208 W: 32529 L: 32418 D: 55261 Ptnml(0-2): 35, 11424, 37080, 11525, 40 closes https://github.com/official-stockfish/Stockfish/pull/4511 bench 3979409 --- src/search.cpp | 5 ++--- src/thread.h | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 4187117b7df..04299dbd887 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -802,7 +802,7 @@ namespace { && ss->staticEval >= beta - 20 * depth - improvement / 13 + 253 + complexity / 25 && !excludedMove && pos.non_pawn_material(us) - && (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor)) + && (ss->ply >= thisThread->nmpMinPly)) { assert(eval - beta >= 0); @@ -830,9 +830,8 @@ namespace { 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. + // until ply exceeds nmpMinPly. thisThread->nmpMinPly = ss->ply + 3 * (depth-R) / 4; - thisThread->nmpColor = us; Value v = search(pos, ss, beta-1, beta, depth-R, false); diff --git a/src/thread.h b/src/thread.h index d6a48eca73d..3a114c797a6 100644 --- a/src/thread.h +++ b/src/thread.h @@ -62,7 +62,6 @@ class Thread { size_t pvIdx, pvLast; std::atomic nodes, tbHits, bestMoveChanges; int selDepth, nmpMinPly; - Color nmpColor; Value bestValue, optimism[COLOR_NB]; Position rootPos; From 9829bceda90d025a5f5d7c04457902413e367041 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Tue, 11 Apr 2023 01:20:29 +0200 Subject: [PATCH 1039/1766] Remove good killer reduction rule. STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 246544 W: 65646 L: 65657 D: 115241 Ptnml(0-2): 706, 27350, 67138, 27405, 673 https://tests.stockfishchess.org/tests/view/642e253277ff3301150e9aa2 LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 165136 W: 44878 L: 44809 D: 75449 Ptnml(0-2): 64, 15991, 50378, 16082, 53 https://tests.stockfishchess.org/tests/view/6430db07028b029b01acd87f closes https://github.com/official-stockfish/Stockfish/pull/4519 Bench: 3746080 --- src/search.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 04299dbd887..411befdedfb 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1187,11 +1187,6 @@ namespace { if ((ss+1)->cutoffCnt > 3) r++; - // Decrease reduction if move is a killer and we have a good history (~1 Elo) - if (move == ss->killers[0] - && (*contHist[0])[movedPiece][to_sq(move)] >= 3722) - r--; - ss->statScore = 2 * thisThread->mainHistory[us][from_to(move)] + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] From acb0d204d56e16398c58822df2cc60b90ef1ae85 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Tue, 11 Apr 2023 19:53:36 +0300 Subject: [PATCH 1040/1766] Simplify stats assignment for Pv nodes This patch is a simplification of my recent elo gainer. Logically the Elo gainer didn't make much sense and this patch simplifies it into smth more logical. Instead of assigning negative bonuses to all non-first moves that enter PV nodes we assign positive bonuses in full depth search after LMR only for moves that will result in a fail high - thus not assigning positive bonuses for moves that will go to pv search - so doing "almost" the same as we do in master now for them. Logic differs for some other moves, though, but this removes some lines of code. Passed STC: https://tests.stockfishchess.org/tests/view/642cf5cf77ff3301150dc5ec LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 409320 W: 109124 L: 109308 D: 190888 Ptnml(0-2): 1149, 45385, 111751, 45251, 1124 Passed LTC: https://tests.stockfishchess.org/tests/view/642fe75d20eb941419bde200 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 260336 W: 70280 L: 70303 D: 119753 Ptnml(0-2): 99, 25236, 79528, 25199, 106 closes https://github.com/official-stockfish/Stockfish/pull/4522 Bench: 4286815 --- src/search.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 411befdedfb..390c6b1c72f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1229,8 +1229,9 @@ namespace { if (newDepth > d) value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode); - int bonus = value > alpha ? stat_bonus(newDepth) - : -stat_bonus(newDepth); + int bonus = value <= alpha ? -stat_bonus(newDepth) + : value >= beta ? stat_bonus(newDepth) + : 0; update_continuation_histories(ss, movedPiece, to_sq(move), bonus); } @@ -1255,9 +1256,6 @@ namespace { (ss+1)->pv[0] = MOVE_NONE; value = -search(pos, ss+1, -beta, -alpha, newDepth, false); - - if (moveCount > 1 && newDepth >= depth && !capture) - update_continuation_histories(ss, movedPiece, to_sq(move), -stat_bonus(newDepth)); } // Step 19. Undo move From 96b6c0b36f28f0ef5fa58f48405db710542521df Mon Sep 17 00:00:00 2001 From: MinetaS Date: Fri, 7 Apr 2023 14:49:05 +0000 Subject: [PATCH 1041/1766] Remove some conditions at PV improvement reduction Non-regression STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 63664 W: 17007 L: 16823 D: 29834 Ptnml(0-2): 163, 6998, 17336, 7162, 173 https://tests.stockfishchess.org/tests/view/6430b124028b029b01ac99f2 Non-regression LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 90016 W: 24399 L: 24258 D: 41359 Ptnml(0-2): 52, 8672, 27405, 8841, 38 https://tests.stockfishchess.org/tests/view/64310e74028b029b01ad3131 closes https://github.com/official-stockfish/Stockfish/pull/4526 Bench: 3661938 --- src/search.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 390c6b1c72f..ed81263a364 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1331,9 +1331,6 @@ namespace { { // Reduce other moves if we have found at least one score improvement (~1 Elo) if ( depth > 1 - && ( (improving && complexity > 971) - || value < (5 * alpha + 75 * beta) / 87 - || depth < 6) && beta < 12535 && value > -12535) depth -= 1; From f9d9c69bc33dc7a17c28cd586d7e67c1bfff66f6 Mon Sep 17 00:00:00 2001 From: Torom Date: Thu, 13 Apr 2023 21:55:13 +0200 Subject: [PATCH 1042/1766] Set the length of GIT_SHA to 8 characters Previously, the length of git commit hashes could vary depending on the git environment. closes https://github.com/official-stockfish/Stockfish/pull/4527 No functional change --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index abcf11b0a11..82664618bb7 100644 --- a/src/Makefile +++ b/src/Makefile @@ -702,7 +702,7 @@ ifeq ($(pext),yes) endif ### 3.7.1 Try to include git commit sha for versioning -GIT_SHA = $(shell git rev-parse --short HEAD 2>/dev/null) +GIT_SHA = $(shell git rev-parse HEAD 2>/dev/null | cut -c 1-8) ifneq ($(GIT_SHA), ) CXXFLAGS += -DGIT_SHA=$(GIT_SHA) endif From c90dd38903206ede56fa73c15d7d2b366d56ebdb Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Fri, 14 Apr 2023 20:48:22 +0800 Subject: [PATCH 1043/1766] Simplify away complexity in evaluation Simplification STC: https://tests.stockfishchess.org/tests/view/64394bc0605991a801b4f6f0 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 72360 W: 19313 L: 19138 D: 33909 Ptnml(0-2): 206, 7883, 19800, 8112, 179 Simplification LTC: https://tests.stockfishchess.org/tests/view/6439e788c233ce943b6bdac1 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 224992 W: 60665 L: 60654 D: 103673 Ptnml(0-2): 96, 21875, 68526, 21920, 79 closes https://github.com/official-stockfish/Stockfish/pull/4530 Bench: 3709369 --- src/evaluate.cpp | 10 +--------- src/evaluate.h | 2 +- src/search.cpp | 27 ++++++++------------------- src/thread.cpp | 1 - src/thread.h | 1 - 5 files changed, 10 insertions(+), 31 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 873dc5d2069..851ccfe11c9 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1046,7 +1046,7 @@ namespace { /// evaluate() is the evaluator for the outer world. It returns a static /// evaluation of the position from the point of view of the side to move. -Value Eval::evaluate(const Position& pos, int* complexity) { +Value Eval::evaluate(const Position& pos) { assert(!pos.checkers()); @@ -1075,10 +1075,6 @@ Value Eval::evaluate(const Position& pos, int* complexity) { + (424 + optimism) * abs(psq - nnue) ) / 1024; - // Return hybrid NNUE complexity to caller - if (complexity) - *complexity = nnueComplexity; - optimism = optimism * (272 + nnueComplexity) / 256; v = (nnue * scale + optimism * (scale - 748)) / 1024; } @@ -1089,10 +1085,6 @@ Value Eval::evaluate(const Position& pos, int* complexity) { // Guarantee evaluation does not hit the tablebase range v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); - // When not using NNUE, return classical complexity to caller - if (complexity && useClassical) - *complexity = abs(v - psq); - return v; } diff --git a/src/evaluate.h b/src/evaluate.h index 61846073300..48076670d2b 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -31,7 +31,7 @@ class Position; namespace Eval { std::string trace(Position& pos); - Value evaluate(const Position& pos, int* complexity = nullptr); + Value evaluate(const Position& pos); extern bool useNNUE; extern std::string currentEvalFileName; diff --git a/src/search.cpp b/src/search.cpp index ed81263a364..3a7f85a85e1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -294,14 +294,6 @@ void Thread::search() { if (mainThread) { - - if (!rootPos.checkers()) - { - int rootComplexity; - Eval::evaluate(rootPos, &rootComplexity); - mainThread->complexity = std::min(1.03 + (rootComplexity - 241) / 1552.0, 1.45); - } - if (mainThread->bestPreviousScore == VALUE_INFINITE) for (int i = 0; i < 4; ++i) mainThread->iterValue[i] = VALUE_ZERO; @@ -478,7 +470,7 @@ void Thread::search() { double reduction = (1.4 + mainThread->previousTimeReduction) / (2.08 * timeReduction); double bestMoveInstability = 1 + 1.8 * totBestMoveChanges / Threads.size(); - double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability * mainThread->complexity; + double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability; // Cap used time in case of a single legal move for a better viewer experience in tournaments // yielding correct scores and sufficiently fast moves. @@ -561,7 +553,7 @@ namespace { bool givesCheck, improving, priorCapture, singularQuietLMR; bool capture, moveCountPruning, ttCapture; Piece movedPiece; - int moveCount, captureCount, quietCount, improvement, complexity; + int moveCount, captureCount, quietCount, improvement; // Step 1. Initialize node Thread* thisThread = pos.this_thread(); @@ -723,7 +715,6 @@ namespace { ss->staticEval = eval = VALUE_NONE; improving = false; improvement = 0; - complexity = 0; goto moves_loop; } else if (excludedMove) @@ -731,17 +722,15 @@ namespace { // Providing the hint that this node's accumulator will be used often brings significant Elo gain (13 Elo) Eval::NNUE::hint_common_parent_position(pos); eval = ss->staticEval; - complexity = abs(ss->staticEval - pos.psq_eg_stm()); } else if (ss->ttHit) { // Never assume anything about values stored in TT ss->staticEval = eval = tte->eval(); if (eval == VALUE_NONE) - ss->staticEval = eval = evaluate(pos, &complexity); - else // Fall back to (semi)classical complexity for TT hits, the NNUE complexity is lost + ss->staticEval = eval = evaluate(pos); + else { - complexity = abs(ss->staticEval - pos.psq_eg_stm()); if (PvNode) Eval::NNUE::hint_common_parent_position(pos); } @@ -753,7 +742,7 @@ namespace { } else { - ss->staticEval = eval = evaluate(pos, &complexity); + ss->staticEval = eval = evaluate(pos); // Save static evaluation into transposition table tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); } @@ -799,15 +788,15 @@ namespace { && (ss-1)->statScore < 18755 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 20 * depth - improvement / 13 + 253 + complexity / 25 + && ss->staticEval >= beta - 20 * depth - improvement / 13 + 253 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly)) { assert(eval - beta >= 0); - // Null move dynamic reduction based on depth, eval and complexity of position - Depth R = std::min(int(eval - beta) / 172, 6) + depth / 3 + 4 - (complexity > 825); + // Null move dynamic reduction based on depth and eval + Depth R = std::min(int(eval - beta) / 172, 6) + depth / 3 + 4; ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; diff --git a/src/thread.cpp b/src/thread.cpp index 202768c863c..c680393e277 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -160,7 +160,6 @@ void ThreadPool::clear() { main()->bestPreviousScore = VALUE_INFINITE; main()->bestPreviousAverageScore = VALUE_INFINITE; main()->previousTimeReduction = 1.0; - main()->complexity = 1.0; } diff --git a/src/thread.h b/src/thread.h index 3a114c797a6..09bdb470b21 100644 --- a/src/thread.h +++ b/src/thread.h @@ -85,7 +85,6 @@ struct MainThread : public Thread { void search() override; void check_time(); - double complexity; double previousTimeReduction; Value bestPreviousScore; Value bestPreviousAverageScore; From 7b9b793fd544aa7a599b113a40533cde16de640b Mon Sep 17 00:00:00 2001 From: Guenther Demetz Date: Mon, 17 Apr 2023 11:38:26 +0200 Subject: [PATCH 1044/1766] Simplification of SEE verification logic Use same logic for all handled pieces. Don't prune the move if opponent King, Queen, Rook gets a discovered attack while or after the exchanges. remove an obsolete comment in position.cpp Passed STC non regression: https://tests.stockfishchess.org/tests/view/6437907594daa91835c290d0 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 107432 W: 28359 L: 28221 D: 50852 Ptnml(0-2): 298, 11724, 29524, 11882, 288 Passed LTC non-regression: https://tests.stockfishchess.org/tests/view/6438ed2ebd1a5470263c51e8 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 236288 W: 63656 L: 63656 D: 108976 Ptnml(0-2): 99, 22960, 72011, 22990, 84 closes https://github.com/official-stockfish/Stockfish/pull/4533 bench: 3741125 --- src/position.cpp | 3 +-- src/search.cpp | 8 +++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index e6fdb511fcc..2a9d798ff7d 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -331,8 +331,7 @@ void Position::set_check_info() const { /// Position::set_state() computes the hash keys of the position, and other /// data that once computed is updated incrementally as moves are made. -/// The function is only used when a new position is set up, and to verify -/// the correctness of the StateInfo data when running in debug mode. +/// The function is only used when a new position is set up void Position::set_state() const { diff --git a/src/search.cpp b/src/search.cpp index 3a7f85a85e1..5205fb5729b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1006,17 +1006,15 @@ namespace { { if (depth < 2 - capture) continue; - // Don't prune the move if opp. King/Queen/Rook is attacked by a slider after the exchanges. - // Since in see_ge we don't update occupied when the king recaptures, we also don't prune the - // move when the opp. King gets a discovered slider attack DURING the exchanges. - Bitboard leftEnemies = pos.pieces(~us, ROOK, QUEEN, KING) & occupied; + // Don't prune the move if opp. King/Queen/Rook gets a discovered attack during or after the exchanges + Bitboard leftEnemies = pos.pieces(~us, KING, QUEEN, ROOK); Bitboard attacks = 0; occupied |= to_sq(move); while (leftEnemies && !attacks) { Square sq = pop_lsb(leftEnemies); attacks = pos.attackers_to(sq, occupied) & pos.pieces(us) & occupied; - // Exclude Queen/Rook(s) which were already threatened before SEE + // Exclude Queen/Rook(s) which were already threatened before SEE (opp King can't be in check when it's our turn) if (attacks && sq != pos.square(~us) && (pos.attackers_to(sq, pos.pieces()) & pos.pieces(us))) attacks = 0; } From d64d4ac426e06cdd52249b0464d22f3cdb7fcf79 Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Fri, 14 Apr 2023 20:33:33 +0800 Subject: [PATCH 1045/1766] Simplify away depth condition for aspiration window adjust Simplification STC: https://tests.stockfishchess.org/tests/view/64351654596a20f264276ded LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 374664 W: 98942 L: 99089 D: 176633 Ptnml(0-2): 1049, 41767, 101878, 41558, 1080 Simplification LTC: https://tests.stockfishchess.org/tests/view/6439499f605991a801b4f684 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 457880 W: 123021 L: 123233 D: 211626 Ptnml(0-2): 166, 44739, 139335, 44541, 159 closes https://github.com/official-stockfish/Stockfish/pull/4534 Bench: 3879281 --- src/search.cpp | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5205fb5729b..e322a1c99aa 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -288,9 +288,7 @@ void Thread::search() { ss->pv = pv; - bestValue = delta = alpha = -VALUE_INFINITE; - beta = VALUE_INFINITE; - optimism[WHITE] = optimism[BLACK] = VALUE_ZERO; + bestValue = -VALUE_INFINITE; if (mainThread) { @@ -349,18 +347,15 @@ void Thread::search() { selDepth = 0; // Reset aspiration window starting size - if (rootDepth >= 4) - { - Value prev = rootMoves[pvIdx].averageScore; - delta = Value(10) + int(prev) * prev / 16502; - alpha = std::max(prev - delta,-VALUE_INFINITE); - beta = std::min(prev + delta, VALUE_INFINITE); - - // Adjust optimism based on root move's previousScore - int opt = 120 * prev / (std::abs(prev) + 161); - optimism[ us] = Value(opt); - optimism[~us] = -optimism[us]; - } + Value prev = rootMoves[pvIdx].averageScore; + delta = Value(10) + int(prev) * prev / 16502; + alpha = std::max(prev - delta,-VALUE_INFINITE); + beta = std::min(prev + delta, VALUE_INFINITE); + + // Adjust optimism based on root move's previousScore + int opt = 120 * prev / (std::abs(prev) + 161); + optimism[ us] = Value(opt); + optimism[~us] = -optimism[us]; // Start with a small aspiration window and, in the case of a fail // high/low, re-search with a bigger window until we don't fail From ba06c480a752458a8159db0c9110bd3b7e34145a Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Fri, 21 Apr 2023 16:22:55 +0200 Subject: [PATCH 1046/1766] Less reduction for tt move. This idea is a result of my second condition combination tuning for reductions: https://tests.stockfishchess.org/tests/view/643ed5573806eca398f06d61 There were used two parameters per combination: one for the 'sign' of the first and the second condition in a combination. Values >= 50 indicate using a condition directly and values <= -50 means use the negation of a condition. Each condition pair (X,Y) had two occurances dependent of the order of the two conditions: - if X < Y the parameters used for more reduction - if X > Y the parameters used for less reduction - if X = Y then only one condition is present and A[X][X][0]/A[X][X][1] stands for using more/less reduction for only this condition. The parameter pair A[7][2][0] (value = -94.70) and A[7][2][1] (value = 93.60) was one of the strongest signals with values near 100/-100. Here condition nr. 7 was '(ss+1)->cutoffCnt > 3' and condition nr. 2 'move == ttMove'. For condition nr. 7 the negation is used because A[7][2][0] is negative. This translates finally to less reduction (because 7 > 2) for tt moves if child cutoffs <= 3. STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 65728 W: 17704 L: 17358 D: 30666 Ptnml(0-2): 184, 7092, 18008, 7354, 226 https://tests.stockfishchess.org/tests/view/643ff767ef2529086a7ed042 LTC: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 139200 W: 37776 L: 37282 D: 64142 Ptnml(0-2): 58, 13241, 42509, 13733, 59 https://tests.stockfishchess.org/tests/view/6440bfa9ef2529086a7edbc7 closes https://github.com/official-stockfish/Stockfish/pull/4538 Bench: 3548023 --- src/search.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index e322a1c99aa..366065b8e9c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1169,6 +1169,9 @@ namespace { if ((ss+1)->cutoffCnt > 3) r++; + else if (move == ttMove) + r--; + ss->statScore = 2 * thisThread->mainHistory[us][from_to(move)] + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] From b22a1b10bbae2bb773afb50eba23dbf15e426365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bo=C5=A1tjan=20Mejak?= Date: Tue, 11 Apr 2023 13:55:14 +0200 Subject: [PATCH 1047/1766] Update AUTHORS Improved some comments in the AUTHORS file, sort contributors closes https://github.com/official-stockfish/Stockfish/pull/4520 No functional change --- AUTHORS | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/AUTHORS b/AUTHORS index 9b36111ea77..b6723246ada 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,17 +1,15 @@ -# List of authors for Stockfish - -# Founders of the Stockfish project and fishtest infrastructure +# Founders of the Stockfish project and Fishtest infrastructure Tord Romstad (romstad) Marco Costalba (mcostalba) Joona Kiiski (zamar) Gary Linscott (glinscott) -# Authors and inventors of NNUE, training, NNUE port +# Authors and inventors of NNUE, training, and NNUE port Yu Nasu (ynasu87) Motohiro Isozaki (yaneurao) Hisayori Noda (nodchip) -# all other authors of the code in alphabetical order +# All other authors of Stockfish code (in alphabetical order) Aditya (absimaldata) Adrian Petrescu (apetresc) Ajith Chandy Jose (ajithcj) @@ -47,12 +45,12 @@ Chess13234 Chris Cain (ceebo) clefrks Dale Weiler (graphitemaster) -Dan Schmidt (dfannius) Daniel Axtens (daxtens) Daniel Dugovic (ddugovic) +Dan Schmidt (dfannius) Dariusz Orzechowski (dorzechowski) -David Zar David (dav1312) +David Zar Daylen Yang (daylen) Deshawn Mohan-Smith (GoldenRare) Dieter Dobbelaere (ddobbelaere) @@ -66,7 +64,6 @@ Eelco de Groot (KingDefender) Elvin Liu (solarlight2) erbsenzaehler Ernesto Gatti -Linmiao Xu (linrock) Fabian Beuke (madnight) Fabian Fichter (ianfab) Fanael Linithien (Fanael) @@ -83,30 +80,30 @@ Gontran Lemaire (gonlem) Goodkov Vasiliy Aleksandrovich (goodkov) Gregor Cramer GuardianRM -Günther Demetz (pb00067, pb00068) Guy Vreuls (gvreuls) +Günther Demetz (pb00067, pb00068) Henri Wiechers Hiraoka Takuya (HiraokaTakuya) homoSapiensSapiens Hongzhi Cheng Ivan Ivec (IIvec) Jacques B. (Timshel) +Jake Senne (w1wwwwww) Jan Ondruš (hxim) Jared Kish (Kurtbusch, kurt22i) -Jake Senne (w1wwwwww) Jarrod Torriero (DU-jdto) -Jean Gauthier (OuaisBla) Jean-Francois Romang (jromang) +Jean Gauthier (OuaisBla) Jekaa Jerry Donald Watson (jerrydonaldwatson) jjoshua2 -Jonathan Calovski (Mysseno) Jonathan Buladas Dumale (SFisGOD) +Jonathan Calovski (Mysseno) Jonathan McDermid (jonathanmcdermid) Joost VandeVondele (vondele) -Jörg Oster (joergoster) Joseph Ellis (jhellis3) Joseph R. Prostko +Jörg Oster (joergoster) Julian Willemer (NightlyKing) jundery Justin Blanchard (UncombedCoconut) @@ -120,6 +117,7 @@ Krystian Kuzniarek (kuzkry) Leonardo Ljubičić (ICCF World Champion) Leonid Pechenik (lp--) Liam Keegan (lkeegan) +Linmiao Xu (linrock) Linus Arver (listx) loco-loco Lub van den Berg (ElbertoOne) @@ -151,11 +149,11 @@ Moez Jellouli (MJZ1977) Mohammed Li (tthsqe12) Muzhen J (XInTheDark) Nathan Rugg (nmrugg) -Nick Pelling (nickpelling) +Nguyen Pham (nguyenpham) Nicklas Persson (NicklasPersson) +Nick Pelling (nickpelling) Niklas Fiekas (niklasf) Nikolay Kostov (NikolayIT) -Nguyen Pham (nguyenpham) Norman Schmidt (FireFather) notruck Ofek Shochat (OfekShochat, ghostway) @@ -170,6 +168,7 @@ Peter Schneider (pschneider1968) Peter Zsifkovits (CoffeeOne) PikaCat Praveen Kumar Tummala (praveentml) +Prokop Randáček (ProkopRandacek) Rahul Dsilva (silversolver1) Ralph Stößer (Ralph Stoesser) Raminder Singh @@ -178,8 +177,8 @@ Reuven Peleg (R-Peleg) Richard Lloyd (Richard-Lloyd) Rodrigo Exterckötter Tjäder Rodrigo Roim (roim) -Ron Britvich (Britvich) Ronald de Man (syzygy1, syzygy) +Ron Britvich (Britvich) rqs Rui Coelho (ruicoelhopedro) Ryan Schmitt @@ -200,13 +199,12 @@ Stefano Di Martino (StefanoD) Steinar Gunderson (sesse) Stéphane Nicolet (snicolet) Syine Mineta (MinetaS) -Prokop Randáček (ProkopRandacek) Thanar2 thaspel theo77186 +Tomasz Sobczyk (Sopel97) Tom Truscott Tom Vijlbrief (tomtor) -Tomasz Sobczyk (Sopel97) Torsten Franz (torfranz, tfranzer) Torsten Hellwig (Torom) Tracey Emery (basepr1me) @@ -217,8 +215,7 @@ Vince Negri (cuddlestmonkey) xefoci7612 zz4032 - # Additionally, we acknowledge the authors and maintainers of fishtest, -# an amazing and essential framework for the development of Stockfish! +# an amazing and essential framework for Stockfish development! # # https://github.com/glinscott/fishtest/blob/master/AUTHORS From c3ce2204083400267592dc088b8ad9e88aed56b1 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Sat, 15 Apr 2023 09:51:01 -0400 Subject: [PATCH 1048/1766] Created by retraining the master net with these changes to the dataset: * Extending v6 filtering to data from T77 dec2021, T79 may2022, and T80 nov2022 * Reducing the number of duplicate positions, prioritizing position scores seen later in time * Using a binpack minimizer to reduce the overall data size Trained the same way as the previous master net, aside from the dataset changes: ``` python3 easy_train.py \ --experiment-name leela96-dfrc99-T60novdec-v2-T80augsep-v6-T80junjuloctnovT79aprmayT78jantosepT77dec-v6dd \ --training-dataset /data/leela96-dfrc99-T60novdec-v2-T80augsep-v6-T80junjuloctnovT79aprmayT78jantosepT77dec-v6dd.binpack \ --nnue-pytorch-branch linrock/nnue-pytorch/misc-fixes \ --start-from-engine-test-net True \ --early-fen-skipping 30 \ --start-lambda 1.0 \ --end-lambda 0.7 \ --max_epoch 900 \ --lr 4.375e-4 \ --gamma 0.995 \ --tui False \ --gpus "0," \ --seed $RANDOM ``` The new v6-dd filtering reduces duplicate positions by iterating over hourly data files within leela test runs, starting with the most recent, then keeping positions the first time they're seen and ignoring positions that are seen again. This ordering was done with the assumption that position scores seen later in time are generally more accurate than scores seen earlier in the test run. Positions are de-duplicated based on piece orientations, the first token in fen strings. The binpack minimizer was run with default settings after first merging monthly data into single binpacks. ``` python3 interleave_binpacks.py \ leela96-filt-v2.binpack \ dfrc99-filt-v2.binpack \ T60-nov2021-12tb7p-eval-filt-v2.binpack \ T60-dec2021-12tb7p-eval-filt-v2.binpack \ filt-v6/test80-aug2022-16tb7p-filter-v6.min-mar2023.binpack \ filt-v6/test80-sep2022-16tb7p-filter-v6.min-mar2023.binpack \ filt-v6-dd/test80-jun2022-16tb7p-filter-v6-dd.min-mar2023.binpack \ filt-v6-dd/test80-jul2022-16tb7p-filter-v6-dd.binpack \ filt-v6-dd/test80-oct2022-16tb7p-filter-v6-dd.binpack \ filt-v6-dd/test80-nov2022-16tb7p-filter-v6-dd.binpack \ filt-v6-dd/test79-apr2022-16tb7p-filter-v6-dd.binpack \ filt-v6-dd/test79-may2022-16tb7p-filter-v6-dd.binpack \ filt-v6-dd/test78-jantomay2022-16tb7p-filter-v6-dd.min-mar2023.binpack \ filt-v6-dd/test78-juntosep2022-16tb7p-filter-v6-dd.binpack \ filt-v6-dd/test77-dec2021-16tb7p-filter-v6-dd.binpack \ /data/leela96-dfrc99-T60novdec-v2-T80augsep-v6-T80junjuloctnovT79aprmayT78jantosepT77dec-v6dd.binpack ``` The code for v6-dd filtering is available along with training data preparation scripts at: https://github.com/linrock/nnue-data Links for downloading the training data components: https://robotmoon.com/nnue-training-data/ The binpack minimizer is from: #4447 Local elo at 25k nodes per move: nn-epoch859.nnue : 1.2 +/- 2.6 Passed STC: https://tests.stockfishchess.org/tests/view/643aad7db08900ff1bc5a832 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 565040 W: 150225 L: 149162 D: 265653 Ptnml(0-2): 1875, 62137, 153229, 63608, 1671 Passed LTC: https://tests.stockfishchess.org/tests/view/643ecf2fa43cf30e719d2042 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 1014840 W: 274645 L: 272456 D: 467739 Ptnml(0-2): 515, 98565, 306970, 100956, 414 closes https://github.com/official-stockfish/Stockfish/pull/4545 bench 3476305 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 48076670d2b..9dc45371e42 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-dabb1ed23026.nnue" + #define EvalFileDefaultName "nn-1ceb1a57d117.nnue" namespace NNUE { From 41f50b2c83a0ba36a2b9c507c1783e57c9b13485 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Fri, 21 Apr 2023 09:37:29 -0400 Subject: [PATCH 1049/1766] Update default net to nn-e1fb1ade4432.nnue Created by retraining nn-dabb1ed23026.nnue with a dataset composed of: * The previous best dataset (nn-1ceb1a57d117.nnue dataset) * Adding de-duplicated T80 data from feb2023 and the last 10 days of jan2023, filtered with v6-dd Initially trained with the same options as the recent master net (nn-1ceb1a57d117.nnue). Around epoch 890, training was manually stopped and max epoch increased to 1000. ``` python3 easy_train.py \ --experiment-name leela96-dfrc99-T60novdec-v2-T80augsep-v6-T80junjuloctnovjanfebT79aprmayT78jantosepT77dec-v6dd \ --training-dataset /data/leela96-dfrc99-T60novdec-v2-T80augsep-v6-T80junjuloctnovjanfebT79aprmayT78jantosepT77dec-v6dd.binpack \ --nnue-pytorch-branch linrock/nnue-pytorch/misc-fixes \ --start-from-engine-test-net True \ --early-fen-skipping 30 \ --start-lambda 1.0 \ --end-lambda 0.7 \ --max_epoch 900 \ --lr 4.375e-4 \ --gamma 0.995 \ --tui False \ --gpus "0," \ --seed $RANDOM ``` The same v6-dd filtering and binpack minimizer was used for preparing the recent nn-1ceb1a57d117.nnue dataset. ``` python3 interleave_binpacks.py \ leela96-filt-v2.binpack \ dfrc99-filt-v2.binpack \ T60-nov2021-12tb7p-eval-filt-v2.binpack \ T60-dec2021-12tb7p-eval-filt-v2.binpack \ filt-v6/test80-aug2022-16tb7p-filter-v6.min-mar2023.binpack \ filt-v6/test80-sep2022-16tb7p-filter-v6.min-mar2023.binpack \ filt-v6-dd/test80-jun2022-16tb7p-filter-v6-dd.min-mar2023.binpack \ filt-v6-dd/test80-jul2022-16tb7p-filter-v6-dd.binpack \ filt-v6-dd/test80-oct2022-16tb7p-filter-v6-dd.binpack \ filt-v6-dd/test80-nov2022-16tb7p-filter-v6-dd.binpack \ filt-v6-dd/test80-jan2022-3of3-16tb7p-filter-v6-dd.min-mar2023.binpack \ filt-v6-dd/test80-feb2023-16tb7p-filter-v6-dd.min-mar2023.binpack \ filt-v6-dd/test79-apr2022-16tb7p-filter-v6-dd.binpack \ filt-v6-dd/test79-may2022-16tb7p-filter-v6-dd.binpack \ filt-v6-dd/test78-jantomay2022-16tb7p-filter-v6-dd.min-mar2023.binpack \ filt-v6-dd/test78-juntosep2022-16tb7p-filter-v6-dd.binpack \ filt-v6-dd/test77-dec2021-16tb7p-filter-v6-dd.binpack \ /data/leela96-dfrc99-T60novdec-v2-T80augsep-v6-T80junjuloctnovjanfebT79aprmayT78jantosepT77dec-v6dd.binpack ``` Links for downloading the training data components can be found at: https://robotmoon.com/nnue-training-data/ Local elo at 25k nodes per move: nn-epoch919.nnue : 2.6 +/- 2.8 Passed STC vs. nn-dabb1ed23026.nnue https://tests.stockfishchess.org/tests/view/644420df94ff3db5625f2af5 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 125960 W: 33898 L: 33464 D: 58598 Ptnml(0-2): 351, 13920, 34021, 14320, 368 Passed LTC vs. nn-1ceb1a57d117.nnue https://tests.stockfishchess.org/tests/view/64469f128d30316529b3dc46 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 24544 W: 6817 L: 6542 D: 11185 Ptnml(0-2): 8, 2252, 7488, 2505, 19 closes https://github.com/official-stockfish/Stockfish/pull/4546 bench 3714847 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 9dc45371e42..f5db1c1e622 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-1ceb1a57d117.nnue" + #define EvalFileDefaultName "nn-e1fb1ade4432.nnue" namespace NNUE { From 21d6b69f7c8d0c0a71fe627714913a59d39a3b57 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Sat, 29 Apr 2023 15:45:22 -0400 Subject: [PATCH 1050/1766] Update 7 eval and optimism params Params found using spsa at 30+0.3 with this tuning config: ``` // evaluate.cpp int nnueOptScaleBase = 1001; int nnueComplexityMult = 406; int nnueComplexityOptOffset = 424; int evalOptComplexityOffset = 272; int evalOptScaleOffset = 748; TUNE(SetRange(801, 1201), nnueOptScaleBase); TUNE(SetRange(306, 506), nnueComplexityMult); TUNE(SetRange(324, 524), nnueComplexityOptOffset); TUNE(SetRange(172, 372), evalOptComplexityOffset); TUNE(SetRange(648, 848), evalOptScaleOffset); // search.cpp int searchOptBase = 120; int searchOptDenom = 161; TUNE(SetRange(20, 220), searchOptBase); TUNE(SetRange(111, 211), searchOptDenom); ``` Passed STC: https://tests.stockfishchess.org/tests/view/644dda8accf5e93df5e50cbe LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 136800 W: 36682 L: 36237 D: 63881 Ptnml(0-2): 353, 14910, 37492, 15229, 416 Passed LTC: https://tests.stockfishchess.org/tests/view/644eaedb3f31c3bbe4a3d345 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 64548 W: 17624 L: 17272 D: 29652 Ptnml(0-2): 33, 6112, 19631, 6466, 32 closes https://github.com/official-stockfish/Stockfish/pull/4550 bench 3670343 --- src/evaluate.cpp | 10 +++++----- src/search.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 851ccfe11c9..d8f4e2e194f 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1063,7 +1063,7 @@ Value Eval::evaluate(const Position& pos) { else { int nnueComplexity; - int scale = 1001 + pos.non_pawn_material() / 64; + int scale = 967 + pos.non_pawn_material() / 64; Color stm = pos.side_to_move(); Value optimism = pos.this_thread()->optimism[stm]; @@ -1071,12 +1071,12 @@ Value Eval::evaluate(const Position& pos) { Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); // Blend nnue complexity with (semi)classical complexity - nnueComplexity = ( 406 * nnueComplexity - + (424 + optimism) * abs(psq - nnue) + nnueComplexity = ( 402 * nnueComplexity + + (454 + optimism) * abs(psq - nnue) ) / 1024; - optimism = optimism * (272 + nnueComplexity) / 256; - v = (nnue * scale + optimism * (scale - 748)) / 1024; + optimism = optimism * (274 + nnueComplexity) / 256; + v = (nnue * scale + optimism * (scale - 791)) / 1024; } // Damp down the evaluation linearly when shuffling diff --git a/src/search.cpp b/src/search.cpp index 366065b8e9c..8ce9c56e42d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -353,7 +353,7 @@ void Thread::search() { beta = std::min(prev + delta, VALUE_INFINITE); // Adjust optimism based on root move's previousScore - int opt = 120 * prev / (std::abs(prev) + 161); + int opt = 102 * prev / (std::abs(prev) + 147); optimism[ us] = Value(opt); optimism[~us] = -optimism[us]; From 72d542f00026fd8437e6033c95802714e4cd45d1 Mon Sep 17 00:00:00 2001 From: candirufish <38038147+candirufish@users.noreply.github.com> Date: Fri, 28 Apr 2023 19:05:56 +0200 Subject: [PATCH 1051/1766] Adjust reductions Decrease further on cutNodes with tte->depth() >= depth + 3 condition. LTC: https://tests.stockfishchess.org/tests/view/644dc84bccf5e93df5e50c13 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 155346 W: 42184 L: 41660 D: 71502 Ptnml(0-2): 59, 14765, 47504, 15283, 62 STC: https://tests.stockfishchess.org/tests/view/644d05de68e01d8194cd9bbb LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 121888 W: 32868 L: 32444 D: 56576 Ptnml(0-2): 332, 13273, 33343, 13631, 365 closes https://github.com/official-stockfish/Stockfish/pull/4552 bench: 3739675 --- src/search.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 8ce9c56e42d..a6618c5b815 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -884,7 +884,7 @@ namespace { // Use qsearch if depth is equal or below zero (~9 Elo) if ( PvNode && !ttMove) - depth -= 2 + 2 * (ss->ttHit && tte->depth() >= depth); + depth -= 2 + 2 * (ss->ttHit && tte->depth() >= depth); if (depth <= 0) return qsearch(pos, ss, alpha, beta); @@ -1141,9 +1141,10 @@ namespace { // Decrease reduction if position is or has been on the PV // and node is not likely to fail low. (~3 Elo) + // Decrease further on cutNodes. (~1 Elo) if ( ss->ttPv && !likelyFailLow) - r -= 2; + r -= cutNode && tte->depth() >= depth + 3 ? 3 : 2; // Decrease reduction if opponent's move count is high (~1 Elo) if ((ss-1)->moveCount > 7) From 2429e162890478a48ab9b1cf0c431de9ebaf9429 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Fri, 5 May 2023 07:54:12 +0300 Subject: [PATCH 1052/1766] Reduce more if current node has a lot of refuted moves. This patch refines idea of cutoff count - in master we reduce more if current node has at least 4 moves that are refuted by search, this patch increases this count by 1 if refutation happened without having a tt move. Passed STC: https://tests.stockfishchess.org/tests/view/645363c36206ee34ebf8191d LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 67616 W: 18220 L: 17874 D: 31522 Ptnml(0-2): 142, 7346, 18504, 7656, 160 Passed LTC: https://tests.stockfishchess.org/tests/view/6453a0ea6206ee34ebf82796 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 195228 W: 52741 L: 52140 D: 90347 Ptnml(0-2): 53, 18718, 59482, 19297, 64 closes https://github.com/official-stockfish/Stockfish/pull/4556 bench 3448916 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index a6618c5b815..a1f916d0515 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1326,7 +1326,7 @@ namespace { } else { - ss->cutoffCnt++; + ss->cutoffCnt += 1 + !ttMove; assert(value >= beta); // Fail high break; } From 28442195c7d168a87221c6f1ae9ac51893427250 Mon Sep 17 00:00:00 2001 From: peregrineshahin Date: Wed, 3 May 2023 14:11:55 +0300 Subject: [PATCH 1053/1766] Clean up after "Simplify away complexity in evaluation" closes https://github.com/official-stockfish/Stockfish/pull/4555 No functional change. --- src/search.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index a1f916d0515..8d9dc04fc48 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -724,11 +724,8 @@ namespace { ss->staticEval = eval = tte->eval(); if (eval == VALUE_NONE) ss->staticEval = eval = evaluate(pos); - else - { - if (PvNode) - Eval::NNUE::hint_common_parent_position(pos); - } + else if (PvNode) + Eval::NNUE::hint_common_parent_position(pos); // ttValue can be used as a better position evaluation (~7 Elo) if ( ttValue != VALUE_NONE From 464ebdf127273db0ccb0084c881b255b880e922e Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Fri, 5 May 2023 23:32:56 +0300 Subject: [PATCH 1054/1766] Small cleanup In search remove one condition check and reorder conditions. Removes some code. Passed non-regression test: https://tests.stockfishchess.org/tests/view/64548fa06206ee34ebf853ad LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 282976 W: 75327 L: 75374 D: 132275 Ptnml(0-2): 604, 29673, 80995, 29598, 618 closes https://github.com/official-stockfish/Stockfish/pull/4557 No functional change --- src/search.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 8d9dc04fc48..45fc1a7e2aa 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1310,7 +1310,13 @@ namespace { if (PvNode && !rootNode) // Update pv even in fail-high case update_pv(ss->pv, move, (ss+1)->pv); - if (PvNode && value < beta) // Update alpha! Always alpha < beta + if (value >= beta) + { + ss->cutoffCnt += 1 + !ttMove; + assert(value >= beta); // Fail high + break; + } + else { // Reduce other moves if we have found at least one score improvement (~1 Elo) if ( depth > 1 @@ -1319,13 +1325,7 @@ namespace { depth -= 1; assert(depth > 0); - alpha = value; - } - else - { - ss->cutoffCnt += 1 + !ttMove; - assert(value >= beta); // Fail high - break; + alpha = value; // Update alpha! Always alpha < beta } } } From 65e2150501b87e6ce00fae4e3f056444f39462fd Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 7 May 2023 23:33:04 +0300 Subject: [PATCH 1055/1766] Refine deeper post-lmr searches This patch improves logic conditions for performing deeper searches after passed LMR. Instead of exceeding alpha by some margin now it requires to exceed the current best value - which may be lower than alpha (but never bigger since we update alpha with bestvalue if it exceeds alpha). Passed STC: https://tests.stockfishchess.org/tests/view/6455f78008858de8313775b6 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 209344 W: 55993 L: 55448 D: 97903 Ptnml(0-2): 507, 22798, 57526, 23325, 516 Passed LTC: https://tests.stockfishchess.org/tests/view/64572d46eb75932ccfebff97 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 66288 W: 17867 L: 17514 D: 30907 Ptnml(0-2): 21, 6240, 20269, 6593, 21 closes https://github.com/official-stockfish/Stockfish/pull/4559 bench 3808503 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 45fc1a7e2aa..bc495c0e5de 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1201,7 +1201,7 @@ namespace { { // Adjust full depth search based on LMR results - if result // was good enough search deeper, if it was bad enough search shallower - const bool doDeeperSearch = value > (alpha + 58 + 12 * (newDepth - d)); + const bool doDeeperSearch = value > (bestValue + 68 + 12 * (newDepth - d)); const bool doEvenDeeperSearch = value > alpha + 588 && ss->doubleExtensions <= 5; const bool doShallowerSearch = value < bestValue + newDepth; From 5f7b26aaa0d0bbdeb50ea6b17f049f167c1eb996 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 19 May 2023 23:02:27 +0200 Subject: [PATCH 1056/1766] Update WLD model using data of May, recalibrate the WLD model. closes https://github.com/official-stockfish/Stockfish/pull/4577 No functional change --- src/uci.cpp | 4 ++-- src/uci.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/uci.cpp b/src/uci.cpp index 8f9684ee265..523d551e0c0 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -207,8 +207,8 @@ namespace { // The coefficients of a third-order polynomial fit is based on the fishtest data // for two parameters that need to transform eval to the argument of a logistic // function. - constexpr double as[] = { 0.33677609, -4.30175627, 33.08810557, 365.60223431}; - constexpr double bs[] = { -2.50471102, 14.23235405, -14.33066859, 71.42705250 }; + constexpr double as[] = { 1.07390458, -6.94334517, 31.95090161, 317.75424048}; + constexpr double bs[] = { -2.82843814, 16.64518180, -19.74439200, 68.39499088 }; // Enforce that NormalizeToPawnValue corresponds to a 50% win rate at ply 64 static_assert(UCI::NormalizeToPawnValue == int(as[0] + as[1] + as[2] + as[3])); diff --git a/src/uci.h b/src/uci.h index 9ca0ed36b4d..680d2d2cc8c 100644 --- a/src/uci.h +++ b/src/uci.h @@ -35,7 +35,7 @@ namespace UCI { // the win_rate_model() such that Stockfish outputs an advantage of // "100 centipawns" for a position if the engine has a 50% probability to win // from this position in selfplay at fishtest LTC time control. -const int NormalizeToPawnValue = 394; +const int NormalizeToPawnValue = 343; class Option; From f030a1c592eabfa602ff8560d365e516298363ce Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Tue, 16 May 2023 21:30:53 +0800 Subject: [PATCH 1057/1766] Search tuning at very long time control Many search parameter changes, tuned (https://tests.stockfishchess.org/tests/view/645e4c67d55cccb2e64220ff) at ~300k games @ VLTC (120+1.2). Failed STC: https://tests.stockfishchess.org/tests/view/6465fcd77968ca827c1410c2 LLR: -2.95 (-2.94,2.94) <0.00,2.00> Total: 33824 W: 8863 L: 9067 D: 15894 Ptnml(0-2): 89, 3833, 9266, 3641, 83 Neutral LTC: https://tests.stockfishchess.org/tests/view/646385ce87f6567dd4df4e37 Elo: -0.48 +-1.2 (95%) LOS: 22.2% Total: 60000 W: 16235 L: 16318 D: 27447 Ptnml(0-2): 27, 5831, 18366, 5750, 26 nElo: -1.08 +-2.8 (95%) PairsRatio: 0.99 Passed VLTC 180+1.8: https://tests.stockfishchess.org/tests/view/646385f787f6567dd4df4e3e LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 126448 W: 34704 L: 34258 D: 57486 Ptnml(0-2): 9, 10970, 40825, 11406, 14 Passed VLTC SMP 60+0.6 8thread: https://tests.stockfishchess.org/tests/view/646628de884ce93b65df2ac9 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 59456 W: 16791 L: 16487 D: 26178 Ptnml(0-2): 5, 4473, 20467, 4779, 4 closes https://github.com/official-stockfish/Stockfish/pull/4574 Bench: 3347573 --- src/search.cpp | 94 +++++++++++++++++++++++++------------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index bc495c0e5de..429db9a5b28 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -64,7 +64,7 @@ namespace { // Futility margin Value futility_margin(Depth d, bool improving) { - return Value(154 * (d - improving)); + return Value(148 * (d - improving)); } // Reductions lookup table, initialized at startup @@ -72,7 +72,7 @@ namespace { Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) { int r = Reductions[d] * Reductions[mn]; - return (r + 1449 - int(delta) * 937 / int(rootDelta)) / 1024 + (!i && r > 941); + return (r + 1356 - int(delta) * 983 / int(rootDelta)) / 1024 + (!i && r > 901); } constexpr int futility_move_count(bool improving, Depth depth) { @@ -82,7 +82,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return std::min(341 * d - 470, 1710); + return std::min(337 * d - 497, 1632); } // Add a small random component to draw evaluations to avoid 3-fold blindness @@ -162,7 +162,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int((19.47 + std::log(Threads.size()) / 2) * std::log(i)); + Reductions[i] = int((20.89 + std::log(Threads.size()) / 2) * std::log(i)); } @@ -348,12 +348,12 @@ void Thread::search() { // Reset aspiration window starting size Value prev = rootMoves[pvIdx].averageScore; - delta = Value(10) + int(prev) * prev / 16502; + delta = Value(11) + int(prev) * prev / 15368; alpha = std::max(prev - delta,-VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE); // Adjust optimism based on root move's previousScore - int opt = 102 * prev / (std::abs(prev) + 147); + int opt = 116 * prev / (std::abs(prev) + 143); optimism[ us] = Value(opt); optimism[~us] = -optimism[us]; @@ -742,7 +742,7 @@ namespace { // Use static evaluation difference to improve quiet move ordering (~4 Elo) if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture) { - int bonus = std::clamp(-19 * int((ss-1)->staticEval + ss->staticEval), -1920, 1920); + int bonus = std::clamp(-19 * int((ss-1)->staticEval + ss->staticEval), -1717, 1717); thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus; } @@ -752,13 +752,13 @@ namespace { // margin and the improving flag are used in various pruning heuristics. improvement = (ss-2)->staticEval != VALUE_NONE ? ss->staticEval - (ss-2)->staticEval : (ss-4)->staticEval != VALUE_NONE ? ss->staticEval - (ss-4)->staticEval - : 156; + : 163; improving = improvement > 0; // Step 7. Razoring (~1 Elo). // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. - if (eval < alpha - 426 - 256 * depth * depth) + if (eval < alpha - 467 - 266 * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -769,18 +769,18 @@ namespace { // The depth condition is important for mate finding. if ( !ss->ttPv && depth < 9 - && eval - futility_margin(depth, improving) - (ss-1)->statScore / 280 >= beta + && eval - futility_margin(depth, improving) - (ss-1)->statScore / 306 >= beta && eval >= beta - && eval < 25128) // larger than VALUE_KNOWN_WIN, but smaller than TB wins + && eval < 22761) // larger than VALUE_KNOWN_WIN, but smaller than TB wins return eval; // Step 9. Null move search with verification search (~35 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 18755 + && (ss-1)->statScore < 18404 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 20 * depth - improvement / 13 + 253 + && ss->staticEval >= beta - 19 * depth - improvement / 13 + 257 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly)) @@ -823,13 +823,13 @@ namespace { } } - probCutBeta = beta + 186 - 54 * improving; + probCutBeta = beta + 174 - 60 * improving; // Step 10. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search returns a value // much above beta, we can (almost) safely prune the previous move. if ( !PvNode - && depth > 4 + && depth > 3 && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY // if value from transposition table is lower than probCutBeta, don't attempt probCut // there and in further interactions with transposition table cutoff depth is set to depth - 3 @@ -887,20 +887,20 @@ namespace { return qsearch(pos, ss, alpha, beta); if ( cutNode - && depth >= 7 + && depth >= 8 && !ttMove) depth -= 2; moves_loop: // When in check, search starts here // Step 12. A small Probcut idea, when we are in check (~4 Elo) - probCutBeta = beta + 391; + probCutBeta = beta + 430; if ( ss->inCheck && !PvNode && depth >= 2 && ttCapture && (tte->bound() & BOUND_LOWER) - && tte->depth() >= depth - 3 + && tte->depth() >= depth - 4 && ttValue >= probCutBeta && abs(ttValue) <= VALUE_KNOWN_WIN && abs(beta) <= VALUE_KNOWN_WIN) @@ -986,15 +986,15 @@ namespace { { // Futility pruning for captures (~2 Elo) if ( !givesCheck - && lmrDepth < 6 + && lmrDepth < 7 && !ss->inCheck - && ss->staticEval + 182 + 230 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] - + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 7 < alpha) + && ss->staticEval + 207 + 223 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] + + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] * 1078 / 7000 < alpha) continue; Bitboard occupied; // SEE based pruning (~11 Elo) - if (!pos.see_ge(move, occupied, Value(-206) * depth)) + if (!pos.see_ge(move, occupied, Value(-205) * depth)) { if (depth < 2 - capture) continue; @@ -1022,24 +1022,24 @@ namespace { // Continuation history based pruning (~2 Elo) if ( lmrDepth < 5 - && history < -4405 * (depth - 1)) + && history < -3792 * (depth - 1)) continue; history += 2 * thisThread->mainHistory[us][from_to(move)]; - lmrDepth += history / 7278; + lmrDepth += history / 7019; lmrDepth = std::max(lmrDepth, -2); // Futility pruning: parent node (~13 Elo) if ( !ss->inCheck - && lmrDepth < 13 - && ss->staticEval + 103 + 138 * lmrDepth <= alpha) + && lmrDepth < 12 + && ss->staticEval + 111 + 136 * lmrDepth <= alpha) continue; lmrDepth = std::max(lmrDepth, 0); // Prune moves with negative SEE (~4 Elo) - if (!pos.see_ge(move, Value(-24 * lmrDepth * lmrDepth - 16 * lmrDepth))) + if (!pos.see_ge(move, Value(-27 * lmrDepth * lmrDepth - 33 * lmrDepth / 2))) continue; } } @@ -1054,7 +1054,7 @@ namespace { // 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 ( !rootNode - && depth >= 4 - (thisThread->completedDepth > 21) + 2 * (PvNode && tte->is_pv()) + && depth >= 4 - (thisThread->completedDepth > 22) + 2 * (PvNode && tte->is_pv()) && move == ttMove && !excludedMove // Avoid recursive singular search /* && ttValue != VALUE_NONE Already implicit in the next condition */ @@ -1062,7 +1062,7 @@ namespace { && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - (3 + 2 * (ss->ttPv && !PvNode)) * depth / 2; + Value singularBeta = ttValue - (99 + 65 * (ss->ttPv && !PvNode)) * depth / 64; Depth singularDepth = (depth - 1) / 2; ss->excludedMove = move; @@ -1076,8 +1076,8 @@ namespace { // Avoid search explosion by limiting the number of double extensions if ( !PvNode - && value < singularBeta - 25 - && ss->doubleExtensions <= 10) + && value < singularBeta - 22 + && ss->doubleExtensions <= 11) { extension = 2; depth += depth < 13; @@ -1107,15 +1107,15 @@ namespace { // Check extensions (~1 Elo) else if ( givesCheck - && depth > 10 - && abs(ss->staticEval) > 88) + && depth > 9 + && abs(ss->staticEval) > 87) extension = 1; // Quiet ttMove extensions (~1 Elo) else if ( PvNode && move == ttMove && move == ss->killers[0] - && (*contHist[0])[movedPiece][to_sq(move)] >= 5705) + && (*contHist[0])[movedPiece][to_sq(move)] >= 5480) extension = 1; } @@ -1144,7 +1144,7 @@ namespace { r -= cutNode && tte->depth() >= depth + 3 ? 3 : 2; // Decrease reduction if opponent's move count is high (~1 Elo) - if ((ss-1)->moveCount > 7) + if ((ss-1)->moveCount > 8) r--; // Increase reduction for cut nodes (~3 Elo) @@ -1157,7 +1157,7 @@ namespace { // Decrease reduction for PvNodes based on depth (~2 Elo) if (PvNode) - r -= 1 + 12 / (3 + depth); + r -= 1 + 11 / (3 + depth); // Decrease reduction if ttMove has been singularly extended (~1 Elo) if (singularQuietLMR) @@ -1174,10 +1174,10 @@ namespace { + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 4082; + - 3755; // Decrease/increase reduction for moves with a good/bad history (~25 Elo) - r -= ss->statScore / (11079 + 4626 * (depth > 6 && depth < 19)); + r -= ss->statScore / (10445 + 4762 * (depth > 6 && depth < 21)); // Step 17. Late moves reduction / extension (LMR, ~117 Elo) // We use various heuristics for the sons of a node after the first son has @@ -1201,8 +1201,8 @@ namespace { { // Adjust full depth search based on LMR results - if result // was good enough search deeper, if it was bad enough search shallower - const bool doDeeperSearch = value > (bestValue + 68 + 12 * (newDepth - d)); - const bool doEvenDeeperSearch = value > alpha + 588 && ss->doubleExtensions <= 5; + const bool doDeeperSearch = value > (bestValue + 63 + 11 * (newDepth - d)); + const bool doEvenDeeperSearch = value > alpha + 662 && ss->doubleExtensions <= 6; const bool doShallowerSearch = value < bestValue + newDepth; ss->doubleExtensions = ss->doubleExtensions + doEvenDeeperSearch; @@ -1227,7 +1227,7 @@ namespace { if (!ttMove && cutNode) r += 2; - value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth - (r > 4), !cutNode); + value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth - (r > 3), !cutNode); } // For PV nodes only, do a full PV search on the first move or after a fail @@ -1320,8 +1320,8 @@ namespace { { // Reduce other moves if we have found at least one score improvement (~1 Elo) if ( depth > 1 - && beta < 12535 - && value > -12535) + && beta < 14001 + && value > -12754) depth -= 1; assert(depth > 0); @@ -1370,7 +1370,7 @@ namespace { // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (depth > 5) + (PvNode || cutNode) + (bestValue < alpha - 97 * depth) + ((ss-1)->moveCount > 10); + int bonus = (depth > 5) + (PvNode || cutNode) + (bestValue < alpha - 100 * depth) + ((ss-1)->moveCount > 11); update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); } @@ -1499,7 +1499,7 @@ namespace { if (PvNode && bestValue > alpha) alpha = bestValue; - futilityBase = bestValue + 168; + futilityBase = bestValue + 190; } const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, @@ -1572,7 +1572,7 @@ namespace { continue; // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, Value(-110))) + if (!pos.see_ge(move, Value(-94))) continue; } @@ -1705,7 +1705,7 @@ namespace { if (!pos.capture_stage(bestMove)) { - int bonus2 = bestValue > beta + 153 ? bonus1 // larger bonus + int bonus2 = bestValue > beta + 143 ? bonus1 // larger bonus : stat_bonus(depth); // smaller bonus // Increase stats for the best move in case it was a quiet move From 4f24ee086828e28df7d0b2dce5c13732139e7c19 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Fri, 19 May 2023 23:16:48 +0200 Subject: [PATCH 1058/1766] Small simplification in history pruning. Remove the constant term of the history threshold which lowers the chance that pruning occurs. As compensation allow pruning at a slightly higher depth. Passed STC: https://tests.stockfishchess.org/tests/view/64634c9a87f6567dd4df4901 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 101536 W: 27156 L: 27012 D: 47368 Ptnml(0-2): 266, 11165, 27772, 11289, 276 Passed LTC: https://tests.stockfishchess.org/tests/view/6463d68b17982fde89d2bc2b LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 32154 W: 8741 L: 8543 D: 14870 Ptnml(0-2): 8, 3093, 9687, 3271, 18 Passed LTC: retest on top of VLTC tuning PR 4571 because this changes the history depth factor (use this new factor here) https://tests.stockfishchess.org/tests/view/6467300e165c4b29ec0afd3f LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 99270 W: 26840 L: 26707 D: 45723 Ptnml(0-2): 36, 9753, 29928, 9878, 40 closes https://github.com/official-stockfish/Stockfish/pull/4578 Bench: 2984341 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 429db9a5b28..10844746ccc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1021,8 +1021,8 @@ namespace { + (*contHist[3])[movedPiece][to_sq(move)]; // Continuation history based pruning (~2 Elo) - if ( lmrDepth < 5 - && history < -3792 * (depth - 1)) + if ( lmrDepth < 6 + && history < -3792 * depth) continue; history += 2 * thisThread->mainHistory[us][from_to(move)]; From 4b085c4777c36939bd0a598f4bc3e0c04606e31b Mon Sep 17 00:00:00 2001 From: xoto10 <23479932+xoto10@users.noreply.github.com> Date: Fri, 19 May 2023 19:58:18 +0100 Subject: [PATCH 1059/1766] Simplify optimism calculation This change removes one of the constants in the calculation of optimism. It also changes the 2 constants used with the scale value so that they are independent, instead of applying a constant to the scale and then adjusting it again when it is applied to the optimism. This might make the tuning of these constants cleaner and more reliable in the future. STC 10+0.1 (accidentally run as an Elo gainer: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 154080 W: 41119 L: 40651 D: 72310 Ptnml(0-2): 375, 16840, 42190, 17212, 423 https://tests.stockfishchess.org/tests/live_elo/64653eabf3b1a4e86c317f77 LTC 60+0.6: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 217434 W: 58382 L: 58363 D: 100689 Ptnml(0-2): 66, 21075, 66419, 21088, 69 https://tests.stockfishchess.org/tests/live_elo/6465d077f3b1a4e86c318d6c closes https://github.com/official-stockfish/Stockfish/pull/4576 bench: 3190961 --- src/evaluate.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d8f4e2e194f..cc789e35d89 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1063,7 +1063,7 @@ Value Eval::evaluate(const Position& pos) { else { int nnueComplexity; - int scale = 967 + pos.non_pawn_material() / 64; + int npm = pos.non_pawn_material() / 64; Color stm = pos.side_to_move(); Value optimism = pos.this_thread()->optimism[stm]; @@ -1071,12 +1071,12 @@ Value Eval::evaluate(const Position& pos) { Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); // Blend nnue complexity with (semi)classical complexity - nnueComplexity = ( 402 * nnueComplexity - + (454 + optimism) * abs(psq - nnue) + nnueComplexity = ( 397 * nnueComplexity + + (477 + optimism) * abs(psq - nnue) ) / 1024; - optimism = optimism * (274 + nnueComplexity) / 256; - v = (nnue * scale + optimism * (scale - 791)) / 1024; + optimism += optimism * nnueComplexity / 256; + v = (nnue * (945 + npm) + optimism * (174 + npm)) / 1024; } // Damp down the evaluation linearly when shuffling From 7cd650f435715f73550d1f8031315e65b701d631 Mon Sep 17 00:00:00 2001 From: pb00067 Date: Wed, 17 May 2023 09:22:02 +0200 Subject: [PATCH 1060/1766] Simplify SEE verfication logic Passed STC https://tests.stockfishchess.org/tests/view/6461d51887f6567dd4df27d0 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 177056 W: 47181 L: 47118 D: 82757 Ptnml(0-2): 456, 19381, 48792, 19442, 457 Passed LTC https://tests.stockfishchess.org/tests/view/64631a9287f6567dd4df4502 2.94 (-2.94,2.94) <-1.75,0.25> Total: 104346 W: 28062 L: 27935 D: 48349 Ptnml(0-2): 25, 10190, 31631, 10287, 40 closes https://github.com/official-stockfish/Stockfish/pull/4578 bench: 2903251 --- src/search.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 10844746ccc..653cbf339a6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -996,9 +996,7 @@ namespace { // SEE based pruning (~11 Elo) if (!pos.see_ge(move, occupied, Value(-205) * depth)) { - if (depth < 2 - capture) - continue; - // Don't prune the move if opp. King/Queen/Rook gets a discovered attack during or after the exchanges + // Don't prune the move if opponent King/Queen/Rook gets a discovered attack during or after the exchanges Bitboard leftEnemies = pos.pieces(~us, KING, QUEEN, ROOK); Bitboard attacks = 0; occupied |= to_sq(move); @@ -1006,7 +1004,7 @@ namespace { { Square sq = pop_lsb(leftEnemies); attacks = pos.attackers_to(sq, occupied) & pos.pieces(us) & occupied; - // Exclude Queen/Rook(s) which were already threatened before SEE (opp King can't be in check when it's our turn) + // Exclude Queen/Rook(s) which were already threatened before SEE (opponent King can't be in check when it's our turn) if (attacks && sq != pos.square(~us) && (pos.attackers_to(sq, pos.pieces()) & pos.pieces(us))) attacks = 0; } From df0fb8471e5015bb4ba0b398c203b7faad45840e Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 14 May 2023 00:30:35 +0300 Subject: [PATCH 1061/1766] Small simplification in low depth pruning Uncap low depth pruning lmr depth. It's anyway capped for most cases apart from futility pruning for captures - removes one std::min call. Passed STC: https://tests.stockfishchess.org/tests/view/645e8fa6d55cccb2e64225a1 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 184064 W: 49039 L: 48982 D: 86043 Ptnml(0-2): 462, 20353, 50349, 20402, 466 Passed LTC: https://tests.stockfishchess.org/tests/view/645f4d48d55cccb2e6423335 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 83886 W: 22613 L: 22465 D: 38808 Ptnml(0-2): 31, 8090, 25546, 8252, 24 closes https://github.com/official-stockfish/Stockfish/pull/4566 bench 3201883 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 653cbf339a6..f58def60138 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -979,7 +979,7 @@ namespace { moveCountPruning = moveCount >= futility_move_count(improving, depth); // Reduced depth of the next LMR search - int lmrDepth = std::max(newDepth - r, 0); + int lmrDepth = newDepth - r; if ( capture || givesCheck) From d7e72d801fd68f2ee3c7d6b814bbc82916c30041 Mon Sep 17 00:00:00 2001 From: candirufish <38038147+candirufish@users.noreply.github.com> Date: Mon, 22 May 2023 00:14:55 +0200 Subject: [PATCH 1062/1766] More Depth Reduction Reduce more for depth > 3 and depth < 12 LTC: https://tests.stockfishchess.org/tests/view/646c5abbd1f14fd69a6f2fab LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 197280 W: 53405 L: 52797 D: 91078 Ptnml(0-2): 62, 19025, 59886, 19577, 90 STC: https://tests.stockfishchess.org/tests/view/646bee71d1f14fd69a6f259d LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 100832 W: 26861 L: 26466 D: 47505 Ptnml(0-2): 240, 10985, 27622, 11278, 291 https://github.com/official-stockfish/Stockfish/pull/4585 bench: 2276617 --- src/search.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index f58def60138..130855c19c9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1317,10 +1317,11 @@ namespace { else { // Reduce other moves if we have found at least one score improvement (~1 Elo) + // Reduce more for depth > 3 and depth < 12 (~1 Elo) if ( depth > 1 && beta < 14001 && value > -12754) - depth -= 1; + depth -= depth > 3 && depth < 12 ? 2 : 1; assert(depth > 0); alpha = value; // Update alpha! Always alpha < beta From b64c97825eb473d9b5cbdb67afe65a8ac0d5ec9f Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Sat, 20 May 2023 21:28:54 +0800 Subject: [PATCH 1063/1766] Simplify delta calculation in aspiration window Simplification STC: https://tests.stockfishchess.org/tests/view/6468cb200db5177f2b76ecbb LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 220416 W: 58503 L: 58487 D: 103426 Ptnml(0-2): 596, 24384, 60188, 24488, 552 Simplification LTC: https://tests.stockfishchess.org/tests/view/646a15840db5177f2b770704 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 177756 W: 47882 L: 47825 D: 82049 Ptnml(0-2): 55, 17430, 53858, 17473, 62 closes https://github.com/official-stockfish/Stockfish/pull/4581 Bench: 2304063 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 130855c19c9..3632a469884 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -409,7 +409,7 @@ void Thread::search() { else break; - delta += delta / 4 + 2; + delta += delta / 3; assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE); } From a989aa1825503ab39e6b2cf77bba2f1f022f367c Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sun, 21 May 2023 16:22:28 +0300 Subject: [PATCH 1064/1766] Simplify Prune moves with negative SEE Passed STC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 57760 W: 15472 L: 15286 D: 27002 Ptnml(0-2): 123, 6025, 16430, 6147, 155 https://tests.stockfishchess.org/tests/view/6468eb6b0db5177f2b76ef62 Passed LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 93966 W: 25274 L: 25141 D: 43551 Ptnml(0-2): 33, 8498, 29792, 8623, 37 https://tests.stockfishchess.org/tests/view/6469570b0db5177f2b76f81b closes: https://github.com/official-stockfish/Stockfish/pull/4579 Bench: 2304063 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 3632a469884..270c5e7c398 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1037,7 +1037,7 @@ namespace { lmrDepth = std::max(lmrDepth, 0); // Prune moves with negative SEE (~4 Elo) - if (!pos.see_ge(move, Value(-27 * lmrDepth * lmrDepth - 33 * lmrDepth / 2))) + if (!pos.see_ge(move, Value(-27 * lmrDepth * lmrDepth - 16 * lmrDepth))) continue; } } From cedd73f4aa9f83c2891105695ac11e743e6ceab7 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Tue, 23 May 2023 20:36:47 +0300 Subject: [PATCH 1065/1766] Simplify Futility pruning for captures Passed STC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 412928 W: 109433 L: 109620 D: 193875 Ptnml(0-2): 1071, 45929, 112650, 45744, 1070 https://tests.stockfishchess.org/tests/view/6468eac40db5177f2b76ef4d Passed LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 190200 W: 51465 L: 51420 D: 87315 Ptnml(0-2): 58, 18585, 57788, 18592, 77 https://tests.stockfishchess.org/tests/view/646b66520db5177f2b772a84 closes https://github.com/official-stockfish/Stockfish/pull/4583 bench: 2486604 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 270c5e7c398..32eeedab4ae 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -989,7 +989,7 @@ namespace { && lmrDepth < 7 && !ss->inCheck && ss->staticEval + 207 + 223 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] - + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] * 1078 / 7000 < alpha) + + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 7 < alpha) continue; Bitboard occupied; From c701745cf243f4816754167e85bf5fabf5b34e47 Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Thu, 25 May 2023 14:34:52 +0800 Subject: [PATCH 1066/1766] Remove ss->ttHit condition where ttValue != VALUE_NONE Simplification is done at 3 separate places in the code. Thanks to peregrineshahin for helping me find 2 of such places. (See original PR #4584) Passed non-regression test LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 120256 W: 32204 L: 32085 D: 55967 Ptnml(0-2): 292, 12473, 34483, 12584, 296 https://tests.stockfishchess.org/tests/view/646f045968661bfd984325e3 closes https://github.com/official-stockfish/Stockfish/pull/4587 No functional change --- AUTHORS | 1 + src/search.cpp | 9 +++------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/AUTHORS b/AUTHORS index b6723246ada..884bffabe7e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -76,6 +76,7 @@ George Sobala (gsobala) gguliash Giacomo Lorenzetti (G-Lorenz) Gian-Carlo Pascutto (gcp) +Goh CJ (cj5716) Gontran Lemaire (gonlem) Goodkov Vasiliy Aleksandrovich (goodkov) Gregor Cramer diff --git a/src/search.cpp b/src/search.cpp index 32eeedab4ae..93212e239b9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -615,10 +615,9 @@ namespace { // At non-PV nodes we check for an early TT cutoff if ( !PvNode - && ss->ttHit && !excludedMove && tte->depth() > depth - (tte->bound() == BOUND_EXACT) - && ttValue != VALUE_NONE // Possible in case of TT access race + && ttValue != VALUE_NONE // Possible in case of TT access race or if !ttHit && (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER))) { // If ttMove is quiet, update move sorting heuristics on TT hit (~2 Elo) @@ -835,8 +834,7 @@ namespace { // there and in further interactions with transposition table cutoff depth is set to depth - 3 // because probCut search has depth set to depth - 4 but we also do a move before it // so effective depth is equal to depth - 3 - && !( ss->ttHit - && tte->depth() >= depth - 3 + && !( tte->depth() >= depth - 3 && ttValue != VALUE_NONE && ttValue < probCutBeta)) { @@ -1453,9 +1451,8 @@ namespace { // At non-PV nodes we check for an early TT cutoff if ( !PvNode - && ss->ttHit && tte->depth() >= ttDepth - && ttValue != VALUE_NONE // Only in case of TT access race + && ttValue != VALUE_NONE // Only in case of TT access race or if !ttHit && (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER))) return ttValue; From 7f0b19dedf7bff7dbe2dd42e73788826486b36b6 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 27 May 2023 18:01:08 +0200 Subject: [PATCH 1067/1766] Update CPU contributors list update CPU contributors list, the previous update was a couple of months ago, and unfortunately, was not quite accurate for the number of games played. This version is based clean calculation from the DB and an updated script that tracks things (see https://github.com/glinscott/fishtest/pull/1702). closes https://github.com/official-stockfish/Stockfish/pull/4589 No functional change --- Top CPU Contributors.txt | 196 ++++++++++++++++++++------------------- 1 file changed, 101 insertions(+), 95 deletions(-) diff --git a/Top CPU Contributors.txt b/Top CPU Contributors.txt index 30c963d7c7e..7b27959071a 100644 --- a/Top CPU Contributors.txt +++ b/Top CPU Contributors.txt @@ -1,212 +1,218 @@ -Contributors to Fishtest with >10,000 CPU hours, as of 2022-11-19. +Contributors to Fishtest with >10,000 CPU hours, as of 2023-05-27. Thank you! Username CPU Hours Games played ------------------------------------------------------------------ -noobpwnftw 36475307 2748033975 -technologov 14570711 760073590 +noobpwnftw 37304027 2833556221 +technologov 13508659 714674674 +linrock 4121386 280027751 mlang 3026000 200065824 -dew 1689222 100034318 -grandphish2 1442171 86798057 -okrout 1439985 133471766 -pemo 1405374 44189811 -linrock 1299003 28382783 -TueRens 1163420 71159522 -JojoM 897158 55177114 +dew 1689162 100033738 +okrout 1541122 145085726 +pemo 1481818 47546583 +grandphish2 1459364 91364265 +TueRens 1178700 69951886 +JojoM 937875 60821044 tvijlbrief 796125 51897690 mibere 703840 46867607 -gvreuls 635982 40652394 -oz 590763 41201352 -sebastronomy 581517 23307132 -cw 517915 34865769 -fastgm 504266 30264740 -CSU_Dynasty 479901 31846710 -ctoks 433503 28180725 +sebastronomy 687502 35585318 +gvreuls 645570 42437926 +oz 541224 39133532 +cw 517856 34869499 +fastgm 503862 30260818 +CSU_Dynasty 464691 31166478 +leszek 460426 32840277 +ctoks 434323 28497451 crunchy 427035 27344275 -leszek 416883 27493447 -bcross 409982 28062127 -velislav 345954 22232274 +maximmasiutin 424154 26534660 +bcross 415722 29060963 +rpngn 344368 24218047 +velislav 342559 22138408 Fisherman 327231 21829379 +mgrabiak 297057 20260882 Dantist 296386 18031762 -mgrabiak 288928 18869896 -rpngn 259965 16281463 -robal 237653 15148350 -ncfish1 231764 15275003 -nordlandia 226923 14624832 +nordlandia 242642 15922516 +robal 240199 15544104 +marrco 234581 17714473 +ncfish1 227517 15233777 glinscott 208125 13277240 drabel 204167 13930674 mhoram 202894 12601997 bking_US 198894 11876016 -thirdlife 198844 5453268 +olafm 192342 14968698 Thanar 179852 12365359 vdv 175544 9904472 -armo9494 168201 11136452 spams 157128 10319326 -marrco 151599 9551115 sqrt2 147963 9724586 -vdbergh 137690 8971569 +DesolatedDodo 144759 9408038 +Calis007 143165 9478764 +vdbergh 138436 9042073 CoffeeOne 137100 5024116 malala 136182 8002293 -DesolatedDodo 135276 8657464 +armo9494 136010 9447548 xoto 133759 9159372 davar 129023 8376525 +DMBK 122960 8980062 dsmith 122059 7570238 amicic 119661 7938029 Data 113305 8220352 BrunoBanani 112960 7436849 CypressChess 108331 7759788 -skiminki 106518 7062598 +skiminki 107583 7218170 MaZePallas 102823 6633619 sterni1971 100532 5880772 +jcAEie 100392 7788270 sunu 100167 7040199 zeryl 99331 6221261 +thirdlife 99124 2242380 ElbertoOne 99028 7023771 -DMBK 97572 6950312 -Calis007 96779 5611552 -cuistot 93111 5536500 +cuistot 98360 6017102 +bigpen0r 94809 6529203 brabos 92118 6186135 -Wolfgang 91769 5720158 +Wolfgang 90855 5998076 psk 89957 5984901 racerschmacer 85805 6122790 -jcAEie 85527 5630616 +Dubslow 84986 6042456 Vizvezdenec 83761 5344740 -sschnee 83557 4853690 +sschnee 83564 4853834 0x3C33 82614 5271253 BRAVONE 81239 5054681 -Dubslow 78461 5042980 +Fifis 77355 5158211 nssy 76497 5259388 jromang 76106 5236025 teddybaer 75125 5407666 -yurikvelo 73933 5031096 -tolkki963 73885 4721430 +Wencey 74181 4711488 +megaman7de 73866 4894960 Pking_cda 73776 5293873 -Bobo1239 71675 4860987 +tolkki963 73531 5020500 +yurikvelo 72847 4972808 +Bobo1239 70579 4794999 solarlight 70517 5028306 dv8silencer 70287 3883992 -Gelma 69304 3980932 +markkulix 70278 5068326 manap 66273 4121774 -megaman7de 65419 4120200 -markkulix 65331 4114860 -bigpen0r 64932 4683883 tinker 64333 4268790 qurashee 61208 3429862 -AGI 58325 4258646 +Mineta 58759 4399960 +AGI 58147 4325994 +Spprtr 58106 3858759 robnjr 57262 4053117 Freja 56938 3733019 MaxKlaxxMiner 56879 3423958 +MarcusTullius 56746 3762951 ttruscott 56010 3680085 rkl 55132 4164467 renouve 53811 3501516 -Spprtr 52736 3410019 finfish 51360 3370515 eva42 51272 3599691 eastorwest 51117 3454811 rap 49985 3219146 -unixwizard 49734 2536230 -pb00067 49727 3298270 +pb00067 49733 3298934 +javran 49178 4190632 +OuaisBla 48606 3442958 ronaldjerum 47654 3240695 biffhero 46564 3111352 -GPUex 45861 2926502 -Fifis 45843 3088497 -oryx 45578 3493978 VoyagerOne 45476 3452465 -Wencey 44943 2654490 +oryx 44532 3450170 +jmdana 43849 2955821 speedycpu 43842 3003273 jbwiebe 43305 2805433 Antihistamine 41788 2761312 mhunt 41735 2691355 -olafm 41277 3284344 +maposora 41534 3733078 +GPUex 41061 2998356 homyur 39893 2850481 gri 39871 2515779 -MarcusTullius 38303 2251097 Garf 37741 2999686 -kdave 37424 2557406 SC 37299 2731694 csnodgrass 36207 2688994 -jmdana 36157 2210661 strelock 34716 2074055 EthanOConnor 33370 2090311 slakovv 32915 2021889 -gopeto 31669 2060958 +Gelma 31771 1551204 +gopeto 31671 2060990 +szupaw 31248 2594920 +kdave 31157 2198362 manapbk 30987 1810399 Prcuvu 30377 2170122 anst 30301 2190091 jkiiski 30136 1904470 -spcc 30135 1903728 +spcc 29925 1901692 hyperbolic.tom 29840 2017394 -xwziegtm 29763 2347412 chuckstablers 29659 2093438 Pyafue 29650 1902349 belzedar94 28846 1811530 -OuaisBla 27636 1578800 chriswk 26902 1868317 +xwziegtm 26897 2124586 achambord 26582 1767323 Patrick_G 26276 1801617 yorkman 26193 1992080 -Ulysses 25289 1674274 +Ulysses 25285 1689346 SFTUser 25182 1675689 nabildanial 24942 1519409 Sharaf_DG 24765 1786697 rodneyc 24376 1416402 agg177 23890 1395014 -Ente 23747 1674582 -Karpovbot 23629 1313186 +Ente 23639 1671638 JanErik 23408 1703875 Isidor 23388 1680691 Norabor 23371 1603244 -cisco2015 22934 1763773 +Goatminola 23338 1910634 +cisco2015 22920 1763301 +Jopo12321 22890 1424926 Zirie 22542 1472937 team-oh 22272 1636708 Roady 22220 1465606 MazeOfGalious 21978 1629593 sg4032 21947 1643353 +jsys14 21935 1499128 ianh2105 21725 1632562 xor12 21628 1680365 dex 21612 1467203 nesoneg 21494 1463031 user213718 21454 1404128 -AndreasKrug 21227 1577833 sphinx 21211 1384728 jjoshua2 21001 1423089 +Zake9298 20938 1565848 +AndreasKrug 20911 1615673 horst.prack 20878 1465656 -jsys14 20729 1221010 0xB00B1ES 20590 1208666 j3corre 20405 941444 Adrian.Schmidt123 20316 1281436 -bonsi 20022 1300682 wei 19973 1745989 -dapper 19754 1167758 -Zake9298 19745 1458416 +Serpensin 19840 1697528 fishtester 19617 1257388 rstoesser 19569 1293588 eudhan 19274 1283717 +Gaster319 18934 1596772 vulcan 18871 1729392 -Jopo12321 18803 1036284 +Karpovbot 18766 1053178 jundery 18445 1115855 +votoanthuan 18012 1508836 ville 17883 1384026 -5t0ckf15hTr4in3r 17809 1105858 chris 17698 1487385 -dju 17697 994333 purplefishies 17595 1092533 +qoo_charly_cai 17494 1182667 +dju 17414 981289 iisiraider 17275 1049015 DragonLord 17014 1162790 -Karby 16457 1010138 -Goatminola 16278 1145026 +redstone59 16842 1461780 +Alb11747 16787 1213926 IgorLeMasson 16064 1147232 -Gaster319 16056 1109070 -redstone59 15953 1161664 +Karby 15982 979610 +notchris 15818 1426762 scuzzi 15757 968735 ako027ako 15671 1173203 Nikolay.IT 15154 1068349 Andrew Grant 15114 895539 Naven94 15054 834762 OssumOpossum 14857 1007129 -qoo_charly_cai 14490 847865 +ZacHFX 14783 1021842 enedene 14476 905279 -szupaw 14252 929130 bpfliegel 14233 882523 mpx86 14019 759568 jpulman 13982 870599 +Skiff84 13826 721996 crocogoat 13803 1117422 Nesa92 13786 1114691 joster 13710 946160 @@ -214,47 +220,47 @@ mbeier 13650 1044928 Hjax 13535 915487 Dark_wizzie 13422 1007152 Rudolphous 13244 883140 +pirt 13100 1009897 Machariel 13010 863104 infinigon 12991 943216 -pirt 12925 985437 -Skiff84 12923 649994 +Maxim 12963 985594 mabichito 12903 749391 thijsk 12886 722107 AdrianSA 12860 804972 Flopzee 12698 894821 +korposzczur 12606 838168 +Nullvalue 12583 1048502 fatmurphy 12547 853210 -woutboat 12419 836696 SapphireBrand 12416 969604 -Oakwen 12406 840961 +Oakwen 12399 844109 deflectooor 12386 579392 modolief 12386 896470 Farseer 12249 694108 +Jackfish 12180 801372 pgontarz 12151 848794 +dbernier 12103 860824 +getraideBFF 12072 1024966 stocky 11954 699440 mschmidt 11941 803401 -MooTheCow 11871 773654 -Jackfish 11867 773550 -dbernier 11705 821780 +MooTheCow 11870 773598 +FormazChar 11689 877727 whelanh 11557 245188 -Maxim 11543 836024 -Nullvalue 11534 731410 -icewulf 11528 650470 -FormazChar 11523 861599 infinity 11470 727027 aga 11412 695127 torbjo 11395 729145 Thomas A. Anderson 11372 732094 savage84 11358 670860 -ali-al-zhrani 11272 781310 d64 11263 789184 -Bourbaki 11108 709144 +ali-al-zhrani 11245 779246 snicolet 11106 869170 -Alb11747 10855 696920 +dapper 11032 771402 +Karmatron 10828 677458 basepi 10637 744851 Cubox 10621 826448 -Karmatron 10616 674818 michaelrpg 10509 739239 OIVAS7572 10420 995586 -Garruk 10348 704905 +jojo2357 10419 929708 +WoodMan777 10380 873720 +Garruk 10365 706465 dzjp 10343 732529 ols 10259 570669 From 7e9b131efb832339ee6cd9e22b7c837c3e69a1b5 Mon Sep 17 00:00:00 2001 From: windfishballad Date: Mon, 22 May 2023 20:13:44 -0400 Subject: [PATCH 1068/1766] Removed quadratic term in optimism Remove term which is quadratic in optimism in the eval. Simplifies and should also remove the bias towards side to move making the eval better for analysis. STC: https://tests.stockfishchess.org/tests/view/6470a9d8c29e0d4352b0bca5 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 154432 W: 41127 L: 41040 D: 72265 Ptnml(0-2): 380, 17094, 42190, 17163, 389 LTC: https://tests.stockfishchess.org/tests/view/6471e9b3e549d9cf2fb219ef LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 127926 W: 34474 L: 34369 D: 59083 Ptnml(0-2): 43, 12505, 38776, 12582, 57 closes https://github.com/official-stockfish/Stockfish/pull/4590 Bench: 2541211 --- AUTHORS | 1 + src/evaluate.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 884bffabe7e..d01d23cd7e9 100644 --- a/AUTHORS +++ b/AUTHORS @@ -213,6 +213,7 @@ tttak Unai Corzo (unaiic) Uri Blass (uriblass) Vince Negri (cuddlestmonkey) +windfishballad xefoci7612 zz4032 diff --git a/src/evaluate.cpp b/src/evaluate.cpp index cc789e35d89..7239fd1eca0 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1072,7 +1072,7 @@ Value Eval::evaluate(const Position& pos) { // Blend nnue complexity with (semi)classical complexity nnueComplexity = ( 397 * nnueComplexity - + (477 + optimism) * abs(psq - nnue) + + 477 * abs(psq - nnue) ) / 1024; optimism += optimism * nnueComplexity / 256; From c1fff71650e2f8bf5a2d63bdc043161cdfe8e460 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Fri, 12 May 2023 18:07:20 -0400 Subject: [PATCH 1069/1766] Update NNUE architecture to SFNNv6 with larger L1 size of 1536 Created by training a new net from scratch with L1 size increased from 1024 to 1536. Thanks to Vizvezdenec for the idea of exploring larger net sizes after recent training data improvements. A new net was first trained with lambda 1.0 and constant LR 8.75e-4. Then a strong net from a later epoch in the training run was chosen for retraining with start-lambda 1.0 and initial LR 4.375e-4 decaying with gamma 0.995. Retraining was performed a total of 3 times, for this 4-step process: 1. 400 epochs, lambda 1.0 on filtered T77+T79 v6 deduplicated data 2. 800 epochs, end-lambda 0.75 on T60T70wIsRightFarseerT60T74T75T76.binpack 3. 800 epochs, end-lambda 0.75 and early-fen-skipping 28 on the master dataset 4. 800 epochs, end-lambda 0.7 and early-fen-skipping 28 on the master dataset In the training sequence that reached the new nn-8d69132723e2.nnue net, the epochs used for the 3x retraining runs were: 1. epoch 379 trained on T77T79-filter-v6-dd.min.binpack 2. epoch 679 trained on T60T70wIsRightFarseerT60T74T75T76.binpack 3. epoch 799 trained on the master dataset For training from scratch: python3 easy_train.py \ --experiment-name new-L1-1536-T77T79-filter-v6dd \ --training-dataset /data/T77T79-filter-v6-dd.min.binpack \ --max_epoch 400 \ --lambda 1.0 \ --start-from-engine-test-net False \ --engine-test-branch linrock/Stockfish/L1-1536 \ --nnue-pytorch-branch linrock/Stockfish/misc-fixes-L1-1536 \ --tui False \ --gpus "0," \ --seed $RANDOM Retraining commands were similar to each other. For the 3rd retraining run: python3 easy_train.py \ --experiment-name L1-1536-T77T79-v6dd-Re1-LeelaFarseer-Re2-masterDataset-Re3-sameData \ --training-dataset /data/leela96-dfrc99-v2-T60novdecT80juntonovjanfebT79aprmayT78jantosepT77dec-v6dd.binpack \ --early-fen-skipping 28 \ --max_epoch 800 \ --start-lambda 1.0 \ --end-lambda 0.7 \ --lr 4.375e-4 \ --gamma 0.995 \ --start-from-engine-test-net False \ --start-from-model /data/L1-1536-T77T79-v6dd-Re1-LeelaFarseer-Re2-masterDataset-nn-epoch799.nnue \ --engine-test-branch linrock/Stockfish/L1-1536 \ --nnue-pytorch-branch linrock/nnue-pytorch/misc-fixes-L1-1536 \ --tui False \ --gpus "0," \ --seed $RANDOM The T77+T79 data used is a subset of the master dataset available at: https://robotmoon.com/nnue-training-data/ T60T70wIsRightFarseerT60T74T75T76.binpack is available at: https://drive.google.com/drive/folders/1S9-ZiQa_3ApmjBtl2e8SyHxj4zG4V8gG Local elo at 25k nodes per move vs. nn-e1fb1ade4432.nnue (L1 size 1024): nn-epoch759.nnue : 26.9 +/- 1.6 Failed STC https://tests.stockfishchess.org/tests/view/64742485d29264e4cfa75f97 LLR: -2.94 (-2.94,2.94) <0.00,2.00> Total: 13728 W: 3588 L: 3829 D: 6311 Ptnml(0-2): 71, 1661, 3610, 1482, 40 Failing LTC https://tests.stockfishchess.org/tests/view/64752d7c4a36543c4c9f3618 LLR: -1.91 (-2.94,2.94) <0.50,2.50> Total: 35424 W: 9522 L: 9603 D: 16299 Ptnml(0-2): 24, 3579, 10585, 3502, 22 Passed VLTC 180+1.8 https://tests.stockfishchess.org/tests/view/64752df04a36543c4c9f3638 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 47616 W: 13174 L: 12863 D: 21579 Ptnml(0-2): 13, 4261, 14952, 4566, 16 Passed VLTC SMP 60+0.6 th 8 https://tests.stockfishchess.org/tests/view/647446ced29264e4cfa761e5 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 19942 W: 5694 L: 5451 D: 8797 Ptnml(0-2): 6, 1504, 6707, 1749, 5 closes https://github.com/official-stockfish/Stockfish/pull/4593 bench 2222567 --- src/evaluate.h | 2 +- src/nnue/nnue_architecture.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evaluate.h b/src/evaluate.h index f5db1c1e622..0990111cf6d 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-e1fb1ade4432.nnue" + #define EvalFileDefaultName "nn-8d69132723e2.nnue" namespace NNUE { diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index 508f3aae0a7..d10434f34b8 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -39,7 +39,7 @@ namespace Stockfish::Eval::NNUE { using FeatureSet = Features::HalfKAv2_hm; // Number of input feature dimensions after conversion -constexpr IndexType TransformedFeatureDimensions = 1024; +constexpr IndexType TransformedFeatureDimensions = 1536; constexpr IndexType PSQTBuckets = 8; constexpr IndexType LayerStacks = 8; From 07bd8adcbce41f076c36f4b65c7f9a786de0b02d Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Sun, 28 May 2023 14:45:24 -0400 Subject: [PATCH 1070/1766] Simplify nnue eval complexity calculation Remove a multiplier when blending nnue complexity with semi-classical complexity. Passed non-regression STC: https://tests.stockfishchess.org/tests/view/6473a71dd29264e4cfa75839 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 124768 W: 33180 L: 33060 D: 58528 Ptnml(0-2): 314, 13797, 34030, 13941, 302 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/6474af3dd29264e4cfa768f4 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 108180 W: 29008 L: 28884 D: 50288 Ptnml(0-2): 29, 10420, 33075, 10530, 36 closes https://github.com/official-stockfish/Stockfish/pull/4592 bench 2316827 --- src/evaluate.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 7239fd1eca0..40c43d23043 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1071,9 +1071,7 @@ Value Eval::evaluate(const Position& pos) { Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); // Blend nnue complexity with (semi)classical complexity - nnueComplexity = ( 397 * nnueComplexity - + 477 * abs(psq - nnue) - ) / 1024; + nnueComplexity = 25 * (nnueComplexity + abs(psq - nnue)) / 64; optimism += optimism * nnueComplexity / 256; v = (nnue * (945 + npm) + optimism * (174 + npm)) / 1024; From d99942f25449789de78c9d36e3dcb67d4eb04e98 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Wed, 31 May 2023 09:22:26 +0300 Subject: [PATCH 1071/1766] Small simplification for probcut in check Remove depth condition from there as not longer needed. Passed STC: https://tests.stockfishchess.org/tests/view/647367cad29264e4cfa753e6 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 254336 W: 67830 L: 67847 D: 118659 Ptnml(0-2): 580, 28181, 69697, 28096, 614 Passed LTC: https://tests.stockfishchess.org/tests/view/647576184a36543c4c9f3af7 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 80706 W: 22048 L: 21898 D: 36760 Ptnml(0-2): 28, 7721, 24712, 7857, 35 closes https://github.com/official-stockfish/Stockfish/pull/4594 bench 2381945 --- src/search.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 93212e239b9..41116eb2206 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -895,7 +895,6 @@ namespace { probCutBeta = beta + 430; if ( ss->inCheck && !PvNode - && depth >= 2 && ttCapture && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 4 From 06186b786e4a73a29d6f0eef80fa7e20084a1e85 Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Fri, 2 Jun 2023 19:55:25 +0800 Subject: [PATCH 1072/1766] Search tuning at very long time control with new net MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The most significant change would be the singularBeta formula. It was first tested by cj5716 (see https://tests.stockfishchess.org/tests/view/647317c9d29264e4cfa74ec7), and I took much inspiration from that idea. LTC (fixed games): https://tests.stockfishchess.org/tests/view/6479d8da54dd118e1d990b12 Elo: 0.61 ± 1.2 (95%) LOS: 83.4% Total: 60000 W: 16278 L: 16172 D: 27550 Ptnml(0-2): 16, 5845, 18179, 5937, 23 nElo: 1.38 ± 2.8 (95%) PairsRatio: 1.02 VLTC 180+1.8: https://tests.stockfishchess.org/tests/view/6479da1454dd118e1d990b2b LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 33224 W: 9261 L: 8984 D: 14979 Ptnml(0-2): 5, 2809, 10710, 3080, 8 SMP VLTC 8-thread: https://tests.stockfishchess.org/tests/view/647b0fe354dd118e1d992425 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 61398 W: 17386 L: 17081 D: 26931 Ptnml(0-2): 7, 4571, 21232, 4888, 1 closes https://github.com/official-stockfish/Stockfish/pull/4603 Bench: 2805878 --- src/search.cpp | 72 ++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 41116eb2206..16122315032 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -64,7 +64,7 @@ namespace { // Futility margin Value futility_margin(Depth d, bool improving) { - return Value(148 * (d - improving)); + return Value(140 * (d - improving)); } // Reductions lookup table, initialized at startup @@ -72,7 +72,7 @@ namespace { Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) { int r = Reductions[d] * Reductions[mn]; - return (r + 1356 - int(delta) * 983 / int(rootDelta)) / 1024 + (!i && r > 901); + return (r + 1372 - int(delta) * 1073 / int(rootDelta)) / 1024 + (!i && r > 936); } constexpr int futility_move_count(bool improving, Depth depth) { @@ -82,7 +82,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return std::min(337 * d - 497, 1632); + return std::min(336 * d - 547, 1561); } // Add a small random component to draw evaluations to avoid 3-fold blindness @@ -162,7 +162,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int((20.89 + std::log(Threads.size()) / 2) * std::log(i)); + Reductions[i] = int((20.57 + std::log(Threads.size()) / 2) * std::log(i)); } @@ -348,12 +348,12 @@ void Thread::search() { // Reset aspiration window starting size Value prev = rootMoves[pvIdx].averageScore; - delta = Value(11) + int(prev) * prev / 15368; + delta = Value(10) + int(prev) * prev / 15799; alpha = std::max(prev - delta,-VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE); // Adjust optimism based on root move's previousScore - int opt = 116 * prev / (std::abs(prev) + 143); + int opt = 109 * prev / (std::abs(prev) + 141); optimism[ us] = Value(opt); optimism[~us] = -optimism[us]; @@ -741,7 +741,7 @@ namespace { // Use static evaluation difference to improve quiet move ordering (~4 Elo) if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture) { - int bonus = std::clamp(-19 * int((ss-1)->staticEval + ss->staticEval), -1717, 1717); + int bonus = std::clamp(-18 * int((ss-1)->staticEval + ss->staticEval), -1817, 1817); thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus; } @@ -751,13 +751,13 @@ namespace { // margin and the improving flag are used in various pruning heuristics. improvement = (ss-2)->staticEval != VALUE_NONE ? ss->staticEval - (ss-2)->staticEval : (ss-4)->staticEval != VALUE_NONE ? ss->staticEval - (ss-4)->staticEval - : 163; + : 173; improving = improvement > 0; // Step 7. Razoring (~1 Elo). // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. - if (eval < alpha - 467 - 266 * depth * depth) + if (eval < alpha - 456 - 252 * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -770,16 +770,16 @@ namespace { && depth < 9 && eval - futility_margin(depth, improving) - (ss-1)->statScore / 306 >= beta && eval >= beta - && eval < 22761) // larger than VALUE_KNOWN_WIN, but smaller than TB wins + && eval < 24923) // larger than VALUE_KNOWN_WIN, but smaller than TB wins return eval; // Step 9. Null move search with verification search (~35 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 18404 + && (ss-1)->statScore < 17329 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 19 * depth - improvement / 13 + 257 + && ss->staticEval >= beta - 21 * depth - improvement * 99 / 1300 + 258 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly)) @@ -787,7 +787,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and eval - Depth R = std::min(int(eval - beta) / 172, 6) + depth / 3 + 4; + Depth R = std::min(int(eval - beta) / 173, 6) + depth / 3 + 4; ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -822,7 +822,7 @@ namespace { } } - probCutBeta = beta + 174 - 60 * improving; + probCutBeta = beta + 168 - 61 * improving; // Step 10. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search returns a value @@ -892,7 +892,7 @@ namespace { moves_loop: // When in check, search starts here // Step 12. A small Probcut idea, when we are in check (~4 Elo) - probCutBeta = beta + 430; + probCutBeta = beta + 413; if ( ss->inCheck && !PvNode && ttCapture @@ -985,13 +985,13 @@ namespace { if ( !givesCheck && lmrDepth < 7 && !ss->inCheck - && ss->staticEval + 207 + 223 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] + && ss->staticEval + 197 + 248 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 7 < alpha) continue; Bitboard occupied; // SEE based pruning (~11 Elo) - if (!pos.see_ge(move, occupied, Value(-205) * depth)) + if (!pos.see_ge(move, occupied, Value(-212) * depth)) { // Don't prune the move if opponent King/Queen/Rook gets a discovered attack during or after the exchanges Bitboard leftEnemies = pos.pieces(~us, KING, QUEEN, ROOK); @@ -1017,18 +1017,18 @@ namespace { // Continuation history based pruning (~2 Elo) if ( lmrDepth < 6 - && history < -3792 * depth) + && history < -3832 * depth) continue; history += 2 * thisThread->mainHistory[us][from_to(move)]; - lmrDepth += history / 7019; + lmrDepth += history / 7011; lmrDepth = std::max(lmrDepth, -2); // Futility pruning: parent node (~13 Elo) if ( !ss->inCheck && lmrDepth < 12 - && ss->staticEval + 111 + 136 * lmrDepth <= alpha) + && ss->staticEval + 112 + 138 * lmrDepth <= alpha) continue; lmrDepth = std::max(lmrDepth, 0); @@ -1057,7 +1057,7 @@ namespace { && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - (99 + 65 * (ss->ttPv && !PvNode)) * depth / 64; + Value singularBeta = ttValue - (82 + 65 * (ss->ttPv && !PvNode)) * depth / 64; Depth singularDepth = (depth - 1) / 2; ss->excludedMove = move; @@ -1071,7 +1071,7 @@ namespace { // Avoid search explosion by limiting the number of double extensions if ( !PvNode - && value < singularBeta - 22 + && value < singularBeta - 21 && ss->doubleExtensions <= 11) { extension = 2; @@ -1101,16 +1101,14 @@ namespace { } // Check extensions (~1 Elo) - else if ( givesCheck - && depth > 9 - && abs(ss->staticEval) > 87) + else if ( givesCheck && depth > 8) extension = 1; // Quiet ttMove extensions (~1 Elo) else if ( PvNode && move == ttMove && move == ss->killers[0] - && (*contHist[0])[movedPiece][to_sq(move)] >= 5480) + && (*contHist[0])[movedPiece][to_sq(move)] >= 5168) extension = 1; } @@ -1152,7 +1150,7 @@ namespace { // Decrease reduction for PvNodes based on depth (~2 Elo) if (PvNode) - r -= 1 + 11 / (3 + depth); + r -= 1 + 12 / (3 + depth); // Decrease reduction if ttMove has been singularly extended (~1 Elo) if (singularQuietLMR) @@ -1169,10 +1167,10 @@ namespace { + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 3755; + - 4006; // Decrease/increase reduction for moves with a good/bad history (~25 Elo) - r -= ss->statScore / (10445 + 4762 * (depth > 6 && depth < 21)); + r -= ss->statScore / (11124 + 4740 * (depth > 5 && depth < 22)); // Step 17. Late moves reduction / extension (LMR, ~117 Elo) // We use various heuristics for the sons of a node after the first son has @@ -1196,8 +1194,8 @@ namespace { { // Adjust full depth search based on LMR results - if result // was good enough search deeper, if it was bad enough search shallower - const bool doDeeperSearch = value > (bestValue + 63 + 11 * (newDepth - d)); - const bool doEvenDeeperSearch = value > alpha + 662 && ss->doubleExtensions <= 6; + const bool doDeeperSearch = value > (bestValue + 64 + 11 * (newDepth - d)); + const bool doEvenDeeperSearch = value > alpha + 711 && ss->doubleExtensions <= 6; const bool doShallowerSearch = value < bestValue + newDepth; ss->doubleExtensions = ss->doubleExtensions + doEvenDeeperSearch; @@ -1316,8 +1314,8 @@ namespace { // Reduce other moves if we have found at least one score improvement (~1 Elo) // Reduce more for depth > 3 and depth < 12 (~1 Elo) if ( depth > 1 - && beta < 14001 - && value > -12754) + && beta < 14362 + && value > -12393) depth -= depth > 3 && depth < 12 ? 2 : 1; assert(depth > 0); @@ -1366,7 +1364,7 @@ namespace { // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (depth > 5) + (PvNode || cutNode) + (bestValue < alpha - 100 * depth) + ((ss-1)->moveCount > 11); + int bonus = (depth > 5) + (PvNode || cutNode) + (bestValue < alpha - 113 * depth) + ((ss-1)->moveCount > 12); update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); } @@ -1494,7 +1492,7 @@ namespace { if (PvNode && bestValue > alpha) alpha = bestValue; - futilityBase = bestValue + 190; + futilityBase = bestValue + 200; } const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, @@ -1567,7 +1565,7 @@ namespace { continue; // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, Value(-94))) + if (!pos.see_ge(move, Value(-95))) continue; } @@ -1700,7 +1698,7 @@ namespace { if (!pos.capture_stage(bestMove)) { - int bonus2 = bestValue > beta + 143 ? bonus1 // larger bonus + int bonus2 = bestValue > beta + 145 ? bonus1 // larger bonus : stat_bonus(depth); // smaller bonus // Increase stats for the best move in case it was a quiet move From 6cf8d938c5950ddedb8a92cdea4712f7d507c614 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Thu, 1 Jun 2023 09:50:19 -0400 Subject: [PATCH 1073/1766] Simplify blending nnue complexity with optimism Passed non-regression STC: https://tests.stockfishchess.org/tests/view/6478a26d54dd118e1d98f21c LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 241248 W: 64058 L: 64063 D: 113127 Ptnml(0-2): 644, 26679, 65960, 26720, 621 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/647b464854dd118e1d9928b2 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 24336 W: 6658 L: 6451 D: 11227 Ptnml(0-2): 8, 2316, 7312, 2525, 7 closes https://github.com/official-stockfish/Stockfish/pull/4602 bench 2425813 --- src/evaluate.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 40c43d23043..bf6dd69a950 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1070,10 +1070,8 @@ Value Eval::evaluate(const Position& pos) { Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); - // Blend nnue complexity with (semi)classical complexity - nnueComplexity = 25 * (nnueComplexity + abs(psq - nnue)) / 64; - - optimism += optimism * nnueComplexity / 256; + // Blend optimism with nnue complexity and (semi)classical complexity + optimism += 25 * optimism * (nnueComplexity + abs(psq - nnue)) / 16384; v = (nnue * (945 + npm) + optimism * (174 + npm)) / 1024; } From 5930c0defbe01576315d7d081447f94a01daf337 Mon Sep 17 00:00:00 2001 From: Guenther Demetz Date: Wed, 31 May 2023 11:48:18 +0200 Subject: [PATCH 1074/1766] Simplify away SEE verification After 4 simplificatons over PR#4453 the idea does not yield significant improvement anymore. Maybe also https://tests.stockfishchess.org/tests/view/640c88092644b62c3394c1c5 was a fluke. Passed non-regression bounds: STC: https://tests.stockfishchess.org/tests/view/64705389c079b6583146d873 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 131936 W: 35040 L: 34930 D: 61966 Ptnml(0-2): 336, 14559, 36035, 14735, 303 LTC: https://tests.stockfishchess.org/tests/view/6471a2ade549d9cf2fb213cd LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 407700 W: 109999 L: 110164 D: 187537 Ptnml(0-2): 279, 39913, 123689, 39632, 337 closes https://github.com/official-stockfish/Stockfish/pull/4595 bench: 2675974 --- src/position.cpp | 19 +++++++------------ src/position.h | 1 - src/search.cpp | 18 +----------------- 3 files changed, 8 insertions(+), 30 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index 2a9d798ff7d..af274d3f2c6 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1061,7 +1061,7 @@ Key Position::key_after(Move m) const { /// SEE value of move is greater or equal to the given threshold. We'll use an /// algorithm similar to alpha-beta pruning with a null window. -bool Position::see_ge(Move m, Bitboard& occupied, Value threshold) const { +bool Position::see_ge(Move m, Value threshold) const { assert(is_ok(m)); @@ -1080,7 +1080,7 @@ bool Position::see_ge(Move m, Bitboard& occupied, Value threshold) const { return true; assert(color_of(piece_on(from)) == sideToMove); - occupied = pieces() ^ from ^ to; // xoring to is important for pinned piece logic + Bitboard occupied = pieces() ^ from ^ to; // xoring to is important for pinned piece logic Color stm = sideToMove; Bitboard attackers = attackers_to(to, occupied); Bitboard stmAttackers, bb; @@ -1111,43 +1111,43 @@ bool Position::see_ge(Move m, Bitboard& occupied, Value threshold) const { // the bitboard 'attackers' any X-ray attackers behind it. if ((bb = stmAttackers & pieces(PAWN))) { - occupied ^= least_significant_square_bb(bb); if ((swap = PawnValueMg - swap) < res) break; + occupied ^= least_significant_square_bb(bb); attackers |= attacks_bb(to, occupied) & pieces(BISHOP, QUEEN); } else if ((bb = stmAttackers & pieces(KNIGHT))) { - occupied ^= least_significant_square_bb(bb); if ((swap = KnightValueMg - swap) < res) break; + occupied ^= least_significant_square_bb(bb); } else if ((bb = stmAttackers & pieces(BISHOP))) { - occupied ^= least_significant_square_bb(bb); if ((swap = BishopValueMg - swap) < res) break; + occupied ^= least_significant_square_bb(bb); attackers |= attacks_bb(to, occupied) & pieces(BISHOP, QUEEN); } else if ((bb = stmAttackers & pieces(ROOK))) { - occupied ^= least_significant_square_bb(bb); if ((swap = RookValueMg - swap) < res) break; + occupied ^= least_significant_square_bb(bb); attackers |= attacks_bb(to, occupied) & pieces(ROOK, QUEEN); } else if ((bb = stmAttackers & pieces(QUEEN))) { - occupied ^= least_significant_square_bb(bb); if ((swap = QueenValueMg - swap) < res) break; + occupied ^= least_significant_square_bb(bb); attackers |= (attacks_bb(to, occupied) & pieces(BISHOP, QUEEN)) | (attacks_bb(to, occupied) & pieces(ROOK , QUEEN)); @@ -1162,11 +1162,6 @@ bool Position::see_ge(Move m, Bitboard& occupied, Value threshold) const { return bool(res); } -bool Position::see_ge(Move m, Value threshold) const { - Bitboard occupied; - return see_ge(m, occupied, threshold); -} - /// Position::is_draw() tests whether the position is drawn by 50-move rule /// or by repetition. It does not detect stalemates. diff --git a/src/position.h b/src/position.h index a736f3e677b..780d463cc64 100644 --- a/src/position.h +++ b/src/position.h @@ -143,7 +143,6 @@ class Position { void undo_null_move(); // Static Exchange Evaluation - bool see_ge(Move m, Bitboard& occupied, Value threshold = VALUE_ZERO) const; bool see_ge(Move m, Value threshold = VALUE_ZERO) const; // Accessing hash keys diff --git a/src/search.cpp b/src/search.cpp index 16122315032..1e82203a777 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -989,25 +989,9 @@ namespace { + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 7 < alpha) continue; - Bitboard occupied; // SEE based pruning (~11 Elo) - if (!pos.see_ge(move, occupied, Value(-212) * depth)) - { - // Don't prune the move if opponent King/Queen/Rook gets a discovered attack during or after the exchanges - Bitboard leftEnemies = pos.pieces(~us, KING, QUEEN, ROOK); - Bitboard attacks = 0; - occupied |= to_sq(move); - while (leftEnemies && !attacks) - { - Square sq = pop_lsb(leftEnemies); - attacks = pos.attackers_to(sq, occupied) & pos.pieces(us) & occupied; - // Exclude Queen/Rook(s) which were already threatened before SEE (opponent King can't be in check when it's our turn) - if (attacks && sq != pos.square(~us) && (pos.attackers_to(sq, pos.pieces()) & pos.pieces(us))) - attacks = 0; - } - if (!attacks) + if (!pos.see_ge(move, Value(-205) * depth)) continue; - } } else { From ced0311890add58ab516b9c19608cbd1e1f295ed Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Tue, 30 May 2023 18:24:54 -0400 Subject: [PATCH 1075/1766] Remove static eval threshold for extensions when giving check Passed non-regression STC: https://tests.stockfishchess.org/tests/view/647685d54a36543c4c9f4f2a LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 114688 W: 30701 L: 30571 D: 53416 Ptnml(0-2): 336, 12708, 31136, 12818, 346 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/64774b02b81f005b572de770 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 107310 W: 28920 L: 28796 D: 49594 Ptnml(0-2): 33, 10427, 32621, 10531, 43 closes https://github.com/official-stockfish/Stockfish/pull/4599 bench 2597974 --- src/search.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 1e82203a777..4365b215c40 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1085,7 +1085,8 @@ namespace { } // Check extensions (~1 Elo) - else if ( givesCheck && depth > 8) + else if ( givesCheck + && depth > 9) extension = 1; // Quiet ttMove extensions (~1 Elo) From 8dea070538dcad790de3c5b9720bdbb836a32440 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Thu, 1 Jun 2023 15:24:13 +0300 Subject: [PATCH 1076/1766] Move internal iterative reduction before probcut This patch moves IIR before probcut which allows probcut to be produced at lower depths. Comments in IIR are also slightly updated. Passed STC: https://tests.stockfishchess.org/tests/view/6472d604d29264e4cfa749fd LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 387616 W: 103295 L: 102498 D: 181823 Ptnml(0-2): 976, 42322, 106381, 43187, 942 Passed LTC: https://tests.stockfishchess.org/tests/view/6475eb8c4a36543c4c9f42e8 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 202836 W: 54901 L: 54281 D: 93654 Ptnml(0-2): 85, 19609, 61422, 20205, 97 closes https://github.com/official-stockfish/Stockfish/pull/4597 bench 2551691 --- src/search.cpp | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 4365b215c40..0e82f04e995 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -822,9 +822,24 @@ namespace { } } + // Step 10. If the position doesn't a have ttMove, decrease depth by 2 + // (or by 4 if the TT entry for the current position was hit and the stored depth is greater than or equal to the current depth). + // Use qsearch if depth is equal or below zero (~9 Elo) + if ( PvNode + && !ttMove) + depth -= 2 + 2 * (ss->ttHit && tte->depth() >= depth); + + if (depth <= 0) + return qsearch(pos, ss, alpha, beta); + + if ( cutNode + && depth >= 8 + && !ttMove) + depth -= 2; + probCutBeta = beta + 168 - 61 * improving; - // Step 10. ProbCut (~10 Elo) + // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search returns a value // much above beta, we can (almost) safely prune the previous move. if ( !PvNode @@ -875,20 +890,6 @@ namespace { Eval::NNUE::hint_common_parent_position(pos); } - // Step 11. If the position is not in TT, decrease depth by 2 (or by 4 if the TT entry for the current position was hit and the stored depth is greater than or equal to the current depth). - // Use qsearch if depth is equal or below zero (~9 Elo) - if ( PvNode - && !ttMove) - depth -= 2 + 2 * (ss->ttHit && tte->depth() >= depth); - - if (depth <= 0) - return qsearch(pos, ss, alpha, beta); - - if ( cutNode - && depth >= 8 - && !ttMove) - depth -= 2; - moves_loop: // When in check, search starts here // Step 12. A small Probcut idea, when we are in check (~4 Elo) From b60738e01b5393e6f85bc10d2f257dd0cd26a2f9 Mon Sep 17 00:00:00 2001 From: peregrineshahin Date: Mon, 5 Jun 2023 00:59:31 +0300 Subject: [PATCH 1077/1766] Fix no previous moves on root. guards against no previous move existing if qSearch is called on the root node (i.e. when razoring). Passed Non-regression STC: https://tests.stockfishchess.org/tests/view/647d242d726f6b400e408143 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 53120 W: 14167 L: 13976 D: 24977 Ptnml(0-2): 109, 5597, 14981, 5740, 133 closes https://github.com/official-stockfish/Stockfish/pull/4604 Bench: 2551691 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 0e82f04e995..c7b00766553 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1489,7 +1489,7 @@ namespace { // to search the moves. Because the depth is <= 0 here, only captures, // queen promotions, and other checks (only if depth >= DEPTH_QS_CHECKS) // will be generated. - Square prevSq = (ss-1)->currentMove != MOVE_NULL ? to_sq((ss-1)->currentMove) : SQ_NONE; + Square prevSq = is_ok((ss-1)->currentMove) ? to_sq((ss-1)->currentMove) : SQ_NONE; MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory, contHist, From 295f57829ea189248c8b4afa8d11222d170f78da Mon Sep 17 00:00:00 2001 From: disservin Date: Mon, 17 Apr 2023 22:16:22 +0200 Subject: [PATCH 1078/1766] Add binaries to releases with github actions when a release is made with a tag matching sf_* the binaries will also be uploaded to the release as assets. closes https://github.com/official-stockfish/Stockfish/pull/4596 No functional change. --- .github/workflows/stockfish.yml | 6 +++-- .github/workflows/stockfish_arm_binaries.yml | 8 ++++++ .github/workflows/stockfish_binaries.yml | 27 +++++++++++++------- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index 6345b27cb74..082c65def18 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -1,6 +1,8 @@ name: Stockfish on: push: + tags: + - '*' branches: - master - tools @@ -17,8 +19,8 @@ jobs: Compiles: uses: ./.github/workflows/stockfish_compile_test.yml Binaries: - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/master' || (startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag') uses: ./.github/workflows/stockfish_binaries.yml ARM_Binaries: - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/master' || (startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag') uses: ./.github/workflows/stockfish_arm_binaries.yml diff --git a/.github/workflows/stockfish_arm_binaries.yml b/.github/workflows/stockfish_arm_binaries.yml index e088c441925..9a4734eeec6 100644 --- a/.github/workflows/stockfish_arm_binaries.yml +++ b/.github/workflows/stockfish_arm_binaries.yml @@ -115,6 +115,8 @@ jobs: cp "Top CPU Contributors.txt" stockfish/ cp Copying.txt stockfish/ cp AUTHORS stockfish/ + cp CITATION.cff stockfish/ + cp README.md stockfish/ tar -cvf stockfish-android-$BINARY.tar stockfish - name: Upload binaries @@ -122,3 +124,9 @@ jobs: with: name: stockfish-android-${{ matrix.binaries }} path: stockfish-android-${{ matrix.binaries }}.tar + + - name: Release + if: startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag' + uses: softprops/action-gh-release@v1 + with: + files: stockfish-android-${{ matrix.binaries }}.tar \ No newline at end of file diff --git a/.github/workflows/stockfish_binaries.yml b/.github/workflows/stockfish_binaries.yml index b22897cf692..86449b97a4b 100644 --- a/.github/workflows/stockfish_binaries.yml +++ b/.github/workflows/stockfish_binaries.yml @@ -9,23 +9,26 @@ jobs: COMPILER: ${{ matrix.config.compiler }} COMP: ${{ matrix.config.comp }} EXT: ${{ matrix.config.ext }} - OS: ${{ matrix.config.os }} + NAME: ${{ matrix.config.simple_name }} BINARY: ${{ matrix.binaries }} strategy: matrix: config: - name: Ubuntu 20.04 GCC os: ubuntu-20.04 + simple_name: ubuntu compiler: g++ comp: gcc shell: bash {0} - name: MacOS 12 Apple Clang os: macos-12 + simple_name: macos compiler: clang++ comp: clang shell: bash {0} - name: Windows 2022 Mingw-w64 GCC x86_64 os: windows-2022 + simple_name: windows compiler: g++ comp: mingw msys_sys: mingw64 @@ -75,19 +78,17 @@ jobs: - name: Compile ${{ matrix.binaries }} build run: | - make clean make -j2 profile-build ARCH=$BINARY COMP=$COMP make strip ARCH=$BINARY COMP=$COMP - mv ./stockfish$EXT ../stockfish-$OS-$BINARY$EXT + mv ./stockfish$EXT ../stockfish-$NAME-$BINARY$EXT - name: Remove non src files - run: rm -f *.o .depend *.nnue + run: git clean -fx - name: Download wiki run: | git clone https://github.com/official-stockfish/Stockfish.wiki.git ../wiki - cd ../wiki - rm -rf .git + rm -rf ../wiki/.git - name: Create tar archive. run: | @@ -95,14 +96,22 @@ jobs: mkdir stockfish cp -r wiki stockfish/ cp -r src stockfish/ - cp stockfish-$OS-$BINARY$EXT stockfish/ + cp stockfish-$NAME-$BINARY$EXT stockfish/ cp "Top CPU Contributors.txt" stockfish/ cp Copying.txt stockfish/ cp AUTHORS stockfish/ - tar -cvf stockfish-$OS-$BINARY.tar stockfish + cp CITATION.cff stockfish/ + cp README.md stockfish/ + tar -cvf stockfish-$NAME-$BINARY.tar stockfish - name: Upload binaries uses: actions/upload-artifact@v3 with: name: stockfish-${{ matrix.config.os }}-${{ matrix.binaries }} - path: stockfish-${{ matrix.config.os }}-${{ matrix.binaries }}.tar + path: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.tar + + - name: Release + if: startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag' + uses: softprops/action-gh-release@v1 + with: + files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.tar From 373359b44d0947cce2628a9a8c9b432a458615a8 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Sun, 4 Jun 2023 01:56:11 -0400 Subject: [PATCH 1079/1766] Update default net to nn-0dd1cebea573.nnue Created by retraining an earlier epoch of the experiment leading to the first SFNNv6 net on a more-randomized version of the nn-e1fb1ade4432.nnue dataset mixed with unfiltered T80 apr2023 data. Trained using early-fen-skipping 28 and max-epoch 960. The trainer settings and epochs used in the 5-step training sequence leading here were: 1. train from scratch for 400 epochs, lambda 1.0, constant LR 9.75e-4, T79T77-filter-v6-dd.min.binpack 2. retrain ep379, max-epoch 800, end-lambda 0.75, T60T70wIsRightFarseerT60T74T75T76.binpack 3. retrain ep679, max-epoch 800, end-lambda 0.75, skip 28, nn-e1fb1ade4432 dataset 4. retrain ep799, max-epoch 800, end-lambda 0.7, skip 28, nn-e1fb1ade4432 dataset 5. retrain ep439, max-epoch 960, end-lambda 0.7, skip 28, shuffled nn-e1fb1ade4432 + T80 apr2023 This net was epoch 559 of the final (step 5) retraining: ```bash python3 easy_train.py \ --experiment-name L1-1536-Re4-leela96-dfrc99-T60novdec-v2-T80juntonovjanfebT79aprmayT78jantosepT77dec-v6dd-T80apr-shuffled-sk28 \ --training-dataset /data/leela96-dfrc99-T60novdec-v2-T80juntonovjanfebT79aprmayT78jantosepT77dec-v6dd-T80apr.binpack \ --nnue-pytorch-branch linrock/nnue-pytorch/misc-fixes-L1-1536 \ --early-fen-skipping 28 \ --start-lambda 1.0 \ --end-lambda 0.7 \ --max_epoch 960 \ --start-from-engine-test-net False \ --start-from-model /data/L1-1536-Re3-nn-epoch439.nnue \ --engine-test-branch linrock/Stockfish/L1-1536 \ --lr 4.375e-4 \ --gamma 0.995 \ --tui False \ --seed $RANDOM \ --gpus "0," ``` During data preparation, most binpacks were unminimized by removing positions with score 32002 (`VALUE_NONE`). This makes the tradeoff of increasing dataset filesize on disk to increase the randomness of positions in interleaved datasets. The code used for unminimizing is at: https://github.com/linrock/Stockfish/tree/tools-unminify For preparing the dataset used in this experiment: ```bash python3 interleave_binpacks.py \ leela96-filt-v2.binpack \ dfrc99-16tb7p-eval-filt-v2.binpack \ filt-v6-dd-min/test60-novdec2021-12tb7p-filter-v6-dd.min-mar2023.unmin.binpack \ filt-v6-dd-min/test80-aug2022-16tb7p-filter-v6-dd.min-mar2023.unmin.binpack \ filt-v6-dd-min/test80-sep2022-16tb7p-filter-v6-dd.min-mar2023.unmin.binpack \ filt-v6-dd-min/test80-jun2022-16tb7p-filter-v6-dd.min-mar2023.unmin.binpack \ filt-v6-dd/test80-jul2022-16tb7p-filter-v6-dd.binpack \ filt-v6-dd/test80-oct2022-16tb7p-filter-v6-dd.binpack \ filt-v6-dd/test80-nov2022-16tb7p-filter-v6-dd.binpack \ filt-v6-dd-min/test80-jan2023-3of3-16tb7p-filter-v6-dd.min-mar2023.unmin.binpack \ filt-v6-dd-min/test80-feb2023-16tb7p-filter-v6-dd.min-mar2023.unmin.binpack \ filt-v6-dd/test79-apr2022-16tb7p-filter-v6-dd.binpack \ filt-v6-dd/test79-may2022-16tb7p-filter-v6-dd.binpack \ filt-v6-dd-min/test78-jantomay2022-16tb7p-filter-v6-dd.min-mar2023.unmin.binpack \ filt-v6-dd/test78-juntosep2022-16tb7p-filter-v6-dd.binpack \ filt-v6-dd/test77-dec2021-16tb7p-filter-v6-dd.binpack \ test80-apr2023-2tb7p.binpack \ /data/leela96-dfrc99-T60novdec-v2-T80juntonovjanfebT79aprmayT78jantosepT77dec-v6dd-T80apr.binpack ``` T80 apr2023 data was converted using lc0-rescorer with ~2tb of tablebases and can be found at: https://robotmoon.com/nnue-training-data/ Local elo at 25k nodes per move vs. nn-e1fb1ade4432.nnue (L1 size 1024): nn-epoch559.nnue : 25.7 +/- 1.6 Passed STC: https://tests.stockfishchess.org/tests/view/647cd3b87cf638f0f53f9cbb LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 59200 W: 16000 L: 15660 D: 27540 Ptnml(0-2): 159, 6488, 15996, 6768, 189 Passed LTC: https://tests.stockfishchess.org/tests/view/647d58de726f6b400e4085d8 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 58800 W: 16002 L: 15657 D: 27141 Ptnml(0-2): 44, 5607, 17748, 5962, 39 closes https://github.com/official-stockfish/Stockfish/pull/4606 bench 2141197 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 0990111cf6d..dcbe6b3c9bc 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-8d69132723e2.nnue" + #define EvalFileDefaultName "nn-0dd1cebea573.nnue" namespace NNUE { From 54ad986768eec524aeab721713ea2009931b51b3 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Mon, 5 Jun 2023 01:08:38 -0400 Subject: [PATCH 1080/1766] Remove optimism multiplier in nnue eval calculation The same formula had passed SPRT against an earlier version of master. Passed non-regression STC vs. d99942f: https://tests.stockfishchess.org/tests/view/6478e76654dd118e1d98f72e LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 118720 W: 31402 L: 31277 D: 56041 Ptnml(0-2): 301, 13148, 32344, 13259, 308 Passed non-regression LTC vs. d99942f: https://tests.stockfishchess.org/tests/view/647a22c154dd118e1d991146 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 74286 W: 20019 L: 19863 D: 34404 Ptnml(0-2): 31, 7189, 22540, 7359, 24 The earlier patch had conflicted with a faster SPRT passer, so this was tested again after rebasing on latest master. Passed non-regression STC: https://tests.stockfishchess.org/tests/view/647d6e46726f6b400e408790 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 166176 W: 44309 L: 44234 D: 77633 Ptnml(0-2): 461, 18252, 45557, 18387, 431 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/647eb00ba268d1bc11255e7b LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 28170 W: 7713 L: 7513 D: 12944 Ptnml(0-2): 14, 2609, 8635, 2817, 10 closes https://github.com/official-stockfish/Stockfish/pull/4607 bench 2503095 --- src/evaluate.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index bf6dd69a950..35d054270ee 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1071,8 +1071,8 @@ Value Eval::evaluate(const Position& pos) { Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); // Blend optimism with nnue complexity and (semi)classical complexity - optimism += 25 * optimism * (nnueComplexity + abs(psq - nnue)) / 16384; - v = (nnue * (945 + npm) + optimism * (174 + npm)) / 1024; + optimism += optimism * (nnueComplexity + abs(psq - nnue)) / 512; + v = (nnue * (945 + npm) + optimism * (150 + npm)) / 1024; } // Damp down the evaluation linearly when shuffling From a9a6915e0839d3f3f54659c86f15868a7db0e386 Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Mon, 5 Jun 2023 07:59:56 +0800 Subject: [PATCH 1081/1766] Simplify multiplier for improvement This simplifies a `* 99 / 1300` term into just `/ 13`. Passed non-regression STC: LLR: 2.92 (-2.94,2.94) <-1.75,0.25> Total: 58816 W: 15727 L: 15540 D: 27549 Ptnml(0-2): 149, 6370, 16203, 6517, 169 https://tests.stockfishchess.org/tests/view/647d25e4726f6b400e408165 Passed non-regression LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 154386 W: 41749 L: 41669 D: 70968 Ptnml(0-2): 94, 14992, 46956, 15042, 109 https://tests.stockfishchess.org/tests/view/647d9b3c726f6b400e408b2a closes https://github.com/official-stockfish/Stockfish/pull/4608 Bench: 2511327 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index c7b00766553..593fdc722e0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -779,7 +779,7 @@ namespace { && (ss-1)->statScore < 17329 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 21 * depth - improvement * 99 / 1300 + 258 + && ss->staticEval >= beta - 21 * depth - improvement / 13 + 258 && !excludedMove && pos.non_pawn_material(us) && (ss->ply >= thisThread->nmpMinPly)) From e1dd005583bd6c2aaf58468efc5de86a3936380a Mon Sep 17 00:00:00 2001 From: Guenther Demetz Date: Wed, 7 Jun 2023 09:01:05 +0200 Subject: [PATCH 1082/1766] Reintroduce SEE verification against discovered attacks Reintroduces https://github.com/official-stockfish/Stockfish/pull/4453 along with https://github.com/official-stockfish/Stockfish/pull/4469 Leaving out https://github.com/official-stockfish/Stockfish/pull/4533 https://github.com/official-stockfish/Stockfish/pull/4572 Passed STC: https://tests.stockfishchess.org/tests/view/647d8c37726f6b400e408a0a LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 143168 W: 38346 L: 37892 D: 66930 Ptnml(0-2): 352, 15672, 39164, 15962, 434 Passed LTC: https://tests.stockfishchess.org/tests/view/647ee8c528c4431bcb58e432 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 71538 W: 19560 L: 19190 D: 32788 Ptnml(0-2): 49, 6905, 21499, 7259, 57 closes https://github.com/official-stockfish/Stockfish/pull/4609 bench: 2595430 --- src/position.cpp | 19 ++++++++++++------- src/position.h | 1 + src/search.cpp | 23 +++++++++++++++++++++-- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index af274d3f2c6..2a9d798ff7d 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1061,7 +1061,7 @@ Key Position::key_after(Move m) const { /// SEE value of move is greater or equal to the given threshold. We'll use an /// algorithm similar to alpha-beta pruning with a null window. -bool Position::see_ge(Move m, Value threshold) const { +bool Position::see_ge(Move m, Bitboard& occupied, Value threshold) const { assert(is_ok(m)); @@ -1080,7 +1080,7 @@ bool Position::see_ge(Move m, Value threshold) const { return true; assert(color_of(piece_on(from)) == sideToMove); - Bitboard occupied = pieces() ^ from ^ to; // xoring to is important for pinned piece logic + occupied = pieces() ^ from ^ to; // xoring to is important for pinned piece logic Color stm = sideToMove; Bitboard attackers = attackers_to(to, occupied); Bitboard stmAttackers, bb; @@ -1111,43 +1111,43 @@ bool Position::see_ge(Move m, Value threshold) const { // the bitboard 'attackers' any X-ray attackers behind it. if ((bb = stmAttackers & pieces(PAWN))) { + occupied ^= least_significant_square_bb(bb); if ((swap = PawnValueMg - swap) < res) break; - occupied ^= least_significant_square_bb(bb); attackers |= attacks_bb(to, occupied) & pieces(BISHOP, QUEEN); } else if ((bb = stmAttackers & pieces(KNIGHT))) { + occupied ^= least_significant_square_bb(bb); if ((swap = KnightValueMg - swap) < res) break; - occupied ^= least_significant_square_bb(bb); } else if ((bb = stmAttackers & pieces(BISHOP))) { + occupied ^= least_significant_square_bb(bb); if ((swap = BishopValueMg - swap) < res) break; - occupied ^= least_significant_square_bb(bb); attackers |= attacks_bb(to, occupied) & pieces(BISHOP, QUEEN); } else if ((bb = stmAttackers & pieces(ROOK))) { + occupied ^= least_significant_square_bb(bb); if ((swap = RookValueMg - swap) < res) break; - occupied ^= least_significant_square_bb(bb); attackers |= attacks_bb(to, occupied) & pieces(ROOK, QUEEN); } else if ((bb = stmAttackers & pieces(QUEEN))) { + occupied ^= least_significant_square_bb(bb); if ((swap = QueenValueMg - swap) < res) break; - occupied ^= least_significant_square_bb(bb); attackers |= (attacks_bb(to, occupied) & pieces(BISHOP, QUEEN)) | (attacks_bb(to, occupied) & pieces(ROOK , QUEEN)); @@ -1162,6 +1162,11 @@ bool Position::see_ge(Move m, Value threshold) const { return bool(res); } +bool Position::see_ge(Move m, Value threshold) const { + Bitboard occupied; + return see_ge(m, occupied, threshold); +} + /// Position::is_draw() tests whether the position is drawn by 50-move rule /// or by repetition. It does not detect stalemates. diff --git a/src/position.h b/src/position.h index 780d463cc64..2e6014dbe6e 100644 --- a/src/position.h +++ b/src/position.h @@ -144,6 +144,7 @@ class Position { // Static Exchange Evaluation bool see_ge(Move m, Value threshold = VALUE_ZERO) const; + bool see_ge(Move m, Bitboard& occupied, Value threshold = VALUE_ZERO) const; // Accessing hash keys Key key() const; diff --git a/src/search.cpp b/src/search.cpp index 593fdc722e0..b2c2344ac0a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -990,9 +990,28 @@ namespace { + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 7 < alpha) continue; + Bitboard occupied; // SEE based pruning (~11 Elo) - if (!pos.see_ge(move, Value(-205) * depth)) - continue; + if (!pos.see_ge(move, occupied, Value(-205) * depth)) + { + if (depth < 2 - capture) + continue; + // Don't prune the move if opponent Queen/Rook is under discovered attack after the exchanges + // Don't prune the move if opponent King is under discovered attack after or during the exchanges + Bitboard leftEnemies = (pos.pieces(~us, KING, QUEEN, ROOK)) & occupied; + Bitboard attacks = 0; + occupied |= to_sq(move); + while (leftEnemies && !attacks) + { + Square sq = pop_lsb(leftEnemies); + attacks |= pos.attackers_to(sq, occupied) & pos.pieces(us) & occupied; + // don't consider pieces which were already threatened/hanging before SEE exchanges + if (attacks && (sq != pos.square(~us) && (pos.attackers_to(sq, pos.pieces()) & pos.pieces(us)))) + attacks = 0; + } + if (!attacks) + continue; + } } else { From 932f5a2d657c846c282adcf2051faef7ca17ae15 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Wed, 7 Jun 2023 14:57:17 -0400 Subject: [PATCH 1083/1766] Update default net to nn-ea57bea57e32.nnue Created by retraining an earlier epoch (ep659) of the experiment that led to the first SFNNv6 net: - First retrained on the nn-0dd1cebea573 dataset - Then retrained with skip 20 on a smaller dataset containing unfiltered Leela data - And then retrained again with skip 27 on the nn-0dd1cebea573 dataset The equivalent 7-step training sequence from scratch that led here was: 1. max-epoch 400, lambda 1.0, constant LR 9.75e-4, T79T77-filter-v6-dd.min.binpack ep379 chosen for retraining in step2 2. max-epoch 800, end-lambda 0.75, T60T70wIsRightFarseerT60T74T75T76.binpack ep679 chosen for retraining in step3 3. max-epoch 800, end-lambda 0.75, skip 28, nn-e1fb1ade4432 dataset ep799 chosen for retraining in step4 4. max-epoch 800, end-lambda 0.7, skip 28, nn-e1fb1ade4432 dataset ep759 became nn-8d69132723e2.nnue (first SFNNv6 net) ep659 chosen for retraining in step5 5. max-epoch 800, end-lambda 0.7, skip 28, nn-0dd1cebea573 dataset ep759 chosen for retraining in step6 6. max-epoch 800, end-lambda 0.7, skip 20, leela-dfrc-v2-T77decT78janfebT79aprT80apr.binpack ep639 chosen for retraining in step7 7. max-epoch 800, end-lambda 0.7, skip 27, nn-0dd1cebea573 dataset ep619 became nn-ea57bea57e32.nnue For the last retraining (step7): python3 easy_train.py --experiment-name L1-1536-Re6-masterShuffled-ep639-sk27-Re5-leela-dfrc-v2-T77toT80small-Re4-masterShuffled-ep659-Re3-sameAs-Re2-leela96-dfrc99-16t-v2-T60novdecT80juntonovjanfebT79aprmayT78jantosepT77dec-v6dd-Re1-LeelaFarseer-new-T77T79 \ --training-dataset /data/leela96-dfrc99-T60novdec-v2-T80juntonovjanfebT79aprmayT78jantosepT77dec-v6dd-T80apr.binpack \ --nnue-pytorch-branch linrock/nnue-pytorch/misc-fixes-L1-1536 \ --early-fen-skipping 27 \ --start-lambda 1.0 \ --end-lambda 0.7 \ --max_epoch 800 \ --start-from-engine-test-net False \ --start-from-model /data/L1-1536-Re5-leela-dfrc-v2-T77toT80small-epoch639.nnue \ --lr 4.375e-4 \ --gamma 0.995 \ --tui False \ --seed $RANDOM \ --gpus "0," For preparing the step6 leela-dfrc-v2-T77decT78janfebT79aprT80apr.binpack dataset: python3 interleave_binpacks.py \ leela96-filt-v2.binpack \ dfrc99-16tb7p-eval-filt-v2.binpack \ test77-dec2021-16tb7p.no-db.min-mar2023.binpack \ test78-janfeb2022-16tb7p.no-db.min-mar2023.binpack \ test79-apr2022-16tb7p-filter-v6-dd.binpack \ test80-apr2022-16tb7p.no-db.min-mar2023.binpack \ /data/leela-dfrc-v2-T77decT78janfebT79aprT80apr.binpack The unfiltered Leela data used for the step6 dataset can be found at: https://robotmoon.com/nnue-training-data Local elo at 25k nodes per move: nn-epoch619.nnue : 2.3 +/- 1.9 Passed STC: https://tests.stockfishchess.org/tests/view/6480d43c6e6ce8d9fc6d7cc8 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 40992 W: 11017 L: 10706 D: 19269 Ptnml(0-2): 113, 4400, 11170, 4689, 124 Passed LTC: https://tests.stockfishchess.org/tests/view/648119ac6e6ce8d9fc6d8208 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 129174 W: 35059 L: 34579 D: 59536 Ptnml(0-2): 66, 12548, 38868, 13050, 55 closes https://github.com/official-stockfish/Stockfish/pull/4611 bench: 2370027 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index dcbe6b3c9bc..fc852c8d751 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-0dd1cebea573.nnue" + #define EvalFileDefaultName "nn-ea57bea57e32.nnue" namespace NNUE { From b7ee7290b552b21352491ec7f390565ff4748647 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 12 Jun 2023 09:51:28 +0200 Subject: [PATCH 1084/1766] Add network export to CI verify the network written by export_net matches the original closes https://github.com/official-stockfish/Stockfish/pull/4613 No functional change --- tests/instrumented.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/instrumented.sh b/tests/instrumented.sh index e9455eabddc..1b37c7a8b75 100755 --- a/tests/instrumented.sh +++ b/tests/instrumented.sh @@ -70,7 +70,8 @@ for args in "eval" \ "go depth 10" \ "go movetime 1000" \ "go wtime 8000 btime 8000 winc 500 binc 500" \ - "bench 128 $threads 8 default depth" + "bench 128 $threads 8 default depth" \ + "export_net verify.nnue" do echo "$prefix $exeprefix ./stockfish $args $postfix" @@ -78,6 +79,11 @@ do done +# verify the generated net equals the base net +network=`./stockfish uci | grep 'option name EvalFile type string default' | awk '{print $NF}'` +echo "Comparing $network to the written verify.nnue" +diff $network verify.nnue + # more general testing, following an uci protocol exchange cat << EOF > game.exp set timeout 240 From 38e61663d836e062af0bc002814ad5149c4b7729 Mon Sep 17 00:00:00 2001 From: AndrovT <31534597+AndrovT@users.noreply.github.com> Date: Sun, 11 Jun 2023 03:24:04 +0200 Subject: [PATCH 1085/1766] Use block sparse input for the first layer. Use block sparse input for the first fully connected layer on architectures with at least SSSE3. Depending on the CPU architecture, this yields a speedup of up to 10%, e.g. ``` Result of 100 runs of 'bench 16 1 13 default depth NNUE' base (...ockfish-base) = 959345 +/- 7477 test (...ckfish-patch) = 1054340 +/- 9640 diff = +94995 +/- 3999 speedup = +0.0990 P(speedup > 0) = 1.0000 CPU: 8 x AMD Ryzen 7 5700U with Radeon Graphics Hyperthreading: on ``` Passed STC: https://tests.stockfishchess.org/tests/view/6485aa0965ffe077ca12409c LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 8864 W: 2479 L: 2223 D: 4162 Ptnml(0-2): 13, 829, 2504, 1061, 25 This commit includes a net with reordered weights, to increase the likelihood of block sparse inputs, but otherwise equivalent to the previous master net (nn-ea57bea57e32.nnue). Activation data collected with https://github.com/AndrovT/Stockfish/tree/log-activations, running bench 16 1 13 varied_1000.epd depth NNUE on this data. Net parameters permuted with https://gist.github.com/AndrovT/9e3fbaebb7082734dc84d27e02094cb3. closes https://github.com/official-stockfish/Stockfish/pull/4612 No functional change --- AUTHORS | 1 + src/evaluate.h | 2 +- .../layers/affine_transform_sparse_input.h | 286 ++++++++++++++++++ src/nnue/nnue_architecture.h | 3 +- 4 files changed, 290 insertions(+), 2 deletions(-) create mode 100644 src/nnue/layers/affine_transform_sparse_input.h diff --git a/AUTHORS b/AUTHORS index d01d23cd7e9..63b862ced05 100644 --- a/AUTHORS +++ b/AUTHORS @@ -159,6 +159,7 @@ Norman Schmidt (FireFather) notruck Ofek Shochat (OfekShochat, ghostway) Ondrej Mosnáček (WOnder93) +Ondřej Mišina (AndrovT) Oskar Werkelin Ahlin Pablo Vazquez Panthee diff --git a/src/evaluate.h b/src/evaluate.h index fc852c8d751..94cd42cc67a 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-ea57bea57e32.nnue" + #define EvalFileDefaultName "nn-fdc1d0fe6455.nnue" namespace NNUE { diff --git a/src/nnue/layers/affine_transform_sparse_input.h b/src/nnue/layers/affine_transform_sparse_input.h new file mode 100644 index 00000000000..00b17c19f12 --- /dev/null +++ b/src/nnue/layers/affine_transform_sparse_input.h @@ -0,0 +1,286 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// Definition of layer AffineTransformSparseInput of NNUE evaluation function + +#ifndef NNUE_LAYERS_AFFINE_TRANSFORM_SPARSE_INPUT_H_INCLUDED +#define NNUE_LAYERS_AFFINE_TRANSFORM_SPARSE_INPUT_H_INCLUDED + +#include +#include +#include +#include +#include "../nnue_common.h" +#include "affine_transform.h" +#include "simd.h" + +/* + This file contains the definition for a fully connected layer (aka affine transform) with block sparse input. +*/ + +namespace Stockfish::Eval::NNUE::Layers { +#if defined(__GNUC__) // GCC, Clang, ICC + + static inline IndexType lsb_(std::uint32_t b) { + assert(b); + return IndexType(__builtin_ctzl(b)); + } + +#elif defined(_MSC_VER) // MSVC + + static inline IndexType lsb_(std::uint32_t b) { + assert(b); + unsigned long idx; + _BitScanForward(&idx, b); + return (IndexType) idx; + } + +#else // Compiler is neither GCC nor MSVC compatible + +#error "Compiler not supported." + +#endif + + +#if defined(USE_SSSE3) + alignas(CacheLineSize) static inline const std::array, 256> lookup_indices = [](){ + std::array, 256> v{}; + for (int i = 0; i < 256; ++i) + { + int j = i; + int k = 0; + while(j) + { + const IndexType lsbIndex = lsb_(std::uint32_t(j)); + j &= j - 1; + v[i][k] = lsbIndex; + ++k; + } + } + return v; + }(); + alignas(CacheLineSize) static inline const std::array lookup_count = [](){ + std::array v; + for (int i = 0; i < 256; ++i) + { + int j = i; + int k = 0; + while(j) + { + j &= j - 1; + ++k; + } + v[i] = k; + } + return v; + }(); + + // Find indices of nonzero numbers in an int32_t array + template + void find_nnz(const std::int32_t* input, std::uint16_t* out, IndexType& count_out) { +#if defined (USE_AVX512) + using vec_t = __m512i; + #define vec_nnz(a) _mm512_cmpgt_epi32_mask(a, _mm512_setzero_si512()) +#elif defined (USE_AVX2) + using vec_t = __m256i; + #define vec_nnz(a) _mm256_movemask_ps((__m256)_mm256_cmpgt_epi32(a, _mm256_setzero_si256())) +#elif defined (USE_SSSE3) + using vec_t = __m128i; + #define vec_nnz(a) _mm_movemask_ps((__m128)_mm_cmpgt_epi32(a, _mm_setzero_si128())) +#endif + constexpr IndexType InputSimdWidth = sizeof(vec_t) / sizeof(std::int32_t); + // Inputs are processed InputSimdWidth at a time and outputs are processed 8 at a time so we process in chunks of max(InputSimdWidth, 8) + constexpr IndexType ChunkSize = std::max(InputSimdWidth, 8); + constexpr IndexType NumChunks = InputDimensions / ChunkSize; + constexpr IndexType InputsPerChunk = ChunkSize / InputSimdWidth; + constexpr IndexType OutputsPerChunk = ChunkSize / 8; + + const auto inputVector = reinterpret_cast(input); + IndexType count = 0; + __m128i base = _mm_set1_epi16(0); + __m128i increment = _mm_set1_epi16(8); + for (IndexType i = 0; i < NumChunks; ++i) + { + // bitmask of nonzero values in this chunk + unsigned nnz = 0; + for (IndexType j = 0; j < InputsPerChunk; ++j) + { + const vec_t inputChunk = inputVector[i * InputsPerChunk + j]; + nnz |= (unsigned)vec_nnz(inputChunk) << (j * InputSimdWidth); + } + for (IndexType j = 0; j < OutputsPerChunk; ++j) + { + const auto lookup = (nnz >> (j * 8)) & 0xFF; + const auto offsets = _mm_loadu_si128(reinterpret_cast(&lookup_indices[lookup])); + _mm_storeu_si128(reinterpret_cast<__m128i*>(out + count), _mm_add_epi16(base, offsets)); + count += lookup_count[lookup]; + base = _mm_add_epi16(base, increment); + } + } + count_out = count; + } +# undef vec_nnz +#endif + + // Sparse input implementation + template + class AffineTransformSparseInput { + public: + // Input/output type + // Input/output type + using InputType = std::uint8_t; + using OutputType = std::int32_t; + + // Number of input/output dimensions + static constexpr IndexType InputDimensions = InDims; + static constexpr IndexType OutputDimensions = OutDims; + + static_assert(OutputDimensions % 16 == 0, "Only implemented for OutputDimensions divisible by 16."); + + static constexpr IndexType PaddedInputDimensions = + ceil_to_multiple(InputDimensions, MaxSimdWidth); + static constexpr IndexType PaddedOutputDimensions = + ceil_to_multiple(OutputDimensions, MaxSimdWidth); + +#if defined (USE_SSSE3) + static constexpr IndexType ChunkSize = 4; +#else + static constexpr IndexType ChunkSize = 1; +#endif + + using OutputBuffer = OutputType[PaddedOutputDimensions]; + + // Hash value embedded in the evaluation file + static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) { + std::uint32_t hashValue = 0xCC03DAE4u; + hashValue += OutputDimensions; + hashValue ^= prevHash >> 1; + hashValue ^= prevHash << 31; + return hashValue; + } + + static IndexType get_weight_index_scrambled(IndexType i) + { + return + (i / ChunkSize) % (PaddedInputDimensions / ChunkSize) * OutputDimensions * ChunkSize + + i / PaddedInputDimensions * ChunkSize + + i % ChunkSize; + } + + static IndexType get_weight_index(IndexType i) + { +#if defined (USE_SSSE3) + return get_weight_index_scrambled(i); +#else + return i; +#endif + } + + // Read network parameters + bool read_parameters(std::istream& stream) { + read_little_endian(stream, biases, OutputDimensions); + for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) + weights[get_weight_index(i)] = read_little_endian(stream); + + return !stream.fail(); + } + + // Write network parameters + bool write_parameters(std::ostream& stream) const { + write_little_endian(stream, biases, OutputDimensions); + + for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) + write_little_endian(stream, weights[get_weight_index(i)]); + + return !stream.fail(); + } + // Forward propagation + const OutputType* propagate( + const InputType* input, OutputType* output) const { + +#if defined (USE_SSSE3) +#if defined (USE_AVX512) + using vec_t = __m512i; + #define vec_setzero _mm512_setzero_si512 + #define vec_set_32 _mm512_set1_epi32 + #define vec_add_dpbusd_32 Simd::m512_add_dpbusd_epi32 +#elif defined (USE_AVX2) + using vec_t = __m256i; + #define vec_setzero _mm256_setzero_si256 + #define vec_set_32 _mm256_set1_epi32 + #define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32 +#elif defined (USE_SSSE3) + using vec_t = __m128i; + #define vec_setzero _mm_setzero_si128 + #define vec_set_32 _mm_set1_epi32 + #define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32 +#endif + static constexpr IndexType OutputSimdWidth = sizeof(vec_t) / sizeof(OutputType); + + constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 8) / ChunkSize; + constexpr IndexType NumRegs = OutputDimensions / OutputSimdWidth; + std::uint16_t nnz[NumChunks]; + IndexType count; + + const auto input32 = reinterpret_cast(input); + + // Find indices of nonzero 32bit blocks + find_nnz(input32, nnz, count); + + const vec_t* biasvec = reinterpret_cast(biases); + vec_t acc[NumRegs]; + for (IndexType k = 0; k < NumRegs; ++k) + acc[k] = biasvec[k]; + + for (IndexType j = 0; j < count; ++j) + { + const auto i = nnz[j]; + const vec_t in = vec_set_32(input32[i]); + const auto col = reinterpret_cast(&weights[i * OutputDimensions * ChunkSize]); + for (IndexType k = 0; k < NumRegs; ++k) + vec_add_dpbusd_32(acc[k], in, col[k]); + } + + vec_t* outptr = reinterpret_cast(output); + for (IndexType k = 0; k < NumRegs; ++k) + outptr[k] = acc[k]; +# undef vec_setzero +# undef vec_set_32 +# undef vec_add_dpbusd_32 +#else + // Use dense implementation for the other architectures. + affine_transform_non_ssse3< + InputDimensions, + PaddedInputDimensions, + OutputDimensions>(output, weights, biases, input); +#endif + + return output; + } + + private: + using BiasType = OutputType; + using WeightType = std::int8_t; + + alignas(CacheLineSize) BiasType biases[OutputDimensions]; + alignas(CacheLineSize) WeightType weights[OutputDimensions * PaddedInputDimensions]; + }; + +} // namespace Stockfish::Eval::NNUE::Layers + +#endif // #ifndef NNUE_LAYERS_AFFINE_TRANSFORM_SPARSE_INPUT_H_INCLUDED diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index d10434f34b8..413dbb3dcd7 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -27,6 +27,7 @@ #include "features/half_ka_v2_hm.h" +#include "layers/affine_transform_sparse_input.h" #include "layers/affine_transform.h" #include "layers/clipped_relu.h" #include "layers/sqr_clipped_relu.h" @@ -48,7 +49,7 @@ struct Network static constexpr int FC_0_OUTPUTS = 15; static constexpr int FC_1_OUTPUTS = 32; - Layers::AffineTransform fc_0; + Layers::AffineTransformSparseInput fc_0; Layers::SqrClippedReLU ac_sqr_0; Layers::ClippedReLU ac_0; Layers::AffineTransform fc_1; From ece90bca9c513fe7b252da1521fc5ff701396f61 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Mon, 12 Jun 2023 22:13:08 +0300 Subject: [PATCH 1086/1766] Improve comments Fix comments for IIR, also document non-linear scaling in extensions. Also add explicitly the bench, to fix an issue after the last commit. closes https://github.com/official-stockfish/Stockfish/pull/4614 bench 2370027 --- src/search.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index b2c2344ac0a..7ee6d43963f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -822,7 +822,7 @@ namespace { } } - // Step 10. If the position doesn't a have ttMove, decrease depth by 2 + // Step 10. If the position doesn't have a ttMove, decrease depth by 2 // (or by 4 if the TT entry for the current position was hit and the stored depth is greater than or equal to the current depth). // Use qsearch if depth is equal or below zero (~9 Elo) if ( PvNode @@ -1052,6 +1052,9 @@ namespace { // 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. + // Depth margin and singularBeta margin are known for having non-linear scaling. + // Their values are optimized to time controls of 180+1.8 and longer + // so changing them requires tests at this type of time controls. if ( !rootNode && depth >= 4 - (thisThread->completedDepth > 22) + 2 * (PvNode && tte->is_pv()) && move == ttMove From 92c949e12e028cb4556de2786a77f2aec178d59f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicolet?= Date: Mon, 12 Jun 2023 23:26:42 +0200 Subject: [PATCH 1087/1766] Clean-up code indentation in qsearch closes https://github.com/official-stockfish/Stockfish/pull/4615 No functional change --- src/search.cpp | 152 +++++++++++++++++++++++++------------------------ 1 file changed, 77 insertions(+), 75 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 7ee6d43963f..d3b5642a236 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1481,10 +1481,11 @@ namespace { bestValue = ttValue; } else + { // In case of null move search use previous static eval with a different sign - ss->staticEval = bestValue = - (ss-1)->currentMove != MOVE_NULL ? evaluate(pos) - : -(ss-1)->staticEval; + ss->staticEval = bestValue = (ss-1)->currentMove != MOVE_NULL ? evaluate(pos) + : -(ss-1)->staticEval; + } // Stand pat. Return immediately if static value is at least beta if (bestValue >= beta) @@ -1523,97 +1524,98 @@ namespace { // or a beta cutoff occurs. while ((move = mp.next_move()) != MOVE_NONE) { - assert(is_ok(move)); + assert(is_ok(move)); - // Check for legality - if (!pos.legal(move)) - continue; + // Check for legality + if (!pos.legal(move)) + continue; - givesCheck = pos.gives_check(move); - capture = pos.capture_stage(move); + givesCheck = pos.gives_check(move); + capture = pos.capture_stage(move); - moveCount++; + moveCount++; - // Step 6. Pruning. - if (bestValue > VALUE_TB_LOSS_IN_MAX_PLY) - { - // Futility pruning and moveCount pruning (~10 Elo) - if ( !givesCheck - && to_sq(move) != prevSq - && futilityBase > -VALUE_KNOWN_WIN - && type_of(move) != PROMOTION) - { - if (moveCount > 2) - continue; + // Step 6. Pruning. + if (bestValue > VALUE_TB_LOSS_IN_MAX_PLY) + { + // Futility pruning and moveCount pruning (~10 Elo) + if ( !givesCheck + && to_sq(move) != prevSq + && futilityBase > -VALUE_KNOWN_WIN + && type_of(move) != PROMOTION) + { + if (moveCount > 2) + continue; - futilityValue = futilityBase + PieceValue[EG][pos.piece_on(to_sq(move))]; + futilityValue = futilityBase + PieceValue[EG][pos.piece_on(to_sq(move))]; - if (futilityValue <= alpha) - { - bestValue = std::max(bestValue, futilityValue); - continue; - } + if (futilityValue <= alpha) + { + bestValue = std::max(bestValue, futilityValue); + continue; + } - if (futilityBase <= alpha && !pos.see_ge(move, VALUE_ZERO + 1)) - { - bestValue = std::max(bestValue, futilityBase); - continue; - } - } + if (futilityBase <= alpha && !pos.see_ge(move, VALUE_ZERO + 1)) + { + bestValue = std::max(bestValue, futilityBase); + continue; + } + } - // We prune after 2nd quiet check evasion where being 'in check' is implicitly checked through the counter - // and being a 'quiet' apart from being a tt move is assumed after an increment because captures are pushed ahead. - if (quietCheckEvasions > 1) - break; + // We prune after the second quiet check evasion move, where being 'in check' is + // implicitly checked through the counter, and being a 'quiet move' apart from + // being a tt move is assumed after an increment because captures are pushed ahead. + if (quietCheckEvasions > 1) + break; - // Continuation history based pruning (~3 Elo) - if ( !capture - && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < 0 - && (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < 0) - continue; + // Continuation history based pruning (~3 Elo) + if ( !capture + && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < 0 + && (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < 0) + continue; - // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, Value(-95))) - continue; - } + // Do not search moves with bad enough SEE values (~5 Elo) + if (!pos.see_ge(move, Value(-95))) + continue; + } - // Speculative prefetch as early as possible - prefetch(TT.first_entry(pos.key_after(move))); + // Speculative prefetch as early as possible + prefetch(TT.first_entry(pos.key_after(move))); - // Update the current move - ss->currentMove = move; - ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck] - [capture] - [pos.moved_piece(move)] - [to_sq(move)]; + // Update the current move + ss->currentMove = move; + ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck] + [capture] + [pos.moved_piece(move)] + [to_sq(move)]; - quietCheckEvasions += !capture && ss->inCheck; + quietCheckEvasions += !capture && ss->inCheck; - // Step 7. Make and search the move - pos.do_move(move, st, givesCheck); - value = -qsearch(pos, ss+1, -beta, -alpha, depth - 1); - pos.undo_move(move); + // Step 7. Make and search the move + pos.do_move(move, st, givesCheck); + value = -qsearch(pos, ss+1, -beta, -alpha, depth - 1); + pos.undo_move(move); - assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); + assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); - // Step 8. Check for a new best move - if (value > bestValue) - { - bestValue = value; + // Step 8. Check for a new best move + if (value > bestValue) + { + bestValue = value; - if (value > alpha) - { - bestMove = move; + if (value > alpha) + { + bestMove = move; - if (PvNode) // Update pv even in fail-high case - update_pv(ss->pv, move, (ss+1)->pv); + if (PvNode) // Update pv even in fail-high case + update_pv(ss->pv, move, (ss+1)->pv); - if (PvNode && value < beta) // Update alpha here! - alpha = value; - else - break; // Fail high - } - } + if (PvNode && value < beta) // Update alpha here! + alpha = value; + else + break; // Fail high + } + } } // Step 9. Check for mate From 7922e07af83dd472da6e5b38fb84214cfe46a630 Mon Sep 17 00:00:00 2001 From: Andreas Matthies Date: Tue, 13 Jun 2023 06:24:04 +0200 Subject: [PATCH 1088/1766] Fix for MSVC compilation. MSVC needs two more explicit casts to compile new affine_transform_sparse_input. See https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_castsi256_ps and https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_castsi128_ps closes https://github.com/official-stockfish/Stockfish/pull/4616 No functional change --- AUTHORS | 1 + src/nnue/layers/affine_transform_sparse_input.h | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index 63b862ced05..a89dc1304ad 100644 --- a/AUTHORS +++ b/AUTHORS @@ -19,6 +19,7 @@ Alexander Kure Alexander Pagel (Lolligerhans) Alfredo Menezes (lonfom169) Ali AlZhrani (Cooffe) +Andreas Matthies (Matthies) Andrei Vetrov (proukornew) Andrew Grant (AndyGrant) Andrey Neporada (nepal) diff --git a/src/nnue/layers/affine_transform_sparse_input.h b/src/nnue/layers/affine_transform_sparse_input.h index 00b17c19f12..e0c3a8a06bb 100644 --- a/src/nnue/layers/affine_transform_sparse_input.h +++ b/src/nnue/layers/affine_transform_sparse_input.h @@ -98,10 +98,10 @@ namespace Stockfish::Eval::NNUE::Layers { #define vec_nnz(a) _mm512_cmpgt_epi32_mask(a, _mm512_setzero_si512()) #elif defined (USE_AVX2) using vec_t = __m256i; - #define vec_nnz(a) _mm256_movemask_ps((__m256)_mm256_cmpgt_epi32(a, _mm256_setzero_si256())) + #define vec_nnz(a) _mm256_movemask_ps(_mm256_castsi256_ps(_mm256_cmpgt_epi32(a, _mm256_setzero_si256()))) #elif defined (USE_SSSE3) using vec_t = __m128i; - #define vec_nnz(a) _mm_movemask_ps((__m128)_mm_cmpgt_epi32(a, _mm_setzero_si128())) + #define vec_nnz(a) _mm_movemask_ps(_mm_castsi128_ps(_mm_cmpgt_epi32(a, _mm_setzero_si128()))) #endif constexpr IndexType InputSimdWidth = sizeof(vec_t) / sizeof(std::int32_t); // Inputs are processed InputSimdWidth at a time and outputs are processed 8 at a time so we process in chunks of max(InputSimdWidth, 8) From 887bbd8b3df8a01307a38bfe529a49842f810a9c Mon Sep 17 00:00:00 2001 From: Viren6 <94880762+Viren6@users.noreply.github.com> Date: Tue, 13 Jun 2023 22:26:20 +0100 Subject: [PATCH 1089/1766] Remove setting of static to none if in check in qsearch Small simplification Passed non-regression STC: https://tests.stockfishchess.org/tests/view/6487924d713491385c8034ae LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 59616 W: 15885 L: 15703 D: 28028 Ptnml(0-2): 144, 6130, 17086, 6296, 152 closes https://github.com/official-stockfish/Stockfish/pull/4618 No functional change. --- AUTHORS | 1 + src/search.cpp | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index a89dc1304ad..ff224954707 100644 --- a/AUTHORS +++ b/AUTHORS @@ -215,6 +215,7 @@ tttak Unai Corzo (unaiic) Uri Blass (uriblass) Vince Negri (cuddlestmonkey) +Viren windfishballad xefoci7612 zz4032 diff --git a/src/search.cpp b/src/search.cpp index d3b5642a236..5de950eb147 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1464,7 +1464,6 @@ namespace { // Step 4. Static evaluation of the position if (ss->inCheck) { - ss->staticEval = VALUE_NONE; bestValue = futilityBase = -VALUE_INFINITE; } else From 14bfec2a981e906d1bfc08331a2e15bddd07ffe4 Mon Sep 17 00:00:00 2001 From: peregrineshahin Date: Wed, 14 Jun 2023 15:57:36 +0300 Subject: [PATCH 1090/1766] Consistent bench extraction with fishtest. Consistent with recent fishtest commit https://github.com/glinscott/fishtest/commit/c0d174396f7fb1c0b3243aaa6cc73769079f3ff9 closes https://github.com/official-stockfish/Stockfish/pull/4619 No functional change --- .github/workflows/stockfish_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stockfish_test.yml b/.github/workflows/stockfish_test.yml index 8c383fe7cf8..28218402702 100644 --- a/.github/workflows/stockfish_test.yml +++ b/.github/workflows/stockfish_test.yml @@ -115,7 +115,7 @@ jobs: - name: Extract the bench number from the commit history run: | - git log HEAD | grep "\b[Bb]ench[ :]\+[0-9]\{7\}" | head -n 1 | sed "s/[^0-9]*\([0-9]*\).*/\1/g" > git_sig + git log HEAD | grep -o "\b[Bb]ench[ :]\+[1-9][0-9]\{5,9\}\b" | head -n 1 | sed "s/[^0-9]//g" > git_sig [ -s git_sig ] && echo "benchref=$(cat git_sig)" >> $GITHUB_ENV && echo "Reference bench:" $(cat git_sig) || echo "No bench found" - name: Check compiler From 32d3284df5b2fd395504efa5319d64856902fef1 Mon Sep 17 00:00:00 2001 From: AndrovT <31534597+AndrovT@users.noreply.github.com> Date: Tue, 13 Jun 2023 03:00:04 +0200 Subject: [PATCH 1091/1766] Permute master net weights to increase sparsity Activation data collection using https://github.com/AndrovT/Stockfish/commit/ac468039ab544b03ad9a22c859a4217729c10a77 run as bench 16 1 13 varied_1000.epd depth NNUE log.bin on FENs from https://gist.github.com/AndrovT/7eae6918eb50764227e2bafe7938953c. Permutation found using https://gist.github.com/AndrovT/359c831b7223c637e9156b01eb96949e. Uses a greedy algorithm that goes sequentially through the output positions and chooses a neuron for that position such that the number of nonzero quartets is the smallest. Net weights permuted using https://gist.github.com/AndrovT/9e3fbaebb7082734dc84d27e02094cb3. Benchmark: Result of 100 runs of 'bench 16 1 13 default depth NNUE' ======================================================== base (...kfish-master) = 885869 +/- 7395 test (./stockfish ) = 895885 +/- 7368 diff = +10016 +/- 2984 speedup = +0.0113 P(speedup > 0) = 1.0000 Passed STC: https://tests.stockfishchess.org/tests/view/648866c4713491385c804728 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 126784 W: 34003 L: 33586 D: 59195 Ptnml(0-2): 283, 13001, 36437, 13358, 313 closes https://github.com/official-stockfish/Stockfish/pull/4620 No functional change. --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 94cd42cc67a..c35b2f07bec 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-fdc1d0fe6455.nnue" + #define EvalFileDefaultName "nn-cd2ff4716c34.nnue" namespace NNUE { From 0187546275cb015d42a7d99789f2f6a650b03651 Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Thu, 15 Jun 2023 21:05:01 +0800 Subject: [PATCH 1092/1766] Small cleanup This non-functional change keeps formatting consistent. closes https://github.com/official-stockfish/Stockfish/pull/4623 Bench 2370027 --- src/search.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5de950eb147..8ace674d11a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1463,9 +1463,7 @@ namespace { // Step 4. Static evaluation of the position if (ss->inCheck) - { bestValue = futilityBase = -VALUE_INFINITE; - } else { if (ss->ttHit) @@ -1480,11 +1478,9 @@ namespace { bestValue = ttValue; } else - { // In case of null move search use previous static eval with a different sign ss->staticEval = bestValue = (ss-1)->currentMove != MOVE_NULL ? evaluate(pos) : -(ss-1)->staticEval; - } // Stand pat. Return immediately if static value is at least beta if (bestValue >= beta) From a46087ee30ace00e1edd8963ee33b0a1b87031b6 Mon Sep 17 00:00:00 2001 From: maxim Date: Tue, 13 Jun 2023 23:09:05 +0300 Subject: [PATCH 1093/1766] Compressed network parameters Implemented LEB128 (de)compression for the feature transformer. Reduces embedded network size from 70 MiB to 39 Mib. The new nn-78bacfcee510.nnue corresponds to the master net compressed. closes https://github.com/official-stockfish/Stockfish/pull/4617 No functional change --- src/evaluate.h | 2 +- src/nnue/nnue_common.h | 77 +++++++++++++++++++++++++++++ src/nnue/nnue_feature_transformer.h | 12 ++--- 3 files changed, 84 insertions(+), 7 deletions(-) diff --git a/src/evaluate.h b/src/evaluate.h index c35b2f07bec..33effb1cfd8 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-cd2ff4716c34.nnue" + #define EvalFileDefaultName "nn-78bacfcee510.nnue" namespace NNUE { diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index 12309d262b1..d338527d96b 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -57,6 +57,9 @@ namespace Stockfish::Eval::NNUE { // Size of cache line (in bytes) constexpr std::size_t CacheLineSize = 64; + constexpr const char Leb128MagicString[] = "COMPRESSED_LEB128"; + constexpr const std::size_t Leb128MagicStringSize = sizeof(Leb128MagicString) - 1; + // SIMD width (in bytes) #if defined(USE_AVX2) constexpr std::size_t SimdWidth = 32; @@ -159,6 +162,80 @@ namespace Stockfish::Eval::NNUE { write_little_endian(stream, values[i]); } + template + inline void read_leb_128(std::istream& stream, IntType* out, std::size_t count) { + static_assert(std::is_signed_v, "Not implemented for unsigned types"); + char leb128MagicString[Leb128MagicStringSize]; + stream.read(leb128MagicString, Leb128MagicStringSize); + assert(strncmp(Leb128MagicString, leb128MagicString, Leb128MagicStringSize) == 0); + const std::uint32_t BUF_SIZE = 4096; + std::uint8_t buf[BUF_SIZE]; + auto bytes_left = read_little_endian(stream); + std::uint32_t buf_pos = BUF_SIZE; + for (std::size_t i = 0; i < count; ++i) { + IntType result = 0; + size_t shift = 0; + do { + if (buf_pos == BUF_SIZE) { + stream.read(reinterpret_cast(buf), std::min(bytes_left, BUF_SIZE)); + buf_pos = 0; + } + std::uint8_t byte = buf[buf_pos++]; + --bytes_left; + result |= (byte & 0x7f) << shift; + shift += 7; + if ((byte & 0x80) == 0) { + out[i] = sizeof(IntType) * 8 <= shift || (byte & 0x40) == 0 ? result : result | ~((1 << shift) - 1); + break; + } + } while (shift < sizeof(IntType) * 8); + } + assert(bytes_left == 0); + } + + template + inline void write_leb_128(std::ostream& stream, const IntType* values, std::size_t count) { + static_assert(std::is_signed_v, "Not implemented for unsigned types"); + stream.write(Leb128MagicString, Leb128MagicStringSize); + std::uint32_t byte_count = 0; + for (std::size_t i = 0; i < count; ++i) { + IntType value = values[i]; + std::uint8_t byte; + do { + byte = value & 0x7f; + value >>= 7; + ++byte_count; + } while ((byte & 0x40) == 0 ? value != 0 : value != -1); + } + write_little_endian(stream, byte_count); + const std::uint32_t BUF_SIZE = 4096; + std::uint8_t buf[BUF_SIZE]; + std::uint32_t buf_pos = 0; + auto flush = [&]() { + if (buf_pos > 0) { + stream.write(reinterpret_cast(buf), buf_pos); + buf_pos = 0; + } + }; + auto write = [&](std::uint8_t byte) { + buf[buf_pos++] = byte; + if (buf_pos == BUF_SIZE) flush(); + }; + for (std::size_t i = 0; i < count; ++i) { + IntType value = values[i]; + while (true) { + std::uint8_t byte = value & 0x7f; + value >>= 7; + if ((byte & 0x40) == 0 ? value == 0 : value == -1) { + write(byte); + break; + } + write(byte | 0x80); + } + } + flush(); + } + } // namespace Stockfish::Eval::NNUE #endif // #ifndef NNUE_COMMON_H_INCLUDED diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index a1888c7a365..7571f398295 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -253,9 +253,9 @@ namespace Stockfish::Eval::NNUE { // Read network parameters bool read_parameters(std::istream& stream) { - read_little_endian(stream, biases , HalfDimensions ); - read_little_endian(stream, weights , HalfDimensions * InputDimensions); - read_little_endian(stream, psqtWeights, PSQTBuckets * InputDimensions); + read_leb_128(stream, biases , HalfDimensions ); + read_leb_128(stream, weights , HalfDimensions * InputDimensions); + read_leb_128(stream, psqtWeights, PSQTBuckets * InputDimensions); return !stream.fail(); } @@ -263,9 +263,9 @@ namespace Stockfish::Eval::NNUE { // Write network parameters bool write_parameters(std::ostream& stream) const { - write_little_endian(stream, biases , HalfDimensions ); - write_little_endian(stream, weights , HalfDimensions * InputDimensions); - write_little_endian(stream, psqtWeights, PSQTBuckets * InputDimensions); + write_leb_128(stream, biases , HalfDimensions ); + write_leb_128(stream, weights , HalfDimensions * InputDimensions); + write_leb_128(stream, psqtWeights, PSQTBuckets * InputDimensions); return !stream.fail(); } From a68a1c11543eef6808181c92e0e7e5fb3f826f21 Mon Sep 17 00:00:00 2001 From: disservin Date: Tue, 13 Jun 2023 19:30:01 +0200 Subject: [PATCH 1094/1766] create prereleases upon push to master using github actions, create a prerelease for the latest commit to master. As such a development version will be available on github, in addition to the latest release. closes https://github.com/official-stockfish/Stockfish/pull/4622 No functional change --- .github/workflows/stockfish.yml | 24 +++++++++ .github/workflows/stockfish_arm_binaries.yml | 26 +++++++++ .github/workflows/stockfish_binaries.yml | 57 ++++++++++++++++++-- 3 files changed, 104 insertions(+), 3 deletions(-) diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index 082c65def18..ca52ffe08b3 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -12,6 +12,30 @@ on: - master - tools jobs: + Prerelease: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + # returns null if no pre-release exists + - name: Get Commit SHA of Latest Pre-release + run: | + # Install required packages + sudo apt-get update + sudo apt-get install -y curl jq + + echo "COMMIT_SHA=$(jq -r 'map(select(.prerelease)) | first | .tag_name' <<< $(curl -s https://api.github.com/repos/${{ github.repository_owner }}/Stockfish/releases))" >> $GITHUB_ENV + + # delete old previous pre-release and tag + - uses: dev-drprasad/delete-tag-and-release@v0.2.1 + if: env.COMMIT_SHA != 'null' + with: + tag_name: ${{ env.COMMIT_SHA }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + Sanitizers: uses: ./.github/workflows/stockfish_sanitizers.yml Tests: diff --git a/.github/workflows/stockfish_arm_binaries.yml b/.github/workflows/stockfish_arm_binaries.yml index 9a4734eeec6..52105eb6ac7 100644 --- a/.github/workflows/stockfish_arm_binaries.yml +++ b/.github/workflows/stockfish_arm_binaries.yml @@ -129,4 +129,30 @@ jobs: if: startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag' uses: softprops/action-gh-release@v1 with: + files: stockfish-android-${{ matrix.binaries }}.tar + + - name: Get last commit sha + id: last_commit + run: echo "COMMIT_SHA=$(git rev-parse HEAD | cut -c 1-8)" >> $GITHUB_ENV + + - name: Get commit date + id: commit_date + run: echo "COMMIT_DATE=$(git show -s --date=format:'%Y%m%d' --format=%cd HEAD)" >> $GITHUB_ENV + + # Make sure that an old ci which still runs on master doesn't recreate a prerelease + - name: Check Pullable Commits + id: check_commits + run: | + git fetch + CHANGES=$(git rev-list HEAD..origin/master --count) + echo "CHANGES=$CHANGES" >> $GITHUB_ENV + + - name: Prerelease + if: github.ref_name == 'master' && env.CHANGES == '0' + continue-on-error: true + uses: softprops/action-gh-release@v1 + with: + name: Stockfish dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} + tag_name: stockfish-dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} + prerelease: true files: stockfish-android-${{ matrix.binaries }}.tar \ No newline at end of file diff --git a/.github/workflows/stockfish_binaries.yml b/.github/workflows/stockfish_binaries.yml index 86449b97a4b..0a53cb03a99 100644 --- a/.github/workflows/stockfish_binaries.yml +++ b/.github/workflows/stockfish_binaries.yml @@ -20,12 +20,14 @@ jobs: compiler: g++ comp: gcc shell: bash {0} + archive_ext: tar - name: MacOS 12 Apple Clang os: macos-12 simple_name: macos compiler: clang++ comp: clang shell: bash {0} + archive_ext: tar - name: Windows 2022 Mingw-w64 GCC x86_64 os: windows-2022 simple_name: windows @@ -35,6 +37,7 @@ jobs: msys_env: x86_64-gcc shell: msys2 {0} ext: .exe + archive_ext: zip binaries: - x86-64 - x86-64-modern @@ -60,7 +63,7 @@ jobs: uses: msys2/setup-msys2@v2 with: msystem: ${{ matrix.config.msys_sys }} - install: mingw-w64-${{ matrix.config.msys_env }} make git + install: mingw-w64-${{ matrix.config.msys_env }} make git zip - name: Download the used network from the fishtest framework run: make net @@ -90,7 +93,7 @@ jobs: git clone https://github.com/official-stockfish/Stockfish.wiki.git ../wiki rm -rf ../wiki/.git - - name: Create tar archive. + - name: Create directory. run: | cd .. mkdir stockfish @@ -102,16 +105,64 @@ jobs: cp AUTHORS stockfish/ cp CITATION.cff stockfish/ cp README.md stockfish/ + + - name: Create tar + if: runner.os != 'Windows' + run: | + cd .. tar -cvf stockfish-$NAME-$BINARY.tar stockfish + - name: Create zip + if: runner.os == 'Windows' + run: | + cd .. + zip -r stockfish-$NAME-$BINARY.zip stockfish + - name: Upload binaries + if: runner.os != 'Windows' uses: actions/upload-artifact@v3 with: name: stockfish-${{ matrix.config.os }}-${{ matrix.binaries }} path: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.tar + # Artifacts automatically get zipped + # to avoid double zipping, we use the unzipped directory + - name: Upload binaries + if: runner.os == 'Windows' + uses: actions/upload-artifact@v3 + with: + name: stockfish-${{ matrix.config.os }}-${{ matrix.binaries }} + path: stockfish + - name: Release if: startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag' uses: softprops/action-gh-release@v1 with: - files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.tar + files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.${{ matrix.config.archive_ext }} + + - name: Get last commit sha + id: last_commit + run: echo "COMMIT_SHA=$(git rev-parse HEAD | cut -c 1-8)" >> $GITHUB_ENV + + - name: Get commit date + id: commit_date + run: echo "COMMIT_DATE=$(git show -s --date=format:'%Y%m%d' --format=%cd HEAD)" >> $GITHUB_ENV + + # Make sure that an old ci which still runs on master doesn't recreate a prerelease + - name: Check Pullable Commits + id: check_commits + run: | + git fetch + CHANGES=$(git rev-list HEAD..origin/master --count) + echo "CHANGES=$CHANGES" >> $GITHUB_ENV + + - name: Prerelease + if: github.ref_name == 'master' && env.CHANGES == '0' + continue-on-error: true + uses: softprops/action-gh-release@v1 + with: + name: Stockfish dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} + tag_name: stockfish-dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} + prerelease: true + files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.${{ matrix.config.archive_ext }} + From 6eaa1c3ecd297404b28f9e80cddf81c4c6926a51 Mon Sep 17 00:00:00 2001 From: Joerg Oster Date: Tue, 20 Jun 2023 10:40:31 +0200 Subject: [PATCH 1095/1766] Fix indentation in qsearch. https://github.com/official-stockfish/Stockfish/pull/4630 No functional change. --- src/search.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 8ace674d11a..9b686a52974 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1555,23 +1555,23 @@ namespace { bestValue = std::max(bestValue, futilityBase); continue; } - } - - // We prune after the second quiet check evasion move, where being 'in check' is - // implicitly checked through the counter, and being a 'quiet move' apart from - // being a tt move is assumed after an increment because captures are pushed ahead. - if (quietCheckEvasions > 1) - break; - - // Continuation history based pruning (~3 Elo) - if ( !capture - && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < 0 - && (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < 0) - continue; + } - // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, Value(-95))) - continue; + // We prune after the second quiet check evasion move, where being 'in check' is + // implicitly checked through the counter, and being a 'quiet move' apart from + // being a tt move is assumed after an increment because captures are pushed ahead. + if (quietCheckEvasions > 1) + break; + + // Continuation history based pruning (~3 Elo) + if ( !capture + && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < 0 + && (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < 0) + continue; + + // Do not search moves with bad enough SEE values (~5 Elo) + if (!pos.see_ge(move, Value(-95))) + continue; } // Speculative prefetch as early as possible From aec8fb3fdec8309a0cc78222a4f674ea6fea9411 Mon Sep 17 00:00:00 2001 From: disservin Date: Tue, 20 Jun 2023 18:27:20 +0200 Subject: [PATCH 1096/1766] Fix failing CI of pull requests adds a guard to prevent pull requests from trying to delete the previous pre-release closing https://github.com/official-stockfish/Stockfish/pull/4631 No functional change. --- .github/workflows/stockfish.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index ca52ffe08b3..99c4259ac75 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -13,6 +13,7 @@ on: - tools jobs: Prerelease: + if: github.ref == 'refs/heads/master' runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 From 02728736edd5915fc0abc8698635e633a3cba201 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Tue, 20 Jun 2023 09:46:02 +0200 Subject: [PATCH 1097/1766] Update top CPU contributors closes https://github.com/official-stockfish/Stockfish/pull/4629 No functional change --- Top CPU Contributors.txt | 117 ++++++++++++++++++++------------------- 1 file changed, 59 insertions(+), 58 deletions(-) diff --git a/Top CPU Contributors.txt b/Top CPU Contributors.txt index 7b27959071a..74c471b7404 100644 --- a/Top CPU Contributors.txt +++ b/Top CPU Contributors.txt @@ -1,55 +1,55 @@ -Contributors to Fishtest with >10,000 CPU hours, as of 2023-05-27. +Contributors to Fishtest with >10,000 CPU hours, as of 2023-06-20. Thank you! Username CPU Hours Games played ------------------------------------------------------------------ -noobpwnftw 37304027 2833556221 -technologov 13508659 714674674 -linrock 4121386 280027751 +noobpwnftw 37457426 2850540907 +technologov 14135647 742892808 +linrock 4423514 303254809 mlang 3026000 200065824 dew 1689162 100033738 -okrout 1541122 145085726 -pemo 1481818 47546583 -grandphish2 1459364 91364265 -TueRens 1178700 69951886 -JojoM 937875 60821044 +okrout 1578136 148855886 +pemo 1508508 48814305 +grandphish2 1461406 91540343 +TueRens 1194790 70400852 +JojoM 947612 61773190 tvijlbrief 796125 51897690 +sebastronomy 742434 38218524 mibere 703840 46867607 -sebastronomy 687502 35585318 -gvreuls 645570 42437926 -oz 541224 39133532 -cw 517856 34869499 +gvreuls 651026 42988582 +oz 543438 39314736 +cw 517858 34869755 fastgm 503862 30260818 -CSU_Dynasty 464691 31166478 -leszek 460426 32840277 -ctoks 434323 28497451 +leszek 467278 33514883 +CSU_Dynasty 464940 31177118 +ctoks 434416 28506889 crunchy 427035 27344275 -maximmasiutin 424154 26534660 +maximmasiutin 424795 26577722 bcross 415722 29060963 -rpngn 344368 24218047 -velislav 342559 22138408 +olafm 395922 32268020 +rpngn 348378 24560289 +velislav 342567 22138992 Fisherman 327231 21829379 -mgrabiak 297057 20260882 +mgrabiak 300612 20608380 Dantist 296386 18031762 -nordlandia 242642 15922516 -robal 240199 15544104 +nordlandia 246201 16189678 +robal 241300 15656382 marrco 234581 17714473 ncfish1 227517 15233777 glinscott 208125 13277240 drabel 204167 13930674 mhoram 202894 12601997 bking_US 198894 11876016 -olafm 192342 14968698 Thanar 179852 12365359 vdv 175544 9904472 spams 157128 10319326 sqrt2 147963 9724586 -DesolatedDodo 144759 9408038 +DesolatedDodo 146350 9536172 Calis007 143165 9478764 -vdbergh 138436 9042073 +vdbergh 138650 9064413 CoffeeOne 137100 5024116 +armo9494 136191 9460264 malala 136182 8002293 -armo9494 136010 9447548 xoto 133759 9159372 davar 129023 8376525 DMBK 122960 8980062 @@ -59,43 +59,43 @@ Data 113305 8220352 BrunoBanani 112960 7436849 CypressChess 108331 7759788 skiminki 107583 7218170 +jcAEie 105675 8238962 MaZePallas 102823 6633619 sterni1971 100532 5880772 -jcAEie 100392 7788270 sunu 100167 7040199 zeryl 99331 6221261 thirdlife 99124 2242380 ElbertoOne 99028 7023771 -cuistot 98360 6017102 +cuistot 98853 6069816 bigpen0r 94809 6529203 brabos 92118 6186135 -Wolfgang 90855 5998076 +Wolfgang 91939 6105872 psk 89957 5984901 +sschnee 88235 5268000 racerschmacer 85805 6122790 +Fifis 85722 5709729 Dubslow 84986 6042456 Vizvezdenec 83761 5344740 -sschnee 83564 4853834 0x3C33 82614 5271253 BRAVONE 81239 5054681 -Fifis 77355 5158211 nssy 76497 5259388 jromang 76106 5236025 teddybaer 75125 5407666 +tolkki963 74762 5149662 +megaman7de 74351 4940352 Wencey 74181 4711488 -megaman7de 73866 4894960 Pking_cda 73776 5293873 -tolkki963 73531 5020500 -yurikvelo 72847 4972808 +yurikvelo 73150 5004382 +markkulix 72607 5304642 Bobo1239 70579 4794999 solarlight 70517 5028306 dv8silencer 70287 3883992 -markkulix 70278 5068326 manap 66273 4121774 tinker 64333 4268790 qurashee 61208 3429862 -Mineta 58759 4399960 +Mineta 59357 4418202 +Spprtr 58723 3911011 AGI 58147 4325994 -Spprtr 58106 3858759 robnjr 57262 4053117 Freja 56938 3733019 MaxKlaxxMiner 56879 3423958 @@ -103,35 +103,35 @@ MarcusTullius 56746 3762951 ttruscott 56010 3680085 rkl 55132 4164467 renouve 53811 3501516 +javran 53785 4627608 finfish 51360 3370515 eva42 51272 3599691 eastorwest 51117 3454811 rap 49985 3219146 pb00067 49733 3298934 -javran 49178 4190632 -OuaisBla 48606 3442958 +OuaisBla 48626 3445134 ronaldjerum 47654 3240695 biffhero 46564 3111352 VoyagerOne 45476 3452465 -oryx 44532 3450170 -jmdana 43849 2955821 +jmdana 44893 3065205 +maposora 44597 4039578 +oryx 44570 3454238 speedycpu 43842 3003273 jbwiebe 43305 2805433 +GPUex 42378 3133332 Antihistamine 41788 2761312 mhunt 41735 2691355 -maposora 41534 3733078 -GPUex 41061 2998356 homyur 39893 2850481 gri 39871 2515779 Garf 37741 2999686 SC 37299 2731694 csnodgrass 36207 2688994 strelock 34716 2074055 +szupaw 34102 2880346 EthanOConnor 33370 2090311 slakovv 32915 2021889 Gelma 31771 1551204 gopeto 31671 2060990 -szupaw 31248 2594920 kdave 31157 2198362 manapbk 30987 1810399 Prcuvu 30377 2170122 @@ -147,52 +147,54 @@ xwziegtm 26897 2124586 achambord 26582 1767323 Patrick_G 26276 1801617 yorkman 26193 1992080 -Ulysses 25285 1689346 +Ulysses 25288 1689730 SFTUser 25182 1675689 nabildanial 24942 1519409 Sharaf_DG 24765 1786697 +Maxim 24705 1502062 rodneyc 24376 1416402 agg177 23890 1395014 +Goatminola 23763 1956036 Ente 23639 1671638 +Jopo12321 23467 1483172 JanErik 23408 1703875 Isidor 23388 1680691 Norabor 23371 1603244 -Goatminola 23338 1910634 cisco2015 22920 1763301 -Jopo12321 22890 1424926 +jsys14 22824 1591906 Zirie 22542 1472937 team-oh 22272 1636708 Roady 22220 1465606 MazeOfGalious 21978 1629593 sg4032 21947 1643353 -jsys14 21935 1499128 ianh2105 21725 1632562 xor12 21628 1680365 dex 21612 1467203 nesoneg 21494 1463031 user213718 21454 1404128 sphinx 21211 1384728 +AndreasKrug 21097 1634811 jjoshua2 21001 1423089 Zake9298 20938 1565848 -AndreasKrug 20911 1615673 horst.prack 20878 1465656 0xB00B1ES 20590 1208666 j3corre 20405 941444 Adrian.Schmidt123 20316 1281436 wei 19973 1745989 +notchris 19958 1800128 Serpensin 19840 1697528 +Gaster319 19712 1677310 fishtester 19617 1257388 rstoesser 19569 1293588 eudhan 19274 1283717 -Gaster319 18934 1596772 +votoanthuan 19108 1609992 vulcan 18871 1729392 Karpovbot 18766 1053178 +qoo_charly_cai 18543 1284937 jundery 18445 1115855 -votoanthuan 18012 1508836 ville 17883 1384026 chris 17698 1487385 purplefishies 17595 1092533 -qoo_charly_cai 17494 1182667 dju 17414 981289 iisiraider 17275 1049015 DragonLord 17014 1162790 @@ -200,7 +202,6 @@ redstone59 16842 1461780 Alb11747 16787 1213926 IgorLeMasson 16064 1147232 Karby 15982 979610 -notchris 15818 1426762 scuzzi 15757 968735 ako027ako 15671 1173203 Nikolay.IT 15154 1068349 @@ -218,33 +219,33 @@ Nesa92 13786 1114691 joster 13710 946160 mbeier 13650 1044928 Hjax 13535 915487 +Nullvalue 13468 1140498 Dark_wizzie 13422 1007152 Rudolphous 13244 883140 pirt 13100 1009897 Machariel 13010 863104 infinigon 12991 943216 -Maxim 12963 985594 mabichito 12903 749391 thijsk 12886 722107 AdrianSA 12860 804972 Flopzee 12698 894821 korposzczur 12606 838168 -Nullvalue 12583 1048502 fatmurphy 12547 853210 SapphireBrand 12416 969604 Oakwen 12399 844109 deflectooor 12386 579392 modolief 12386 896470 Farseer 12249 694108 -Jackfish 12180 801372 +Jackfish 12213 805008 pgontarz 12151 848794 dbernier 12103 860824 getraideBFF 12072 1024966 stocky 11954 699440 mschmidt 11941 803401 MooTheCow 11870 773598 -FormazChar 11689 877727 +FormazChar 11766 885707 whelanh 11557 245188 +3cho 11494 1031076 infinity 11470 727027 aga 11412 695127 torbjo 11395 729145 @@ -254,6 +255,7 @@ d64 11263 789184 ali-al-zhrani 11245 779246 snicolet 11106 869170 dapper 11032 771402 +ols 10947 624903 Karmatron 10828 677458 basepi 10637 744851 Cubox 10621 826448 @@ -263,4 +265,3 @@ jojo2357 10419 929708 WoodMan777 10380 873720 Garruk 10365 706465 dzjp 10343 732529 -ols 10259 570669 From 52e84e4b4675aae52a619c309479684dc5478bf5 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 22 Jun 2023 09:59:03 +0200 Subject: [PATCH 1098/1766] Update winrate model with June data Retained 748191776 scored positions for analysis const int NormalizeToPawnValue = 328; Corresponding spread = 60; Corresponding normalized spread = 0.18337766691628035; Draw rate at 0.0 eval at move 32 = 0.9914715947898592; closes https://github.com/official-stockfish/Stockfish/pull/4636 No functional change --- src/uci.cpp | 4 ++-- src/uci.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/uci.cpp b/src/uci.cpp index 523d551e0c0..ed16f24c382 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -207,8 +207,8 @@ namespace { // The coefficients of a third-order polynomial fit is based on the fishtest data // for two parameters that need to transform eval to the argument of a logistic // function. - constexpr double as[] = { 1.07390458, -6.94334517, 31.95090161, 317.75424048}; - constexpr double bs[] = { -2.82843814, 16.64518180, -19.74439200, 68.39499088 }; + constexpr double as[] = { 0.38036525, -2.82015070, 23.17882135, 307.36768407}; + constexpr double bs[] = { -2.29434733, 13.27689788, -14.26828904, 63.45318330 }; // Enforce that NormalizeToPawnValue corresponds to a 50% win rate at ply 64 static_assert(UCI::NormalizeToPawnValue == int(as[0] + as[1] + as[2] + as[3])); diff --git a/src/uci.h b/src/uci.h index 680d2d2cc8c..8f1be00c7cb 100644 --- a/src/uci.h +++ b/src/uci.h @@ -35,7 +35,7 @@ namespace UCI { // the win_rate_model() such that Stockfish outputs an advantage of // "100 centipawns" for a position if the engine has a 50% probability to win // from this position in selfplay at fishtest LTC time control. -const int NormalizeToPawnValue = 343; +const int NormalizeToPawnValue = 328; class Option; From 48f7c74f15f4cae4b77596cd468802054314d701 Mon Sep 17 00:00:00 2001 From: peregrineshahin Date: Mon, 12 Jun 2023 12:57:41 +0300 Subject: [PATCH 1099/1766] Fix Potential in TB cutoffs for NMP. Removes the second dependency on beta and caps the return value to VALUE_TB_WIN_IN_MAX_PLY - 1 Earlier tests: STC: LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 193632 W: 51372 L: 51326 D: 90934 Ptnml(0-2): 447, 20111, 55687, 20091, 480 https://tests.stockfishchess.org/tests/view/6486ee4465ffe077ca125bc1 LTC: LLR: 2.97 (-2.94,2.94) <-1.75,0.25> Total: 331758 W: 89538 L: 89624 D: 152596 Ptnml(0-2): 114, 30121, 105516, 29993, 135 https://tests.stockfishchess.org/tests/view/6489401af42a44347ed7be42 updated constant: LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 100260 W: 27143 L: 27017 D: 46100 Ptnml(0-2): 34, 8842, 32248, 8976, 30 https://tests.stockfishchess.org/tests/view/6492fcafdc7002ce609c818c closes: https://github.com/official-stockfish/Stockfish/pull/4632 fixes: https://github.com/official-stockfish/Stockfish/issues/4598 bench: 2370027 --- src/search.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 9b686a52974..740ad71efee 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -801,10 +801,9 @@ namespace { if (nullValue >= beta) { // Do not return unproven mate or TB scores - if (nullValue >= VALUE_TB_WIN_IN_MAX_PLY) - nullValue = beta; + nullValue = std::min(nullValue, VALUE_TB_WIN_IN_MAX_PLY-1); - if (thisThread->nmpMinPly || (abs(beta) < VALUE_KNOWN_WIN && depth < 14)) + if (thisThread->nmpMinPly || depth < 14) return nullValue; assert(!thisThread->nmpMinPly); // Recursive verification is not allowed From a49b3ba7ed5d9be9151c8ceb5eed40efe3387c75 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Wed, 21 Jun 2023 00:43:46 -0400 Subject: [PATCH 1100/1766] Update default net to nn-5af11540bbfe.nnue Created by retraining the sparsified master net (nn-cd2ff4716c34.nnue) on a 100% minified dataset including Leela transformers data from T80 may2023. Weights permuted with the exact methods and code in: https://github.com/official-stockfish/Stockfish/pull/4620 LEB128 compression done with the new serialize.py param in: https://github.com/glinscott/nnue-pytorch/pull/251 Initially trained with max epoch 800. Around epoch 780, training was paused and max epoch raised to 960. python3 easy_train.py \ --experiment-name L1-1536-sparse-master-retrain \ --training-dataset /data/leela96-dfrc99-v2-T60novdecT77decT78jantosepT79aprmayT80juntonovjan-v6dd-T80febtomay2023.min.binpack \ --early-fen-skipping 27 \ --start-from-engine-test-net True \ --max_epoch 960 \ --lr 4.375e-4 \ --gamma 0.995 \ --start-lambda 1.0 \ --end-lambda 0.7 \ --tui False \ --seed $RANDOM \ --gpus 0 For preparing the training dataset (interleaved size 328G): python3 interleave_binpacks.py \ leela96-filt-v2.min.binpack \ dfrc99-16tb7p-eval-filt-v2.min.binpack \ filt-v6-dd-min/test60-novdec2021-12tb7p-filter-v6-dd.min.binpack \ filt-v6-dd-min/test77-dec2021-16tb7p-filter-v6-dd.min.binpack \ filt-v6-dd-min/test78-jantomay2022-16tb7p-filter-v6-dd.min.binpack \ filt-v6-dd-min/test78-juntosep2022-16tb7p-filter-v6-dd.min.binpack \ filt-v6-dd-min/test79-apr2022-16tb7p-filter-v6-dd.min.binpack \ filt-v6-dd-min/test79-may2022-16tb7p-filter-v6-dd.min.binpack \ filt-v6-dd-min/test80-jun2022-16tb7p-filter-v6-dd.min.binpack \ filt-v6-dd-min/test80-jul2022-16tb7p-filter-v6-dd.min.binpack \ filt-v6-dd-min/test80-aug2022-16tb7p-filter-v6-dd.min.binpack \ filt-v6-dd-min/test80-sep2022-16tb7p-filter-v6-dd.min.binpack \ filt-v6-dd-min/test80-oct2022-16tb7p-filter-v6-dd.min.binpack \ filt-v6-dd-min/test80-nov2022-16tb7p-filter-v6-dd.min.binpack \ filt-v6-dd-min/test80-jan2023-16tb7p-filter-v6-dd.min.binpack \ test80-2023/test80-feb2023-16tb7p-no-db.min.binpack \ test80-2023/test80-mar2023-2tb7p-no-db.min.binpack \ test80-2023/test80-apr2023-2tb7p-no-db.min.binpack \ test80-2023/test80-may2023-2tb7p-no-db.min.binpack \ /data/leela96-dfrc99-v2-T60novdecT77decT78jantosepT79aprmayT80juntonovjan-v6dd-T80febtomay2023.min.binpack Minified binpacks and Leela T80 training data from 2023 available at: https://robotmoon.com/nnue-training-data/ Local elo at 25k nodes per move: nn-epoch879.nnue : 3.9 +/- 5.7 Passed STC: https://tests.stockfishchess.org/tests/view/64928c1bdc7002ce609c7690 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 72000 W: 19242 L: 18889 D: 33869 Ptnml(0-2): 182, 7787, 19716, 8126, 189 Passed LTC: https://tests.stockfishchess.org/tests/view/64930a37dc7002ce609c82e3 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 54552 W: 14978 L: 14647 D: 24927 Ptnml(0-2): 23, 5123, 16650, 5460, 20 closes https://github.com/official-stockfish/Stockfish/pull/4635 bench 2593605 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 33effb1cfd8..b9d7231d1e1 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-78bacfcee510.nnue" + #define EvalFileDefaultName "nn-5af11540bbfe.nnue" namespace NNUE { From 68e1e9b3811e16cad014b590d7443b9063b3eb52 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 24 Jun 2023 08:58:17 +0200 Subject: [PATCH 1101/1766] Stockfish 16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Official release version of Stockfish 16 Bench: 2593605 --- Stockfish 16 A new major release of Stockfish is now available at https://stockfishchess.org/download/ *Quality of chess play* Stockfish continues to demonstrate its ability to discover superior moves with remarkable speed. In self-play against Stockfish 15, this new release gains up to 50 Elo[1] and wins up to 12 times more game pairs[2] than it loses. In major chess engine tournaments, Stockfish reliably tops the rankings[3] winning the TCEC season 24 Superfinal, Swiss, Fischer Random, and Double Random Chess tournaments and the CCC 19 Bullet, 20 Blitz, and 20 Rapid competitions. Leela Chess Zero[4] was the challenger in most finals, putting top-engine chess now firmly in the hands of teams embracing free and open-source software. *Progress made* This updated version of Stockfish introduces several enhancements, including an upgraded neural net architecture (SFNNv6)[5], improved implementation, and refined parameterization. The ongoing utilization of Leela’s data combined with a novel inference approach exploiting sparsity[6], and network compression[7] ensure a speedy evaluation and modest binary sizes while allowing for more weights and higher accuracy. The search has undergone more optimization, leading to improved performance, particularly in longer analyses[8]. Additionally, the Fishtest framework has been improved and is now able to run the tests needed to validate new ideas with 10000s of CPU cores. *Usability improvements* Stockfish now comes with documentation, found in the wiki folder when downloading it or on GitHub[9]. Additionally, Stockfish now includes a clear and consistent forced tablebase win score, displaying a value of 200 minus the number of plies required to reach a tablebase win[10]. Furthermore, the UCI_Elo option, to reduce its strength, has been calibrated[11]. It is worth noting that the evaluation system remains consistent with Stockfish 15.1[12], maintaining the choice that 100cp means a 50% chance of winning the game against an equal opponent[13]. Finally, binaries of our latest development version are now provided continuously as pre-releases on GitHub making it easier for enthusiasts to download the latest and strongest version of the program[14], we thank Roman Korba for having provided a similar service for a long time. *Thank you* The success of the Stockfish project relies on the vibrant community of passionate enthusiasts (we appreciate each and every one of you!) who generously contribute their knowledge, time, and resources. Together, this dedicated community works towards the common goal of developing a powerful, freely accessible, and open-source chess engine. We invite all chess enthusiasts to join the Fishtest testing framework and contribute to the project[15]. The Stockfish team [1] https://tests.stockfishchess.org/tests/view/649409f0dc7002ce609c99cc [2] https://tests.stockfishchess.org/tests/view/649409d7dc7002ce609c99c6 [3] https://en.wikipedia.org/wiki/Stockfish_(chess)#Competition_results [4] https://lczero.org/ [5] https://github.com/official-stockfish/Stockfish/commit/c1fff71 [6] https://github.com/official-stockfish/Stockfish/commit/38e6166 [7] https://github.com/official-stockfish/Stockfish/commit/a46087e [8] https://github.com/official-stockfish/Stockfish/commit/472e726 [9] https://github.com/official-stockfish/Stockfish/wiki/ [10] https://github.com/official-stockfish/Stockfish/commit/def2966 [11] https://github.com/official-stockfish/Stockfish/commit/a08b8d4 [12] https://github.com/official-stockfish/Stockfish/commit/52e84e4 [13] https://github.com/official-stockfish/Stockfish/wiki/Stockfish-FAQ#interpretation-of-the-stockfish-evaluation [14] https://github.com/official-stockfish/Stockfish/releases?q=prerelease%3Atrue [15] https://stockfishchess.org/get-involved/ --- src/misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index f1554060d5e..bbfa40617db 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -73,7 +73,7 @@ namespace Stockfish { namespace { /// Version number or dev. -constexpr string_view version = "dev"; +constexpr string_view version = "16"; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We From 0fd186fb28e7e1e5f2cc5ef8388115c950eaad9e Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 1 Jul 2023 12:18:46 +0200 Subject: [PATCH 1102/1766] Restore development closes https://github.com/official-stockfish/Stockfish/pull/4651 No functional change --- src/misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index bbfa40617db..f1554060d5e 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -73,7 +73,7 @@ namespace Stockfish { namespace { /// Version number or dev. -constexpr string_view version = "16"; +constexpr string_view version = "dev"; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We From ef94f77f8c827a2395f1c40f53311a3b1f20bc5b Mon Sep 17 00:00:00 2001 From: Daniel Monroe <39802758+Ergodice@users.noreply.github.com> Date: Fri, 23 Jun 2023 18:36:27 -0400 Subject: [PATCH 1103/1766] Update default net to nn-a3d1bfca1672.nnue faster permutation of master net weights Activation data taken from https://drive.google.com/drive/folders/1Ec9YuuRx4N03GPnVPoQOW70eucOKngQe?usp=sharing Permutation found using https://github.com/Ergodice/nnue-pytorch/blob/836387a0e5e690431d404158c46648710f13904d/ftperm.py See also https://github.com/glinscott/nnue-pytorch/pull/254 The algorithm greedily selects 2- and 3-cycles that can be permuted to increase the number of runs of zeroes. The percent of zero runs from the master net increased from 68.46 to 70.11 from 2-cycles and only increased to 70.32 when considering 3-cycles. Interestingly, allowing both halves of L1 to intermix when creating zero runs can give another 0.5% zero-run density increase with this method. Measured speedup: ``` CPU: 16 x AMD Ryzen 9 3950X 16-Core Processor Result of 50 runs base (./stockfish.master ) = 1561556 +/- 5439 test (./stockfish.patch ) = 1575788 +/- 5427 diff = +14231 +/- 2636 speedup = +0.0091 P(speedup > 0) = 1.0000 ``` closes https://github.com/official-stockfish/Stockfish/pull/4640 No functional change --- AUTHORS | 1 + src/evaluate.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index ff224954707..2e9ae7805fd 100644 --- a/AUTHORS +++ b/AUTHORS @@ -48,6 +48,7 @@ clefrks Dale Weiler (graphitemaster) Daniel Axtens (daxtens) Daniel Dugovic (ddugovic) +Daniel Monroe (Ergodice) Dan Schmidt (dfannius) Dariusz Orzechowski (dorzechowski) David (dav1312) diff --git a/src/evaluate.h b/src/evaluate.h index b9d7231d1e1..c3321965d52 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-5af11540bbfe.nnue" + #define EvalFileDefaultName "nn-a3d1bfca1672.nnue" namespace NNUE { From e355c7059468048bbb8b9f10e2b32606aa72eb77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Tue, 27 Jun 2023 12:03:00 +0200 Subject: [PATCH 1104/1766] Document the LEB128 patch Add some comments and harmonize style for the LEB128 patch. closes https://github.com/official-stockfish/Stockfish/pull/4642 No functional change --- src/nnue/nnue_common.h | 78 +++++++++++++++++++++++++++++++++--------- 1 file changed, 62 insertions(+), 16 deletions(-) diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index d338527d96b..e8ed2bc68e7 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -86,6 +86,7 @@ namespace Stockfish::Eval::NNUE { return (n + base - 1) / base * base; } + // read_little_endian() is our utility to read an integer (signed or unsigned, any size) // from a stream in little-endian order. We swap the byte order after the read if // necessary to return a result with the byte ordering of the compiling machine. @@ -110,6 +111,7 @@ namespace Stockfish::Eval::NNUE { return result; } + // write_little_endian() is our utility to write an integer (signed or unsigned, any size) // to a stream in little-endian order. We swap the byte order before the write if // necessary to always write in little endian order, independently of the byte @@ -140,6 +142,7 @@ namespace Stockfish::Eval::NNUE { } } + // read_little_endian(s, out, N) : read integers in bulk from a little indian stream. // This reads N integers from stream s and put them in array out. template @@ -151,6 +154,7 @@ namespace Stockfish::Eval::NNUE { out[i] = read_little_endian(stream); } + // write_little_endian(s, values, N) : write integers in bulk to a little indian stream. // This takes N integers from array values and writes them on stream s. template @@ -162,77 +166,119 @@ namespace Stockfish::Eval::NNUE { write_little_endian(stream, values[i]); } + + // read_leb_128(s, out, N) : read N signed integers from the stream s, putting them in + // the array out. The stream is assumed to be compressed using the signed LEB128 format. + // See https://en.wikipedia.org/wiki/LEB128 for a description of the compression scheme. template inline void read_leb_128(std::istream& stream, IntType* out, std::size_t count) { - static_assert(std::is_signed_v, "Not implemented for unsigned types"); + + // Check the presence of our LEB128 magic string char leb128MagicString[Leb128MagicStringSize]; stream.read(leb128MagicString, Leb128MagicStringSize); assert(strncmp(Leb128MagicString, leb128MagicString, Leb128MagicStringSize) == 0); + + static_assert(std::is_signed_v, "Not implemented for unsigned types"); + const std::uint32_t BUF_SIZE = 4096; std::uint8_t buf[BUF_SIZE]; + auto bytes_left = read_little_endian(stream); + std::uint32_t buf_pos = BUF_SIZE; - for (std::size_t i = 0; i < count; ++i) { + for (std::size_t i = 0; i < count; ++i) + { IntType result = 0; size_t shift = 0; - do { - if (buf_pos == BUF_SIZE) { + do + { + if (buf_pos == BUF_SIZE) + { stream.read(reinterpret_cast(buf), std::min(bytes_left, BUF_SIZE)); buf_pos = 0; } + std::uint8_t byte = buf[buf_pos++]; --bytes_left; result |= (byte & 0x7f) << shift; shift += 7; - if ((byte & 0x80) == 0) { - out[i] = sizeof(IntType) * 8 <= shift || (byte & 0x40) == 0 ? result : result | ~((1 << shift) - 1); + + if ((byte & 0x80) == 0) + { + out[i] = (sizeof(IntType) * 8 <= shift || (byte & 0x40) == 0) ? result + : result | ~((1 << shift) - 1); break; } - } while (shift < sizeof(IntType) * 8); + } + while (shift < sizeof(IntType) * 8); } + assert(bytes_left == 0); } + + // write_leb_128(s, values, N) : write signed integers to a stream with LEB128 compression. + // This takes N integers from array values, compress them with the LEB128 algorithm and + // writes the result on the stream s. + // See https://en.wikipedia.org/wiki/LEB128 for a description of the compression scheme. template inline void write_leb_128(std::ostream& stream, const IntType* values, std::size_t count) { - static_assert(std::is_signed_v, "Not implemented for unsigned types"); + + // Write our LEB128 magic string stream.write(Leb128MagicString, Leb128MagicStringSize); + + static_assert(std::is_signed_v, "Not implemented for unsigned types"); + std::uint32_t byte_count = 0; - for (std::size_t i = 0; i < count; ++i) { + for (std::size_t i = 0; i < count; ++i) + { IntType value = values[i]; std::uint8_t byte; - do { + do + { byte = value & 0x7f; value >>= 7; ++byte_count; - } while ((byte & 0x40) == 0 ? value != 0 : value != -1); + } + while ((byte & 0x40) == 0 ? value != 0 : value != -1); } + write_little_endian(stream, byte_count); + const std::uint32_t BUF_SIZE = 4096; std::uint8_t buf[BUF_SIZE]; std::uint32_t buf_pos = 0; + auto flush = [&]() { - if (buf_pos > 0) { + if (buf_pos > 0) + { stream.write(reinterpret_cast(buf), buf_pos); buf_pos = 0; } }; + auto write = [&](std::uint8_t byte) { buf[buf_pos++] = byte; - if (buf_pos == BUF_SIZE) flush(); + if (buf_pos == BUF_SIZE) + flush(); }; - for (std::size_t i = 0; i < count; ++i) { + + for (std::size_t i = 0; i < count; ++i) + { IntType value = values[i]; - while (true) { + while (true) + { std::uint8_t byte = value & 0x7f; value >>= 7; - if ((byte & 0x40) == 0 ? value == 0 : value == -1) { + if ((byte & 0x40) == 0 ? value == 0 : value == -1) + { write(byte); break; } write(byte | 0x80); } } + flush(); } From e551964ef63e4e4af4bb6132538b98fad4a51afe Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Mon, 26 Jun 2023 19:40:22 +0800 Subject: [PATCH 1105/1766] Negative extension on cutNodes based on depth This patch was inspired by candirufish original attempt at negative extensions here that failed yellow: https://tests.stockfishchess.org/tests/view/6486529065ffe077ca124f32 I tested some variations of the idea and tuned a depth condition for a modified version of it here https://tests.stockfishchess.org/tests/view/648db80a91c58631ce31fe00 after noticing abnormal scaling (ie many passed STC but not LTC) After some small tweaks I got the final version here Passed STC: LLR: 2.98 (-2.94,2.94) <0.00,2.00> Total: 122208 W: 32776 L: 32350 D: 57082 Ptnml(0-2): 310, 13250, 33553, 13686, 305 https://tests.stockfishchess.org/tests/view/64997934dc7002ce609d01e3 Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 145092 W: 39617 L: 39115 D: 66360 Ptnml(0-2): 54, 13691, 44552, 14197, 52 https://tests.stockfishchess.org/tests/view/649a1c5ddc7002ce609d0bff closes https://github.com/official-stockfish/Stockfish/pull/4644 Bench: 2637784 --- src/search.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 740ad71efee..fbc1755be73 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1097,6 +1097,10 @@ namespace { else if (ttValue >= beta) extension = -2 - !PvNode; + // If we are on a cutNode, reduce it based on depth (negative extension) (~1 Elo) + else if (cutNode) + extension = depth > 8 && depth < 17 ? -3 : -1; + // If the eval of ttMove is less than value, we reduce it (negative extension) (~1 Elo) else if (ttValue <= value) extension = -1; From 915532181f11812c80ef0b57bc018de4ea2155ec Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Sun, 25 Jun 2023 17:44:28 -0400 Subject: [PATCH 1106/1766] Update NNUE architecture to SFNNv7 with larger L1 size of 2048 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Creating this net involved: - a 5-step training process from scratch - greedy permuting L1 weights with https://github.com/official-stockfish/Stockfish/pull/4620 - leb128 compression with https://github.com/glinscott/nnue-pytorch/pull/251 - greedy 2- and 3- cycle permuting with https://github.com/official-stockfish/Stockfish/pull/4640 The 5 training steps were: 1. 400 epochs, lambda 1.0, lr 9.75e-4 UHOx2-wIsRight-multinet-dfrc-n5000-largeGensfen-d9.binpack (178G) nodes5000pv2_UHO.binpack data_pv-2_diff-100_nodes-5000.binpack wrongIsRight_nodes5000pv2.binpack multinet_pv-2_diff-100_nodes-5000.binpack dfrc_n5000.binpack large_gensfen_multipvdiff_100_d9.binpack ep399 chosen as start model for step2 2. 800 epochs, end-lambda 0.75, skip 16 LeelaFarseer-T78juntoaugT79marT80dec.binpack (141G) T60T70wIsRightFarseerT60T74T75T76.binpack test78-junjulaug2022-16tb7p.no-db.min.binpack test79-mar2022-16tb7p.no-db.min.binpack test80-dec2022-16tb7p.no-db.min.binpack ep559 chosen as start model for step3 3. 800 epochs, end-lambda 0.725, skip 20 leela96-dfrc99-v2-T80dectofeb-sk20-mar-v6-T77decT78janfebT79apr.binpack (223G) leela96-filt-v2.min.binpack dfrc99-16tb7p-eval-filt-v2.min.binpack test80-dec2022-16tb7p-filter-v6-sk20.min-mar2023.binpack test80-jan2023-16tb7p-filter-v6-sk20.min-mar2023.binpack test80-feb2023-16tb7p-filter-v6-sk20.min-mar2023.binpack test80-mar2023-2tb7p-filter-v6.min.binpack test77-dec2021-16tb7p.no-db.min.binpack test78-janfeb2022-16tb7p.no-db.min.binpack test79-apr2022-16tb7p.no-db.min.binpack ep499 chosen as start model for step4 4. 800 epochs, end-lambda 0.7, skip 24 0dd1cebea57 dataset https://github.com/official-stockfish/Stockfish/pull/4606 ep599 chosen as start model for step5 5. 800 epochs, end-lambda 0.7, skip 28 same dataset as step4 ep619 became nn-1b951f8b449d.nnue For the final step5 training: python3 easy_train.py \ --experiment-name L1-2048-S5-sameData-sk28-S4-0dd1cebea57-shuffled-S3-leela96-dfrc99-v2-T80dectofeb-sk20-mar-v6-T77decT78janfebT79apr-sk20-S2-LeelaFarseerT78T79T80-ep399-S1-UHOx2-wIsRight-multinet-dfrc-n5000-largeGensfen-d9 \ --training-dataset /data/leela96-dfrc99-T60novdec-v2-T80juntonovjanfebT79aprmayT78jantosepT77dec-v6dd-T80apr.binpack \ --early-fen-skipping 28 \ --nnue-pytorch-branch linrock/nnue-pytorch/misc-fixes-L1-2048 \ --engine-test-branch linrock/Stockfish/L1-2048 \ --start-from-engine-test-net False \ --start-from-model /data/experiments/experiment_L1-2048-S4-0dd1cebea57-shuffled-S3-leela96-dfrc99-v2-T80dectofeb-sk20-mar-v6-T77decT78janfebT79apr-sk20-S2-LeelaFarseerT78T79T80-ep399-S1-UHOx2-wIsRight-multinet-dfrc-n5000-largeGensfen-d9/training/run_0/nn-epoch599.nnue --max_epoch 800 \ --lr 4.375e-4 \ --gamma 0.995 \ --start-lambda 1.0 \ --end-lambda 0.7 \ --tui False \ --seed $RANDOM \ --gpus 0 SF training data components for the step1 dataset: https://drive.google.com/drive/folders/1yLCEmioC3Xx9KQr4T7uB6GnLm5icAYGU Leela training data for steps 2-5 can be found at: https://robotmoon.com/nnue-training-data/ Due to larger L1 size and slower inference, the speed penalty loses elo at STC. Measurements from 100 bench runs at depth 13 with x86-64-modern on Intel Core i5-1038NG7 2.00GHz: sf_base = 1240730 +/- 3443 (95%) sf_test = 1153341 +/- 2832 (95%) diff = -87388 +/- 1616 (95%) speedup = -7.04330% +/- 0.130% (95%) Local elo at 25k nodes per move (vs. L1-1536 nn-fdc1d0fe6455.nnue): nn-epoch619.nnue : 21.1 +/- 3.2 Failed STC: https://tests.stockfishchess.org/tests/view/6498ee93dc7002ce609cf979 LLR: -2.95 (-2.94,2.94) <0.00,2.00> Total: 11680 W: 3058 L: 3299 D: 5323 Ptnml(0-2): 44, 1422, 3149, 1181, 44 LTC: https://tests.stockfishchess.org/tests/view/649b32f5dc7002ce609d20cf Elo: 0.68 ± 1.5 (95%) LOS: 80.5% Total: 40000 W: 10887 L: 10809 D: 18304 Ptnml(0-2): 36, 3938, 11958, 4048, 20 nElo: 1.50 ± 3.4 (95%) PairsRatio: 1.02 Passed VLTC 180+1.8: https://tests.stockfishchess.org/tests/view/64992b43dc7002ce609cfd20 LLR: 3.06 (-2.94,2.94) <0.00,2.00> Total: 38086 W: 10612 L: 10338 D: 17136 Ptnml(0-2): 9, 3316, 12115, 3598, 5 Passed VLTC SMP 60+0.6 th 8: https://tests.stockfishchess.org/tests/view/649a21fedc7002ce609d0c7d LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 38936 W: 11091 L: 10820 D: 17025 Ptnml(0-2): 1, 2948, 13305, 3207, 7 closes https://github.com/official-stockfish/Stockfish/pull/4646 Bench: 2505168 --- src/evaluate.h | 2 +- src/nnue/nnue_architecture.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evaluate.h b/src/evaluate.h index c3321965d52..a1d46111938 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-a3d1bfca1672.nnue" + #define EvalFileDefaultName "nn-1b951f8b449d.nnue" namespace NNUE { diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index 413dbb3dcd7..65319b14bde 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -40,7 +40,7 @@ namespace Stockfish::Eval::NNUE { using FeatureSet = Features::HalfKAv2_hm; // Number of input feature dimensions after conversion -constexpr IndexType TransformedFeatureDimensions = 1536; +constexpr IndexType TransformedFeatureDimensions = 2048; constexpr IndexType PSQTBuckets = 8; constexpr IndexType LayerStacks = 8; From 9a2d50ecccfc737249245280586924ee3ef53abb Mon Sep 17 00:00:00 2001 From: ppigazzini Date: Sat, 1 Jul 2023 14:36:52 +0200 Subject: [PATCH 1107/1766] Make posix and msys2 shells consistent in CI In CI, it is typical for the process to halt immediately when an error is encountered. However, with our `shell: bash {0}` configuration, the process continues despite errors for posix shells. This commit updates the behavior of posix and msys2 shells to ensure consistency in terms of pipeline exit codes and stop conditions. We adopt the most appropriate default behavior as recommended by the GitHub documentation. Update the code that searches for the bench value in the git log: - to be compatible with the new shell settings - to retry the value from the first line that contains only the template and spaces/tabs/newlines see also https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsshell https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#exit-codes-and-error-action-preference https://github.com/msys2/setup-msys2/blob/main/main.js closes https://github.com/official-stockfish/Stockfish/pull/4653 No functional change --- .github/workflows/stockfish_arm_binaries.yml | 4 ++-- .github/workflows/stockfish_binaries.yml | 4 ++-- .github/workflows/stockfish_compile_test.yml | 8 ++++---- .github/workflows/stockfish_sanitizers.yml | 2 +- .github/workflows/stockfish_test.yml | 16 ++++++++-------- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/stockfish_arm_binaries.yml b/.github/workflows/stockfish_arm_binaries.yml index 52105eb6ac7..1afd8efa7a4 100644 --- a/.github/workflows/stockfish_arm_binaries.yml +++ b/.github/workflows/stockfish_arm_binaries.yml @@ -20,13 +20,13 @@ jobs: compiler: aarch64-linux-android21-clang++ emu: qemu-aarch64 comp: ndk - shell: bash {0} + shell: bash - name: Android NDK arm os: ubuntu-22.04 compiler: armv7a-linux-androideabi21-clang++ emu: qemu-arm comp: ndk - shell: bash {0} + shell: bash binaries: - armv8 - armv7 diff --git a/.github/workflows/stockfish_binaries.yml b/.github/workflows/stockfish_binaries.yml index 0a53cb03a99..5fe67d151d6 100644 --- a/.github/workflows/stockfish_binaries.yml +++ b/.github/workflows/stockfish_binaries.yml @@ -19,14 +19,14 @@ jobs: simple_name: ubuntu compiler: g++ comp: gcc - shell: bash {0} + shell: bash archive_ext: tar - name: MacOS 12 Apple Clang os: macos-12 simple_name: macos compiler: clang++ comp: clang - shell: bash {0} + shell: bash archive_ext: tar - name: Windows 2022 Mingw-w64 GCC x86_64 os: windows-2022 diff --git a/.github/workflows/stockfish_compile_test.yml b/.github/workflows/stockfish_compile_test.yml index c7280a85537..41f61daba8a 100644 --- a/.github/workflows/stockfish_compile_test.yml +++ b/.github/workflows/stockfish_compile_test.yml @@ -15,22 +15,22 @@ jobs: os: ubuntu-20.04 compiler: g++ comp: gcc - shell: bash {0} + shell: bash - name: Ubuntu 20.04 Clang os: ubuntu-20.04 compiler: clang++ comp: clang - shell: bash {0} + shell: bash - name: MacOS 12 Apple Clang os: macos-12 compiler: clang++ comp: clang - shell: bash {0} + shell: bash - name: MacOS 12 GCC 11 os: macos-12 compiler: g++-11 comp: gcc - shell: bash {0} + shell: bash - name: Windows 2022 Mingw-w64 GCC x86_64 os: windows-2022 compiler: g++ diff --git a/.github/workflows/stockfish_sanitizers.yml b/.github/workflows/stockfish_sanitizers.yml index 708c92275bf..ebfd809c295 100644 --- a/.github/workflows/stockfish_sanitizers.yml +++ b/.github/workflows/stockfish_sanitizers.yml @@ -16,7 +16,7 @@ jobs: os: ubuntu-20.04 compiler: g++ comp: gcc - shell: bash {0} + shell: bash sanitizers: - name: Run with thread sanitizer make_option: sanitize=thread diff --git a/.github/workflows/stockfish_test.yml b/.github/workflows/stockfish_test.yml index 28218402702..8a71d76b8ea 100644 --- a/.github/workflows/stockfish_test.yml +++ b/.github/workflows/stockfish_test.yml @@ -18,38 +18,38 @@ jobs: comp: gcc run_32bit_tests: true run_64bit_tests: true - shell: bash {0} + shell: bash - name: Ubuntu 20.04 Clang os: ubuntu-20.04 compiler: clang++ comp: clang run_32bit_tests: true run_64bit_tests: true - shell: bash {0} + shell: bash - name: Android NDK aarch64 os: ubuntu-22.04 compiler: aarch64-linux-android21-clang++ comp: ndk run_armv8_tests: true - shell: bash {0} + shell: bash - name: Android NDK arm os: ubuntu-22.04 compiler: armv7a-linux-androideabi21-clang++ comp: ndk run_armv7_tests: true - shell: bash {0} + shell: bash - name: MacOS 12 Apple Clang os: macos-12 compiler: clang++ comp: clang run_64bit_tests: true - shell: bash {0} + shell: bash - name: MacOS 12 GCC 11 os: macos-12 compiler: g++-11 comp: gcc run_64bit_tests: true - shell: bash {0} + shell: bash - name: Windows 2022 Mingw-w64 GCC x86_64 os: windows-2022 compiler: g++ @@ -115,8 +115,8 @@ jobs: - name: Extract the bench number from the commit history run: | - git log HEAD | grep -o "\b[Bb]ench[ :]\+[1-9][0-9]\{5,9\}\b" | head -n 1 | sed "s/[^0-9]//g" > git_sig - [ -s git_sig ] && echo "benchref=$(cat git_sig)" >> $GITHUB_ENV && echo "Reference bench:" $(cat git_sig) || echo "No bench found" + benchref=$(git log HEAD | grep -m 1 -o -x "[[:space:]]*\b[Bb]ench[ :]\+[1-9][0-9]\{5,7\}\b[[:space:]]*" | sed "s/[^0-9]//g") || true + [[ -n "$benchref" ]] && echo "benchref=$benchref" >> $GITHUB_ENV && echo "Reference bench: $benchref" || echo "No bench found" - name: Check compiler run: | From 8634717c6457f2b5fb0127cfb81c18505ff0072c Mon Sep 17 00:00:00 2001 From: disservin <45608332+Disservin@users.noreply.github.com> Date: Mon, 3 Jul 2023 08:20:56 +0200 Subject: [PATCH 1108/1766] Add bmi2 to CI generated binaries verify bench for avx2 and bmi2 as well closes https://github.com/official-stockfish/Stockfish/pull/4658 No functional change --- .github/workflows/stockfish_binaries.yml | 6 ++-- .github/workflows/stockfish_test.yml | 36 ++++++++++++++++-------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/.github/workflows/stockfish_binaries.yml b/.github/workflows/stockfish_binaries.yml index 5fe67d151d6..f7669b479aa 100644 --- a/.github/workflows/stockfish_binaries.yml +++ b/.github/workflows/stockfish_binaries.yml @@ -42,9 +42,12 @@ jobs: - x86-64 - x86-64-modern - x86-64-avx2 + - x86-64-bmi2 exclude: - binaries: x86-64-avx2 - config: {os: macos-12} + config: { os: macos-12 } + - binaries: x86-64-bmi2 + config: { os: macos-12 } defaults: run: working-directory: src @@ -165,4 +168,3 @@ jobs: tag_name: stockfish-dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} prerelease: true files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.${{ matrix.config.archive_ext }} - diff --git a/.github/workflows/stockfish_test.yml b/.github/workflows/stockfish_test.yml index 8a71d76b8ea..9d6bc20cf5f 100644 --- a/.github/workflows/stockfish_test.yml +++ b/.github/workflows/stockfish_test.yml @@ -134,7 +134,7 @@ jobs: # x86-32 tests - name: Test debug x86-32 build - if: ${{ matrix.config.run_32bit_tests }} + if: matrix.config.run_32bit_tests run: | export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" make clean @@ -142,28 +142,28 @@ jobs: ../tests/signature.sh $benchref - name: Test x86-32 build - if: ${{ matrix.config.run_32bit_tests }} + if: matrix.config.run_32bit_tests run: | make clean make -j2 ARCH=x86-32 build ../tests/signature.sh $benchref - name: Test x86-32-sse41-popcnt build - if: ${{ matrix.config.run_32bit_tests }} + if: matrix.config.run_32bit_tests run: | make clean make -j2 ARCH=x86-32-sse41-popcnt build ../tests/signature.sh $benchref - name: Test x86-32-sse2 build - if: ${{ matrix.config.run_32bit_tests }} + if: matrix.config.run_32bit_tests run: | make clean make -j2 ARCH=x86-32-sse2 build ../tests/signature.sh $benchref - name: Test general-32 build - if: ${{ matrix.config.run_32bit_tests }} + if: matrix.config.run_32bit_tests run: | make clean make -j2 ARCH=general-32 build @@ -172,36 +172,50 @@ jobs: # x86-64 tests - name: Test debug x86-64-modern build - if: ${{ matrix.config.run_64bit_tests }} + if: matrix.config.run_64bit_tests run: | export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" make clean make -j2 ARCH=x86-64-modern optimize=no debug=yes build ../tests/signature.sh $benchref + - name: Test x86-64-bmi2 build + if: matrix.config.run_64bit_tests && runner.os != 'macOS' + run: | + make clean + make -j2 ARCH=x86-64-bmi2 build + ../tests/signature.sh $benchref + + - name: Test x86-64-avx2 build + if: matrix.config.run_64bit_tests && runner.os != 'macOS' + run: | + make clean + make -j2 ARCH=x86-64-avx2 build + ../tests/signature.sh $benchref + - name: Test x86-64-modern build - if: ${{ matrix.config.run_64bit_tests }} + if: matrix.config.run_64bit_tests run: | make clean make -j2 ARCH=x86-64-modern build ../tests/signature.sh $benchref - name: Test x86-64-ssse3 build - if: ${{ matrix.config.run_64bit_tests }} + if: matrix.config.run_64bit_tests run: | make clean make -j2 ARCH=x86-64-ssse3 build ../tests/signature.sh $benchref - name: Test x86-64-sse3-popcnt build - if: ${{ matrix.config.run_64bit_tests }} + if: matrix.config.run_64bit_tests run: | make clean make -j2 ARCH=x86-64-sse3-popcnt build ../tests/signature.sh $benchref - name: Test x86-64 build - if: ${{ matrix.config.run_64bit_tests }} + if: matrix.config.run_64bit_tests run: | make clean make -j2 ARCH=x86-64 build @@ -248,7 +262,7 @@ jobs: # Other tests - name: Check perft and search reproducibility - if: ${{ matrix.config.run_64bit_tests }} + if: matrix.config.run_64bit_tests run: | make clean make -j2 ARCH=x86-64-modern build From 80564bcfcd3523c2a61e7a2c4bee36d4aada49d1 Mon Sep 17 00:00:00 2001 From: mstembera Date: Sun, 2 Jul 2023 19:48:18 -0700 Subject: [PATCH 1109/1766] Simplify lookup_count and clean up pieces(). https://github.com/official-stockfish/Stockfish/pull/4656 No functional change --- src/nnue/layers/affine_transform_sparse_input.h | 12 ++---------- src/position.h | 4 ++-- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/nnue/layers/affine_transform_sparse_input.h b/src/nnue/layers/affine_transform_sparse_input.h index e0c3a8a06bb..18c166cd9d7 100644 --- a/src/nnue/layers/affine_transform_sparse_input.h +++ b/src/nnue/layers/affine_transform_sparse_input.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "../nnue_common.h" #include "affine_transform.h" @@ -77,16 +78,7 @@ namespace Stockfish::Eval::NNUE::Layers { alignas(CacheLineSize) static inline const std::array lookup_count = [](){ std::array v; for (int i = 0; i < 256; ++i) - { - int j = i; - int k = 0; - while(j) - { - j &= j - 1; - ++k; - } - v[i] = k; - } + v[i] = unsigned(std::bitset<8>(i).count()); return v; }(); diff --git a/src/position.h b/src/position.h index 2e6014dbe6e..7d4b3771912 100644 --- a/src/position.h +++ b/src/position.h @@ -91,7 +91,7 @@ class Position { std::string fen() const; // Position representation - Bitboard pieces(PieceType pt) const; + Bitboard pieces(PieceType pt = ALL_PIECES) const; template Bitboard pieces(PieceType pt, PieceTypes... pts) const; Bitboard pieces(Color c) const; template Bitboard pieces(Color c, PieceTypes... pts) const; @@ -224,7 +224,7 @@ inline Piece Position::moved_piece(Move m) const { return piece_on(from_sq(m)); } -inline Bitboard Position::pieces(PieceType pt = ALL_PIECES) const { +inline Bitboard Position::pieces(PieceType pt) const { return byTypeBB[pt]; } From fa143922aecaab6f22fe818a5ef23b6ac42fe307 Mon Sep 17 00:00:00 2001 From: peregrineshahin Date: Tue, 27 Jun 2023 06:07:20 +0300 Subject: [PATCH 1110/1766] Fix pruning to (in TB loss) in Null move pruning. Current logic can apply Null move pruning on a dead-lost position returning an unproven loss (i.e. in TB loss score or mated in losing score) on nonPv nodes. on a default bench, this can be observed by adding this debugging line: ``` if (nullValue >= beta) { // Do not return unproven mate or TB scores nullValue = std::min(nullValue, VALUE_TB_WIN_IN_MAX_PLY-1); dbg_hit_on(nullValue <= VALUE_TB_LOSS_IN_MAX_PLY); // Hit #0: Total 73983 Hits 1 Hit Rate (%) 0.00135166 if (thisThread->nmpMinPly || depth < 14) return nullValue; ``` This fixes this very rare issue (happens at ~0.00135166% of the time) by eliminating the need to try Null Move Pruning with dead-lost positions and leaving it to be determined by a normal searching flow. The previous try to fix was not as safe enough because it was capping the returned value to (out of TB range) thus reviving the dead-lost position based on an artificial clamp (i.e. the in TB score/mate score can be lost on that nonPv node): https://tests.stockfishchess.org/tests/view/649756d5dc7002ce609cd794 Final fix: Passed STC: https://tests.stockfishchess.org/tests/view/649a5446dc7002ce609d1049 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 577280 W: 153613 L: 153965 D: 269702 Ptnml(0-2): 1320, 60594, 165190, 60190, 1346 Passed LTC: https://tests.stockfishchess.org/tests/view/649cd048dc7002ce609d4801 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 246432 W: 66769 L: 66778 D: 112885 Ptnml(0-2): 83, 22105, 78847, 22100, 81 closes https://github.com/official-stockfish/Stockfish/pull/4649 Bench: 2425978 --- src/search.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index fbc1755be73..8bd5ec9b48b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -782,7 +782,8 @@ namespace { && ss->staticEval >= beta - 21 * depth - improvement / 13 + 258 && !excludedMove && pos.non_pawn_material(us) - && (ss->ply >= thisThread->nmpMinPly)) + && ss->ply >= thisThread->nmpMinPly + && beta > VALUE_TB_LOSS_IN_MAX_PLY) { assert(eval - beta >= 0); From eb9aaf94891f17a62798c59226642fa172972204 Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Wed, 28 Jun 2023 18:36:11 +0800 Subject: [PATCH 1111/1766] Simplify away improvement term in null move search passed STC: https://tests.stockfishchess.org/tests/view/649c0d2edc7002ce609d33b5 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 271104 W: 72181 L: 72217 D: 126706 Ptnml(0-2): 691, 30042, 74129, 29992, 698 passed LTC: https://tests.stockfishchess.org/tests/view/649d0dd7dc7002ce609d4efa LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 183120 W: 49469 L: 49418 D: 84233 Ptnml(0-2): 84, 17636, 56063, 17699, 78 closes https://github.com/official-stockfish/Stockfish/pull/4650 Bench: 2642851 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 8bd5ec9b48b..656558f87c4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -779,7 +779,7 @@ namespace { && (ss-1)->statScore < 17329 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 21 * depth - improvement / 13 + 258 + && ss->staticEval >= beta - 21 * depth + 258 && !excludedMove && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly From 5f8480a730cbc789d230dd28f276b8d35ce0a8a4 Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Thu, 22 Jun 2023 19:19:21 +0800 Subject: [PATCH 1112/1766] Simplify score improvement reduction Reduce depth by 2 based on score improvement, only for depths 3 to 11. Simplification STC: https://tests.stockfishchess.org/tests/view/64929a53dc7002ce609c7807 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 238912 W: 63466 L: 63468 D: 111978 Ptnml(0-2): 564, 26262, 65805, 26262, 563 Simplification LTC: https://tests.stockfishchess.org/tests/view/64942e47dc7002ce609c9e07 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 64452 W: 17485 L: 17320 D: 29647 Ptnml(0-2): 19, 6161, 19706, 6316, 24 closes https://github.com/official-stockfish/Stockfish/pull/4637 Bench: 2740142 --- src/search.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 656558f87c4..ed2b5743e6e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1323,12 +1323,12 @@ namespace { } else { - // Reduce other moves if we have found at least one score improvement (~1 Elo) - // Reduce more for depth > 3 and depth < 12 (~1 Elo) - if ( depth > 1 + // Reduce other moves if we have found at least one score improvement (~2 Elo) + if ( depth > 2 + && depth < 12 && beta < 14362 && value > -12393) - depth -= depth > 3 && depth < 12 ? 2 : 1; + depth -= 2; assert(depth > 0); alpha = value; // Update alpha! Always alpha < beta From 9cd563cb54b4091c48e16b524b3c9c15b7824c4f Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Tue, 27 Jun 2023 15:13:59 +0300 Subject: [PATCH 1113/1766] Improving grammar and readability of comments closes https://github.com/official-stockfish/Stockfish/pull/4643 No functional change --- src/search.cpp | 62 +++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ed2b5743e6e..76d055e3039 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -67,7 +67,7 @@ namespace { return Value(140 * (d - improving)); } - // Reductions lookup table, initialized at startup + // Reductions lookup table initialized at startup int Reductions[MAX_MOVES]; // [depth or moveNumber] Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) { @@ -92,7 +92,7 @@ namespace { // Skill structure is used to implement strength limit. If we have an uci_elo then // we convert it 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) + // (goldfish 1.13 = 2000) and a fit through Ordo derived Elo for a match (TC 60+0.6) // results spanning a wide range of k values. struct Skill { Skill(int skill_level, int uci_elo) { @@ -304,7 +304,7 @@ void Thread::search() { Skill skill(Options["Skill Level"], Options["UCI_LimitStrength"] ? int(Options["UCI_Elo"]) : 0); // When playing with strength handicap enable MultiPV search that we will - // use behind the scenes to retrieve a set of possible moves. + // use behind-the-scenes to retrieve a set of possible moves. if (skill.enabled()) multiPV = std::max(multiPV, (size_t)4); @@ -321,7 +321,7 @@ void Thread::search() { if (mainThread) totBestMoveChanges /= 2; - // Save the last iteration's scores before first PV line is searched and + // Save the last iteration's scores before the first PV line is searched and // all the move scores except the (new) PV are set to -VALUE_INFINITE. for (RootMove& rm : rootMoves) rm.previousScore = rm.score; @@ -363,16 +363,16 @@ void Thread::search() { int failedHighCnt = 0; while (true) { - // Adjust the effective depth searched, but ensuring at least one effective increment for every + // Adjust the effective depth searched, but ensure at least one effective increment for every // four searchAgain steps (see issue #2717). Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt - 3 * (searchAgainCounter + 1) / 4); bestValue = Stockfish::search(rootPos, ss, alpha, beta, adjustedDepth, false); // Bring the best move to the front. It is critical that sorting // is done with a stable algorithm because all the values but the - // first and eventually the new best one are set to -VALUE_INFINITE + // first and eventually the new best one is set to -VALUE_INFINITE // and we want to keep the same order for all the moves except the - // new PV that goes to the front. Note that in case of MultiPV + // new PV that goes to the front. Note that in the case of MultiPV // search the already searched PV lines are preserved. std::stable_sort(rootMoves.begin() + pvIdx, rootMoves.begin() + pvLast); @@ -440,7 +440,7 @@ void Thread::search() { if (!mainThread) continue; - // If skill level is enabled and time is up, pick a sub-optimal best move + // If the skill level is enabled and time is up, pick a sub-optimal best move if (skill.enabled() && skill.time_to_pick(rootDepth)) skill.pick_best(multiPV); @@ -498,7 +498,7 @@ void Thread::search() { mainThread->previousTimeReduction = timeReduction; - // If skill level is enabled, swap best PV line with the sub-optimal one + // If the skill level is enabled, swap the best PV line with the sub-optimal one if (skill.enabled()) std::swap(rootMoves[0], *std::find(rootMoves.begin(), rootMoves.end(), skill.best ? skill.best : skill.pick_best(multiPV))); @@ -515,7 +515,7 @@ namespace { constexpr bool PvNode = nodeType != NonPV; constexpr bool rootNode = nodeType == Root; - // Check if we have an upcoming move which draws by repetition, or + // Check if we have an upcoming move that draws by repetition, or // if the opponent had an alternative move earlier to this position. if ( !rootNode && pos.rule50_count() >= 3 @@ -580,8 +580,8 @@ namespace { // would be at best mate_in(ss->ply+1), but if alpha is already bigger because // a shorter mate was found upward in the tree then there is no need to search // because we will never beat the current alpha. Same logic but with reversed - // signs applies also in the opposite condition of being mated instead of giving - // mate. In this case return a fail-high score. + // signs apply also in the opposite condition of being mated instead of giving + // mate. In this case, return a fail-high score. alpha = std::max(mated_in(ss->ply), alpha); beta = std::min(mate_in(ss->ply+1), beta); if (alpha >= beta) @@ -734,7 +734,7 @@ namespace { else { ss->staticEval = eval = evaluate(pos); - // Save static evaluation into transposition table + // Save static evaluation into the transposition table tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); } @@ -845,10 +845,10 @@ namespace { if ( !PvNode && depth > 3 && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY - // if value from transposition table is lower than probCutBeta, don't attempt probCut + // If value from transposition table is lower than probCutBeta, don't attempt probCut // there and in further interactions with transposition table cutoff depth is set to depth - 3 // because probCut search has depth set to depth - 4 but we also do a move before it - // so effective depth is equal to depth - 3 + // So effective depth is equal to depth - 3 && !( tte->depth() >= depth - 3 && ttValue != VALUE_NONE && ttValue < probCutBeta)) @@ -920,7 +920,7 @@ namespace { moveCountPruning = singularQuietLMR = false; // Indicate PvNodes that will probably fail low if the node was searched - // at a depth equal or greater than the current depth, and the result of this search was a fail low. + // at a depth equal to or greater than the current depth, and the result of this search was a fail low. bool likelyFailLow = PvNode && ttMove && (tte->bound() & BOUND_UPPER) @@ -936,8 +936,8 @@ namespace { continue; // At root obey the "searchmoves" option and skip moves not listed in Root - // Move List. As a consequence any illegal move is also skipped. In MultiPV - // mode we also skip PV moves which have been already searched and those + // Move List. As a consequence, any illegal move is also skipped. In MultiPV + // mode we also skip PV moves that have been already searched and those // of lower "TB rank" if we are in a TB root position. if (rootNode && !std::count(thisThread->rootMoves.begin() + thisThread->pvIdx, thisThread->rootMoves.begin() + thisThread->pvLast, move)) @@ -1005,7 +1005,7 @@ namespace { { Square sq = pop_lsb(leftEnemies); attacks |= pos.attackers_to(sq, occupied) & pos.pieces(us) & occupied; - // don't consider pieces which were already threatened/hanging before SEE exchanges + // Don't consider pieces that were already threatened/hanging before SEE exchanges if (attacks && (sq != pos.square(~us) && (pos.attackers_to(sq, pos.pieces()) & pos.pieces(us)))) attacks = 0; } @@ -1090,7 +1090,7 @@ namespace { // Our ttMove is assumed to fail high, and now we failed high also on a reduced // search without the ttMove. So we assume this expected Cut-node is not singular, // that multiple moves fail high, and we can prune the whole subtree by returning - // a soft bound. + // a softbound. else if (singularBeta >= beta) return singularBeta; @@ -1186,7 +1186,7 @@ namespace { // Step 17. Late moves reduction / extension (LMR, ~117 Elo) // We use various heuristics for the sons of a node after the first son has - // been searched. In general we would like to reduce them, but there are many + // been searched. In general, we would like to reduce them, but there are many // cases where we extend a son if it has good chances to be "interesting". if ( depth >= 2 && moveCount > 1 + (PvNode && ss->ply <= 1) @@ -1201,10 +1201,10 @@ namespace { value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); - // Do full depth search when reduced LMR search fails high + // Do a full-depth search when reduced LMR search fails high if (value > alpha && d < newDepth) { - // Adjust full depth search based on LMR results - if result + // Adjust full-depth search based on LMR results - if the result // was good enough search deeper, if it was bad enough search shallower const bool doDeeperSearch = value > (bestValue + 64 + 11 * (newDepth - d)); const bool doEvenDeeperSearch = value > alpha + 711 && ss->doubleExtensions <= 6; @@ -1225,7 +1225,7 @@ namespace { } } - // Step 18. Full depth search when LMR is skipped. If expected reduction is high, reduce its depth by 1. + // Step 18. Full-depth search when LMR is skipped. If expected reduction is high, reduce its depth by 1. else if (!PvNode || moveCount > 1) { // Increase reduction for cut nodes and not ttMove (~1 Elo) @@ -1298,7 +1298,7 @@ namespace { ++thisThread->bestMoveChanges; } else - // All other moves but the PV are set to the lowest value: this + // All other moves but the PV, are set to the lowest value: this // is not a problem when sorting because the sort is stable and the // move position in the list is preserved - just the PV is pushed up. rm.score = -VALUE_INFINITE; @@ -1337,7 +1337,7 @@ namespace { } - // If the move is worse than some previously searched move, remember it to update its stats later + // If the move is worse than some previously searched move, remember it, to update its stats later if (move != bestMove) { if (capture && captureCount < 32) @@ -1349,7 +1349,7 @@ namespace { } // The following condition would detect a stop only after move loop has been - // completed. But in this case bestValue is valid because we have fully + // completed. But in this case, bestValue is valid because we have fully // searched our subtree, and we can anyhow save the result in TT. /* if (Threads.stop) @@ -1368,7 +1368,7 @@ namespace { ss->inCheck ? mated_in(ss->ply) : VALUE_DRAW; - // If there is a move which produces search value greater than alpha we update stats of searched moves + // If there is a move that produces search value greater than alpha we update the stats of searched moves else if (bestMove) update_all_stats(pos, ss, bestMove, bestValue, beta, prevSq, quietsSearched, quietCount, capturesSearched, captureCount, depth); @@ -1751,7 +1751,7 @@ namespace { for (int i : {1, 2, 4, 6}) { - // Only update first 2 continuation histories if we are in check + // Only update the first 2 continuation histories if we are in check if (ss->inCheck && i > 2) break; if (is_ok((ss-i)->currentMove)) @@ -1784,7 +1784,7 @@ namespace { } } - // When playing with strength handicap, choose best move among a set of RootMoves + // When playing with strength handicap, choose the best move among a set of RootMoves // using a statistical rule dependent on 'level'. Idea by Heinz van Saanen. Move Skill::pick_best(size_t multiPV) { @@ -1915,7 +1915,7 @@ string UCI::pv(const Position& pos, Depth depth) { /// RootMove::extract_ponder_from_tt() is called in case we have no ponder move /// before exiting the search, for instance, in case we stop the search during a /// fail high at root. We try hard to have a ponder move to return to the GUI, -/// otherwise in case of 'ponder on' we have nothing to think on. +/// otherwise in case of 'ponder on' we have nothing to think about. bool RootMove::extract_ponder_from_tt(Position& pos) { From 95ce443aaacadea777f34d87b0abf984e724f0dd Mon Sep 17 00:00:00 2001 From: rn5f107s2 Date: Fri, 16 Jun 2023 18:49:31 +0200 Subject: [PATCH 1114/1766] simplified gives check castling tested verifying perft and bench is unchanged on a larger set of epds for both standard and FRC chess. Passed non-regression STC: https://tests.stockfishchess.org/tests/live_elo/648587be65ffe077ca123d78 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 153632 W: 41015 L: 40928 D: 71689 Ptnml(0-2): 377, 16077, 43816, 16174, 372 closes https://github.com/official-stockfish/Stockfish/pull/4628 No functional change --- AUTHORS | 1 + src/position.cpp | 10 ++++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/AUTHORS b/AUTHORS index 2e9ae7805fd..792893944c4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -179,6 +179,7 @@ Raminder Singh renouve Reuven Peleg (R-Peleg) Richard Lloyd (Richard-Lloyd) +rn5f107s2 Rodrigo Exterckötter Tjäder Rodrigo Roim (roim) Ronald de Man (syzygy1, syzygy) diff --git a/src/position.cpp b/src/position.cpp index 2a9d798ff7d..a052cf32f03 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -638,9 +638,9 @@ bool Position::gives_check(Move m) const { return true; // Is there a discovered check? - if ( (blockers_for_king(~sideToMove) & from) - && !aligned(from, to, square(~sideToMove))) - return true; + if (blockers_for_king(~sideToMove) & from) + return !aligned(from, to, square(~sideToMove)) + || type_of(m) == CASTLING; switch (type_of(m)) { @@ -665,11 +665,9 @@ bool Position::gives_check(Move m) const { default: //CASTLING { // Castling is encoded as 'king captures the rook' - Square ksq = square(~sideToMove); Square rto = relative_square(sideToMove, to > from ? SQ_F1 : SQ_D1); - return (attacks_bb(rto) & ksq) - && (attacks_bb(rto, pieces() ^ from ^ to) & ksq); + return check_squares(ROOK) & rto; } } } From ca5d9a5ff0739a63f7b0a184193dfb9de3c57156 Mon Sep 17 00:00:00 2001 From: ppigazzini Date: Fri, 16 Jun 2023 13:01:20 +0200 Subject: [PATCH 1115/1766] Extract bench according to wiki instructions - loop through the commits starting from the latest one - read the bench value from the last match, if any, of the template in the commit body text closes https://github.com/official-stockfish/Stockfish/pull/4627 No functional change --- .github/workflows/stockfish_test.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/stockfish_test.yml b/.github/workflows/stockfish_test.yml index 9d6bc20cf5f..1ea4b309235 100644 --- a/.github/workflows/stockfish_test.yml +++ b/.github/workflows/stockfish_test.yml @@ -103,6 +103,10 @@ jobs: echo "ANDROID_NDK_BIN=$ANDROID_NDK_BIN" >> $GITHUB_ENV fi + - name: Download required macOS packages + if: runner.os == 'macOS' + run: brew install coreutils + - name: Setup msys and install required packages if: runner.os == 'Windows' uses: msys2/setup-msys2@v2 @@ -115,8 +119,10 @@ jobs: - name: Extract the bench number from the commit history run: | - benchref=$(git log HEAD | grep -m 1 -o -x "[[:space:]]*\b[Bb]ench[ :]\+[1-9][0-9]\{5,7\}\b[[:space:]]*" | sed "s/[^0-9]//g") || true - [[ -n "$benchref" ]] && echo "benchref=$benchref" >> $GITHUB_ENV && echo "Reference bench: $benchref" || echo "No bench found" + for ((n=0; n<100; n++)); do + benchref=$(git log HEAD~$n -1 | tac | grep -m 1 -o -x '[[:space:]]*\b[Bb]ench[ :]\+[1-9][0-9]\{5,7\}\b[[:space:]]*' | sed 's/[^0-9]//g') && break || true + done + [[ -n "$benchref" ]] && echo "benchref=$benchref" >> $GITHUB_ENV && echo "From commit: $(git rev-parse HEAD~$n)" && echo "Reference bench: $benchref" || echo "No bench found" - name: Check compiler run: | From e87e103ca994570d42f30f61f923986656a5df14 Mon Sep 17 00:00:00 2001 From: ppigazzini Date: Mon, 3 Jul 2023 19:41:13 +0200 Subject: [PATCH 1116/1766] Remove leftover braces for if conditional in CI closes https://github.com/official-stockfish/Stockfish/pull/4660 No functional change --- .github/workflows/stockfish_test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/stockfish_test.yml b/.github/workflows/stockfish_test.yml index 1ea4b309235..b53d7e275d0 100644 --- a/.github/workflows/stockfish_test.yml +++ b/.github/workflows/stockfish_test.yml @@ -237,7 +237,7 @@ jobs: # armv8 tests - name: Test armv8 build - if: ${{ matrix.config.run_armv8_tests }} + if: matrix.config.run_armv8_tests run: | export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH export LDFLAGS="-static -Wno-unused-command-line-argument" @@ -248,7 +248,7 @@ jobs: # armv7 tests - name: Test armv7 build - if: ${{ matrix.config.run_armv7_tests }} + if: matrix.config.run_armv7_tests run: | export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH export LDFLAGS="-static -Wno-unused-command-line-argument" @@ -257,7 +257,7 @@ jobs: ../tests/signature.sh $benchref - name: Test armv7-neon build - if: ${{ matrix.config.run_armv7_tests }} + if: matrix.config.run_armv7_tests run: | export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH export LDFLAGS="-static -Wno-unused-command-line-argument" From 19e2a8850483c67835c0829ef016c6ede988817b Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 6 Jul 2023 18:30:51 +0200 Subject: [PATCH 1117/1766] Revise extract bench from git log in CI order commits differently closes https://github.com/official-stockfish/Stockfish/pull/4668 No functional change --- .github/workflows/stockfish_test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/stockfish_test.yml b/.github/workflows/stockfish_test.yml index b53d7e275d0..05592dae530 100644 --- a/.github/workflows/stockfish_test.yml +++ b/.github/workflows/stockfish_test.yml @@ -119,8 +119,8 @@ jobs: - name: Extract the bench number from the commit history run: | - for ((n=0; n<100; n++)); do - benchref=$(git log HEAD~$n -1 | tac | grep -m 1 -o -x '[[:space:]]*\b[Bb]ench[ :]\+[1-9][0-9]\{5,7\}\b[[:space:]]*' | sed 's/[^0-9]//g') && break || true + for hash in $(git rev-list -100 HEAD); do + benchref=$(git show -s $hash | tac | grep -m 1 -o -x '[[:space:]]*\b[Bb]ench[ :]\+[1-9][0-9]\{5,7\}\b[[:space:]]*' | sed 's/[^0-9]//g') && break || true done [[ -n "$benchref" ]] && echo "benchref=$benchref" >> $GITHUB_ENV && echo "From commit: $(git rev-parse HEAD~$n)" && echo "Reference bench: $benchref" || echo "No bench found" From f8e65d82ebf5754427a63116532733b7b7002f29 Mon Sep 17 00:00:00 2001 From: mstembera Date: Mon, 3 Jul 2023 12:59:42 -0700 Subject: [PATCH 1118/1766] Simplify away lookup_count. https://tests.stockfishchess.org/tests/view/64a3c1a93ee09aa549c53167 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 32832 W: 8497 L: 8280 D: 16055 Ptnml(0-2): 80, 3544, 8967, 3729, 96 closes https://github.com/official-stockfish/Stockfish/pull/4662 No functional change --- src/nnue/layers/affine_transform_sparse_input.h | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/nnue/layers/affine_transform_sparse_input.h b/src/nnue/layers/affine_transform_sparse_input.h index 18c166cd9d7..3c7defcc42c 100644 --- a/src/nnue/layers/affine_transform_sparse_input.h +++ b/src/nnue/layers/affine_transform_sparse_input.h @@ -24,7 +24,6 @@ #include #include #include -#include #include #include "../nnue_common.h" #include "affine_transform.h" @@ -75,12 +74,6 @@ namespace Stockfish::Eval::NNUE::Layers { } return v; }(); - alignas(CacheLineSize) static inline const std::array lookup_count = [](){ - std::array v; - for (int i = 0; i < 256; ++i) - v[i] = unsigned(std::bitset<8>(i).count()); - return v; - }(); // Find indices of nonzero numbers in an int32_t array template @@ -120,7 +113,7 @@ namespace Stockfish::Eval::NNUE::Layers { const auto lookup = (nnz >> (j * 8)) & 0xFF; const auto offsets = _mm_loadu_si128(reinterpret_cast(&lookup_indices[lookup])); _mm_storeu_si128(reinterpret_cast<__m128i*>(out + count), _mm_add_epi16(base, offsets)); - count += lookup_count[lookup]; + count += popcount(lookup); base = _mm_add_epi16(base, increment); } } From 9ba24912c1bac753fdbde0ae78e19867dccb7500 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 5 Jul 2023 20:15:49 +0200 Subject: [PATCH 1119/1766] Add armv8-dotprod to CI binaries also generate binaries for more recent Android hardware. closes https://github.com/official-stockfish/Stockfish/pull/4663 No functional change --- .github/workflows/stockfish_arm_binaries.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/stockfish_arm_binaries.yml b/.github/workflows/stockfish_arm_binaries.yml index 1afd8efa7a4..4db216eb5fb 100644 --- a/.github/workflows/stockfish_arm_binaries.yml +++ b/.github/workflows/stockfish_arm_binaries.yml @@ -28,10 +28,13 @@ jobs: comp: ndk shell: bash binaries: + - armv8-dotprod - armv8 - armv7 - armv7-neon exclude: + - binaries: armv8-dotprod + config: {compiler: armv7a-linux-androideabi21-clang++} - binaries: armv8 config: {compiler: armv7a-linux-androideabi21-clang++} - binaries: armv7 @@ -155,4 +158,4 @@ jobs: name: Stockfish dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} tag_name: stockfish-dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} prerelease: true - files: stockfish-android-${{ matrix.binaries }}.tar \ No newline at end of file + files: stockfish-android-${{ matrix.binaries }}.tar From e699fee513ce26b3794ac43d08826c89106e10ea Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Sun, 25 Jun 2023 20:57:50 -0400 Subject: [PATCH 1120/1766] Update default net to nn-c38c3d8d3920.nnue This was a later epoch from the same experiment that led to the previous master net. After training, it was prepared the same way: 1. greedy permuting L1 weights with https://github.com/official-stockfish/Stockfish/pull/4620 2. leb128 compression with https://github.com/glinscott/nnue-pytorch/pull/251 3. greedy 2- and 3- cycle permuting with https://github.com/official-stockfish/Stockfish/pull/4640 Local elo at 25k nodes per move (vs. L1-1536 nn-fdc1d0fe6455.nnue): nn-epoch739.nnue : 20.2 +/- 1.7 Passed STC: https://tests.stockfishchess.org/tests/view/64a050b33ee09aa549c4e4c8 LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 195552 W: 49977 L: 49430 D: 96145 Ptnml(0-2): 556, 22775, 50607, 23242, 596 Passed LTC: https://tests.stockfishchess.org/tests/view/64a127bd3ee09aa549c4f60c LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 235452 W: 60327 L: 59609 D: 115516 Ptnml(0-2): 119, 25173, 66426, 25887, 121 closes https://github.com/official-stockfish/Stockfish/pull/4666 bench 2427629 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index a1d46111938..abdbef9010d 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-1b951f8b449d.nnue" + #define EvalFileDefaultName "nn-c38c3d8d3920.nnue" namespace NNUE { From ee023d7fd78a96c10ae157c0d3174f091a4e09d1 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 6 Jul 2023 23:29:11 +0200 Subject: [PATCH 1121/1766] Fix CI output closes https://github.com/official-stockfish/Stockfish/pull/4669 No functional change --- .github/workflows/stockfish_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stockfish_test.yml b/.github/workflows/stockfish_test.yml index 05592dae530..cd80e223853 100644 --- a/.github/workflows/stockfish_test.yml +++ b/.github/workflows/stockfish_test.yml @@ -122,7 +122,7 @@ jobs: for hash in $(git rev-list -100 HEAD); do benchref=$(git show -s $hash | tac | grep -m 1 -o -x '[[:space:]]*\b[Bb]ench[ :]\+[1-9][0-9]\{5,7\}\b[[:space:]]*' | sed 's/[^0-9]//g') && break || true done - [[ -n "$benchref" ]] && echo "benchref=$benchref" >> $GITHUB_ENV && echo "From commit: $(git rev-parse HEAD~$n)" && echo "Reference bench: $benchref" || echo "No bench found" + [[ -n "$benchref" ]] && echo "benchref=$benchref" >> $GITHUB_ENV && echo "From commit: $hash" && echo "Reference bench: $benchref" || echo "No bench found" - name: Check compiler run: | From 6a8767a0d5d9502e6d4de1bef97468b5d6fab80a Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Fri, 7 Jul 2023 20:19:31 +0800 Subject: [PATCH 1122/1766] Simplify PvNode reduction Simplification STC: https://tests.stockfishchess.org/tests/view/64a415803ee09aa549c539c3 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 37856 W: 9719 L: 9504 D: 18633 Ptnml(0-2): 98, 4277, 9977, 4464, 112 Simplification LTC: https://tests.stockfishchess.org/tests/view/64a5ffe202cd07745c60f360 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 55878 W: 14323 L: 14138 D: 27417 Ptnml(0-2): 21, 5993, 15732, 6166, 27 closes https://github.com/official-stockfish/Stockfish/pull/4673 Bench: 2604965 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 76d055e3039..1f8f361c409 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1162,7 +1162,7 @@ namespace { // Decrease reduction for PvNodes based on depth (~2 Elo) if (PvNode) - r -= 1 + 12 / (3 + depth); + r -= 1 + (depth < 6); // Decrease reduction if ttMove has been singularly extended (~1 Elo) if (singularQuietLMR) From af110e02ec96cdb46cf84c68252a1da15a902395 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 1 Jun 2023 08:09:07 +0200 Subject: [PATCH 1123/1766] Remove classical evaluation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit since the introduction of NNUE (first released with Stockfish 12), we have maintained the classical evaluation as part of SF in frozen form. The idea that this code could lead to further inputs to the NN or search did not materialize. Now, after five releases, this PR removes the classical evaluation from SF. Even though this evaluation is probably the best of its class, it has become unimportant for the engine's strength, and there is little need to maintain this code (roughly 25% of SF) going forward, or to expend resources on trying to improve its integration in the NNUE eval. Indeed, it had still a very limited use in the current SF, namely for the evaluation of positions that are nearly decided based on material difference, where the speed of the classical evaluation outweights its inaccuracies. This impact on strength is small, roughly 2Elo, and probably decreasing in importance as the TC grows. Potentially, removal of this code could lead to the development of techniques to have faster, but less accurate NN evaluation, for certain positions. STC https://tests.stockfishchess.org/tests/view/64a320173ee09aa549c52157 Elo: -2.35 ± 1.1 (95%) LOS: 0.0% Total: 100000 W: 24916 L: 25592 D: 49492 Ptnml(0-2): 287, 12123, 25841, 11477, 272 nElo: -4.62 ± 2.2 (95%) PairsRatio: 0.95 LTC https://tests.stockfishchess.org/tests/view/64a320293ee09aa549c5215b Elo: -1.74 ± 1.0 (95%) LOS: 0.0% Total: 100000 W: 25010 L: 25512 D: 49478 Ptnml(0-2): 44, 11069, 28270, 10579, 38 nElo: -3.72 ± 2.2 (95%) PairsRatio: 0.96 VLTC SMP https://tests.stockfishchess.org/tests/view/64a3207c3ee09aa549c52168 Elo: -1.70 ± 0.9 (95%) LOS: 0.0% Total: 100000 W: 25673 L: 26162 D: 48165 Ptnml(0-2): 8, 9455, 31569, 8954, 14 nElo: -3.95 ± 2.2 (95%) PairsRatio: 0.95 closes https://github.com/official-stockfish/Stockfish/pull/4674 Bench: 1444646 --- src/Makefile | 4 +- src/benchmark.cpp | 9 - src/bitbase.cpp | 172 ------- src/bitboard.h | 7 - src/endgame.cpp | 747 ---------------------------- src/endgame.h | 126 ----- src/evaluate.cpp | 992 +------------------------------------ src/main.cpp | 3 - src/material.cpp | 229 --------- src/material.h | 71 --- src/nnue/evaluate_nnue.cpp | 3 +- src/pawns.cpp | 305 ------------ src/pawns.h | 70 --- src/position.cpp | 38 +- src/thread.h | 4 - src/ucioption.cpp | 2 - 16 files changed, 37 insertions(+), 2745 deletions(-) delete mode 100644 src/bitbase.cpp delete mode 100644 src/endgame.cpp delete mode 100644 src/endgame.h delete mode 100644 src/material.cpp delete mode 100644 src/material.h delete mode 100644 src/pawns.cpp delete mode 100644 src/pawns.h diff --git a/src/Makefile b/src/Makefile index 82664618bb7..a0f098fa678 100644 --- a/src/Makefile +++ b/src/Makefile @@ -56,8 +56,8 @@ else endif ### Source and object files -SRCS = benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.cpp main.cpp \ - material.cpp misc.cpp movegen.cpp movepick.cpp pawns.cpp position.cpp psqt.cpp \ +SRCS = benchmark.cpp bitboard.cpp evaluate.cpp main.cpp \ + misc.cpp movegen.cpp movepick.cpp position.cpp psqt.cpp \ search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \ nnue/evaluate_nnue.cpp nnue/features/half_ka_v2_hm.cpp diff --git a/src/benchmark.cpp b/src/benchmark.cpp index a1ad055057b..baa90140f9b 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -153,24 +153,15 @@ vector setup_bench(const Position& current, istream& is) { list.emplace_back("setoption name Hash value " + ttSize); list.emplace_back("ucinewgame"); - size_t posCounter = 0; - for (const string& fen : fens) if (fen.find("setoption") != string::npos) list.emplace_back(fen); else { - if (evalType == "classical" || (evalType == "mixed" && posCounter % 2 == 0)) - list.emplace_back("setoption name Use NNUE value false"); - else if (evalType == "NNUE" || (evalType == "mixed" && posCounter % 2 != 0)) - list.emplace_back("setoption name Use NNUE value true"); list.emplace_back("position fen " + fen); list.emplace_back(go); - ++posCounter; } - list.emplace_back("setoption name Use NNUE value true"); - return list; } diff --git a/src/bitbase.cpp b/src/bitbase.cpp deleted file mode 100644 index e21d1fe9536..00000000000 --- a/src/bitbase.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include -#include -#include - -#include "bitboard.h" -#include "types.h" - -namespace Stockfish { - -namespace { - - // There are 24 possible pawn squares: files A to D and ranks from 2 to 7. - // Positions with the pawn on files E to H will be mirrored before probing. - constexpr unsigned MAX_INDEX = 2*24*64*64; // stm * psq * wksq * bksq = 196608 - - std::bitset KPKBitbase; - - // A KPK bitbase index is an integer in [0, IndexMax] range - // - // Information is mapped in a way that minimizes the number of iterations: - // - // bit 0- 5: white king square (from SQ_A1 to SQ_H8) - // bit 6-11: black king square (from SQ_A1 to SQ_H8) - // bit 12: side to move (WHITE or BLACK) - // bit 13-14: white pawn file (from FILE_A to FILE_D) - // bit 15-17: white pawn RANK_7 - rank (from RANK_7 - RANK_7 to RANK_7 - RANK_2) - unsigned index(Color stm, Square bksq, Square wksq, Square psq) { - return int(wksq) | (bksq << 6) | (stm << 12) | (file_of(psq) << 13) | ((RANK_7 - rank_of(psq)) << 15); - } - - enum Result { - INVALID = 0, - UNKNOWN = 1, - DRAW = 2, - WIN = 4 - }; - - Result& operator|=(Result& r, Result v) { return r = Result(r | v); } - - struct KPKPosition { - KPKPosition() = default; - explicit KPKPosition(unsigned idx); - operator Result() const { return result; } - Result classify(const std::vector& db); - - Color stm; - Square ksq[COLOR_NB], psq; - Result result; - }; - -} // namespace - -bool Bitbases::probe(Square wksq, Square wpsq, Square bksq, Color stm) { - - assert(file_of(wpsq) <= FILE_D); - - return KPKBitbase[index(stm, bksq, wksq, wpsq)]; -} - - -void Bitbases::init() { - - std::vector db(MAX_INDEX); - unsigned idx, repeat = 1; - - // Initialize db with known win / draw positions - for (idx = 0; idx < MAX_INDEX; ++idx) - db[idx] = KPKPosition(idx); - - // Iterate through the positions until none of the unknown positions can be - // changed to either wins or draws (15 cycles needed). - while (repeat) - for (repeat = idx = 0; idx < MAX_INDEX; ++idx) - repeat |= (db[idx] == UNKNOWN && db[idx].classify(db) != UNKNOWN); - - // Fill the bitbase with the decisive results - for (idx = 0; idx < MAX_INDEX; ++idx) - if (db[idx] == WIN) - KPKBitbase.set(idx); -} - -namespace { - - KPKPosition::KPKPosition(unsigned idx) { - - ksq[WHITE] = Square((idx >> 0) & 0x3F); - ksq[BLACK] = Square((idx >> 6) & 0x3F); - stm = Color ((idx >> 12) & 0x01); - psq = make_square(File((idx >> 13) & 0x3), Rank(RANK_7 - ((idx >> 15) & 0x7))); - - // Invalid if two pieces are on the same square or if a king can be captured - if ( distance(ksq[WHITE], ksq[BLACK]) <= 1 - || ksq[WHITE] == psq - || ksq[BLACK] == psq - || (stm == WHITE && (pawn_attacks_bb(WHITE, psq) & ksq[BLACK]))) - result = INVALID; - - // Win if the pawn can be promoted without getting captured - else if ( stm == WHITE - && rank_of(psq) == RANK_7 - && ksq[WHITE] != psq + NORTH - && ( distance(ksq[BLACK], psq + NORTH) > 1 - || (distance(ksq[WHITE], psq + NORTH) == 1))) - result = WIN; - - // Draw if it is stalemate or the black king can capture the pawn - else if ( stm == BLACK - && ( !(attacks_bb(ksq[BLACK]) & ~(attacks_bb(ksq[WHITE]) | pawn_attacks_bb(WHITE, psq))) - || (attacks_bb(ksq[BLACK]) & ~attacks_bb(ksq[WHITE]) & psq))) - result = DRAW; - - // Position will be classified later - else - result = UNKNOWN; - } - - Result KPKPosition::classify(const std::vector& db) { - - // White to move: If one move leads to a position classified as WIN, the result - // of the current position is WIN. If all moves lead to positions classified - // as DRAW, the current position is classified as DRAW, otherwise the current - // position is classified as UNKNOWN. - // - // Black to move: If one move leads to a position classified as DRAW, the result - // of the current position is DRAW. If all moves lead to positions classified - // as WIN, the position is classified as WIN, otherwise the current position is - // classified as UNKNOWN. - const Result Good = (stm == WHITE ? WIN : DRAW); - const Result Bad = (stm == WHITE ? DRAW : WIN); - - Result r = INVALID; - Bitboard b = attacks_bb(ksq[stm]); - - while (b) - r |= stm == WHITE ? db[index(BLACK, ksq[BLACK], pop_lsb(b), psq)] - : db[index(WHITE, pop_lsb(b), ksq[WHITE], psq)]; - - if (stm == WHITE) - { - if (rank_of(psq) < RANK_7) // Single push - r |= db[index(BLACK, ksq[BLACK], ksq[WHITE], psq + NORTH)]; - - if ( rank_of(psq) == RANK_2 // Double push - && psq + NORTH != ksq[WHITE] - && psq + NORTH != ksq[BLACK]) - r |= db[index(BLACK, ksq[BLACK], ksq[WHITE], psq + NORTH + NORTH)]; - } - - return result = r & Good ? Good : r & UNKNOWN ? UNKNOWN : Bad; - } - -} // namespace - -} // namespace Stockfish diff --git a/src/bitboard.h b/src/bitboard.h index 42fd0e97ec6..d21d390b1fb 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -25,13 +25,6 @@ namespace Stockfish { -namespace Bitbases { - -void init(); -bool probe(Square wksq, Square wpsq, Square bksq, Color us); - -} // namespace Stockfish::Bitbases - namespace Bitboards { void init(); diff --git a/src/endgame.cpp b/src/endgame.cpp deleted file mode 100644 index 9021f242313..00000000000 --- a/src/endgame.cpp +++ /dev/null @@ -1,747 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include - -#include "bitboard.h" -#include "endgame.h" -#include "movegen.h" - -namespace Stockfish { - -namespace { - - // Used to drive the king towards the edge of the board - // in KX vs K and KQ vs KR endgames. - // Values range from 27 (center squares) to 90 (in the corners) - inline int push_to_edge(Square s) { - int rd = edge_distance(rank_of(s)), fd = edge_distance(file_of(s)); - return 90 - (7 * fd * fd / 2 + 7 * rd * rd / 2); - } - - // Used to drive the king towards A1H8 corners in KBN vs K endgames. - // Values range from 0 on A8H1 diagonal to 7 in A1H8 corners - inline int push_to_corner(Square s) { - return abs(7 - rank_of(s) - file_of(s)); - } - - // Drive a piece close to or away from another piece - inline int push_close(Square s1, Square s2) { return 140 - 20 * distance(s1, s2); } - inline int push_away(Square s1, Square s2) { return 120 - push_close(s1, s2); } - -#ifndef NDEBUG - bool verify_material(const Position& pos, Color c, Value npm, int pawnsCnt) { - return pos.non_pawn_material(c) == npm && pos.count(c) == pawnsCnt; - } -#endif - - // Map the square as if strongSide is white and strongSide's only pawn - // is on the left half of the board. - Square normalize(const Position& pos, Color strongSide, Square sq) { - - assert(pos.count(strongSide) == 1); - - if (file_of(pos.square(strongSide)) >= FILE_E) - sq = flip_file(sq); - - return strongSide == WHITE ? sq : flip_rank(sq); - } - -} // namespace - - -namespace Endgames { - - std::pair, Map> maps; - - void init() { - - add("KPK"); - add("KNNK"); - add("KBNK"); - add("KRKP"); - add("KRKB"); - add("KRKN"); - add("KQKP"); - add("KQKR"); - add("KNNKP"); - - add("KRPKR"); - add("KRPKB"); - add("KBPKB"); - add("KBPKN"); - add("KBPPKB"); - add("KRPPKRP"); - } -} - - -/// Mate with KX vs K. This function is used to evaluate positions with -/// king and plenty of material vs a lone king. It simply gives the -/// attacking side a bonus for driving the defending king towards the edge -/// of the board, and for keeping the distance between the two kings small. -template<> -Value Endgame::operator()(const Position& pos) const { - - assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); - assert(!pos.checkers()); // Eval is never called when in check - - // Stalemate detection with lone king - if (pos.side_to_move() == weakSide && !MoveList(pos).size()) - return VALUE_DRAW; - - Square strongKing = pos.square(strongSide); - Square weakKing = pos.square(weakSide); - - Value result = pos.non_pawn_material(strongSide) - + pos.count(strongSide) * PawnValueEg - + push_to_edge(weakKing) - + push_close(strongKing, weakKing); - - if ( pos.count(strongSide) - || pos.count(strongSide) - ||(pos.count(strongSide) && pos.count(strongSide)) - || ( (pos.pieces(strongSide, BISHOP) & ~DarkSquares) - && (pos.pieces(strongSide, BISHOP) & DarkSquares))) - result = std::min(result + VALUE_KNOWN_WIN, VALUE_TB_WIN_IN_MAX_PLY - 1); - - return strongSide == pos.side_to_move() ? result : -result; -} - - -/// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the -/// defending king towards a corner square that our bishop attacks. -template<> -Value Endgame::operator()(const Position& pos) const { - - assert(verify_material(pos, strongSide, KnightValueMg + BishopValueMg, 0)); - assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); - - Square strongKing = pos.square(strongSide); - Square strongBishop = pos.square(strongSide); - Square weakKing = pos.square(weakSide); - - // If our bishop does not attack A1/H8, we flip the enemy king square - // to drive to opposite corners (A8/H1). - - Value result = (VALUE_KNOWN_WIN + 3520) - + push_close(strongKing, weakKing) - + 420 * push_to_corner(opposite_colors(strongBishop, SQ_A1) ? flip_file(weakKing) : weakKing); - - assert(abs(result) < VALUE_TB_WIN_IN_MAX_PLY); - return strongSide == pos.side_to_move() ? result : -result; -} - - -/// KP vs K. This endgame is evaluated with the help of a bitbase -template<> -Value Endgame::operator()(const Position& pos) const { - - assert(verify_material(pos, strongSide, VALUE_ZERO, 1)); - assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); - - // Assume strongSide is white and the pawn is on files A-D - Square strongKing = normalize(pos, strongSide, pos.square(strongSide)); - Square strongPawn = normalize(pos, strongSide, pos.square(strongSide)); - Square weakKing = normalize(pos, strongSide, pos.square(weakSide)); - - Color us = strongSide == pos.side_to_move() ? WHITE : BLACK; - - if (!Bitbases::probe(strongKing, strongPawn, weakKing, us)) - return VALUE_DRAW; - - Value result = VALUE_KNOWN_WIN + PawnValueEg + Value(rank_of(strongPawn)); - - return strongSide == pos.side_to_move() ? result : -result; -} - - -/// KR vs KP. This is a somewhat tricky endgame to evaluate precisely without -/// a bitbase. The function below returns drawish scores when the pawn is -/// far advanced with support of the king, while the attacking king is far -/// away. -template<> -Value Endgame::operator()(const Position& pos) const { - - assert(verify_material(pos, strongSide, RookValueMg, 0)); - assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); - - Square strongKing = pos.square(strongSide); - Square weakKing = pos.square(weakSide); - Square strongRook = pos.square(strongSide); - Square weakPawn = pos.square(weakSide); - Square queeningSquare = make_square(file_of(weakPawn), relative_rank(weakSide, RANK_8)); - Value result; - - // If the stronger side's king is in front of the pawn, it's a win - if (forward_file_bb(strongSide, strongKing) & weakPawn) - result = RookValueEg - distance(strongKing, weakPawn); - - // If the weaker side's king is too far from the pawn and the rook, - // it's a win. - else if ( distance(weakKing, weakPawn) >= 3 + (pos.side_to_move() == weakSide) - && distance(weakKing, strongRook) >= 3) - result = RookValueEg - distance(strongKing, weakPawn); - - // If the pawn is far advanced and supported by the defending king, - // the position is drawish - else if ( relative_rank(strongSide, weakKing) <= RANK_3 - && distance(weakKing, weakPawn) == 1 - && relative_rank(strongSide, strongKing) >= RANK_4 - && distance(strongKing, weakPawn) > 2 + (pos.side_to_move() == strongSide)) - result = Value(80) - 8 * distance(strongKing, weakPawn); - - else - result = Value(200) - 8 * ( distance(strongKing, weakPawn + pawn_push(weakSide)) - - distance(weakKing, weakPawn + pawn_push(weakSide)) - - distance(weakPawn, queeningSquare)); - - return strongSide == pos.side_to_move() ? result : -result; -} - - -/// KR vs KB. This is very simple, and always returns drawish scores. The -/// score is slightly bigger when the defending king is close to the edge. -template<> -Value Endgame::operator()(const Position& pos) const { - - assert(verify_material(pos, strongSide, RookValueMg, 0)); - assert(verify_material(pos, weakSide, BishopValueMg, 0)); - - Value result = Value(push_to_edge(pos.square(weakSide))); - return strongSide == pos.side_to_move() ? result : -result; -} - - -/// KR vs KN. The attacking side has slightly better winning chances than -/// in KR vs KB, particularly if the king and the knight are far apart. -template<> -Value Endgame::operator()(const Position& pos) const { - - assert(verify_material(pos, strongSide, RookValueMg, 0)); - assert(verify_material(pos, weakSide, KnightValueMg, 0)); - - Square weakKing = pos.square(weakSide); - Square weakKnight = pos.square(weakSide); - Value result = Value(push_to_edge(weakKing) + push_away(weakKing, weakKnight)); - return strongSide == pos.side_to_move() ? result : -result; -} - - -/// KQ vs KP. In general, this is a win for the stronger side, but there are a -/// few important exceptions. A pawn on 7th rank and on the A,C,F or H files -/// with a king positioned next to it can be a draw, so in that case, we only -/// use the distance between the kings. -template<> -Value Endgame::operator()(const Position& pos) const { - - assert(verify_material(pos, strongSide, QueenValueMg, 0)); - assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); - - Square strongKing = pos.square(strongSide); - Square weakKing = pos.square(weakSide); - Square weakPawn = pos.square(weakSide); - - Value result = Value(push_close(strongKing, weakKing)); - - if ( relative_rank(weakSide, weakPawn) != RANK_7 - || distance(weakKing, weakPawn) != 1 - || ((FileBBB | FileDBB | FileEBB | FileGBB) & weakPawn)) - result += QueenValueEg - PawnValueEg; - - return strongSide == pos.side_to_move() ? result : -result; -} - - -/// KQ vs KR. This is almost identical to KX vs K: we give the attacking -/// king a bonus for having the kings close together, and for forcing the -/// defending king towards the edge. If we also take care to avoid null move for -/// the defending side in the search, this is usually sufficient to win KQ vs KR. -template<> -Value Endgame::operator()(const Position& pos) const { - - assert(verify_material(pos, strongSide, QueenValueMg, 0)); - assert(verify_material(pos, weakSide, RookValueMg, 0)); - - Square strongKing = pos.square(strongSide); - Square weakKing = pos.square(weakSide); - - Value result = QueenValueEg - - RookValueEg - + push_to_edge(weakKing) - + push_close(strongKing, weakKing); - - return strongSide == pos.side_to_move() ? result : -result; -} - - -/// KNN vs KP. Very drawish, but there are some mate opportunities if we can -/// press the weakSide King to a corner before the pawn advances too much. -template<> -Value Endgame::operator()(const Position& pos) const { - - assert(verify_material(pos, strongSide, 2 * KnightValueMg, 0)); - assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); - - Square weakKing = pos.square(weakSide); - Square weakPawn = pos.square(weakSide); - - Value result = PawnValueEg - + 2 * push_to_edge(weakKing) - - 10 * relative_rank(weakSide, weakPawn); - - return strongSide == pos.side_to_move() ? result : -result; -} - - -/// Some cases of trivial draws -template<> Value Endgame::operator()(const Position&) const { return VALUE_DRAW; } - - -/// KB and one or more pawns vs K. It checks for draws with rook pawns and -/// a bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_DRAW -/// is returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling -/// will be used. -template<> -ScaleFactor Endgame::operator()(const Position& pos) const { - - assert(pos.non_pawn_material(strongSide) == BishopValueMg); - assert(pos.count(strongSide) >= 1); - - // No assertions about the material of weakSide, because we want draws to - // be detected even when the weaker side has some pawns. - - Bitboard strongPawns = pos.pieces(strongSide, PAWN); - Bitboard allPawns = pos.pieces(PAWN); - - Square strongBishop = pos.square(strongSide); - Square weakKing = pos.square(weakSide); - Square strongKing = pos.square(strongSide); - - // All strongSide pawns are on a single rook file? - if (!(strongPawns & ~FileABB) || !(strongPawns & ~FileHBB)) - { - Square queeningSquare = relative_square(strongSide, make_square(file_of(lsb(strongPawns)), RANK_8)); - - if ( opposite_colors(queeningSquare, strongBishop) - && distance(queeningSquare, weakKing) <= 1) - return SCALE_FACTOR_DRAW; - } - - // If all the pawns are on the same B or G file, then it's potentially a draw - if ((!(allPawns & ~FileBBB) || !(allPawns & ~FileGBB)) - && pos.non_pawn_material(weakSide) == 0 - && pos.count(weakSide) >= 1) - { - // Get the least advanced weakSide pawn - Square weakPawn = frontmost_sq(strongSide, pos.pieces(weakSide, PAWN)); - - // There's potential for a draw if our pawn is blocked on the 7th rank, - // the bishop cannot attack it or they only have one pawn left. - if ( relative_rank(strongSide, weakPawn) == RANK_7 - && (strongPawns & (weakPawn + pawn_push(weakSide))) - && (opposite_colors(strongBishop, weakPawn) || !more_than_one(strongPawns))) - { - int strongKingDist = distance(weakPawn, strongKing); - int weakKingDist = distance(weakPawn, weakKing); - - // It's a draw if the weak king is on its back two ranks, within 2 - // squares of the blocking pawn and the strong king is not - // closer. (I think this rule only fails in practically - // unreachable positions such as 5k1K/6p1/6P1/8/8/3B4/8/8 w - // and positions where qsearch will immediately correct the - // problem such as 8/4k1p1/6P1/1K6/3B4/8/8/8 w). - if ( relative_rank(strongSide, weakKing) >= RANK_7 - && weakKingDist <= 2 - && weakKingDist <= strongKingDist) - return SCALE_FACTOR_DRAW; - } - } - - return SCALE_FACTOR_NONE; -} - - -/// KQ vs KR and one or more pawns. It tests for fortress draws with a rook on -/// the third rank defended by a pawn. -template<> -ScaleFactor Endgame::operator()(const Position& pos) const { - - assert(verify_material(pos, strongSide, QueenValueMg, 0)); - assert(pos.count(weakSide) == 1); - assert(pos.count(weakSide) >= 1); - - Square strongKing = pos.square(strongSide); - Square weakKing = pos.square(weakSide); - Square weakRook = pos.square(weakSide); - - if ( relative_rank(weakSide, weakKing) <= RANK_2 - && relative_rank(weakSide, strongKing) >= RANK_4 - && relative_rank(weakSide, weakRook) == RANK_3 - && ( pos.pieces(weakSide, PAWN) - & attacks_bb(weakKing) - & pawn_attacks_bb(strongSide, weakRook))) - return SCALE_FACTOR_DRAW; - - return SCALE_FACTOR_NONE; -} - - -/// KRP vs KR. This function knows a handful of the most important classes of -/// drawn positions, but is far from perfect. It would probably be a good idea -/// to add more knowledge in the future. -/// -/// It would also be nice to rewrite the actual code for this function, -/// which is mostly copied from Glaurung 1.x, and isn't very pretty. -template<> -ScaleFactor Endgame::operator()(const Position& pos) const { - - assert(verify_material(pos, strongSide, RookValueMg, 1)); - assert(verify_material(pos, weakSide, RookValueMg, 0)); - - // Assume strongSide is white and the pawn is on files A-D - Square strongKing = normalize(pos, strongSide, pos.square(strongSide)); - Square strongRook = normalize(pos, strongSide, pos.square(strongSide)); - Square strongPawn = normalize(pos, strongSide, pos.square(strongSide)); - Square weakKing = normalize(pos, strongSide, pos.square(weakSide)); - Square weakRook = normalize(pos, strongSide, pos.square(weakSide)); - - File pawnFile = file_of(strongPawn); - Rank pawnRank = rank_of(strongPawn); - Square queeningSquare = make_square(pawnFile, RANK_8); - int tempo = (pos.side_to_move() == strongSide); - - // If the pawn is not too far advanced and the defending king defends the - // queening square, use the third-rank defence. - if ( pawnRank <= RANK_5 - && distance(weakKing, queeningSquare) <= 1 - && strongKing <= SQ_H5 - && (rank_of(weakRook) == RANK_6 || (pawnRank <= RANK_3 && rank_of(strongRook) != RANK_6))) - return SCALE_FACTOR_DRAW; - - // The defending side saves a draw by checking from behind in case the pawn - // has advanced to the 6th rank with the king behind. - if ( pawnRank == RANK_6 - && distance(weakKing, queeningSquare) <= 1 - && rank_of(strongKing) + tempo <= RANK_6 - && (rank_of(weakRook) == RANK_1 || (!tempo && distance(weakRook, strongPawn) >= 3))) - return SCALE_FACTOR_DRAW; - - if ( pawnRank >= RANK_6 - && weakKing == queeningSquare - && rank_of(weakRook) == RANK_1 - && (!tempo || distance(strongKing, strongPawn) >= 2)) - return SCALE_FACTOR_DRAW; - - // White pawn on a7 and rook on a8 is a draw if black's king is on g7 or h7 - // and the black rook is behind the pawn. - if ( strongPawn == SQ_A7 - && strongRook == SQ_A8 - && (weakKing == SQ_H7 || weakKing == SQ_G7) - && file_of(weakRook) == FILE_A - && (rank_of(weakRook) <= RANK_3 || file_of(strongKing) >= FILE_D || rank_of(strongKing) <= RANK_5)) - return SCALE_FACTOR_DRAW; - - // If the defending king blocks the pawn and the attacking king is too far - // away, it's a draw. - if ( pawnRank <= RANK_5 - && weakKing == strongPawn + NORTH - && distance(strongKing, strongPawn) - tempo >= 2 - && distance(strongKing, weakRook) - tempo >= 2) - return SCALE_FACTOR_DRAW; - - // Pawn on the 7th rank supported by the rook from behind usually wins if the - // attacking king is closer to the queening square than the defending king, - // and the defending king cannot gain tempi by threatening the attacking rook. - if ( pawnRank == RANK_7 - && pawnFile != FILE_A - && file_of(strongRook) == pawnFile - && strongRook != queeningSquare - && (distance(strongKing, queeningSquare) < distance(weakKing, queeningSquare) - 2 + tempo) - && (distance(strongKing, queeningSquare) < distance(weakKing, strongRook) + tempo)) - return ScaleFactor(SCALE_FACTOR_MAX - 2 * distance(strongKing, queeningSquare)); - - // Similar to the above, but with the pawn further back - if ( pawnFile != FILE_A - && file_of(strongRook) == pawnFile - && strongRook < strongPawn - && (distance(strongKing, queeningSquare) < distance(weakKing, queeningSquare) - 2 + tempo) - && (distance(strongKing, strongPawn + NORTH) < distance(weakKing, strongPawn + NORTH) - 2 + tempo) - && ( distance(weakKing, strongRook) + tempo >= 3 - || ( distance(strongKing, queeningSquare) < distance(weakKing, strongRook) + tempo - && (distance(strongKing, strongPawn + NORTH) < distance(weakKing, strongPawn) + tempo)))) - return ScaleFactor( SCALE_FACTOR_MAX - - 8 * distance(strongPawn, queeningSquare) - - 2 * distance(strongKing, queeningSquare)); - - // If the pawn is not far advanced and the defending king is somewhere in - // the pawn's path, it's probably a draw. - if (pawnRank <= RANK_4 && weakKing > strongPawn) - { - if (file_of(weakKing) == file_of(strongPawn)) - return ScaleFactor(10); - if ( distance(weakKing, strongPawn) == 1 - && distance(strongKing, weakKing) > 2) - return ScaleFactor(24 - 2 * distance(strongKing, weakKing)); - } - return SCALE_FACTOR_NONE; -} - -template<> -ScaleFactor Endgame::operator()(const Position& pos) const { - - assert(verify_material(pos, strongSide, RookValueMg, 1)); - assert(verify_material(pos, weakSide, BishopValueMg, 0)); - - // Test for a rook pawn - if (pos.pieces(PAWN) & (FileABB | FileHBB)) - { - Square weakKing = pos.square(weakSide); - Square weakBishop = pos.square(weakSide); - Square strongKing = pos.square(strongSide); - Square strongPawn = pos.square(strongSide); - Rank pawnRank = relative_rank(strongSide, strongPawn); - Direction push = pawn_push(strongSide); - - // If the pawn is on the 5th rank and the pawn (currently) is on - // the same color square as the bishop then there is a chance of - // a fortress. Depending on the king position give a moderate - // reduction or a stronger one if the defending king is near the - // corner but not trapped there. - if (pawnRank == RANK_5 && !opposite_colors(weakBishop, strongPawn)) - { - int d = distance(strongPawn + 3 * push, weakKing); - - if (d <= 2 && !(d == 0 && weakKing == strongKing + 2 * push)) - return ScaleFactor(24); - else - return ScaleFactor(48); - } - - // When the pawn has moved to the 6th rank we can be fairly sure - // it's drawn if the bishop attacks the square in front of the - // pawn from a reasonable distance and the defending king is near - // the corner - if ( pawnRank == RANK_6 - && distance(strongPawn + 2 * push, weakKing) <= 1 - && (attacks_bb(weakBishop) & (strongPawn + push)) - && distance(weakBishop, strongPawn) >= 2) - return ScaleFactor(8); - } - - return SCALE_FACTOR_NONE; -} - -/// KRPP vs KRP. There is just a single rule: if the stronger side has no passed -/// pawns and the defending king is actively placed, the position is drawish. -template<> -ScaleFactor Endgame::operator()(const Position& pos) const { - - assert(verify_material(pos, strongSide, RookValueMg, 2)); - assert(verify_material(pos, weakSide, RookValueMg, 1)); - - Square strongPawn1 = lsb(pos.pieces(strongSide, PAWN)); - Square strongPawn2 = msb(pos.pieces(strongSide, PAWN)); - Square weakKing = pos.square(weakSide); - - // Does the stronger side have a passed pawn? - if (pos.pawn_passed(strongSide, strongPawn1) || pos.pawn_passed(strongSide, strongPawn2)) - return SCALE_FACTOR_NONE; - - Rank pawnRank = std::max(relative_rank(strongSide, strongPawn1), relative_rank(strongSide, strongPawn2)); - - if ( distance(weakKing, strongPawn1) <= 1 - && distance(weakKing, strongPawn2) <= 1 - && relative_rank(strongSide, weakKing) > pawnRank) - { - assert(pawnRank > RANK_1 && pawnRank < RANK_7); - return ScaleFactor(7 * pawnRank); - } - return SCALE_FACTOR_NONE; -} - - -/// K and two or more pawns vs K. There is just a single rule here: if all pawns -/// are on the same rook file and are blocked by the defending king, it's a draw. -template<> -ScaleFactor Endgame::operator()(const Position& pos) const { - - assert(pos.non_pawn_material(strongSide) == VALUE_ZERO); - assert(pos.count(strongSide) >= 2); - assert(verify_material(pos, weakSide, VALUE_ZERO, 0)); - - Square weakKing = pos.square(weakSide); - Bitboard strongPawns = pos.pieces(strongSide, PAWN); - - // If all pawns are ahead of the king on a single rook file, it's a draw. - if ( !(strongPawns & ~(FileABB | FileHBB)) - && !(strongPawns & ~passed_pawn_span(weakSide, weakKing))) - return SCALE_FACTOR_DRAW; - - return SCALE_FACTOR_NONE; -} - - -/// KBP vs KB. There are two rules: if the defending king is somewhere along the -/// path of the pawn, and the square of the king is not of the same color as the -/// stronger side's bishop, it's a draw. If the two bishops have opposite color, -/// it's almost always a draw. -template<> -ScaleFactor Endgame::operator()(const Position& pos) const { - - assert(verify_material(pos, strongSide, BishopValueMg, 1)); - assert(verify_material(pos, weakSide, BishopValueMg, 0)); - - Square strongPawn = pos.square(strongSide); - Square strongBishop = pos.square(strongSide); - Square weakBishop = pos.square(weakSide); - Square weakKing = pos.square(weakSide); - - // Case 1: Defending king blocks the pawn, and cannot be driven away - if ( (forward_file_bb(strongSide, strongPawn) & weakKing) - && ( opposite_colors(weakKing, strongBishop) - || relative_rank(strongSide, weakKing) <= RANK_6)) - return SCALE_FACTOR_DRAW; - - // Case 2: Opposite colored bishops - if (opposite_colors(strongBishop, weakBishop)) - return SCALE_FACTOR_DRAW; - - return SCALE_FACTOR_NONE; -} - - -/// KBPP vs KB. It detects a few basic draws with opposite-colored bishops -template<> -ScaleFactor Endgame::operator()(const Position& pos) const { - - assert(verify_material(pos, strongSide, BishopValueMg, 2)); - assert(verify_material(pos, weakSide, BishopValueMg, 0)); - - Square strongBishop = pos.square(strongSide); - Square weakBishop = pos.square(weakSide); - - if (!opposite_colors(strongBishop, weakBishop)) - return SCALE_FACTOR_NONE; - - Square weakKing = pos.square(weakSide); - Square strongPawn1 = lsb(pos.pieces(strongSide, PAWN)); - Square strongPawn2 = msb(pos.pieces(strongSide, PAWN)); - Square blockSq1, blockSq2; - - if (relative_rank(strongSide, strongPawn1) > relative_rank(strongSide, strongPawn2)) - { - blockSq1 = strongPawn1 + pawn_push(strongSide); - blockSq2 = make_square(file_of(strongPawn2), rank_of(strongPawn1)); - } - else - { - blockSq1 = strongPawn2 + pawn_push(strongSide); - blockSq2 = make_square(file_of(strongPawn1), rank_of(strongPawn2)); - } - - switch (distance(strongPawn1, strongPawn2)) - { - case 0: - // Both pawns are on the same file. It's an easy draw if the defender firmly - // controls some square in the frontmost pawn's path. - if ( file_of(weakKing) == file_of(blockSq1) - && relative_rank(strongSide, weakKing) >= relative_rank(strongSide, blockSq1) - && opposite_colors(weakKing, strongBishop)) - return SCALE_FACTOR_DRAW; - else - return SCALE_FACTOR_NONE; - - case 1: - // Pawns on adjacent files. It's a draw if the defender firmly controls the - // square in front of the frontmost pawn's path, and the square diagonally - // behind this square on the file of the other pawn. - if ( weakKing == blockSq1 - && opposite_colors(weakKing, strongBishop) - && ( weakBishop == blockSq2 - || (attacks_bb(blockSq2, pos.pieces()) & pos.pieces(weakSide, BISHOP)) - || distance(strongPawn1, strongPawn2) >= 2)) - return SCALE_FACTOR_DRAW; - - else if ( weakKing == blockSq2 - && opposite_colors(weakKing, strongBishop) - && ( weakBishop == blockSq1 - || (attacks_bb(blockSq1, pos.pieces()) & pos.pieces(weakSide, BISHOP)))) - return SCALE_FACTOR_DRAW; - else - return SCALE_FACTOR_NONE; - - default: - // The pawns are not on the same file or adjacent files. No scaling. - return SCALE_FACTOR_NONE; - } -} - - -/// KBP vs KN. There is a single rule: if the defending king is somewhere along -/// the path of the pawn, and the square of the king is not of the same color as -/// the stronger side's bishop, it's a draw. -template<> -ScaleFactor Endgame::operator()(const Position& pos) const { - - assert(verify_material(pos, strongSide, BishopValueMg, 1)); - assert(verify_material(pos, weakSide, KnightValueMg, 0)); - - Square strongPawn = pos.square(strongSide); - Square strongBishop = pos.square(strongSide); - Square weakKing = pos.square(weakSide); - - if ( file_of(weakKing) == file_of(strongPawn) - && relative_rank(strongSide, strongPawn) < relative_rank(strongSide, weakKing) - && ( opposite_colors(weakKing, strongBishop) - || relative_rank(strongSide, weakKing) <= RANK_6)) - return SCALE_FACTOR_DRAW; - - return SCALE_FACTOR_NONE; -} - - -/// KP vs KP. This is done by removing the weakest side's pawn and probing the -/// KP vs K bitbase: if the weakest side has a draw without the pawn, it probably -/// has at least a draw with the pawn as well. The exception is when the stronger -/// side's pawn is far advanced and not on a rook file; in this case it is often -/// possible to win (e.g. 8/4k3/3p4/3P4/6K1/8/8/8 w - - 0 1). -template<> -ScaleFactor Endgame::operator()(const Position& pos) const { - - assert(verify_material(pos, strongSide, VALUE_ZERO, 1)); - assert(verify_material(pos, weakSide, VALUE_ZERO, 1)); - - // Assume strongSide is white and the pawn is on files A-D - Square strongKing = normalize(pos, strongSide, pos.square(strongSide)); - Square weakKing = normalize(pos, strongSide, pos.square(weakSide)); - Square strongPawn = normalize(pos, strongSide, pos.square(strongSide)); - - Color us = strongSide == pos.side_to_move() ? WHITE : BLACK; - - // If the pawn has advanced to the fifth rank or further, and is not a - // rook pawn, it's too dangerous to assume that it's at least a draw. - if (rank_of(strongPawn) >= RANK_5 && file_of(strongPawn) != FILE_A) - return SCALE_FACTOR_NONE; - - // Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw, - // it's probably at least a draw even with the pawn. - return Bitbases::probe(strongKing, strongPawn, weakKing, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW; -} - -} // namespace Stockfish diff --git a/src/endgame.h b/src/endgame.h deleted file mode 100644 index c184cb3fd50..00000000000 --- a/src/endgame.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef ENDGAME_H_INCLUDED -#define ENDGAME_H_INCLUDED - -#include -#include -#include -#include -#include - -#include "position.h" -#include "types.h" - -namespace Stockfish { - -/// EndgameCode lists all supported endgame functions by corresponding codes - -enum EndgameCode { - - EVALUATION_FUNCTIONS, - KNNK, // KNN vs K - KNNKP, // KNN vs KP - KXK, // Generic "mate lone king" eval - KBNK, // KBN vs K - KPK, // KP vs K - KRKP, // KR vs KP - KRKB, // KR vs KB - KRKN, // KR vs KN - KQKP, // KQ vs KP - KQKR, // KQ vs KR - - SCALING_FUNCTIONS, - KBPsK, // KB and pawns vs K - KQKRPs, // KQ vs KR and pawns - KRPKR, // KRP vs KR - KRPKB, // KRP vs KB - KRPPKRP, // KRPP vs KRP - KPsK, // K and pawns vs K - KBPKB, // KBP vs KB - KBPPKB, // KBPP vs KB - KBPKN, // KBP vs KN - KPKP // KP vs KP -}; - - -/// Endgame functions can be of two types depending on whether they return a -/// Value or a ScaleFactor. - -template using -eg_type = typename std::conditional<(E < SCALING_FUNCTIONS), Value, ScaleFactor>::type; - - -/// Base and derived functors for endgame evaluation and scaling functions - -template -struct EndgameBase { - - explicit EndgameBase(Color c) : strongSide(c), weakSide(~c) {} - virtual ~EndgameBase() = default; - virtual T operator()(const Position&) const = 0; - - const Color strongSide, weakSide; -}; - - -template> -struct Endgame : public EndgameBase { - - explicit Endgame(Color c) : EndgameBase(c) {} - T operator()(const Position&) const override; -}; - - -/// The Endgames namespace handles the pointers to endgame evaluation and scaling -/// base objects in two std::map. We use polymorphism to invoke the actual -/// endgame function by calling its virtual operator(). - -namespace Endgames { - - template using Ptr = std::unique_ptr>; - template using Map = std::unordered_map>; - - extern std::pair, Map> maps; - - void init(); - - template - Map& map() { - return std::get::value>(maps); - } - - template> - void add(const std::string& code) { - - StateInfo st; - map()[Position().set(code, WHITE, &st).material_key()] = Ptr(new Endgame(WHITE)); - map()[Position().set(code, BLACK, &st).material_key()] = Ptr(new Endgame(BLACK)); - } - - template - const EndgameBase* probe(Key key) { - auto it = map().find(key); - return it != map().end() ? it->second.get() : nullptr; - } -} - -} // namespace Stockfish - -#endif // #ifndef ENDGAME_H_INCLUDED diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 35d054270ee..2ab4fa404e0 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -18,8 +18,6 @@ #include #include -#include -#include // For std::memset #include #include #include @@ -29,9 +27,7 @@ #include "bitboard.h" #include "evaluate.h" -#include "material.h" #include "misc.h" -#include "pawns.h" #include "thread.h" #include "timeman.h" #include "uci.h" @@ -60,9 +56,10 @@ namespace Stockfish { namespace Eval { - bool useNNUE; string currentEvalFileName = "None"; + static double to_cp(Value v) { return double(v) / UCI::NormalizeToPawnValue; } + /// NNUE::init() tries to load a NNUE network at startup time, or when the engine /// receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue" /// The name of the NNUE network is always retrieved from the EvalFile option. @@ -73,10 +70,6 @@ namespace Eval { void NNUE::init() { - useNNUE = Options["Use NNUE"]; - if (!useNNUE) - return; - string eval_file = string(Options["EvalFile"]); if (eval_file.empty()) eval_file = EvalFileDefaultName; @@ -122,10 +115,10 @@ namespace Eval { if (eval_file.empty()) eval_file = EvalFileDefaultName; - if (useNNUE && currentEvalFileName != eval_file) + if (currentEvalFileName != eval_file) { - string msg1 = "If the UCI option \"Use NNUE\" is set to true, network evaluation parameters compatible with the engine must be available."; + string msg1 = "Network evaluation parameters compatible with the engine must be available."; string msg2 = "The option is set to true, but the network file " + eval_file + " was not loaded successfully."; string msg3 = "The UCI option EvalFile might need to specify the full path, including the directory name, to the network file."; string msg4 = "The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/" + std::string(EvalFileDefaultName); @@ -140,909 +133,10 @@ namespace Eval { exit(EXIT_FAILURE); } - if (useNNUE) - sync_cout << "info string NNUE evaluation using " << eval_file << " enabled" << sync_endl; - else - sync_cout << "info string classical evaluation enabled" << sync_endl; - } -} - -namespace Trace { - - enum Tracing { NO_TRACE, TRACE }; - - enum Term { // The first 8 entries are reserved for PieceType - MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, WINNABLE, TOTAL, TERM_NB - }; - - Score scores[TERM_NB][COLOR_NB]; - - static double to_cp(Value v) { return double(v) / UCI::NormalizeToPawnValue; } - - static void add(int idx, Color c, Score s) { - scores[idx][c] = s; - } - - static void add(int idx, Score w, Score b = SCORE_ZERO) { - scores[idx][WHITE] = w; - scores[idx][BLACK] = b; - } - - static std::ostream& operator<<(std::ostream& os, Score s) { - os << std::setw(5) << to_cp(mg_value(s)) << " " - << std::setw(5) << to_cp(eg_value(s)); - return os; - } - - static std::ostream& operator<<(std::ostream& os, Term t) { - - if (t == MATERIAL || t == IMBALANCE || t == WINNABLE || t == TOTAL) - os << " ---- ----" << " | " << " ---- ----"; - else - os << scores[t][WHITE] << " | " << scores[t][BLACK]; - - os << " | " << scores[t][WHITE] - scores[t][BLACK] << " |\n"; - return os; + sync_cout << "info string NNUE evaluation using " << eval_file << sync_endl; } } -using namespace Trace; - -namespace { - - // Threshold for lazy and space evaluation - constexpr Value LazyThreshold1 = Value(3622); - constexpr Value LazyThreshold2 = Value(1962); - constexpr Value SpaceThreshold = Value(11551); - - // KingAttackWeights[PieceType] contains king attack weights by piece type - constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 76, 46, 45, 14 }; - - // SafeCheck[PieceType][single/multiple] contains safe check bonus by piece type, - // higher if multiple safe checks are possible for that piece type. - constexpr int SafeCheck[][2] = { - {}, {}, {805, 1292}, {650, 984}, {1071, 1886}, {730, 1128} - }; - -#define S(mg, eg) make_score(mg, eg) - - // MobilityBonus[PieceType-2][attacked] contains bonuses for middle and end game, - // indexed by piece type and number of attacked squares in the mobility area. - constexpr Score MobilityBonus[][32] = { - { S(-62,-79), S(-53,-57), S(-12,-31), S( -3,-17), S( 3, 7), S( 12, 13), // Knight - S( 21, 16), S( 28, 21), S( 37, 26) }, - { S(-47,-59), S(-20,-25), S( 14, -8), S( 29, 12), S( 39, 21), S( 53, 40), // Bishop - S( 53, 56), S( 60, 58), S( 62, 65), S( 69, 72), S( 78, 78), S( 83, 87), - S( 91, 88), S( 96, 98) }, - { S(-60,-82), S(-24,-15), S( 0, 17) ,S( 3, 43), S( 4, 72), S( 14,100), // Rook - S( 20,102), S( 30,122), S( 41,133), S(41 ,139), S( 41,153), S( 45,160), - S( 57,165), S( 58,170), S( 67,175) }, - { S(-29,-49), S(-16,-29), S( -8, -8), S( -8, 17), S( 18, 39), S( 25, 54), // Queen - S( 23, 59), S( 37, 73), S( 41, 76), S( 54, 95), S( 65, 95) ,S( 68,101), - S( 69,124), S( 70,128), S( 70,132), S( 70,133) ,S( 71,136), S( 72,140), - S( 74,147), S( 76,149), S( 90,153), S(104,169), S(105,171), S(106,171), - S(112,178), S(114,185), S(114,187), S(119,221) } - }; - - // BishopPawns[distance from edge] contains a file-dependent penalty for pawns on - // squares of the same color as our bishop. - constexpr Score BishopPawns[int(FILE_NB) / 2] = { - S(3, 8), S(3, 9), S(2, 7), S(3, 7) - }; - - // KingProtector[knight/bishop] contains penalty for each distance unit to own king - constexpr Score KingProtector[] = { S(9, 9), S(7, 9) }; - - // Outpost[knight/bishop] contains bonuses for each knight or bishop occupying a - // pawn protected square on rank 4 to 6 which is also safe from a pawn attack. - constexpr Score Outpost[] = { S(54, 34), S(31, 25) }; - - // PassedRank[Rank] contains a bonus according to the rank of a passed pawn - constexpr Score PassedRank[RANK_NB] = { - S(0, 0), S(2, 38), S(15, 36), S(22, 50), S(64, 81), S(166, 184), S(284, 269) - }; - - constexpr Score RookOnClosedFile = S(10, 5); - constexpr Score RookOnOpenFile[] = { S(18, 8), S(49, 26) }; - - // 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(6, 37), S(64, 50), S(82, 57), S(103, 130), S(81, 163) - }; - - constexpr Score ThreatByRook[PIECE_TYPE_NB] = { - S(0, 0), S(3, 44), S(36, 71), S(44, 59), S(0, 39), S(60, 39) - }; - - constexpr Value CorneredBishop = Value(50); - - // Assorted bonuses and penalties - constexpr Score UncontestedOutpost = S( 0, 10); - constexpr Score BishopOnKingRing = S( 24, 0); - constexpr Score BishopXRayPawns = S( 4, 5); - constexpr Score FlankAttacks = S( 8, 0); - constexpr Score Hanging = S( 72, 40); - constexpr Score KnightOnQueen = S( 16, 11); - constexpr Score LongDiagonalBishop = S( 45, 0); - constexpr Score MinorBehindPawn = S( 18, 3); - constexpr Score PassedFile = S( 13, 8); - constexpr Score PawnlessFlank = S( 19, 97); - constexpr Score ReachableOutpost = S( 33, 19); - constexpr Score RestrictedPiece = S( 6, 7); - constexpr Score RookOnKingRing = S( 16, 0); - constexpr Score SliderOnQueen = S( 62, 21); - constexpr Score ThreatByKing = S( 24, 87); - constexpr Score ThreatByPawnPush = S( 48, 39); - constexpr Score ThreatBySafePawn = S(167, 99); - constexpr Score TrappedRook = S( 55, 13); - constexpr Score WeakQueenProtection = S( 14, 0); - constexpr Score WeakQueen = S( 57, 19); - - -#undef S - - // Evaluation class computes and stores attacks tables and other working data - template - class Evaluation { - - public: - Evaluation() = delete; - explicit Evaluation(const Position& p) : pos(p) {} - Evaluation& operator=(const Evaluation&) = delete; - Value value(); - - private: - template void initialize(); - template Score pieces(); - template Score king() const; - template Score threats() const; - template Score passed() const; - template Score space() const; - Value winnable(Score score) const; - - const Position& pos; - Material::Entry* me; - Pawns::Entry* pe; - Bitboard mobilityArea[COLOR_NB]; - Score mobility[COLOR_NB] = { SCORE_ZERO, SCORE_ZERO }; - - // attackedBy[color][piece type] is a bitboard representing all squares - // attacked by a given color and piece type. Special "piece types" which - // is also calculated is ALL_PIECES. - Bitboard attackedBy[COLOR_NB][PIECE_TYPE_NB]; - - // attackedBy2[color] are the squares attacked by at least 2 units of a given - // color, including x-rays. But diagonal x-rays through pawns are not computed. - Bitboard attackedBy2[COLOR_NB]; - - // kingRing[color] are the squares adjacent to the king plus some other - // very near squares, depending on king position. - Bitboard kingRing[COLOR_NB]; - - // kingAttackersCount[color] is the number of pieces of the given color - // which attack a square in the kingRing of the enemy king. - int kingAttackersCount[COLOR_NB]; - - // kingAttackersWeight[color] is the sum of the "weights" of the pieces of - // the given color which attack a square in the kingRing of the enemy king. - // The weights of the individual piece types are given by the elements in - // the KingAttackWeights array. - int kingAttackersWeight[COLOR_NB]; - - // kingAttacksCount[color] is the number of attacks by the given color to - // squares directly adjacent to the enemy king. Pieces which attack more - // than one square are counted multiple times. For instance, if there is - // a white knight on g5 and black's king is on g8, this white knight adds 2 - // to kingAttacksCount[WHITE]. - int kingAttacksCount[COLOR_NB]; - }; - - - // Evaluation::initialize() computes king and pawn attacks, and the king ring - // bitboard for a given color. This is done at the beginning of the evaluation. - - template template - void Evaluation::initialize() { - - constexpr Color Them = ~Us; - constexpr Direction Up = pawn_push(Us); - constexpr Direction Down = -Up; - constexpr Bitboard LowRanks = (Us == WHITE ? Rank2BB | Rank3BB : Rank7BB | Rank6BB); - - const Square ksq = pos.square(Us); - - Bitboard dblAttackByPawn = pawn_double_attacks_bb(pos.pieces(Us, PAWN)); - - // Find our pawns that are blocked or on the first two ranks - Bitboard b = pos.pieces(Us, PAWN) & (shift(pos.pieces()) | LowRanks); - - // Squares occupied by those pawns, by our king or queen, by blockers to attacks on our king - // or controlled by enemy pawns are excluded from the mobility area. - mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pos.blockers_for_king(Us) | pe->pawn_attacks(Them)); - - // Initialize attackedBy[] for king and pawns - attackedBy[Us][KING] = attacks_bb(ksq); - attackedBy[Us][PAWN] = pe->pawn_attacks(Us); - attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN]; - attackedBy2[Us] = dblAttackByPawn | (attackedBy[Us][KING] & attackedBy[Us][PAWN]); - - // Init our king safety tables - Square s = make_square(std::clamp(file_of(ksq), FILE_B, FILE_G), - std::clamp(rank_of(ksq), RANK_2, RANK_7)); - kingRing[Us] = attacks_bb(s) | s; - - kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them)); - kingAttacksCount[Them] = kingAttackersWeight[Them] = 0; - - // Remove from kingRing[] the squares defended by two pawns - kingRing[Us] &= ~dblAttackByPawn; - } - - - // Evaluation::pieces() scores pieces of a given color and type - - template template - Score Evaluation::pieces() { - - constexpr Color Them = ~Us; - [[maybe_unused]] constexpr Direction Down = -pawn_push(Us); - [[maybe_unused]] constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB - : Rank5BB | Rank4BB | Rank3BB); - Bitboard b1 = pos.pieces(Us, Pt); - Bitboard b, bb; - Score score = SCORE_ZERO; - - attackedBy[Us][Pt] = 0; - - while (b1) - { - Square s = pop_lsb(b1); - - // Find attacked squares, including x-ray attacks for bishops and rooks - b = Pt == BISHOP ? attacks_bb(s, pos.pieces() ^ pos.pieces(QUEEN)) - : Pt == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(QUEEN) ^ pos.pieces(Us, ROOK)) - : attacks_bb(s, pos.pieces()); - - if (pos.blockers_for_king(Us) & s) - b &= line_bb(pos.square(Us), s); - - attackedBy2[Us] |= attackedBy[Us][ALL_PIECES] & b; - attackedBy[Us][Pt] |= b; - attackedBy[Us][ALL_PIECES] |= b; - - if (b & kingRing[Them]) - { - kingAttackersCount[Us]++; - kingAttackersWeight[Us] += KingAttackWeights[Pt]; - kingAttacksCount[Us] += popcount(b & attackedBy[Them][KING]); - } - - else if (Pt == ROOK && (file_bb(s) & kingRing[Them])) - score += RookOnKingRing; - - else if (Pt == BISHOP && (attacks_bb(s, pos.pieces(PAWN)) & kingRing[Them])) - score += BishopOnKingRing; - - int mob = popcount(b & mobilityArea[Us]); - mobility[Us] += MobilityBonus[Pt - 2][mob]; - - if constexpr (Pt == BISHOP || Pt == KNIGHT) - { - // Bonus if the piece is on an outpost square or can reach one - // Bonus for knights (UncontestedOutpost) if few relevant targets - bb = OutpostRanks & (attackedBy[Us][PAWN] | shift(pos.pieces(PAWN))) - & ~pe->pawn_attacks_span(Them); - Bitboard targets = pos.pieces(Them) & ~pos.pieces(PAWN); - - if ( Pt == KNIGHT - && bb & s & ~CenterFiles // on a side outpost - && !(b & targets) // no relevant attacks - && (!more_than_one(targets & (s & QueenSide ? QueenSide : KingSide)))) - score += UncontestedOutpost * popcount(pos.pieces(PAWN) & (s & QueenSide ? QueenSide : KingSide)); - else if (bb & s) - score += Outpost[Pt == BISHOP]; - else if (Pt == KNIGHT && bb & b & ~pos.pieces(Us)) - score += ReachableOutpost; - - // Bonus for a knight or bishop shielded by pawn - if (shift(pos.pieces(PAWN)) & s) - score += MinorBehindPawn; - - // Penalty if the piece is far from the king - score -= KingProtector[Pt == BISHOP] * distance(pos.square(Us), s); - - if constexpr (Pt == BISHOP) - { - // Penalty according to the number of our pawns on the same color square as the - // bishop, bigger when the center files are blocked with pawns and smaller - // when the bishop is outside the pawn chain. - Bitboard blocked = pos.pieces(Us, PAWN) & shift(pos.pieces()); - - score -= BishopPawns[edge_distance(file_of(s))] * pos.pawns_on_same_color_squares(Us, s) - * (!(attackedBy[Us][PAWN] & s) + popcount(blocked & CenterFiles)); - - // Penalty for all enemy pawns x-rayed - score -= BishopXRayPawns * popcount(attacks_bb(s) & pos.pieces(Them, PAWN)); - - // Bonus for bishop on a long diagonal which can "see" both center squares - if (more_than_one(attacks_bb(s, pos.pieces(PAWN)) & Center)) - score += LongDiagonalBishop; - - // An important Chess960 pattern: a cornered bishop blocked by a friendly - // pawn diagonally in front of it is a very serious problem, especially - // when that pawn is also blocked. - if ( pos.is_chess960() - && (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1))) - { - Direction d = pawn_push(Us) + (file_of(s) == FILE_A ? EAST : WEST); - if (pos.piece_on(s + d) == make_piece(Us, PAWN)) - score -= !pos.empty(s + d + pawn_push(Us)) ? 4 * make_score(CorneredBishop, CorneredBishop) - : 3 * make_score(CorneredBishop, CorneredBishop); - } - } - } - - if constexpr (Pt == ROOK) - { - // Bonuses for rook on a (semi-)open or closed file - if (pos.is_on_semiopen_file(Us, s)) - { - score += RookOnOpenFile[pos.is_on_semiopen_file(Them, s)]; - } - else - { - // If our pawn on this file is blocked, increase penalty - if ( pos.pieces(Us, PAWN) - & shift(pos.pieces()) - & file_bb(s)) - { - score -= RookOnClosedFile; - } - - // Penalty when trapped by the king, even more if the king cannot castle - if (mob <= 3) - { - File kf = file_of(pos.square(Us)); - if ((kf < FILE_E) == (file_of(s) < kf)) - score -= TrappedRook * (1 + !pos.castling_rights(Us)); - } - } - } - - if constexpr (Pt == QUEEN) - { - // Penalty if any relative pin or discovered attack against the queen - Bitboard queenPinners; - if (pos.slider_blockers(pos.pieces(Them, ROOK, BISHOP), s, queenPinners)) - score -= WeakQueen; - } - } - if constexpr (T) - Trace::add(Pt, Us, score); - - return score; - } - - - // Evaluation::king() assigns bonuses and penalties to a king of a given color - - template template - Score Evaluation::king() const { - - constexpr Color Them = ~Us; - constexpr Bitboard Camp = (Us == WHITE ? AllSquares ^ Rank6BB ^ Rank7BB ^ Rank8BB - : AllSquares ^ Rank1BB ^ Rank2BB ^ Rank3BB); - - Bitboard weak, b1, b2, b3, safe, unsafeChecks = 0; - Bitboard rookChecks, queenChecks, bishopChecks, knightChecks; - int kingDanger = 0; - const Square ksq = pos.square(Us); - - // Init the score with king shelter and enemy pawns storm - Score score = pe->king_safety(pos); - - // Attacked squares defended at most once by our queen or king - weak = attackedBy[Them][ALL_PIECES] - & ~attackedBy2[Us] - & (~attackedBy[Us][ALL_PIECES] | attackedBy[Us][KING] | attackedBy[Us][QUEEN]); - - // Analyse the safe enemy's checks which are possible on next move - safe = ~pos.pieces(Them); - safe &= ~attackedBy[Us][ALL_PIECES] | (weak & attackedBy2[Them]); - - b1 = attacks_bb(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN)); - b2 = attacks_bb(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN)); - - // Enemy rooks checks - rookChecks = b1 & attackedBy[Them][ROOK] & safe; - if (rookChecks) - kingDanger += SafeCheck[ROOK][more_than_one(rookChecks)]; - else - unsafeChecks |= b1 & attackedBy[Them][ROOK]; - - // Enemy queen safe checks: count them only if the checks are from squares from - // which opponent cannot give a rook check, because rook checks are more valuable. - queenChecks = (b1 | b2) & attackedBy[Them][QUEEN] & safe - & ~(attackedBy[Us][QUEEN] | rookChecks); - if (queenChecks) - kingDanger += SafeCheck[QUEEN][more_than_one(queenChecks)]; - - // Enemy bishops checks: count them only if they are from squares from which - // opponent cannot give a queen check, because queen checks are more valuable. - bishopChecks = b2 & attackedBy[Them][BISHOP] & safe - & ~queenChecks; - if (bishopChecks) - kingDanger += SafeCheck[BISHOP][more_than_one(bishopChecks)]; - - else - unsafeChecks |= b2 & attackedBy[Them][BISHOP]; - - // Enemy knights checks - knightChecks = attacks_bb(ksq) & attackedBy[Them][KNIGHT]; - if (knightChecks & safe) - kingDanger += SafeCheck[KNIGHT][more_than_one(knightChecks & safe)]; - else - unsafeChecks |= knightChecks; - - // Find the squares that opponent attacks in our king flank, the squares - // which they attack twice in that flank, and the squares that we defend. - b1 = attackedBy[Them][ALL_PIECES] & KingFlank[file_of(ksq)] & Camp; - b2 = b1 & attackedBy2[Them]; - b3 = attackedBy[Us][ALL_PIECES] & KingFlank[file_of(ksq)] & Camp; - - int kingFlankAttack = popcount(b1) + popcount(b2); - int kingFlankDefense = popcount(b3); - - kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them] // (~10 Elo) - + 183 * popcount(kingRing[Us] & weak) // (~15 Elo) - + 148 * popcount(unsafeChecks) // (~4 Elo) - + 98 * popcount(pos.blockers_for_king(Us)) // (~2 Elo) - + 69 * kingAttacksCount[Them] // (~0.5 Elo) - + 3 * kingFlankAttack * kingFlankAttack / 8 // (~0.5 Elo) - + mg_value(mobility[Them] - mobility[Us]) // (~0.5 Elo) - - 873 * !pos.count(Them) // (~24 Elo) - - 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING]) // (~5 Elo) - - 6 * mg_value(score) / 8 // (~8 Elo) - - 4 * kingFlankDefense // (~5 Elo) - + 37; // (~0.5 Elo) - - // Transform the kingDanger units into a Score, and subtract it from the evaluation - if (kingDanger > 100) - score -= make_score(kingDanger * kingDanger / 4096, kingDanger / 16); - - // Penalty when our king is on a pawnless flank - if (!(pos.pieces(PAWN) & KingFlank[file_of(ksq)])) - score -= PawnlessFlank; - - // Penalty if king flank is under attack, potentially moving toward the king - score -= FlankAttacks * kingFlankAttack; - - if constexpr (T) - Trace::add(KING, Us, score); - - return score; - } - - - // Evaluation::threats() assigns bonuses according to the types of the - // attacking and the attacked pieces. - - template template - Score Evaluation::threats() const { - - constexpr Color Them = ~Us; - constexpr Direction Up = pawn_push(Us); - constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); - - Bitboard b, weak, defended, nonPawnEnemies, stronglyProtected, safe; - Score score = SCORE_ZERO; - - // Non-pawn enemies - nonPawnEnemies = pos.pieces(Them) & ~pos.pieces(PAWN); - - // Squares strongly protected by the enemy, either because they defend the - // square with a pawn, or because they defend the square twice and we don't. - stronglyProtected = attackedBy[Them][PAWN] - | (attackedBy2[Them] & ~attackedBy2[Us]); - - // Non-pawn enemies, strongly protected - defended = nonPawnEnemies & stronglyProtected; - - // Enemies not strongly protected and under our attack - weak = pos.pieces(Them) & ~stronglyProtected & attackedBy[Us][ALL_PIECES]; - - // Bonus according to the kind of attacking pieces - if (defended | weak) - { - b = (defended | weak) & (attackedBy[Us][KNIGHT] | attackedBy[Us][BISHOP]); - while (b) - score += ThreatByMinor[type_of(pos.piece_on(pop_lsb(b)))]; - - b = weak & attackedBy[Us][ROOK]; - while (b) - score += ThreatByRook[type_of(pos.piece_on(pop_lsb(b)))]; - - if (weak & attackedBy[Us][KING]) - score += ThreatByKing; - - b = ~attackedBy[Them][ALL_PIECES] - | (nonPawnEnemies & attackedBy2[Us]); - score += Hanging * popcount(weak & b); - - // Additional bonus if weak piece is only protected by a queen - score += WeakQueenProtection * popcount(weak & attackedBy[Them][QUEEN]); - } - - // Bonus for restricting their piece moves - b = attackedBy[Them][ALL_PIECES] - & ~stronglyProtected - & attackedBy[Us][ALL_PIECES]; - 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(); - - // Keep only the squares which are relatively safe - b &= ~attackedBy[Them][PAWN] & safe; - - // Bonus for safe pawn threats on the next move - b = pawn_attacks_bb(b) & nonPawnEnemies; - score += ThreatByPawnPush * popcount(b); - - // Bonus for threats on the next moves against enemy queen - if (pos.count(Them) == 1) - { - bool queenImbalance = pos.count() == 1; - - Square s = pos.square(Them); - safe = mobilityArea[Us] - & ~pos.pieces(Us, PAWN) - & ~stronglyProtected; - - b = attackedBy[Us][KNIGHT] & attacks_bb(s); - - score += KnightOnQueen * popcount(b & safe) * (1 + queenImbalance); - - b = (attackedBy[Us][BISHOP] & attacks_bb(s, pos.pieces())) - | (attackedBy[Us][ROOK ] & attacks_bb(s, pos.pieces())); - - score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]) * (1 + queenImbalance); - } - - if constexpr (T) - Trace::add(THREAT, Us, score); - - return score; - } - - // Evaluation::passed() evaluates the passed pawns and candidate passed - // pawns of the given color. - - template template - Score Evaluation::passed() const { - - constexpr Color Them = ~Us; - constexpr Direction Up = pawn_push(Us); - constexpr Direction Down = -Up; - - auto king_proximity = [&](Color c, Square s) { - return std::min(distance(pos.square(c), s), 5); - }; - - Bitboard b, bb, squaresToQueen, unsafeSquares, blockedPassers, helpers; - Score score = SCORE_ZERO; - - b = pe->passed_pawns(Us); - - blockedPassers = b & shift(pos.pieces(Them, PAWN)); - if (blockedPassers) - { - helpers = shift(pos.pieces(Us, PAWN)) - & ~pos.pieces(Them) - & (~attackedBy2[Them] | attackedBy[Us][ALL_PIECES]); - - // Remove blocked candidate passers that don't have help to pass - b &= ~blockedPassers - | shift(helpers) - | shift(helpers); - } - - while (b) - { - Square s = pop_lsb(b); - - assert(!(pos.pieces(Them, PAWN) & forward_file_bb(Us, s + Up))); - - int r = relative_rank(Us, s); - - Score bonus = PassedRank[r]; - - if (r > RANK_3) - { - int w = 5 * r - 13; - Square blockSq = s + Up; - - // Adjust bonus based on the king's proximity - bonus += make_score(0, ( king_proximity(Them, blockSq) * 19 / 4 - - king_proximity(Us, blockSq) * 2) * w); - - // If blockSq is not the queening square then consider also a second push - if (r != RANK_7) - bonus -= make_score(0, king_proximity(Us, blockSq + Up) * w); - - // If the pawn is free to advance, then increase the bonus - if (pos.empty(blockSq)) - { - 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(Them) & bb)) - unsafeSquares &= attackedBy[Them][ALL_PIECES] | pos.pieces(Them); - - // If there are no enemy pieces or attacks on passed pawn span, assign a big bonus. - // Or if there is some, but they are all attacked by our pawns, assign a bit smaller bonus. - // Otherwise assign a smaller bonus if the path to queen is not attacked - // and even smaller bonus if it is attacked but block square is not. - int k = !unsafeSquares ? 36 : - !(unsafeSquares & ~attackedBy[Us][PAWN]) ? 30 : - !(unsafeSquares & squaresToQueen) ? 17 : - !(unsafeSquares & blockSq) ? 7 : - 0 ; - - // Assign a larger bonus if the block square is defended - if ((pos.pieces(Us) & bb) || (attackedBy[Us][ALL_PIECES] & blockSq)) - k += 5; - - bonus += make_score(k * w, k * w); - } - } // r > RANK_3 - - score += bonus - PassedFile * edge_distance(file_of(s)); - } - - if constexpr (T) - Trace::add(PASSED, Us, score); - - return score; - } - - - // Evaluation::space() computes a space evaluation for a given side, aiming to improve game - // play in the opening. It is based on the number of safe squares on the four central files - // on ranks 2 to 4. Completely safe squares behind a friendly pawn are counted twice. - // Finally, the space bonus is multiplied by a weight which decreases according to occupancy. - - template template - Score Evaluation::space() const { - - // Early exit if, for example, both queens or 6 minor pieces have been exchanged - if (pos.non_pawn_material() < SpaceThreshold) - return SCORE_ZERO; - - constexpr Color Them = ~Us; - constexpr Direction Down = -pawn_push(Us); - constexpr Bitboard SpaceMask = - Us == WHITE ? CenterFiles & (Rank2BB | Rank3BB | Rank4BB) - : CenterFiles & (Rank7BB | Rank6BB | Rank5BB); - - // Find the available squares for our pieces inside the area defined by SpaceMask - Bitboard safe = SpaceMask - & ~pos.pieces(Us, PAWN) - & ~attackedBy[Them][PAWN]; - - // Find all squares which are at most three squares behind some friendly pawn - Bitboard behind = pos.pieces(Us, PAWN); - behind |= shift(behind); - behind |= shift(behind); - - // Compute space score based on the number of safe squares and number of our pieces - // increased with number of total blocked pawns in position. - int bonus = popcount(safe) + popcount(behind & safe & ~attackedBy[Them][ALL_PIECES]); - int weight = pos.count(Us) - 3 + std::min(pe->blocked_count(), 9); - Score score = make_score(bonus * weight * weight / 16, 0); - - if constexpr (T) - Trace::add(SPACE, Us, score); - - return score; - } - - - // Evaluation::winnable() adjusts the midgame and endgame score components, based on - // the known attacking/defending status of the players. The final value is derived - // by interpolation from the midgame and endgame values. - - template - Value Evaluation::winnable(Score score) const { - - int outflanking = distance(pos.square(WHITE), pos.square(BLACK)) - + int(rank_of(pos.square(WHITE)) - rank_of(pos.square(BLACK))); - - bool pawnsOnBothFlanks = (pos.pieces(PAWN) & QueenSide) - && (pos.pieces(PAWN) & KingSide); - - bool almostUnwinnable = outflanking < 0 - && !pawnsOnBothFlanks; - - bool infiltration = rank_of(pos.square(WHITE)) > RANK_4 - || rank_of(pos.square(BLACK)) < RANK_5; - - // Compute the initiative bonus for the attacking side - int complexity = 9 * pe->passed_count() - + 12 * pos.count() - + 9 * outflanking - + 21 * pawnsOnBothFlanks - + 24 * infiltration - + 51 * !pos.non_pawn_material() - - 43 * almostUnwinnable - -110 ; - - Value mg = mg_value(score); - Value eg = eg_value(score); - - // 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::clamp(complexity + 50, -abs(mg), 0); - int v = ((eg > 0) - (eg < 0)) * std::max(complexity, -abs(eg)); - - mg += u; - eg += v; - - // Compute the scale factor for the winning side - Color strongSide = eg > VALUE_DRAW ? WHITE : BLACK; - int sf = me->scale_factor(pos, strongSide); - - // If scale factor is not already specific, scale up/down via general heuristics - if (sf == SCALE_FACTOR_NORMAL) - { - if (pos.opposite_bishops()) - { - // For pure opposite colored bishops endgames use scale factor - // based on the number of passed pawns of the strong side. - if ( pos.non_pawn_material(WHITE) == BishopValueMg - && pos.non_pawn_material(BLACK) == BishopValueMg) - sf = 18 + 4 * popcount(pe->passed_pawns(strongSide)); - // For every other opposite colored bishops endgames use scale factor - // based on the number of all pieces of the strong side. - else - sf = 22 + 3 * pos.count(strongSide); - } - // For rook endgames with strong side not having overwhelming pawn number advantage - // and its pawns being on one flank and weak side protecting its pieces with a king - // use lower scale factor. - else if ( pos.non_pawn_material(WHITE) == RookValueMg - && pos.non_pawn_material(BLACK) == RookValueMg - && pos.count(strongSide) - pos.count(~strongSide) <= 1 - && bool(KingSide & pos.pieces(strongSide, PAWN)) != bool(QueenSide & pos.pieces(strongSide, PAWN)) - && (attacks_bb(pos.square(~strongSide)) & pos.pieces(~strongSide, PAWN))) - sf = 36; - // For queen vs no queen endgames use scale factor - // based on number of minors of side that doesn't have queen. - else if (pos.count() == 1) - sf = 37 + 3 * (pos.count(WHITE) == 1 ? pos.count(BLACK) + pos.count(BLACK) - : pos.count(WHITE) + pos.count(WHITE)); - // In every other case use scale factor based on - // the number of pawns of the strong side reduced if pawns are on a single flank. - else - sf = std::min(sf, 36 + 7 * pos.count(strongSide)) - 4 * !pawnsOnBothFlanks; - - // Reduce scale factor in case of pawns being on a single flank - sf -= 4 * !pawnsOnBothFlanks; - } - - // Interpolate between the middlegame and (scaled by 'sf') endgame score - v = mg * int(me->game_phase()) - + eg * int(PHASE_MIDGAME - me->game_phase()) * ScaleFactor(sf) / SCALE_FACTOR_NORMAL; - v /= PHASE_MIDGAME; - - if constexpr (T) - { - Trace::add(WINNABLE, make_score(u, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL - eg_value(score))); - Trace::add(TOTAL, make_score(mg, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL)); - } - - return Value(v); - } - - - // Evaluation::value() is the main function of the class. It computes the various - // parts of the evaluation and returns the value of the position from the point - // of view of the side to move. - - template - Value Evaluation::value() { - - assert(!pos.checkers()); - - // Probe the material hash table - me = Material::probe(pos); - - // If we have a specialized evaluation function for the current material - // configuration, call it and return. - if (me->specialized_eval_exists()) - return me->evaluate(pos); - - // Initialize score by reading the incrementally updated scores included in - // the position object (material + piece square tables) and the material - // imbalance. Score is computed internally from the white point of view. - Score score = pos.psq_score() + me->imbalance(); - - // Probe the pawn hash table - pe = Pawns::probe(pos); - score += pe->pawn_score(WHITE) - pe->pawn_score(BLACK); - - // Early exit if score is high - auto lazy_skip = [&](Value lazyThreshold) { - return abs(mg_value(score) + eg_value(score)) > lazyThreshold - + std::abs(pos.this_thread()->bestValue) * 5 / 4 - + pos.non_pawn_material() / 32; - }; - - if (lazy_skip(LazyThreshold1)) - goto make_v; - - // Main evaluation begins here - initialize(); - initialize(); - - // Pieces evaluated first (also populates attackedBy, attackedBy2). - // Note that the order of evaluation of the terms is left unspecified. - score += pieces() - pieces() - + pieces() - pieces() - + pieces() - pieces() - + pieces() - pieces(); - - score += mobility[WHITE] - mobility[BLACK]; - - // More complex interactions that require fully populated attack bitboards - score += king< WHITE>() - king< BLACK>() - + passed< WHITE>() - passed< BLACK>(); - - if (lazy_skip(LazyThreshold2)) - goto make_v; - - score += threats() - threats() - + space< WHITE>() - space< BLACK>(); - -make_v: - // Derive single value from mg and eg parts of score - Value v = winnable(score); - - // In case of tracing add all remaining individual evaluation terms - if constexpr (T) - { - Trace::add(MATERIAL, pos.psq_score()); - Trace::add(IMBALANCE, me->imbalance()); - Trace::add(PAWN, pe->pawn_score(WHITE), pe->pawn_score(BLACK)); - Trace::add(MOBILITY, mobility[WHITE], mobility[BLACK]); - } - - // Evaluation grain - v = (v / 16) * 16; - - // Side to move point of view - v = (pos.side_to_move() == WHITE ? v : -v); - - return v; - } - -} // namespace Eval - - /// evaluate() is the evaluator for the outer world. It returns a static /// evaluation of the position from the point of view of the side to move. @@ -1053,27 +147,17 @@ Value Eval::evaluate(const Position& pos) { Value v; Value psq = pos.psq_eg_stm(); - // We use the much less accurate but faster Classical eval when the NNUE - // option is set to false. Otherwise we use the NNUE eval unless the - // PSQ advantage is decisive. (~4 Elo at STC, 1 Elo at LTC) - bool useClassical = !useNNUE || abs(psq) > 2048; + int nnueComplexity; + int npm = pos.non_pawn_material() / 64; - if (useClassical) - v = Evaluation(pos).value(); - else - { - int nnueComplexity; - int npm = pos.non_pawn_material() / 64; + Color stm = pos.side_to_move(); + Value optimism = pos.this_thread()->optimism[stm]; - Color stm = pos.side_to_move(); - Value optimism = pos.this_thread()->optimism[stm]; + Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); - Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); - - // Blend optimism with nnue complexity and (semi)classical complexity - optimism += optimism * (nnueComplexity + abs(psq - nnue)) / 512; - v = (nnue * (945 + npm) + optimism * (150 + npm)) / 1024; - } + // Blend optimism with nnue complexity and (semi)classical complexity + optimism += optimism * (nnueComplexity + abs(psq - nnue)) / 512; + v = (nnue * (945 + npm) + optimism * (150 + npm)) / 1024; // Damp down the evaluation linearly when shuffling v = v * (200 - pos.rule50_count()) / 214; @@ -1094,62 +178,26 @@ std::string Eval::trace(Position& pos) { if (pos.checkers()) return "Final evaluation: none (in check)"; - std::stringstream ss; - ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2); - - Value v; - - std::memset(scores, 0, sizeof(scores)); - // Reset any global variable used in eval pos.this_thread()->bestValue = VALUE_ZERO; pos.this_thread()->optimism[WHITE] = VALUE_ZERO; pos.this_thread()->optimism[BLACK] = VALUE_ZERO; - v = Evaluation(pos).value(); - - ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2) - << " Contributing terms for the classical eval:\n" - << "+------------+-------------+-------------+-------------+\n" - << "| Term | White | Black | Total |\n" - << "| | MG EG | MG EG | MG EG |\n" - << "+------------+-------------+-------------+-------------+\n" - << "| Material | " << Term(MATERIAL) - << "| Imbalance | " << Term(IMBALANCE) - << "| Pawns | " << Term(PAWN) - << "| Knights | " << Term(KNIGHT) - << "| Bishops | " << Term(BISHOP) - << "| Rooks | " << Term(ROOK) - << "| Queens | " << Term(QUEEN) - << "| Mobility | " << Term(MOBILITY) - << "|King safety | " << Term(KING) - << "| Threats | " << Term(THREAT) - << "| Passed | " << Term(PASSED) - << "| Space | " << Term(SPACE) - << "| Winnable | " << Term(WINNABLE) - << "+------------+-------------+-------------+-------------+\n" - << "| Total | " << Term(TOTAL) - << "+------------+-------------+-------------+-------------+\n"; - - if (Eval::useNNUE) - ss << '\n' << NNUE::trace(pos) << '\n'; + std::stringstream ss; + ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2); + ss << '\n' << NNUE::trace(pos) << '\n'; ss << std::showpoint << std::showpos << std::fixed << std::setprecision(2) << std::setw(15); + Value v; + v = NNUE::evaluate(pos, false); v = pos.side_to_move() == WHITE ? v : -v; - ss << "\nClassical evaluation " << to_cp(v) << " (white side)\n"; - if (Eval::useNNUE) - { - v = NNUE::evaluate(pos, false); - v = pos.side_to_move() == WHITE ? v : -v; - ss << "NNUE evaluation " << to_cp(v) << " (white side)\n"; - } + ss << "NNUE evaluation " << to_cp(v) << " (white side)\n"; v = evaluate(pos); v = pos.side_to_move() == WHITE ? v : -v; ss << "Final evaluation " << to_cp(v) << " (white side)"; - if (Eval::useNNUE) - ss << " [with scaled NNUE, hybrid, ...]"; + ss << " [with scaled NNUE, ...]"; ss << "\n"; return ss.str(); diff --git a/src/main.cpp b/src/main.cpp index c40e0fa3492..593408f63a7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,7 +19,6 @@ #include #include "bitboard.h" -#include "endgame.h" #include "position.h" #include "psqt.h" #include "search.h" @@ -40,8 +39,6 @@ int main(int argc, char* argv[]) { PSQT::init(); Bitboards::init(); Position::init(); - Bitbases::init(); - Endgames::init(); Threads.set(size_t(Options["Threads"])); Search::clear(); // After threads are up Eval::NNUE::init(); diff --git a/src/material.cpp b/src/material.cpp deleted file mode 100644 index 7102f8799ea..00000000000 --- a/src/material.cpp +++ /dev/null @@ -1,229 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include -#include // For std::memset - -#include "material.h" -#include "thread.h" - -using namespace std; - -namespace Stockfish { - -namespace { - #define S(mg, eg) make_score(mg, eg) - - // Polynomial material imbalance parameters - - // One Score parameter for each pair (our piece, another of our pieces) - constexpr Score QuadraticOurs[][PIECE_TYPE_NB] = { - // OUR PIECE 2 - // bishop pair pawn knight bishop rook queen - {S(1419, 1455) }, // Bishop pair - {S( 101, 28), S( 37, 39) }, // Pawn - {S( 57, 64), S(249, 187), S(-49, -62) }, // Knight OUR PIECE 1 - {S( 0, 0), S(118, 137), S( 10, 27), S( 0, 0) }, // Bishop - {S( -63, -68), S( -5, 3), S(100, 81), S(132, 118), S(-246, -244) }, // Rook - {S(-210, -211), S( 37, 14), S(147, 141), S(161, 105), S(-158, -174), S(-9,-31) } // Queen - }; - - // One Score parameter for each pair (our piece, their piece) - constexpr Score QuadraticTheirs[][PIECE_TYPE_NB] = { - // THEIR PIECE - // bishop pair pawn knight bishop rook queen - { }, // Bishop pair - {S( 33, 30) }, // Pawn - {S( 46, 18), S(106, 84) }, // Knight OUR PIECE - {S( 75, 35), S( 59, 44), S( 60, 15) }, // Bishop - {S( 26, 35), S( 6, 22), S( 38, 39), S(-12, -2) }, // Rook - {S( 97, 93), S(100, 163), S(-58, -91), S(112, 192), S(276, 225) } // Queen - }; - - #undef S - - // Endgame evaluation and scaling functions are accessed directly and not through - // the function maps because they correspond to more than one material hash key. - Endgame EvaluateKXK[] = { Endgame(WHITE), Endgame(BLACK) }; - - Endgame ScaleKBPsK[] = { Endgame(WHITE), Endgame(BLACK) }; - Endgame ScaleKQKRPs[] = { Endgame(WHITE), Endgame(BLACK) }; - Endgame ScaleKPsK[] = { Endgame(WHITE), Endgame(BLACK) }; - Endgame ScaleKPKP[] = { Endgame(WHITE), Endgame(BLACK) }; - - // Helper used to detect a given material distribution - bool is_KXK(const Position& pos, Color us) { - return !more_than_one(pos.pieces(~us)) - && pos.non_pawn_material(us) >= RookValueMg; - } - - bool is_KBPsK(const Position& pos, Color us) { - return pos.non_pawn_material(us) == BishopValueMg - && pos.count(us) >= 1; - } - - bool is_KQKRPs(const Position& pos, Color us) { - return !pos.count(us) - && pos.non_pawn_material(us) == QueenValueMg - && pos.count(~us) == 1 - && pos.count(~us) >= 1; - } - - - /// imbalance() calculates the imbalance by comparing the piece count of each - /// piece type for both colors. - - template - Score imbalance(const int pieceCount[][PIECE_TYPE_NB]) { - - constexpr Color Them = ~Us; - - Score bonus = SCORE_ZERO; - - // Second-degree polynomial material imbalance, by Tord Romstad - for (int pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1) - { - if (!pieceCount[Us][pt1]) - continue; - - int v = QuadraticOurs[pt1][pt1] * pieceCount[Us][pt1]; - - for (int pt2 = NO_PIECE_TYPE; pt2 < pt1; ++pt2) - v += QuadraticOurs[pt1][pt2] * pieceCount[Us][pt2] - + QuadraticTheirs[pt1][pt2] * pieceCount[Them][pt2]; - - bonus += pieceCount[Us][pt1] * v; - } - - return bonus; - } - -} // namespace - -namespace Material { - - -/// Material::probe() looks up the current position's material configuration in -/// the material hash table. It returns a pointer to the Entry if the position -/// is found. Otherwise a new Entry is computed and stored there, so we don't -/// have to recompute all when the same material configuration occurs again. - -Entry* probe(const Position& pos) { - - Key key = pos.material_key(); - Entry* e = pos.this_thread()->materialTable[key]; - - if (e->key == key) - return e; - - std::memset(e, 0, sizeof(Entry)); - e->key = key; - e->factor[WHITE] = e->factor[BLACK] = (uint8_t)SCALE_FACTOR_NORMAL; - - Value npm_w = pos.non_pawn_material(WHITE); - Value npm_b = pos.non_pawn_material(BLACK); - Value npm = std::clamp(npm_w + npm_b, EndgameLimit, MidgameLimit); - - // Map total non-pawn material into [PHASE_ENDGAME, PHASE_MIDGAME] - e->gamePhase = Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit)); - - // Let's look if we have a specialized evaluation function for this particular - // material configuration. Firstly we look for a fixed configuration one, then - // for a generic one if the previous search failed. - if ((e->evaluationFunction = Endgames::probe(key)) != nullptr) - return e; - - for (Color c : { WHITE, BLACK }) - if (is_KXK(pos, c)) - { - e->evaluationFunction = &EvaluateKXK[c]; - return e; - } - - // OK, we didn't find any special evaluation function for the current material - // configuration. Is there a suitable specialized scaling function? - const auto* sf = Endgames::probe(key); - - if (sf) - { - e->scalingFunction[sf->strongSide] = sf; // Only strong color assigned - return e; - } - - // 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, BLACK }) - { - if (is_KBPsK(pos, c)) - e->scalingFunction[c] = &ScaleKBPsK[c]; - - else if (is_KQKRPs(pos, c)) - e->scalingFunction[c] = &ScaleKQKRPs[c]; - } - - if (npm_w + npm_b == VALUE_ZERO && pos.pieces(PAWN)) // Only pawns on the board - { - if (!pos.count(BLACK)) - { - assert(pos.count(WHITE) >= 2); - - e->scalingFunction[WHITE] = &ScaleKPsK[WHITE]; - } - else if (!pos.count(WHITE)) - { - assert(pos.count(BLACK) >= 2); - - e->scalingFunction[BLACK] = &ScaleKPsK[BLACK]; - } - else if (pos.count(WHITE) == 1 && pos.count(BLACK) == 1) - { - // This is a special case because we set scaling functions - // for both colors instead of only one. - e->scalingFunction[WHITE] = &ScaleKPKP[WHITE]; - e->scalingFunction[BLACK] = &ScaleKPKP[BLACK]; - } - } - - // Zero or just one pawn makes it difficult to win, even with a small material - // advantage. This catches some trivial draws like KK, KBK and KNK and gives a - // drawish scale factor for cases such as KRKBP and KmmKm (except for KBBKN). - if (!pos.count(WHITE) && npm_w - npm_b <= BishopValueMg) - e->factor[WHITE] = uint8_t(npm_w < RookValueMg ? SCALE_FACTOR_DRAW : - npm_b <= BishopValueMg ? 4 : 14); - - if (!pos.count(BLACK) && npm_b - npm_w <= BishopValueMg) - e->factor[BLACK] = uint8_t(npm_b < RookValueMg ? SCALE_FACTOR_DRAW : - npm_w <= BishopValueMg ? 4 : 14); - - // Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder - // for the bishop pair "extended piece", which allows us to be more flexible - // in defining bishop pair bonuses. - const int pieceCount[COLOR_NB][PIECE_TYPE_NB] = { - { pos.count(WHITE) > 1, pos.count(WHITE), pos.count(WHITE), - pos.count(WHITE) , pos.count(WHITE), pos.count(WHITE) }, - { pos.count(BLACK) > 1, pos.count(BLACK), pos.count(BLACK), - pos.count(BLACK) , pos.count(BLACK), pos.count(BLACK) } }; - - e->score = (imbalance(pieceCount) - imbalance(pieceCount)) / 16; - return e; -} - -} // namespace Material - -} // namespace Stockfish diff --git a/src/material.h b/src/material.h deleted file mode 100644 index 9acf78f5ab4..00000000000 --- a/src/material.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef MATERIAL_H_INCLUDED -#define MATERIAL_H_INCLUDED - -#include "endgame.h" -#include "misc.h" -#include "position.h" -#include "types.h" - -namespace Stockfish::Material { - -/// Material::Entry contains various information about a material configuration. -/// It contains a material imbalance evaluation, a function pointer to a special -/// endgame evaluation function (which in most cases is nullptr, meaning that the -/// standard evaluation function will be used), and scale factors. -/// -/// The scale factors are used to scale the evaluation score up or down. For -/// instance, in KRB vs KR endgames, the score is scaled down by a factor of 4, -/// which will result in scores of absolute value less than one pawn. - -struct Entry { - - Score imbalance() const { return score; } - Phase game_phase() const { return (Phase)gamePhase; } - bool specialized_eval_exists() const { return evaluationFunction != nullptr; } - Value evaluate(const Position& pos) const { return (*evaluationFunction)(pos); } - - // scale_factor() takes a position and a color as input and returns a scale factor - // for the given color. We have to provide the position in addition to the color - // because the scale factor may also be a function which should be applied to - // the position. For instance, in KBP vs K endgames, the scaling function looks - // for rook pawns and wrong-colored bishops. - ScaleFactor scale_factor(const Position& pos, Color c) const { - ScaleFactor sf = scalingFunction[c] ? (*scalingFunction[c])(pos) - : SCALE_FACTOR_NONE; - return sf != SCALE_FACTOR_NONE ? sf : ScaleFactor(factor[c]); - } - - Key key; - const EndgameBase* evaluationFunction; - const EndgameBase* scalingFunction[COLOR_NB]; // Could be one for each - // side (e.g. KPKP, KBPsK) - Score score; - int16_t gamePhase; - uint8_t factor[COLOR_NB]; -}; - -using Table = HashTable; - -Entry* probe(const Position& pos); - -} // namespace Stockfish::Material - -#endif // #ifndef MATERIAL_H_INCLUDED diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 329adfdaa9e..a1a90023909 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -137,8 +137,7 @@ namespace Stockfish::Eval::NNUE { } void hint_common_parent_position(const Position& pos) { - if (Eval::useNNUE) - featureTransformer->hint_common_access(pos); + featureTransformer->hint_common_access(pos); } // Evaluation function. Perform differential calculation. diff --git a/src/pawns.cpp b/src/pawns.cpp deleted file mode 100644 index 0ccafd9e9c8..00000000000 --- a/src/pawns.cpp +++ /dev/null @@ -1,305 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include -#include - -#include "bitboard.h" -#include "pawns.h" -#include "position.h" -#include "thread.h" - -namespace Stockfish { - -namespace { - - #define V Value - #define S(mg, eg) make_score(mg, eg) - - // Pawn penalties - constexpr Score Backward = S( 6, 19); - constexpr Score Doubled = S(11, 51); - constexpr Score DoubledEarly = S(17, 7); - constexpr Score Isolated = S( 1, 20); - constexpr Score WeakLever = S( 2, 57); - constexpr Score WeakUnopposed = S(15, 18); - - // Bonus for blocked pawns at 5th or 6th rank - constexpr Score BlockedPawn[2] = { S(-19, -8), S(-7, 3) }; - - constexpr Score BlockedStorm[RANK_NB] = { - S(0, 0), S(0, 0), S(64, 75), S(-3, 14), S(-12, 19), S(-7, 4), S(-10, 5) - }; - - // Connected pawn bonus - constexpr int Connected[RANK_NB] = { 0, 3, 7, 7, 15, 54, 86 }; - - // Strength of pawn shelter for our king by [distance from edge][rank]. - // RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king. - constexpr Value ShelterStrength[int(FILE_NB) / 2][RANK_NB] = { - { V(-2), V(85), V(95), V(53), V(39), V(23), V(25) }, - { V(-55), V(64), V(32), V(-55), V(-30), V(-11), V(-61) }, - { V(-11), V(75), V(19), V(-6), V(26), V(9), V(-47) }, - { V(-41), V(-11), V(-27), V(-58), V(-42), V(-66), V(-163) } - }; - - // 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. 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(94), V(-280), V(-170), V(90), V(59), V(47), V(53) }, - { V(43), V(-17), V(128), V(39), V(26), V(-17), V(15) }, - { V(-9), V(62), V(170), V(34), V(-5), V(-20), V(-11) }, - { V(-27), V(-19), V(106), V(10), V(2), V(-13), V(-24) } - }; - - - // KingOnFile[semi-open Us][semi-open Them] contains bonuses/penalties - // for king when the king is on a semi-open or open file. - constexpr Score KingOnFile[2][2] = {{ S(-18,11), S(-6,-3) }, - { S( 0, 0), S( 5,-4) }}; - - #undef S - #undef V - - - /// evaluate() calculates a score for the static pawn structure of the given position. - /// We cannot use the location of pieces or king in this function, as the evaluation - /// of the pawn structure will be stored in a small cache for speed reasons, and will - /// be re-used even when the pieces have moved. - - template - Score evaluate(const Position& pos, Pawns::Entry* e) { - - constexpr Color Them = ~Us; - constexpr Direction Up = pawn_push(Us); - constexpr Direction Down = -Up; - - Bitboard neighbours, stoppers, support, phalanx, opposed; - Bitboard lever, leverPush, blocked; - Square s; - bool backward, passed, doubled; - Score score = SCORE_ZERO; - Bitboard b = pos.pieces(Us, PAWN); - - Bitboard ourPawns = pos.pieces( Us, PAWN); - Bitboard theirPawns = pos.pieces(Them, PAWN); - - Bitboard doubleAttackThem = pawn_double_attacks_bb(theirPawns); - - e->passedPawns[Us] = 0; - e->kingSquares[Us] = SQ_NONE; - e->pawnAttacks[Us] = e->pawnAttacksSpan[Us] = pawn_attacks_bb(ourPawns); - e->blockedCount += popcount(shift(ourPawns) & (theirPawns | doubleAttackThem)); - - // Loop through all pawns of the current color and score each pawn - while (b) - { - s = pop_lsb(b); - - assert(pos.piece_on(s) == make_piece(Us, PAWN)); - - Rank r = relative_rank(Us, s); - - // Flag the pawn - opposed = theirPawns & forward_file_bb(Us, s); - blocked = theirPawns & (s + Up); - stoppers = theirPawns & passed_pawn_span(Us, s); - lever = theirPawns & pawn_attacks_bb(Us, s); - leverPush = theirPawns & pawn_attacks_bb(Us, s + Up); - doubled = ourPawns & (s - Up); - neighbours = ourPawns & adjacent_files_bb(s); - phalanx = neighbours & rank_bb(s); - support = neighbours & rank_bb(s - Up); - - if (doubled) - { - // Additional doubled penalty if none of their pawns is fixed - if (!(ourPawns & shift(theirPawns | pawn_attacks_bb(theirPawns)))) - score -= DoubledEarly; - } - - // A pawn is backward when it is behind all pawns of the same color on - // the adjacent files and cannot safely advance. - backward = !(neighbours & forward_ranks_bb(Them, s + Up)) - && (leverPush | blocked); - - // 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 - // (b) the only stoppers are the leverPush, but we outnumber them - // (c) there is only one front stopper which can be levered. - // (Refined in Evaluation::passed) - passed = !(stoppers ^ lever) - || ( !(stoppers ^ leverPush) - && popcount(phalanx) >= popcount(leverPush)) - || ( stoppers == blocked && r >= RANK_5 - && (shift(support) & ~(theirPawns | doubleAttackThem))); - - passed &= !(forward_file_bb(Us, s) & ourPawns); - - // 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) - { - int v = Connected[r] * (2 + bool(phalanx) - bool(opposed)) - + 22 * popcount(support); - - score += make_score(v, v * (r - 2) / 4); - } - - else if (!neighbours) - { - if ( opposed - && (ourPawns & forward_file_bb(Them, s)) - && !(theirPawns & adjacent_files_bb(s))) - score -= Doubled; - else - score -= Isolated - + WeakUnopposed * !opposed; - } - - else if (backward) - score -= Backward - + WeakUnopposed * !opposed * bool(~(FileABB | FileHBB) & s); - - if (!support) - score -= Doubled * doubled - + WeakLever * more_than_one(lever); - - if (blocked && r >= RANK_5) - score += BlockedPawn[r - RANK_5]; - } - - return score; - } - -} // namespace - -namespace Pawns { - - -/// Pawns::probe() looks up the current position's pawns configuration in -/// the pawns hash table. It returns a pointer to the Entry if the position -/// is found. Otherwise a new Entry is computed and stored there, so we don't -/// have to recompute all when the same pawns configuration occurs again. - -Entry* probe(const Position& pos) { - - Key key = pos.pawn_key(); - Entry* e = pos.this_thread()->pawnsTable[key]; - - if (e->key == key) - return e; - - e->key = key; - e->blockedCount = 0; - e->scores[WHITE] = evaluate(pos, e); - e->scores[BLACK] = evaluate(pos, e); - - return e; -} - - -/// Entry::evaluate_shelter() calculates the shelter bonus and the storm -/// penalty for a king, looking at the king file and the two closest files. - -template -Score Entry::evaluate_shelter(const Position& pos, Square ksq) const { - - constexpr Color Them = ~Us; - - Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq); - Bitboard ourPawns = b & pos.pieces(Us) & ~pawnAttacks[Them]; - Bitboard theirPawns = b & pos.pieces(Them); - - Score bonus = make_score(5, 5); - - File center = std::clamp(file_of(ksq), FILE_B, FILE_G); - for (File f = File(center - 1); f <= File(center + 1); ++f) - { - b = ourPawns & file_bb(f); - int ourRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0; - - b = theirPawns & file_bb(f); - int theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : 0; - - int d = edge_distance(f); - bonus += make_score(ShelterStrength[d][ourRank], 0); - - if (ourRank && (ourRank == theirRank - 1)) - bonus -= BlockedStorm[theirRank]; - else - bonus -= make_score(UnblockedStorm[d][theirRank], 0); - } - - // King On File - bonus -= KingOnFile[pos.is_on_semiopen_file(Us, ksq)][pos.is_on_semiopen_file(Them, ksq)]; - - return bonus; -} - - -/// Entry::do_king_safety() calculates a bonus for king safety. It is called only -/// when king square changes, which is about 20% of total king_safety() calls. - -template -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 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)) - shelter = std::max(shelter, evaluate_shelter(pos, relative_square(Us, SQ_G1)), compare); - - if (pos.can_castle(Us & QUEEN_SIDE)) - 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); - int minPawnDist = 6; - - if (pawns & attacks_bb(ksq)) - minPawnDist = 1; - else while (pawns) - minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(pawns))); - - return shelter - make_score(0, 16 * minPawnDist); -} - -// Explicit template instantiation -template Score Entry::do_king_safety(const Position& pos); -template Score Entry::do_king_safety(const Position& pos); - -} // namespace Pawns - -} // namespace Stockfish diff --git a/src/pawns.h b/src/pawns.h deleted file mode 100644 index d20e7c2ebe5..00000000000 --- a/src/pawns.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef PAWNS_H_INCLUDED -#define PAWNS_H_INCLUDED - -#include "misc.h" -#include "position.h" -#include "types.h" - -namespace Stockfish::Pawns { - -/// Pawns::Entry contains various information about a pawn structure. A lookup -/// to the pawn hash table (performed by calling the probe function) returns a -/// pointer to an Entry object. - -struct Entry { - - Score pawn_score(Color c) const { return scores[c]; } - Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; } - Bitboard passed_pawns(Color c) const { return passedPawns[c]; } - Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; } - int passed_count() const { return popcount(passedPawns[WHITE] | passedPawns[BLACK]); } - int blocked_count() const { return blockedCount; } - - template - Score king_safety(const Position& pos) { - return kingSquares[Us] == pos.square(Us) && castlingRights[Us] == pos.castling_rights(Us) - ? kingSafety[Us] : (kingSafety[Us] = do_king_safety(pos)); - } - - template - Score do_king_safety(const Position& pos); - - template - Score evaluate_shelter(const Position& pos, Square ksq) const; - - Key key; - Score scores[COLOR_NB]; - Bitboard passedPawns[COLOR_NB]; - Bitboard pawnAttacks[COLOR_NB]; - Bitboard pawnAttacksSpan[COLOR_NB]; - Square kingSquares[COLOR_NB]; - Score kingSafety[COLOR_NB]; - int castlingRights[COLOR_NB]; - int blockedCount; -}; - -using Table = HashTable; - -Entry* probe(const Position& pos); - -} // namespace Stockfish::Pawns - -#endif // #ifndef PAWNS_H_INCLUDED diff --git a/src/position.cpp b/src/position.cpp index a052cf32f03..6ecc52f8446 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -751,13 +751,10 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { else st->nonPawnMaterial[them] -= PieceValue[MG][captured]; - if (Eval::useNNUE) - { - dp.dirty_num = 2; // 1 piece moved, 1 piece captured - dp.piece[1] = captured; - dp.from[1] = capsq; - dp.to[1] = SQ_NONE; - } + dp.dirty_num = 2; // 1 piece moved, 1 piece captured + dp.piece[1] = captured; + dp.from[1] = capsq; + dp.to[1] = SQ_NONE; // Update board and piece lists remove_piece(capsq); @@ -765,7 +762,6 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // Update material hash key and prefetch access to materialTable k ^= Zobrist::psq[captured][capsq]; st->materialKey ^= Zobrist::psq[captured][pieceCount[captured]]; - prefetch(thisThread->materialTable[st->materialKey]); // Reset rule 50 counter st->rule50 = 0; @@ -792,12 +788,9 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // Move the piece. The tricky Chess960 castling is handled earlier if (type_of(m) != CASTLING) { - if (Eval::useNNUE) - { - dp.piece[0] = pc; - dp.from[0] = from; - dp.to[0] = to; - } + dp.piece[0] = pc; + dp.from[0] = from; + dp.to[0] = to; move_piece(from, to); } @@ -823,15 +816,12 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { remove_piece(to); put_piece(promotion, to); - if (Eval::useNNUE) - { - // Promoting pawn to SQ_NONE, promoted piece from SQ_NONE - dp.to[0] = SQ_NONE; - dp.piece[dp.dirty_num] = promotion; - dp.from[dp.dirty_num] = SQ_NONE; - dp.to[dp.dirty_num] = to; - dp.dirty_num++; - } + // Promoting pawn to SQ_NONE, promoted piece from SQ_NONE + dp.to[0] = SQ_NONE; + dp.piece[dp.dirty_num] = promotion; + dp.from[dp.dirty_num] = SQ_NONE; + dp.to[dp.dirty_num] = to; + dp.dirty_num++; // Update hash keys k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[promotion][to]; @@ -961,7 +951,7 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1); to = relative_square(us, kingSide ? SQ_G1 : SQ_C1); - if (Do && Eval::useNNUE) + if (Do) { auto& dp = st->dirtyPiece; dp.piece[0] = make_piece(us, KING); diff --git a/src/thread.h b/src/thread.h index 09bdb470b21..aa9db2f3633 100644 --- a/src/thread.h +++ b/src/thread.h @@ -25,9 +25,7 @@ #include #include -#include "material.h" #include "movepick.h" -#include "pawns.h" #include "position.h" #include "search.h" #include "thread_win32_osx.h" @@ -57,8 +55,6 @@ class Thread { void wait_for_search_finished(); size_t id() const { return idx; } - Pawns::Table pawnsTable; - Material::Table materialTable; size_t pvIdx, pvLast; std::atomic nodes, tbHits, bestMoveChanges; int selDepth, nmpMinPly; diff --git a/src/ucioption.cpp b/src/ucioption.cpp index f6342e5cb57..27f436d3b37 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -43,7 +43,6 @@ static void on_hash_size(const Option& o) { TT.resize(size_t(o)); } static void on_logger(const Option& o) { start_logger(o); } static void on_threads(const Option& o) { Threads.set(size_t(o)); } static void on_tb_path(const Option& o) { Tablebases::init(o); } -static void on_use_NNUE(const Option&) { Eval::NNUE::init(); } static void on_eval_file(const Option&) { Eval::NNUE::init(); } /// Our case insensitive less() function as required by UCI protocol @@ -79,7 +78,6 @@ void init(OptionsMap& o) { o["SyzygyProbeDepth"] << Option(1, 1, 100); o["Syzygy50MoveRule"] << Option(true); o["SyzygyProbeLimit"] << Option(7, 0, 7); - o["Use NNUE"] << Option(true, on_use_NNUE); o["EvalFile"] << Option(EvalFileDefaultName, on_eval_file); } From f972947492c513b7c0c4046058354b44a9ae9f04 Mon Sep 17 00:00:00 2001 From: Sebastian Buchwald Date: Wed, 12 Jul 2023 19:46:33 +0200 Subject: [PATCH 1124/1766] Cleanup code after removal of classical evaluation This includes the following changes: - Remove declaration of removed global variable - Adapt string that mentions removed UCI option closes https://github.com/official-stockfish/Stockfish/pull/4675 No functional change --- src/evaluate.cpp | 2 +- src/evaluate.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 2ab4fa404e0..a1bcdd20420 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -119,7 +119,7 @@ namespace Eval { { string msg1 = "Network evaluation parameters compatible with the engine must be available."; - string msg2 = "The option is set to true, but the network file " + eval_file + " was not loaded successfully."; + string msg2 = "The network file " + eval_file + " was not loaded successfully."; string msg3 = "The UCI option EvalFile might need to specify the full path, including the directory name, to the network file."; string msg4 = "The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/" + std::string(EvalFileDefaultName); string msg5 = "The engine will be terminated now."; diff --git a/src/evaluate.h b/src/evaluate.h index abdbef9010d..586e3b81f04 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -33,7 +33,6 @@ namespace Eval { std::string trace(Position& pos); Value evaluate(const Position& pos); - extern bool useNNUE; extern std::string currentEvalFileName; // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue From 529d3be8e245c06b62a45525cb9325ed6e50b636 Mon Sep 17 00:00:00 2001 From: mstembera Date: Tue, 11 Jul 2023 22:19:48 -0700 Subject: [PATCH 1125/1766] More simplifications and cleanup in affine_transform_sparse_input.h closes https://github.com/official-stockfish/Stockfish/pull/4677 No functional change --- .../layers/affine_transform_sparse_input.h | 44 +++++-------------- 1 file changed, 10 insertions(+), 34 deletions(-) diff --git a/src/nnue/layers/affine_transform_sparse_input.h b/src/nnue/layers/affine_transform_sparse_input.h index 3c7defcc42c..a5bea08e74b 100644 --- a/src/nnue/layers/affine_transform_sparse_input.h +++ b/src/nnue/layers/affine_transform_sparse_input.h @@ -34,43 +34,15 @@ */ namespace Stockfish::Eval::NNUE::Layers { -#if defined(__GNUC__) // GCC, Clang, ICC - - static inline IndexType lsb_(std::uint32_t b) { - assert(b); - return IndexType(__builtin_ctzl(b)); - } - -#elif defined(_MSC_VER) // MSVC - - static inline IndexType lsb_(std::uint32_t b) { - assert(b); - unsigned long idx; - _BitScanForward(&idx, b); - return (IndexType) idx; - } - -#else // Compiler is neither GCC nor MSVC compatible - -#error "Compiler not supported." - -#endif - #if defined(USE_SSSE3) alignas(CacheLineSize) static inline const std::array, 256> lookup_indices = [](){ std::array, 256> v{}; - for (int i = 0; i < 256; ++i) + for (unsigned i = 0; i < 256; ++i) { - int j = i; - int k = 0; + std::uint64_t j = i, k = 0; while(j) - { - const IndexType lsbIndex = lsb_(std::uint32_t(j)); - j &= j - 1; - v[i][k] = lsbIndex; - ++k; - } + v[i][k++] = pop_lsb(j); } return v; }(); @@ -83,7 +55,11 @@ namespace Stockfish::Eval::NNUE::Layers { #define vec_nnz(a) _mm512_cmpgt_epi32_mask(a, _mm512_setzero_si512()) #elif defined (USE_AVX2) using vec_t = __m256i; - #define vec_nnz(a) _mm256_movemask_ps(_mm256_castsi256_ps(_mm256_cmpgt_epi32(a, _mm256_setzero_si256()))) + #if defined(USE_VNNI) && !defined(USE_AVXVNNI) + #define vec_nnz(a) _mm256_cmpgt_epi32_mask(a, _mm256_setzero_si256()) + #else + #define vec_nnz(a) _mm256_movemask_ps(_mm256_castsi256_ps(_mm256_cmpgt_epi32(a, _mm256_setzero_si256()))) + #endif #elif defined (USE_SSSE3) using vec_t = __m128i; #define vec_nnz(a) _mm_movemask_ps(_mm_castsi128_ps(_mm_cmpgt_epi32(a, _mm_setzero_si128()))) @@ -97,8 +73,8 @@ namespace Stockfish::Eval::NNUE::Layers { const auto inputVector = reinterpret_cast(input); IndexType count = 0; - __m128i base = _mm_set1_epi16(0); - __m128i increment = _mm_set1_epi16(8); + __m128i base = _mm_setzero_si128(); + const __m128i increment = _mm_set1_epi16(8); for (IndexType i = 0; i < NumChunks; ++i) { // bitmask of nonzero values in this chunk From f5ab5832c6d02ec742b507bcb42181403a848bb9 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 12 Jul 2023 19:34:07 +0200 Subject: [PATCH 1126/1766] Generate binaries for more advanced architectures use intel's Software Development Emulator (SDE) in the actions that build the binaries. This allows for building on Windows and Linux binaries for - x86-64-avx512 - x86-64-vnni256 - x86-64-vnni512 (x86-64-avxvnni needs more recent gcc in the actions) also build x86-64-avx2 on macos. closes https://github.com/official-stockfish/Stockfish/pull/4679 No functional change --- .github/workflows/stockfish_binaries.yml | 26 +++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/.github/workflows/stockfish_binaries.yml b/.github/workflows/stockfish_binaries.yml index f7669b479aa..fa330974dd2 100644 --- a/.github/workflows/stockfish_binaries.yml +++ b/.github/workflows/stockfish_binaries.yml @@ -9,6 +9,7 @@ jobs: COMPILER: ${{ matrix.config.compiler }} COMP: ${{ matrix.config.comp }} EXT: ${{ matrix.config.ext }} + SDE: ${{ matrix.config.sde }} NAME: ${{ matrix.config.simple_name }} BINARY: ${{ matrix.binaries }} strategy: @@ -21,6 +22,7 @@ jobs: comp: gcc shell: bash archive_ext: tar + sde: /home/runner/work/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.14.0-2022-10-25-lin/sde -future - name: MacOS 12 Apple Clang os: macos-12 simple_name: macos @@ -37,17 +39,28 @@ jobs: msys_env: x86_64-gcc shell: msys2 {0} ext: .exe + sde: /d/a/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.14.0-2022-10-25-win/sde.exe -future archive_ext: zip binaries: - x86-64 - x86-64-modern - x86-64-avx2 - x86-64-bmi2 + # - x86-64-avxvnni needs more recent gcc + - x86-64-avx512 + - x86-64-vnni256 + - x86-64-vnni512 exclude: - - binaries: x86-64-avx2 - config: { os: macos-12 } - binaries: x86-64-bmi2 config: { os: macos-12 } + #- binaries: x86-64-avxvnni + # config: { os: macos-12 } + - binaries: x86-64-avx512 + config: { os: macos-12 } + - binaries: x86-64-vnni256 + config: { os: macos-12 } + - binaries: x86-64-vnni512 + config: { os: macos-12 } defaults: run: working-directory: src @@ -68,6 +81,13 @@ jobs: msystem: ${{ matrix.config.msys_sys }} install: mingw-w64-${{ matrix.config.msys_env }} make git zip + - name: Download SDE package + if: runner.os == 'Linux' || runner.os == 'Windows' + uses: petarpetrovt/setup-sde@v2.1 + with: + environmentVariableName: SDE_DIR + sdeVersion: 9.14.0 + - name: Download the used network from the fishtest framework run: make net @@ -84,7 +104,7 @@ jobs: - name: Compile ${{ matrix.binaries }} build run: | - make -j2 profile-build ARCH=$BINARY COMP=$COMP + make -j2 profile-build ARCH=$BINARY COMP=$COMP SDE_PATH="$SDE" make strip ARCH=$BINARY COMP=$COMP mv ./stockfish$EXT ../stockfish-$NAME-$BINARY$EXT From acdbf45171ba8bd0322e3bda0900e9cb2f8fb846 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sat, 15 Jul 2023 00:51:14 +0300 Subject: [PATCH 1127/1766] Use more expressive names for bonus1 and bonus2 closes https://github.com/official-stockfish/Stockfish/pull/4683 No functional change --- src/search.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 1f8f361c409..11a52326dbe 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1703,28 +1703,28 @@ namespace { Piece moved_piece = pos.moved_piece(bestMove); PieceType captured; - int bonus1 = stat_bonus(depth + 1); + int quietMoveBonus = stat_bonus(depth + 1); if (!pos.capture_stage(bestMove)) { - int bonus2 = bestValue > beta + 145 ? bonus1 // larger bonus - : stat_bonus(depth); // smaller bonus + int bestMoveBonus = bestValue > beta + 145 ? quietMoveBonus // larger bonus + : stat_bonus(depth); // smaller bonus // Increase stats for the best move in case it was a quiet move - update_quiet_stats(pos, ss, bestMove, bonus2); + update_quiet_stats(pos, ss, bestMove, bestMoveBonus); // Decrease stats for all non-best quiet moves for (int i = 0; i < quietCount; ++i) { - thisThread->mainHistory[us][from_to(quietsSearched[i])] << -bonus2; - update_continuation_histories(ss, pos.moved_piece(quietsSearched[i]), to_sq(quietsSearched[i]), -bonus2); + thisThread->mainHistory[us][from_to(quietsSearched[i])] << -bestMoveBonus; + update_continuation_histories(ss, pos.moved_piece(quietsSearched[i]), to_sq(quietsSearched[i]), -bestMoveBonus); } } else { // Increase stats for the best move in case it was a capture move captured = type_of(pos.piece_on(to_sq(bestMove))); - captureHistory[moved_piece][to_sq(bestMove)][captured] << bonus1; + captureHistory[moved_piece][to_sq(bestMove)][captured] << quietMoveBonus; } // Extra penalty for a quiet early move that was not a TT move or @@ -1732,14 +1732,14 @@ namespace { if ( prevSq != SQ_NONE && ((ss-1)->moveCount == 1 + (ss-1)->ttHit || ((ss-1)->currentMove == (ss-1)->killers[0])) && !pos.captured_piece()) - update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -bonus1); + update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -quietMoveBonus); // Decrease stats for all non-best capture moves for (int i = 0; i < captureCount; ++i) { moved_piece = pos.moved_piece(capturesSearched[i]); captured = type_of(pos.piece_on(to_sq(capturesSearched[i]))); - captureHistory[moved_piece][to_sq(capturesSearched[i])][captured] << -bonus1; + captureHistory[moved_piece][to_sq(capturesSearched[i])][captured] << -quietMoveBonus; } } From a3a91f3f9f4184a699a827c66e806d3a01656446 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 14 Jul 2023 17:43:56 +0200 Subject: [PATCH 1128/1766] Build and test more binaries in CI use a fixed compiler on Linux and Windows (right now gcc 11). build avxvnni on Windows (Linux needs updated core utils) build x86-32 on Linux (Windows needs other mingw) fix a Makefile issue where a failed PGOBENCH would not stop the build reuse the WINE_PATH for SDE as we do for QEMU use WINE_PATH variable also for the signature verify the bench for each of the binaries do not build x86-64-avx2 on macos closes https://github.com/official-stockfish/Stockfish/pull/4682 No functional change --- .github/workflows/stockfish_arm_binaries.yml | 8 ++++ .github/workflows/stockfish_binaries.yml | 45 ++++++++++++++++---- src/Makefile | 11 ++--- tests/signature.sh | 2 +- 4 files changed, 50 insertions(+), 16 deletions(-) diff --git a/.github/workflows/stockfish_arm_binaries.yml b/.github/workflows/stockfish_arm_binaries.yml index 4db216eb5fb..dfe4e2a24ce 100644 --- a/.github/workflows/stockfish_arm_binaries.yml +++ b/.github/workflows/stockfish_arm_binaries.yml @@ -70,6 +70,13 @@ jobs: echo "ANDROID_NDK_BIN=$ANDROID_NDK_BIN" >> $GITHUB_ENV fi + - name: Extract the bench number from the commit history + run: | + for hash in $(git rev-list -100 HEAD); do + benchref=$(git show -s $hash | tac | grep -m 1 -o -x '[[:space:]]*\b[Bb]ench[ :]\+[1-9][0-9]\{5,7\}\b[[:space:]]*' | sed 's/[^0-9]//g') && break || true + done + [[ -n "$benchref" ]] && echo "benchref=$benchref" >> $GITHUB_ENV && echo "From commit: $hash" && echo "Reference bench: $benchref" || echo "No bench found" + - name: Download the used network from the fishtest framework run: make net @@ -97,6 +104,7 @@ jobs: make clean make -j2 profile-build ARCH=$BINARY COMP=$COMP WINE_PATH=$EMU make strip ARCH=$BINARY COMP=$COMP + WINE_PATH=$EMU ../tests/signature.sh $benchref mv ./stockfish$EXT ../stockfish-android-$BINARY$EXT - name: Remove non src files diff --git a/.github/workflows/stockfish_binaries.yml b/.github/workflows/stockfish_binaries.yml index fa330974dd2..e761c845396 100644 --- a/.github/workflows/stockfish_binaries.yml +++ b/.github/workflows/stockfish_binaries.yml @@ -22,7 +22,7 @@ jobs: comp: gcc shell: bash archive_ext: tar - sde: /home/runner/work/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.14.0-2022-10-25-lin/sde -future + sde: /home/runner/work/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.14.0-2022-10-25-lin/sde -future -- - name: MacOS 12 Apple Clang os: macos-12 simple_name: macos @@ -39,22 +39,29 @@ jobs: msys_env: x86_64-gcc shell: msys2 {0} ext: .exe - sde: /d/a/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.14.0-2022-10-25-win/sde.exe -future + sde: /d/a/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.14.0-2022-10-25-win/sde.exe -future -- archive_ext: zip binaries: + - x86-32 - x86-64 - x86-64-modern - x86-64-avx2 - x86-64-bmi2 - # - x86-64-avxvnni needs more recent gcc + - x86-64-avxvnni - x86-64-avx512 - x86-64-vnni256 - x86-64-vnni512 exclude: + - binaries: x86-32 + config: { os: macos-12 } + - binaries: x86-64-avx2 + config: { os: macos-12 } - binaries: x86-64-bmi2 config: { os: macos-12 } - #- binaries: x86-64-avxvnni - # config: { os: macos-12 } + - binaries: x86-64-avxvnni + config: { os: macos-12 } + - binaries: x86-64-avxvnni + config: { ubuntu-20.04 } - binaries: x86-64-avx512 config: { os: macos-12 } - binaries: x86-64-vnni256 @@ -70,9 +77,17 @@ jobs: with: fetch-depth: 0 - - name: Download required linux packages + - name: Download required Linux packages + if: runner.os == 'Linux' + run: | + sudo apt update + sudo apt install g++-multilib g++-11-multilib + + - name: Install fixed GCC on Linux if: runner.os == 'Linux' - run: sudo apt update + uses: egor-tensin/setup-gcc@v1 + with: + version: 11 - name: Setup msys and install required packages if: runner.os == 'Windows' @@ -81,6 +96,12 @@ jobs: msystem: ${{ matrix.config.msys_sys }} install: mingw-w64-${{ matrix.config.msys_env }} make git zip + - name: Install fixed GCC on Windows + if: runner.os == 'Windows' + run: | + wget https://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-gcc-11.3.0-2-any.pkg.tar.zst + pacman -U mingw-w64-x86_64-gcc-11.3.0-2-any.pkg.tar.zst --noconfirm + - name: Download SDE package if: runner.os == 'Linux' || runner.os == 'Windows' uses: petarpetrovt/setup-sde@v2.1 @@ -91,6 +112,13 @@ jobs: - name: Download the used network from the fishtest framework run: make net + - name: Extract the bench number from the commit history + run: | + for hash in $(git rev-list -100 HEAD); do + benchref=$(git show -s $hash | tac | grep -m 1 -o -x '[[:space:]]*\b[Bb]ench[ :]\+[1-9][0-9]\{5,7\}\b[[:space:]]*' | sed 's/[^0-9]//g') && break || true + done + [[ -n "$benchref" ]] && echo "benchref=$benchref" >> $GITHUB_ENV && echo "From commit: $hash" && echo "Reference bench: $benchref" || echo "No bench found" + - name: Check compiler run: $COMPILER -v @@ -104,8 +132,9 @@ jobs: - name: Compile ${{ matrix.binaries }} build run: | - make -j2 profile-build ARCH=$BINARY COMP=$COMP SDE_PATH="$SDE" + make -j2 profile-build ARCH=$BINARY COMP=$COMP WINE_PATH="$SDE" make strip ARCH=$BINARY COMP=$COMP + WINE_PATH="$SDE" ../tests/signature.sh $benchref mv ./stockfish$EXT ../stockfish-$NAME-$BINARY$EXT - name: Remove non src files diff --git a/src/Makefile b/src/Makefile index a0f098fa678..966ac2a7a60 100644 --- a/src/Makefile +++ b/src/Makefile @@ -49,11 +49,7 @@ PREFIX = /usr/local BINDIR = $(PREFIX)/bin ### Built-in benchmark for pgo-builds -ifeq ($(SDE_PATH),) - PGOBENCH = $(WINE_PATH) ./$(EXE) bench -else - PGOBENCH = $(SDE_PATH) -- $(WINE_PATH) ./$(EXE) bench -endif +PGOBENCH = $(WINE_PATH) ./$(EXE) bench ### Source and object files SRCS = benchmark.cpp bitboard.cpp evaluate.cpp main.cpp \ @@ -849,7 +845,8 @@ profile-build: net config-sanity objclean profileclean $(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_make) @echo "" @echo "Step 2/4. Running benchmark for pgo-build ..." - $(PGOBENCH) 2>&1 | tail -n 4 + $(PGOBENCH) > PGOBENCH.out 2>&1 + tail -n 4 PGOBENCH.out @echo "" @echo "Step 3/4. Building optimized executable ..." $(MAKE) ARCH=$(ARCH) COMP=$(COMP) objclean @@ -913,7 +910,7 @@ objclean: # clean auxiliary profiling files profileclean: @rm -rf profdir - @rm -f bench.txt *.gcda *.gcno ./syzygy/*.gcda ./nnue/*.gcda ./nnue/features/*.gcda *.s + @rm -f bench.txt *.gcda *.gcno ./syzygy/*.gcda ./nnue/*.gcda ./nnue/features/*.gcda *.s PGOBENCH.out @rm -f stockfish.profdata *.profraw @rm -f stockfish.*args* @rm -f stockfish.*lt* diff --git a/tests/signature.sh b/tests/signature.sh index 2e5c183a073..06bd1892e6c 100755 --- a/tests/signature.sh +++ b/tests/signature.sh @@ -11,7 +11,7 @@ trap 'error ${LINENO}' ERR # obtain -signature=`./stockfish bench 2>&1 | grep "Nodes searched : " | awk '{print $4}'` +signature=`eval "$WINE_PATH ./stockfish bench 2>&1" | grep "Nodes searched : " | awk '{print $4}'` if [ $# -gt 0 ]; then # compare to given reference From ee53f8ed2f9418b55b05811da93ddf62f03c255f Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Sun, 2 Jul 2023 22:26:52 -0400 Subject: [PATCH 1129/1766] Reintroduce nnue eval pawn count multipliers again With separate multipliers for nnue eval and optimism scaling. This patch used 4 out of 7 params tuned with spsa at 30+0.3 using this tuning config: Value LazyThreshold1 = Value(3622); Value LazyThreshold2 = Value(1962); int psqThresh = 2048; int nnueNpmBase = 945; int nnuePcMult = 0; int optNpmBase = 150; int optPcMult = 0; TUNE(SetRange(3322, 3922), LazyThreshold1); TUNE(SetRange(1662, 2262), LazyThreshold2); TUNE(SetRange(1748, 2348), psqThresh); TUNE(SetRange(745, 1145), nnueNpmBase); TUNE(SetRange(-16, 16), nnuePcMult); TUNE(SetRange(0, 300), optNpmBase); TUNE(SetRange(-16, 16), optPcMult); Passed STC: https://tests.stockfishchess.org/tests/view/64a5a9b402cd07745c60ed07 LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 173632 W: 44417 L: 43903 D: 85312 Ptnml(0-2): 547, 20025, 45068, 20719, 457 Passed LTC: https://tests.stockfishchess.org/tests/view/64a972a302cd07745c6136af LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 277644 W: 70955 L: 70147 D: 136542 Ptnml(0-2): 193, 29902, 77787, 30784, 156 closes https://github.com/official-stockfish/Stockfish/pull/4681 bench 1556301 --- src/evaluate.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index a1bcdd20420..d4d8daee4da 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -157,7 +157,9 @@ Value Eval::evaluate(const Position& pos) { // Blend optimism with nnue complexity and (semi)classical complexity optimism += optimism * (nnueComplexity + abs(psq - nnue)) / 512; - v = (nnue * (945 + npm) + optimism * (150 + npm)) / 1024; + + v = ( nnue * (915 + npm + 9 * pos.count()) + + optimism * (154 + npm + pos.count())) / 1024; // Damp down the evaluation linearly when shuffling v = v * (200 - pos.rule50_count()) / 214; From a42ab95e1f2392406499b385149385a60cac5b66 Mon Sep 17 00:00:00 2001 From: AndrovT <31534597+AndrovT@users.noreply.github.com> Date: Sat, 15 Jul 2023 11:00:18 +0200 Subject: [PATCH 1130/1766] remove large input specialization Removes unused large input specialization for dense affine transform. It has been obsolete since #4612 was merged. closes https://github.com/official-stockfish/Stockfish/pull/4684 No functional change --- src/nnue/layers/affine_transform.h | 261 +---------------------------- 1 file changed, 3 insertions(+), 258 deletions(-) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 9e2f2f97323..b0169306cb7 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -29,25 +29,10 @@ /* This file contains the definition for a fully connected layer (aka affine transform). - Two approaches are employed, depending on the sizes of the transform. - - Approach 1 (a specialization for large inputs): - - used when the PaddedInputDimensions >= 128 - - uses AVX512 if possible - - processes inputs in batches of 2*InputSimdWidth - - so in batches of 128 for AVX512 - - the weight blocks of size InputSimdWidth are transposed such that - access is sequential - - N columns of the weight matrix are processed a time, where N - depends on the architecture (the amount of registers) - - accumulate + hadd is used - - Approach 2 (a specialization for small inputs): - - used when the PaddedInputDimensions < 128 + - expected use-case is for when PaddedInputDimensions == 32 and InputDimensions <= 32. - that's why AVX512 is hard to implement - expected use-case is small layers - - not optimized as well as the approach 1 - inputs are processed in chunks of 4, weights are respectively transposed - accumulation happens directly to int32s */ @@ -55,7 +40,7 @@ namespace Stockfish::Eval::NNUE::Layers { // Fallback implementation for older/other architectures. -// Identical for both approaches. Requires the input to be padded to at least 16 values. +// Requires the input to be padded to at least 16 values. #if !defined(USE_SSSE3) template static void affine_transform_non_ssse3(std::int32_t* output, const std::int8_t* weights, const std::int32_t* biases, const std::uint8_t* input) @@ -159,18 +144,8 @@ namespace Stockfish::Eval::NNUE::Layers { } #endif - template - class AffineTransform; - -#if defined (USE_AVX512) - constexpr IndexType LargeInputSize = 2 * 64; -#else - constexpr IndexType LargeInputSize = std::numeric_limits::max(); -#endif - - // A specialization for large inputs template - class AffineTransform(InDims, MaxSimdWidth) >= LargeInputSize)>> { + class AffineTransform { public: // Input/output type using InputType = std::uint8_t; @@ -187,236 +162,6 @@ namespace Stockfish::Eval::NNUE::Layers { using OutputBuffer = OutputType[PaddedOutputDimensions]; - static_assert(PaddedInputDimensions >= LargeInputSize, "Something went wrong. This specialization (for large inputs) should not have been chosen."); - -#if defined (USE_AVX512) - static constexpr IndexType InputSimdWidth = 64; - static constexpr IndexType MaxNumOutputRegs = 16; -#elif defined (USE_AVX2) - static constexpr IndexType InputSimdWidth = 32; - static constexpr IndexType MaxNumOutputRegs = 8; -#elif defined (USE_SSSE3) - static constexpr IndexType InputSimdWidth = 16; - static constexpr IndexType MaxNumOutputRegs = 8; -#elif defined (USE_NEON_DOTPROD) - static constexpr IndexType InputSimdWidth = 16; - static constexpr IndexType MaxNumOutputRegs = 8; -#elif defined (USE_NEON) - static constexpr IndexType InputSimdWidth = 8; - static constexpr IndexType MaxNumOutputRegs = 8; -#else - // The fallback implementation will not have permuted weights. - // We define these to avoid a lot of ifdefs later. - static constexpr IndexType InputSimdWidth = 1; - static constexpr IndexType MaxNumOutputRegs = 1; -#endif - - // A big block is a region in the weight matrix of the size [PaddedInputDimensions, NumOutputRegs]. - // A small block is a region of size [InputSimdWidth, 1] - - static constexpr IndexType NumOutputRegs = std::min(MaxNumOutputRegs, OutputDimensions); - static constexpr IndexType SmallBlockSize = InputSimdWidth; - static constexpr IndexType BigBlockSize = NumOutputRegs * PaddedInputDimensions; - static constexpr IndexType NumSmallBlocksInBigBlock = BigBlockSize / SmallBlockSize; - static constexpr IndexType NumSmallBlocksPerOutput = PaddedInputDimensions / SmallBlockSize; - static constexpr IndexType NumBigBlocks = OutputDimensions / NumOutputRegs; - - static_assert(OutputDimensions % NumOutputRegs == 0); - - // Hash value embedded in the evaluation file - static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) { - std::uint32_t hashValue = 0xCC03DAE4u; - hashValue += OutputDimensions; - hashValue ^= prevHash >> 1; - hashValue ^= prevHash << 31; - return hashValue; - } - - /* - Transposes the small blocks within a block. - Effectively means that weights can be traversed sequentially during inference. - */ - static IndexType get_weight_index(IndexType i) - { - const IndexType smallBlock = (i / SmallBlockSize) % NumSmallBlocksInBigBlock; - const IndexType smallBlockCol = smallBlock / NumSmallBlocksPerOutput; - const IndexType smallBlockRow = smallBlock % NumSmallBlocksPerOutput; - const IndexType bigBlock = i / BigBlockSize; - const IndexType rest = i % SmallBlockSize; - - const IndexType idx = - bigBlock * BigBlockSize - + smallBlockRow * SmallBlockSize * NumOutputRegs - + smallBlockCol * SmallBlockSize - + rest; - - return idx; - } - - // Read network parameters - bool read_parameters(std::istream& stream) { - read_little_endian(stream, biases, OutputDimensions); - - for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) - weights[get_weight_index(i)] = read_little_endian(stream); - - return !stream.fail(); - } - - // Write network parameters - bool write_parameters(std::ostream& stream) const { - write_little_endian(stream, biases, OutputDimensions); - - for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) - write_little_endian(stream, weights[get_weight_index(i)]); - - return !stream.fail(); - } - - // Forward propagation - const OutputType* propagate( - const InputType* input, OutputType* output) const { - -#if defined (USE_AVX512) - using acc_vec_t = __m512i; - using bias_vec_t = __m128i; - using weight_vec_t = __m512i; - using in_vec_t = __m512i; - #define vec_zero _mm512_setzero_si512() - #define vec_add_dpbusd_32x2 Simd::m512_add_dpbusd_epi32x2 - #define vec_hadd Simd::m512_hadd - #define vec_haddx4 Simd::m512_haddx4 -#elif defined (USE_AVX2) - using acc_vec_t = __m256i; - using bias_vec_t = __m128i; - using weight_vec_t = __m256i; - using in_vec_t = __m256i; - #define vec_zero _mm256_setzero_si256() - #define vec_add_dpbusd_32x2 Simd::m256_add_dpbusd_epi32x2 - #define vec_hadd Simd::m256_hadd - #define vec_haddx4 Simd::m256_haddx4 -#elif defined (USE_SSSE3) - using acc_vec_t = __m128i; - using bias_vec_t = __m128i; - using weight_vec_t = __m128i; - using in_vec_t = __m128i; - #define vec_zero _mm_setzero_si128() - #define vec_add_dpbusd_32x2 Simd::m128_add_dpbusd_epi32x2 - #define vec_hadd Simd::m128_hadd - #define vec_haddx4 Simd::m128_haddx4 -#elif defined (USE_NEON_DOTPROD) - using acc_vec_t = int32x4_t; - using bias_vec_t = int32x4_t; - using weight_vec_t = int8x16_t; - using in_vec_t = int8x16_t; - #define vec_zero {0} - #define vec_add_dpbusd_32x2 Simd::dotprod_m128_add_dpbusd_epi32x2 - #define vec_hadd Simd::neon_m128_hadd - #define vec_haddx4 Simd::neon_m128_haddx4 -#elif defined (USE_NEON) - using acc_vec_t = int32x4_t; - using bias_vec_t = int32x4_t; - using weight_vec_t = int8x8_t; - using in_vec_t = int8x8_t; - #define vec_zero {0} - #define vec_add_dpbusd_32x2 Simd::neon_m128_add_dpbusd_epi32x2 - #define vec_hadd Simd::neon_m128_hadd - #define vec_haddx4 Simd::neon_m128_haddx4 -#endif - -#if defined (USE_SSSE3) || defined (USE_NEON) - const in_vec_t* invec = reinterpret_cast(input); - - // Perform accumulation to registers for each big block - for (IndexType bigBlock = 0; bigBlock < NumBigBlocks; ++bigBlock) - { - acc_vec_t acc[NumOutputRegs] = { vec_zero }; - - // Each big block has NumOutputRegs small blocks in each "row", one per register. - // We process two small blocks at a time to save on one addition without VNNI. - for (IndexType smallBlock = 0; smallBlock < NumSmallBlocksPerOutput; smallBlock += 2) - { - const weight_vec_t* weightvec = - reinterpret_cast( - weights - + bigBlock * BigBlockSize - + smallBlock * SmallBlockSize * NumOutputRegs); - - const in_vec_t in0 = invec[smallBlock + 0]; - const in_vec_t in1 = invec[smallBlock + 1]; - - for (IndexType k = 0; k < NumOutputRegs; ++k) - vec_add_dpbusd_32x2(acc[k], in0, weightvec[k], in1, weightvec[k + NumOutputRegs]); - } - - // Horizontally add all accumulators. - if constexpr (NumOutputRegs % 4 == 0) - { - bias_vec_t* outputvec = reinterpret_cast(output); - const bias_vec_t* biasvec = reinterpret_cast(biases); - - for (IndexType k = 0; k < NumOutputRegs; k += 4) - { - const IndexType idx = (bigBlock * NumOutputRegs + k) / 4; - outputvec[idx] = vec_haddx4(acc[k+0], acc[k+1], acc[k+2], acc[k+3], biasvec[idx]); - } - } - else - { - for (IndexType k = 0; k < NumOutputRegs; ++k) - { - const IndexType idx = (bigBlock * NumOutputRegs + k); - output[idx] = vec_hadd(acc[k], biases[idx]); - } - } - } - -# undef vec_zero -# undef vec_add_dpbusd_32x2 -# undef vec_hadd -# undef vec_haddx4 -#else - // Use old implementation for the other architectures. - affine_transform_non_ssse3< - InputDimensions, - PaddedInputDimensions, - OutputDimensions>(output, weights, biases, input); - -#endif - - return output; - } - - private: - using BiasType = OutputType; - using WeightType = std::int8_t; - - alignas(CacheLineSize) BiasType biases[OutputDimensions]; - alignas(CacheLineSize) WeightType weights[OutputDimensions * PaddedInputDimensions]; - }; - - // A specialization for small inputs - template - class AffineTransform(InDims, MaxSimdWidth) < LargeInputSize)>> { - public: - // Input/output type - // Input/output type - using InputType = std::uint8_t; - using OutputType = std::int32_t; - - // Number of input/output dimensions - static constexpr IndexType InputDimensions = InDims; - static constexpr IndexType OutputDimensions = OutDims; - - static constexpr IndexType PaddedInputDimensions = - ceil_to_multiple(InputDimensions, MaxSimdWidth); - static constexpr IndexType PaddedOutputDimensions = - ceil_to_multiple(OutputDimensions, MaxSimdWidth); - - using OutputBuffer = OutputType[PaddedOutputDimensions]; - - static_assert(PaddedInputDimensions < LargeInputSize, "Something went wrong. This specialization (for small inputs) should not have been chosen."); - // Hash value embedded in the evaluation file static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) { std::uint32_t hashValue = 0xCC03DAE4u; From e89469925d9afd060f85d4047edbaad8903e67ef Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 15 Jul 2023 11:59:27 +0200 Subject: [PATCH 1131/1766] Remove some CI parts not yet working downgrading compiler didn't work for windows build. Stick to gcc 13 for now. Windows x86-32 not a 32bit binary, remove. closes https://github.com/official-stockfish/Stockfish/pull/4685 No functional change --- .github/workflows/stockfish_binaries.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/stockfish_binaries.yml b/.github/workflows/stockfish_binaries.yml index e761c845396..f856d403af4 100644 --- a/.github/workflows/stockfish_binaries.yml +++ b/.github/workflows/stockfish_binaries.yml @@ -54,6 +54,8 @@ jobs: exclude: - binaries: x86-32 config: { os: macos-12 } + - binaries: x86-32 + config: { os: windows-2022} - binaries: x86-64-avx2 config: { os: macos-12 } - binaries: x86-64-bmi2 @@ -96,12 +98,6 @@ jobs: msystem: ${{ matrix.config.msys_sys }} install: mingw-w64-${{ matrix.config.msys_env }} make git zip - - name: Install fixed GCC on Windows - if: runner.os == 'Windows' - run: | - wget https://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-gcc-11.3.0-2-any.pkg.tar.zst - pacman -U mingw-w64-x86_64-gcc-11.3.0-2-any.pkg.tar.zst --noconfirm - - name: Download SDE package if: runner.os == 'Linux' || runner.os == 'Windows' uses: petarpetrovt/setup-sde@v2.1 From e8a5c64988d4c0d3d6c87e3ba3204ab4cf512bcb Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 15 Jul 2023 13:34:16 +0200 Subject: [PATCH 1132/1766] Consolidate to centipawns conversion avoid doing this calculations in multiple places. closes https://github.com/official-stockfish/Stockfish/pull/4686 No functional change --- src/evaluate.cpp | 6 ++---- src/nnue/evaluate_nnue.cpp | 8 ++++---- src/uci.cpp | 9 ++++++++- src/uci.h | 1 + 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d4d8daee4da..7f0ea4bc604 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -58,8 +58,6 @@ namespace Eval { string currentEvalFileName = "None"; - static double to_cp(Value v) { return double(v) / UCI::NormalizeToPawnValue; } - /// NNUE::init() tries to load a NNUE network at startup time, or when the engine /// receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue" /// The name of the NNUE network is always retrieved from the EvalFile option. @@ -194,11 +192,11 @@ std::string Eval::trace(Position& pos) { Value v; v = NNUE::evaluate(pos, false); v = pos.side_to_move() == WHITE ? v : -v; - ss << "NNUE evaluation " << to_cp(v) << " (white side)\n"; + ss << "NNUE evaluation " << 0.01 * UCI::to_cp(v) << " (white side)\n"; v = evaluate(pos); v = pos.side_to_move() == WHITE ? v : -v; - ss << "Final evaluation " << to_cp(v) << " (white side)"; + ss << "Final evaluation " << 0.01 * UCI::to_cp(v) << " (white side)"; ss << " [with scaled NNUE, ...]"; ss << "\n"; diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index a1a90023909..d90f59a222b 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -224,7 +224,7 @@ namespace Stockfish::Eval::NNUE { buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' '); - int cp = std::abs(100 * v / UCI::NormalizeToPawnValue); + int cp = std::abs(UCI::to_cp(v)); if (cp >= 10000) { buffer[1] = '0' + cp / 10000; cp %= 10000; @@ -249,15 +249,15 @@ namespace Stockfish::Eval::NNUE { } - // format_cp_aligned_dot() converts a Value into (centi)pawns, always keeping two decimals. + // format_cp_aligned_dot() converts a Value into pawns, always keeping two decimals. static void format_cp_aligned_dot(Value v, std::stringstream &stream) { - const double cp = 1.0 * std::abs(int(v)) / UCI::NormalizeToPawnValue; + const double pawns = std::abs(0.01 * UCI::to_cp(v)); stream << (v < 0 ? '-' : v > 0 ? '+' : ' ') << std::setiosflags(std::ios::fixed) << std::setw(6) << std::setprecision(2) - << cp; + << pawns; } diff --git a/src/uci.cpp b/src/uci.cpp index ed16f24c382..f893bd9cece 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -303,6 +303,13 @@ void UCI::loop(int argc, char* argv[]) { } +/// Turns a Value to an integer centipawn number, +/// without treatment of mate and similar special scores. +int UCI::to_cp(Value v) { + + return 100 * v / UCI::NormalizeToPawnValue; +} + /// UCI::value() converts a Value to a string by adhering to the UCI protocol specification: /// /// cp The score from the engine's point of view in centipawns. @@ -316,7 +323,7 @@ string UCI::value(Value v) { stringstream ss; if (abs(v) < VALUE_TB_WIN_IN_MAX_PLY) - ss << "cp " << v * 100 / NormalizeToPawnValue; + ss << "cp " << UCI::to_cp(v); else if (abs(v) < VALUE_MATE_IN_MAX_PLY) { const int ply = VALUE_MATE_IN_MAX_PLY - 1 - std::abs(v); // recompute ss->ply diff --git a/src/uci.h b/src/uci.h index 8f1be00c7cb..2e40c9125d1 100644 --- a/src/uci.h +++ b/src/uci.h @@ -76,6 +76,7 @@ class Option { void init(OptionsMap&); void loop(int argc, char* argv[]); +int to_cp(Value v); std::string value(Value v); std::string square(Square s); std::string move(Move m, bool chess960); From 18fdc2df3c1fe0eeaae26a9235fb9bc17221e9c0 Mon Sep 17 00:00:00 2001 From: maxim Date: Thu, 13 Jul 2023 19:09:02 +0300 Subject: [PATCH 1133/1766] Remove pawnKey from StateInfo Remove pawnKey since it is not used anymore. Passed Non-regression STC: https://tests.stockfishchess.org/tests/view/64b023110cdec37b9573265c LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 334848 W: 85440 L: 85545 D: 163863 Ptnml(0-2): 1134, 38101, 89075, 37964, 1150 closes https://github.com/official-stockfish/Stockfish/pull/4687 No functional change --- src/position.cpp | 12 +----------- src/position.h | 5 ----- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index 6ecc52f8446..31cdbc06b67 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -336,7 +336,6 @@ void Position::set_check_info() const { void Position::set_state() const { st->key = st->materialKey = 0; - st->pawnKey = Zobrist::noPawns; st->nonPawnMaterial[WHITE] = st->nonPawnMaterial[BLACK] = VALUE_ZERO; st->checkersBB = attackers_to(square(sideToMove)) & pieces(~sideToMove); @@ -348,10 +347,7 @@ void Position::set_state() const { Piece pc = piece_on(s); st->key ^= Zobrist::psq[pc][s]; - if (type_of(pc) == PAWN) - st->pawnKey ^= Zobrist::psq[pc][s]; - - else if (type_of(pc) != KING) + if (type_of(pc) != KING && type_of(pc) != PAWN) st->nonPawnMaterial[color_of(pc)] += PieceValue[MG][pc]; } @@ -745,8 +741,6 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { assert(piece_on(to) == NO_PIECE); assert(piece_on(capsq) == make_piece(them, PAWN)); } - - st->pawnKey ^= Zobrist::psq[captured][capsq]; } else st->nonPawnMaterial[them] -= PieceValue[MG][captured]; @@ -825,7 +819,6 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // Update hash keys k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[promotion][to]; - st->pawnKey ^= Zobrist::psq[pc][to]; st->materialKey ^= Zobrist::psq[promotion][pieceCount[promotion]-1] ^ Zobrist::psq[pc][pieceCount[pc]]; @@ -833,9 +826,6 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { st->nonPawnMaterial[us] += PieceValue[MG][promotion]; } - // Update pawn hash key - st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; - // Reset rule 50 draw counter st->rule50 = 0; } diff --git a/src/position.h b/src/position.h index 7d4b3771912..e3917ede040 100644 --- a/src/position.h +++ b/src/position.h @@ -40,7 +40,6 @@ namespace Stockfish { struct StateInfo { // Copied when making a move - Key pawnKey; Key materialKey; Value nonPawnMaterial[COLOR_NB]; int castlingRights; @@ -338,10 +337,6 @@ inline Key Position::adjust_key50(Key k) const ? k : k ^ make_key((st->rule50 - (14 - AfterMove)) / 8); } -inline Key Position::pawn_key() const { - return st->pawnKey; -} - inline Key Position::material_key() const { return st->materialKey; } From 913574f42123d16b0b473dcd8e373e95d3103633 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 16 Jul 2023 11:52:37 +0300 Subject: [PATCH 1134/1766] Remove improvement variable No longer used in a meaningful way. Improve comments. Closes https://github.com/official-stockfish/Stockfish/pull/4688 No functional change --- src/search.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 11a52326dbe..61c75d7dd9f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -548,7 +548,7 @@ namespace { bool givesCheck, improving, priorCapture, singularQuietLMR; bool capture, moveCountPruning, ttCapture; Piece movedPiece; - int moveCount, captureCount, quietCount, improvement; + int moveCount, captureCount, quietCount; // Step 1. Initialize node Thread* thisThread = pos.this_thread(); @@ -708,7 +708,6 @@ namespace { // Skip early pruning when in check ss->staticEval = eval = VALUE_NONE; improving = false; - improvement = 0; goto moves_loop; } else if (excludedMove) @@ -745,14 +744,14 @@ namespace { thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus; } - // Set up the improvement variable, which is the difference between the current - // static evaluation and the previous static evaluation at our turn (if we were - // in check at our previous move we look at the move prior to it). The improvement - // margin and the improving flag are used in various pruning heuristics. - improvement = (ss-2)->staticEval != VALUE_NONE ? ss->staticEval - (ss-2)->staticEval - : (ss-4)->staticEval != VALUE_NONE ? ss->staticEval - (ss-4)->staticEval - : 173; - improving = improvement > 0; + // Set up the improving flag, which is true if current static evaluation is + // bigger than the previous static evaluation at our turn (if we were in + // check at our previous move we look at static evaluaion at move prior to it + // and if we were in check at move prior to it flag is set to true) and is + // false otherwise. The improving flag is used in various pruning heuristics. + improving = (ss-2)->staticEval != VALUE_NONE ? ss->staticEval > (ss-2)->staticEval + : (ss-4)->staticEval != VALUE_NONE ? ss->staticEval > (ss-4)->staticEval + : true; // Step 7. Razoring (~1 Elo). // If eval is really low check with qsearch if it can exceed alpha, if it can't, From d70a905ce3bc7372529affa8df898fc946b91282 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 16 Jul 2023 16:25:03 +0200 Subject: [PATCH 1135/1766] Deprecate the x86-64-modern arch Explicitly describe the architecture as deprecated, it remains available as its current alias x86-64-sse41-popcnt CPUs that support just this instruction set are now years old, any few years old Intel or AMD CPU supports x86-64-avx2. However, naming things 'modern' doesn't age well, so instead use explicit names. Adjust CI accordingly. Wiki, fishtest, downloader done as well. closes https://github.com/official-stockfish/Stockfish/pull/4691 No functional change. --- .github/workflows/stockfish_binaries.yml | 2 +- .github/workflows/stockfish_sanitizers.yml | 2 +- .github/workflows/stockfish_test.yml | 14 +++++++++++--- README.md | 4 ++-- src/Makefile | 12 +++++++----- 5 files changed, 22 insertions(+), 12 deletions(-) diff --git a/.github/workflows/stockfish_binaries.yml b/.github/workflows/stockfish_binaries.yml index f856d403af4..cd90507ec4e 100644 --- a/.github/workflows/stockfish_binaries.yml +++ b/.github/workflows/stockfish_binaries.yml @@ -44,7 +44,7 @@ jobs: binaries: - x86-32 - x86-64 - - x86-64-modern + - x86-64-sse41-popcnt - x86-64-avx2 - x86-64-bmi2 - x86-64-avxvnni diff --git a/.github/workflows/stockfish_sanitizers.yml b/.github/workflows/stockfish_sanitizers.yml index ebfd809c295..305b8557bfb 100644 --- a/.github/workflows/stockfish_sanitizers.yml +++ b/.github/workflows/stockfish_sanitizers.yml @@ -62,5 +62,5 @@ jobs: run: | export CXXFLAGS="-O1 -fno-inline" make clean - make -j2 ARCH=x86-64-modern ${{ matrix.sanitizers.make_option }} debug=yes optimize=no build > /dev/null + make -j2 ARCH=x86-64-sse41-popcnt ${{ matrix.sanitizers.make_option }} debug=yes optimize=no build > /dev/null ../tests/instrumented.sh --${{ matrix.sanitizers.instrumented_option }} diff --git a/.github/workflows/stockfish_test.yml b/.github/workflows/stockfish_test.yml index cd80e223853..c2ed7a4b94d 100644 --- a/.github/workflows/stockfish_test.yml +++ b/.github/workflows/stockfish_test.yml @@ -177,12 +177,12 @@ jobs: # x86-64 tests - - name: Test debug x86-64-modern build + - name: Test debug x86-64-sse41-popcnt build if: matrix.config.run_64bit_tests run: | export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" make clean - make -j2 ARCH=x86-64-modern optimize=no debug=yes build + make -j2 ARCH=x86-64-sse41-popcnt optimize=no debug=yes build ../tests/signature.sh $benchref - name: Test x86-64-bmi2 build @@ -199,6 +199,7 @@ jobs: make -j2 ARCH=x86-64-avx2 build ../tests/signature.sh $benchref + # Test a deprecated arch - name: Test x86-64-modern build if: matrix.config.run_64bit_tests run: | @@ -206,6 +207,13 @@ jobs: make -j2 ARCH=x86-64-modern build ../tests/signature.sh $benchref + - name: Test x86-64-sse41-popcnt build + if: matrix.config.run_64bit_tests + run: | + make clean + make -j2 ARCH=x86-64-sse41-popcnt build + ../tests/signature.sh $benchref + - name: Test x86-64-ssse3 build if: matrix.config.run_64bit_tests run: | @@ -271,6 +279,6 @@ jobs: if: matrix.config.run_64bit_tests run: | make clean - make -j2 ARCH=x86-64-modern build + make -j2 ARCH=x86-64-sse41-popcnt build ../tests/perft.sh ../tests/reprosearch.sh diff --git a/README.md b/README.md index 1f462d315af..e0e3da394f5 100644 --- a/README.md +++ b/README.md @@ -80,11 +80,11 @@ big-endian machines such as Power PC, and other platforms. On Unix-like systems, it should be easy to compile Stockfish directly from the source code with the included Makefile in the folder `src`. In general, it is recommended to run `make help` to see a list of make targets with corresponding -descriptions. +descriptions. An example suitable for most Intel and AMD chips: ``` cd src -make -j build ARCH=x86-64-modern +make -j profile-build ARCH=x86-64-avx2 ``` Detailed compilation instructions for all platforms can be found in our diff --git a/src/Makefile b/src/Makefile index 966ac2a7a60..f66d84d5547 100644 --- a/src/Makefile +++ b/src/Makefile @@ -104,7 +104,7 @@ VPATH = syzygy:nnue:nnue/features ### 2.1. General and architecture defaults ifeq ($(ARCH),) - ARCH = x86-64-modern + ARCH = x86-64-avx2 help_skip_sanity = yes endif # explicitly check for the list of supported architectures (as listed with make help), @@ -189,6 +189,8 @@ ifeq ($(findstring -sse41,$(ARCH)),-sse41) endif ifeq ($(findstring -modern,$(ARCH)),-modern) + $(warning *** ARCH=$(ARCH) is deprecated, defaulting to ARCH=x86-64-sse41-popcnt. Execute `make help` for a list of available architectures. ***) + $(shell sleep 5) popcnt = yes sse = yes sse2 = yes @@ -781,7 +783,7 @@ help: @echo "x86-64-bmi2 > x86 64-bit with bmi2 support" @echo "x86-64-avx2 > x86 64-bit with avx2 support" @echo "x86-64-sse41-popcnt > x86 64-bit with sse41 and popcnt support" - @echo "x86-64-modern > common modern CPU, currently x86-64-sse41-popcnt" + @echo "x86-64-modern > deprecated, currently x86-64-sse41-popcnt" @echo "x86-64-ssse3 > x86 64-bit with ssse3 support" @echo "x86-64-sse3-popcnt > x86 64-bit with sse3 and popcnt support" @echo "x86-64 > x86 64-bit generic (with sse2 support)" @@ -811,13 +813,13 @@ help: @echo "Simple examples. If you don't know what to do, you likely want to run one of: " @echo "" @echo "make -j profile-build ARCH=x86-64-avx2 # typically a fast compile for common systems " - @echo "make -j profile-build ARCH=x86-64-modern # A more portable compile for 64-bit systems " + @echo "make -j profile-build ARCH=x86-64-sse41-popcnt # A more portable compile for 64-bit systems " @echo "make -j profile-build ARCH=x86-64 # A portable compile for 64-bit systems " @echo "" @echo "Advanced examples, for experienced users: " @echo "" - @echo "make -j profile-build ARCH=x86-64-bmi2" - @echo "make -j profile-build ARCH=x86-64-bmi2 COMP=gcc COMPCXX=g++-9.0" + @echo "make -j profile-build ARCH=x86-64-avxvnni" + @echo "make -j profile-build ARCH=x86-64-avxvnni COMP=gcc COMPCXX=g++-12.0" @echo "make -j build ARCH=x86-64-ssse3 COMP=clang" @echo "" @echo "-------------------------------" From 34d0c1b5272a7af322825426e90c6fbc6b69c6b4 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 16 Jul 2023 18:14:38 +0200 Subject: [PATCH 1136/1766] Switch to macos 13 for CI allows for building x86-64-avx2 and x86-64-bmi2 binaries for mac install coreutils show hardware capabilities as seen by the compilers move some tests from sse41 to avx2 as platforms support it closes https://github.com/official-stockfish/Stockfish/pull/4692 No functional change --- .github/workflows/stockfish_binaries.yml | 36 ++++++++++++-------- .github/workflows/stockfish_compile_test.yml | 8 ++--- .github/workflows/stockfish_test.yml | 18 +++++----- 3 files changed, 35 insertions(+), 27 deletions(-) diff --git a/.github/workflows/stockfish_binaries.yml b/.github/workflows/stockfish_binaries.yml index cd90507ec4e..7c7341ef655 100644 --- a/.github/workflows/stockfish_binaries.yml +++ b/.github/workflows/stockfish_binaries.yml @@ -23,8 +23,8 @@ jobs: shell: bash archive_ext: tar sde: /home/runner/work/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.14.0-2022-10-25-lin/sde -future -- - - name: MacOS 12 Apple Clang - os: macos-12 + - name: MacOS 13 Apple Clang + os: macos-13 simple_name: macos compiler: clang++ comp: clang @@ -52,24 +52,20 @@ jobs: - x86-64-vnni256 - x86-64-vnni512 exclude: - - binaries: x86-32 - config: { os: macos-12 } + - binaries: x86-64-avxvnni + config: { ubuntu-20.04 } - binaries: x86-32 config: { os: windows-2022} - - binaries: x86-64-avx2 - config: { os: macos-12 } - - binaries: x86-64-bmi2 - config: { os: macos-12 } - - binaries: x86-64-avxvnni - config: { os: macos-12 } + - binaries: x86-32 + config: { os: macos-13 } - binaries: x86-64-avxvnni - config: { ubuntu-20.04 } + config: { os: macos-13 } - binaries: x86-64-avx512 - config: { os: macos-12 } + config: { os: macos-13 } - binaries: x86-64-vnni256 - config: { os: macos-12 } + config: { os: macos-13 } - binaries: x86-64-vnni512 - config: { os: macos-12 } + config: { os: macos-13 } defaults: run: working-directory: src @@ -85,6 +81,10 @@ jobs: sudo apt update sudo apt install g++-multilib g++-11-multilib + - name: Download required macOS packages + if: runner.os == 'macOS' + run: brew install coreutils + - name: Install fixed GCC on Linux if: runner.os == 'Linux' uses: egor-tensin/setup-gcc@v1 @@ -118,6 +118,14 @@ jobs: - name: Check compiler run: $COMPILER -v + - name: Show g++ cpu info + if: runner.os != 'macOS' + run: g++ -Q -march=native --help=target + + - name: Show clang++ cpu info + if: runner.os == 'macOS' + run: clang++ -E - -march=native -### + - name: Test help target run: make help diff --git a/.github/workflows/stockfish_compile_test.yml b/.github/workflows/stockfish_compile_test.yml index 41f61daba8a..90e01537d57 100644 --- a/.github/workflows/stockfish_compile_test.yml +++ b/.github/workflows/stockfish_compile_test.yml @@ -21,13 +21,13 @@ jobs: compiler: clang++ comp: clang shell: bash - - name: MacOS 12 Apple Clang - os: macos-12 + - name: MacOS 13 Apple Clang + os: macos-13 compiler: clang++ comp: clang shell: bash - - name: MacOS 12 GCC 11 - os: macos-12 + - name: MacOS 13 GCC 11 + os: macos-13 compiler: g++-11 comp: gcc shell: bash diff --git a/.github/workflows/stockfish_test.yml b/.github/workflows/stockfish_test.yml index c2ed7a4b94d..cb6c4c5901b 100644 --- a/.github/workflows/stockfish_test.yml +++ b/.github/workflows/stockfish_test.yml @@ -38,14 +38,14 @@ jobs: comp: ndk run_armv7_tests: true shell: bash - - name: MacOS 12 Apple Clang - os: macos-12 + - name: MacOS 13 Apple Clang + os: macos-13 compiler: clang++ comp: clang run_64bit_tests: true shell: bash - - name: MacOS 12 GCC 11 - os: macos-12 + - name: MacOS 13 GCC 11 + os: macos-13 compiler: g++-11 comp: gcc run_64bit_tests: true @@ -177,23 +177,23 @@ jobs: # x86-64 tests - - name: Test debug x86-64-sse41-popcnt build + - name: Test debug x86-64-avx2 build if: matrix.config.run_64bit_tests run: | export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" make clean - make -j2 ARCH=x86-64-sse41-popcnt optimize=no debug=yes build + make -j2 ARCH=x86-64-avx2 optimize=no debug=yes build ../tests/signature.sh $benchref - name: Test x86-64-bmi2 build - if: matrix.config.run_64bit_tests && runner.os != 'macOS' + if: matrix.config.run_64bit_tests run: | make clean make -j2 ARCH=x86-64-bmi2 build ../tests/signature.sh $benchref - name: Test x86-64-avx2 build - if: matrix.config.run_64bit_tests && runner.os != 'macOS' + if: matrix.config.run_64bit_tests run: | make clean make -j2 ARCH=x86-64-avx2 build @@ -279,6 +279,6 @@ jobs: if: matrix.config.run_64bit_tests run: | make clean - make -j2 ARCH=x86-64-sse41-popcnt build + make -j2 ARCH=x86-64-avx2 build ../tests/perft.sh ../tests/reprosearch.sh From 6a31f54d3cacf8c4dc57f301e0e3aa40d7aec435 Mon Sep 17 00:00:00 2001 From: "Robert Nurnberg @ elitebook" Date: Mon, 17 Jul 2023 18:02:22 +0200 Subject: [PATCH 1137/1766] remove evalType from bench no longer used closes https://github.com/official-stockfish/Stockfish/pull/4694 No functional change --- AUTHORS | 1 + src/benchmark.cpp | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 792893944c4..462ae5e82d5 100644 --- a/AUTHORS +++ b/AUTHORS @@ -180,6 +180,7 @@ renouve Reuven Peleg (R-Peleg) Richard Lloyd (Richard-Lloyd) rn5f107s2 +Robert Nürnberg (robertnurnberg) Rodrigo Exterckötter Tjäder Rodrigo Roim (roim) Ronald de Man (syzygy1, syzygy) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index baa90140f9b..c41092a9de8 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -121,7 +121,6 @@ vector setup_bench(const Position& current, istream& is) { string limit = (is >> token) ? token : "13"; string fenFile = (is >> token) ? token : "default"; string limitType = (is >> token) ? token : "depth"; - string evalType = (is >> token) ? token : "mixed"; go = limitType == "eval" ? "eval" : "go " + limitType + " " + limit; From 42d28424bc64246e141e9f06a2a518592272f8fd Mon Sep 17 00:00:00 2001 From: rn5f107s2 Date: Mon, 17 Jul 2023 21:41:03 +0200 Subject: [PATCH 1138/1766] Removes a few Bitboards and functions No longer used closes https://github.com/official-stockfish/Stockfish/pull/4695 No functional change --- src/bitboard.h | 74 -------------------------------------------------- src/position.h | 30 -------------------- 2 files changed, 104 deletions(-) diff --git a/src/bitboard.h b/src/bitboard.h index d21d390b1fb..bbed9177507 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -32,9 +32,6 @@ std::string pretty(Bitboard b); } // namespace Stockfish::Bitboards -constexpr Bitboard AllSquares = ~Bitboard(0); -constexpr Bitboard DarkSquares = 0xAA55AA55AA55AA55ULL; - constexpr Bitboard FileABB = 0x0101010101010101ULL; constexpr Bitboard FileBBB = FileABB << 1; constexpr Bitboard FileCBB = FileABB << 2; @@ -53,17 +50,6 @@ constexpr Bitboard Rank6BB = Rank1BB << (8 * 5); constexpr Bitboard Rank7BB = Rank1BB << (8 * 6); constexpr Bitboard Rank8BB = Rank1BB << (8 * 7); -constexpr Bitboard QueenSide = FileABB | FileBBB | FileCBB | FileDBB; -constexpr Bitboard CenterFiles = FileCBB | FileDBB | FileEBB | FileFBB; -constexpr Bitboard KingSide = FileEBB | FileFBB | FileGBB | FileHBB; -constexpr Bitboard Center = (FileDBB | FileEBB) & (Rank4BB | Rank5BB); - -constexpr Bitboard KingFlank[FILE_NB] = { - QueenSide ^ FileDBB, QueenSide, QueenSide, - CenterFiles, CenterFiles, - KingSide, KingSide, KingSide ^ FileEBB -}; - extern uint8_t PopCnt16[1 << 16]; extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; @@ -124,11 +110,6 @@ constexpr bool more_than_one(Bitboard b) { } -constexpr bool opposite_colors(Square s1, Square s2) { - return (s1 + rank_of(s1) + s2 + rank_of(s2)) & 1; -} - - /// rank_bb() and file_bb() return a bitboard representing all the squares on /// the given file or rank. @@ -177,25 +158,6 @@ inline Bitboard pawn_attacks_bb(Color c, Square s) { return PawnAttacks[c][s]; } - -/// pawn_double_attacks_bb() returns the squares doubly attacked by pawns of the -/// given color from the squares in the given bitboard. - -template -constexpr Bitboard pawn_double_attacks_bb(Bitboard b) { - return C == WHITE ? shift(b) & shift(b) - : shift(b) & shift(b); -} - - -/// adjacent_files_bb() returns a bitboard representing all the squares on the -/// adjacent files of a given square. - -constexpr Bitboard adjacent_files_bb(Square s) { - return shift(file_bb(s)) | shift(file_bb(s)); -} - - /// line_bb() returns a bitboard representing an entire line (from board edge /// to board edge) that intersects the two given squares. If the given squares /// are not on a same file/rank/diagonal, the function returns 0. For instance, @@ -234,32 +196,6 @@ constexpr Bitboard forward_ranks_bb(Color c, Square s) { : ~Rank8BB >> 8 * relative_rank(BLACK, s); } - -/// forward_file_bb() returns a bitboard representing all the squares along the -/// line in front of the given one, from the point of view of the given color. - -constexpr Bitboard forward_file_bb(Color c, Square s) { - return forward_ranks_bb(c, s) & file_bb(s); -} - - -/// pawn_attack_span() returns a bitboard representing all the squares that can -/// be attacked by a pawn of the given color when it moves along its file, starting -/// from the given square. - -constexpr Bitboard pawn_attack_span(Color c, Square s) { - return forward_ranks_bb(c, s) & adjacent_files_bb(s); -} - - -/// passed_pawn_span() returns a bitboard which can be used to test if a pawn of -/// the given color and on the given square is a passed pawn. - -constexpr Bitboard passed_pawn_span(Color c, Square s) { - return pawn_attack_span(c, s) | forward_file_bb(c, s); -} - - /// aligned() returns true if the squares s1, s2 and s3 are aligned either on a /// straight or on a diagonal line. @@ -277,8 +213,6 @@ template<> inline int distance(Square x, Square y) { return std::abs(rank_ template<> inline int distance(Square x, Square y) { return SquareDistance[x][y]; } inline int edge_distance(File f) { return std::min(f, File(FILE_H - f)); } -inline int edge_distance(Rank r) { return std::min(r, Rank(RANK_8 - r)); } - /// attacks_bb(Square) returns the pseudo attacks of the give piece type /// assuming an empty board. @@ -430,14 +364,6 @@ inline Square pop_lsb(Bitboard& b) { return s; } - -/// frontmost_sq() returns the most advanced square for the given color, -/// requires a non-zero bitboard. -inline Square frontmost_sq(Color c, Bitboard b) { - assert(b); - return c == WHITE ? msb(b) : lsb(b); -} - } // namespace Stockfish #endif // #ifndef BITBOARD_H_INCLUDED diff --git a/src/position.h b/src/position.h index e3917ede040..dc4c5837299 100644 --- a/src/position.h +++ b/src/position.h @@ -100,7 +100,6 @@ class Position { template int count(Color c) const; template int count() const; template Square square(Color c) const; - bool is_on_semiopen_file(Color c, Square s) const; // Castling CastlingRights castling_rights(Color c) const; @@ -129,11 +128,6 @@ class Position { Piece moved_piece(Move m) const; Piece captured_piece() const; - // Piece specific - bool pawn_passed(Color c, Square s) const; - bool opposite_bishops() const; - int pawns_on_same_color_squares(Color c, Square s) const; - // Doing and undoing moves void do_move(Move m, StateInfo& newSt); void do_move(Move m, StateInfo& newSt, bool givesCheck); @@ -149,7 +143,6 @@ class Position { Key key() const; Key key_after(Move m) const; Key material_key() const; - Key pawn_key() const; // Other properties of the position Color side_to_move() const; @@ -160,7 +153,6 @@ class Position { bool has_game_cycle(int ply) const; bool has_repeated() const; int rule50_count() const; - Score psq_score() const; Value psq_eg_stm() const; Value non_pawn_material(Color c) const; Value non_pawn_material() const; @@ -258,10 +250,6 @@ inline Square Position::ep_square() const { return st->epSquare; } -inline bool Position::is_on_semiopen_file(Color c, Square s) const { - return !(pieces(c, PAWN) & file_bb(s)); -} - inline bool Position::can_castle(CastlingRights cr) const { return st->castlingRights & cr; } @@ -318,14 +306,6 @@ inline Bitboard Position::check_squares(PieceType pt) const { return st->checkSquares[pt]; } -inline bool Position::pawn_passed(Color c, Square s) const { - return !(pieces(~c, PAWN) & passed_pawn_span(c, s)); -} - -inline int Position::pawns_on_same_color_squares(Color c, Square s) const { - return popcount(pieces(c, PAWN) & ((DarkSquares & s) ? DarkSquares : ~DarkSquares)); -} - inline Key Position::key() const { return adjust_key50(st->key); } @@ -341,10 +321,6 @@ inline Key Position::material_key() const { return st->materialKey; } -inline Score Position::psq_score() const { - return psq; -} - inline Value Position::psq_eg_stm() const { return (sideToMove == WHITE ? 1 : -1) * eg_value(psq); } @@ -365,12 +341,6 @@ inline int Position::rule50_count() const { return st->rule50; } -inline bool Position::opposite_bishops() const { - return count(WHITE) == 1 - && count(BLACK) == 1 - && opposite_colors(square(WHITE), square(BLACK)); -} - inline bool Position::is_chess960() const { return chess960; } From 6abd0bd9fbd8aff7dad653d33ab344d3f47f0042 Mon Sep 17 00:00:00 2001 From: Jorge <46056498+jorgectf@users.noreply.github.com> Date: Mon, 3 Jul 2023 09:17:24 +0200 Subject: [PATCH 1139/1766] Add CodeQL workflow add CI tooling to detect security bugs. closes https://github.com/official-stockfish/Stockfish/pull/4659 No functional change --- .github/workflows/codeql.yml | 53 ++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000000..863f219ca7b --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,53 @@ +name: "CodeQL" + +on: + push: + branches: [ 'master' ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ 'master' ] + schedule: + - cron: '17 18 * * 1' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'cpp' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Use only 'java' to analyze code written in Java, Kotlin or both + # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + - name: Build + working-directory: src + run: make -j build ARCH=x86-64-modern + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" From 3fe0d5c53389b3d548cf1fbbba3f6c978e9f02ae Mon Sep 17 00:00:00 2001 From: Cody Ho Date: Tue, 18 Jul 2023 13:03:26 -0700 Subject: [PATCH 1140/1766] Unused code cleanup closes https://github.com/official-stockfish/Stockfish/pull/4696 No functional change --- AUTHORS | 1 + src/bitboard.h | 10 ---------- src/misc.h | 8 -------- 3 files changed, 1 insertion(+), 18 deletions(-) diff --git a/AUTHORS b/AUTHORS index 462ae5e82d5..b345ff0b34b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -45,6 +45,7 @@ candirufish Chess13234 Chris Cain (ceebo) clefrks +Cody Ho (aesrentai) Dale Weiler (graphitemaster) Daniel Axtens (daxtens) Daniel Dugovic (ddugovic) diff --git a/src/bitboard.h b/src/bitboard.h index bbed9177507..244dc034c33 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -186,16 +186,6 @@ inline Bitboard between_bb(Square s1, Square s2) { return BetweenBB[s1][s2]; } - -/// forward_ranks_bb() returns a bitboard representing the squares on the ranks in -/// front of the given one, from the point of view of the given color. For instance, -/// forward_ranks_bb(BLACK, SQ_D3) will return the 16 squares on ranks 1 and 2. - -constexpr Bitboard forward_ranks_bb(Color c, Square s) { - return c == WHITE ? ~Rank1BB << 8 * relative_rank(WHITE, s) - : ~Rank8BB >> 8 * relative_rank(BLACK, s); -} - /// aligned() returns true if the squares s1, s2 and s3 are aligned either on a /// straight or on a diagonal line. diff --git a/src/misc.h b/src/misc.h index 69d470c22f8..0005fc0fb09 100644 --- a/src/misc.h +++ b/src/misc.h @@ -55,14 +55,6 @@ inline TimePoint now() { (std::chrono::steady_clock::now().time_since_epoch()).count(); } -template -struct HashTable { - Entry* operator[](Key key) { return &table[(uint32_t)key & (Size - 1)]; } - -private: - std::vector table = std::vector(Size); // Allocate on the heap -}; - enum SyncCout { IO_LOCK, IO_UNLOCK }; std::ostream& operator<<(std::ostream&, SyncCout); From 1444837887e5982a2f1f343312e9080248ed9ca8 Mon Sep 17 00:00:00 2001 From: mstembera Date: Tue, 18 Jul 2023 11:50:34 -0700 Subject: [PATCH 1141/1766] Remove inline assembly closes https://github.com/official-stockfish/Stockfish/pull/4698 No functional change --- src/nnue/layers/simd.h | 118 ----------------------------------------- 1 file changed, 118 deletions(-) diff --git a/src/nnue/layers/simd.h b/src/nnue/layers/simd.h index 22c51980ecc..fae31a62955 100644 --- a/src/nnue/layers/simd.h +++ b/src/nnue/layers/simd.h @@ -38,21 +38,6 @@ # include #endif -// The inline asm is only safe for GCC, where it is necessary to get good codegen. -// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101693 -// Clang does fine without it. -// Play around here: https://godbolt.org/z/7EWqrYq51 -#if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) -#define USE_INLINE_ASM -#endif - -// Use either the AVX512 or AVX-VNNI version of the VNNI instructions. -#if defined(USE_AVXVNNI) -#define VNNI_PREFIX "%{vex%} " -#else -#define VNNI_PREFIX "" -#endif - namespace Stockfish::Simd { #if defined (USE_AVX512) @@ -117,29 +102,11 @@ namespace Stockfish::Simd { __m512i b) { # if defined (USE_VNNI) -# if defined (USE_INLINE_ASM) - asm( - "vpdpbusd %[b], %[a], %[acc]\n\t" - : [acc]"+v"(acc) - : [a]"v"(a), [b]"vm"(b) - ); -# else acc = _mm512_dpbusd_epi32(acc, a, b); -# endif # else -# if defined (USE_INLINE_ASM) - __m512i tmp = _mm512_maddubs_epi16(a, b); - asm( - "vpmaddwd %[tmp], %[ones], %[tmp]\n\t" - "vpaddd %[acc], %[tmp], %[acc]\n\t" - : [acc]"+v"(acc), [tmp]"+&v"(tmp) - : [ones]"v"(_mm512_set1_epi16(1)) - ); -# else __m512i product0 = _mm512_maddubs_epi16(a, b); product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1)); acc = _mm512_add_epi32(acc, product0); -# endif # endif } @@ -149,36 +116,14 @@ namespace Stockfish::Simd { __m512i a1, __m512i b1) { # if defined (USE_VNNI) -# if defined (USE_INLINE_ASM) - asm( - "vpdpbusd %[b0], %[a0], %[acc]\n\t" - "vpdpbusd %[b1], %[a1], %[acc]\n\t" - : [acc]"+&v"(acc) - : [a0]"v"(a0), [b0]"vm"(b0), [a1]"v"(a1), [b1]"vm"(b1) - ); -# else acc = _mm512_dpbusd_epi32(acc, a0, b0); acc = _mm512_dpbusd_epi32(acc, a1, b1); -# endif # else -# if defined (USE_INLINE_ASM) - __m512i tmp0 = _mm512_maddubs_epi16(a0, b0); - __m512i tmp1 = _mm512_maddubs_epi16(a1, b1); - asm( - "vpmaddwd %[tmp0], %[ones], %[tmp0]\n\t" - "vpmaddwd %[tmp1], %[ones], %[tmp1]\n\t" - "vpaddd %[tmp0], %[tmp1], %[tmp0]\n\t" - "vpaddd %[acc], %[tmp0], %[acc]\n\t" - : [acc]"+v"(acc), [tmp0]"+&v"(tmp0), [tmp1]"+&v"(tmp1) - : [ones]"v"(_mm512_set1_epi16(1)) - ); -# else __m512i product0 = _mm512_maddubs_epi16(a0, b0); __m512i product1 = _mm512_maddubs_epi16(a1, b1); product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1)); product1 = _mm512_madd_epi16(product1, _mm512_set1_epi16(1)); acc = _mm512_add_epi32(acc, _mm512_add_epi32(product0, product1)); -# endif # endif } @@ -214,29 +159,11 @@ namespace Stockfish::Simd { __m256i b) { # if defined (USE_VNNI) -# if defined (USE_INLINE_ASM) - asm( - VNNI_PREFIX "vpdpbusd %[b], %[a], %[acc]\n\t" - : [acc]"+v"(acc) - : [a]"v"(a), [b]"vm"(b) - ); -# else acc = _mm256_dpbusd_epi32(acc, a, b); -# endif # else -# if defined (USE_INLINE_ASM) - __m256i tmp = _mm256_maddubs_epi16(a, b); - asm( - "vpmaddwd %[tmp], %[ones], %[tmp]\n\t" - "vpaddd %[acc], %[tmp], %[acc]\n\t" - : [acc]"+v"(acc), [tmp]"+&v"(tmp) - : [ones]"v"(_mm256_set1_epi16(1)) - ); -# else __m256i product0 = _mm256_maddubs_epi16(a, b); product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1)); acc = _mm256_add_epi32(acc, product0); -# endif # endif } @@ -246,36 +173,14 @@ namespace Stockfish::Simd { __m256i a1, __m256i b1) { # if defined (USE_VNNI) -# if defined (USE_INLINE_ASM) - asm( - VNNI_PREFIX "vpdpbusd %[b0], %[a0], %[acc]\n\t" - VNNI_PREFIX "vpdpbusd %[b1], %[a1], %[acc]\n\t" - : [acc]"+&v"(acc) - : [a0]"v"(a0), [b0]"vm"(b0), [a1]"v"(a1), [b1]"vm"(b1) - ); -# else acc = _mm256_dpbusd_epi32(acc, a0, b0); acc = _mm256_dpbusd_epi32(acc, a1, b1); -# endif # else -# if defined (USE_INLINE_ASM) - __m256i tmp0 = _mm256_maddubs_epi16(a0, b0); - __m256i tmp1 = _mm256_maddubs_epi16(a1, b1); - asm( - "vpmaddwd %[tmp0], %[ones], %[tmp0]\n\t" - "vpmaddwd %[tmp1], %[ones], %[tmp1]\n\t" - "vpaddd %[tmp0], %[tmp1], %[tmp0]\n\t" - "vpaddd %[acc], %[tmp0], %[acc]\n\t" - : [acc]"+v"(acc), [tmp0]"+&v"(tmp0), [tmp1]"+&v"(tmp1) - : [ones]"v"(_mm256_set1_epi16(1)) - ); -# else __m256i product0 = _mm256_maddubs_epi16(a0, b0); __m256i product1 = _mm256_maddubs_epi16(a1, b1); product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1)); product1 = _mm256_madd_epi16(product1, _mm256_set1_epi16(1)); acc = _mm256_add_epi32(acc, _mm256_add_epi32(product0, product1)); -# endif # endif } @@ -304,19 +209,9 @@ namespace Stockfish::Simd { __m128i a, __m128i b) { -# if defined (USE_INLINE_ASM) - __m128i tmp = _mm_maddubs_epi16(a, b); - asm( - "pmaddwd %[ones], %[tmp]\n\t" - "paddd %[tmp], %[acc]\n\t" - : [acc]"+v"(acc), [tmp]"+&v"(tmp) - : [ones]"v"(_mm_set1_epi16(1)) - ); -# else __m128i product0 = _mm_maddubs_epi16(a, b); product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1)); acc = _mm_add_epi32(acc, product0); -# endif } [[maybe_unused]] static void m128_add_dpbusd_epi32x2( @@ -324,24 +219,11 @@ namespace Stockfish::Simd { __m128i a0, __m128i b0, __m128i a1, __m128i b1) { -# if defined (USE_INLINE_ASM) - __m128i tmp0 = _mm_maddubs_epi16(a0, b0); - __m128i tmp1 = _mm_maddubs_epi16(a1, b1); - asm( - "pmaddwd %[ones], %[tmp0]\n\t" - "pmaddwd %[ones], %[tmp1]\n\t" - "paddd %[tmp1], %[tmp0]\n\t" - "paddd %[tmp0], %[acc]\n\t" - : [acc]"+v"(acc), [tmp0]"+&v"(tmp0), [tmp1]"+&v"(tmp1) - : [ones]"v"(_mm_set1_epi16(1)) - ); -# else __m128i product0 = _mm_maddubs_epi16(a0, b0); __m128i product1 = _mm_maddubs_epi16(a1, b1); product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1)); product1 = _mm_madd_epi16(product1, _mm_set1_epi16(1)); acc = _mm_add_epi32(acc, _mm_add_epi32(product0, product1)); -# endif } #endif From 5ea1cbc778508a9a7b720becaf22dd96a4472826 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Wed, 19 Jul 2023 11:58:02 +0300 Subject: [PATCH 1142/1766] Do more futility pruning for cutNodes that are not in TT This is somewhat similar to IIR for cutnodes but instead of reducing depth for cutnodes that don't have tt move we reduce margin multiplier in futility pruning for cutnodes that are not in TT. Passed STC: https://tests.stockfishchess.org/tests/view/64b244b90cdec37b95734c5b LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 75552 W: 19404 L: 19029 D: 37119 Ptnml(0-2): 233, 8806, 19378, 9071, 288 Passed LTC: https://tests.stockfishchess.org/tests/view/64b3ae5a0cdec37b95736516 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 104988 W: 27152 L: 26697 D: 51139 Ptnml(0-2): 41, 11259, 29446, 11700, 48 closes https://github.com/official-stockfish/Stockfish/pull/4700 bench 1727577 --- src/search.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 61c75d7dd9f..8fdaca7cff0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -63,8 +63,8 @@ namespace { enum NodeType { NonPV, PV, Root }; // Futility margin - Value futility_margin(Depth d, bool improving) { - return Value(140 * (d - improving)); + Value futility_margin(Depth d, bool noTtCutNode, bool improving) { + return Value((140 - 40 * noTtCutNode) * (d - improving)); } // Reductions lookup table initialized at startup @@ -767,7 +767,7 @@ namespace { // The depth condition is important for mate finding. if ( !ss->ttPv && depth < 9 - && eval - futility_margin(depth, improving) - (ss-1)->statScore / 306 >= beta + && eval - futility_margin(depth, cutNode && !ss->ttHit, improving) - (ss-1)->statScore / 306 >= beta && eval >= beta && eval < 24923) // larger than VALUE_KNOWN_WIN, but smaller than TB wins return eval; From 4b2979760f3862700c6a0b8d3ab0f6a6e0a638c0 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 22 Jul 2023 09:41:55 +0200 Subject: [PATCH 1143/1766] Check clock more often This patch changes the frequency with which the time is checked, changing frequency from every 1024 counted nodes to every 512 counted nodes. The master value was tuned for the old classical eval, the patch takes the roughly 2x slowdown in nps with SFNNUEv7 into account. This could reduce a bit the losses on time on fishtest, but they are probably unrelated. passed STC: https://tests.stockfishchess.org/tests/view/64bb8ae5dc56e1650abb1b11 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 76576 W: 19677 L: 19501 D: 37398 Ptnml(0-2): 274, 8592, 20396, 8736, 290 closes https://github.com/official-stockfish/Stockfish/pull/4704 No functional change --- src/search.cpp | 4 ++-- src/timeman.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 8fdaca7cff0..db9a5a8d01d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1828,7 +1828,7 @@ void MainThread::check_time() { return; // When using nodes, ensure checking rate is not lower than 0.1% of nodes - callsCnt = Limits.nodes ? std::min(1024, int(Limits.nodes / 1024)) : 1024; + callsCnt = Limits.nodes ? std::min(512, int(Limits.nodes / 1024)) : 512; static TimePoint lastInfoTime = now(); @@ -1845,7 +1845,7 @@ void MainThread::check_time() { if (ponder) return; - if ( (Limits.use_time_management() && (elapsed > Time.maximum() - 10 || stopOnPonderhit)) + if ( (Limits.use_time_management() && (elapsed > Time.maximum() || stopOnPonderhit)) || (Limits.movetime && elapsed >= Limits.movetime) || (Limits.nodes && Threads.nodes_searched() >= (uint64_t)Limits.nodes)) Threads.stop = true; diff --git a/src/timeman.cpp b/src/timeman.cpp index 061de0182f7..169c7821c94 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -100,7 +100,7 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { // Never use more than 80% of the available time for this move optimumTime = TimePoint(optScale * timeLeft); - maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, maxScale * optimumTime)); + maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, maxScale * optimumTime)) - 10; if (Options["Ponder"]) optimumTime += optimumTime / 4; From 78e3d2ad78f1835d627ec40b9228e8b7dbb676ef Mon Sep 17 00:00:00 2001 From: windfishballad Date: Sat, 22 Jul 2023 20:35:40 -0400 Subject: [PATCH 1144/1766] Simplify some qsearch conditions Use the assert which ensures that beta == alpha+1 at PVNodes to simplify a little bit the conditions further down in the code. passed STC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 56160 W: 14370 L: 14173 D: 27617 Ptnml(0-2): 210, 6192, 15076, 6395, 207 https://tests.stockfishchess.org/tests/view/64bc769cdc56e1650abb2e26 closes https://tests.stockfishchess.org/tests/view/64bc769cdc56e1650abb2e26 No functional change --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index db9a5a8d01d..616d11336ee 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1496,7 +1496,7 @@ namespace { return bestValue; } - if (PvNode && bestValue > alpha) + if (bestValue > alpha) alpha = bestValue; futilityBase = bestValue + 200; @@ -1608,7 +1608,7 @@ namespace { if (PvNode) // Update pv even in fail-high case update_pv(ss->pv, move, (ss+1)->pv); - if (PvNode && value < beta) // Update alpha here! + if (value < beta) // Update alpha here! alpha = value; else break; // Fail high From 76e1e8fd39a08c0586259a76c880c3267cb85f62 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Thu, 20 Jul 2023 22:12:15 +0200 Subject: [PATCH 1145/1766] Simplify TT cutoff Remove the exact bound condition from TT depth check. STC: https://tests.stockfishchess.org/tests/view/64b30b320cdec37b957359e9 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 332928 W: 84895 L: 85003 D: 163030 Ptnml(0-2): 1242, 39200, 85604, 39260, 1158 LTC: https://tests.stockfishchess.org/tests/view/64b74e2adc56e1650abac0b6 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 92946 W: 23628 L: 23482 D: 45836 Ptnml(0-2): 38, 10033, 26192, 10165, 45 closes https://github.com/official-stockfish/Stockfish/pull/4702 Bench: 1601764 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 616d11336ee..c2d35796231 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -616,7 +616,7 @@ namespace { // At non-PV nodes we check for an early TT cutoff if ( !PvNode && !excludedMove - && tte->depth() > depth - (tte->bound() == BOUND_EXACT) + && tte->depth() > depth && ttValue != VALUE_NONE // Possible in case of TT access race or if !ttHit && (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER))) { From 027713c4b4738446818aedfbfb480e962b624e31 Mon Sep 17 00:00:00 2001 From: Stephen Touset Date: Tue, 25 Jul 2023 00:06:14 +0200 Subject: [PATCH 1146/1766] Remove Zobrist::noPawns Zobrist::noPawns is no longer used. closes https://github.com/official-stockfish/Stockfish/pull/4344 no functional change --- AUTHORS | 1 + src/position.cpp | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index b345ff0b34b..2f323d64334 100644 --- a/AUTHORS +++ b/AUTHORS @@ -205,6 +205,7 @@ Stefano Cardanobile (Stefano80) Stefano Di Martino (StefanoD) Steinar Gunderson (sesse) Stéphane Nicolet (snicolet) +Stephen Touset (stouset) Syine Mineta (MinetaS) Thanar2 thaspel diff --git a/src/position.cpp b/src/position.cpp index 31cdbc06b67..16181e96249 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -42,7 +42,7 @@ namespace Zobrist { Key psq[PIECE_NB][SQUARE_NB]; Key enpassant[FILE_NB]; Key castling[CASTLING_RIGHT_NB]; - Key side, noPawns; + Key side; } namespace { @@ -125,7 +125,6 @@ void Position::init() { Zobrist::castling[cr] = rng.rand(); Zobrist::side = rng.rand(); - Zobrist::noPawns = rng.rand(); // Prepare the cuckoo tables std::memset(cuckoo, 0, sizeof(cuckoo)); From 2667316ffcf1b3396e42be3d5cb6bcbdcc98c216 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Tue, 25 Jul 2023 13:55:29 +0300 Subject: [PATCH 1147/1766] Simplify one multicut extension Simplify away the ttValue <= alpha extension in the multicut block. Passed STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 318336 W: 81307 L: 81398 D: 155631 Ptnml(0-2): 1088, 37291, 82469, 37264, 1056 https://tests.stockfishchess.org/tests/view/64b8589fdc56e1650abad61d Passed LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 89388 W: 22925 L: 22775 D: 43688 Ptnml(0-2): 34, 9635, 25210, 9777, 38 https://tests.stockfishchess.org/tests/view/64bc41d0dc56e1650abb29cb closes https://github.com/official-stockfish/Stockfish/pull/4709 bench: 1604592 --- src/search.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c2d35796231..96b29d1e8ed 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1104,10 +1104,6 @@ namespace { // If the eval of ttMove is less than value, we reduce it (negative extension) (~1 Elo) else if (ttValue <= value) extension = -1; - - // If the eval of ttMove is less than alpha, we reduce it (negative extension) (~1 Elo) - else if (ttValue <= alpha) - extension = -1; } // Check extensions (~1 Elo) From cb22520a9c7e1d716a56f08390aa638a23a597b2 Mon Sep 17 00:00:00 2001 From: mstembera Date: Mon, 24 Jul 2023 19:02:49 -0700 Subject: [PATCH 1148/1766] Remove unused return type from propagate() Also make two get_weight_index() static methods constexpr, for consistency with the other static get_hash_value() method right above. Tested for speed by user Torom (thanks). closes https://github.com/official-stockfish/Stockfish/pull/4708 No functional change --- src/nnue/layers/affine_transform.h | 8 +++----- src/nnue/layers/affine_transform_sparse_input.h | 9 +++------ src/nnue/layers/clipped_relu.h | 4 +--- src/nnue/layers/sqr_clipped_relu.h | 4 +--- 4 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index b0169306cb7..c936a83ed66 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -171,7 +171,7 @@ namespace Stockfish::Eval::NNUE::Layers { return hashValue; } - static IndexType get_weight_index_scrambled(IndexType i) + static constexpr IndexType get_weight_index_scrambled(IndexType i) { return (i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 + @@ -179,7 +179,7 @@ namespace Stockfish::Eval::NNUE::Layers { i % 4; } - static IndexType get_weight_index(IndexType i) + static constexpr IndexType get_weight_index(IndexType i) { #if defined (USE_SSSE3) return get_weight_index_scrambled(i); @@ -207,7 +207,7 @@ namespace Stockfish::Eval::NNUE::Layers { return !stream.fail(); } // Forward propagation - const OutputType* propagate( + void propagate( const InputType* input, OutputType* output) const { #if defined (USE_AVX512) @@ -291,8 +291,6 @@ namespace Stockfish::Eval::NNUE::Layers { PaddedInputDimensions, OutputDimensions>(output, weights, biases, input); #endif - - return output; } private: diff --git a/src/nnue/layers/affine_transform_sparse_input.h b/src/nnue/layers/affine_transform_sparse_input.h index a5bea08e74b..134b7d13e19 100644 --- a/src/nnue/layers/affine_transform_sparse_input.h +++ b/src/nnue/layers/affine_transform_sparse_input.h @@ -102,7 +102,6 @@ namespace Stockfish::Eval::NNUE::Layers { template class AffineTransformSparseInput { public: - // Input/output type // Input/output type using InputType = std::uint8_t; using OutputType = std::int32_t; @@ -135,7 +134,7 @@ namespace Stockfish::Eval::NNUE::Layers { return hashValue; } - static IndexType get_weight_index_scrambled(IndexType i) + static constexpr IndexType get_weight_index_scrambled(IndexType i) { return (i / ChunkSize) % (PaddedInputDimensions / ChunkSize) * OutputDimensions * ChunkSize + @@ -143,7 +142,7 @@ namespace Stockfish::Eval::NNUE::Layers { i % ChunkSize; } - static IndexType get_weight_index(IndexType i) + static constexpr IndexType get_weight_index(IndexType i) { #if defined (USE_SSSE3) return get_weight_index_scrambled(i); @@ -171,7 +170,7 @@ namespace Stockfish::Eval::NNUE::Layers { return !stream.fail(); } // Forward propagation - const OutputType* propagate( + void propagate( const InputType* input, OutputType* output) const { #if defined (USE_SSSE3) @@ -230,8 +229,6 @@ namespace Stockfish::Eval::NNUE::Layers { PaddedInputDimensions, OutputDimensions>(output, weights, biases, input); #endif - - return output; } private: diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index 51e562dad34..d5aa6fbfbd1 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -59,7 +59,7 @@ namespace Stockfish::Eval::NNUE::Layers { } // Forward propagation - const OutputType* propagate( + void propagate( const InputType* input, OutputType* output) const { #if defined(USE_AVX2) @@ -170,8 +170,6 @@ namespace Stockfish::Eval::NNUE::Layers { output[i] = static_cast( std::max(0, std::min(127, input[i] >> WeightScaleBits))); } - - return output; } }; diff --git a/src/nnue/layers/sqr_clipped_relu.h b/src/nnue/layers/sqr_clipped_relu.h index 3fbb243cfd6..69bd51471d7 100644 --- a/src/nnue/layers/sqr_clipped_relu.h +++ b/src/nnue/layers/sqr_clipped_relu.h @@ -59,7 +59,7 @@ namespace Stockfish::Eval::NNUE::Layers { } // Forward propagation - const OutputType* propagate( + void propagate( const InputType* input, OutputType* output) const { #if defined(USE_SSE2) @@ -110,8 +110,6 @@ namespace Stockfish::Eval::NNUE::Layers { // needs to be accounted for in the trainer std::max(0ll, std::min(127ll, (((long long)input[i] * input[i]) >> (2 * WeightScaleBits)) / 128))); } - - return output; } }; From f84eb1f3ef9dc4078368b849f8deb55982882390 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Fri, 28 Jul 2023 23:38:37 +0200 Subject: [PATCH 1149/1766] Improve some comments - clarify the examples for the bench command - typo in search.cpp closes https://github.com/official-stockfish/Stockfish/pull/4710 No functional change --- src/benchmark.cpp | 15 +++++++-------- src/nnue/evaluate_nnue.cpp | 6 +++--- src/search.cpp | 4 ++-- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index c41092a9de8..e340ebcd309 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -100,15 +100,14 @@ namespace Stockfish { /// setup_bench() builds a list of UCI commands to be run by bench. There /// are five parameters: TT size in MB, number of search threads that /// should be used, the limit value spent for each position, a file name -/// where to look for positions in FEN format, the type of the limit: -/// depth, perft, nodes and movetime (in millisecs), and evaluation type -/// mixed (default), classical, NNUE. +/// where to look for positions in FEN format, and the type of the limit: +/// depth, perft, nodes and movetime (in milliseconds). Examples: /// -/// bench -> search default positions up to depth 13 -/// bench 64 1 15 -> search default positions up to depth 15 (TT = 64MB) -/// bench 64 4 5000 current movetime -> search current position with 4 threads for 5 sec -/// bench 64 1 100000 default nodes -> search default positions for 100K nodes each -/// bench 16 1 5 default perft -> run a perft 5 on default positions +/// bench : search default positions up to depth 13 +/// bench 64 1 15 : search default positions up to depth 15 (TT = 64MB) +/// bench 64 1 100000 default nodes : search default positions for 100K nodes each +/// bench 64 4 5000 current movetime : search current position with 4 threads for 5 sec +/// bench 16 1 5 blah perft : run a perft 5 on positions in file "blah" vector setup_bench(const Position& current, istream& is) { diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index d90f59a222b..cff1d0243db 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -82,6 +82,7 @@ namespace Stockfish::Eval::NNUE { } // namespace Detail + // Initialize the evaluation function parameters static void initialize() { @@ -187,7 +188,6 @@ namespace Stockfish::Eval::NNUE { // We manually align the arrays on the stack because with gcc < 9.3 // overaligning stack variables with alignas() doesn't work correctly. - constexpr uint64_t alignment = CacheLineSize; #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) @@ -249,8 +249,9 @@ namespace Stockfish::Eval::NNUE { } - // format_cp_aligned_dot() converts a Value into pawns, always keeping two decimals. + // format_cp_aligned_dot() converts a Value into pawns, always keeping two decimals static void format_cp_aligned_dot(Value v, std::stringstream &stream) { + const double pawns = std::abs(0.01 * UCI::to_cp(v)); stream << (v < 0 ? '-' : v > 0 ? '+' : ' ') @@ -263,7 +264,6 @@ namespace Stockfish::Eval::NNUE { // trace() returns a string with the value of each piece on a board, // and a table for (PSQT, Layers) values bucket by bucket. - std::string trace(Position& pos) { std::stringstream ss; diff --git a/src/search.cpp b/src/search.cpp index 96b29d1e8ed..45758031669 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -745,8 +745,8 @@ namespace { } // Set up the improving flag, which is true if current static evaluation is - // bigger than the previous static evaluation at our turn (if we were in - // check at our previous move we look at static evaluaion at move prior to it + // bigger than the previous static evaluation at our turn (if we were in + // check at our previous move we look at static evaluation at move prior to it // and if we were in check at move prior to it flag is set to true) and is // false otherwise. The improving flag is used in various pruning heuristics. improving = (ss-2)->staticEval != VALUE_NONE ? ss->staticEval > (ss-2)->staticEval From 65ece7d985291cc787d6c804a33f1dd82b75736d Mon Sep 17 00:00:00 2001 From: rn5f107s2 Date: Wed, 26 Jul 2023 14:31:16 +0200 Subject: [PATCH 1150/1766] Malus during move ordering for putting pieces en prise The original idea is the reverse of a previous patch [1] which added bonuses in our move picker to moves escaping threats. In this patch, in addition to bonuses for evading threats, we apply penalties to moves moving to threatened squares. Further tweaks of that basic idea resulted in this specific version which further increases the penalty of moves moving to squares threatend depending on the piece threatening it. So for example a queen moving to a square attacked by a pawn would receive a larger penalty than a queen moving to square attacked by a rook. [1]: https://github.com/official-stockfish/Stockfish/commit/08e0f52b77edb929989c68c49e954b9bc5d7d67e -------- Passed STC: https://tests.stockfishchess.org/tests/live_elo/64c11269dc56e1650abb935d LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 95552 W: 24654 L: 24250 D: 46648 Ptnml(0-2): 322, 11098, 24562, 11442, 352 Passed LTC: https://tests.stockfishchess.org/tests/live_elo/64c2004ddc56e1650abba8b3 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 190230 W: 48806 L: 48178 D: 93246 Ptnml(0-2): 90, 20439, 53453, 21019, 114 ------- closes https://github.com/official-stockfish/Stockfish/pull/4711 Bench: 1350831 --- src/movepick.cpp | 50 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 6fbcb2c3d2f..4050810338f 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -123,21 +123,45 @@ void MovePicker::score() { for (auto& m : *this) if constexpr (Type == CAPTURES) m.value = (7 * int(PieceValue[MG][pos.piece_on(to_sq(m))]) - + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]) / 16; + + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]) / 16; else if constexpr (Type == QUIETS) - m.value = 2 * (*mainHistory)[pos.side_to_move()][from_to(m)] - + 2 * (*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)] - + (threatenedPieces & from_sq(m) ? - (type_of(pos.moved_piece(m)) == QUEEN && !(to_sq(m) & threatenedByRook) ? 50000 - : type_of(pos.moved_piece(m)) == ROOK && !(to_sq(m) & threatenedByMinor) ? 25000 - : !(to_sq(m) & threatenedByPawn) ? 15000 - : 0) - : 0) - + bool(pos.check_squares(type_of(pos.moved_piece(m))) & to_sq(m)) * 16384; + { + Piece pc = pos.moved_piece(m); + PieceType pt = type_of(pos.moved_piece(m)); + Square from = from_sq(m); + Square to = to_sq(m); + + // histories + m.value = 2 * (*mainHistory)[pos.side_to_move()][from_to(m)]; + m.value += 2 * (*continuationHistory[0])[pc][to]; + m.value += (*continuationHistory[1])[pc][to]; + m.value += (*continuationHistory[3])[pc][to]; + m.value += (*continuationHistory[5])[pc][to]; + + // bonus for checks + m.value += bool(pos.check_squares(pt) & to) * 16384; + + // bonus for escaping from capture + m.value += threatenedPieces & from ? + (pt == QUEEN && !(to & threatenedByRook) ? 50000 + : pt == ROOK && !(to & threatenedByMinor) ? 25000 + : !(to & threatenedByPawn) ? 15000 + : 0 ) + : 0 ; + + // malus for putting piece en prise + m.value -= !(threatenedPieces & from) ? + (pt == QUEEN ? bool(to & threatenedByRook) * 50000 + + bool(to & threatenedByMinor) * 10000 + + bool(to & threatenedByPawn) * 20000 + : pt == ROOK ? bool(to & threatenedByMinor) * 25000 + + bool(to & threatenedByPawn) * 10000 + : pt != PAWN ? bool(to & threatenedByPawn) * 15000 + : 0 ) + : 0 ; + } + else // Type == EVASIONS { if (pos.capture_stage(m)) From 002a50457ce1975bf049b7bac904f23d0eab4d3b Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sat, 29 Jul 2023 14:34:58 +0000 Subject: [PATCH 1151/1766] Identify NEON_DOTPROD in compiler_info() closes https://github.com/official-stockfish/Stockfish/pull/4712 No functional change --- src/misc.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index f1554060d5e..29ef757e938 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -280,7 +280,9 @@ std::string compiler_info() { #if defined(USE_MMX) compiler += " MMX"; #endif - #if defined(USE_NEON) + #if defined(USE_NEON_DOTPROD) + compiler += " NEON_DOTPROD"; + #elif defined(USE_NEON) compiler += " NEON"; #endif From 4c43e1e27ce990735fb0226e35248fc82ea6a519 Mon Sep 17 00:00:00 2001 From: ppigazzini Date: Mon, 31 Jul 2023 13:41:28 +0200 Subject: [PATCH 1152/1766] Add new CPU archs in CI Tests workflow Add CPU archs: armv8-dotprod, riscv64 and ppc64le. The last two archs are built using QEMU multiarch docker container. References: https://docs.docker.com/build/building/multi-platform/ https://github.com/docker/setup-buildx-action https://github.com/docker/setup-qemu-action https://github.com/tonistiigi/binfmt https://stackoverflow.com/questions/72444103/what-does-running-the-multiarch-qemu-user-static-does-before-building-a-containe closes https://github.com/official-stockfish/Stockfish/pull/4718 No functional change --- .github/workflows/stockfish_test.yml | 63 +++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/.github/workflows/stockfish_test.yml b/.github/workflows/stockfish_test.yml index cb6c4c5901b..307d3a02b30 100644 --- a/.github/workflows/stockfish_test.yml +++ b/.github/workflows/stockfish_test.yml @@ -38,6 +38,22 @@ jobs: comp: ndk run_armv7_tests: true shell: bash + - name: Linux GCC riscv64 + os: ubuntu-22.04 + compiler: g++ + comp: gcc + run_riscv64_tests: true + base_image: 'riscv64/alpine:edge' + platform: linux/riscv64 + shell: bash + - name: Linux GCC ppc64 + os: ubuntu-22.04 + compiler: g++ + comp: gcc + run_ppc64_tests: true + base_image: 'ppc64le/alpine:latest' + platform: linux/ppc64le + shell: bash - name: MacOS 13 Apple Clang os: macos-13 compiler: clang++ @@ -87,7 +103,7 @@ jobs: if: runner.os == 'Linux' run: | sudo apt update - sudo apt install expect valgrind g++-multilib qemu-user + sudo apt install expect valgrind g++-multilib qemu-user-static - name: Install NDK if: runner.os == 'Linux' @@ -103,6 +119,24 @@ jobs: echo "ANDROID_NDK_BIN=$ANDROID_NDK_BIN" >> $GITHUB_ENV fi + - name: Set up QEMU + if: matrix.config.base_image + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + if: matrix.config.base_image + uses: docker/setup-buildx-action@v2 + + - name: Build Docker container + if: matrix.config.base_image + run: | + docker buildx build --load -t sf_builder - << EOF + FROM ${{ matrix.config.base_image }} + WORKDIR /app + RUN apk update && apk add make g++ + CMD sh make_sf.sh + EOF + - name: Download required macOS packages if: runner.os == 'macOS' run: brew install coreutils @@ -253,6 +287,15 @@ jobs: make -j2 ARCH=armv8 build ../tests/signature.sh $benchref + - name: Test armv8-dotprod build + if: matrix.config.run_armv8_tests + run: | + export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH + export LDFLAGS="-static -Wno-unused-command-line-argument" + make clean + make -j2 ARCH=armv8-dotprod build + ../tests/signature.sh $benchref + # armv7 tests - name: Test armv7 build @@ -273,6 +316,24 @@ jobs: make -j2 ARCH=armv7-neon build ../tests/signature.sh $benchref + # riscv64 tests + + - name: Test riscv64 build + if: matrix.config.run_riscv64_tests + run: | + echo "export LDFLAGS='-static' && make clean && make -j2 ARCH=riscv64 build" > make_sf.sh + docker run --rm --platform ${{ matrix.config.platform }} -v ${{ github.workspace }}/src:/app sf_builder + ../tests/signature.sh $benchref + + # ppc64 tests + + - name: Test ppc64 build + if: matrix.config.run_ppc64_tests + run: | + echo "export LDFLAGS='-static' && make clean && make -j2 ARCH=ppc-64 build" > make_sf.sh + docker run --rm --platform ${{ matrix.config.platform }} -v ${{ github.workspace }}/src:/app sf_builder + ../tests/signature.sh $benchref + # Other tests - name: Check perft and search reproducibility From a6d9a302b867a76c3df5b658de6206e77b649a4d Mon Sep 17 00:00:00 2001 From: AndrovT <31534597+AndrovT@users.noreply.github.com> Date: Tue, 1 Aug 2023 14:43:37 +0200 Subject: [PATCH 1153/1766] Implement AffineTransformSparseInput for armv8 Implements AffineTransformSparseInput layer for the NNUE evaluation for the armv8 and armv8-dotprod architectures. We measured some nice speed improvements via 10 runs of our benchmark: armv8, Cortex-X1 : 18.5% speed-up armv8, Cortex-A76 : 13.2% speed-up armv8-dotprod, Cortex-X1 : 27.1% speed-up armv8-dotprod, Cortex-A76 : 12.1% speed-up armv8, Cortex-A72, Raspberry Pi 4 : 8.2% speed-up (thanks Torom!) closes https://github.com/official-stockfish/Stockfish/pull/4719 No functional change --- .../layers/affine_transform_sparse_input.h | 100 ++++++++++++------ src/nnue/layers/simd.h | 18 +++- 2 files changed, 83 insertions(+), 35 deletions(-) diff --git a/src/nnue/layers/affine_transform_sparse_input.h b/src/nnue/layers/affine_transform_sparse_input.h index 134b7d13e19..63cbaf45a34 100644 --- a/src/nnue/layers/affine_transform_sparse_input.h +++ b/src/nnue/layers/affine_transform_sparse_input.h @@ -35,7 +35,7 @@ namespace Stockfish::Eval::NNUE::Layers { -#if defined(USE_SSSE3) +#if (USE_SSSE3 | (USE_NEON >= 8)) alignas(CacheLineSize) static inline const std::array, 256> lookup_indices = [](){ std::array, 256> v{}; for (unsigned i = 0; i < 256; ++i) @@ -50,19 +50,37 @@ namespace Stockfish::Eval::NNUE::Layers { // Find indices of nonzero numbers in an int32_t array template void find_nnz(const std::int32_t* input, std::uint16_t* out, IndexType& count_out) { -#if defined (USE_AVX512) - using vec_t = __m512i; - #define vec_nnz(a) _mm512_cmpgt_epi32_mask(a, _mm512_setzero_si512()) -#elif defined (USE_AVX2) - using vec_t = __m256i; - #if defined(USE_VNNI) && !defined(USE_AVXVNNI) - #define vec_nnz(a) _mm256_cmpgt_epi32_mask(a, _mm256_setzero_si256()) - #else - #define vec_nnz(a) _mm256_movemask_ps(_mm256_castsi256_ps(_mm256_cmpgt_epi32(a, _mm256_setzero_si256()))) +#if defined (USE_SSSE3) + #if defined (USE_AVX512) + using vec_t = __m512i; + #define vec_nnz(a) _mm512_cmpgt_epi32_mask(a, _mm512_setzero_si512()) + #elif defined (USE_AVX2) + using vec_t = __m256i; + #if defined(USE_VNNI) && !defined(USE_AVXVNNI) + #define vec_nnz(a) _mm256_cmpgt_epi32_mask(a, _mm256_setzero_si256()) + #else + #define vec_nnz(a) _mm256_movemask_ps(_mm256_castsi256_ps(_mm256_cmpgt_epi32(a, _mm256_setzero_si256()))) + #endif + #elif defined (USE_SSSE3) + using vec_t = __m128i; + #define vec_nnz(a) _mm_movemask_ps(_mm_castsi128_ps(_mm_cmpgt_epi32(a, _mm_setzero_si128()))) #endif -#elif defined (USE_SSSE3) - using vec_t = __m128i; - #define vec_nnz(a) _mm_movemask_ps(_mm_castsi128_ps(_mm_cmpgt_epi32(a, _mm_setzero_si128()))) + using vec128_t = __m128i; + #define vec128_zero _mm_setzero_si128() + #define vec128_set_16(a) _mm_set1_epi16(a) + #define vec128_load(a) _mm_load_si128(a) + #define vec128_storeu(a, b) _mm_storeu_si128(a, b) + #define vec128_add(a, b) _mm_add_epi16(a, b) +#elif defined (USE_NEON) + using vec_t = int32x4_t; + static const std::uint32_t Mask[4] = {1, 2, 4, 8}; + #define vec_nnz(a) vaddvq_u32(vandq_u32(vtstq_u32(a, a), vld1q_u32(Mask))) + using vec128_t = int16x8_t; + #define vec128_zero vdupq_n_u16(0) + #define vec128_set_16(a) vdupq_n_u16(a) + #define vec128_load(a) vld1q_u16(reinterpret_cast(a)) + #define vec128_storeu(a, b) vst1q_u16(reinterpret_cast(a), b) + #define vec128_add(a, b) vaddq_u16(a, b) #endif constexpr IndexType InputSimdWidth = sizeof(vec_t) / sizeof(std::int32_t); // Inputs are processed InputSimdWidth at a time and outputs are processed 8 at a time so we process in chunks of max(InputSimdWidth, 8) @@ -73,8 +91,8 @@ namespace Stockfish::Eval::NNUE::Layers { const auto inputVector = reinterpret_cast(input); IndexType count = 0; - __m128i base = _mm_setzero_si128(); - const __m128i increment = _mm_set1_epi16(8); + vec128_t base = vec128_zero; + const vec128_t increment = vec128_set_16(8); for (IndexType i = 0; i < NumChunks; ++i) { // bitmask of nonzero values in this chunk @@ -87,15 +105,20 @@ namespace Stockfish::Eval::NNUE::Layers { for (IndexType j = 0; j < OutputsPerChunk; ++j) { const auto lookup = (nnz >> (j * 8)) & 0xFF; - const auto offsets = _mm_loadu_si128(reinterpret_cast(&lookup_indices[lookup])); - _mm_storeu_si128(reinterpret_cast<__m128i*>(out + count), _mm_add_epi16(base, offsets)); + const auto offsets = vec128_load(reinterpret_cast(&lookup_indices[lookup])); + vec128_storeu(reinterpret_cast(out + count), vec128_add(base, offsets)); count += popcount(lookup); - base = _mm_add_epi16(base, increment); + base = vec128_add(base, increment); } } count_out = count; } # undef vec_nnz +# undef vec128_zero +# undef vec128_set_16 +# undef vec128_load +# undef vec128_storeu +# undef vec128_add #endif // Sparse input implementation @@ -117,7 +140,7 @@ namespace Stockfish::Eval::NNUE::Layers { static constexpr IndexType PaddedOutputDimensions = ceil_to_multiple(OutputDimensions, MaxSimdWidth); -#if defined (USE_SSSE3) +#if (USE_SSSE3 | (USE_NEON >= 8)) static constexpr IndexType ChunkSize = 4; #else static constexpr IndexType ChunkSize = 1; @@ -144,7 +167,7 @@ namespace Stockfish::Eval::NNUE::Layers { static constexpr IndexType get_weight_index(IndexType i) { -#if defined (USE_SSSE3) +#if (USE_SSSE3 | (USE_NEON >= 8)) return get_weight_index_scrambled(i); #else return i; @@ -173,24 +196,34 @@ namespace Stockfish::Eval::NNUE::Layers { void propagate( const InputType* input, OutputType* output) const { -#if defined (USE_SSSE3) +#if (USE_SSSE3 | (USE_NEON >= 8)) #if defined (USE_AVX512) - using vec_t = __m512i; - #define vec_setzero _mm512_setzero_si512 + using invec_t = __m512i; + using outvec_t = __m512i; #define vec_set_32 _mm512_set1_epi32 #define vec_add_dpbusd_32 Simd::m512_add_dpbusd_epi32 #elif defined (USE_AVX2) - using vec_t = __m256i; - #define vec_setzero _mm256_setzero_si256 + using invec_t = __m256i; + using outvec_t = __m256i; #define vec_set_32 _mm256_set1_epi32 #define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32 #elif defined (USE_SSSE3) - using vec_t = __m128i; - #define vec_setzero _mm_setzero_si128 + using invec_t = __m128i; + using outvec_t = __m128i; #define vec_set_32 _mm_set1_epi32 #define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32 +#elif defined (USE_NEON_DOTPROD) + using invec_t = int8x16_t; + using outvec_t = int32x4_t; + #define vec_set_32(a) vreinterpretq_s8_u32(vdupq_n_u32(a)) + #define vec_add_dpbusd_32 Simd::dotprod_m128_add_dpbusd_epi32 +#elif defined (USE_NEON) + using invec_t = int8x16_t; + using outvec_t = int32x4_t; + #define vec_set_32(a) vreinterpretq_s8_u32(vdupq_n_u32(a)) + #define vec_add_dpbusd_32 Simd::neon_m128_add_dpbusd_epi32 #endif - static constexpr IndexType OutputSimdWidth = sizeof(vec_t) / sizeof(OutputType); + static constexpr IndexType OutputSimdWidth = sizeof(outvec_t) / sizeof(OutputType); constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 8) / ChunkSize; constexpr IndexType NumRegs = OutputDimensions / OutputSimdWidth; @@ -202,24 +235,23 @@ namespace Stockfish::Eval::NNUE::Layers { // Find indices of nonzero 32bit blocks find_nnz(input32, nnz, count); - const vec_t* biasvec = reinterpret_cast(biases); - vec_t acc[NumRegs]; + const outvec_t* biasvec = reinterpret_cast(biases); + outvec_t acc[NumRegs]; for (IndexType k = 0; k < NumRegs; ++k) acc[k] = biasvec[k]; for (IndexType j = 0; j < count; ++j) { const auto i = nnz[j]; - const vec_t in = vec_set_32(input32[i]); - const auto col = reinterpret_cast(&weights[i * OutputDimensions * ChunkSize]); + const invec_t in = vec_set_32(input32[i]); + const auto col = reinterpret_cast(&weights[i * OutputDimensions * ChunkSize]); for (IndexType k = 0; k < NumRegs; ++k) vec_add_dpbusd_32(acc[k], in, col[k]); } - vec_t* outptr = reinterpret_cast(output); + outvec_t* outptr = reinterpret_cast(output); for (IndexType k = 0; k < NumRegs; ++k) outptr[k] = acc[k]; -# undef vec_setzero # undef vec_set_32 # undef vec_add_dpbusd_32 #else diff --git a/src/nnue/layers/simd.h b/src/nnue/layers/simd.h index fae31a62955..638e39941a8 100644 --- a/src/nnue/layers/simd.h +++ b/src/nnue/layers/simd.h @@ -239,6 +239,12 @@ namespace Stockfish::Simd { acc = vdotq_s32(acc, a1, b1); } + [[maybe_unused]] static void dotprod_m128_add_dpbusd_epi32( + int32x4_t& acc, + int8x16_t a, int8x16_t b) { + + acc = vdotq_s32(acc, a, b); + } #endif #if defined (USE_NEON) @@ -277,9 +283,19 @@ namespace Stockfish::Simd { product = vmlal_s8(product, a1, b1); acc = vpadalq_s16(acc, product); } - #endif +#if USE_NEON >= 8 + [[maybe_unused]] static void neon_m128_add_dpbusd_epi32( + int32x4_t& acc, + int8x16_t a, int8x16_t b) { + + int16x8_t product0 = vmull_s8(vget_low_s8(a), vget_low_s8(b)); + int16x8_t product1 = vmull_high_s8(a, b); + int16x8_t sum = vpaddq_s16(product0, product1); + acc = vpadalq_s16(acc, sum); + } +#endif } #endif // STOCKFISH_SIMD_H_INCLUDED From 0ad9b51deaaa1f2a8273ed064fbf6425cfbbe4f2 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Mon, 24 Jul 2023 01:22:21 -0400 Subject: [PATCH 1154/1766] Remove classical psqt Based on vondele's deletepsqt branch: https://github.com/vondele/Stockfish/commit/369f5b051 This huge simplification uses a weighted material differences instead of the positional piece square tables (psqt) in the semi-classical complexity calculation. Tuned weights using spsa at 45+0.45 with: int pawnMult = 100; int knightMult = 325; int bishopMult = 350; int rookMult = 500; int queenMult = 900; TUNE(SetRange(0, 200), pawnMult); TUNE(SetRange(0, 650), knightMult); TUNE(SetRange(0, 700), bishopMult); TUNE(SetRange(200, 800), rookMult); TUNE(SetRange(600, 1200), queenMult); The values obtained via this tuning session were for a model where the psqt replacement formula was always from the point of view of White, even if the side to move was Black. We re-used the same values for an implementation with a psqt replacement from the point of view of the side to move, testing the result both on our standard book on positions with a strong White bias, and an alternate book with positions with a strong Black bias. We note that with the patch the last use of the venerable "Score" type disappears in Stockfish codebase (the Score type was used in classical evaluation to get a tampered eval interpolating values smoothly from the early midgame stage to the endgame stage). We leave it to another commit to clean all occurrences of Score in the code and the comments. ------- Passed non-regression LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 142542 W: 36264 L: 36168 D: 70110 Ptnml(0-2): 76, 15578, 39856, 15696, 65 https://tests.stockfishchess.org/tests/view/64c8cb495b17f7c21c0cf9f8 Passed non-regression LTC (with a book with Black bias): https://tests.stockfishchess.org/tests/view/64c8f9295b17f7c21c0cfdaf LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 494814 W: 125565 L: 125827 D: 243422 Ptnml(0-2): 244, 53926, 139346, 53630, 261 ------ closes https://github.com/official-stockfish/Stockfish/pull/4713 Bench: 1655985 --- src/Makefile | 2 +- src/evaluate.cpp | 9 +++- src/main.cpp | 2 - src/position.h | 10 ---- src/psqt.cpp | 131 ----------------------------------------------- src/psqt.h | 38 -------------- 6 files changed, 8 insertions(+), 184 deletions(-) delete mode 100644 src/psqt.cpp delete mode 100644 src/psqt.h diff --git a/src/Makefile b/src/Makefile index f66d84d5547..8811d15e868 100644 --- a/src/Makefile +++ b/src/Makefile @@ -53,7 +53,7 @@ PGOBENCH = $(WINE_PATH) ./$(EXE) bench ### Source and object files SRCS = benchmark.cpp bitboard.cpp evaluate.cpp main.cpp \ - misc.cpp movegen.cpp movepick.cpp position.cpp psqt.cpp \ + misc.cpp movegen.cpp movepick.cpp position.cpp \ search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \ nnue/evaluate_nnue.cpp nnue/features/half_ka_v2_hm.cpp diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 7f0ea4bc604..c37dd98ad1c 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -143,7 +143,6 @@ Value Eval::evaluate(const Position& pos) { assert(!pos.checkers()); Value v; - Value psq = pos.psq_eg_stm(); int nnueComplexity; int npm = pos.non_pawn_material() / 64; @@ -153,8 +152,14 @@ Value Eval::evaluate(const Position& pos) { Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); + int material = 67 * (pos.count(stm) - pos.count(~stm)) + + 395 * (pos.count(stm) - pos.count(~stm)) + + 288 * (pos.count(stm) - pos.count(~stm)) + + 630 * (pos.count(stm) - pos.count(~stm)) + + 857 * (pos.count(stm) - pos.count(~stm)); + // Blend optimism with nnue complexity and (semi)classical complexity - optimism += optimism * (nnueComplexity + abs(psq - nnue)) / 512; + optimism += optimism * (nnueComplexity + abs(material - nnue)) / 512; v = ( nnue * (915 + npm + 9 * pos.count()) + optimism * (154 + npm + pos.count())) / 1024; diff --git a/src/main.cpp b/src/main.cpp index 593408f63a7..c854ac0cd16 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,7 +20,6 @@ #include "bitboard.h" #include "position.h" -#include "psqt.h" #include "search.h" #include "syzygy/tbprobe.h" #include "thread.h" @@ -36,7 +35,6 @@ int main(int argc, char* argv[]) { CommandLine::init(argc, argv); UCI::init(Options); Tune::init(); - PSQT::init(); Bitboards::init(); Position::init(); Threads.set(size_t(Options["Threads"])); diff --git a/src/position.h b/src/position.h index dc4c5837299..393c1ac9226 100644 --- a/src/position.h +++ b/src/position.h @@ -26,7 +26,6 @@ #include "bitboard.h" #include "evaluate.h" -#include "psqt.h" #include "types.h" #include "nnue/nnue_accumulator.h" @@ -153,7 +152,6 @@ class Position { bool has_game_cycle(int ply) const; bool has_repeated() const; int rule50_count() const; - Value psq_eg_stm() const; Value non_pawn_material(Color c) const; Value non_pawn_material() const; @@ -192,7 +190,6 @@ class Position { StateInfo* st; int gamePly; Color sideToMove; - Score psq; bool chess960; }; @@ -321,10 +318,6 @@ inline Key Position::material_key() const { return st->materialKey; } -inline Value Position::psq_eg_stm() const { - return (sideToMove == WHITE ? 1 : -1) * eg_value(psq); -} - inline Value Position::non_pawn_material(Color c) const { return st->nonPawnMaterial[c]; } @@ -374,7 +367,6 @@ inline void Position::put_piece(Piece pc, Square s) { byColorBB[color_of(pc)] |= s; pieceCount[pc]++; pieceCount[make_piece(color_of(pc), ALL_PIECES)]++; - psq += PSQT::psq[pc][s]; } inline void Position::remove_piece(Square s) { @@ -386,7 +378,6 @@ inline void Position::remove_piece(Square s) { board[s] = NO_PIECE; pieceCount[pc]--; pieceCount[make_piece(color_of(pc), ALL_PIECES)]--; - psq -= PSQT::psq[pc][s]; } inline void Position::move_piece(Square from, Square to) { @@ -398,7 +389,6 @@ inline void Position::move_piece(Square from, Square to) { byColorBB[color_of(pc)] ^= fromTo; board[from] = NO_PIECE; board[to] = pc; - psq += PSQT::psq[pc][to] - PSQT::psq[pc][from]; } inline void Position::do_move(Move m, StateInfo& newSt) { diff --git a/src/psqt.cpp b/src/psqt.cpp deleted file mode 100644 index d3ebb20da1b..00000000000 --- a/src/psqt.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - - -#include "psqt.h" - -#include - -#include "bitboard.h" -#include "types.h" - -namespace Stockfish { - -namespace -{ - -auto constexpr S = make_score; - -// 'Bonus' contains Piece-Square parameters. -// Scores are explicit for files A to D, implicitly mirrored for E to H. -constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = { - { }, - { }, - { // Knight - { S(-175, -96), S(-92,-65), S(-74,-49), S(-73,-21) }, - { S( -77, -67), S(-41,-54), S(-27,-18), S(-15, 8) }, - { S( -61, -40), S(-17,-27), S( 6, -8), S( 12, 29) }, - { S( -35, -35), S( 8, -2), S( 40, 13), S( 49, 28) }, - { S( -34, -45), S( 13,-16), S( 44, 9), S( 51, 39) }, - { S( -9, -51), S( 22,-44), S( 58,-16), S( 53, 17) }, - { S( -67, -69), S(-27,-50), S( 4,-51), S( 37, 12) }, - { S(-201,-100), S(-83,-88), S(-56,-56), S(-26,-17) } - }, - { // Bishop - { S(-37,-40), S(-4 ,-21), S( -6,-26), S(-16, -8) }, - { S(-11,-26), S( 6, -9), S( 13,-12), S( 3, 1) }, - { S(-5 ,-11), S( 15, -1), S( -4, -1), S( 12, 7) }, - { S(-4 ,-14), S( 8, -4), S( 18, 0), S( 27, 12) }, - { S(-8 ,-12), S( 20, -1), S( 15,-10), S( 22, 11) }, - { S(-11,-21), S( 4, 4), S( 1, 3), S( 8, 4) }, - { S(-12,-22), S(-10,-14), S( 4, -1), S( 0, 1) }, - { S(-34,-32), S( 1,-29), S(-10,-26), S(-16,-17) } - }, - { // Rook - { 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) }, - { S(-3,-54), S( 5,-31), S( 8,-22), S(12, -4) }, - { S(-3,-39), S( 6,-18), S(13, -9), S( 7, 3) }, - { S( 4,-23), S( 5, -3), S( 9, 13), S( 8, 24) }, - { S( 0,-29), S(14, -6), S(12, 9), S( 5, 21) }, - { S(-4,-38), S(10,-18), S( 6,-11), S( 8, 1) }, - { S(-5,-50), S( 6,-27), S(10,-24), S( 8, -8) }, - { S(-2,-74), S(-2,-52), S( 1,-43), S(-2,-34) } - }, - { // King - { S(271, 1), S(327, 45), S(271, 85), S(198, 76) }, - { S(278, 53), S(303,100), S(234,133), S(179,135) }, - { S(195, 88), S(258,130), S(169,169), S(120,175) }, - { S(164,103), S(190,156), S(138,172), S( 98,172) }, - { S(154, 96), S(179,166), S(105,199), S( 70,199) }, - { S(123, 92), S(145,172), S( 81,184), S( 31,191) }, - { S( 88, 47), S(120,121), S( 65,116), S( 33,131) }, - { S( 59, 11), S( 89, 59), S( 45, 73), S( -1, 78) } - } -}; - -constexpr Score PBonus[RANK_NB][FILE_NB] = - { // Pawn (asymmetric distribution) - { }, - { S( 2, -8), S( 4, -6), S( 11, 9), S( 18, 5), S( 16, 16), S( 21, 6), S( 9, -6), S( -3,-18) }, - { S( -9, -9), S(-15, -7), S( 11,-10), S( 15, 5), S( 31, 2), S( 23, 3), S( 6, -8), S(-20, -5) }, - { S( -3, 7), S(-20, 1), S( 8, -8), S( 19, -2), S( 39,-14), S( 17,-13), S( 2,-11), S( -5, -6) }, - { S( 11, 12), S( -4, 6), S(-11, 2), S( 2, -6), S( 11, -5), S( 0, -4), S(-12, 14), S( 5, 9) }, - { S( 3, 27), S(-11, 18), S( -6, 19), S( 22, 29), S( -8, 30), S( -5, 9), S(-14, 8), S(-11, 14) }, - { S( -7, -1), S( 6,-14), S( -2, 13), S(-11, 22), S( 4, 24), S(-14, 17), S( 10, 7), S( -9, 7) } - }; - -} // namespace - - -namespace PSQT -{ - -Score psq[PIECE_NB][SQUARE_NB]; - -// PSQT::init() initializes piece-square tables: the white halves of the tables are -// copied from Bonus[] and PBonus[], adding the piece value, then the black halves of -// the tables are initialized by flipping and changing the sign of the white scores. -void init() { - - for (Piece pc : {W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING}) - { - Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]); - - for (Square s = SQ_A1; s <= SQ_H8; ++s) - { - File f = File(edge_distance(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][flip_rank(s)] = -psq[pc][s]; - } - } -} - -} // namespace PSQT - -} // namespace Stockfish diff --git a/src/psqt.h b/src/psqt.h deleted file mode 100644 index 9630f446bc3..00000000000 --- a/src/psqt.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - - -#ifndef PSQT_H_INCLUDED -#define PSQT_H_INCLUDED - - -#include "types.h" - - -namespace Stockfish::PSQT -{ - -extern Score psq[PIECE_NB][SQUARE_NB]; - -// Fill psqt array from a set of internally linked parameters -void init(); - -} // namespace Stockfish::PSQT - - -#endif // PSQT_H_INCLUDED From a26f8d37e108c103ada129e619a17597c7e50046 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sat, 5 Aug 2023 14:21:08 +0300 Subject: [PATCH 1155/1766] Tweak formula for pruning moves losing material Simplify the "Prune moves with negative SEE" formula, by removing one multiplication and subtraction operation. Passed STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 214272 W: 54596 L: 54572 D: 105104 Ptnml(0-2): 741, 25160, 55320, 25164, 751 https://tests.stockfishchess.org/tests/view/64c430d1dc56e1650abbdbf6 Passed LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 238380 W: 60600 L: 60601 D: 117179 Ptnml(0-2): 132, 26069, 66791, 26064, 134 https://tests.stockfishchess.org/tests/view/64c81f155b17f7c21c0cee2b closes https://github.com/official-stockfish/Stockfish/pull/4721 bench: 1655337 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 45758031669..dc439ed00e5 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1037,7 +1037,7 @@ namespace { lmrDepth = std::max(lmrDepth, 0); // Prune moves with negative SEE (~4 Elo) - if (!pos.see_ge(move, Value(-27 * lmrDepth * lmrDepth - 16 * lmrDepth))) + if (!pos.see_ge(move, Value(-31 * lmrDepth * lmrDepth))) continue; } } From 5c2111cc30b283aa5b7e1cc1c1e9d7c52e1e910b Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Mon, 7 Aug 2023 02:32:38 +0300 Subject: [PATCH 1156/1766] Adjust futility pruning base in qsearch Current master used value from transposition table there if it existed, this patch uses minimum between this tt value and the static eval instead (this thus is closer to the main search function, which uses the static eval). Passed STC: https://tests.stockfishchess.org/tests/view/64cd57285b17f7c21c0d6a8c LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 252544 W: 64671 L: 64039 D: 123834 Ptnml(0-2): 839, 29207, 65575, 29785, 866 Passed LTC: https://tests.stockfishchess.org/tests/view/64cf6c915b17f7c21c0d9fcb LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 60150 W: 15374 L: 15012 D: 29764 Ptnml(0-2): 24, 6321, 17024, 6681, 25 closes https://github.com/official-stockfish/Stockfish/pull/4725 Bench: 1573024 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index dc439ed00e5..24f54c32bb3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1495,7 +1495,7 @@ namespace { if (bestValue > alpha) alpha = bestValue; - futilityBase = bestValue + 200; + futilityBase = std::min(ss->staticEval, bestValue) + 200; } const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, From e64b817e0a2335824ef1f1f7ba9f5cd4310994e1 Mon Sep 17 00:00:00 2001 From: Cody Ho Date: Sun, 6 Aug 2023 14:21:22 -0700 Subject: [PATCH 1157/1766] Remove all references to Score type Score is obsolete with the removal of psqt. No functional change. Signed-off-by: Cody Ho closes https://github.com/official-stockfish/Stockfish/pull/4724 --- src/tune.cpp | 13 ------------- src/tune.h | 6 ++---- src/types.h | 55 ---------------------------------------------------- 3 files changed, 2 insertions(+), 72 deletions(-) diff --git a/src/tune.cpp b/src/tune.cpp index 41f6664d9ca..ccfc33c5082 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -92,19 +92,6 @@ template<> void Tune::Entry::read_option() { value = Value(int(Options[name])); } -template<> void Tune::Entry::init_option() { - make_option("m" + name, mg_value(value), range); - make_option("e" + name, eg_value(value), range); -} - -template<> void Tune::Entry::read_option() { - if (Options.count("m" + name)) - value = make_score(int(Options["m" + name]), eg_value(value)); - - if (Options.count("e" + name)) - value = make_score(mg_value(value), int(Options["e" + name])); -} - // Instead of a variable here we have a PostUpdate function: just call it template<> void Tune::Entry::init_option() {} template<> void Tune::Entry::read_option() { value(); } diff --git a/src/tune.h b/src/tune.h index 440d950a9ec..bdbee14e014 100644 --- a/src/tune.h +++ b/src/tune.h @@ -51,18 +51,17 @@ struct SetRange { /// qualifiers from the variables you want to tune and flag them for tuning, so /// if you have: /// -/// const Score myScore = S(10, 15); /// const Value myValue[][2] = { { V(100), V(20) }, { V(7), V(78) } }; /// /// If you have a my_post_update() function to run after values have been updated, /// and a my_range() function to set custom Option's min-max values, then you just /// remove the 'const' qualifiers and write somewhere below in the file: /// -/// TUNE(SetRange(my_range), myScore, myValue, my_post_update); +/// TUNE(SetRange(my_range), myValue, my_post_update); /// /// You can also set the range directly, and restore the default at the end /// -/// TUNE(SetRange(-100, 100), myScore, SetDefaultRange); +/// TUNE(SetRange(-100, 100), myValue, SetDefaultRange); /// /// In case update function is slow and you have many parameters, you can add: /// @@ -98,7 +97,6 @@ class Tune { static_assert( std::is_same::value || std::is_same::value - || std::is_same::value || std::is_same::value, "Parameter type not supported!"); Entry(const std::string& n, T& v, const SetRange& r) : name(n), value(v), range(r) {} diff --git a/src/types.h b/src/types.h index 06b0a05985a..b0c11778c9d 100644 --- a/src/types.h +++ b/src/types.h @@ -154,8 +154,6 @@ enum CastlingRights { }; enum Phase { - PHASE_ENDGAME, - PHASE_MIDGAME = 128, MG = 0, EG = 1, PHASE_NB = 2 }; @@ -194,8 +192,6 @@ enum Value : int { BishopValueMg = 825, BishopValueEg = 915, RookValueMg = 1276, RookValueEg = 1380, QueenValueMg = 2538, QueenValueEg = 2682, - - MidgameLimit = 15258, EndgameLimit = 3915 }; enum PieceType { @@ -281,29 +277,6 @@ struct DirtyPiece { Square to[3]; }; -/// Score enum stores a middlegame and an endgame value in a single integer (enum). -/// The least significant 16 bits are used to store the middlegame value and the -/// upper 16 bits are used to store the endgame value. We have to take care to -/// avoid left-shifting a signed int to avoid undefined behavior. -enum Score : int { SCORE_ZERO }; - -constexpr Score make_score(int mg, int eg) { - return Score((int)((unsigned int)eg << 16) + mg); -} - -/// Extracting the signed lower and upper 16 bits is not so trivial because -/// according to the standard a simple cast to short is implementation defined -/// and so is a right shift of a signed integer. -inline Value eg_value(Score s) { - union { uint16_t u; int16_t s; } eg = { uint16_t(unsigned(s + 0x8000) >> 16) }; - return Value(eg.s); -} - -inline Value mg_value(Score s) { - union { uint16_t u; int16_t s; } mg = { uint16_t(unsigned(s)) }; - return Value(mg.s); -} - #define ENABLE_BASE_OPERATORS_ON(T) \ constexpr T operator+(T d1, int d2) { return T(int(d1) + d2); } \ constexpr T operator-(T d1, int d2) { return T(int(d1) - d2); } \ @@ -333,8 +306,6 @@ ENABLE_INCR_OPERATORS_ON(Square) ENABLE_INCR_OPERATORS_ON(File) ENABLE_INCR_OPERATORS_ON(Rank) -ENABLE_BASE_OPERATORS_ON(Score) - #undef ENABLE_FULL_OPERATORS_ON #undef ENABLE_INCR_OPERATORS_ON #undef ENABLE_BASE_OPERATORS_ON @@ -345,32 +316,6 @@ constexpr Square operator-(Square s, Direction d) { return Square(int(s) - int(d inline Square& operator+=(Square& s, Direction d) { return s = s + d; } inline Square& operator-=(Square& s, Direction d) { return s = s - d; } -/// Only declared but not defined. We don't want to multiply two scores due to -/// a very high risk of overflow. So user should explicitly convert to integer. -Score operator*(Score, Score) = delete; - -/// Division of a Score must be handled separately for each term -inline Score operator/(Score s, int i) { - return make_score(mg_value(s) / i, eg_value(s) / i); -} - -/// Multiplication of a Score by an integer. We check for overflow in debug mode. -inline Score operator*(Score s, int i) { - - Score result = Score(int(s) * i); - - assert(eg_value(result) == (i * eg_value(s))); - assert(mg_value(result) == (i * mg_value(s))); - assert((i == 0) || (result / i) == s); - - return result; -} - -/// Multiplication of a Score by a boolean -inline Score operator*(Score s, bool b) { - return b ? s : SCORE_ZERO; -} - constexpr Color operator~(Color c) { return Color(c ^ BLACK); // Toggle color } From 0d2ddb81ef44211e7bf40369dc8fc52160d0ee18 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Mon, 7 Aug 2023 13:11:09 +0200 Subject: [PATCH 1158/1766] Fix Makefile for incorrect nnue file If an incorrect network file is present at the start of the compilation stage, the Makefile script now correctly removes it before trying to download a clean version. closes https://github.com/official-stockfish/Stockfish/pull/4726 No functional change --- src/Makefile | 83 +++++++++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/src/Makefile b/src/Makefile index 8811d15e868..c7e059ea925 100644 --- a/src/Makefile +++ b/src/Makefile @@ -869,42 +869,6 @@ install: clean: objclean profileclean @rm -f .depend *~ core -# evaluation network (nnue) -net: - $(eval nnuenet := $(shell grep EvalFileDefaultName evaluate.h | grep define | sed 's/.*\(nn-[a-z0-9]\{12\}.nnue\).*/\1/')) - @echo "Default net: $(nnuenet)" - $(eval nnuedownloadurl1 := https://tests.stockfishchess.org/api/nn/$(nnuenet)) - $(eval nnuedownloadurl2 := https://github.com/official-stockfish/networks/raw/master/$(nnuenet)) - $(eval curl_or_wget := $(shell if hash curl 2>/dev/null; then echo "curl -skL"; elif hash wget 2>/dev/null; then echo "wget -qO-"; fi)) - @if [ "x$(curl_or_wget)" = "x" ]; then \ - echo "Neither curl nor wget is installed. Install one of these tools unless the net has been downloaded manually"; \ - fi - $(eval shasum_command := $(shell if hash shasum 2>/dev/null; then echo "shasum -a 256 "; elif hash sha256sum 2>/dev/null; then echo "sha256sum "; fi)) - @if [ "x$(shasum_command)" = "x" ]; then \ - echo "shasum / sha256sum not found, skipping net validation"; \ - fi - @for nnuedownloadurl in "$(nnuedownloadurl1)" "$(nnuedownloadurl2)"; do \ - if test -f "$(nnuenet)"; then \ - echo "$(nnuenet) available."; \ - else \ - if [ "x$(curl_or_wget)" != "x" ]; then \ - echo "Downloading $${nnuedownloadurl}"; $(curl_or_wget) $${nnuedownloadurl} > $(nnuenet);\ - else \ - echo "No net found and download not possible"; exit 1;\ - fi; \ - fi; \ - if [ "x$(shasum_command)" != "x" ]; then \ - if [ "$(nnuenet)" != "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then \ - echo "Removing failed download"; rm -f $(nnuenet); \ - else \ - echo "Network validated"; break; \ - fi; \ - fi; \ - done - @if ! test -f "$(nnuenet)"; then \ - echo "Failed to download $(nnuenet)."; \ - fi - # clean binaries and objects objclean: @rm -f stockfish stockfish.exe *.o ./syzygy/*.o ./nnue/*.o ./nnue/features/*.o @@ -919,6 +883,53 @@ profileclean: @rm -f stockfish.res @rm -f ./-lstdc++.res +# set up shell variables for the net stuff +netvariables: + $(eval nnuenet := $(shell grep EvalFileDefaultName evaluate.h | grep define | sed 's/.*\(nn-[a-z0-9]\{12\}.nnue\).*/\1/')) + $(eval nnuedownloadurl1 := https://tests.stockfishchess.org/api/nn/$(nnuenet)) + $(eval nnuedownloadurl2 := https://github.com/official-stockfish/networks/raw/master/$(nnuenet)) + $(eval curl_or_wget := $(shell if hash curl 2>/dev/null; then echo "curl -skL"; elif hash wget 2>/dev/null; then echo "wget -qO-"; fi)) + $(eval shasum_command := $(shell if hash shasum 2>/dev/null; then echo "shasum -a 256 "; elif hash sha256sum 2>/dev/null; then echo "sha256sum "; fi)) + +# evaluation network (nnue) +net: netvariables + @echo "Default net: $(nnuenet)" + @if [ "x$(curl_or_wget)" = "x" ]; then \ + echo "Neither curl nor wget is installed. Install one of these tools unless the net has been downloaded manually"; \ + fi + @if [ "x$(shasum_command)" = "x" ]; then \ + echo "shasum / sha256sum not found, skipping net validation"; \ + elif test -f "$(nnuenet)"; then \ + if [ "$(nnuenet)" != "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then \ + echo "Removing invalid network"; rm -f $(nnuenet); \ + fi; \ + fi; + @for nnuedownloadurl in "$(nnuedownloadurl1)" "$(nnuedownloadurl2)"; do \ + if test -f "$(nnuenet)"; then \ + echo "$(nnuenet) available : OK"; break; \ + else \ + if [ "x$(curl_or_wget)" != "x" ]; then \ + echo "Downloading $${nnuedownloadurl}"; $(curl_or_wget) $${nnuedownloadurl} > $(nnuenet);\ + else \ + echo "No net found and download not possible"; exit 1;\ + fi; \ + fi; \ + if [ "x$(shasum_command)" != "x" ]; then \ + if [ "$(nnuenet)" != "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then \ + echo "Removing failed download"; rm -f $(nnuenet); \ + fi; \ + fi; \ + done + @if ! test -f "$(nnuenet)"; then \ + echo "Failed to download $(nnuenet)."; \ + fi; + @if [ "x$(shasum_command)" != "x" ]; then \ + if [ "$(nnuenet)" = "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then \ + echo "Network validated"; break; \ + fi; \ + fi; \ + +# default target default: help From 8192945870967fb9c8801247d0b040b2bc657443 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 9 Aug 2023 15:34:53 +0200 Subject: [PATCH 1159/1766] Improve testing coverage, remove unused code a) Add further tests to CI to cover most features. This uncovered a potential race in case setoption was sent between two searches. As the UCI protocol requires this sent to be went the engine is not searching, setoption now ensures that this is the case. b) Remove some unused code closes https://github.com/official-stockfish/Stockfish/pull/4730 No functional change --- src/nnue/layers/simd.h | 55 ------------------------------------------ src/syzygy/tbprobe.h | 23 ------------------ src/types.h | 1 - src/uci.cpp | 2 ++ tests/instrumented.sh | 52 +++++++++++++++++++++++++++++++++++++-- 5 files changed, 52 insertions(+), 81 deletions(-) diff --git a/src/nnue/layers/simd.h b/src/nnue/layers/simd.h index 638e39941a8..f478cd7819f 100644 --- a/src/nnue/layers/simd.h +++ b/src/nnue/layers/simd.h @@ -79,23 +79,6 @@ namespace Stockfish::Simd { return _mm512_add_epi32(sum0123a, sum0123b); } - [[maybe_unused]] static __m128i m512_haddx4( - __m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3, - __m128i bias) { - - __m512i sum = m512_hadd128x16_interleave(sum0, sum1, sum2, sum3); - - __m256i sum256lo = _mm512_castsi512_si256(sum); - __m256i sum256hi = _mm512_extracti64x4_epi64(sum, 1); - - sum256lo = _mm256_add_epi32(sum256lo, sum256hi); - - __m128i sum128lo = _mm256_castsi256_si128(sum256lo); - __m128i sum128hi = _mm256_extracti128_si256(sum256lo, 1); - - return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias); - } - [[maybe_unused]] static void m512_add_dpbusd_epi32( __m512i& acc, __m512i a, @@ -138,21 +121,6 @@ namespace Stockfish::Simd { return _mm_cvtsi128_si32(sum128) + bias; } - [[maybe_unused]] static __m128i m256_haddx4( - __m256i sum0, __m256i sum1, __m256i sum2, __m256i sum3, - __m128i bias) { - - sum0 = _mm256_hadd_epi32(sum0, sum1); - sum2 = _mm256_hadd_epi32(sum2, sum3); - - sum0 = _mm256_hadd_epi32(sum0, sum2); - - __m128i sum128lo = _mm256_castsi256_si128(sum0); - __m128i sum128hi = _mm256_extracti128_si256(sum0, 1); - - return _mm_add_epi32(_mm_add_epi32(sum128lo, sum128hi), bias); - } - [[maybe_unused]] static void m256_add_dpbusd_epi32( __m256i& acc, __m256i a, @@ -194,16 +162,6 @@ namespace Stockfish::Simd { return _mm_cvtsi128_si32(sum) + bias; } - [[maybe_unused]] static __m128i m128_haddx4( - __m128i sum0, __m128i sum1, __m128i sum2, __m128i sum3, - __m128i bias) { - - sum0 = _mm_hadd_epi32(sum0, sum1); - sum2 = _mm_hadd_epi32(sum2, sum3); - sum0 = _mm_hadd_epi32(sum0, sum2); - return _mm_add_epi32(sum0, bias); - } - [[maybe_unused]] static void m128_add_dpbusd_epi32( __m128i& acc, __m128i a, @@ -261,19 +219,6 @@ namespace Stockfish::Simd { return neon_m128_reduce_add_epi32(sum) + bias; } - [[maybe_unused]] static int32x4_t neon_m128_haddx4( - int32x4_t sum0, int32x4_t sum1, int32x4_t sum2, int32x4_t sum3, - int32x4_t bias) { - - int32x4_t hsums { - neon_m128_reduce_add_epi32(sum0), - neon_m128_reduce_add_epi32(sum1), - neon_m128_reduce_add_epi32(sum2), - neon_m128_reduce_add_epi32(sum3) - }; - return vaddq_s32(hsums, bias); - } - [[maybe_unused]] static void neon_m128_add_dpbusd_epi32x2( int32x4_t& acc, int8x8_t a0, int8x8_t b0, diff --git a/src/syzygy/tbprobe.h b/src/syzygy/tbprobe.h index 159c68652d1..fe994f68e91 100644 --- a/src/syzygy/tbprobe.h +++ b/src/syzygy/tbprobe.h @@ -19,8 +19,6 @@ #ifndef TBPROBE_H #define TBPROBE_H -#include - #include "../search.h" namespace Stockfish::Tablebases { @@ -50,27 +48,6 @@ bool root_probe(Position& pos, Search::RootMoves& rootMoves); bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves); void rank_root_moves(Position& pos, Search::RootMoves& rootMoves); -inline std::ostream& operator<<(std::ostream& os, const WDLScore v) { - - os << (v == WDLLoss ? "Loss" : - v == WDLBlessedLoss ? "Blessed loss" : - v == WDLDraw ? "Draw" : - v == WDLCursedWin ? "Cursed win" : - v == WDLWin ? "Win" : "None"); - - return os; -} - -inline std::ostream& operator<<(std::ostream& os, const ProbeState v) { - - os << (v == FAIL ? "Failed" : - v == OK ? "Success" : - v == CHANGE_STM ? "Probed opponent side" : - v == ZEROING_BEST_MOVE ? "Best move zeroes DTZ" : "None"); - - return os; -} - } // namespace Stockfish::Tablebases #endif diff --git a/src/types.h b/src/types.h index b0c11778c9d..5d78377690a 100644 --- a/src/types.h +++ b/src/types.h @@ -300,7 +300,6 @@ inline T& operator/=(T& d, int i) { return d = T(int(d) / i); } ENABLE_FULL_OPERATORS_ON(Value) ENABLE_FULL_OPERATORS_ON(Direction) -ENABLE_INCR_OPERATORS_ON(Piece) ENABLE_INCR_OPERATORS_ON(PieceType) ENABLE_INCR_OPERATORS_ON(Square) ENABLE_INCR_OPERATORS_ON(File) diff --git a/src/uci.cpp b/src/uci.cpp index f893bd9cece..ffe5e0576ea 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -98,6 +98,8 @@ namespace { void setoption(istringstream& is) { + Threads.main()->wait_for_search_finished(); + string token, name, value; is >> token; // Consume the "name" token diff --git a/tests/instrumented.sh b/tests/instrumented.sh index 1b37c7a8b75..637d19f9d63 100755 --- a/tests/instrumented.sh +++ b/tests/instrumented.sh @@ -64,14 +64,32 @@ EOF ;; esac +cat << EOF > bench_tmp.epd +Rn6/1rbq1bk1/2p2n1p/2Bp1p2/3Pp1pP/1N2P1P1/2Q1NPB1/6K1 w - - 2 26 +rnbqkb1r/ppp1pp2/5n1p/3p2p1/P2PP3/5P2/1PP3PP/RNBQKBNR w KQkq - 0 3 +3qnrk1/4bp1p/1p2p1pP/p2bN3/1P1P1B2/P2BQ3/5PP1/4R1K1 w - - 9 28 +r4rk1/1b2ppbp/pq4pn/2pp1PB1/1p2P3/1P1P1NN1/1PP3PP/R2Q1RK1 w - - 0 13 +EOF + # simple command line testing for args in "eval" \ "go nodes 1000" \ "go depth 10" \ + "go perft 4" \ "go movetime 1000" \ "go wtime 8000 btime 8000 winc 500 binc 500" \ + "go wtime 1000 btime 1000 winc 0 binc 0" \ + "go wtime 1000 btime 1000 winc 0 binc 0" \ + "go wtime 1000 btime 1000 winc 0 binc 0 movestogo 5" \ + "go movetime 200" \ + "go nodes 20000 searchmoves e2e4 d2d4" \ "bench 128 $threads 8 default depth" \ - "export_net verify.nnue" + "bench 128 $threads 3 bench_tmp.epd depth" \ + "export_net verify.nnue" \ + "d" \ + "compiler" \ + "license" \ + "uci" do echo "$prefix $exeprefix ./stockfish $args $postfix" @@ -92,6 +110,7 @@ cat << EOF > game.exp send "uci\n" expect "uciok" + # send "setoption name Debug Log File value debug.log\n" send "setoption name Threads value $threads\n" send "ucinewgame\n" @@ -107,6 +126,28 @@ cat << EOF > game.exp send "go depth 10\n" expect "bestmove" + send "setoption name UCI_ShowWDL value true\n" + send "position startpos\n" + send "flip\n" + send "go depth 5\n" + expect "bestmove" + + send "setoption name Skill Level value 10\n" + send "position startpos\n" + send "go depth 5\n" + expect "bestmove" + + send "setoption name Clear Hash\n" + + send "setoption name EvalFile value verify.nnue\n" + send "position startpos\n" + send "go depth 5\n" + expect "bestmove" + + send "setoption name MultiPV value 4\n" + send "position startpos\n" + send "go depth 5\n" + send "quit\n" expect eof @@ -128,6 +169,13 @@ cat << EOF > syzygy.exp send "setoption name SyzygyPath value ../tests/syzygy/\n" expect "info string Found 35 tablebases" {} timeout {exit 1} send "bench 128 1 8 default depth\n" + send "ucinewgame\n" + send "position fen 4k3/PP6/8/8/8/8/8/4K3 w - - 0 1\n" + send "go depth 5\n" + expect "bestmove" + send "position fen 8/1P6/2B5/8/4K3/8/6k1/8 w - - 0 1\n" + send "go depth 5\n" + expect "bestmove" send "quit\n" expect eof @@ -146,6 +194,6 @@ do done -rm -f tsan.supp +rm -f tsan.supp bench_tmp.epd echo "instrumented testing OK" From 4be94f41a6119f6d463e13adc6aaf5e02383da63 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 13 Aug 2023 10:59:35 +0200 Subject: [PATCH 1160/1766] Update sanitizer CI to ubuntu 22.04 might fix the tsan errors closes https://github.com/official-stockfish/Stockfish/pull/4745 No functional change --- .github/workflows/stockfish_sanitizers.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/stockfish_sanitizers.yml b/.github/workflows/stockfish_sanitizers.yml index 305b8557bfb..228742b3f12 100644 --- a/.github/workflows/stockfish_sanitizers.yml +++ b/.github/workflows/stockfish_sanitizers.yml @@ -12,8 +12,8 @@ jobs: strategy: matrix: config: - - name: Ubuntu 20.04 GCC - os: ubuntu-20.04 + - name: Ubuntu 22.04 GCC + os: ubuntu-22.04 compiler: g++ comp: gcc shell: bash From d97a02ea2b9328e666aff7a906820c9ec65ab381 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 13 Aug 2023 11:03:28 +0300 Subject: [PATCH 1161/1766] Give extra bonus to main history for moves that caused a fail low. #4744 Current master gives this type of bonuses to continuation history, this patch also gives them to main history. Passed STC: https://tests.stockfishchess.org/tests/view/64d4802a5b17f7c21c0e27b3 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 480768 W: 122767 L: 121798 D: 236203 Ptnml(0-2): 1563, 56190, 123834, 57309, 1488 Passed LTC: https://tests.stockfishchess.org/tests/view/64d7e4c05b17f7c21c0e7456 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 32052 W: 8329 L: 8022 D: 15701 Ptnml(0-2): 19, 3335, 9015, 3634, 23 closes https://github.com/official-stockfish/Stockfish/pull/4744 Bench: 1711793 --- src/search.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/search.cpp b/src/search.cpp index 24f54c32bb3..ce9ed9508aa 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1373,6 +1373,7 @@ namespace { { int bonus = (depth > 5) + (PvNode || cutNode) + (bestValue < alpha - 113 * depth) + ((ss-1)->moveCount > 12); update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); + thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << stat_bonus(depth) * bonus / 2; } if (PvNode) From 222f3ea55bab2414c4c260391ffd14dabc1684df Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Tue, 8 Aug 2023 17:06:31 +0800 Subject: [PATCH 1162/1766] Simplify a depth condition As the negative extension term has sensitive scaling, it would make more sense to allow more negative extension also at lower depth, and not just a region between low and high depth. Passed STC: LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 124096 W: 31611 L: 31485 D: 61000 Ptnml(0-2): 422, 14437, 32205, 14561, 423 https://tests.stockfishchess.org/tests/view/64d205d75b17f7c21c0dea82 Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 387882 W: 97840 L: 97993 D: 192049 Ptnml(0-2): 198, 42004, 109668, 41895, 176 https://tests.stockfishchess.org/tests/view/64d333f85b17f7c21c0e06c6 closes https://github.com/official-stockfish/Stockfish/pull/4743 Bench: 1542357 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index ce9ed9508aa..03d72b95373 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1099,7 +1099,7 @@ namespace { // If we are on a cutNode, reduce it based on depth (negative extension) (~1 Elo) else if (cutNode) - extension = depth > 8 && depth < 17 ? -3 : -1; + extension = depth < 17 ? -3 : -1; // If the eval of ttMove is less than value, we reduce it (negative extension) (~1 Elo) else if (ttValue <= value) From b7b7a3f3fa786449832bd84d501c1183290f3e3a Mon Sep 17 00:00:00 2001 From: "Shahin M. Shahin" <41402573+peregrineshahin@users.noreply.github.com> Date: Thu, 10 Aug 2023 10:19:40 +0300 Subject: [PATCH 1163/1766] Detect repetitions before they happen in qsearch Passed STC: https://tests.stockfishchess.org/tests/view/64d495995b17f7c21c0e29ed LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 340288 W: 86664 L: 85910 D: 167714 Ptnml(0-2): 1030, 38855, 89697, 39455, 1107 Passed LTC: https://tests.stockfishchess.org/tests/view/64d5e1085b17f7c21c0e4ab5 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 193230 W: 49235 L: 48606 D: 95389 Ptnml(0-2): 98, 20432, 54921, 21071, 93 closes https://github.com/official-stockfish/Stockfish/pull/4742 Bench: 1579576 --- src/search.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 03d72b95373..44e13dae8d3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1410,6 +1410,18 @@ namespace { assert(PvNode || (alpha == beta - 1)); assert(depth <= 0); + // Check if we have an upcoming move that draws by repetition, or + // if the opponent had an alternative move earlier to this position. + if ( depth < 0 + && pos.rule50_count() >= 3 + && alpha < VALUE_DRAW + && pos.has_game_cycle(ss->ply)) + { + alpha = value_draw(pos.this_thread()); + if (alpha >= beta) + return alpha; + } + Move pv[MAX_PLY+1]; StateInfo st; ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize); From 84e97a38a3966a38add0f3b07bd011fa707ec5be Mon Sep 17 00:00:00 2001 From: Gabrik <> Date: Fri, 11 Aug 2023 23:54:48 +0200 Subject: [PATCH 1164/1766] Remove the unused enum ScaleFactor closes https://github.com/official-stockfish/Stockfish/pull/4740 No functional change --- AUTHORS | 1 + src/types.h | 7 ------- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/AUTHORS b/AUTHORS index 2f323d64334..5622ca8cec9 100644 --- a/AUTHORS +++ b/AUTHORS @@ -74,6 +74,7 @@ fanon Fauzi Akram Dabat (FauziAkram) Felix Wittmann gamander +Gabriele Lombardo (gabe) Gary Heckman (gheckman) George Sobala (gsobala) gguliash diff --git a/src/types.h b/src/types.h index 5d78377690a..637e1675abf 100644 --- a/src/types.h +++ b/src/types.h @@ -157,13 +157,6 @@ enum Phase { MG = 0, EG = 1, PHASE_NB = 2 }; -enum ScaleFactor { - SCALE_FACTOR_DRAW = 0, - SCALE_FACTOR_NORMAL = 64, - SCALE_FACTOR_MAX = 128, - SCALE_FACTOR_NONE = 255 -}; - enum Bound { BOUND_NONE, BOUND_UPPER, From 796d9df6438b416a63364c7cf5edbc9be5434101 Mon Sep 17 00:00:00 2001 From: ppigazzini Date: Fri, 11 Aug 2023 16:57:26 +0200 Subject: [PATCH 1165/1766] Check compiler for docker builds in CI closes https://github.com/official-stockfish/Stockfish/pull/4739 No functional change --- .github/workflows/stockfish_test.yml | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/stockfish_test.yml b/.github/workflows/stockfish_test.yml index 307d3a02b30..72f0c22e136 100644 --- a/.github/workflows/stockfish_test.yml +++ b/.github/workflows/stockfish_test.yml @@ -134,7 +134,7 @@ jobs: FROM ${{ matrix.config.base_image }} WORKDIR /app RUN apk update && apk add make g++ - CMD sh make_sf.sh + CMD ["sh", "script.sh"] EOF - name: Download required macOS packages @@ -160,10 +160,15 @@ jobs: - name: Check compiler run: | - if [ $COMP == ndk ]; then - export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH + if [ -z "${{ matrix.config.base_image }}" ]; then + if [ $COMP == ndk ]; then + export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH + fi + $COMPILER -v + else + echo "$COMPILER -v" > script.sh + docker run --rm --platform ${{ matrix.config.platform }} -v ${{ github.workspace }}/src:/app sf_builder fi - $COMPILER -v - name: Test help target run: make help @@ -321,7 +326,7 @@ jobs: - name: Test riscv64 build if: matrix.config.run_riscv64_tests run: | - echo "export LDFLAGS='-static' && make clean && make -j2 ARCH=riscv64 build" > make_sf.sh + echo "export LDFLAGS='-static' && make clean && make -j2 ARCH=riscv64 build" > script.sh docker run --rm --platform ${{ matrix.config.platform }} -v ${{ github.workspace }}/src:/app sf_builder ../tests/signature.sh $benchref @@ -330,7 +335,7 @@ jobs: - name: Test ppc64 build if: matrix.config.run_ppc64_tests run: | - echo "export LDFLAGS='-static' && make clean && make -j2 ARCH=ppc-64 build" > make_sf.sh + echo "export LDFLAGS='-static' && make clean && make -j2 ARCH=ppc-64 build" > script.sh docker run --rm --platform ${{ matrix.config.platform }} -v ${{ github.workspace }}/src:/app sf_builder ../tests/signature.sh $benchref From c02ee70927bcb90240f40d8e580e30dc622d5ce9 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Thu, 10 Aug 2023 18:44:58 +0300 Subject: [PATCH 1166/1766] Simplify prior countermove bonus Swapping a multiplication operation between two terms with a simple constant Passed STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 60512 W: 15424 L: 15231 D: 29857 Ptnml(0-2): 200, 6985, 15712, 7140, 219 https://tests.stockfishchess.org/tests/view/64d2addf5b17f7c21c0dfae6 Passed LTC: LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 108456 W: 27545 L: 27414 D: 53497 Ptnml(0-2): 63, 11629, 30698, 11790, 48 https://tests.stockfishchess.org/tests/view/64d3ab6e5b17f7c21c0e1188 closes https://github.com/official-stockfish/Stockfish/pull/4738 Bench: 1636213 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 44e13dae8d3..e51e2f4dae1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1371,7 +1371,7 @@ namespace { // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (depth > 5) + (PvNode || cutNode) + (bestValue < alpha - 113 * depth) + ((ss-1)->moveCount > 12); + int bonus = (depth > 5) + (PvNode || cutNode) + (bestValue < alpha - 800) + ((ss-1)->moveCount > 12); update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << stat_bonus(depth) * bonus / 2; } From 495852fecdd9ce0fe0c1e9c0518f1bc01ccfa239 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicolet?= Date: Thu, 10 Aug 2023 06:31:48 +0200 Subject: [PATCH 1167/1766] Simplify SEE pruning for captures It seems that the current search is smart enough to allow us to remove (again) the block of code that checks for discovered attacks after the first pruning condition for captures. STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 278848 W: 70856 L: 70903 D: 137089 Ptnml(0-2): 960, 32829, 71894, 32780, 961 https://tests.stockfishchess.org/tests/view/64d0af095b17f7c21c0dc440 LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 100704 W: 25564 L: 25425 D: 49715 Ptnml(0-2): 56, 10858, 28381, 11005, 52 https://tests.stockfishchess.org/tests/view/64d293e85b17f7c21c0df844 closes https://github.com/official-stockfish/Stockfish/pull/4736 Bench: 1470572 --- src/search.cpp | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index e51e2f4dae1..970b0f4764b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -989,28 +989,9 @@ namespace { + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 7 < alpha) continue; - Bitboard occupied; - // SEE based pruning (~11 Elo) - if (!pos.see_ge(move, occupied, Value(-205) * depth)) - { - if (depth < 2 - capture) - continue; - // Don't prune the move if opponent Queen/Rook is under discovered attack after the exchanges - // Don't prune the move if opponent King is under discovered attack after or during the exchanges - Bitboard leftEnemies = (pos.pieces(~us, KING, QUEEN, ROOK)) & occupied; - Bitboard attacks = 0; - occupied |= to_sq(move); - while (leftEnemies && !attacks) - { - Square sq = pop_lsb(leftEnemies); - attacks |= pos.attackers_to(sq, occupied) & pos.pieces(us) & occupied; - // Don't consider pieces that were already threatened/hanging before SEE exchanges - if (attacks && (sq != pos.square(~us) && (pos.attackers_to(sq, pos.pieces()) & pos.pieces(us)))) - attacks = 0; - } - if (!attacks) - continue; - } + // SEE based pruning for captures and checks (~11 Elo) + if (!pos.see_ge(move, Value(-205) * depth)) + continue; } else { From 3322349c1a3dbe2f4c42f84141745c4d94efde2e Mon Sep 17 00:00:00 2001 From: "Shahin M. Shahin" <41402573+peregrineshahin@users.noreply.github.com> Date: Mon, 7 Aug 2023 22:27:12 +0300 Subject: [PATCH 1168/1766] Simplify pieceValue to one phase. Simplifies the usage of pieceValues to mg values with the exception of pawnValues, After the removal of PSQT. passed STC: https://tests.stockfishchess.org/tests/view/64d147845b17f7c21c0dd86c LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 197248 W: 50168 L: 50125 D: 96955 Ptnml(0-2): 651, 23029, 51222, 23070, 652 passed LTC: https://tests.stockfishchess.org/tests/view/64d212de5b17f7c21c0debbb LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 181170 W: 45949 L: 45893 D: 89328 Ptnml(0-2): 84, 19656, 51052, 19706, 87 closes https://github.com/official-stockfish/Stockfish/pull/4734 Bench: 1494401 --- src/movepick.cpp | 4 ++-- src/position.cpp | 20 ++++++++++---------- src/search.cpp | 6 +++--- src/syzygy/tbprobe.cpp | 4 ++-- src/types.h | 22 +++++++--------------- 5 files changed, 24 insertions(+), 32 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 4050810338f..9d5805a70a4 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -122,7 +122,7 @@ void MovePicker::score() { for (auto& m : *this) if constexpr (Type == CAPTURES) - m.value = (7 * int(PieceValue[MG][pos.piece_on(to_sq(m))]) + m.value = (7 * int(PieceValue[pos.piece_on(to_sq(m))]) + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]) / 16; else if constexpr (Type == QUIETS) @@ -165,7 +165,7 @@ void MovePicker::score() { else // Type == EVASIONS { if (pos.capture_stage(m)) - m.value = PieceValue[MG][pos.piece_on(to_sq(m))] + m.value = PieceValue[pos.piece_on(to_sq(m))] - Value(type_of(pos.moved_piece(m))) + (1 << 28); else diff --git a/src/position.cpp b/src/position.cpp index 16181e96249..675dec99f04 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -347,7 +347,7 @@ void Position::set_state() const { st->key ^= Zobrist::psq[pc][s]; if (type_of(pc) != KING && type_of(pc) != PAWN) - st->nonPawnMaterial[color_of(pc)] += PieceValue[MG][pc]; + st->nonPawnMaterial[color_of(pc)] += PieceValue[pc]; } if (st->epSquare != SQ_NONE) @@ -742,7 +742,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { } } else - st->nonPawnMaterial[them] -= PieceValue[MG][captured]; + st->nonPawnMaterial[them] -= PieceValue[captured]; dp.dirty_num = 2; // 1 piece moved, 1 piece captured dp.piece[1] = captured; @@ -822,7 +822,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { ^ Zobrist::psq[pc][pieceCount[pc]]; // Update material - st->nonPawnMaterial[us] += PieceValue[MG][promotion]; + st->nonPawnMaterial[us] += PieceValue[promotion]; } // Reset rule 50 draw counter @@ -1048,11 +1048,11 @@ bool Position::see_ge(Move m, Bitboard& occupied, Value threshold) const { Square from = from_sq(m), to = to_sq(m); - int swap = PieceValue[MG][piece_on(to)] - threshold; + int swap = PieceValue[piece_on(to)] - threshold; if (swap < 0) return false; - swap = PieceValue[MG][piece_on(from)] - swap; + swap = PieceValue[piece_on(from)] - swap; if (swap <= 0) return true; @@ -1089,7 +1089,7 @@ bool Position::see_ge(Move m, Bitboard& occupied, Value threshold) const { if ((bb = stmAttackers & pieces(PAWN))) { occupied ^= least_significant_square_bb(bb); - if ((swap = PawnValueMg - swap) < res) + if ((swap = PawnValue - swap) < res) break; attackers |= attacks_bb(to, occupied) & pieces(BISHOP, QUEEN); @@ -1098,14 +1098,14 @@ bool Position::see_ge(Move m, Bitboard& occupied, Value threshold) const { else if ((bb = stmAttackers & pieces(KNIGHT))) { occupied ^= least_significant_square_bb(bb); - if ((swap = KnightValueMg - swap) < res) + if ((swap = KnightValue - swap) < res) break; } else if ((bb = stmAttackers & pieces(BISHOP))) { occupied ^= least_significant_square_bb(bb); - if ((swap = BishopValueMg - swap) < res) + if ((swap = BishopValue - swap) < res) break; attackers |= attacks_bb(to, occupied) & pieces(BISHOP, QUEEN); @@ -1114,7 +1114,7 @@ bool Position::see_ge(Move m, Bitboard& occupied, Value threshold) const { else if ((bb = stmAttackers & pieces(ROOK))) { occupied ^= least_significant_square_bb(bb); - if ((swap = RookValueMg - swap) < res) + if ((swap = RookValue - swap) < res) break; attackers |= attacks_bb(to, occupied) & pieces(ROOK, QUEEN); @@ -1123,7 +1123,7 @@ bool Position::see_ge(Move m, Bitboard& occupied, Value threshold) const { else if ((bb = stmAttackers & pieces(QUEEN))) { occupied ^= least_significant_square_bb(bb); - if ((swap = QueenValueMg - swap) < res) + if ((swap = QueenValue - swap) < res) break; attackers |= (attacks_bb(to, occupied) & pieces(BISHOP, QUEEN)) diff --git a/src/search.cpp b/src/search.cpp index 970b0f4764b..697c8cfed1f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -985,7 +985,7 @@ namespace { if ( !givesCheck && lmrDepth < 7 && !ss->inCheck - && ss->staticEval + 197 + 248 * lmrDepth + PieceValue[EG][pos.piece_on(to_sq(move))] + && ss->staticEval + 197 + 248 * lmrDepth + PieceValue[pos.piece_on(to_sq(move))] + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 7 < alpha) continue; @@ -1535,7 +1535,7 @@ namespace { if (moveCount > 2) continue; - futilityValue = futilityBase + PieceValue[EG][pos.piece_on(to_sq(move))]; + futilityValue = futilityBase + PieceValue[pos.piece_on(to_sq(move))]; if (futilityValue <= alpha) { @@ -1783,7 +1783,7 @@ namespace { // RootMoves are already sorted by score in descending order Value topScore = rootMoves[0].score; - int delta = std::min(topScore - rootMoves[multiPV - 1].score, PawnValueMg); + int delta = std::min(topScore - rootMoves[multiPV - 1].score, PawnValue); int maxScore = -VALUE_INFINITE; double weakness = 120 - 2 * level; diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 9cb0bfdbc0f..56cc016a4d5 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1573,9 +1573,9 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves) { // 1 cp to cursed wins and let it grow to 49 cp as the positions gets // closer to a real win. m.tbScore = r >= bound ? VALUE_MATE - MAX_PLY - 1 - : r > 0 ? Value((std::max( 3, r - (MAX_DTZ - 200)) * int(PawnValueEg)) / 200) + : r > 0 ? Value((std::max( 3, r - (MAX_DTZ - 200)) * int(PawnValue)) / 200) : r == 0 ? VALUE_DRAW - : r > -bound ? Value((std::min(-3, r + (MAX_DTZ - 200)) * int(PawnValueEg)) / 200) + : r > -bound ? Value((std::min(-3, r + (MAX_DTZ - 200)) * int(PawnValue)) / 200) : -VALUE_MATE + MAX_PLY + 1; } diff --git a/src/types.h b/src/types.h index 637e1675abf..34dc42e173e 100644 --- a/src/types.h +++ b/src/types.h @@ -153,10 +153,6 @@ enum CastlingRights { CASTLING_RIGHT_NB = 16 }; -enum Phase { - MG = 0, EG = 1, PHASE_NB = 2 -}; - enum Bound { BOUND_NONE, BOUND_UPPER, @@ -180,11 +176,11 @@ enum Value : int { // In the code, we make the assumption that these values // are such that non_pawn_material() can be used to uniquely // identify the material on the board. - PawnValueMg = 126, PawnValueEg = 208, - KnightValueMg = 781, KnightValueEg = 854, - BishopValueMg = 825, BishopValueEg = 915, - RookValueMg = 1276, RookValueEg = 1380, - QueenValueMg = 2538, QueenValueEg = 2682, + PawnValue = 208, + KnightValue = 781, + BishopValue = 825, + RookValue = 1276, + QueenValue = 2538, }; enum PieceType { @@ -200,12 +196,8 @@ enum Piece { PIECE_NB = 16 }; -constexpr Value PieceValue[PHASE_NB][PIECE_NB] = { - { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, VALUE_ZERO, VALUE_ZERO, - VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg, VALUE_ZERO, VALUE_ZERO }, - { VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg, VALUE_ZERO, VALUE_ZERO, - VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg, VALUE_ZERO, VALUE_ZERO } -}; +constexpr Value PieceValue[PIECE_NB] = { VALUE_ZERO, PawnValue, KnightValue, BishopValue, RookValue, QueenValue, VALUE_ZERO, VALUE_ZERO, + VALUE_ZERO, PawnValue, KnightValue, BishopValue, RookValue, QueenValue, VALUE_ZERO, VALUE_ZERO }; using Depth = int; From 9b80897657bde99cfb6568d8bd3386c3999f22c4 Mon Sep 17 00:00:00 2001 From: mstembera Date: Wed, 9 Aug 2023 11:48:33 -0700 Subject: [PATCH 1169/1766] Simplify material difference in evaluate STC: https://tests.stockfishchess.org/tests/view/64d166235b17f7c21c0ddc15 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 100032 W: 25698 L: 25547 D: 48787 Ptnml(0-2): 308, 11748, 25771, 11863, 326 LTC: https://tests.stockfishchess.org/tests/view/64d28c085b17f7c21c0df775 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 123870 W: 31463 L: 31348 D: 61059 Ptnml(0-2): 63, 13487, 34719, 13604, 62 Besides rebasing I replaced PawnValueMg w/ 126 explicitly to decouple from https://tests.stockfishchess.org/tests/view/64d212de5b17f7c21c0debbb by @peregrineshahin which also passed. #4734 closes https://github.com/official-stockfish/Stockfish/pull/4731 Bench: 1447866 --- src/evaluate.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index c37dd98ad1c..728990680f6 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -152,11 +152,8 @@ Value Eval::evaluate(const Position& pos) { Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); - int material = 67 * (pos.count(stm) - pos.count(~stm)) - + 395 * (pos.count(stm) - pos.count(~stm)) - + 288 * (pos.count(stm) - pos.count(~stm)) - + 630 * (pos.count(stm) - pos.count(~stm)) - + 857 * (pos.count(stm) - pos.count(~stm)); + int material = pos.non_pawn_material(stm) - pos.non_pawn_material(~stm) + + 126 * (pos.count(stm) - pos.count(~stm)); // Blend optimism with nnue complexity and (semi)classical complexity optimism += optimism * (nnueComplexity + abs(material - nnue)) / 512; From a77a8448ffe3736e44a0125eece5d87abf082a60 Mon Sep 17 00:00:00 2001 From: Disservin Date: Sun, 13 Aug 2023 17:14:38 +0200 Subject: [PATCH 1170/1766] Add CONTRIBUTING.md closes https://github.com/official-stockfish/Stockfish/pull/4741 No functional change --- .github/CONTRIBUTING.md | 85 +++++++++++++++++++++++++++++++++++++++++ README.md | 2 + 2 files changed, 87 insertions(+) create mode 100644 .github/CONTRIBUTING.md diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 00000000000..0dff8a9dfb0 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,85 @@ +# Contributing to Stockfish + +Welcome to the Stockfish project! We are excited that you are interested in +contributing. This document outlines the guidelines and steps to follow when +making contributions to Stockfish. + +## Table of Contents + +- [Building Stockfish](#building-stockfish) +- [Making Contributions](#making-contributions) + - [Reporting Issues](#reporting-issues) + - [Submitting Pull Requests](#submitting-pull-requests) +- [Code Style](#code-style) +- [Community and Communication](#community-and-communication) +- [License](#license) + +## Building Stockfish + +In case you do not have a C++ compiler installed, you can follow the +instructions from our wiki. + +- [Linux][linux-compiling-link] +- [Windows][windows-compiling-link] +- [macOS][macos-compiling-link] + +## Making Contributions + +### Reporting Issues + +If you find a bug, please open an issue on the +[issue tracker][issue-tracker-link]. Be sure to include relevant information +like your operating system, build environment, and a detailed description of the +problem. + +_Please note that Stockfish's development is not focused on adding new features. +Thus any issue regarding missing features will potentially be closed without +further discussion._ + +### Submitting Pull Requests + +- Functional changes need to be tested on fishtest. See + [Creating my First Test][creating-my-first-test] for more details. + The accompanying pull request should include a link to the test results and + the new bench. + +- Non-functional changes (e.g. refactoring, code style, documentation) do not + need to be tested on fishtest, unless they might impact performance. + +- Provide a clear and concise description of the changes in the pull request + description. + +_First time contributors should add their name to [AUTHORS](../AUTHORS)._ + +_Stockfish's development is not focused on adding new features. Thus any pull +request introducing new features will potentially be closed without further +discussion._ + +## Code Style + +We do not have a strict code style. But it is best to stick to the existing +style of the file you are editing. + +## Community and Communication + +- Join the [Stockfish discord][discord-link] to discuss ideas, issues, and + development. +- Participate in the [Stockfish GitHub discussions][discussions-link] for + broader conversations. + +## License + +By contributing to Stockfish, you agree that your contributions will be licensed +under the GNU General Public License v3.0. See [Copying.txt][copying-link] for +more details. + +Thank you for contributing to Stockfish and helping us make it even better! + +[copying-link]: https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt +[discord-link]: https://discord.gg/GWDRS3kU6R +[discussions-link]: https://github.com/official-stockfish/Stockfish/discussions/new +[creating-my-first-test]: https://github.com/glinscott/fishtest/wiki/Creating-my-first-test#create-your-test +[issue-tracker-link]: https://github.com/official-stockfish/Stockfish/issues +[linux-compiling-link]: https://github.com/official-stockfish/Stockfish/wiki/Compiling-from-source#linux +[windows-compiling-link]: https://github.com/official-stockfish/Stockfish/wiki/Compiling-from-source#windows +[macos-compiling-link]: https://github.com/official-stockfish/Stockfish/wiki/Compiling-from-source#macos diff --git a/README.md b/README.md index e0e3da394f5..249bff1c04f 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,8 @@ Detailed compilation instructions for all platforms can be found in our ## Contributing +__See [Contributing Guide](./.github/CONTRIBUTING.md).__ + ### Donating hardware Improving Stockfish requires a massive amount of testing. You can donate your From 3e5a817fd243bf395474c88b3bf351e57e56a119 Mon Sep 17 00:00:00 2001 From: SzilBalazs Date: Sun, 13 Aug 2023 17:52:08 +0200 Subject: [PATCH 1171/1766] Fix dead link to compression algorithm in tbprobe closes https://github.com/official-stockfish/Stockfish/pull/4746 No functional change --- AUTHORS | 1 + src/syzygy/tbprobe.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 5622ca8cec9..d20278e1e13 100644 --- a/AUTHORS +++ b/AUTHORS @@ -29,6 +29,7 @@ Aram Tumanian (atumanian) Arjun Temurnikar Artem Solopiy (EntityFX) Auguste Pop +Balazs Szilagyi Balint Pfliegel Ben Chaney (Chaneybenjamini) Ben Koshy (BKSpurgeon) diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 56cc016a4d5..838453b6645 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1023,7 +1023,7 @@ uint8_t* set_sizes(PairsData* d, uint8_t* data) { // frequent adjacent pair of symbols in the source message by a new symbol, // reevaluating the frequencies of all of the symbol pairs with respect to // the extended alphabet, and then repeating the process. - // See http://www.larsson.dogma.net/dcc99.pdf + // See https://web.archive.org/web/20201106232444/http://www.larsson.dogma.net/dcc99.pdf std::vector visited(d->symlen.size()); for (Sym sym = 0; sym < d->symlen.size(); ++sym) From fe0dca12f1207a3a1ecca678d7d13bc533fd5332 Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Sun, 13 Aug 2023 22:02:17 +0800 Subject: [PATCH 1172/1766] Simplify PvNode Reduction Remove the depth condition for PvNode reduction. Simplification STC: https://tests.stockfishchess.org/tests/view/64d308fa5b17f7c21c0e0303 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 38976 W: 10106 L: 9889 D: 18981 Ptnml(0-2): 129, 4479, 10040, 4726, 114 Simplification LTC: https://tests.stockfishchess.org/tests/view/64d457db5b17f7c21c0e236f LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 156402 W: 39727 L: 39645 D: 77030 Ptnml(0-2): 71, 17143, 43696, 17215, 76 closes https://github.com/official-stockfish/Stockfish/pull/4747 Bench: 1493904 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 697c8cfed1f..c7e6fff053c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1136,9 +1136,9 @@ namespace { if (ttCapture) r++; - // Decrease reduction for PvNodes based on depth (~2 Elo) + // Decrease reduction for PvNodes (~2 Elo) if (PvNode) - r -= 1 + (depth < 6); + r--; // Decrease reduction if ttMove has been singularly extended (~1 Elo) if (singularQuietLMR) From 46756996e7884c665da18f357208c2344a0f9374 Mon Sep 17 00:00:00 2001 From: Disservin Date: Mon, 14 Aug 2023 13:49:41 +0200 Subject: [PATCH 1173/1766] Add -funroll-loops to CXXFLAGS Optimize profiling data accuracy by enabling -funroll-loops during the profile generation phase, in addition to its default activation by -fprofile-use. This seems to produce a slightly faster binary, for most compilers. make -j profile-build ARCH=x86-64-avx2 sf_base = 1392875 +/- 5905 (95%) sf_test = 1402332 +/- 7303 (95%) diff = 9457 +/- 4413 (95%) speedup = 0.67896% +/- 0.317% (95%) STC: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 34784 W: 8970 L: 8665 D: 17149 Ptnml(0-2): 115, 3730, 9405, 4019, 123 https://tests.stockfishchess.org/tests/view/64d944815b17f7c21c0e92e1 closes https://github.com/official-stockfish/Stockfish/pull/4750 No functional change --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index c7e059ea925..0a3f8329859 100644 --- a/src/Makefile +++ b/src/Makefile @@ -562,7 +562,7 @@ endif ### 3.3 Optimization ifeq ($(optimize),yes) - CXXFLAGS += -O3 + CXXFLAGS += -O3 -funroll-loops ifeq ($(comp),gcc) ifeq ($(OS), Android) From a9a0dbbcd0749b4e6255c7e9a17f19cffedaa531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Tue, 22 Aug 2023 10:39:03 +0200 Subject: [PATCH 1174/1766] Fix some 'possible loss of data' warnings Patch by Maxim Masiutin closes https://github.com/official-stockfish/Stockfish/pull/4440 No functional change --- src/misc.cpp | 6 +++--- src/syzygy/tbprobe.cpp | 14 ++++++++++---- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index 29ef757e938..922fad96c13 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -375,7 +375,7 @@ void dbg_print() { for (int i = 0; i < MaxDebugSlots; ++i) if ((n = stdev[i][0])) { - double r = sqrtl(E(stdev[i][2]) - sqr(E(stdev[i][1]))); + double r = sqrt(E(stdev[i][2]) - sqr(E(stdev[i][1]))); std::cerr << "Stdev #" << i << ": Total " << n << " Stdev " << r << std::endl; @@ -385,8 +385,8 @@ void dbg_print() { if ((n = correl[i][0])) { double r = (E(correl[i][5]) - E(correl[i][1]) * E(correl[i][3])) - / ( sqrtl(E(correl[i][2]) - sqr(E(correl[i][1]))) - * sqrtl(E(correl[i][4]) - sqr(E(correl[i][3])))); + / ( sqrt(E(correl[i][2]) - sqr(E(correl[i][1]))) + * sqrt(E(correl[i][4]) - sqr(E(correl[i][3])))); std::cerr << "Correl. #" << i << ": Total " << n << " Coefficient " << r << std::endl; diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 838453b6645..ba727825d4c 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -995,13 +995,19 @@ uint8_t* set_sizes(PairsData* d, uint8_t* data) { d->lowestSym = (Sym*)data; d->base64.resize(d->maxSymLen - d->minSymLen + 1); + // See https://en.wikipedia.org/wiki/Huffman_coding // The canonical code is ordered such that longer symbols (in terms of // the number of bits of their Huffman code) have lower numeric value, // so that d->lowestSym[i] >= d->lowestSym[i+1] (when read as LittleEndian). // Starting from this we compute a base64[] table indexed by symbol length // and containing 64 bit values so that d->base64[i] >= d->base64[i+1]. - // See https://en.wikipedia.org/wiki/Huffman_coding - for (int i = d->base64.size() - 2; i >= 0; --i) { + + // Implementation note: we first cast the unsigned size_t "base64.size()" + // to a signed int "base64_size" variable and then we are able to subtract 2, + // avoiding unsigned overflow warnings. + + int base64_size = static_cast(d->base64.size()); + for (int i = base64_size - 2; i >= 0; --i) { d->base64[i] = (d->base64[i + 1] + number(&d->lowestSym[i]) - number(&d->lowestSym[i + 1])) / 2; @@ -1012,10 +1018,10 @@ uint8_t* set_sizes(PairsData* d, uint8_t* data) { // than d->base64[i+1] and given the above assert condition, we ensure that // d->base64[i] >= d->base64[i+1]. Moreover for any symbol s64 of length i // and right-padded to 64 bits holds d->base64[i-1] >= s64 >= d->base64[i]. - for (size_t i = 0; i < d->base64.size(); ++i) + for (int i = 0; i < base64_size; ++i) d->base64[i] <<= 64 - i - d->minSymLen; // Right-padding to 64 bits - data += d->base64.size() * sizeof(Sym); + data += base64_size * sizeof(Sym); d->symlen.resize(number(data)); data += sizeof(uint16_t); d->btree = (LR*)data; From 9abef246a9ce7c17a21c2cc0d609dc61ddc5be67 Mon Sep 17 00:00:00 2001 From: Matthies Date: Wed, 16 Aug 2023 11:11:27 +0200 Subject: [PATCH 1175/1766] Allow compilation on Raspi (for ARMv8) Current master fails to compile for ARMv8 on Raspi cause gcc (version 10.2.1) does not like to cast between signed and unsigned vector types. This patch fixes it by using unsigned vector pointer for ARM to avoid implicite cast. closes https://github.com/official-stockfish/Stockfish/pull/4752 No functional change --- src/nnue/layers/affine_transform_sparse_input.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nnue/layers/affine_transform_sparse_input.h b/src/nnue/layers/affine_transform_sparse_input.h index 63cbaf45a34..2cd77e49933 100644 --- a/src/nnue/layers/affine_transform_sparse_input.h +++ b/src/nnue/layers/affine_transform_sparse_input.h @@ -72,10 +72,10 @@ namespace Stockfish::Eval::NNUE::Layers { #define vec128_storeu(a, b) _mm_storeu_si128(a, b) #define vec128_add(a, b) _mm_add_epi16(a, b) #elif defined (USE_NEON) - using vec_t = int32x4_t; + using vec_t = uint32x4_t; static const std::uint32_t Mask[4] = {1, 2, 4, 8}; #define vec_nnz(a) vaddvq_u32(vandq_u32(vtstq_u32(a, a), vld1q_u32(Mask))) - using vec128_t = int16x8_t; + using vec128_t = uint16x8_t; #define vec128_zero vdupq_n_u16(0) #define vec128_set_16(a) vdupq_n_u16(a) #define vec128_load(a) vld1q_u16(reinterpret_cast(a)) From fe7353f7027e2d49b7ffb60b01d88ce0d3b04fff Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 21 Aug 2023 22:45:26 +0200 Subject: [PATCH 1176/1766] Update links to fishtest Fishtest has moved to https://github.com/official-stockfish/fishtest/ closes https://github.com/official-stockfish/Stockfish/pull/4758 No functional change --- .github/CONTRIBUTING.md | 15 ++++++++------- AUTHORS | 2 +- README.md | 4 ++-- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 0dff8a9dfb0..7667a942325 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -75,11 +75,12 @@ more details. Thank you for contributing to Stockfish and helping us make it even better! -[copying-link]: https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt -[discord-link]: https://discord.gg/GWDRS3kU6R -[discussions-link]: https://github.com/official-stockfish/Stockfish/discussions/new -[creating-my-first-test]: https://github.com/glinscott/fishtest/wiki/Creating-my-first-test#create-your-test -[issue-tracker-link]: https://github.com/official-stockfish/Stockfish/issues -[linux-compiling-link]: https://github.com/official-stockfish/Stockfish/wiki/Compiling-from-source#linux + +[copying-link]: https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt +[discord-link]: https://discord.gg/GWDRS3kU6R +[discussions-link]: https://github.com/official-stockfish/Stockfish/discussions/new +[creating-my-first-test]: https://github.com/official-stockfish/fishtest/wiki/Creating-my-first-test#create-your-test +[issue-tracker-link]: https://github.com/official-stockfish/Stockfish/issues +[linux-compiling-link]: https://github.com/official-stockfish/Stockfish/wiki/Compiling-from-source#linux [windows-compiling-link]: https://github.com/official-stockfish/Stockfish/wiki/Compiling-from-source#windows -[macos-compiling-link]: https://github.com/official-stockfish/Stockfish/wiki/Compiling-from-source#macos +[macos-compiling-link]: https://github.com/official-stockfish/Stockfish/wiki/Compiling-from-source#macos diff --git a/AUTHORS b/AUTHORS index d20278e1e13..9314f5cbd33 100644 --- a/AUTHORS +++ b/AUTHORS @@ -230,4 +230,4 @@ zz4032 # Additionally, we acknowledge the authors and maintainers of fishtest, # an amazing and essential framework for Stockfish development! # -# https://github.com/glinscott/fishtest/blob/master/AUTHORS +# https://github.com/official-stockfish/fishtest/blob/master/AUTHORS diff --git a/README.md b/README.md index 249bff1c04f..4d63b71e35b 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ also be made available under GPL v3. [issue-link]: https://github.com/official-stockfish/Stockfish/issues/new?assignees=&labels=&template=BUG-REPORT.yml [discussions-link]: https://github.com/official-stockfish/Stockfish/discussions/new [fishtest-link]: https://tests.stockfishchess.org/tests -[guideline-link]: https://github.com/glinscott/fishtest/wiki/Creating-my-first-test +[guideline-link]: https://github.com/official-stockfish/fishtest/wiki/Creating-my-first-test [license-link]: https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt [programming-link]: https://www.chessprogramming.org/Main_Page [programmingsf-link]: https://www.chessprogramming.org/Stockfish @@ -155,7 +155,7 @@ also be made available under GPL v3. [wiki-usage-link]: https://github.com/official-stockfish/Stockfish/wiki/Download-and-usage [wiki-compile-link]: https://github.com/official-stockfish/Stockfish/wiki/Compiling-from-source [wiki-commands-link]: https://github.com/official-stockfish/Stockfish/wiki/Commands -[worker-link]: https://github.com/glinscott/fishtest/wiki/Running-the-worker +[worker-link]: https://github.com/official-stockfish/fishtest/wiki/Running-the-worker [build-badge]: https://img.shields.io/github/actions/workflow/status/official-stockfish/Stockfish/stockfish.yml?branch=master&style=for-the-badge&label=stockfish&logo=github [commits-badge]: https://img.shields.io/github/commits-since/official-stockfish/Stockfish/latest?style=for-the-badge From 4c5919fa95d543b1cd5d0403f3a89e10a2bdd10c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Tue, 22 Aug 2023 10:00:03 +0200 Subject: [PATCH 1177/1766] Fix some tabs in Makefile Avoid mixing spaces and tabs for indentation in Makefile closes https://github.com/official-stockfish/Stockfish/pull/4759 No functional change --- src/Makefile | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Makefile b/src/Makefile index 0a3f8329859..d6443845477 100644 --- a/src/Makefile +++ b/src/Makefile @@ -895,33 +895,33 @@ netvariables: net: netvariables @echo "Default net: $(nnuenet)" @if [ "x$(curl_or_wget)" = "x" ]; then \ - echo "Neither curl nor wget is installed. Install one of these tools unless the net has been downloaded manually"; \ + echo "Neither curl nor wget is installed. Install one of these tools unless the net has been downloaded manually"; \ fi @if [ "x$(shasum_command)" = "x" ]; then \ - echo "shasum / sha256sum not found, skipping net validation"; \ - elif test -f "$(nnuenet)"; then \ - if [ "$(nnuenet)" != "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then \ - echo "Removing invalid network"; rm -f $(nnuenet); \ - fi; \ + echo "shasum / sha256sum not found, skipping net validation"; \ + elif test -f "$(nnuenet)"; then \ + if [ "$(nnuenet)" != "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then \ + echo "Removing invalid network"; rm -f $(nnuenet); \ + fi; \ fi; @for nnuedownloadurl in "$(nnuedownloadurl1)" "$(nnuedownloadurl2)"; do \ if test -f "$(nnuenet)"; then \ - echo "$(nnuenet) available : OK"; break; \ + echo "$(nnuenet) available : OK"; break; \ else \ - if [ "x$(curl_or_wget)" != "x" ]; then \ + if [ "x$(curl_or_wget)" != "x" ]; then \ echo "Downloading $${nnuedownloadurl}"; $(curl_or_wget) $${nnuedownloadurl} > $(nnuenet);\ else \ echo "No net found and download not possible"; exit 1;\ - fi; \ + fi; \ fi; \ if [ "x$(shasum_command)" != "x" ]; then \ - if [ "$(nnuenet)" != "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then \ + if [ "$(nnuenet)" != "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then \ echo "Removing failed download"; rm -f $(nnuenet); \ - fi; \ + fi; \ fi; \ done @if ! test -f "$(nnuenet)"; then \ - echo "Failed to download $(nnuenet)."; \ + echo "Failed to download $(nnuenet)."; \ fi; @if [ "x$(shasum_command)" != "x" ]; then \ if [ "$(nnuenet)" = "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then \ From c6f62363a657263a567a0cc9bae09f3c4016156d Mon Sep 17 00:00:00 2001 From: Gian-Carlo Pascutto Date: Mon, 14 Aug 2023 17:30:10 +0200 Subject: [PATCH 1178/1766] Simplify Square Clipped ReLU code. Squared numbers are never negative, so barring any wraparound there is no need to clamp to 0. From reading the code, there's no obvious way to get wraparound, so the entire operation can be simplified away. Updated original truncated code comments to be sensible. Verified by running ./stockfish bench 128 1 24 and by the following test: STC: https://tests.stockfishchess.org/tests/view/64da4db95b17f7c21c0eabe7 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 60224 W: 15425 L: 15236 D: 29563 Ptnml(0-2): 195, 6576, 16382, 6763, 196 closes https://github.com/official-stockfish/Stockfish/pull/4751 No functional change --- src/nnue/layers/sqr_clipped_relu.h | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/src/nnue/layers/sqr_clipped_relu.h b/src/nnue/layers/sqr_clipped_relu.h index 69bd51471d7..5c1b9e6cd06 100644 --- a/src/nnue/layers/sqr_clipped_relu.h +++ b/src/nnue/layers/sqr_clipped_relu.h @@ -65,12 +65,6 @@ namespace Stockfish::Eval::NNUE::Layers { #if defined(USE_SSE2) constexpr IndexType NumChunks = InputDimensions / 16; - #ifdef USE_SSE41 - const __m128i Zero = _mm_setzero_si128(); - #else - const __m128i k0x80s = _mm_set1_epi8(-128); - #endif - static_assert(WeightScaleBits == 6); const auto in = reinterpret_cast(input); const auto out = reinterpret_cast<__m128i*>(output); @@ -82,21 +76,13 @@ namespace Stockfish::Eval::NNUE::Layers { _mm_load_si128(&in[i * 4 + 2]), _mm_load_si128(&in[i * 4 + 3])); - // Not sure if + // We shift by WeightScaleBits * 2 = 12 and divide by 128 + // which is an additional shift-right of 7, meaning 19 in total. + // MulHi strips the lower 16 bits so we need to shift out 3 more to match. words0 = _mm_srli_epi16(_mm_mulhi_epi16(words0, words0), 3); words1 = _mm_srli_epi16(_mm_mulhi_epi16(words1, words1), 3); - const __m128i packedbytes = _mm_packs_epi16(words0, words1); - - _mm_store_si128(&out[i], - - #ifdef USE_SSE41 - _mm_max_epi8(packedbytes, Zero) - #else - _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s) - #endif - - ); + _mm_store_si128(&out[i], _mm_packs_epi16(words0, words1)); } constexpr IndexType Start = NumChunks * 16; @@ -108,7 +94,7 @@ namespace Stockfish::Eval::NNUE::Layers { output[i] = static_cast( // really should be /127 but we need to make it fast // needs to be accounted for in the trainer - std::max(0ll, std::min(127ll, (((long long)input[i] * input[i]) >> (2 * WeightScaleBits)) / 128))); + std::min(127ll, (((long long)input[i] * input[i]) >> (2 * WeightScaleBits)) / 128)); } } }; From 030b87182a7fff98b1724b857d6d40cda5d90b9f Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Sat, 12 Aug 2023 14:56:23 +0800 Subject: [PATCH 1179/1766] Do more full window searches Remove the value < beta condition for doing full window searches. As an added bonus the condition for full-window search is now much more similar to other fail-soft engines. Passed STC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 244608 W: 62286 L: 62294 D: 120028 Ptnml(0-2): 758, 28772, 63214, 28840, 720 https://tests.stockfishchess.org/tests/view/64d72d365b17f7c21c0e6675 Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 311460 W: 78909 L: 78985 D: 153566 Ptnml(0-2): 129, 33959, 87656, 33831, 155 https://tests.stockfishchess.org/tests/view/64dca2265b17f7c21c0ee06c closes https://github.com/official-stockfish/Stockfish/pull/4755 Bench: 1624221 --- src/search.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c7e6fff053c..d911593c031 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1211,10 +1211,9 @@ namespace { value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth - (r > 3), !cutNode); } - // 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. - if (PvNode && (moveCount == 1 || (value > alpha && (rootNode || value < beta)))) + // For PV nodes only, do a full PV search on the first move or after a fail high, + // otherwise let the parent node fail low with value <= alpha and try another move. + if (PvNode && (moveCount == 1 || value > alpha)) { (ss+1)->pv = pv; (ss+1)->pv[0] = MOVE_NONE; From 440feecb4da2f051da6dafb70b4eb6cf443ccc1e Mon Sep 17 00:00:00 2001 From: "Shahin M. Shahin" <41402573+peregrineshahin@users.noreply.github.com> Date: Sun, 20 Aug 2023 01:15:22 +0300 Subject: [PATCH 1180/1766] Reduce repetitions branches Increase reduction on retrying a move we just retreated that falls in a repetition: if current move can be the same move from previous previous turn then we retreated that move on the previous turn, this patch increases reduction if retrying that move results in a repetition. How to continue from there? Maybe we some variants of this idea could bring Elo too (only testing the destination square, or triangulations, etc.) Passed STC: https://tests.stockfishchess.org/tests/view/64e1aede883cbb7cbd9ad976 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 424000 W: 108675 L: 107809 D: 207516 Ptnml(0-2): 1296, 47350, 113896, 48108, 1350 Passed LTC: https://tests.stockfishchess.org/tests/view/64e32d629970091252666872 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 89682 W: 22976 L: 22569 D: 44137 Ptnml(0-2): 39, 8843, 26675, 9240, 44 closes https://github.com/official-stockfish/Stockfish/pull/4757 bench: 1574347 --- src/search.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index d911593c031..d9b41cb35c4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1143,6 +1143,11 @@ namespace { // Decrease reduction if ttMove has been singularly extended (~1 Elo) if (singularQuietLMR) r--; + + // Increase reduction on repetition (~1 Elo) + if ( move == (ss-4)->currentMove + && pos.has_repeated()) + r += 2; // Increase reduction if next ply has a lot of fail high (~5 Elo) if ((ss+1)->cutoffCnt > 3) From 4c4cb185aaaa0b3175ca35ab6473f17e9ec64055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Wed, 23 Aug 2023 07:50:36 +0200 Subject: [PATCH 1181/1766] Play turbulent when defending, simpler when attacking This patch decays a little the evaluation (up to a few percent) for positions which have a large complexity measure (material imbalance, positional compensations, etc). This may have nice consequences on the playing style, as it modifies the search differently for attack and defense, both effects being desirable: - to see the effect on positions when Stockfish is defending, let us suppose for instance that the side to move is Stockfish and the nnue evaluation on the principal variation is -100 : this patch will decay positions with an evaluation of -103 (say) to the same level, provided they have huge material imbalance or huge positional compensation. In other words, chaotic positions with an evaluation of -103 are now comparable in our search tree to stable positions with an evaluation of -100, and chaotic positions with an evaluation of -102 are now preferred to stable positions with an evaluation of -100. - the effect on positions when Stockfish is attacking is the opposite. Let us suppose for instance that the side to move is Stockfish and the nnue evaluation on the principal variation is +100 : this patch will decay the evaluation to +97 if the positions on the principal variation have huge material imbalance or huge positional compensation. In other words, stable positions with an evaluation of +97 are now comparable in our search tree to chaotic positions with an evaluation of +100, and stable positions with an evaluation of +98 are now preferred to chaotic positions with an evaluation of +100. So the effect of this small change of evaluation on the playing style is that Stockfish should now play a little bit more turbulent when defending, and choose slightly simpler lines when attacking. passed STC: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 268448 W: 68713 L: 68055 D: 131680 Ptnml(0-2): 856, 31514, 68943, 31938, 973 https://tests.stockfishchess.org/tests/view/64e252bb99700912526653ed passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 141060 W: 36066 L: 35537 D: 69457 Ptnml(0-2): 71, 15179, 39522, 15666, 92 https://tests.stockfishchess.org/tests/view/64e4447a9009777747553725 closes https://github.com/official-stockfish/Stockfish/pull/4762 Bench: 1426295 --- src/evaluate.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 728990680f6..54216b97206 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -155,8 +155,9 @@ Value Eval::evaluate(const Position& pos) { int material = pos.non_pawn_material(stm) - pos.non_pawn_material(~stm) + 126 * (pos.count(stm) - pos.count(~stm)); - // Blend optimism with nnue complexity and (semi)classical complexity + // Blend optimism and eval with nnue complexity and material imbalance optimism += optimism * (nnueComplexity + abs(material - nnue)) / 512; + nnue -= nnue * (nnueComplexity + abs(material - nnue)) / 32768; v = ( nnue * (915 + npm + 9 * pos.count()) + optimism * (154 + npm + pos.count())) / 1024; From 8cd5cbf6939d76b33a744f1379a6f84a4ac3a6cb Mon Sep 17 00:00:00 2001 From: ttruscott Date: Fri, 25 Aug 2023 15:47:52 -0400 Subject: [PATCH 1182/1766] Omit two unneeded tests These redundant tests were intended as a speed-up, but they do not seem to provide any speed anymore. STC: https://tests.stockfishchess.org/tests/view/64e9079c85e3e95030fd8259 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 134688 W: 34338 L: 34226 D: 66124 Ptnml(0-2): 426, 15122, 36124, 15258, 414 closes https://github.com/official-stockfish/Stockfish/pull/4767 No functional change --- src/search.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index d9b41cb35c4..18e4aa56df2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -518,7 +518,6 @@ namespace { // Check if we have an upcoming move that draws by repetition, or // if the opponent had an alternative move earlier to this position. if ( !rootNode - && pos.rule50_count() >= 3 && alpha < VALUE_DRAW && pos.has_game_cycle(ss->ply)) { @@ -1398,7 +1397,6 @@ namespace { // Check if we have an upcoming move that draws by repetition, or // if the opponent had an alternative move earlier to this position. if ( depth < 0 - && pos.rule50_count() >= 3 && alpha < VALUE_DRAW && pos.has_game_cycle(ss->ply)) { From 3c0e86a91e48baea273306e45fb6cf13a59373cf Mon Sep 17 00:00:00 2001 From: Disservin Date: Wed, 23 Aug 2023 19:36:55 +0200 Subject: [PATCH 1183/1766] Cleanup includes Reorder a few includes, include "position.h" where it was previously missing and apply include-what-you-use suggestions. Also make the order of the includes consistent, in the following way: 1. Related header (for .cpp files) 2. A blank line 3. C/C++ headers 4. A blank line 5. All other header files closes https://github.com/official-stockfish/Stockfish/pull/4763 fixes https://github.com/official-stockfish/Stockfish/issues/4707 No functional change --- src/benchmark.cpp | 2 +- src/bitboard.cpp | 4 +++- src/bitboard.h | 5 +++++ src/evaluate.cpp | 15 ++++++++------- src/evaluate.h | 4 +--- src/main.cpp | 7 +++++-- src/misc.cpp | 11 ++++++----- src/misc.h | 8 +++----- src/movegen.cpp | 5 ++++- src/movegen.h | 1 + src/movepick.cpp | 9 +++++++-- src/movepick.h | 5 ++++- src/nnue/evaluate_nnue.cpp | 15 ++++++++++----- src/nnue/evaluate_nnue.h | 13 ++++++++++++- src/nnue/features/half_ka_v2_hm.cpp | 3 +++ src/nnue/features/half_ka_v2_hm.h | 6 ++++-- src/nnue/layers/affine_transform.h | 4 ++-- .../layers/affine_transform_sparse_input.h | 6 ++++-- src/nnue/layers/clipped_relu.h | 4 ++++ src/nnue/layers/sqr_clipped_relu.h | 4 ++++ src/nnue/nnue_accumulator.h | 3 +++ src/nnue/nnue_architecture.h | 12 +++++------- src/nnue/nnue_common.h | 6 +++++- src/nnue/nnue_feature_transformer.h | 15 +++++++++++---- src/position.cpp | 15 +++++++++++---- src/position.h | 7 +++---- src/search.cpp | 19 ++++++++++++++----- src/search.h | 1 + src/syzygy/tbprobe.cpp | 19 ++++++++++++------- src/syzygy/tbprobe.h | 6 ++++++ src/thread.cpp | 16 ++++++++++++---- src/thread.h | 4 +++- src/timeman.cpp | 4 ++-- src/timeman.h | 3 +++ src/tt.cpp | 9 ++++++--- src/tt.h | 3 +++ src/tune.cpp | 11 ++++++++--- src/tune.h | 3 +++ src/types.h | 3 --- src/uci.cpp | 17 ++++++++++++----- src/uci.h | 2 ++ src/ucioption.cpp | 9 ++++++++- 42 files changed, 224 insertions(+), 94 deletions(-) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index e340ebcd309..f3401c61c8b 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -18,9 +18,9 @@ #include "benchmark.h" +#include #include #include -#include #include #include "position.h" diff --git a/src/bitboard.cpp b/src/bitboard.cpp index fd5c3c22536..bed2b3ee309 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -16,10 +16,12 @@ along with this program. If not, see . */ +#include "bitboard.h" + #include #include +#include -#include "bitboard.h" #include "misc.h" namespace Stockfish { diff --git a/src/bitboard.h b/src/bitboard.h index 244dc034c33..f9175333745 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -19,6 +19,11 @@ #ifndef BITBOARD_H_INCLUDED #define BITBOARD_H_INCLUDED +#include +#include +#include +#include +#include #include #include "types.h" diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 54216b97206..25a6545564a 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -16,23 +16,24 @@ along with this program. If not, see . */ +#include "evaluate.h" + #include #include +#include #include #include -#include #include -#include +#include #include -#include "bitboard.h" -#include "evaluate.h" +#include "incbin/incbin.h" #include "misc.h" +#include "nnue/evaluate_nnue.h" +#include "position.h" #include "thread.h" -#include "timeman.h" +#include "types.h" #include "uci.h" -#include "incbin/incbin.h" -#include "nnue/evaluate_nnue.h" // Macro to embed the default efficiently updatable neural network (NNUE) file // data in the engine binary (using incbin.h, by Dale Weiler). diff --git a/src/evaluate.h b/src/evaluate.h index 586e3b81f04..a222da7361a 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -20,13 +20,11 @@ #define EVALUATE_H_INCLUDED #include -#include - -#include "types.h" namespace Stockfish { class Position; +enum Value : int; namespace Eval { diff --git a/src/main.cpp b/src/main.cpp index c854ac0cd16..eee149fb455 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,14 +16,17 @@ along with this program. If not, see . */ +#include #include #include "bitboard.h" +#include "evaluate.h" +#include "misc.h" #include "position.h" #include "search.h" -#include "syzygy/tbprobe.h" #include "thread.h" -#include "tt.h" +#include "tune.h" +#include "types.h" #include "uci.h" using namespace Stockfish; diff --git a/src/misc.cpp b/src/misc.cpp index 922fad96c13..42083e0a94f 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -16,6 +16,8 @@ along with this program. If not, see . */ +#include "misc.h" + #ifdef _WIN32 #if _WIN32_WINNT < 0x0601 #undef _WIN32_WINNT @@ -44,17 +46,19 @@ using fun8_t = bool(*)(HANDLE, BOOL, PTOKEN_PRIVILEGES, DWORD, PTOKEN_PRIVILEGES } #endif +#include #include #include #include #include #include +#include #include #include -#include + +#include "types.h" #if defined(__linux__) && !defined(__ANDROID__) -#include #include #endif @@ -63,9 +67,6 @@ using fun8_t = bool(*)(HANDLE, BOOL, PTOKEN_PRIVILEGES, DWORD, PTOKEN_PRIVILEGES #include #endif -#include "misc.h" -#include "thread.h" - using namespace std; namespace Stockfish { diff --git a/src/misc.h b/src/misc.h index 0005fc0fb09..aed677b5b29 100644 --- a/src/misc.h +++ b/src/misc.h @@ -21,12 +21,10 @@ #include #include -#include -#include -#include +#include #include - -#include "types.h" +#include +#include #define stringify2(x) #x #define stringify(x) stringify2(x) diff --git a/src/movegen.cpp b/src/movegen.cpp index 6b28a52ecf0..f0733c73b66 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -16,9 +16,12 @@ along with this program. If not, see . */ +#include "movegen.h" + #include +#include -#include "movegen.h" +#include "bitboard.h" #include "position.h" namespace Stockfish { diff --git a/src/movegen.h b/src/movegen.h index b8df3e65d5c..6449de25794 100644 --- a/src/movegen.h +++ b/src/movegen.h @@ -20,6 +20,7 @@ #define MOVEGEN_H_INCLUDED #include +#include #include "types.h" diff --git a/src/movepick.cpp b/src/movepick.cpp index 9d5805a70a4..d4f8ab092a8 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -16,10 +16,15 @@ along with this program. If not, see . */ +#include "movepick.h" + +#include #include +#include +#include #include "bitboard.h" -#include "movepick.h" +#include "position.h" namespace Stockfish { @@ -161,7 +166,7 @@ void MovePicker::score() { : 0 ) : 0 ; } - + else // Type == EVASIONS { if (pos.capture_stage(m)) diff --git a/src/movepick.h b/src/movepick.h index 0b44557f198..5243f89cf2c 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -20,14 +20,17 @@ #define MOVEPICK_H_INCLUDED #include +#include +#include +#include #include #include #include "movegen.h" -#include "position.h" #include "types.h" namespace Stockfish { +class Position; /// StatsEntry stores the stat table value. It is usually a number but could /// be a move or even a nested history. We use a class instead of naked value diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index cff1d0243db..456f2edfdf3 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -18,19 +18,24 @@ // Code for calculating NNUE evaluation function +#include "evaluate_nnue.h" + +#include +#include +#include #include #include #include -#include #include #include #include "../evaluate.h" +#include "../misc.h" #include "../position.h" -#include "../uci.h" #include "../types.h" - -#include "evaluate_nnue.h" +#include "../uci.h" +#include "nnue_accumulator.h" +#include "nnue_common.h" namespace Stockfish::Eval::NNUE { @@ -251,7 +256,7 @@ namespace Stockfish::Eval::NNUE { // format_cp_aligned_dot() converts a Value into pawns, always keeping two decimals static void format_cp_aligned_dot(Value v, std::stringstream &stream) { - + const double pawns = std::abs(0.01 * UCI::to_cp(v)); stream << (v < 0 ? '-' : v > 0 ? '+' : ' ') diff --git a/src/nnue/evaluate_nnue.h b/src/nnue/evaluate_nnue.h index b84bed8b90d..8faec6cce43 100644 --- a/src/nnue/evaluate_nnue.h +++ b/src/nnue/evaluate_nnue.h @@ -21,9 +21,20 @@ #ifndef NNUE_EVALUATE_NNUE_H_INCLUDED #define NNUE_EVALUATE_NNUE_H_INCLUDED +#include +#include +#include +#include +#include + +#include "../misc.h" +#include "nnue_architecture.h" #include "nnue_feature_transformer.h" -#include +namespace Stockfish { + class Position; + enum Value : int; +} namespace Stockfish::Eval::NNUE { diff --git a/src/nnue/features/half_ka_v2_hm.cpp b/src/nnue/features/half_ka_v2_hm.cpp index 19ebb15fca4..016934b8c4d 100644 --- a/src/nnue/features/half_ka_v2_hm.cpp +++ b/src/nnue/features/half_ka_v2_hm.cpp @@ -20,7 +20,10 @@ #include "half_ka_v2_hm.h" +#include "../../bitboard.h" #include "../../position.h" +#include "../../types.h" +#include "../nnue_common.h" namespace Stockfish::Eval::NNUE::Features { diff --git a/src/nnue/features/half_ka_v2_hm.h b/src/nnue/features/half_ka_v2_hm.h index 78063c3684c..9da1cc05531 100644 --- a/src/nnue/features/half_ka_v2_hm.h +++ b/src/nnue/features/half_ka_v2_hm.h @@ -21,13 +21,15 @@ #ifndef NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED #define NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED -#include "../nnue_common.h" +#include -#include "../../evaluate.h" #include "../../misc.h" +#include "../../types.h" +#include "../nnue_common.h" namespace Stockfish { struct StateInfo; + class Position; } namespace Stockfish::Eval::NNUE::Features { diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index c936a83ed66..e9d0beace17 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -21,9 +21,9 @@ #ifndef NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED #define NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED +#include #include -#include -#include + #include "../nnue_common.h" #include "simd.h" diff --git a/src/nnue/layers/affine_transform_sparse_input.h b/src/nnue/layers/affine_transform_sparse_input.h index 2cd77e49933..c9894f5d96e 100644 --- a/src/nnue/layers/affine_transform_sparse_input.h +++ b/src/nnue/layers/affine_transform_sparse_input.h @@ -21,10 +21,12 @@ #ifndef NNUE_LAYERS_AFFINE_TRANSFORM_SPARSE_INPUT_H_INCLUDED #define NNUE_LAYERS_AFFINE_TRANSFORM_SPARSE_INPUT_H_INCLUDED -#include #include #include -#include +#include +#include + +#include "../../bitboard.h" #include "../nnue_common.h" #include "affine_transform.h" #include "simd.h" diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index d5aa6fbfbd1..2856bfb0a63 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -21,6 +21,10 @@ #ifndef NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED #define NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED +#include +#include +#include + #include "../nnue_common.h" namespace Stockfish::Eval::NNUE::Layers { diff --git a/src/nnue/layers/sqr_clipped_relu.h b/src/nnue/layers/sqr_clipped_relu.h index 5c1b9e6cd06..503b283b25e 100644 --- a/src/nnue/layers/sqr_clipped_relu.h +++ b/src/nnue/layers/sqr_clipped_relu.h @@ -21,6 +21,10 @@ #ifndef NNUE_LAYERS_SQR_CLIPPED_RELU_H_INCLUDED #define NNUE_LAYERS_SQR_CLIPPED_RELU_H_INCLUDED +#include +#include +#include + #include "../nnue_common.h" namespace Stockfish::Eval::NNUE::Layers { diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index 8eba4497464..03fc3bd5cd8 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -21,7 +21,10 @@ #ifndef NNUE_ACCUMULATOR_H_INCLUDED #define NNUE_ACCUMULATOR_H_INCLUDED +#include + #include "nnue_architecture.h" +#include "nnue_common.h" namespace Stockfish::Eval::NNUE { diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index 65319b14bde..b50c52df31f 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -21,18 +21,16 @@ #ifndef NNUE_ARCHITECTURE_H_INCLUDED #define NNUE_ARCHITECTURE_H_INCLUDED -#include - -#include "nnue_common.h" +#include +#include +#include #include "features/half_ka_v2_hm.h" - -#include "layers/affine_transform_sparse_input.h" #include "layers/affine_transform.h" +#include "layers/affine_transform_sparse_input.h" #include "layers/clipped_relu.h" #include "layers/sqr_clipped_relu.h" - -#include "../misc.h" +#include "nnue_common.h" namespace Stockfish::Eval::NNUE { diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index e8ed2bc68e7..a42a86c980d 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -21,10 +21,14 @@ #ifndef NNUE_COMMON_H_INCLUDED #define NNUE_COMMON_H_INCLUDED +#include +#include +#include #include #include +#include -#include "../misc.h" // for IsLittleEndian +#include "../misc.h" #if defined(USE_AVX2) #include diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 7571f398295..0af0ed96cc5 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -21,11 +21,18 @@ #ifndef NNUE_FEATURE_TRANSFORMER_H_INCLUDED #define NNUE_FEATURE_TRANSFORMER_H_INCLUDED -#include "nnue_common.h" +#include +#include +#include +#include +#include +#include + +#include "../position.h" +#include "../types.h" +#include "nnue_accumulator.h" #include "nnue_architecture.h" - -#include // std::memset() -#include // std::pair +#include "nnue_common.h" namespace Stockfish::Eval::NNUE { diff --git a/src/position.cpp b/src/position.cpp index 675dec99f04..0f15727d2ca 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -16,22 +16,29 @@ along with this program. If not, see . */ +#include "position.h" + #include +#include #include -#include // For offsetof() -#include // For std::memset, std::memcmp +#include +#include +#include +#include #include +#include #include #include +#include #include "bitboard.h" #include "misc.h" #include "movegen.h" -#include "position.h" +#include "nnue/nnue_common.h" +#include "syzygy/tbprobe.h" #include "thread.h" #include "tt.h" #include "uci.h" -#include "syzygy/tbprobe.h" using std::string; diff --git a/src/position.h b/src/position.h index 393c1ac9226..f0546af33f7 100644 --- a/src/position.h +++ b/src/position.h @@ -21,14 +21,13 @@ #include #include -#include // For std::unique_ptr +#include +#include #include #include "bitboard.h" -#include "evaluate.h" -#include "types.h" - #include "nnue/nnue_accumulator.h" +#include "types.h" namespace Stockfish { diff --git a/src/search.cpp b/src/search.cpp index 18e4aa56df2..76d0545ac50 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -16,25 +16,34 @@ along with this program. If not, see . */ +#include "search.h" + #include +#include +#include #include #include -#include // For std::memset +#include +#include +#include #include #include +#include +#include +#include "bitboard.h" #include "evaluate.h" #include "misc.h" #include "movegen.h" #include "movepick.h" +#include "nnue/evaluate_nnue.h" +#include "nnue/nnue_common.h" #include "position.h" -#include "search.h" +#include "syzygy/tbprobe.h" #include "thread.h" #include "timeman.h" #include "tt.h" #include "uci.h" -#include "syzygy/tbprobe.h" -#include "nnue/evaluate_nnue.h" namespace Stockfish { @@ -1142,7 +1151,7 @@ namespace { // Decrease reduction if ttMove has been singularly extended (~1 Elo) if (singularQuietLMR) r--; - + // Increase reduction on repetition (~1 Elo) if ( move == (ss-4)->currentMove && pos.has_repeated()) diff --git a/src/search.h b/src/search.h index 806e4be63fa..c6dbffce0c7 100644 --- a/src/search.h +++ b/src/search.h @@ -19,6 +19,7 @@ #ifndef SEARCH_H_INCLUDED #define SEARCH_H_INCLUDED +#include #include #include "misc.h" diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index ba727825d4c..d1b32d242c9 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -16,33 +16,38 @@ along with this program. If not, see . */ +#include "tbprobe.h" + +#include #include #include +#include #include -#include // For std::memset and std::memcpy +#include +#include #include #include +#include #include -#include #include #include #include #include +#include +#include #include "../bitboard.h" +#include "../misc.h" #include "../movegen.h" #include "../position.h" #include "../search.h" #include "../types.h" #include "../uci.h" -#include "tbprobe.h" - #ifndef _WIN32 #include -#include #include -#include +#include #else #define WIN32_LEAN_AND_MEAN #ifndef NOMINMAX @@ -1002,7 +1007,7 @@ uint8_t* set_sizes(PairsData* d, uint8_t* data) { // Starting from this we compute a base64[] table indexed by symbol length // and containing 64 bit values so that d->base64[i] >= d->base64[i+1]. - // Implementation note: we first cast the unsigned size_t "base64.size()" + // Implementation note: we first cast the unsigned size_t "base64.size()" // to a signed int "base64_size" variable and then we are able to subtract 2, // avoiding unsigned overflow warnings. diff --git a/src/syzygy/tbprobe.h b/src/syzygy/tbprobe.h index fe994f68e91..b2ba35ff4b0 100644 --- a/src/syzygy/tbprobe.h +++ b/src/syzygy/tbprobe.h @@ -19,8 +19,14 @@ #ifndef TBPROBE_H #define TBPROBE_H +#include + #include "../search.h" +namespace Stockfish { +class Position; +} + namespace Stockfish::Tablebases { enum WDLScore { diff --git a/src/thread.cpp b/src/thread.cpp index c680393e277..9cf85310c39 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -16,15 +16,23 @@ along with this program. If not, see . */ -#include +#include "thread.h" -#include // For std::count +#include +#include +#include +#include +#include +#include +#include +#include + +#include "misc.h" #include "movegen.h" #include "search.h" -#include "thread.h" -#include "uci.h" #include "syzygy/tbprobe.h" #include "tt.h" +#include "uci.h" namespace Stockfish { diff --git a/src/thread.h b/src/thread.h index aa9db2f3633..a421af9e3bc 100644 --- a/src/thread.h +++ b/src/thread.h @@ -21,14 +21,16 @@ #include #include +#include +#include #include -#include #include #include "movepick.h" #include "position.h" #include "search.h" #include "thread_win32_osx.h" +#include "types.h" namespace Stockfish { diff --git a/src/timeman.cpp b/src/timeman.cpp index 169c7821c94..5e57f8f98c5 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -16,12 +16,12 @@ along with this program. If not, see . */ +#include "timeman.h" + #include -#include #include #include "search.h" -#include "timeman.h" #include "uci.h" namespace Stockfish { diff --git a/src/timeman.h b/src/timeman.h index 3462b8236a7..9ad6bdcccf9 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -19,9 +19,12 @@ #ifndef TIMEMAN_H_INCLUDED #define TIMEMAN_H_INCLUDED +#include + #include "misc.h" #include "search.h" #include "thread.h" +#include "types.h" namespace Stockfish { diff --git a/src/tt.cpp b/src/tt.cpp index 3339c993c41..1582121fd6d 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -16,14 +16,17 @@ along with this program. If not, see . */ -#include // For std::memset +#include "tt.h" + +#include +#include +#include #include #include +#include -#include "bitboard.h" #include "misc.h" #include "thread.h" -#include "tt.h" #include "uci.h" namespace Stockfish { diff --git a/src/tt.h b/src/tt.h index 3e335b44696..df962faaa7b 100644 --- a/src/tt.h +++ b/src/tt.h @@ -19,6 +19,9 @@ #ifndef TT_H_INCLUDED #define TT_H_INCLUDED +#include +#include + #include "misc.h" #include "types.h" diff --git a/src/tune.cpp b/src/tune.cpp index ccfc33c5082..97baeb784e9 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -16,14 +16,20 @@ along with this program. If not, see . */ +#include "tune.h" + #include #include +#include #include +#include -#include "types.h" -#include "misc.h" #include "uci.h" +namespace Stockfish { +enum Value : int; +} + using std::string; namespace Stockfish { @@ -108,7 +114,6 @@ template<> void Tune::Entry::read_option() { value(); } // // Then paste the output below, as the function body -#include namespace Stockfish { diff --git a/src/tune.h b/src/tune.h index bdbee14e014..3e94f7efc6c 100644 --- a/src/tune.h +++ b/src/tune.h @@ -19,12 +19,15 @@ #ifndef TUNE_H_INCLUDED #define TUNE_H_INCLUDED +#include #include #include #include +#include #include namespace Stockfish { +enum Value : int; using Range = std::pair; // Option's min-max values using RangeFun = Range (int); diff --git a/src/types.h b/src/types.h index 34dc42e173e..bb319c2bb08 100644 --- a/src/types.h +++ b/src/types.h @@ -37,10 +37,7 @@ /// | only in 64-bit mode and requires hardware with pext support. #include -#include #include -#include -#include #if defined(_MSC_VER) // Disable some silly and noisy warning from MSVC compiler diff --git a/src/uci.cpp b/src/uci.cpp index ffe5e0576ea..2a35a40fd4e 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -16,23 +16,30 @@ along with this program. If not, see . */ +#include "uci.h" + +#include #include +#include #include +#include +#include +#include #include +#include +#include #include #include +#include #include "benchmark.h" #include "evaluate.h" +#include "misc.h" #include "movegen.h" +#include "nnue/evaluate_nnue.h" #include "position.h" #include "search.h" #include "thread.h" -#include "timeman.h" -#include "tt.h" -#include "uci.h" -#include "syzygy/tbprobe.h" -#include "nnue/evaluate_nnue.h" using namespace std; diff --git a/src/uci.h b/src/uci.h index 2e40c9125d1..7ca97d5c6bc 100644 --- a/src/uci.h +++ b/src/uci.h @@ -19,6 +19,8 @@ #ifndef UCI_H_INCLUDED #define UCI_H_INCLUDED +#include +#include #include #include diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 27f436d3b37..8d2c5c098ed 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -18,16 +18,23 @@ #include #include +#include +#include +#include +#include +#include #include #include +#include #include "evaluate.h" #include "misc.h" #include "search.h" +#include "syzygy/tbprobe.h" #include "thread.h" #include "tt.h" +#include "types.h" #include "uci.h" -#include "syzygy/tbprobe.h" using std::string; From 4f7fe255c7ac9cf705350cabfc231d87a3e75018 Mon Sep 17 00:00:00 2001 From: Disservin Date: Sat, 26 Aug 2023 09:49:04 +0200 Subject: [PATCH 1184/1766] Simplify README The UCI protocol is rather technical and has little value in our README. Instead it should be explained in our wiki. "Contributing" is moved above "Compiling Stockfish" to make it more prominent. Also move the CONTRIBUTING.md into the root directory and include it in the distributed artifacts/releases. closes https://github.com/official-stockfish/Stockfish/pull/4766 No functional change --- .github/workflows/stockfish_arm_binaries.yml | 1 + .github/workflows/stockfish_binaries.yml | 1 + .github/CONTRIBUTING.md => CONTRIBUTING.md | 0 README.md | 56 ++++++++------------ 4 files changed, 24 insertions(+), 34 deletions(-) rename .github/CONTRIBUTING.md => CONTRIBUTING.md (100%) diff --git a/.github/workflows/stockfish_arm_binaries.yml b/.github/workflows/stockfish_arm_binaries.yml index dfe4e2a24ce..4d7f3d55c1a 100644 --- a/.github/workflows/stockfish_arm_binaries.yml +++ b/.github/workflows/stockfish_arm_binaries.yml @@ -128,6 +128,7 @@ jobs: cp AUTHORS stockfish/ cp CITATION.cff stockfish/ cp README.md stockfish/ + cp CONTRIBUTING.md stockfish/ tar -cvf stockfish-android-$BINARY.tar stockfish - name: Upload binaries diff --git a/.github/workflows/stockfish_binaries.yml b/.github/workflows/stockfish_binaries.yml index 7c7341ef655..fadfbcfcd04 100644 --- a/.github/workflows/stockfish_binaries.yml +++ b/.github/workflows/stockfish_binaries.yml @@ -161,6 +161,7 @@ jobs: cp AUTHORS stockfish/ cp CITATION.cff stockfish/ cp README.md stockfish/ + cp CONTRIBUTING.md stockfish/ - name: Create tar if: runner.os != 'Windows' diff --git a/.github/CONTRIBUTING.md b/CONTRIBUTING.md similarity index 100% rename from .github/CONTRIBUTING.md rename to CONTRIBUTING.md diff --git a/README.md b/README.md index 4d63b71e35b..52b123cbd25 100644 --- a/README.md +++ b/README.md @@ -59,40 +59,9 @@ This distribution of Stockfish consists of the following files: * a file with the .nnue extension, storing the neural network for the NNUE evaluation. Binary distributions will have this file embedded. -## The UCI protocol - -The [Universal Chess Interface][uci-link] (UCI) is a standard text-based protocol -used to communicate with a chess engine and is the recommended way to do so for -typical graphical user interfaces (GUI) or chess tools. Stockfish implements the -majority of its options. - -Developers can see the default values for the UCI options available in Stockfish -by typing `./stockfish uci` in a terminal, but most users should typically use a -chess GUI to interact with Stockfish. - -For more information on UCI or debug commands, see our [documentation][wiki-commands-link]. - -## Compiling Stockfish - -Stockfish has support for 32 or 64-bit CPUs, certain hardware instructions, -big-endian machines such as Power PC, and other platforms. - -On Unix-like systems, it should be easy to compile Stockfish directly from the -source code with the included Makefile in the folder `src`. In general, it is -recommended to run `make help` to see a list of make targets with corresponding -descriptions. An example suitable for most Intel and AMD chips: - -``` -cd src -make -j profile-build ARCH=x86-64-avx2 -``` - -Detailed compilation instructions for all platforms can be found in our -[documentation][wiki-compile-link]. - ## Contributing -__See [Contributing Guide](./.github/CONTRIBUTING.md).__ +__See [Contributing Guide](CONTRIBUTING.md).__ ### Donating hardware @@ -116,6 +85,25 @@ Discussions about Stockfish take place these days mainly in the Stockfish [Discord server][discord-link]. This is also the best place to ask questions about the codebase and how to improve it. +## Compiling Stockfish + +Stockfish has support for 32 or 64-bit CPUs, certain hardware instructions, +big-endian machines such as Power PC, and other platforms. + +On Unix-like systems, it should be easy to compile Stockfish directly from the +source code with the included Makefile in the folder `src`. In general, it is +recommended to run `make help` to see a list of make targets with corresponding +descriptions. An example suitable for most Intel and AMD chips: + +``` +cd src +make -j profile-build ARCH=x86-64-avx2 +``` + +Detailed compilation instructions for all platforms can be found in our +[documentation][wiki-compile-link]. Our wiki also has information about +the [UCI commands][wiki-uci-link] supported by Stockfish. + ## Terms of use Stockfish is free and distributed under the @@ -152,9 +140,9 @@ also be made available under GPL v3. [website-link]: https://stockfishchess.org [website-blog-link]: https://stockfishchess.org/blog/ [wiki-link]: https://github.com/official-stockfish/Stockfish/wiki -[wiki-usage-link]: https://github.com/official-stockfish/Stockfish/wiki/Download-and-usage [wiki-compile-link]: https://github.com/official-stockfish/Stockfish/wiki/Compiling-from-source -[wiki-commands-link]: https://github.com/official-stockfish/Stockfish/wiki/Commands +[wiki-uci-link]: https://github.com/official-stockfish/Stockfish/wiki/UCI-&-Commands +[wiki-usage-link]: https://github.com/official-stockfish/Stockfish/wiki/Download-and-usage [worker-link]: https://github.com/official-stockfish/fishtest/wiki/Running-the-worker [build-badge]: https://img.shields.io/github/actions/workflow/status/official-stockfish/Stockfish/stockfish.yml?branch=master&style=for-the-badge&label=stockfish&logo=github From 1f7ff8406d323e634a2aa1e1264042340707cdd9 Mon Sep 17 00:00:00 2001 From: pb00067 Date: Thu, 17 Aug 2023 14:31:05 +0200 Subject: [PATCH 1185/1766] Simplify slider_blocker calculation Now that classical evaluation was removed, we can adapt this method to the needs of set_check_info. STC: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 298176 W: 75802 L: 75868 D: 146506 Ptnml(0-2): 908, 33608, 80192, 33402, 978 https://tests.stockfishchess.org/tests/view/64e70b899009777747557b43 closes https://github.com/official-stockfish/Stockfish/pull/4753 no functional change --- src/position.cpp | 34 +++++++++++++++------------------- src/position.h | 2 +- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index 0f15727d2ca..120677432b6 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -321,8 +321,8 @@ void Position::set_castling_right(Color c, Square rfrom) { void Position::set_check_info() const { - st->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), square(WHITE), st->pinners[BLACK]); - st->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square(BLACK), st->pinners[WHITE]); + update_slider_blockers(WHITE); + update_slider_blockers(BLACK); Square ksq = square(~sideToMove); @@ -443,37 +443,33 @@ string Position::fen() const { return ss.str(); } +/// update_slider_blockers() calculates st->blockersForKing[c] and st->pinners[~c], +/// which store respectively the pieces preventing king of color c from being in check +/// and the slider pieces of color ~c pinning pieces of color c to the king. +void Position::update_slider_blockers(Color c) const { -/// Position::slider_blockers() returns a bitboard of all the pieces (both colors) -/// that are blocking attacks on the square 's' from 'sliders'. A piece blocks a -/// slider if removing that piece from the board would result in a position where -/// square 's' is attacked. For example, a king-attack blocking piece can be either -/// a pinned or a discovered check piece, according if its color is the opposite -/// or the same of the color of the slider. + Square ksq = square(c); -Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const { - - Bitboard blockers = 0; - pinners = 0; + st->blockersForKing[c] = 0; + st->pinners[~c] = 0; // Snipers are sliders that attack 's' when a piece and other snipers are removed - Bitboard snipers = ( (attacks_bb< ROOK>(s) & pieces(QUEEN, ROOK)) - | (attacks_bb(s) & pieces(QUEEN, BISHOP))) & sliders; + Bitboard snipers = ( (attacks_bb< ROOK>(ksq) & pieces(QUEEN, ROOK)) + | (attacks_bb(ksq) & pieces(QUEEN, BISHOP))) & pieces(~c); Bitboard occupancy = pieces() ^ snipers; while (snipers) { Square sniperSq = pop_lsb(snipers); - Bitboard b = between_bb(s, sniperSq) & occupancy; + Bitboard b = between_bb(ksq, sniperSq) & occupancy; if (b && !more_than_one(b)) { - blockers |= b; - if (b & pieces(color_of(piece_on(s)))) - pinners |= sniperSq; + st->blockersForKing[c] |= b; + if (b & pieces(c)) + st->pinners[~c] |= sniperSq; } } - return blockers; } diff --git a/src/position.h b/src/position.h index f0546af33f7..ca7c3ace811 100644 --- a/src/position.h +++ b/src/position.h @@ -114,7 +114,7 @@ class Position { // Attacks to/from a given square Bitboard attackers_to(Square s) const; Bitboard attackers_to(Square s, Bitboard occupied) const; - Bitboard slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const; + void update_slider_blockers(Color c) const; template Bitboard attacks_by(Color c) const; // Properties of moves From adf29b3fd69cdca035d1aa6675e01acafbf4d07f Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Fri, 25 Aug 2023 15:42:44 +0300 Subject: [PATCH 1186/1766] Rename one variable To enhance code clarity and prevent potential confusion with the 'r' variable assigned to reduction later in the code, this pull request renames it to 'reductionScale' when we use the same name in the reduction() function. Using distinct variable names for separate functions improves code readability and maintainability. closes https://github.com/official-stockfish/Stockfish/pull/4765 No functional change --- src/search.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 76d0545ac50..eefe5a3bcf9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -80,8 +80,9 @@ namespace { int Reductions[MAX_MOVES]; // [depth or moveNumber] Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) { - int r = Reductions[d] * Reductions[mn]; - return (r + 1372 - int(delta) * 1073 / int(rootDelta)) / 1024 + (!i && r > 936); + int reductionScale = Reductions[d] * Reductions[mn]; + return (reductionScale + 1372 - int(delta) * 1073 / int(rootDelta)) / 1024 + + (!i && reductionScale > 936); } constexpr int futility_move_count(bool improving, Depth depth) { From b25d68f6ee2d016cc0c14b076e79e6c44fdaea2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sat, 2 Sep 2023 08:39:16 +0200 Subject: [PATCH 1187/1766] Introduce simple_eval() for lazy evaluations This patch implements the pure materialistic evaluation called simple_eval() to gain a speed-up during Stockfish search. We use the so-called lazy evaluation trick: replace the accurate but slow NNUE network evaluation by the super-fast simple_eval() if the position seems to be already won (high material advantage). To guard against some of the most obvious blunders introduced by this idea, this patch uses the following features which will raise the lazy evaluation threshold in some situations: - avoid lazy evals on shuffling branches in the search tree - avoid lazy evals if the position at root already has a material imbalance - avoid lazy evals if the search value at root is already winning/losing. Moreover, we add a small random noise to the simple_eval() term. This idea (stochastic mobility in the minimax tree) was worth about 200 Elo in the pure simple_eval() player on Lichess. Overall, the current implementation in this patch evaluates about 2% of the leaves in the search tree lazily. -------------------------------------------- STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 60352 W: 15585 L: 15234 D: 29533 Ptnml(0-2): 216, 6906, 15578, 7263, 213 https://tests.stockfishchess.org/tests/view/64f1d9bcbd9967ffae366209 LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 35106 W: 8990 L: 8678 D: 17438 Ptnml(0-2): 14, 3668, 9887, 3960, 24 https://tests.stockfishchess.org/tests/view/64f25204f5b0c54e3f04c0e7 verification run at VLTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 74362 W: 19088 L: 18716 D: 36558 Ptnml(0-2): 6, 7226, 22348, 7592, 9 https://tests.stockfishchess.org/tests/view/64f2ecdbf5b0c54e3f04d3ae All three tests above were run with adjudication off, we also verified that there was no regression on matetracker (thanks Disservin!). ---------------------------------------------- closes https://github.com/official-stockfish/Stockfish/pull/4771 Bench: 1393714 --- src/evaluate.cpp | 60 ++++++++++++++++++++++++++++++++---------------- src/evaluate.h | 4 ++++ src/thread.cpp | 2 ++ src/thread.h | 1 + 4 files changed, 47 insertions(+), 20 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 25a6545564a..46ebbb49920 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -136,35 +136,54 @@ namespace Eval { } } -/// evaluate() is the evaluator for the outer world. It returns a static -/// evaluation of the position from the point of view of the side to move. -Value Eval::evaluate(const Position& pos) { - - assert(!pos.checkers()); - - Value v; +/// simple_eval() returns a static, purely materialistic evaluation of the position +/// from the point of view of the given color. It can be divided by PawnValue to get +/// an approximation of the material advantage on the board in terms of pawns. - int nnueComplexity; - int npm = pos.non_pawn_material() / 64; +Value Eval::simple_eval(const Position& pos, Color c) { + return PawnValue * (pos.count(c) - pos.count(~c)) + + (pos.non_pawn_material(c) - pos.non_pawn_material(~c)); +} - Color stm = pos.side_to_move(); - Value optimism = pos.this_thread()->optimism[stm]; - Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); +/// evaluate() is the evaluator for the outer world. It returns a static evaluation +/// of the position from the point of view of the side to move. - int material = pos.non_pawn_material(stm) - pos.non_pawn_material(~stm) - + 126 * (pos.count(stm) - pos.count(~stm)); +Value Eval::evaluate(const Position& pos) { - // Blend optimism and eval with nnue complexity and material imbalance - optimism += optimism * (nnueComplexity + abs(material - nnue)) / 512; - nnue -= nnue * (nnueComplexity + abs(material - nnue)) / 32768; + assert(!pos.checkers()); - v = ( nnue * (915 + npm + 9 * pos.count()) - + optimism * (154 + npm + pos.count())) / 1024; + Value v; + Color stm = pos.side_to_move(); + int shuffling = pos.rule50_count(); + int simpleEval = simple_eval(pos, stm) + (int(pos.key() & 7) - 3); + + bool lazy = abs(simpleEval) >= RookValue + KnightValue + + 16 * shuffling * shuffling + + abs(pos.this_thread()->bestValue) + + abs(pos.this_thread()->rootSimpleEval); + + if (lazy) + v = Value(simpleEval); + else + { + int nnueComplexity; + Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); + + Value optimism = pos.this_thread()->optimism[stm]; + + // Blend optimism and eval with nnue complexity and material imbalance + optimism += optimism * (nnueComplexity + abs(simpleEval - nnue)) / 512; + nnue -= nnue * (nnueComplexity + abs(simpleEval - nnue)) / 32768; + + int npm = pos.non_pawn_material() / 64; + v = ( nnue * (915 + npm + 9 * pos.count()) + + optimism * (154 + npm + pos.count())) / 1024; + } // Damp down the evaluation linearly when shuffling - v = v * (200 - pos.rule50_count()) / 214; + v = v * (200 - shuffling) / 214; // Guarantee evaluation does not hit the tablebase range v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); @@ -184,6 +203,7 @@ std::string Eval::trace(Position& pos) { // Reset any global variable used in eval pos.this_thread()->bestValue = VALUE_ZERO; + pos.this_thread()->rootSimpleEval = VALUE_ZERO; pos.this_thread()->optimism[WHITE] = VALUE_ZERO; pos.this_thread()->optimism[BLACK] = VALUE_ZERO; diff --git a/src/evaluate.h b/src/evaluate.h index a222da7361a..7f4feedf0c6 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -21,6 +21,8 @@ #include +#include "types.h" + namespace Stockfish { class Position; @@ -29,6 +31,8 @@ enum Value : int; namespace Eval { std::string trace(Position& pos); + + Value simple_eval(const Position& pos, Color c); Value evaluate(const Position& pos); extern std::string currentEvalFileName; diff --git a/src/thread.cpp b/src/thread.cpp index 9cf85310c39..60f760ed46e 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -27,6 +27,7 @@ #include #include +#include "evaluate.h" #include "misc.h" #include "movegen.h" #include "search.h" @@ -212,6 +213,7 @@ void ThreadPool::start_thinking(Position& pos, StateListPtr& states, th->rootMoves = rootMoves; th->rootPos.set(pos.fen(), pos.is_chess960(), &th->rootState, th); th->rootState = setupStates->back(); + th->rootSimpleEval = Eval::simple_eval(pos, pos.side_to_move()); } main()->start_searching(); diff --git a/src/thread.h b/src/thread.h index a421af9e3bc..8d0adcf0340 100644 --- a/src/thread.h +++ b/src/thread.h @@ -67,6 +67,7 @@ class Thread { Search::RootMoves rootMoves; Depth rootDepth, completedDepth; Value rootDelta; + Value rootSimpleEval; CounterMoveHistory counterMoves; ButterflyHistory mainHistory; CapturePieceToHistory captureHistory; From a8b4fd16716e74a9819e798fc09e5926e003013e Mon Sep 17 00:00:00 2001 From: Sebastian Buchwald Date: Mon, 4 Sep 2023 22:01:20 +0200 Subject: [PATCH 1188/1766] Avoid "using namespace std" This is a cleanup PR that prepares the automatic checking of missing or superfluous #include directives via the include-what-you-use (IWYU) tool on the CI. Unfortunately, IWYU proposes additional includes for "namespace std" although we don't need them. To avoid the problem, the commit removes all "using namespace std" statements from the code and directly uses the std:: prefix instead. Alternatively, we could add specific usings (e.g. "using std::string") foreach used type. Also, a mix of both approaches would be possible. I decided for the prefix approach because most of the files were already using the std:: prefixes despite the "using namespace std". closes https://github.com/official-stockfish/Stockfish/pull/4772 No functional change --- src/benchmark.cpp | 30 +++++++++++------------- src/evaluate.cpp | 30 +++++++++++------------- src/misc.cpp | 48 ++++++++++++++++++------------------- src/uci.cpp | 60 +++++++++++++++++++++++------------------------ 4 files changed, 80 insertions(+), 88 deletions(-) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index f3401c61c8b..8e28184a3cd 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -25,11 +25,9 @@ #include "position.h" -using namespace std; - namespace { -const vector Defaults = { +const std::vector Defaults = { "setoption name UCI_Chess960 value false", "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 10", @@ -109,17 +107,17 @@ namespace Stockfish { /// bench 64 4 5000 current movetime : search current position with 4 threads for 5 sec /// bench 16 1 5 blah perft : run a perft 5 on positions in file "blah" -vector setup_bench(const Position& current, istream& is) { +std::vector setup_bench(const Position& current, std::istream& is) { - vector fens, list; - string go, token; + std::vector fens, list; + std::string go, token; // Assign default values to missing arguments - string ttSize = (is >> token) ? token : "16"; - string threads = (is >> token) ? token : "1"; - string limit = (is >> token) ? token : "13"; - string fenFile = (is >> token) ? token : "default"; - string limitType = (is >> token) ? token : "depth"; + std::string ttSize = (is >> token) ? token : "16"; + std::string threads = (is >> token) ? token : "1"; + std::string limit = (is >> token) ? token : "13"; + std::string fenFile = (is >> token) ? token : "default"; + std::string limitType = (is >> token) ? token : "depth"; go = limitType == "eval" ? "eval" : "go " + limitType + " " + limit; @@ -131,12 +129,12 @@ vector setup_bench(const Position& current, istream& is) { else { - string fen; - ifstream file(fenFile); + std::string fen; + std::ifstream file(fenFile); if (!file.is_open()) { - cerr << "Unable to open file " << fenFile << endl; + std::cerr << "Unable to open file " << fenFile << std::endl; exit(EXIT_FAILURE); } @@ -151,8 +149,8 @@ vector setup_bench(const Position& current, istream& is) { list.emplace_back("setoption name Hash value " + ttSize); list.emplace_back("ucinewgame"); - for (const string& fen : fens) - if (fen.find("setoption") != string::npos) + for (const std::string& fen : fens) + if (fen.find("setoption") != std::string::npos) list.emplace_back(fen); else { diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 46ebbb49920..9ca0e4566f6 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -51,13 +51,11 @@ #endif -using namespace std; - namespace Stockfish { namespace Eval { - string currentEvalFileName = "None"; + std::string currentEvalFileName = "None"; /// NNUE::init() tries to load a NNUE network at startup time, or when the engine /// receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue" @@ -69,22 +67,22 @@ namespace Eval { void NNUE::init() { - string eval_file = string(Options["EvalFile"]); + std::string eval_file = std::string(Options["EvalFile"]); if (eval_file.empty()) eval_file = EvalFileDefaultName; #if defined(DEFAULT_NNUE_DIRECTORY) - vector dirs = { "" , "" , CommandLine::binaryDirectory , stringify(DEFAULT_NNUE_DIRECTORY) }; + std::vector dirs = { "" , "" , CommandLine::binaryDirectory , stringify(DEFAULT_NNUE_DIRECTORY) }; #else - vector dirs = { "" , "" , CommandLine::binaryDirectory }; + std::vector dirs = { "" , "" , CommandLine::binaryDirectory }; #endif - for (const string& directory : dirs) + for (const std::string& directory : dirs) if (currentEvalFileName != eval_file) { if (directory != "") { - ifstream stream(directory + eval_file, ios::binary); + std::ifstream stream(directory + eval_file, std::ios::binary); if (NNUE::load_eval(eval_file, stream)) currentEvalFileName = eval_file; } @@ -92,7 +90,7 @@ namespace Eval { if (directory == "" && eval_file == EvalFileDefaultName) { // C++ way to prepare a buffer for a memory stream - class MemoryBuffer : public basic_streambuf { + class MemoryBuffer : public std::basic_streambuf { public: MemoryBuffer(char* p, size_t n) { setg(p, p, p + n); setp(p, p + n); } }; @@ -100,7 +98,7 @@ namespace Eval { size_t(gEmbeddedNNUESize)); (void) gEmbeddedNNUEEnd; // Silence warning on unused variable - istream stream(&buffer); + std::istream stream(&buffer); if (NNUE::load_eval(eval_file, stream)) currentEvalFileName = eval_file; } @@ -110,18 +108,18 @@ namespace Eval { /// NNUE::verify() verifies that the last net used was loaded successfully void NNUE::verify() { - string eval_file = string(Options["EvalFile"]); + std::string eval_file = std::string(Options["EvalFile"]); if (eval_file.empty()) eval_file = EvalFileDefaultName; if (currentEvalFileName != eval_file) { - string msg1 = "Network evaluation parameters compatible with the engine must be available."; - string msg2 = "The network file " + eval_file + " was not loaded successfully."; - string msg3 = "The UCI option EvalFile might need to specify the full path, including the directory name, to the network file."; - string msg4 = "The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/" + std::string(EvalFileDefaultName); - string msg5 = "The engine will be terminated now."; + std::string msg1 = "Network evaluation parameters compatible with the engine must be available."; + std::string msg2 = "The network file " + eval_file + " was not loaded successfully."; + std::string msg3 = "The UCI option EvalFile might need to specify the full path, including the directory name, to the network file."; + std::string msg4 = "The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/" + std::string(EvalFileDefaultName); + std::string msg5 = "The engine will be terminated now."; sync_cout << "info string ERROR: " << msg1 << sync_endl; sync_cout << "info string ERROR: " << msg2 << sync_endl; diff --git a/src/misc.cpp b/src/misc.cpp index 42083e0a94f..a72a1c13319 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -67,14 +67,12 @@ using fun8_t = bool(*)(HANDLE, BOOL, PTOKEN_PRIVILEGES, DWORD, PTOKEN_PRIVILEGES #include #endif -using namespace std; - namespace Stockfish { namespace { /// Version number or dev. -constexpr string_view version = "dev"; +constexpr std::string_view version = "dev"; /// Our fancy logging facility. The trick here is to replace cin.rdbuf() and /// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We @@ -82,16 +80,16 @@ constexpr string_view version = "dev"; /// usual I/O functionality, all without changing a single line of code! /// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81 -struct Tie: public streambuf { // MSVC requires split streambuf for cin and cout +struct Tie: public std::streambuf { // MSVC requires split streambuf for cin and cout - Tie(streambuf* b, streambuf* l) : buf(b), logBuf(l) {} + Tie(std::streambuf* b, std::streambuf* l) : buf(b), logBuf(l) {} int sync() override { return logBuf->pubsync(), buf->pubsync(); } int overflow(int c) override { return log(buf->sputc((char)c), "<< "); } int underflow() override { return buf->sgetc(); } int uflow() override { return log(buf->sbumpc(), ">> "); } - streambuf *buf, *logBuf; + std::streambuf *buf, *logBuf; int log(int c, const char* prefix) { @@ -106,10 +104,10 @@ struct Tie: public streambuf { // MSVC requires split streambuf for cin and cout class Logger { - Logger() : in(cin.rdbuf(), file.rdbuf()), out(cout.rdbuf(), file.rdbuf()) {} + Logger() : in(std::cin.rdbuf(), file.rdbuf()), out(std::cout.rdbuf(), file.rdbuf()) {} ~Logger() { start(""); } - ofstream file; + std::ofstream file; Tie in, out; public: @@ -119,23 +117,23 @@ class Logger { if (l.file.is_open()) { - cout.rdbuf(l.out.buf); - cin.rdbuf(l.in.buf); + std::cout.rdbuf(l.out.buf); + std::cin.rdbuf(l.in.buf); l.file.close(); } if (!fname.empty()) { - l.file.open(fname, ifstream::out); + l.file.open(fname, std::ifstream::out); if (!l.file.is_open()) { - cerr << "Unable to open debug log file " << fname << endl; + std::cerr << "Unable to open debug log file " << fname << std::endl; exit(EXIT_FAILURE); } - cin.rdbuf(&l.in); - cout.rdbuf(&l.out); + std::cin.rdbuf(&l.in); + std::cout.rdbuf(&l.out); } } }; @@ -153,9 +151,9 @@ class Logger { /// For releases (non dev builds) we only include the version number: /// Stockfish version -string engine_info(bool to_uci) { - stringstream ss; - ss << "Stockfish " << version << setfill('0'); +std::string engine_info(bool to_uci) { + std::stringstream ss; + ss << "Stockfish " << version << std::setfill('0'); if constexpr (version == "dev") { @@ -163,12 +161,12 @@ string engine_info(bool to_uci) { #ifdef GIT_DATE ss << stringify(GIT_DATE); #else - constexpr string_view months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); - string month, day, year; - stringstream date(__DATE__); // From compiler, format is "Sep 21 2008" + constexpr std::string_view months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); + std::string month, day, year; + std::stringstream date(__DATE__); // From compiler, format is "Sep 21 2008" date >> month >> day >> year; - ss << year << setw(2) << setfill('0') << (1 + months.find(month) / 4) << setw(2) << setfill('0') << day; + ss << year << std::setw(2) << std::setfill('0') << (1 + months.find(month) / 4) << std::setw(2) << std::setfill('0') << day; #endif ss << "-"; @@ -741,12 +739,12 @@ void bindThisThread(size_t idx) { namespace CommandLine { -string argv0; // path+name of the executable binary, as given by argv[0] -string binaryDirectory; // path of the executable directory -string workingDirectory; // path of the working directory +std::string argv0; // path+name of the executable binary, as given by argv[0] +std::string binaryDirectory; // path of the executable directory +std::string workingDirectory; // path of the working directory void init([[maybe_unused]] int argc, char* argv[]) { - string pathSeparator; + std::string pathSeparator; // extract the path+name of the executable binary argv0 = argv[0]; diff --git a/src/uci.cpp b/src/uci.cpp index 2a35a40fd4e..f3e436ef3aa 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -41,8 +41,6 @@ #include "search.h" #include "thread.h" -using namespace std; - namespace Stockfish { namespace { @@ -56,10 +54,10 @@ namespace { // the initial position ("startpos") and then makes the moves given in the following // move list ("moves"). - void position(Position& pos, istringstream& is, StateListPtr& states) { + void position(Position& pos, std::istringstream& is, StateListPtr& states) { Move m; - string token, fen; + std::string token, fen; is >> token; @@ -103,11 +101,11 @@ namespace { // setoption() is called when the engine receives the "setoption" UCI command. // The function updates the UCI option ("name") to the given value ("value"). - void setoption(istringstream& is) { + void setoption(std::istringstream& is) { Threads.main()->wait_for_search_finished(); - string token, name, value; + std::string token, name, value; is >> token; // Consume the "name" token @@ -130,10 +128,10 @@ namespace { // sets the thinking time and other parameters from the input string, then starts // with a search. - void go(Position& pos, istringstream& is, StateListPtr& states) { + void go(Position& pos, std::istringstream& is, StateListPtr& states) { Search::LimitsType limits; - string token; + std::string token; bool ponderMode = false; limits.startTime = now(); // The search starts as early as possible @@ -164,24 +162,24 @@ namespace { // Firstly, a list of UCI commands is set up according to the bench // parameters, then it is run one by one, printing a summary at the end. - void bench(Position& pos, istream& args, StateListPtr& states) { + void bench(Position& pos, std::istream& args, StateListPtr& states) { - string token; + std::string token; uint64_t num, nodes = 0, cnt = 1; - vector list = setup_bench(pos, args); - num = count_if(list.begin(), list.end(), [](const string& s) { return s.find("go ") == 0 || s.find("eval") == 0; }); + std::vector list = setup_bench(pos, args); + num = count_if(list.begin(), list.end(), [](const std::string& s) { return s.find("go ") == 0 || s.find("eval") == 0; }); TimePoint elapsed = now(); for (const auto& cmd : list) { - istringstream is(cmd); - is >> skipws >> token; + std::istringstream is(cmd); + is >> std::skipws >> token; if (token == "go" || token == "eval") { - cerr << "\nPosition: " << cnt++ << '/' << num << " (" << pos.fen() << ")" << endl; + std::cerr << "\nPosition: " << cnt++ << '/' << num << " (" << pos.fen() << ")" << std::endl; if (token == "go") { go(pos, is, states); @@ -200,10 +198,10 @@ namespace { dbg_print(); - cerr << "\n===========================" - << "\nTotal time (ms) : " << elapsed - << "\nNodes searched : " << nodes - << "\nNodes/second : " << 1000 * nodes / elapsed << endl; + std::cerr << "\n===========================" + << "\nTotal time (ms) : " << elapsed + << "\nNodes searched : " << nodes + << "\nNodes/second : " << 1000 * nodes / elapsed << std::endl; } // The win rate model returns the probability of winning (in per mille units) given an @@ -244,7 +242,7 @@ namespace { void UCI::loop(int argc, char* argv[]) { Position pos; - string token, cmd; + std::string token, cmd; StateListPtr states(new std::deque(1)); pos.set(StartFEN, false, &states->back(), Threads.main()); @@ -253,13 +251,13 @@ void UCI::loop(int argc, char* argv[]) { cmd += std::string(argv[i]) + " "; do { - if (argc == 1 && !getline(cin, cmd)) // Wait for an input or an end-of-file (EOF) indication + if (argc == 1 && !getline(std::cin, cmd)) // Wait for an input or an end-of-file (EOF) indication cmd = "quit"; - istringstream is(cmd); + std::istringstream is(cmd); token.clear(); // Avoid a stale if getline() returns nothing or a blank line - is >> skipws >> token; + is >> std::skipws >> token; if ( token == "quit" || token == "stop") @@ -294,7 +292,7 @@ void UCI::loop(int argc, char* argv[]) { { std::optional filename; std::string f; - if (is >> skipws >> f) + if (is >> std::skipws >> f) filename = f; Eval::NNUE::save_eval(filename); } @@ -325,11 +323,11 @@ int UCI::to_cp(Value v) { /// mate Mate in 'y' moves (not plies). If the engine is getting mated, /// uses negative values for 'y'. -string UCI::value(Value v) { +std::string UCI::value(Value v) { assert(-VALUE_INFINITE < v && v < VALUE_INFINITE); - stringstream ss; + std::stringstream ss; if (abs(v) < VALUE_TB_WIN_IN_MAX_PLY) ss << "cp " << UCI::to_cp(v); @@ -348,9 +346,9 @@ string UCI::value(Value v) { /// UCI::wdl() reports the win-draw-loss (WDL) statistics given an evaluation /// and a game ply based on the data gathered for fishtest LTC games. -string UCI::wdl(Value v, int ply) { +std::string UCI::wdl(Value v, int ply) { - stringstream ss; + std::stringstream ss; int wdl_w = win_rate_model( v, ply); int wdl_l = win_rate_model(-v, ply); @@ -373,7 +371,7 @@ std::string UCI::square(Square s) { /// standard chess mode and in e1h1 notation it is printed in Chess960 mode. /// Internally, all castling moves are always encoded as 'king captures rook'. -string UCI::move(Move m, bool chess960) { +std::string UCI::move(Move m, bool chess960) { if (m == MOVE_NONE) return "(none)"; @@ -387,7 +385,7 @@ string UCI::move(Move m, bool chess960) { if (type_of(m) == CASTLING && !chess960) to = make_square(to > from ? FILE_G : FILE_C, rank_of(from)); - string move = UCI::square(from) + UCI::square(to); + std::string move = UCI::square(from) + UCI::square(to); if (type_of(m) == PROMOTION) move += " pnbrqk"[promotion_type(m)]; @@ -399,7 +397,7 @@ string UCI::move(Move m, bool chess960) { /// UCI::to_move() converts a string representing a move in coordinate notation /// (g1f3, a7a8q) to the corresponding legal Move, if any. -Move UCI::to_move(const Position& pos, string& str) { +Move UCI::to_move(const Position& pos, std::string& str) { if (str.length() == 5) str[4] = char(tolower(str[4])); // The promotion piece character must be lowercased From 1461d861c8240e29df690f1e34dc50eee37ae1b5 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Mon, 4 Sep 2023 13:53:30 +0200 Subject: [PATCH 1189/1766] Prevent usage of AVX-512 for the last layer. Add more static checks regarding the SIMD width match. STC: https://tests.stockfishchess.org/tests/view/64f5c568a9bc5a78c669e70e LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 125216 W: 31756 L: 31636 D: 61824 Ptnml(0-2): 327, 13993, 33848, 14113, 327 Fixes a bug introduced in 2f2f45f, where with AVX-512 the weights and input to the last layer were being read out of bounds. Now AVX-512 is only used for the layers it can be used for. Additional static assertions have been added to prevent more errors like this in the future. closes https://github.com/official-stockfish/Stockfish/pull/4773 No functional change --- src/nnue/layers/affine_transform.h | 50 ++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index e9d0beace17..61cdb781866 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -210,6 +210,11 @@ namespace Stockfish::Eval::NNUE::Layers { void propagate( const InputType* input, OutputType* output) const { +#if defined (USE_SSSE3) + + if constexpr (OutputDimensions > 1) + { + #if defined (USE_AVX512) using vec_t = __m512i; #define vec_setzero _mm512_setzero_si512 @@ -233,15 +238,10 @@ namespace Stockfish::Eval::NNUE::Layers { #define vec_hadd Simd::m128_hadd #endif -#if defined (USE_SSSE3) - const auto inputVector = reinterpret_cast(input); + static constexpr IndexType OutputSimdWidth = sizeof(vec_t) / sizeof(OutputType); - static constexpr IndexType OutputSimdWidth = sizeof(vec_t) / sizeof(OutputType); + static_assert(OutputDimensions % OutputSimdWidth == 0); - static_assert(OutputDimensions % OutputSimdWidth == 0 || OutputDimensions == 1); - - if constexpr (OutputDimensions % OutputSimdWidth == 0) - { constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 8) / 4; constexpr IndexType NumRegs = OutputDimensions / OutputSimdWidth; @@ -264,10 +264,41 @@ namespace Stockfish::Eval::NNUE::Layers { vec_t* outptr = reinterpret_cast(output); for (IndexType k = 0; k < NumRegs; ++k) outptr[k] = acc[k]; + +# undef vec_setzero +# undef vec_set_32 +# undef vec_add_dpbusd_32 +# undef vec_add_dpbusd_32x2 +# undef vec_hadd + } else if constexpr (OutputDimensions == 1) { - constexpr IndexType NumChunks = PaddedInputDimensions / SimdWidth; + +// We cannot use AVX512 for the last layer because there's only 32 inputs and the buffer is not padded to 64 elements. +#if defined (USE_AVX2) + using vec_t = __m256i; + #define vec_setzero _mm256_setzero_si256 + #define vec_set_32 _mm256_set1_epi32 + #define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32 + #define vec_add_dpbusd_32x2 Simd::m256_add_dpbusd_epi32x2 + #define vec_hadd Simd::m256_hadd +#elif defined (USE_SSSE3) + using vec_t = __m128i; + #define vec_setzero _mm_setzero_si128 + #define vec_set_32 _mm_set1_epi32 + #define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32 + #define vec_add_dpbusd_32x2 Simd::m128_add_dpbusd_epi32x2 + #define vec_hadd Simd::m128_hadd +#endif + + const auto inputVector = reinterpret_cast(input); + + static constexpr IndexType InputSimdWidth = sizeof(vec_t) / sizeof(InputType); + + static_assert(PaddedInputDimensions % InputSimdWidth == 0); + + constexpr IndexType NumChunks = PaddedInputDimensions / InputSimdWidth; vec_t sum0 = vec_setzero(); const auto row0 = reinterpret_cast(&weights[0]); @@ -277,13 +308,14 @@ namespace Stockfish::Eval::NNUE::Layers { vec_add_dpbusd_32(sum0, in, row0[j]); } output[0] = vec_hadd(sum0, biases[0]); - } # undef vec_setzero # undef vec_set_32 # undef vec_add_dpbusd_32 # undef vec_add_dpbusd_32x2 # undef vec_hadd + + } #else // Use old implementation for the other architectures. affine_transform_non_ssse3< From 46a5cedc11bbad4a35f36aec660f270680008f37 Mon Sep 17 00:00:00 2001 From: Sebastian Buchwald Date: Sun, 10 Sep 2023 12:15:06 +0200 Subject: [PATCH 1190/1766] Cleanup git checkout actions We now fetch only the current commit for jobs that don't need the git history. For the Prerelease job, we don't checkout the code at all. closes https://github.com/official-stockfish/Stockfish/pull/4779 No functional change --- .github/workflows/stockfish.yml | 4 ---- .github/workflows/stockfish_compile_test.yml | 2 -- .github/workflows/stockfish_sanitizers.yml | 2 -- 3 files changed, 8 deletions(-) diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index 99c4259ac75..8ea1837d032 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -16,10 +16,6 @@ jobs: if: github.ref == 'refs/heads/master' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - # returns null if no pre-release exists - name: Get Commit SHA of Latest Pre-release run: | diff --git a/.github/workflows/stockfish_compile_test.yml b/.github/workflows/stockfish_compile_test.yml index 90e01537d57..808fcb55f7b 100644 --- a/.github/workflows/stockfish_compile_test.yml +++ b/.github/workflows/stockfish_compile_test.yml @@ -52,8 +52,6 @@ jobs: shell: ${{ matrix.config.shell }} steps: - uses: actions/checkout@v3 - with: - fetch-depth: 0 - name: Setup msys and install required packages if: runner.os == 'Windows' diff --git a/.github/workflows/stockfish_sanitizers.yml b/.github/workflows/stockfish_sanitizers.yml index 228742b3f12..b137f50eb06 100644 --- a/.github/workflows/stockfish_sanitizers.yml +++ b/.github/workflows/stockfish_sanitizers.yml @@ -36,8 +36,6 @@ jobs: shell: ${{ matrix.config.shell }} steps: - uses: actions/checkout@v3 - with: - fetch-depth: 0 - name: Download required linux packages run: | From 6d85f43e26cb8632337e67cea5ef88bab78121f3 Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Sun, 10 Sep 2023 08:49:18 +0800 Subject: [PATCH 1191/1766] Simplify cutnode depth condition With this patch, the depth condition for the cutnodes reduction is loosened from tte->depth() >= depth + 3 to just tte->depth() >= depth. Passed STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 101152 W: 25830 L: 25682 D: 49640 Ptnml(0-2): 312, 11788, 26258, 11876, 342 https://tests.stockfishchess.org/tests/view/64fd15635dab775b5359eaa6 Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 82542 W: 20980 L: 20824 D: 40738 Ptnml(0-2): 42, 8795, 23440, 8953, 41 https://tests.stockfishchess.org/tests/view/64fda3545dab775b5359fbf1 closes https://github.com/official-stockfish/Stockfish/pull/4780 Bench: 1479029 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index eefe5a3bcf9..a745d3bfdd0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1131,7 +1131,7 @@ namespace { // Decrease further on cutNodes. (~1 Elo) if ( ss->ttPv && !likelyFailLow) - r -= cutNode && tte->depth() >= depth + 3 ? 3 : 2; + r -= cutNode && tte->depth() >= depth ? 3 : 2; // Decrease reduction if opponent's move count is high (~1 Elo) if ((ss-1)->moveCount > 8) From ef2282961602f47a9c0c11adc2c0da7af39dab0f Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Mon, 11 Sep 2023 15:37:18 +0300 Subject: [PATCH 1192/1766] Do more futility pruning in qsearch This patch introduces a third futility pruning heuristic in qsearch. The idea is that the static exchange evaluation is much worse than the difference between futility base and alpha. Thus we can assume that the probability of the move being good enough to beat alpha is low so it can be pruned. Passed STC: https://tests.stockfishchess.org/tests/view/64fc982a5dab775b5359dc83 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 36576 W: 9484 L: 9170 D: 17922 Ptnml(0-2): 121, 4119, 9495, 4431, 122 Passed LTC: https://tests.stockfishchess.org/tests/view/64fcc7935dab775b5359e1a9 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 135408 W: 34556 L: 34041 D: 66811 Ptnml(0-2): 56, 14462, 38165, 14953, 68 closes https://github.com/official-stockfish/Stockfish/pull/4781 Bench: 1330793 --- src/search.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index a745d3bfdd0..beb1cb54822 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1549,17 +1549,29 @@ namespace { futilityValue = futilityBase + PieceValue[pos.piece_on(to_sq(move))]; + // If static eval + value of piece we are going to capture is much lower + // than alpha we can prune this move if (futilityValue <= alpha) { bestValue = std::max(bestValue, futilityValue); continue; } + // If static eval is much lower than alpha and move is not winning material + // we can prune this move if (futilityBase <= alpha && !pos.see_ge(move, VALUE_ZERO + 1)) { bestValue = std::max(bestValue, futilityBase); continue; } + + // If static exchange evaluation is much worse than what is needed to not + // fall below alpha we can prune this move + if (futilityBase > alpha && !pos.see_ge(move, (alpha - futilityBase) * 4)) + { + bestValue = alpha; + continue; + } } // We prune after the second quiet check evasion move, where being 'in check' is From 3d1b067d853d6e8cc22cf18c1abb4cd9833dd38f Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Sat, 9 Sep 2023 10:24:57 -0400 Subject: [PATCH 1193/1766] Update default net to nn-1ee1aba5ed4c.nnue Created by retraining the master net on a dataset composed by: - adding Leela data from T60 jul-dec 2020, T77 nov 2021, T80 jun-jul 2023 - deduplicating and unminimizing parts of the dataset before interleaving Trained initially with max epoch 800, then increased near the end of training twice. First to 960, then 1200. After training, post-processing involved: - greedy permuting L1 weights with https://github.com/official-stockfish/Stockfish/pull/4620 - greedy 2- and 3- cycle permuting with https://github.com/official-stockfish/Stockfish/pull/4640 python3 easy_train.py \ --experiment-name 2048-retrain-S6-sk28 \ --training-dataset /data/S6.binpack \ --early-fen-skipping 28 \ --start-from-engine-test-net True \ --max_epoch 1200 \ --lr 4.375e-4 \ --gamma 0.995 \ --start-lambda 1.0 \ --end-lambda 0.7 \ --tui False \ --seed $RANDOM \ --gpus 0 In the list of datasets below, periods in the filename represent the sequence of steps applied to arrive at the particular binpack. For example: test77-dec2021-16tb7p.filter-v6-dd.min-mar2023.unminimized.binpack 1. test77 dec2021 data rescored with 16 TB of syzygy tablebases during data conversion 2. filtered with csv_filter_v6_dd.py - v6 filtering and deduplication in one step 3. minimized with the original mar2023 implementation of `minimize_binpack` in the tools branch 4. unminimized by removing all positions with score == 32002 (`VALUE_NONE`) Binpacks were: - filtered with: https://github.com/linrock/nnue-data - unminimized with: https://github.com/linrock/Stockfish/tree/tools-unminify - deduplicated with: https://github.com/linrock/Stockfish/tree/tools-dd DATASETS=( leela96-filt-v2.min.unminimized.binpack dfrc99-16tb7p-eval-filt-v2.min.unminimized.binpack # most of the 0dd1cebea57 v6-dd dataset (without test80-jul2022) # https://github.com/official-stockfish/Stockfish/pull/4606 test60-novdec2021-12tb7p.filter-v6-dd.min-mar2023.unminimized.binpack test77-dec2021-16tb7p.filter-v6-dd.min-mar2023.unminimized.binpack test78-jantomay2022-16tb7p.filter-v6-dd.min-mar2023.unminimized.binpack test78-juntosep2022-16tb7p.filter-v6-dd.min-mar2023.unminimized.binpack test79-apr2022-16tb7p.filter-v6-dd.min-mar2023.unminimized.binpack test79-may2022-16tb7p.filter-v6-dd.min-mar2023.unminimized.binpack test80-jun2022-16tb7p.filter-v6-dd.min-mar2023.unminimized.binpack test80-aug2022-16tb7p.filter-v6-dd.min-mar2023.unminimized.binpack test80-sep2022-16tb7p.filter-v6-dd.min-mar2023.unminimized.binpack test80-oct2022-16tb7p.filter-v6-dd.min.binpack test80-nov2022-16tb7p.filter-v6-dd.min.binpack test80-jan2023-3of3-16tb7p.filter-v6-dd.min-mar2023.unminimized.binpack test80-feb2023-16tb7p.filter-v6-dd.min-mar2023.unminimized.binpack # older Leela data, recently converted test60-octnovdec2020-2tb7p.min.unminimized.binpack test60-julaugsep2020-2tb7p.min.binpack test77-nov2021-2tb7p.min.dd.binpack # newer Leela data test80-mar2023-2tb7p.min.unminimized.binpack test80-apr2023-2tb7p.filter-v6-sk16.min.unminimized.binpack test80-may2023-2tb7p.min.dd.binpack test80-jun2023-2tb7p.min.binpack test80-jul2023-2tb7p.binpack ) python3 interleave_binpacks.py ${DATASETS[@]} /data/S6.binpack Training data can be found at: https://robotmoon.com/nnue-training-data/ Local elo at 25k nodes per move: nn-epoch1059 : 2.7 +/- 1.6 Passed STC: https://tests.stockfishchess.org/tests/view/64fc8d705dab775b5359db42 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 168352 W: 43216 L: 42704 D: 82432 Ptnml(0-2): 599, 19672, 43134, 20160, 611 Passed LTC: https://tests.stockfishchess.org/tests/view/64fd44a75dab775b5359f065 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 154194 W: 39436 L: 38881 D: 75877 Ptnml(0-2): 78, 16577, 43238, 17120, 84 closes https://github.com/official-stockfish/Stockfish/pull/4782 Bench: 1603079 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 7f4feedf0c6..8ac24daea17 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -40,7 +40,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-c38c3d8d3920.nnue" + #define EvalFileDefaultName "nn-1ee1aba5ed4c.nnue" namespace NNUE { From b9319c4fa4f42438f484d144be9a1306765cf998 Mon Sep 17 00:00:00 2001 From: Sebastian Buchwald Date: Thu, 31 Aug 2023 21:56:34 +0200 Subject: [PATCH 1194/1766] Cleanup code after dropping ICC support in favor of ICX The commit removes all uses of ICC's __INTEL_COMPILER macro and other references to ICC. It also adds ICX info to the compiler command and fixes two typos in Makefile's help output. closes https://github.com/official-stockfish/Stockfish/pull/4769 No functional change --- src/Makefile | 4 ++-- src/bitboard.h | 4 ++-- src/misc.cpp | 29 +++++++++++------------------ src/types.h | 19 ++++++++++--------- 4 files changed, 25 insertions(+), 31 deletions(-) diff --git a/src/Makefile b/src/Makefile index d6443845477..f5a420b7ce0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -804,8 +804,8 @@ help: @echo "" @echo "Supported compilers:" @echo "" - @echo "gcc > Gnu compiler (default)" - @echo "mingw > Gnu compiler with MinGW under Windows" + @echo "gcc > GNU compiler (default)" + @echo "mingw > GNU compiler with MinGW under Windows" @echo "clang > LLVM Clang compiler" @echo "icx > Intel oneAPI DPC++/C++ Compiler" @echo "ndk > Google NDK to cross-compile for Android" diff --git a/src/bitboard.h b/src/bitboard.h index f9175333745..c05b6e3f8cf 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -262,7 +262,7 @@ inline int popcount(Bitboard b) { union { Bitboard bb; uint16_t u[4]; } v = { b }; return PopCnt16[v.u[0]] + PopCnt16[v.u[1]] + PopCnt16[v.u[2]] + PopCnt16[v.u[3]]; -#elif defined(_MSC_VER) || defined(__INTEL_COMPILER) +#elif defined(_MSC_VER) return (int)_mm_popcnt_u64(b); @@ -276,7 +276,7 @@ inline int popcount(Bitboard b) { /// lsb() and msb() return the least/most significant bit in a non-zero bitboard -#if defined(__GNUC__) // GCC, Clang, ICC +#if defined(__GNUC__) // GCC, Clang, ICX inline Square lsb(Bitboard b) { assert(b); diff --git a/src/misc.cpp b/src/misc.cpp index a72a1c13319..83ea8e10fbf 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -193,22 +193,21 @@ std::string compiler_info() { /// 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 +/// __GNUC__ Compiler is GCC, Clang or ICX +/// __clang__ Compiler is Clang or ICX +/// __INTEL_LLVM_COMPILER Compiler is ICX +/// _MSC_VER Compiler is MSVC +/// _WIN32 Building on Windows (any) +/// _WIN64 Building on Windows 64 bit std::string compiler = "\nCompiled by "; - #ifdef __clang__ + #if defined(__INTEL_LLVM_COMPILER) + compiler += "ICX "; + compiler += stringify(__INTEL_LLVM_COMPILER); + #elif defined(__clang__) compiler += "clang++ "; compiler += make_version_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 "; @@ -425,13 +424,7 @@ void prefetch(void*) {} void prefetch(void* addr) { -# if defined(__INTEL_COMPILER) - // This hack prevents prefetches from being optimized away by - // Intel compiler. Both MSVC and gcc seem not be affected by this. - __asm__ (""); -# endif - -# if defined(__INTEL_COMPILER) || defined(_MSC_VER) +# if defined(_MSC_VER) _mm_prefetch((char*)addr, _MM_HINT_T0); # else __builtin_prefetch(addr); diff --git a/src/types.h b/src/types.h index bb319c2bb08..f81d30fe032 100644 --- a/src/types.h +++ b/src/types.h @@ -48,11 +48,12 @@ /// 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 +/// __GNUC__ Compiler is GCC, Clang or ICX +/// __clang__ Compiler is Clang or ICX +/// __INTEL_LLVM_COMPILER Compiler is ICX +/// _MSC_VER Compiler is MSVC +/// _WIN32 Building on Windows (any) +/// _WIN64 Building on Windows 64 bit #if defined(__GNUC__ ) && (__GNUC__ < 9 || (__GNUC__ == 9 && __GNUC_MINOR__ <= 2)) && defined(_WIN32) && !defined(__clang__) #define ALIGNAS_ON_STACK_VARIABLES_BROKEN @@ -65,12 +66,12 @@ # define IS_64BIT #endif -#if defined(USE_POPCNT) && (defined(__INTEL_COMPILER) || defined(_MSC_VER)) -# include // Intel and Microsoft header for _mm_popcnt_u64() +#if defined(USE_POPCNT) && defined(_MSC_VER) +# include // Microsoft header for _mm_popcnt_u64() #endif -#if !defined(NO_PREFETCH) && (defined(__INTEL_COMPILER) || defined(_MSC_VER)) -# include // Intel and Microsoft header for _mm_prefetch() +#if !defined(NO_PREFETCH) && defined(_MSC_VER) +# include // Microsoft header for _mm_prefetch() #endif #if defined(USE_PEXT) From 3f7fb5ac1d58e1c90db063053e9f913b9df79994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Mon, 11 Sep 2023 23:19:06 +0200 Subject: [PATCH 1195/1766] Reformat some comments Also include the bench to make Continuation Integration happy on Github. Bench: 1603079 --- src/search.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index beb1cb54822..4b403c49d8e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -928,7 +928,8 @@ namespace { moveCountPruning = singularQuietLMR = false; // Indicate PvNodes that will probably fail low if the node was searched - // at a depth equal to or greater than the current depth, and the result of this search was a fail low. + // at a depth equal to or greater than the current depth, and the result + // of this search was a fail low. bool likelyFailLow = PvNode && ttMove && (tte->bound() & BOUND_UPPER) @@ -1039,10 +1040,10 @@ namespace { // Singular extension search (~94 Elo). If all moves but one fail low on a // search of (alpha-s, beta-s), and just one fails high on (alpha, beta), // 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. - // Depth margin and singularBeta margin are known for having non-linear scaling. - // Their values are optimized to time controls of 180+1.8 and longer + // 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. Note + // that depth margin and singularBeta margin are known for having non-linear + // scaling. Their values are optimized to time controls of 180+1.8 and longer // so changing them requires tests at this type of time controls. if ( !rootNode && depth >= 4 - (thisThread->completedDepth > 22) + 2 * (PvNode && tte->is_pv()) @@ -1076,10 +1077,10 @@ namespace { } // Multi-cut pruning - // Our ttMove is assumed to fail high, and now we failed high also on a reduced - // search without the ttMove. So we assume this expected Cut-node is not singular, - // that multiple moves fail high, and we can prune the whole subtree by returning - // a softbound. + // Our ttMove is assumed to fail high, and now we failed high also on a + // reduced search without the ttMove. So we assume this expected cut-node + // is not singular, that multiple moves fail high, and we can prune the + // whole subtree by returning a softbound. else if (singularBeta >= beta) return singularBeta; @@ -1126,8 +1127,7 @@ namespace { // Step 16. Make the move pos.do_move(move, st, givesCheck); - // Decrease reduction if position is or has been on the PV - // and node is not likely to fail low. (~3 Elo) + // Decrease reduction if position is or has been on the PV and not likely to fail low. (~3 Elo) // Decrease further on cutNodes. (~1 Elo) if ( ss->ttPv && !likelyFailLow) @@ -1162,6 +1162,7 @@ namespace { if ((ss+1)->cutoffCnt > 3) r++; + // Decrease reduction for first generated move (ttMove) else if (move == ttMove) r--; From 0e32287af470dee230a30d9f513682c3ce798668 Mon Sep 17 00:00:00 2001 From: peregrineshahin Date: Thu, 14 Sep 2023 14:15:43 +0300 Subject: [PATCH 1196/1766] Reorder some lines Now that qsearch has its own repetition detection we can flip the order of lines and remove the guard of depth < 0 which is not needed after reordering (i.e. it was there to prevent checking repetition again at depth ==0). Passed STC: https://tests.stockfishchess.org/tests/view/6502ecbb2cd016da89abc3fb LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 69536 W: 17668 L: 17490 D: 34378 Ptnml(0-2): 190, 7652, 18929, 7784, 213 Passed LTC: https://tests.stockfishchess.org/tests/view/6505ce9072620bc881ea9086 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 52116 W: 13294 L: 13113 D: 25709 Ptnml(0-2): 26, 5176, 15471, 5361, 24 closes https://github.com/official-stockfish/Stockfish/pull/4791 No functional change --- src/search.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 4b403c49d8e..cae91018931 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -525,6 +525,10 @@ namespace { constexpr bool PvNode = nodeType != NonPV; constexpr bool rootNode = nodeType == Root; + // Dive into quiescence search when the depth reaches zero + if (depth <= 0) + return qsearch(pos, ss, alpha, beta); + // Check if we have an upcoming move that draws by repetition, or // if the opponent had an alternative move earlier to this position. if ( !rootNode @@ -536,10 +540,6 @@ namespace { return alpha; } - // Dive into quiescence search when the depth reaches zero - if (depth <= 0) - return qsearch(pos, ss, alpha, beta); - assert(-VALUE_INFINITE <= alpha && alpha < beta && beta <= VALUE_INFINITE); assert(PvNode || (alpha == beta - 1)); assert(0 < depth && depth < MAX_PLY); @@ -1407,8 +1407,7 @@ namespace { // Check if we have an upcoming move that draws by repetition, or // if the opponent had an alternative move earlier to this position. - if ( depth < 0 - && alpha < VALUE_DRAW + if ( alpha < VALUE_DRAW && pos.has_game_cycle(ss->ply)) { alpha = value_draw(pos.this_thread()); From 97f706ecc11459c8d0aa1901134d12fba00b4b15 Mon Sep 17 00:00:00 2001 From: mstembera Date: Tue, 12 Sep 2023 12:23:24 -0700 Subject: [PATCH 1197/1766] Sparse impl of affine_transform_non_ssse3() deal with the general case About a 8.6% speedup (for general arch) Results for 200 tests for each version: Base Test Diff Mean 141741 153998 -12257 StDev 2990 3042 3742 p-value: 0.999 speedup: 0.086 closes https://github.com/official-stockfish/Stockfish/pull/4786 No functional change --- src/nnue/layers/affine_transform.h | 20 ++++++++++++++------ src/nnue/layers/clipped_relu.h | 2 +- src/nnue/layers/sqr_clipped_relu.h | 6 +++--- src/nnue/nnue_feature_transformer.h | 6 +++--- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 61cdb781866..af85c817c2a 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -45,6 +45,7 @@ namespace Stockfish::Eval::NNUE::Layers { template static void affine_transform_non_ssse3(std::int32_t* output, const std::int8_t* weights, const std::int32_t* biases, const std::uint8_t* input) { +# if defined(USE_SSE2) || defined(USE_MMX) || defined(USE_NEON_DOTPROD) || defined(USE_NEON) # if defined(USE_SSE2) // At least a multiple of 16, with SSE2. constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 16) / 16; @@ -129,18 +130,25 @@ namespace Stockfish::Eval::NNUE::Layers { } output[i] = sum[0] + sum[1] + sum[2] + sum[3]; -# else - std::int32_t sum = biases[i]; - for (IndexType j = 0; j < InputDimensions; ++j) { - sum += weights[offset + j] * input[j]; - } - output[i] = sum; # endif } # if defined(USE_MMX) _mm_empty(); # endif + +# else + std::memcpy(output, biases, sizeof(std::int32_t) * OutputDimensions); + + // Traverse weights in transpose order to take advantage of input sparsity + for (IndexType i = 0; i < InputDimensions; ++i) + if (input[i]) { + const std::int8_t* w = &weights[i]; + const int in = input[i]; + for (IndexType j = 0; j < OutputDimensions; ++j) + output[j] += w[j * PaddedInputDimensions] * in; + } +# endif } #endif diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index 2856bfb0a63..aab824b3572 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -172,7 +172,7 @@ namespace Stockfish::Eval::NNUE::Layers { for (IndexType i = Start; i < InputDimensions; ++i) { output[i] = static_cast( - std::max(0, std::min(127, input[i] >> WeightScaleBits))); + std::clamp(input[i] >> WeightScaleBits, 0, 127)); } } }; diff --git a/src/nnue/layers/sqr_clipped_relu.h b/src/nnue/layers/sqr_clipped_relu.h index 503b283b25e..a3d2059b4de 100644 --- a/src/nnue/layers/sqr_clipped_relu.h +++ b/src/nnue/layers/sqr_clipped_relu.h @@ -96,9 +96,9 @@ namespace Stockfish::Eval::NNUE::Layers { for (IndexType i = Start; i < InputDimensions; ++i) { output[i] = static_cast( - // really should be /127 but we need to make it fast - // needs to be accounted for in the trainer - std::min(127ll, (((long long)input[i] * input[i]) >> (2 * WeightScaleBits)) / 128)); + // Really should be /127 but we need to make it fast so we right shift + // by an extra 7 bits instead. Needs to be accounted for in the trainer. + std::min(127ll, ((long long)input[i] * input[i]) >> (2 * WeightScaleBits + 7))); } } }; diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 0af0ed96cc5..56442bac9b1 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -327,9 +327,9 @@ namespace Stockfish::Eval::NNUE { for (IndexType j = 0; j < HalfDimensions / 2; ++j) { BiasType sum0 = accumulation[static_cast(perspectives[p])][j + 0]; BiasType sum1 = accumulation[static_cast(perspectives[p])][j + HalfDimensions / 2]; - sum0 = std::max(0, std::min(127, sum0)); - sum1 = std::max(0, std::min(127, sum1)); - output[offset + j] = static_cast(sum0 * sum1 / 128); + sum0 = std::clamp(sum0, 0, 127); + sum1 = std::clamp(sum1, 0, 127); + output[offset + j] = static_cast(unsigned(sum0 * sum1) / 128); } #endif From 708319a433951ee5d5d74e0bf1cda218c14dd18e Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 26 Aug 2023 11:38:16 +0200 Subject: [PATCH 1198/1766] Enable a default native ARCH uses a posix compatible script to find the native arch. (based on ppigazzini's https://github.com/ppigazzini/stockfish-downloader ) use that native arch by default, no changes if ARCH is specified explicitly. SF can now be compiled in an optimal way simply using make -j profile-build closes https://github.com/official-stockfish/Stockfish/pull/4777 No functional change --- scripts/get_native_properties.sh | 121 +++++++++++++++++++++++++++++++ src/Makefile | 18 ++--- 2 files changed, 130 insertions(+), 9 deletions(-) create mode 100755 scripts/get_native_properties.sh diff --git a/scripts/get_native_properties.sh b/scripts/get_native_properties.sh new file mode 100755 index 00000000000..cffb0ce2731 --- /dev/null +++ b/scripts/get_native_properties.sh @@ -0,0 +1,121 @@ +#!/bin/sh + +# +# Returns properties of the native system. +# best architecture as supported by the CPU +# filename of the best binary uploaded as an artifact during CI +# + +# Check if all the given flags are present in the CPU flags list +check_flags() { + for flag; do + printf '%s\n' "$flags" | grep -q -w "$flag" || return 1 + done +} + +# Set the CPU flags list +get_flags() { + flags="$(awk '/^flags[ \t]*:/{gsub(/^flags[ \t]*:[ \t]*/, ""); line=$0} END{print line}' /proc/cpuinfo) $(awk '/^Features[ \t]*:/{gsub(/^Features[ \t]*:[ \t]*/, ""); line=$0} END{print line}' /proc/cpuinfo)" + # remove underscores and points from flags, e.g. gcc uses avx512vnni, while some cpuinfo can have avx512_vnni, some systems use sse4_1 others sse4.1 + flags=$(printf '%s' "$flags" | sed "s/[_.]//g") +} + +# Check for gcc march "znver1" or "znver2" https://en.wikichip.org/wiki/amd/cpuid +check_znver_1_2() { + vendor_id=$(awk '/^vendor_id/{print $3; exit}' /proc/cpuinfo) + cpu_family=$(awk '/^cpu family/{print $4; exit}' /proc/cpuinfo) + [ "$vendor_id" = "AuthenticAMD" ] && [ "$cpu_family" = "23" ] && znver_1_2=true +} + +# Set the file CPU x86_64 architecture +set_arch_x86_64() { + if check_flags 'avx512vnni' 'avx512dq' 'avx512f' 'avx512bw' 'avx512vl'; then + true_arch='x86-64-vnni256' + elif check_flags 'avx512f' 'avx512bw'; then + true_arch='x86-64-avx512' + elif [ -z "${znver_1_2+1}" ] && check_flags 'bmi2'; then + true_arch='x86-64-bmi2' + elif check_flags 'avx2'; then + true_arch='x86-64-avx2' + elif check_flags 'sse41' && check_flags 'popcnt'; then + true_arch='x86-64-sse41-popcnt' + else + true_arch='x86-64' + fi +} + +# Check the system type +uname_s=$(uname -s) +uname_m=$(uname -m) +case $uname_s in + 'Darwin') # Mac OSX system + case $uname_m in + 'arm64') + true_arch='apple-silicon' + file_arch='x86-64-sse41-popcnt' # Supported by Rosetta 2 + ;; + 'x86_64') + flags=$(sysctl -n machdep.cpu.features machdep.cpu.leaf7_features | tr '\n' ' ' | tr '[:upper:]' '[:lower:]' | sed "s/[_.]//g") + set_arch_x86_64 + if [ "$true_arch" = 'x86-64-vnni256' ] || [ "$true_arch" = 'x86-64-avx512' ]; then + file_arch='x86-64-bmi2' + fi + ;; + esac + file_os='macos' + file_ext='tar' + ;; + 'Linux') # Linux system + get_flags + case $uname_m in + 'x86_64') + file_os='ubuntu' + check_znver_1_2 + set_arch_x86_64 + ;; + 'i686') + file_os='ubuntu' + true_arch='x86-32' + ;; + 'aarch64') + file_os='android' + true_arch='armv8' + if check_flags 'dotprod'; then + true_arch="$true_arch-dotprod" + fi + ;; + 'armv7'*) + file_os='android' + true_arch='armv7' + if check_flags 'neon'; then + true_arch="$true_arch-neon" + fi + ;; + *) # Unsupported machine type, exit with error + printf 'Unsupported machine type: %s\n' "$uname_m" + exit 1 + ;; + esac + file_ext='tar' + ;; + 'CYGWIN'*|'MINGW'*|'MSYS'*) # Windows system with POSIX compatibility layer + get_flags + check_znver_1_2 + set_arch_x86_64 + file_os='windows' + file_ext='zip' + ;; + *) + # Unknown system type, exit with error + printf 'Unsupported system type: %s\n' "$uname_s" + exit 1 + ;; +esac + +if [ -z "$file_arch" ]; then + file_arch=$true_arch +fi + +file_name="stockfish-$file_os-$file_arch.$file_ext" + +printf '%s %s\n' "$true_arch" "$file_name" diff --git a/src/Makefile b/src/Makefile index f5a420b7ce0..bf483f8c77e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -104,9 +104,13 @@ VPATH = syzygy:nnue:nnue/features ### 2.1. General and architecture defaults ifeq ($(ARCH),) - ARCH = x86-64-avx2 - help_skip_sanity = yes + ARCH = native endif + +ifeq ($(ARCH), native) + override ARCH = $(shell ../scripts/get_native_properties.sh | cut -d " " -f 1) +endif + # explicitly check for the list of supported architectures (as listed with make help), # the user can override with `make ARCH=x86-32-vnni256 SUPPORTED_ARCH=true` ifeq ($(ARCH), $(filter $(ARCH), \ @@ -757,12 +761,11 @@ endif ### Section 4. Public Targets ### ========================================================================== - help: @echo "" @echo "To compile stockfish, type: " @echo "" - @echo "make target ARCH=arch [COMP=compiler] [COMPCXX=cxx]" + @echo "make -j target [ARCH=arch] [COMP=compiler] [COMPCXX=cxx]" @echo "" @echo "Supported targets:" @echo "" @@ -776,6 +779,7 @@ help: @echo "" @echo "Supported archs:" @echo "" + @echo "native > select the best architecture for the host processor (default)" @echo "x86-64-vnni512 > x86 64-bit with vnni 512bit support" @echo "x86-64-vnni256 > x86 64-bit with vnni 512bit support, limit operands to 256bit wide" @echo "x86-64-avx512 > x86 64-bit with avx512 support" @@ -822,11 +826,7 @@ help: @echo "make -j profile-build ARCH=x86-64-avxvnni COMP=gcc COMPCXX=g++-12.0" @echo "make -j build ARCH=x86-64-ssse3 COMP=clang" @echo "" - @echo "-------------------------------" -ifeq ($(SUPPORTED_ARCH)$(help_skip_sanity), true) - @echo "The selected architecture $(ARCH) will enable the following configuration: " - @$(MAKE) ARCH=$(ARCH) COMP=$(COMP) config-sanity -else +ifneq ($(SUPPORTED_ARCH), true) @echo "Specify a supported architecture with the ARCH option for more details" @echo "" endif From e594aa74290cf37881432f268befde9ad3f3c498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Thu, 14 Sep 2023 11:04:36 +0200 Subject: [PATCH 1199/1766] Export makefile ARCH in binary Example of the `./stockfish compiler` command: Compiled by : g++ (GNUC) 10.3.0 on Apple Compilation architecture : x86-64-bmi2 Compilation settings : 64bit BMI2 AVX2 SSE41 SSSE3 SSE2 POPCNT Compiler __VERSION__ macro : 10.3.0 closes https://github.com/official-stockfish/Stockfish/pull/4789 no functional change --- src/Makefile | 5 +++++ src/misc.cpp | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/Makefile b/src/Makefile index bf483f8c77e..e7c06389bdd 100644 --- a/src/Makefile +++ b/src/Makefile @@ -715,6 +715,11 @@ ifneq ($(GIT_DATE), ) CXXFLAGS += -DGIT_DATE=$(GIT_DATE) endif +### 3.7.3 Try to include architecture +ifneq ($(ARCH), ) + CXXFLAGS += -DARCH=$(ARCH) +endif + ### 3.8 Link Time Optimization ### This is a mix of compile and link time options because the lto link phase ### needs access to the optimization flags. diff --git a/src/misc.cpp b/src/misc.cpp index 83ea8e10fbf..aecc4d23e8c 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -200,7 +200,7 @@ std::string compiler_info() { /// _WIN32 Building on Windows (any) /// _WIN64 Building on Windows 64 bit - std::string compiler = "\nCompiled by "; + std::string compiler = "\nCompiled by : "; #if defined(__INTEL_LLVM_COMPILER) compiler += "ICX "; @@ -253,8 +253,15 @@ std::string compiler_info() { compiler += " on unknown system"; #endif - compiler += "\nCompilation settings include: "; - compiler += (Is64Bit ? " 64bit" : " 32bit"); + compiler += "\nCompilation architecture : "; + #if defined(ARCH) + compiler += stringify(ARCH); + #else + compiler += "(undefined architecture)"; + #endif + + compiler += "\nCompilation settings : "; + compiler += (Is64Bit ? "64bit" : "32bit"); #if defined(USE_VNNI) compiler += " VNNI"; #endif @@ -288,12 +295,13 @@ std::string compiler_info() { compiler += " DEBUG"; #endif - compiler += "\n__VERSION__ macro expands to: "; + compiler += "\nCompiler __VERSION__ macro : "; #ifdef __VERSION__ compiler += __VERSION__; #else compiler += "(undefined macro)"; #endif + compiler += "\n"; return compiler; From 952740b36ca46961a64457767f58dfbe71ae1ead Mon Sep 17 00:00:00 2001 From: Sebastian Buchwald Date: Tue, 12 Sep 2023 00:03:04 +0200 Subject: [PATCH 1200/1766] Let CI check C++ includes The commit adds a CI workflow that uses the included-what-you-use (IWYU) tool to check for missing or superfluous includes in .cpp files and their corresponding .h files. This means that some .h files (especially in the nnue folder) are not checked yet. The CI setup looks like this: - We build IWYU from source to include some yet unreleased fixes. This IWYU version targets LLVM 17. Thus, we get the latest release candidate of LLVM 17 from LLVM's nightly packages. - The Makefile now has an analyze target that just build the object files (without linking) - The CI uses the analyze target with the IWYU tool as compiler to analyze the compiled .cpp file and its corresponding .h file. - If IWYU suggests a change the build fails (-Xiwyu --error). - To avoid false positives we use LLVM's libc++ as standard library - We have a custom mappings file that adds some mappings that are missing in IWYU's default mappings We also had to add one IWYU pragma to prevent a false positive in movegen.h. https://github.com/official-stockfish/Stockfish/pull/4783 No functional change --- .github/workflows/libcxx17.imp | 21 ++++++++++ .github/workflows/stockfish.yml | 2 + .github/workflows/stockfish_analyzers.yml | 47 +++++++++++++++++++++++ src/Makefile | 7 +++- src/evaluate.h | 1 - src/movegen.h | 2 +- 6 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/libcxx17.imp create mode 100644 .github/workflows/stockfish_analyzers.yml diff --git a/.github/workflows/libcxx17.imp b/.github/workflows/libcxx17.imp new file mode 100644 index 00000000000..7bdcf5bc2de --- /dev/null +++ b/.github/workflows/libcxx17.imp @@ -0,0 +1,21 @@ +[ + # Mappings for libcxx's internal headers + { include: [ "<__fwd/fstream.h>", private, "", public ] }, + { include: [ "<__fwd/ios.h>", private, "", public ] }, + { include: [ "<__fwd/istream.h>", private, "", public ] }, + { include: [ "<__fwd/ostream.h>", private, "", public ] }, + { include: [ "<__fwd/sstream.h>", private, "", public ] }, + { include: [ "<__fwd/streambuf.h>", private, "", public ] }, + { include: [ "<__fwd/string_view.h>", private, "", public ] }, + + # Mappings for includes between public headers + { include: [ "", public, "", public ] }, + { include: [ "", public, "", public ] }, + { include: [ "", public, "", public ] }, + { include: [ "", public, "", public ] }, + { include: [ "", public, "", public ] }, + + # Missing mappings in include-what-you-use's libcxx.imp + { include: ["@<__condition_variable/.*>", private, "", public ] }, + { include: ["@<__mutex/.*>", private, "", public ] }, +] diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index 8ea1837d032..1ed4b92d4ca 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -33,6 +33,8 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + Analyzers: + uses: ./.github/workflows/stockfish_analyzers.yml Sanitizers: uses: ./.github/workflows/stockfish_sanitizers.yml Tests: diff --git a/.github/workflows/stockfish_analyzers.yml b/.github/workflows/stockfish_analyzers.yml new file mode 100644 index 00000000000..5f985cc859f --- /dev/null +++ b/.github/workflows/stockfish_analyzers.yml @@ -0,0 +1,47 @@ +name: Stockfish +on: + workflow_call: +jobs: + Analyzers: + name: Check includes + runs-on: ubuntu-22.04 + defaults: + run: + working-directory: Stockfish/src + shell: bash + steps: + - name: Checkout Stockfish + uses: actions/checkout@v3 + with: + path: Stockfish + + - name: Checkout include-what-you-use + uses: actions/checkout@v3 + with: + repository: include-what-you-use/include-what-you-use + ref: f25caa280dc3277c4086ec345ad279a2463fea0f + path: include-what-you-use + + - name: Download required linux packages + run: | + sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-17 main' + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - + sudo apt update + sudo apt install -y libclang-17-dev clang-17 libc++-17-dev + + - name: Set up include-what-you-use + run: | + mkdir build && cd build + cmake -G "Unix Makefiles" -DCMAKE_PREFIX_PATH="/usr/lib/llvm-17" .. + sudo make install + working-directory: include-what-you-use + + - name: Check include-what-you-use + run: include-what-you-use --version + + - name: Check includes + run: > + make analyze + COMP=clang + CXX=include-what-you-use + CXXFLAGS="-stdlib=libc++ -Xiwyu --comment_style=long -Xiwyu --mapping='${{ github.workspace }}/Stockfish/.github/workflows/libcxx17.imp' -Xiwyu --error" diff --git a/src/Makefile b/src/Makefile index e7c06389bdd..1b03bbc2b0a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -837,12 +837,15 @@ ifneq ($(SUPPORTED_ARCH), true) endif -.PHONY: help build profile-build strip install clean net objclean profileclean \ - config-sanity \ +.PHONY: help analyze build profile-build strip install clean net \ + objclean profileclean config-sanity \ icx-profile-use icx-profile-make \ gcc-profile-use gcc-profile-make \ clang-profile-use clang-profile-make FORCE +analyze: net config-sanity objclean + $(MAKE) -k ARCH=$(ARCH) COMP=$(COMP) $(OBJS) + build: net config-sanity $(MAKE) ARCH=$(ARCH) COMP=$(COMP) all diff --git a/src/evaluate.h b/src/evaluate.h index 8ac24daea17..fd1b0de1f6e 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -26,7 +26,6 @@ namespace Stockfish { class Position; -enum Value : int; namespace Eval { diff --git a/src/movegen.h b/src/movegen.h index 6449de25794..b15f1230b13 100644 --- a/src/movegen.h +++ b/src/movegen.h @@ -19,7 +19,7 @@ #ifndef MOVEGEN_H_INCLUDED #define MOVEGEN_H_INCLUDED -#include +#include // IWYU pragma: keep #include #include "types.h" From fce4cc1829f25fd52c5dd637ab54d867eec065fb Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Tue, 19 Sep 2023 18:06:12 +0800 Subject: [PATCH 1201/1766] Make casting styles consistent Make casting styles consistent with the rest of the code. closes https://github.com/official-stockfish/Stockfish/pull/4793 No functional change --- src/bitboard.h | 2 +- src/misc.cpp | 24 ++++++++-------- src/misc.h | 8 +++--- src/movegen.cpp | 2 +- src/nnue/evaluate_nnue.cpp | 6 ++-- src/nnue/layers/affine_transform.h | 2 +- .../layers/affine_transform_sparse_input.h | 2 +- src/search.cpp | 6 ++-- src/syzygy/tbprobe.cpp | 28 +++++++++---------- src/tt.cpp | 20 ++++++------- src/tt.h | 12 ++++---- 11 files changed, 56 insertions(+), 56 deletions(-) diff --git a/src/bitboard.h b/src/bitboard.h index c05b6e3f8cf..dee73b4b3f7 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -264,7 +264,7 @@ inline int popcount(Bitboard b) { #elif defined(_MSC_VER) - return (int)_mm_popcnt_u64(b); + return int(_mm_popcnt_u64(b)); #else // Assumed gcc or compatible compiler diff --git a/src/misc.cpp b/src/misc.cpp index aecc4d23e8c..98e346a6689 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -85,7 +85,7 @@ struct Tie: public std::streambuf { // MSVC requires split streambuf for cin and Tie(std::streambuf* b, std::streambuf* l) : buf(b), logBuf(l) {} int sync() override { return logBuf->pubsync(), buf->pubsync(); } - int overflow(int c) override { return log(buf->sputc((char)c), "<< "); } + int overflow(int c) override { return log(buf->sputc(char(c)), "<< "); } int underflow() override { return buf->sgetc(); } int uflow() override { return log(buf->sbumpc(), ">> "); } @@ -98,7 +98,7 @@ struct Tie: public std::streambuf { // MSVC requires split streambuf for cin and if (last == '\n') logBuf->sputn(prefix, 3); - return last = logBuf->sputc((char)c); + return last = logBuf->sputc(char(c)); } }; @@ -215,9 +215,9 @@ std::string compiler_info() { compiler += ")"; #elif defined(__e2k__) && defined(__LCC__) #define dot_ver2(n) \ - compiler += (char)'.'; \ - compiler += (char)('0' + (n) / 10); \ - compiler += (char)('0' + (n) % 10); + compiler += char('.'); \ + compiler += char('0' + (n) / 10); \ + compiler += char('0' + (n) % 10); compiler += "MCST LCC "; compiler += "(version "; @@ -498,13 +498,13 @@ static void* aligned_large_pages_alloc_windows([[maybe_unused]] size_t allocSize if (!hAdvapi32) hAdvapi32 = LoadLibrary(TEXT("advapi32.dll")); - auto fun6 = (fun6_t)(void(*)())GetProcAddress(hAdvapi32, "OpenProcessToken"); + auto fun6 = fun6_t((void(*)())GetProcAddress(hAdvapi32, "OpenProcessToken")); if (!fun6) return nullptr; - auto fun7 = (fun7_t)(void(*)())GetProcAddress(hAdvapi32, "LookupPrivilegeValueA"); + auto fun7 = fun7_t((void(*)())GetProcAddress(hAdvapi32, "LookupPrivilegeValueA")); if (!fun7) return nullptr; - auto fun8 = (fun8_t)(void(*)())GetProcAddress(hAdvapi32, "AdjustTokenPrivileges"); + auto fun8 = fun8_t((void(*)())GetProcAddress(hAdvapi32, "AdjustTokenPrivileges")); if (!fun8) return nullptr; @@ -699,10 +699,10 @@ void bindThisThread(size_t idx) { // Early exit if the needed API are not available at runtime HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll")); - auto fun2 = (fun2_t)(void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMaskEx"); - auto fun3 = (fun3_t)(void(*)())GetProcAddress(k32, "SetThreadGroupAffinity"); - auto fun4 = (fun4_t)(void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMask2"); - auto fun5 = (fun5_t)(void(*)())GetProcAddress(k32, "GetMaximumProcessorGroupCount"); + auto fun2 = fun2_t((void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMaskEx")); + auto fun3 = fun3_t((void(*)())GetProcAddress(k32, "SetThreadGroupAffinity")); + auto fun4 = fun4_t((void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMask2")); + auto fun5 = fun5_t((void(*)())GetProcAddress(k32, "GetMaximumProcessorGroupCount")); if (!fun2 || !fun3) return; diff --git a/src/misc.h b/src/misc.h index aed677b5b29..c0387f7c4c9 100644 --- a/src/misc.h +++ b/src/misc.h @@ -133,13 +133,13 @@ class PRNG { inline uint64_t mul_hi64(uint64_t a, uint64_t b) { #if defined(__GNUC__) && defined(IS_64BIT) __extension__ using uint128 = unsigned __int128; - return ((uint128)a * (uint128)b) >> 64; + return (uint128(a) * uint128(b)) >> 64; #else - uint64_t aL = (uint32_t)a, aH = a >> 32; - uint64_t bL = (uint32_t)b, bH = b >> 32; + uint64_t aL = uint32_t(a), aH = a >> 32; + uint64_t bL = uint32_t(b), bH = b >> 32; uint64_t c1 = (aL * bL) >> 32; uint64_t c2 = aH * bL + c1; - uint64_t c3 = aL * bH + (uint32_t)c2; + uint64_t c3 = aL * bH + uint32_t(c2); return aH * bH + (c2 >> 32) + (c3 >> 32); #endif } diff --git a/src/movegen.cpp b/src/movegen.cpp index f0733c73b66..c6a8dbb8cb7 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -246,7 +246,7 @@ template ExtMove* generate(const Position& pos, ExtMove* moveList) { static_assert(Type != LEGAL, "Unsupported type in generate()"); - assert((Type == EVASIONS) == (bool)pos.checkers()); + assert((Type == EVASIONS) == bool(pos.checkers())); Color us = pos.side_to_move(); diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 456f2edfdf3..e1fa3b814a1 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -139,7 +139,7 @@ namespace Stockfish::Eval::NNUE { if (!Detail::write_parameters(stream, *featureTransformer)) return false; for (std::size_t i = 0; i < LayerStacks; ++i) if (!Detail::write_parameters(stream, *(network[i]))) return false; - return (bool)stream; + return bool(stream); } void hint_common_parent_position(const Position& pos) { @@ -281,8 +281,8 @@ namespace Stockfish::Eval::NNUE { // A lambda to output one box of the board auto writeSquare = [&board](File file, Rank rank, Piece pc, Value value) { - const int x = ((int)file) * 8; - const int y = (7 - (int)rank) * 3; + const int x = int(file) * 8; + const int y = (7 - int(rank)) * 3; for (int i = 1; i < 8; ++i) board[y][x+i] = board[y+3][x+i] = '-'; for (int i = 1; i < 3; ++i) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index af85c817c2a..42839bb5bdc 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -310,7 +310,7 @@ namespace Stockfish::Eval::NNUE::Layers { vec_t sum0 = vec_setzero(); const auto row0 = reinterpret_cast(&weights[0]); - for (int j = 0; j < (int)NumChunks; ++j) + for (int j = 0; j < int(NumChunks); ++j) { const vec_t in = inputVector[j]; vec_add_dpbusd_32(sum0, in, row0[j]); diff --git a/src/nnue/layers/affine_transform_sparse_input.h b/src/nnue/layers/affine_transform_sparse_input.h index c9894f5d96e..1dc42109844 100644 --- a/src/nnue/layers/affine_transform_sparse_input.h +++ b/src/nnue/layers/affine_transform_sparse_input.h @@ -102,7 +102,7 @@ namespace Stockfish::Eval::NNUE::Layers { for (IndexType j = 0; j < InputsPerChunk; ++j) { const vec_t inputChunk = inputVector[i * InputsPerChunk + j]; - nnz |= (unsigned)vec_nnz(inputChunk) << (j * InputSimdWidth); + nnz |= unsigned(vec_nnz(inputChunk)) << (j * InputSimdWidth); } for (IndexType j = 0; j < OutputsPerChunk; ++j) { diff --git a/src/search.cpp b/src/search.cpp index cae91018931..936aa0db77f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -316,7 +316,7 @@ void Thread::search() { // When playing with strength handicap enable MultiPV search that we will // use behind-the-scenes to retrieve a set of possible moves. if (skill.enabled()) - multiPV = std::max(multiPV, (size_t)4); + multiPV = std::max(multiPV, size_t(4)); multiPV = std::min(multiPV, rootMoves.size()); @@ -1861,7 +1861,7 @@ void MainThread::check_time() { if ( (Limits.use_time_management() && (elapsed > Time.maximum() || stopOnPonderhit)) || (Limits.movetime && elapsed >= Limits.movetime) - || (Limits.nodes && Threads.nodes_searched() >= (uint64_t)Limits.nodes)) + || (Limits.nodes && Threads.nodes_searched() >= uint64_t(Limits.nodes))) Threads.stop = true; } @@ -1875,7 +1875,7 @@ string UCI::pv(const Position& pos, Depth depth) { TimePoint elapsed = Time.elapsed() + 1; const RootMoves& rootMoves = pos.this_thread()->rootMoves; size_t pvIdx = pos.this_thread()->pvIdx; - size_t multiPV = std::min((size_t)Options["MultiPV"], rootMoves.size()); + size_t multiPV = std::min(size_t(Options["MultiPV"]), rootMoves.size()); uint64_t nodesSearched = Threads.nodes_searched(); uint64_t tbHits = Threads.tb_hits() + (TB::RootInTB ? rootMoves.size() : 0); diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index d1b32d242c9..13d271fce8a 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -114,7 +114,7 @@ template T number(void* addr) { T v; - if ((uintptr_t)addr & (alignof(T) - 1)) // Unaligned pointer (very rare) + if (uintptr_t(addr) & (alignof(T) - 1)) // Unaligned pointer (very rare) std::memcpy(&v, addr, sizeof(T)); else v = *((T*)addr); @@ -263,7 +263,7 @@ class TBFile : public std::ifstream { exit(EXIT_FAILURE); } - *mapping = (uint64_t)mmap; + *mapping = uint64_t(mmap); *baseAddress = MapViewOfFile(mmap, FILE_MAP_READ, 0, 0, 0); if (!*baseAddress) @@ -429,7 +429,7 @@ class TBTables { std::deque> dtzTable; void insert(Key key, TBTable* wdl, TBTable* dtz) { - uint32_t homeBucket = (uint32_t)key & (Size - 1); + uint32_t homeBucket = uint32_t(key) & (Size - 1); Entry entry{ key, wdl, dtz }; // Ensure last element is empty to avoid overflow when looking up @@ -442,7 +442,7 @@ class TBTables { // Robin Hood hashing: If we've probed for longer than this element, // insert here and search for a new spot for the other element instead. - uint32_t otherHomeBucket = (uint32_t)otherKey & (Size - 1); + uint32_t otherHomeBucket = uint32_t(otherKey) & (Size - 1); if (otherHomeBucket > homeBucket) { std::swap(entry, hashTable[bucket]); key = otherKey; @@ -456,7 +456,7 @@ class TBTables { public: template TBTable* get(Key key) { - for (const Entry* entry = &hashTable[(uint32_t)key & (Size - 1)]; ; ++entry) { + for (const Entry* entry = &hashTable[uint32_t(key) & (Size - 1)]; ; ++entry) { if (entry->key == key || !entry->get()) return entry->get(); } @@ -489,7 +489,7 @@ void TBTables::add(const std::vector& pieces) { file.close(); - MaxCardinality = std::max((int)pieces.size(), MaxCardinality); + MaxCardinality = std::max(int(pieces.size()), MaxCardinality); wdlTable.emplace_back(code); dtzTable.emplace_back(wdlTable.back()); @@ -560,7 +560,7 @@ int decompress_pairs(PairsData* d, uint64_t idx) { offset -= d->blockLength[block++] + 1; // Finally, we find the start address of our block of canonical Huffman symbols - uint32_t* ptr = (uint32_t*)(d->data + ((uint64_t)block * d->sizeofBlock)); + uint32_t* ptr = (uint32_t*)(d->data + (uint64_t(block) * d->sizeofBlock)); // Read the first 64 bits in our block, this is a (truncated) sequence of // unknown number of symbols of unknown length but we know the first one @@ -600,7 +600,7 @@ int decompress_pairs(PairsData* d, uint64_t idx) { if (buf64Size <= 32) { // Refill the buffer buf64Size += 32; - buf64 |= (uint64_t)number(ptr++) << (64 - buf64Size); + buf64 |= uint64_t(number(ptr++)) << (64 - buf64Size); } } @@ -1054,22 +1054,22 @@ uint8_t* set_dtz_map(TBTable& e, uint8_t* data, File maxFile) { auto flags = e.get(0, f)->flags; if (flags & TBFlag::Mapped) { if (flags & TBFlag::Wide) { - data += (uintptr_t)data & 1; // Word alignment, we may have a mixed table + data += uintptr_t(data) & 1; // Word alignment, we may have a mixed table for (int i = 0; i < 4; ++i) { // Sequence like 3,x,x,x,1,x,0,2,x,x - e.get(0, f)->map_idx[i] = (uint16_t)((uint16_t *)data - (uint16_t *)e.map + 1); + e.get(0, f)->map_idx[i] = uint16_t((uint16_t*)data - (uint16_t*)e.map + 1); data += 2 * number(data) + 2; } } else { for (int i = 0; i < 4; ++i) { - e.get(0, f)->map_idx[i] = (uint16_t)(data - e.map + 1); + e.get(0, f)->map_idx[i] = uint16_t(data - e.map + 1); data += *data + 1; } } } } - return data += (uintptr_t)data & 1; // Word alignment + return data += uintptr_t(data) & 1; // Word alignment } // Populate entry's PairsData records with data from the just memory mapped file. @@ -1110,7 +1110,7 @@ void set(T& e, uint8_t* data) { set_groups(e, e.get(i, f), order[i], f); } - data += (uintptr_t)data & 1; // Word alignment + data += uintptr_t(data) & 1; // Word alignment for (File f = FILE_A; f <= maxFile; ++f) for (int i = 0; i < sides; i++) @@ -1132,7 +1132,7 @@ void set(T& e, uint8_t* data) { for (File f = FILE_A; f <= maxFile; ++f) for (int i = 0; i < sides; i++) { - data = (uint8_t*)(((uintptr_t)data + 0x3F) & ~0x3F); // 64 byte alignment + data = (uint8_t*)((uintptr_t(data) + 0x3F) & ~0x3F); // 64 byte alignment (d = e.get(i, f))->data = data; data += d->blocksNum * d->sizeofBlock; } diff --git a/src/tt.cpp b/src/tt.cpp index 1582121fd6d..adcfe6289a0 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -39,22 +39,22 @@ TranspositionTable TT; // Our global transposition table void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) { // Preserve any existing move for the same position - if (m || (uint16_t)k != key16) - move16 = (uint16_t)m; + if (m || uint16_t(k) != key16) + move16 = uint16_t(m); // Overwrite less valuable entries (cheapest checks first) if ( b == BOUND_EXACT - || (uint16_t)k != key16 + || uint16_t(k) != key16 || d - DEPTH_OFFSET + 2 * pv > depth8 - 4) { assert(d > DEPTH_OFFSET); assert(d < 256 + DEPTH_OFFSET); - key16 = (uint16_t)k; - depth8 = (uint8_t)(d - DEPTH_OFFSET); - genBound8 = (uint8_t)(TT.generation8 | uint8_t(pv) << 2 | b); - value16 = (int16_t)v; - eval16 = (int16_t)ev; + key16 = uint16_t(k); + depth8 = uint8_t(d - DEPTH_OFFSET); + genBound8 = uint8_t(TT.generation8 | uint8_t(pv) << 2 | b); + value16 = int16_t(v); + eval16 = int16_t(ev); } } @@ -123,14 +123,14 @@ void TranspositionTable::clear() { TTEntry* TranspositionTable::probe(const Key key, bool& found) const { TTEntry* const tte = first_entry(key); - const uint16_t key16 = (uint16_t)key; // Use the low 16 bits as key inside the cluster + const uint16_t key16 = uint16_t(key); // Use the low 16 bits as key inside the cluster for (int i = 0; i < ClusterSize; ++i) if (tte[i].key16 == key16 || !tte[i].depth8) { tte[i].genBound8 = uint8_t(generation8 | (tte[i].genBound8 & (GENERATION_DELTA - 1))); // Refresh - return found = (bool)tte[i].depth8, &tte[i]; + return found = bool(tte[i].depth8), &tte[i]; } // Find an entry to be replaced according to the replacement strategy diff --git a/src/tt.h b/src/tt.h index df962faaa7b..c11cf085220 100644 --- a/src/tt.h +++ b/src/tt.h @@ -40,12 +40,12 @@ namespace Stockfish { 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 + DEPTH_OFFSET; } - bool is_pv() const { return (bool)(genBound8 & 0x4); } - Bound bound() const { return (Bound)(genBound8 & 0x3); } + Move move() const { return Move (move16); } + Value value() const { return Value(value16); } + Value eval() const { return Value(eval16); } + 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); private: From 95fe2b9a9d33811a7fcad1cdfea79c54e8fdb074 Mon Sep 17 00:00:00 2001 From: mstembera Date: Thu, 21 Sep 2023 19:26:11 -0700 Subject: [PATCH 1202/1766] Reduce SIMD register count from 32 to 16 in the case of avx512 and vnni512 archs. Up to 17% speedup, depending on the compiler, e.g. ``` AMD pro 7840u (zen4 phoenix apu 4nm) bash bench_parallel.sh ./stockfish_avx512_gcc13 ./stockfish_avx512_pr_gcc13 20 10 sf_base = 1077737 +/- 8446 (95%) sf_test = 1264268 +/- 8543 (95%) diff = 186531 +/- 4280 (95%) speedup = 17.308% +/- 0.397% (95%) ``` Prior to this patch, it appears gcc spills registers. closes https://github.com/official-stockfish/Stockfish/pull/4796 No functional change --- src/nnue/nnue_feature_transformer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 56442bac9b1..902918b2c6f 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -69,7 +69,7 @@ namespace Stockfish::Eval::NNUE { #define vec_add_psqt_32(a,b) _mm256_add_epi32(a,b) #define vec_sub_psqt_32(a,b) _mm256_sub_epi32(a,b) #define vec_zero_psqt() _mm256_setzero_si256() - #define NumRegistersSIMD 32 + #define NumRegistersSIMD 16 #define MaxChunkSize 64 #elif USE_AVX2 From 154b8d3ecb19d0b3fa9ec11cc3a1e666dfe0d2ce Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Tue, 19 Sep 2023 09:08:58 +0200 Subject: [PATCH 1203/1766] Remove VALUE_KNOWN_WIN. After removing classic evaluation VALUE_KNOWN_WIN is not anymore returned explicit evaluation. So remove and replace it with VALUE_TB_WIN_IN_MAX_PLY. Measurement on my big bench (bench 16 1 16 pos1000.fen) verifies that at least with current net the calculated evaluation lies always in the open interval (-VALUE_KNOWN_WIN, VALUE_KNOWN_WIN). So i consider this a non-functional change. But to be safe i tested this also at LTC as requested by Stephane Nicolet. STC: https://tests.stockfishchess.org/tests/view/64f9db40eaf01be8259a6ed5 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 455296 W: 115981 L: 116217 D: 223098 Ptnml(0-2): 1415, 50835, 123420, 50527, 1451 LTC: https://tests.stockfishchess.org/tests/view/650bfd867ca0d3f7bbf25feb LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 35826 W: 9170 L: 8973 D: 17683 Ptnml(0-2): 12, 3523, 10645, 3722, 11 closes https://github.com/official-stockfish/Stockfish/pull/4792 Bench: 1603079 --- src/search.cpp | 10 +++++----- src/types.h | 1 - 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 936aa0db77f..3e19000a5d6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -778,7 +778,7 @@ namespace { && depth < 9 && eval - futility_margin(depth, cutNode && !ss->ttHit, improving) - (ss-1)->statScore / 306 >= beta && eval >= beta - && eval < 24923) // larger than VALUE_KNOWN_WIN, but smaller than TB wins + && eval < 24923) // smaller than TB wins return eval; // Step 9. Null move search with verification search (~35 Elo) @@ -908,8 +908,8 @@ namespace { && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 4 && ttValue >= probCutBeta - && abs(ttValue) <= VALUE_KNOWN_WIN - && abs(beta) <= VALUE_KNOWN_WIN) + && abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY + && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) return probCutBeta; const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, @@ -1050,7 +1050,7 @@ namespace { && move == ttMove && !excludedMove // Avoid recursive singular search /* && ttValue != VALUE_NONE Already implicit in the next condition */ - && abs(ttValue) < VALUE_KNOWN_WIN + && abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { @@ -1541,7 +1541,7 @@ namespace { // Futility pruning and moveCount pruning (~10 Elo) if ( !givesCheck && to_sq(move) != prevSq - && futilityBase > -VALUE_KNOWN_WIN + && futilityBase > VALUE_TB_LOSS_IN_MAX_PLY && type_of(move) != PROMOTION) { if (moveCount > 2) diff --git a/src/types.h b/src/types.h index f81d30fe032..340c47a5fc2 100644 --- a/src/types.h +++ b/src/types.h @@ -161,7 +161,6 @@ enum Bound { enum Value : int { VALUE_ZERO = 0, VALUE_DRAW = 0, - VALUE_KNOWN_WIN = 10000, VALUE_MATE = 32000, VALUE_INFINITE = 32001, VALUE_NONE = 32002, From 70ba9de85cddc5460b1ec53e0a99bee271e26ece Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Fri, 12 May 2023 18:07:20 -0400 Subject: [PATCH 1204/1766] Update NNUE architecture to SFNNv8: L1-2560 nn-ac1dbea57aa3.nnue Creating this net involved: - a 6-stage training process from scratch. The datasets used in stages 1-5 were fully minimized. - permuting L1 weights with https://github.com/official-stockfish/nnue-pytorch/pull/254 A strong epoch after each training stage was chosen for the next. The 6 stages were: ``` 1. 400 epochs, lambda 1.0, default LR and gamma UHOx2-wIsRight-multinet-dfrc-n5000 (135G) nodes5000pv2_UHO.binpack data_pv-2_diff-100_nodes-5000.binpack wrongIsRight_nodes5000pv2.binpack multinet_pv-2_diff-100_nodes-5000.binpack dfrc_n5000.binpack 2. 800 epochs, end-lambda 0.75, LR 4.375e-4, gamma 0.995, skip 12 LeelaFarseer-T78juntoaugT79marT80dec.binpack (141G) T60T70wIsRightFarseerT60T74T75T76.binpack test78-junjulaug2022-16tb7p.no-db.min.binpack test79-mar2022-16tb7p.no-db.min.binpack test80-dec2022-16tb7p.no-db.min.binpack 3. 800 epochs, end-lambda 0.725, LR 4.375e-4, gamma 0.995, skip 20 leela93-v1-dfrc99-v2-T78juntosepT80jan-v6dd-T78janfebT79aprT80aprmay.min.binpack leela93-filt-v1.min.binpack dfrc99-16tb7p-filt-v2.min.binpack test78-juntosep2022-16tb7p-filter-v6-dd.min-mar2023.binpack test80-jan2023-3of3-16tb7p-filter-v6-dd.min-mar2023.binpack test78-janfeb2022-16tb7p.min.binpack test79-apr2022-16tb7p.min.binpack test80-apr2022-16tb7p.min.binpack test80-may2022-16tb7p.min.binpack 4. 800 epochs, end-lambda 0.7, LR 4.375e-4, gamma 0.995, skip 24 leela96-dfrc99-v2-T78juntosepT79mayT80junsepnovjan-v6dd-T80mar23-v6-T60novdecT77decT78aprmayT79aprT80may23.min.binpack leela96-filt-v2.min.binpack dfrc99-16tb7p-filt-v2.min.binpack test78-juntosep2022-16tb7p-filter-v6-dd.min-mar2023.binpack test79-may2022-16tb7p.filter-v6-dd.min.binpack test80-jun2022-16tb7p.filter-v6-dd.min.binpack test80-sep2022-16tb7p.filter-v6-dd.min.binpack test80-nov2022-16tb7p.filter-v6-dd.min.binpack test80-jan2023-3of3-16tb7p-filter-v6-dd.min-mar2023.binpack test80-mar2023-2tb7p.v6-sk16.min.binpack test60-novdec2021-16tb7p.min.binpack test77-dec2021-16tb7p.min.binpack test78-aprmay2022-16tb7p.min.binpack test79-apr2022-16tb7p.min.binpack test80-may2023-2tb7p.min.binpack 5. 960 epochs, end-lambda 0.7, LR 4.375e-4, gamma 0.995, skip 28 Increased max-epoch to 960 near the end of the first 800 epochs 5af11540bbfe dataset: https://github.com/official-stockfish/Stockfish/pull/4635 6. 1000 epochs, end-lambda 0.7, LR 4.375e-4, gamma 0.995, skip 28 Increased max-epoch to 1000 near the end of the first 800 epochs 1ee1aba5ed dataset: https://github.com/official-stockfish/Stockfish/pull/4782 ``` L1 weights permuted with: ```bash python3 serialize.py $nnue $nnue_permuted \ --features=HalfKAv2_hm \ --ft_optimize \ --ft_optimize_data=/data/fishpack32.binpack \ --ft_optimize_count=10000 ``` Speed measurements from 100 bench runs at depth 13 with profile-build x86-64-avx2: ``` sf_base = 1329051 +/- 2224 (95%) sf_test = 1163344 +/- 2992 (95%) diff = -165706 +/- 4913 (95%) speedup = -12.46807% +/- 0.370% (95%) ``` Training data can be found at: https://robotmoon.com/nnue-training-data/ Local elo at 25k nodes per move (vs. L1-2048 nn-1ee1aba5ed4c.nnue) ep959 : 16.2 +/- 2.3 Failed 10+0.1 STC: https://tests.stockfishchess.org/tests/view/6501beee2cd016da89abab21 LLR: -2.92 (-2.94,2.94) <0.00,2.00> Total: 13184 W: 3285 L: 3535 D: 6364 Ptnml(0-2): 85, 1662, 3334, 1440, 71 Failed 180+1.8 VLTC: https://tests.stockfishchess.org/tests/view/6505cf9a72620bc881ea908e LLR: -2.94 (-2.94,2.94) <0.00,2.00> Total: 64248 W: 16224 L: 16374 D: 31650 Ptnml(0-2): 26, 6788, 18640, 6650, 20 Passed 60+0.6 th 8 VLTC SMP (STC bounds): https://tests.stockfishchess.org/tests/view/65084a4618698b74c2e541dc LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 90630 W: 23372 L: 23033 D: 44225 Ptnml(0-2): 13, 8490, 27968, 8833, 11 Passed 60+0.6 th 8 VLTC SMP: https://tests.stockfishchess.org/tests/view/6501d45d2cd016da89abacdb LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 137804 W: 35764 L: 35276 D: 66764 Ptnml(0-2): 31, 13006, 42326, 13522, 17 closes https://github.com/official-stockfish/Stockfish/pull/4795 bench 1246812 --- src/evaluate.h | 2 +- src/nnue/nnue_architecture.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evaluate.h b/src/evaluate.h index fd1b0de1f6e..acf9edd2b34 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-1ee1aba5ed4c.nnue" + #define EvalFileDefaultName "nn-ac1dbea57aa3.nnue" namespace NNUE { diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index b50c52df31f..2a7f064bbaa 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -38,7 +38,7 @@ namespace Stockfish::Eval::NNUE { using FeatureSet = Features::HalfKAv2_hm; // Number of input feature dimensions after conversion -constexpr IndexType TransformedFeatureDimensions = 2048; +constexpr IndexType TransformedFeatureDimensions = 2560; constexpr IndexType PSQTBuckets = 8; constexpr IndexType LayerStacks = 8; From 22cdb6c1ea1f5ca429333bcbe26706c8b4dd38d7 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 23 Sep 2023 23:26:29 +0200 Subject: [PATCH 1205/1766] Explicitly invoke shell in some cases the permission on the script might be incorrect (zip downloads?). Explicitly invoke the shell closes https://github.com/official-stockfish/Stockfish/pull/4803 No functional change --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 1b03bbc2b0a..95f0fe9a577 100644 --- a/src/Makefile +++ b/src/Makefile @@ -108,7 +108,7 @@ ifeq ($(ARCH),) endif ifeq ($(ARCH), native) - override ARCH = $(shell ../scripts/get_native_properties.sh | cut -d " " -f 1) + override ARCH = $(shell $(SHELL) ../scripts/get_native_properties.sh | cut -d " " -f 1) endif # explicitly check for the list of supported architectures (as listed with make help), From ce99b4b2ef74d09499f35f09bc33102d203791cd Mon Sep 17 00:00:00 2001 From: Jasper Shovelton Date: Fri, 29 Sep 2023 22:02:24 +0200 Subject: [PATCH 1206/1766] Increment minor section number from 3.7.1 to 3.8.1. Pext has nothing to do with git commit sha/date, so separate the two sub-sections. closes https://github.com/official-stockfish/Stockfish/pull/4785 No functional change --- AUTHORS | 1 + src/Makefile | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/AUTHORS b/AUTHORS index 9314f5cbd33..b1e82806644 100644 --- a/AUTHORS +++ b/AUTHORS @@ -98,6 +98,7 @@ Jake Senne (w1wwwwww) Jan Ondruš (hxim) Jared Kish (Kurtbusch, kurt22i) Jarrod Torriero (DU-jdto) +Jasper Shovelton (Beanie496) Jean-Francois Romang (jromang) Jean Gauthier (OuaisBla) Jekaa diff --git a/src/Makefile b/src/Makefile index 95f0fe9a577..a59303acd79 100644 --- a/src/Makefile +++ b/src/Makefile @@ -703,24 +703,24 @@ ifeq ($(pext),yes) endif endif -### 3.7.1 Try to include git commit sha for versioning +### 3.8.1 Try to include git commit sha for versioning GIT_SHA = $(shell git rev-parse HEAD 2>/dev/null | cut -c 1-8) ifneq ($(GIT_SHA), ) CXXFLAGS += -DGIT_SHA=$(GIT_SHA) endif -### 3.7.2 Try to include git commit date for versioning +### 3.8.2 Try to include git commit date for versioning GIT_DATE = $(shell git show -s --date=format:'%Y%m%d' --format=%cd HEAD 2>/dev/null) ifneq ($(GIT_DATE), ) CXXFLAGS += -DGIT_DATE=$(GIT_DATE) endif -### 3.7.3 Try to include architecture +### 3.8.3 Try to include architecture ifneq ($(ARCH), ) CXXFLAGS += -DARCH=$(ARCH) endif -### 3.8 Link Time Optimization +### 3.9 Link Time Optimization ### This is a mix of compile and link time options because the lto link phase ### needs access to the optimization flags. ifeq ($(optimize),yes) @@ -755,7 +755,7 @@ ifeq ($(debug), no) endif endif -### 3.9 Android 5 can only run position independent executables. Note that this +### 3.10 Android 5 can only run position independent executables. Note that this ### breaks Android 4.0 and earlier. ifeq ($(OS), Android) CXXFLAGS += -fPIE From 9739ed7a97c153c3223b608b24717edbf2dfd7bc Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sat, 23 Sep 2023 13:24:09 +0300 Subject: [PATCH 1207/1766] Simplify pawn count in evaluation This simplifies the evaluation by removing the unnecessary pawn count term when combining nnue and optimism values. Passed STC LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 61472 W: 15748 L: 15554 D: 30170 Ptnml(0-2): 191, 7123, 15933, 7279, 210 https://tests.stockfishchess.org/tests/view/650c34cf7ca0d3f7bbf264ff Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 81264 W: 20657 L: 20500 D: 40107 Ptnml(0-2): 30, 8713, 22997, 8854, 38 https://tests.stockfishchess.org/tests/view/650cc30efb151d43ae6d5987 closes https://github.com/official-stockfish/Stockfish/pull/4800 Bench: 1530568 --- src/evaluate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 9ca0e4566f6..208e3ed5ba4 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -177,7 +177,7 @@ Value Eval::evaluate(const Position& pos) { int npm = pos.non_pawn_material() / 64; v = ( nnue * (915 + npm + 9 * pos.count()) - + optimism * (154 + npm + pos.count())) / 1024; + + optimism * (154 + npm )) / 1024; } // Damp down the evaluation linearly when shuffling From 243f7b264a81c2981cec2818b47d609d9d3ca119 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Fri, 29 Sep 2023 22:16:57 +0200 Subject: [PATCH 1208/1766] Improve grammar of comments closes https://github.com/official-stockfish/Stockfish/pull/4801 No functional change --- src/bitboard.h | 2 +- src/movegen.h | 5 +++-- src/movepick.cpp | 8 ++++---- src/position.cpp | 17 +++++++++-------- src/search.cpp | 25 +++++++++++++------------ src/types.h | 4 ++-- src/uci.cpp | 6 +++--- 7 files changed, 35 insertions(+), 32 deletions(-) diff --git a/src/bitboard.h b/src/bitboard.h index dee73b4b3f7..eb2f949d5aa 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -209,7 +209,7 @@ template<> inline int distance(Square x, Square y) { return SquareDistan inline int edge_distance(File f) { return std::min(f, File(FILE_H - f)); } -/// attacks_bb(Square) returns the pseudo attacks of the give piece type +/// attacks_bb(Square) returns the pseudo attacks of the given piece type /// assuming an empty board. template diff --git a/src/movegen.h b/src/movegen.h index b15f1230b13..5eee2f1acf8 100644 --- a/src/movegen.h +++ b/src/movegen.h @@ -56,8 +56,9 @@ inline bool operator<(const ExtMove& f, const ExtMove& s) { template ExtMove* generate(const Position& pos, ExtMove* moveList); -/// The MoveList struct is a simple wrapper around generate(). It sometimes comes -/// in handy to use this class instead of the low level generate() function. +/// The MoveList struct wraps the generate() function and returns a convenient +/// list of moves. Using MoveList is sometimes preferable to directly calling +/// the lower level generate() function. template struct MoveList { diff --git a/src/movepick.cpp b/src/movepick.cpp index d4f8ab092a8..eea1d49e447 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -55,11 +55,11 @@ namespace { } // namespace -/// Constructors of the MovePicker class. As arguments we pass information -/// to help it to return the (presumably) good moves first, to decide which +/// Constructors of the MovePicker class. As arguments, we pass information +/// to help it return the (presumably) good moves first, to decide which /// moves to return (in the quiescence search, for instance, we only want to -/// search captures, promotions, and some checks) and how important good move -/// ordering is at the current node. +/// search captures, promotions, and some checks) and how important a good +/// move ordering is at the current node. /// MovePicker constructor for the main search MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, diff --git a/src/position.cpp b/src/position.cpp index 120677432b6..67dafd8dd78 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -102,8 +102,9 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) { } -// Marcel van Kervinck's cuckoo algorithm for fast detection of "upcoming repetition" -// situations. Description of the algorithm in the following paper: +// Implements Marcel van Kervinck's cuckoo algorithm to detect repetition of positions +// for 3-fold repetition draws. The algorithm uses two hash tables with Zobrist hashes to +// allow fast detection of recurring positions. For details see: // http://web.archive.org/web/20201107002606/https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf // First and second hash functions for indexing the cuckoo tables @@ -549,7 +550,7 @@ bool Position::legal(Move m) const { /// Position::pseudo_legal() takes a random move and tests whether the move is -/// pseudo legal. It is used to validate moves from TT that can be corrupted +/// pseudo-legal. It is used to validate moves from TT that can be corrupted /// due to SMP concurrent access or hash position key aliasing. bool Position::pseudo_legal(const Move m) const { @@ -565,7 +566,7 @@ bool Position::pseudo_legal(const Move m) const { return checkers() ? MoveList< EVASIONS>(*this).contains(m) : MoveList(*this).contains(m); - // Is not a promotion, so promotion piece must be empty + // Is not a promotion, so the promotion piece must be empty assert(promotion_type(m) - KNIGHT == NO_PIECE_TYPE); // If the 'from' square is not occupied by a piece belonging to the side to @@ -603,7 +604,7 @@ bool Position::pseudo_legal(const Move m) const { { if (type_of(pc) != KING) { - // Double check? In this case a king move is required + // Double check? In this case, a king move is required if (more_than_one(checkers())) return false; @@ -611,7 +612,7 @@ bool Position::pseudo_legal(const Move m) const { if (!(between_bb(square(us), lsb(checkers())) & to)) return false; } - // In case of king moves under check we have to remove king so as to catch + // In case of king moves under check we have to remove the king so as to catch // invalid moves like b1a1 when opposite queen is on c1. else if (attackers_to(to, pieces() ^ from) & pieces(~us)) return false; @@ -1134,7 +1135,7 @@ bool Position::see_ge(Move m, Bitboard& occupied, Value threshold) const { } else // KING - // If we "capture" with the king but opponent still has attackers, + // If we "capture" with the king but the opponent still has attackers, // reverse the result. return (attackers & ~pieces(stm)) ? res ^ 1 : res; } @@ -1265,7 +1266,7 @@ void Position::flip() { /// Position::pos_is_ok() performs some consistency checks for the -/// position object and raises an asserts if something wrong is detected. +/// position object and raise an assert if something wrong is detected. /// This is meant to be helpful when debugging. bool Position::pos_is_ok() const { diff --git a/src/search.cpp b/src/search.cpp index 3e19000a5d6..9949e2aeb23 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -100,10 +100,12 @@ namespace { return VALUE_DRAW - 1 + Value(thisThread->nodes & 0x2); } - // Skill structure is used to implement strength limit. If we have an uci_elo then - // we convert it to a suitable fractional skill level using anchoring to CCRL Elo - // (goldfish 1.13 = 2000) and a fit through Ordo derived Elo for a match (TC 60+0.6) - // results spanning a wide range of k values. + // Skill structure is used to implement strength limit. + // If we have a UCI_Elo, we convert it to an appropriate skill level, anchored to the Stash engine. + // This method is based on a fit of the Elo results for games played between the master at various + // skill levels and various versions of the Stash engine, all ranked at CCRL. + // Skill 0 .. 19 now covers CCRL Blitz Elo from 1320 to 3190, approximately + // Reference: https://github.com/vondele/Stockfish/commit/a08b8d4e9711c20acedbfe17d618c3c384b339ec struct Skill { Skill(int skill_level, int uci_elo) { if (uci_elo) @@ -272,10 +274,9 @@ void MainThread::search() { void Thread::search() { - // To allow access to (ss-7) up to (ss+2), the stack must be oversized. - // The former is needed to allow update_continuation_histories(ss-1, ...), - // which accesses its argument at ss-6, also near the root. - // The latter is needed for statScore and killer initialization. + // Allocate stack with extra size to allow access from (ss-7) to (ss+2) + // (ss-7) is needed for update_continuation_histories(ss-1, ...) which accesses (ss-6) + // (ss+2) is needed for initialization of statScore and killers Stack stack[MAX_PLY+10], *ss = stack+7; Move pv[MAX_PLY+1]; Value alpha, beta, delta; @@ -362,7 +363,7 @@ void Thread::search() { alpha = std::max(prev - delta,-VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE); - // Adjust optimism based on root move's previousScore + // Adjust optimism based on root move's previousScore (~4 Elo) int opt = 109 * prev / (std::abs(prev) + 141); optimism[ us] = Value(opt); optimism[~us] = -optimism[us]; @@ -721,7 +722,7 @@ namespace { } else if (excludedMove) { - // Providing the hint that this node's accumulator will be used often brings significant Elo gain (13 Elo) + // Providing the hint that this node's accumulator will be used often brings significant Elo gain (~13 Elo) Eval::NNUE::hint_common_parent_position(pos); eval = ss->staticEval; } @@ -762,7 +763,7 @@ namespace { : (ss-4)->staticEval != VALUE_NONE ? ss->staticEval > (ss-4)->staticEval : true; - // Step 7. Razoring (~1 Elo). + // Step 7. Razoring (~1 Elo) // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. if (eval < alpha - 456 - 252 * depth * depth) @@ -772,7 +773,7 @@ namespace { return value; } - // Step 8. Futility pruning: child node (~40 Elo). + // Step 8. Futility pruning: child node (~40 Elo) // The depth condition is important for mate finding. if ( !ss->ttPv && depth < 9 diff --git a/src/types.h b/src/types.h index 340c47a5fc2..f682e764f17 100644 --- a/src/types.h +++ b/src/types.h @@ -40,7 +40,7 @@ #include #if defined(_MSC_VER) -// Disable some silly and noisy warning from MSVC compiler +// Disable some silly and noisy warnings from MSVC compiler #pragma warning(disable: 4127) // Conditional expression is constant #pragma warning(disable: 4146) // Unary minus operator applied to unsigned type #pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false' @@ -405,7 +405,7 @@ constexpr Move make(Square from, Square to, PieceType pt = KNIGHT) { return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to); } -/// Based on a congruential pseudo random number generator +/// Based on a congruential pseudo-random number generator constexpr Key make_key(uint64_t seed) { return seed * 6364136223846793005ULL + 1442695040888963407ULL; } diff --git a/src/uci.cpp b/src/uci.cpp index f3e436ef3aa..f62bb8bf4e0 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -159,7 +159,7 @@ namespace { // bench() is called when the engine receives the "bench" command. - // Firstly, a list of UCI commands is set up according to the bench + // First, a list of UCI commands is set up according to the bench // parameters, then it is run one by one, printing a summary at the end. void bench(Position& pos, std::istream& args, StateListPtr& states) { @@ -226,14 +226,14 @@ namespace { // Transform the eval to centipawns with limited range double x = std::clamp(double(v), -4000.0, 4000.0); - // Return the win rate in per mille units rounded to the nearest value + // Return the win rate in per mille units, rounded to the nearest integer return int(0.5 + 1000 / (1 + std::exp((a - x) / b))); } } // namespace -/// UCI::loop() waits for a command from the stdin, parses it and then calls the appropriate +/// UCI::loop() waits for a command from the stdin, parses it, and then calls the appropriate /// function. It also intercepts an end-of-file (EOF) indication from the stdin to ensure a /// graceful exit if the GUI dies unexpectedly. When called with some command-line arguments, /// like running 'bench', the function returns immediately after the command is executed. From 31d0b7fe932458d6661f4d4c2ce88502086616c5 Mon Sep 17 00:00:00 2001 From: mstembera Date: Sun, 24 Sep 2023 18:09:52 -0700 Subject: [PATCH 1209/1766] Remove unused see_ge() code closes https://github.com/official-stockfish/Stockfish/pull/4805 No functional change --- src/position.cpp | 20 +++++++------------- src/position.h | 1 - 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index 67dafd8dd78..a2b377af9e5 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1042,7 +1042,7 @@ Key Position::key_after(Move m) const { /// SEE value of move is greater or equal to the given threshold. We'll use an /// algorithm similar to alpha-beta pruning with a null window. -bool Position::see_ge(Move m, Bitboard& occupied, Value threshold) const { +bool Position::see_ge(Move m, Value threshold) const { assert(is_ok(m)); @@ -1061,7 +1061,7 @@ bool Position::see_ge(Move m, Bitboard& occupied, Value threshold) const { return true; assert(color_of(piece_on(from)) == sideToMove); - occupied = pieces() ^ from ^ to; // xoring to is important for pinned piece logic + Bitboard occupied = pieces() ^ from ^ to; // xoring to is important for pinned piece logic Color stm = sideToMove; Bitboard attackers = attackers_to(to, occupied); Bitboard stmAttackers, bb; @@ -1092,43 +1092,43 @@ bool Position::see_ge(Move m, Bitboard& occupied, Value threshold) const { // the bitboard 'attackers' any X-ray attackers behind it. if ((bb = stmAttackers & pieces(PAWN))) { - occupied ^= least_significant_square_bb(bb); if ((swap = PawnValue - swap) < res) break; + occupied ^= least_significant_square_bb(bb); attackers |= attacks_bb(to, occupied) & pieces(BISHOP, QUEEN); } else if ((bb = stmAttackers & pieces(KNIGHT))) { - occupied ^= least_significant_square_bb(bb); if ((swap = KnightValue - swap) < res) break; + occupied ^= least_significant_square_bb(bb); } else if ((bb = stmAttackers & pieces(BISHOP))) { - occupied ^= least_significant_square_bb(bb); if ((swap = BishopValue - swap) < res) break; + occupied ^= least_significant_square_bb(bb); attackers |= attacks_bb(to, occupied) & pieces(BISHOP, QUEEN); } else if ((bb = stmAttackers & pieces(ROOK))) { - occupied ^= least_significant_square_bb(bb); if ((swap = RookValue - swap) < res) break; + occupied ^= least_significant_square_bb(bb); attackers |= attacks_bb(to, occupied) & pieces(ROOK, QUEEN); } else if ((bb = stmAttackers & pieces(QUEEN))) { - occupied ^= least_significant_square_bb(bb); if ((swap = QueenValue - swap) < res) break; + occupied ^= least_significant_square_bb(bb); attackers |= (attacks_bb(to, occupied) & pieces(BISHOP, QUEEN)) | (attacks_bb(to, occupied) & pieces(ROOK , QUEEN)); @@ -1143,12 +1143,6 @@ bool Position::see_ge(Move m, Bitboard& occupied, Value threshold) const { return bool(res); } -bool Position::see_ge(Move m, Value threshold) const { - Bitboard occupied; - return see_ge(m, occupied, threshold); -} - - /// Position::is_draw() tests whether the position is drawn by 50-move rule /// or by repetition. It does not detect stalemates. diff --git a/src/position.h b/src/position.h index ca7c3ace811..aae4db94a9c 100644 --- a/src/position.h +++ b/src/position.h @@ -135,7 +135,6 @@ class Position { // Static Exchange Evaluation bool see_ge(Move m, Value threshold = VALUE_ZERO) const; - bool see_ge(Move m, Bitboard& occupied, Value threshold = VALUE_ZERO) const; // Accessing hash keys Key key() const; From 4f0fecad8a0f5258114f63f0ac0c905a54d65219 Mon Sep 17 00:00:00 2001 From: Sebastian Buchwald Date: Mon, 25 Sep 2023 12:24:48 +0200 Subject: [PATCH 1210/1766] Use C++17 variable templates for type traits The C++17 variable templates are slightly more readable and allow us to remove the typename keyword in a few cases. closes https://github.com/official-stockfish/Stockfish/pull/4806 No functional change --- src/movepick.h | 4 ++-- src/nnue/nnue_common.h | 4 ++-- src/syzygy/tbprobe.cpp | 4 ++-- src/tune.h | 10 +++++----- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/movepick.h b/src/movepick.h index 5243f89cf2c..dd9de0b2161 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -24,7 +24,7 @@ #include #include #include -#include +#include // IWYU pragma: keep #include "movegen.h" #include "types.h" @@ -70,7 +70,7 @@ struct Stats : public std::array, Size> void fill(const T& v) { // For standard-layout 'this' points to first struct member - assert(std::is_standard_layout::value); + assert(std::is_standard_layout_v); using entry = StatsEntry; entry* p = reinterpret_cast(this); diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index a42a86c980d..e159c5dc3da 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -103,7 +103,7 @@ namespace Stockfish::Eval::NNUE { else { std::uint8_t u[sizeof(IntType)]; - typename std::make_unsigned::type v = 0; + std::make_unsigned_t v = 0; stream.read(reinterpret_cast(u), sizeof(IntType)); for (std::size_t i = 0; i < sizeof(IntType); ++i) @@ -128,7 +128,7 @@ namespace Stockfish::Eval::NNUE { else { std::uint8_t u[sizeof(IntType)]; - typename std::make_unsigned::type v = value; + std::make_unsigned_t v = value; std::size_t i = 0; // if constexpr to silence the warning about shift by 8 diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 13d271fce8a..ffe29ce1626 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -102,7 +102,7 @@ constexpr Value WDL_to_value[] = { template inline void swap_endian(T& x) { - static_assert(std::is_unsigned::value, "Argument of swap_endian not unsigned"); + static_assert(std::is_unsigned_v, "Argument of swap_endian not unsigned"); uint8_t tmp, *c = (uint8_t*)&x; for (int i = 0; i < Half; ++i) @@ -332,7 +332,7 @@ struct PairsData { // first access, when the corresponding file is memory mapped. template struct TBTable { - using Ret = typename std::conditional::type; + using Ret = std::conditional_t; static constexpr int Sides = Type == WDL ? 2 : 1; diff --git a/src/tune.h b/src/tune.h index 3e94f7efc6c..dde03b324ea 100644 --- a/src/tune.h +++ b/src/tune.h @@ -22,7 +22,7 @@ #include #include #include -#include +#include // IWYU pragma: keep #include #include @@ -96,11 +96,11 @@ class Tune { template struct Entry : public EntryBase { - static_assert(!std::is_const::value, "Parameter cannot be const!"); + static_assert(!std::is_const_v, "Parameter cannot be const!"); - static_assert( std::is_same::value - || std::is_same::value - || std::is_same::value, "Parameter type not supported!"); + static_assert( std::is_same_v + || std::is_same_v + || std::is_same_v, "Parameter type not supported!"); Entry(const std::string& n, T& v, const SetRange& r) : name(n), value(v), range(r) {} void operator=(const Entry&) = delete; // Because 'value' is a reference From 660da1ca7b4c2c03dce03d14ef3496d9fb4aead2 Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Mon, 25 Sep 2023 13:18:05 +0800 Subject: [PATCH 1211/1766] Skip moves-loop pruning in qsearch if we have only pawns At first my idea was only to cover movecount and futility pruning, but @peregrineshahin suggested to test it on all moves-loop pruning and it worked. Passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 167968 W: 42970 L: 42480 D: 82518 Ptnml(0-2): 444, 18324, 46002, 18726, 488 https://tests.stockfishchess.org/tests/view/6511181a55b420c569d0d54c Passed LTC: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 40794 W: 10496 L: 10182 D: 20116 Ptnml(0-2): 12, 4021, 12025, 4319, 20 https://tests.stockfishchess.org/tests/view/6512ccc4b3e74811c8aee86c closes https://github.com/official-stockfish/Stockfish/pull/4809 Bench: 1338472 --- src/search.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 9949e2aeb23..97b70b8fefd 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1427,6 +1427,7 @@ namespace { Value bestValue, value, ttValue, futilityValue, futilityBase; bool pvHit, givesCheck, capture; int moveCount; + Color us = pos.side_to_move(); // Step 1. Initialize node if (PvNode) @@ -1537,7 +1538,7 @@ namespace { moveCount++; // Step 6. Pruning. - if (bestValue > VALUE_TB_LOSS_IN_MAX_PLY) + if (bestValue > VALUE_TB_LOSS_IN_MAX_PLY && pos.non_pawn_material(us)) { // Futility pruning and moveCount pruning (~10 Elo) if ( !givesCheck From afe7f4d9b0c5e1a1aa224484d2cd9e04c7f099b9 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Wed, 27 Sep 2023 20:58:28 -0400 Subject: [PATCH 1212/1766] Update default net to nn-0000000000a0.nnue This is a later epoch from the same experiment that led to the previous master net. In training stage 6, max-epoch was raised to 1,200 near the end of the first 1,000 epochs. For more details, see https://github.com/official-stockfish/Stockfish/pull/4795 Local elo at 25k nodes per move (vs. L1-2048 nn-1ee1aba5ed4c.nnue) ep1079 : 15.6 +/- 1.2 Passed STC: https://tests.stockfishchess.org/tests/view/651503b3b3e74811c8af1e2a LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 29408 W: 7607 L: 7304 D: 14497 Ptnml(0-2): 97, 3277, 7650, 3586, 94 Passed LTC: https://tests.stockfishchess.org/tests/view/651585ceb3e74811c8af2a5f LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 73164 W: 18828 L: 18440 D: 35896 Ptnml(0-2): 30, 7749, 20644, 8121, 38 closes https://github.com/official-stockfish/Stockfish/pull/4810 Bench: 1453057 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index acf9edd2b34..26f2fc4f985 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ namespace Eval { // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-ac1dbea57aa3.nnue" + #define EvalFileDefaultName "nn-0000000000a0.nnue" namespace NNUE { From 8a912951de6d4bff78d3ff5258213a0c7e6f494e Mon Sep 17 00:00:00 2001 From: mstembera Date: Sun, 24 Sep 2023 15:15:50 -0700 Subject: [PATCH 1213/1766] Remove handcrafted MMX code too small a benefit to maintain this old target closes https://github.com/official-stockfish/Stockfish/pull/4804 No functional change --- src/Makefile | 5 ++-- src/misc.cpp | 3 --- src/nnue/layers/affine_transform.h | 32 +---------------------- src/nnue/layers/clipped_relu.h | 18 ------------- src/nnue/layers/simd.h | 3 --- src/nnue/nnue_common.h | 6 ----- src/nnue/nnue_feature_transformer.h | 40 ----------------------------- 7 files changed, 3 insertions(+), 104 deletions(-) diff --git a/src/Makefile b/src/Makefile index a59303acd79..5b43c35fdd7 100644 --- a/src/Makefile +++ b/src/Makefile @@ -674,7 +674,6 @@ ifeq ($(sse2),yes) endif ifeq ($(mmx),yes) - CXXFLAGS += -DUSE_MMX ifeq ($(comp),$(filter $(comp),gcc clang mingw icx)) CXXFLAGS += -mmmx endif @@ -794,11 +793,11 @@ help: @echo "x86-64-sse41-popcnt > x86 64-bit with sse41 and popcnt support" @echo "x86-64-modern > deprecated, currently x86-64-sse41-popcnt" @echo "x86-64-ssse3 > x86 64-bit with ssse3 support" - @echo "x86-64-sse3-popcnt > x86 64-bit with sse3 and popcnt support" + @echo "x86-64-sse3-popcnt > x86 64-bit with sse3 compile and popcnt support" @echo "x86-64 > x86 64-bit generic (with sse2 support)" @echo "x86-32-sse41-popcnt > x86 32-bit with sse41 and popcnt support" @echo "x86-32-sse2 > x86 32-bit with sse2 support" - @echo "x86-32 > x86 32-bit generic (with mmx and sse support)" + @echo "x86-32 > x86 32-bit generic (with mmx compile support)" @echo "ppc-64 > PPC 64-bit" @echo "ppc-32 > PPC 32-bit" @echo "armv7 > ARMv7 32-bit" diff --git a/src/misc.cpp b/src/misc.cpp index 98e346a6689..2f6ffd28e70 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -282,9 +282,6 @@ std::string compiler_info() { compiler += " SSE2"; #endif compiler += (HasPopCnt ? " POPCNT" : ""); - #if defined(USE_MMX) - compiler += " MMX"; - #endif #if defined(USE_NEON_DOTPROD) compiler += " NEON_DOTPROD"; #elif defined(USE_NEON) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 42839bb5bdc..fc65c34339f 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -45,18 +45,13 @@ namespace Stockfish::Eval::NNUE::Layers { template static void affine_transform_non_ssse3(std::int32_t* output, const std::int8_t* weights, const std::int32_t* biases, const std::uint8_t* input) { -# if defined(USE_SSE2) || defined(USE_MMX) || defined(USE_NEON_DOTPROD) || defined(USE_NEON) +# if defined(USE_SSE2) || defined(USE_NEON_DOTPROD) || defined(USE_NEON) # if defined(USE_SSE2) // At least a multiple of 16, with SSE2. constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 16) / 16; const __m128i Zeros = _mm_setzero_si128(); const auto inputVector = reinterpret_cast(input); -# elif defined(USE_MMX) - constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 8) / 8; - const __m64 Zeros = _mm_setzero_si64(); - const auto inputVector = reinterpret_cast(input); - # elif defined(USE_NEON_DOTPROD) constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 16) / 16; const auto inputVector = reinterpret_cast(input); @@ -92,26 +87,6 @@ namespace Stockfish::Eval::NNUE::Layers { sum = _mm_add_epi32(sum, sum_second_32); output[i] = _mm_cvtsi128_si32(sum); -# elif defined(USE_MMX) - __m64 sumLo = _mm_cvtsi32_si64(biases[i]); - __m64 sumHi = Zeros; - const auto row = reinterpret_cast(&weights[offset]); - for (IndexType j = 0; j < NumChunks; ++j) { - __m64 row_j = row[j]; - __m64 input_j = inputVector[j]; - __m64 extendedRowLo = _mm_srai_pi16(_mm_unpacklo_pi8(row_j, row_j), 8); - __m64 extendedRowHi = _mm_srai_pi16(_mm_unpackhi_pi8(row_j, row_j), 8); - __m64 extendedInputLo = _mm_unpacklo_pi8(input_j, Zeros); - __m64 extendedInputHi = _mm_unpackhi_pi8(input_j, Zeros); - __m64 productLo = _mm_madd_pi16(extendedRowLo, extendedInputLo); - __m64 productHi = _mm_madd_pi16(extendedRowHi, extendedInputHi); - sumLo = _mm_add_pi32(sumLo, productLo); - sumHi = _mm_add_pi32(sumHi, productHi); - } - __m64 sum = _mm_add_pi32(sumLo, sumHi); - sum = _mm_add_pi32(sum, _mm_unpackhi_pi32(sum, sum)); - output[i] = _mm_cvtsi64_si32(sum); - # elif defined(USE_NEON_DOTPROD) int32x4_t sum = {biases[i]}; const auto row = reinterpret_cast(&weights[offset]); @@ -132,11 +107,6 @@ namespace Stockfish::Eval::NNUE::Layers { # endif } - -# if defined(USE_MMX) - _mm_empty(); -# endif - # else std::memcpy(output, biases, sizeof(std::int32_t) * OutputDimensions); diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index aab824b3572..48cd6c69345 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -135,24 +135,6 @@ namespace Stockfish::Eval::NNUE::Layers { } constexpr IndexType Start = NumChunks * SimdWidth; - #elif defined(USE_MMX) - constexpr IndexType NumChunks = InputDimensions / SimdWidth; - const __m64 k0x80s = _mm_set1_pi8(-128); - const auto in = reinterpret_cast(input); - const auto out = reinterpret_cast<__m64*>(output); - for (IndexType i = 0; i < NumChunks; ++i) { - const __m64 words0 = _mm_srai_pi16( - _mm_packs_pi32(in[i * 4 + 0], in[i * 4 + 1]), - WeightScaleBits); - const __m64 words1 = _mm_srai_pi16( - _mm_packs_pi32(in[i * 4 + 2], in[i * 4 + 3]), - WeightScaleBits); - const __m64 packedbytes = _mm_packs_pi16(words0, words1); - out[i] = _mm_subs_pi8(_mm_adds_pi8(packedbytes, k0x80s), k0x80s); - } - _mm_empty(); - constexpr IndexType Start = NumChunks * SimdWidth; - #elif defined(USE_NEON) constexpr IndexType NumChunks = InputDimensions / (SimdWidth / 2); const int8x8_t Zero = {0}; diff --git a/src/nnue/layers/simd.h b/src/nnue/layers/simd.h index f478cd7819f..349217edb7a 100644 --- a/src/nnue/layers/simd.h +++ b/src/nnue/layers/simd.h @@ -31,9 +31,6 @@ #elif defined(USE_SSE2) # include -#elif defined(USE_MMX) -# include - #elif defined(USE_NEON) # include #endif diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index e159c5dc3da..779f4e75555 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -42,9 +42,6 @@ #elif defined(USE_SSE2) #include -#elif defined(USE_MMX) -#include - #elif defined(USE_NEON) #include #endif @@ -71,9 +68,6 @@ namespace Stockfish::Eval::NNUE { #elif defined(USE_SSE2) constexpr std::size_t SimdWidth = 16; - #elif defined(USE_MMX) - constexpr std::size_t SimdWidth = 8; - #elif defined(USE_NEON) constexpr std::size_t SimdWidth = 16; #endif diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 902918b2c6f..77a175f50c9 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -117,34 +117,6 @@ namespace Stockfish::Eval::NNUE { #define NumRegistersSIMD (Is64Bit ? 16 : 8) #define MaxChunkSize 16 - #elif USE_MMX - using vec_t = __m64; - using psqt_vec_t = __m64; - #define vec_load(a) (*(a)) - #define vec_store(a,b) *(a)=(b) - #define vec_add_16(a,b) _mm_add_pi16(a,b) - #define vec_sub_16(a,b) _mm_sub_pi16(a,b) - #define vec_mul_16(a,b) _mm_mullo_pi16(a,b) - #define vec_zero() _mm_setzero_si64() - #define vec_set_16(a) _mm_set1_pi16(a) - inline vec_t vec_max_16(vec_t a,vec_t b){ - vec_t comparison = _mm_cmpgt_pi16(a,b); - return _mm_or_si64(_mm_and_si64(comparison, a), _mm_andnot_si64(comparison, b)); - } - inline vec_t vec_min_16(vec_t a,vec_t b){ - vec_t comparison = _mm_cmpgt_pi16(a,b); - return _mm_or_si64(_mm_and_si64(comparison, b), _mm_andnot_si64(comparison, a)); - } - #define vec_msb_pack_16(a,b) _mm_packs_pi16(_mm_srli_pi16(a,7),_mm_srli_pi16(b,7)) - #define vec_load_psqt(a) (*(a)) - #define vec_store_psqt(a,b) *(a)=(b) - #define vec_add_psqt_32(a,b) _mm_add_pi32(a,b) - #define vec_sub_psqt_32(a,b) _mm_sub_pi32(a,b) - #define vec_zero_psqt() _mm_setzero_si64() - #define vec_cleanup() _mm_empty() - #define NumRegistersSIMD 8 - #define MaxChunkSize 8 - #elif USE_NEON using vec_t = int16x8_t; using psqt_vec_t = int32x4_t; @@ -335,10 +307,6 @@ namespace Stockfish::Eval::NNUE { #endif } -#if defined(vec_cleanup) - vec_cleanup(); -#endif - return psqt; } // end of function transform() @@ -529,10 +497,6 @@ namespace Stockfish::Eval::NNUE { } } #endif - - #if defined(USE_MMX) - _mm_empty(); - #endif } template @@ -613,10 +577,6 @@ namespace Stockfish::Eval::NNUE { accumulator.psqtAccumulation[Perspective][k] += psqtWeights[index * PSQTBuckets + k]; } #endif - - #if defined(USE_MMX) - _mm_empty(); - #endif } template From f1ce1cd4751a098b7ee09e304fa6397d08fe8d7f Mon Sep 17 00:00:00 2001 From: "Robert Nurnberg @ elitebook" Date: Sat, 30 Sep 2023 08:11:14 +0200 Subject: [PATCH 1214/1766] Update links in license matches https://www.gnu.org/licenses/gpl-3.0.txt closes https://github.com/official-stockfish/Stockfish/pull/4813 No functional change --- Copying.txt | 1348 +++++++++++++++++++++++++-------------------------- 1 file changed, 674 insertions(+), 674 deletions(-) diff --git a/Copying.txt b/Copying.txt index 818433ecc0e..f288702d2fa 100644 --- a/Copying.txt +++ b/Copying.txt @@ -1,674 +1,674 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. From 040dfedb3457ca6971d98c754362cde4dc767aff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sun, 1 Oct 2023 20:19:49 +0200 Subject: [PATCH 1215/1766] Remove one test in the move loop Simplification passed STC test: https://tests.stockfishchess.org/tests/view/6519fc91cff46e538ee014f6 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 191264 W: 48550 L: 48501 D: 94213 Ptnml(0-2): 576, 21529, 51392, 21540, 595 closes #4815 Non functional change --- src/search.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 97b70b8fefd..5508478862f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -945,18 +945,17 @@ namespace { if (move == excludedMove) continue; + // Check for legality + if (!pos.legal(move)) + continue; + // At root obey the "searchmoves" option and skip moves not listed in Root - // Move List. As a consequence, any illegal move is also skipped. In MultiPV - // mode we also skip PV moves that have been already searched and those - // of lower "TB rank" if we are in a TB root position. + // Move List. In MultiPV mode we also skip PV moves that have been already + // searched and those of lower "TB rank" if we are in a TB root position. if (rootNode && !std::count(thisThread->rootMoves.begin() + thisThread->pvIdx, thisThread->rootMoves.begin() + thisThread->pvLast, move)) continue; - // Check for legality - if (!rootNode && !pos.legal(move)) - continue; - ss->moveCount = ++moveCount; if (rootNode && thisThread == Threads.main() && Time.elapsed() > 3000) From c17a657b045d4dc720c8c36558fe649a1c3f4a05 Mon Sep 17 00:00:00 2001 From: mstembera Date: Sat, 30 Sep 2023 23:12:02 -0700 Subject: [PATCH 1216/1766] Optimize the most common update accumalator cases w/o tiling In the most common case where we only update a single state it's faster to not use temporary accumulation registers and tiling. (Also includes a couple of small cleanups.) passed STC https://tests.stockfishchess.org/tests/view/651918e3cff46e538ee0023b LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 34944 W: 8989 L: 8687 D: 17268 Ptnml(0-2): 88, 3743, 9512, 4037, 92 A simpler version https://tests.stockfishchess.org/tests/view/65190dfacff46e538ee00155 also passed but this version is stronger still https://tests.stockfishchess.org/tests/view/6519b95fcff46e538ee00fa2 closes https://github.com/official-stockfish/Stockfish/pull/4816 No functional change --- src/misc.h | 1 + src/nnue/nnue_feature_transformer.h | 176 +++++++++++++++++++--------- 2 files changed, 120 insertions(+), 57 deletions(-) diff --git a/src/misc.h b/src/misc.h index c0387f7c4c9..52595fb906b 100644 --- a/src/misc.h +++ b/src/misc.h @@ -87,6 +87,7 @@ class ValueList { void push_back(const T& value) { values_[size_++] = value; } const T* begin() const { return values_; } const T* end() const { return values_ + size_; } + const T& operator[](int index) const { return values_[index]; } private: T values_[MaxSize]; diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 77a175f50c9..25f686dacbc 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -370,13 +370,13 @@ namespace Stockfish::Eval::NNUE { while (states_to_update[i] == nullptr) --i; - StateInfo *st2 = states_to_update[i]; + StateInfo* st2 = states_to_update[i]; for (; i >= 0; --i) { states_to_update[i]->accumulator.computed[Perspective] = true; - StateInfo* end_state = i == 0 ? computed_st : states_to_update[i - 1]; + const StateInfo* end_state = i == 0 ? computed_st : states_to_update[i - 1]; for (; st2 != end_state; st2 = st2->previous) FeatureSet::append_changed_indices( @@ -388,78 +388,140 @@ namespace Stockfish::Eval::NNUE { // Now update the accumulators listed in states_to_update[], where the last element is a sentinel. #ifdef VECTOR - for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) + + if ( states_to_update[1] == nullptr + && (removed[0].size() == 1 || removed[0].size() == 2) + && added[0].size() == 1) { - // Load accumulator - auto accTile = reinterpret_cast( - &st->accumulator.accumulation[Perspective][j * TileHeight]); - for (IndexType k = 0; k < NumRegs; ++k) - acc[k] = vec_load(&accTile[k]); + assert(states_to_update[0]); - for (IndexType i = 0; states_to_update[i]; ++i) - { - // Difference calculation for the deactivated features - for (const auto index : removed[i]) + auto accTileIn = reinterpret_cast( + &st->accumulator.accumulation[Perspective][0]); + auto accTileOut = reinterpret_cast( + &states_to_update[0]->accumulator.accumulation[Perspective][0]); + + const IndexType offsetR0 = HalfDimensions * removed[0][0]; + auto columnR0 = reinterpret_cast(&weights[offsetR0]); + const IndexType offsetA = HalfDimensions * added[0][0]; + auto columnA = reinterpret_cast(&weights[offsetA]); + + if (removed[0].size() == 1) { - const IndexType offset = HalfDimensions * index + j * TileHeight; - auto column = reinterpret_cast(&weights[offset]); - for (IndexType k = 0; k < NumRegs; ++k) - acc[k] = vec_sub_16(acc[k], column[k]); + for (IndexType k = 0; k < HalfDimensions * sizeof(std::int16_t) / sizeof(vec_t); ++k) + accTileOut[k] = vec_add_16(vec_sub_16(accTileIn[k], columnR0[k]), columnA[k]); } + else + { + const IndexType offsetR1 = HalfDimensions * removed[0][1]; + auto columnR1 = reinterpret_cast(&weights[offsetR1]); + + for (IndexType k = 0; k < HalfDimensions * sizeof(std::int16_t) / sizeof(vec_t); ++k) + accTileOut[k] = vec_sub_16( + vec_add_16(accTileIn[k], columnA[k]), + vec_add_16(columnR0[k], columnR1[k])); + } + + auto accTilePsqtIn = reinterpret_cast( + &st->accumulator.psqtAccumulation[Perspective][0]); + auto accTilePsqtOut = reinterpret_cast( + &states_to_update[0]->accumulator.psqtAccumulation[Perspective][0]); - // Difference calculation for the activated features - for (const auto index : added[i]) + const IndexType offsetPsqtR0 = PSQTBuckets * removed[0][0]; + auto columnPsqtR0 = reinterpret_cast(&psqtWeights[offsetPsqtR0]); + const IndexType offsetPsqtA = PSQTBuckets * added[0][0]; + auto columnPsqtA = reinterpret_cast(&psqtWeights[offsetPsqtA]); + + if (removed[0].size() == 1) { - const IndexType offset = HalfDimensions * index + j * TileHeight; - auto column = reinterpret_cast(&weights[offset]); - for (IndexType k = 0; k < NumRegs; ++k) - acc[k] = vec_add_16(acc[k], column[k]); + for (std::size_t k = 0; k < PSQTBuckets * sizeof(std::int32_t) / sizeof(psqt_vec_t); ++k) + accTilePsqtOut[k] = vec_add_psqt_32(vec_sub_psqt_32( + accTilePsqtIn[k], columnPsqtR0[k]), columnPsqtA[k]); } + else + { + const IndexType offsetPsqtR1 = PSQTBuckets * removed[0][1]; + auto columnPsqtR1 = reinterpret_cast(&psqtWeights[offsetPsqtR1]); - // Store accumulator - accTile = reinterpret_cast( - &states_to_update[i]->accumulator.accumulation[Perspective][j * TileHeight]); - for (IndexType k = 0; k < NumRegs; ++k) - vec_store(&accTile[k], acc[k]); - } + for (std::size_t k = 0; k < PSQTBuckets * sizeof(std::int32_t) / sizeof(psqt_vec_t); ++k) + accTilePsqtOut[k] = vec_sub_psqt_32( + vec_add_psqt_32(accTilePsqtIn[k], columnPsqtA[k]), + vec_add_psqt_32(columnPsqtR0[k], columnPsqtR1[k])); + } } - - for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j) + else { - // Load accumulator - auto accTilePsqt = reinterpret_cast( - &st->accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); - for (std::size_t k = 0; k < NumPsqtRegs; ++k) - psqt[k] = vec_load_psqt(&accTilePsqt[k]); - - for (IndexType i = 0; states_to_update[i]; ++i) - { - // Difference calculation for the deactivated features - for (const auto index : removed[i]) + for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) { - const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; - auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); - for (std::size_t k = 0; k < NumPsqtRegs; ++k) - psqt[k] = vec_sub_psqt_32(psqt[k], columnPsqt[k]); + // Load accumulator + auto accTileIn = reinterpret_cast( + &st->accumulator.accumulation[Perspective][j * TileHeight]); + for (IndexType k = 0; k < NumRegs; ++k) + acc[k] = vec_load(&accTileIn[k]); + + for (IndexType i = 0; states_to_update[i]; ++i) + { + // Difference calculation for the deactivated features + for (const auto index : removed[i]) + { + const IndexType offset = HalfDimensions * index + j * TileHeight; + auto column = reinterpret_cast(&weights[offset]); + for (IndexType k = 0; k < NumRegs; ++k) + acc[k] = vec_sub_16(acc[k], column[k]); + } + + // Difference calculation for the activated features + for (const auto index : added[i]) + { + const IndexType offset = HalfDimensions * index + j * TileHeight; + auto column = reinterpret_cast(&weights[offset]); + for (IndexType k = 0; k < NumRegs; ++k) + acc[k] = vec_add_16(acc[k], column[k]); + } + + // Store accumulator + auto accTileOut = reinterpret_cast( + &states_to_update[i]->accumulator.accumulation[Perspective][j * TileHeight]); + for (IndexType k = 0; k < NumRegs; ++k) + vec_store(&accTileOut[k], acc[k]); + } } - // Difference calculation for the activated features - for (const auto index : added[i]) + for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j) { - const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; - auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); + // Load accumulator + auto accTilePsqtIn = reinterpret_cast( + &st->accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); for (std::size_t k = 0; k < NumPsqtRegs; ++k) - psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]); + psqt[k] = vec_load_psqt(&accTilePsqtIn[k]); + + for (IndexType i = 0; states_to_update[i]; ++i) + { + // Difference calculation for the deactivated features + for (const auto index : removed[i]) + { + const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; + auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + psqt[k] = vec_sub_psqt_32(psqt[k], columnPsqt[k]); + } + + // Difference calculation for the activated features + for (const auto index : added[i]) + { + const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; + auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]); + } + + // Store accumulator + auto accTilePsqtOut = reinterpret_cast( + &states_to_update[i]->accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + vec_store_psqt(&accTilePsqtOut[k], psqt[k]); + } } - - // Store accumulator - accTilePsqt = reinterpret_cast( - &states_to_update[i]->accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); - for (std::size_t k = 0; k < NumPsqtRegs; ++k) - vec_store_psqt(&accTilePsqt[k], psqt[k]); - } } - #else for (IndexType i = 0; states_to_update[i]; ++i) { From 008d59512ac38e1e4a2f7880fe4e07b902845bb0 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Sat, 30 Sep 2023 06:57:39 +0200 Subject: [PATCH 1217/1766] Simplify collection of bad moves for history updates. 1. collect only the first 32 moves searched and ignore the rest. So late bad moves get no further negative history updates. 2. collect now for quiet moves also at most 32 bad moves STC: https://tests.stockfishchess.org/tests/view/6517b3aeb3e74811c8af5651 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 51168 W: 13013 L: 12810 D: 25345 Ptnml(0-2): 120, 6006, 13186, 6095, 177 LTC: https://tests.stockfishchess.org/tests/view/651adafecff46e538ee02734 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 109866 W: 27786 L: 27656 D: 54424 Ptnml(0-2): 52, 11816, 31069, 11942, 54 closes https://github.com/official-stockfish/Stockfish/pull/4818 Bench: 1338617 --- src/search.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5508478862f..f49c23aed65 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -546,7 +546,7 @@ namespace { assert(0 < depth && depth < MAX_PLY); assert(!(PvNode && cutNode)); - Move pv[MAX_PLY+1], capturesSearched[32], quietsSearched[64]; + Move pv[MAX_PLY+1], capturesSearched[32], quietsSearched[32]; StateInfo st; ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize); @@ -1328,12 +1328,12 @@ namespace { // If the move is worse than some previously searched move, remember it, to update its stats later - if (move != bestMove) + if (move != bestMove && moveCount <= 32) { - if (capture && captureCount < 32) + if (capture) capturesSearched[captureCount++] = move; - else if (!capture && quietCount < 64) + else quietsSearched[quietCount++] = move; } } From 25d444ed60e3873c02a70525776b145f03833103 Mon Sep 17 00:00:00 2001 From: candirufish <38038147+candirufish@users.noreply.github.com> Date: Sat, 7 Oct 2023 14:46:03 +0200 Subject: [PATCH 1218/1766] Razor more if ss+1 cutoffCnt > 3 STC: LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 221760 W: 56726 L: 56144 D: 108890 Ptnml(0-2): 655, 25453, 58123, 25953, 696 https://tests.stockfishchess.org/tests/view/651d34dbcff46e538ee05d91 LTC: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 130326 W: 33188 L: 32681 D: 64457 Ptnml(0-2): 69, 13949, 36620, 14456, 69 https://tests.stockfishchess.org/tests/view/651f844eac577114367273d5 closes https://github.com/official-stockfish/Stockfish/pull/4822 bench: 1291708 --- src/search.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index f49c23aed65..a5b5c101e69 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -766,7 +766,8 @@ namespace { // Step 7. Razoring (~1 Elo) // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. - if (eval < alpha - 456 - 252 * depth * depth) + // Adjust razor margin according to cutoffCnt. (~1 Elo) + if (eval < alpha - 456 - (252 - 200 * ((ss+1)->cutoffCnt > 3)) * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) From f7fbc6880efae2ec9d97d6f1d65e2ad00547e32c Mon Sep 17 00:00:00 2001 From: gabe Date: Fri, 6 Oct 2023 22:59:22 +0200 Subject: [PATCH 1219/1766] Avoid recomputing moveCountPruning In search, when moveCountPruning becomes true, it can never turn false again. Passed STC https://tests.stockfishchess.org/tests/view/652075ceac57711436728aac LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 136448 W: 34923 L: 34472 D: 67053 Ptnml(0-2): 420, 15094, 36767, 15501, 442 closes https://github.com/official-stockfish/Stockfish/pull/4823 Non functional change --- src/search.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index a5b5c101e69..69d8decff36 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -984,7 +984,8 @@ namespace { && bestValue > VALUE_TB_LOSS_IN_MAX_PLY) { // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold (~8 Elo) - moveCountPruning = moveCount >= futility_move_count(improving, depth); + if (!moveCountPruning) + moveCountPruning = moveCount >= futility_move_count(improving, depth); // Reduced depth of the next LMR search int lmrDepth = newDepth - r; From 7a4de96159f76f2465d474d76e08a1c8ca3383b8 Mon Sep 17 00:00:00 2001 From: Taras Vuk <117687515+TarasVuk@users.noreply.github.com> Date: Sat, 7 Oct 2023 23:25:34 +0200 Subject: [PATCH 1220/1766] Skip futility pruning if ttMove has bad history Passed STC: LLR: 2.96 (-2.94,2.94) <0.00,2.00> Total: 52416 W: 13465 L: 13128 D: 25823 Ptnml(0-2): 128, 6024, 13604, 6287, 165 https://tests.stockfishchess.org/tests/view/651fadd4ac577114367277bf Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 87348 W: 22234 L: 21818 D: 43296 Ptnml(0-2): 38, 9240, 24698, 9664, 34 https://tests.stockfishchess.org/tests/view/65201932ac57711436728218 closes https://github.com/official-stockfish/Stockfish/pull/4825 bench: 1246560 --- AUTHORS | 1 + src/search.cpp | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index b1e82806644..d7b64b62e8c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -210,6 +210,7 @@ Steinar Gunderson (sesse) Stéphane Nicolet (snicolet) Stephen Touset (stouset) Syine Mineta (MinetaS) +Taras Vuk (TarasVuk) Thanar2 thaspel theo77186 diff --git a/src/search.cpp b/src/search.cpp index 69d8decff36..8510651360c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -780,7 +780,10 @@ namespace { && depth < 9 && eval - futility_margin(depth, cutNode && !ss->ttHit, improving) - (ss-1)->statScore / 306 >= beta && eval >= beta - && eval < 24923) // smaller than TB wins + && eval < 24923 // smaller than TB wins + && !( !ttCapture + && ttMove + && thisThread->mainHistory[us][from_to(ttMove)] < 989)) return eval; // Step 9. Null move search with verification search (~35 Elo) From 002636362e175134c6d0d53b332b527ec4a12db0 Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Tue, 10 Oct 2023 17:43:36 +0200 Subject: [PATCH 1221/1766] Search parameters tune at 180+1.8 Passed VLTC: https://tests.stockfishchess.org/tests/view/65200c58ac577114367280bc LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 146180 W: 37407 L: 36988 D: 71785 Ptnml(0-2): 21, 14474, 43675, 14905, 15 Passed VLTC SMP: https://tests.stockfishchess.org/tests/view/652403da3125598fc7eb4b6d LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 57580 W: 15061 L: 14739 D: 27780 Ptnml(0-2): 2, 5001, 18460, 5327, 0 closes https://github.com/official-stockfish/Stockfish/pull/4826 Bench: 1099336 --- src/search.cpp | 78 +++++++++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 8510651360c..71332e506df 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -73,7 +73,7 @@ namespace { // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving) { - return Value((140 - 40 * noTtCutNode) * (d - improving)); + return Value((126 - 42 * noTtCutNode) * (d - improving)); } // Reductions lookup table initialized at startup @@ -81,8 +81,8 @@ namespace { Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) { int reductionScale = Reductions[d] * Reductions[mn]; - return (reductionScale + 1372 - int(delta) * 1073 / int(rootDelta)) / 1024 - + (!i && reductionScale > 936); + return (reductionScale + 1560 - int(delta) * 945 / int(rootDelta)) / 1024 + + (!i && reductionScale > 791); } constexpr int futility_move_count(bool improving, Depth depth) { @@ -92,7 +92,7 @@ namespace { // History and stats update bonus, based on depth int stat_bonus(Depth d) { - return std::min(336 * d - 547, 1561); + return std::min(334 * d - 531, 1538); } // Add a small random component to draw evaluations to avoid 3-fold blindness @@ -174,7 +174,7 @@ namespace { void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int((20.57 + std::log(Threads.size()) / 2) * std::log(i)); + Reductions[i] = int((20.37 + std::log(Threads.size()) / 2) * std::log(i)); } @@ -359,12 +359,12 @@ void Thread::search() { // Reset aspiration window starting size Value prev = rootMoves[pvIdx].averageScore; - delta = Value(10) + int(prev) * prev / 15799; + delta = Value(10) + int(prev) * prev / 17470; alpha = std::max(prev - delta,-VALUE_INFINITE); beta = std::min(prev + delta, VALUE_INFINITE); // Adjust optimism based on root move's previousScore (~4 Elo) - int opt = 109 * prev / (std::abs(prev) + 141); + int opt = 113 * prev / (std::abs(prev) + 109); optimism[ us] = Value(opt); optimism[~us] = -optimism[us]; @@ -750,7 +750,7 @@ namespace { // Use static evaluation difference to improve quiet move ordering (~4 Elo) if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture) { - int bonus = std::clamp(-18 * int((ss-1)->staticEval + ss->staticEval), -1817, 1817); + int bonus = std::clamp(-18 * int((ss-1)->staticEval + ss->staticEval), -1812, 1812); thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus; } @@ -767,7 +767,7 @@ namespace { // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. // Adjust razor margin according to cutoffCnt. (~1 Elo) - if (eval < alpha - 456 - (252 - 200 * ((ss+1)->cutoffCnt > 3)) * depth * depth) + if (eval < alpha - 492 - (257 - 200 * ((ss+1)->cutoffCnt > 3)) * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -778,9 +778,9 @@ namespace { // The depth condition is important for mate finding. if ( !ss->ttPv && depth < 9 - && eval - futility_margin(depth, cutNode && !ss->ttHit, improving) - (ss-1)->statScore / 306 >= beta + && eval - futility_margin(depth, cutNode && !ss->ttHit, improving) - (ss-1)->statScore / 321 >= beta && eval >= beta - && eval < 24923 // smaller than TB wins + && eval < 29462 // smaller than TB wins && !( !ttCapture && ttMove && thisThread->mainHistory[us][from_to(ttMove)] < 989)) @@ -789,10 +789,10 @@ namespace { // Step 9. Null move search with verification search (~35 Elo) if ( !PvNode && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 17329 + && (ss-1)->statScore < 17257 && eval >= beta && eval >= ss->staticEval - && ss->staticEval >= beta - 21 * depth + 258 + && ss->staticEval >= beta - 24 * depth + 281 && !excludedMove && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly @@ -801,7 +801,7 @@ namespace { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and eval - Depth R = std::min(int(eval - beta) / 173, 6) + depth / 3 + 4; + Depth R = std::min(int(eval - beta) / 152, 6) + depth / 3 + 4; ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -850,7 +850,7 @@ namespace { && !ttMove) depth -= 2; - probCutBeta = beta + 168 - 61 * improving; + probCutBeta = beta + 168 - 70 * improving; // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search returns a value @@ -906,7 +906,7 @@ namespace { moves_loop: // When in check, search starts here // Step 12. A small Probcut idea, when we are in check (~4 Elo) - probCutBeta = beta + 413; + probCutBeta = beta + 416; if ( ss->inCheck && !PvNode && ttCapture @@ -1000,12 +1000,12 @@ namespace { if ( !givesCheck && lmrDepth < 7 && !ss->inCheck - && ss->staticEval + 197 + 248 * lmrDepth + PieceValue[pos.piece_on(to_sq(move))] + && ss->staticEval + 188 + 206 * lmrDepth + PieceValue[pos.piece_on(to_sq(move))] + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 7 < alpha) continue; // SEE based pruning for captures and checks (~11 Elo) - if (!pos.see_ge(move, Value(-205) * depth)) + if (!pos.see_ge(move, Value(-185) * depth)) continue; } else @@ -1016,24 +1016,24 @@ namespace { // Continuation history based pruning (~2 Elo) if ( lmrDepth < 6 - && history < -3832 * depth) + && history < -3232 * depth) continue; history += 2 * thisThread->mainHistory[us][from_to(move)]; - lmrDepth += history / 7011; + lmrDepth += history / 5793; lmrDepth = std::max(lmrDepth, -2); // Futility pruning: parent node (~13 Elo) if ( !ss->inCheck - && lmrDepth < 12 - && ss->staticEval + 112 + 138 * lmrDepth <= alpha) + && lmrDepth < 13 + && ss->staticEval + 115 + 122 * lmrDepth <= alpha) continue; lmrDepth = std::max(lmrDepth, 0); // Prune moves with negative SEE (~4 Elo) - if (!pos.see_ge(move, Value(-31 * lmrDepth * lmrDepth))) + if (!pos.see_ge(move, Value(-27 * lmrDepth * lmrDepth))) continue; } } @@ -1051,7 +1051,7 @@ namespace { // scaling. Their values are optimized to time controls of 180+1.8 and longer // so changing them requires tests at this type of time controls. if ( !rootNode - && depth >= 4 - (thisThread->completedDepth > 22) + 2 * (PvNode && tte->is_pv()) + && depth >= 4 - (thisThread->completedDepth > 24) + 2 * (PvNode && tte->is_pv()) && move == ttMove && !excludedMove // Avoid recursive singular search /* && ttValue != VALUE_NONE Already implicit in the next condition */ @@ -1059,7 +1059,7 @@ namespace { && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - (82 + 65 * (ss->ttPv && !PvNode)) * depth / 64; + Value singularBeta = ttValue - (64 + 57 * (ss->ttPv && !PvNode)) * depth / 64; Depth singularDepth = (depth - 1) / 2; ss->excludedMove = move; @@ -1073,11 +1073,11 @@ namespace { // Avoid search explosion by limiting the number of double extensions if ( !PvNode - && value < singularBeta - 21 + && value < singularBeta - 18 && ss->doubleExtensions <= 11) { extension = 2; - depth += depth < 13; + depth += depth < 15; } } @@ -1095,7 +1095,7 @@ namespace { // If we are on a cutNode, reduce it based on depth (negative extension) (~1 Elo) else if (cutNode) - extension = depth < 17 ? -3 : -1; + extension = depth < 19 ? -2 : -1; // If the eval of ttMove is less than value, we reduce it (negative extension) (~1 Elo) else if (ttValue <= value) @@ -1111,7 +1111,7 @@ namespace { else if ( PvNode && move == ttMove && move == ss->killers[0] - && (*contHist[0])[movedPiece][to_sq(move)] >= 5168) + && (*contHist[0])[movedPiece][to_sq(move)] >= 4194) extension = 1; } @@ -1139,7 +1139,7 @@ namespace { r -= cutNode && tte->depth() >= depth ? 3 : 2; // Decrease reduction if opponent's move count is high (~1 Elo) - if ((ss-1)->moveCount > 8) + if ((ss-1)->moveCount > 7) r--; // Increase reduction for cut nodes (~3 Elo) @@ -1175,10 +1175,10 @@ namespace { + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 4006; + - 3848; // Decrease/increase reduction for moves with a good/bad history (~25 Elo) - r -= ss->statScore / (11124 + 4740 * (depth > 5 && depth < 22)); + r -= ss->statScore / (10216 + 3855 * (depth > 5 && depth < 23)); // Step 17. Late moves reduction / extension (LMR, ~117 Elo) // We use various heuristics for the sons of a node after the first son has @@ -1202,8 +1202,8 @@ namespace { { // Adjust full-depth search based on LMR results - if the result // was good enough search deeper, if it was bad enough search shallower - const bool doDeeperSearch = value > (bestValue + 64 + 11 * (newDepth - d)); - const bool doEvenDeeperSearch = value > alpha + 711 && ss->doubleExtensions <= 6; + const bool doDeeperSearch = value > (bestValue + 51 + 10 * (newDepth - d)); + const bool doEvenDeeperSearch = value > alpha + 700 && ss->doubleExtensions <= 6; const bool doShallowerSearch = value < bestValue + newDepth; ss->doubleExtensions = ss->doubleExtensions + doEvenDeeperSearch; @@ -1321,8 +1321,8 @@ namespace { // Reduce other moves if we have found at least one score improvement (~2 Elo) if ( depth > 2 && depth < 12 - && beta < 14362 - && value > -12393) + && beta < 13828 + && value > -11369) depth -= 2; assert(depth > 0); @@ -1371,7 +1371,7 @@ namespace { // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (depth > 5) + (PvNode || cutNode) + (bestValue < alpha - 800) + ((ss-1)->moveCount > 12); + int bonus = (depth > 6) + (PvNode || cutNode) + (bestValue < alpha - 653) + ((ss-1)->moveCount > 11); update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << stat_bonus(depth) * bonus / 2; } @@ -1593,7 +1593,7 @@ namespace { continue; // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, Value(-95))) + if (!pos.see_ge(move, Value(-90))) continue; } @@ -1726,7 +1726,7 @@ namespace { if (!pos.capture_stage(bestMove)) { - int bestMoveBonus = bestValue > beta + 145 ? quietMoveBonus // larger bonus + int bestMoveBonus = bestValue > beta + 168 ? quietMoveBonus // larger bonus : stat_bonus(depth); // smaller bonus // Increase stats for the best move in case it was a quiet move From 38e830af4bfa6c9e9c11279a8e6a60b6ca4ec2cd Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 14 Oct 2023 17:41:41 +0300 Subject: [PATCH 1222/1766] Use more continuation histories. This patch allows stats updates and movepicker bonuses for continuation history 3 plies deep - so counter counter move. Updates and movepicker usage are done with 1/4 multiplier compared to other histories. Passed STC: https://tests.stockfishchess.org/tests/view/6528f28d3125598fc7ebb5a3 LLR: 2.96 (-2.94,2.94) <0.00,2.00> Total: 161344 W: 41369 L: 40868 D: 79107 Ptnml(0-2): 535, 18720, 41679, 19185, 553 Passed LTC: https://tests.stockfishchess.org/tests/view/652a397a3125598fc7ebd1d6 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 48564 W: 12556 L: 12215 D: 23793 Ptnml(0-2): 25, 5149, 13595, 5486, 27 closes https://github.com/official-stockfish/Stockfish/pull/4827 bench 1327410 --- src/movepick.cpp | 1 + src/search.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index eea1d49e447..bc3fcf7ed6b 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -141,6 +141,7 @@ void MovePicker::score() { m.value = 2 * (*mainHistory)[pos.side_to_move()][from_to(m)]; m.value += 2 * (*continuationHistory[0])[pc][to]; m.value += (*continuationHistory[1])[pc][to]; + m.value += (*continuationHistory[2])[pc][to] / 4; m.value += (*continuationHistory[3])[pc][to]; m.value += (*continuationHistory[5])[pc][to]; diff --git a/src/search.cpp b/src/search.cpp index 71332e506df..a1834ab99b3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -918,7 +918,7 @@ namespace { return probCutBeta; const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, - nullptr , (ss-4)->continuationHistory, + (ss-3)->continuationHistory, (ss-4)->continuationHistory, nullptr , (ss-6)->continuationHistory }; Move countermove = prevSq != SQ_NONE ? thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] : MOVE_NONE; @@ -1511,7 +1511,7 @@ namespace { } const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, - nullptr , (ss-4)->continuationHistory, + (ss-3)->continuationHistory, (ss-4)->continuationHistory, nullptr , (ss-6)->continuationHistory }; // Initialize a MovePicker object for the current position, and prepare @@ -1768,13 +1768,13 @@ namespace { void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { - for (int i : {1, 2, 4, 6}) + for (int i : {1, 2, 3, 4, 6}) { // Only update the first 2 continuation histories if we are in check if (ss->inCheck && i > 2) break; if (is_ok((ss-i)->currentMove)) - (*(ss-i)->continuationHistory)[pc][to] << bonus; + (*(ss-i)->continuationHistory)[pc][to] << bonus / (1 + 3 * (i == 3)); } } From a4fedd8152e717a37ec8b8ddda0658364c1f5ee4 Mon Sep 17 00:00:00 2001 From: "Shahin M. Shahin" <41402573+peregrineshahin@users.noreply.github.com> Date: Thu, 27 Jul 2023 06:21:54 +0300 Subject: [PATCH 1223/1766] Fix greater than TB scores in null move pruning. This patch is a simplification and a fix to dealing with null moves scores that returns proven mates or TB scores by preventing 'null move pruning' if the nullvalue is in that range. Current solution downgrades nullValues on the non-PV node but the value can be used in a transposed PV-node to the same position afterwards (Triangulation), the later is prone to propagate a wrong score (96.05) to root that will not be refuted unless we search further. Score of (96.05) can be obtained be two methods, maxim static-eval returned on Pv update (mostly qSearch) this downgrade (clamp) in NMP and theoretically can happen with or without TBs but the second scenario is more dangerous than the first. This fixes the reproducible case in very common scenarios with TBs as shown in the debugging at discord. Fixes: #4699 Passed STC: https://tests.stockfishchess.org/tests/view/64c1eca8dc56e1650abba6f9 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 670048 W: 171132 L: 171600 D: 327316 Ptnml(0-2): 2134, 75687, 179820, 75279, 2104 Passed LTC: https://tests.stockfishchess.org/tests/view/64c5e130dc56e1650abc0438 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 92868 W: 23642 L: 23499 D: 45727 Ptnml(0-2): 52, 9509, 27171, 9648, 54 closes https://github.com/official-stockfish/Stockfish/pull/4715 Bench: 1327410 --- src/search.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index a1834ab99b3..1b019a08e82 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -812,11 +812,9 @@ namespace { pos.undo_null_move(); - if (nullValue >= beta) + // Do not return unproven mate or TB scores + if (nullValue >= beta && nullValue < VALUE_TB_WIN_IN_MAX_PLY) { - // Do not return unproven mate or TB scores - nullValue = std::min(nullValue, VALUE_TB_WIN_IN_MAX_PLY-1); - if (thisThread->nmpMinPly || depth < 14) return nullValue; From fe53a18f7a149f7e6d1a9dde8a7478692ef82997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sun, 1 Oct 2023 19:18:05 +0200 Subject: [PATCH 1224/1766] Reformat some comments and conditions closes https://github.com/official-stockfish/Stockfish/pull/4814 No functional change --- src/movegen.cpp | 5 +-- src/position.cpp | 18 +++++----- src/search.cpp | 88 ++++++++++++++++++++++++------------------------ 3 files changed, 56 insertions(+), 55 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index c6a8dbb8cb7..cda43b3a582 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -236,9 +236,10 @@ namespace { /// Generates all pseudo-legal captures plus queen promotions /// Generates all pseudo-legal non-captures and underpromotions -/// Generates all pseudo-legal check evasions when the side to move is in check -/// Generates all pseudo-legal non-captures giving check, except castling and promotions +/// Generates all pseudo-legal check evasions /// Generates all pseudo-legal captures and non-captures +/// Generates all pseudo-legal non-captures giving check, +/// except castling and promotions /// /// Returns a pointer to the end of the move list. diff --git a/src/position.cpp b/src/position.cpp index a2b377af9e5..0d7d957141f 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -102,9 +102,9 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) { } -// Implements Marcel van Kervinck's cuckoo algorithm to detect repetition of positions -// for 3-fold repetition draws. The algorithm uses two hash tables with Zobrist hashes to -// allow fast detection of recurring positions. For details see: +// Implements Marcel van Kervinck's cuckoo algorithm to detect repetition of positions +// for 3-fold repetition draws. The algorithm uses two hash tables with Zobrist hashes +// to allow fast detection of recurring positions. For details see: // http://web.archive.org/web/20201107002606/https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf // First and second hash functions for indexing the cuckoo tables @@ -188,9 +188,9 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th 4) En passant target square (in algebraic notation). If there's no en passant target square, this is "-". If a pawn has just made a 2-square move, this - is the position "behind" the pawn. Following X-FEN standard, this is recorded only - if there is a pawn in position to make an en passant capture, and if there really - is a pawn that might have advanced two squares. + is the position "behind" the pawn. Following X-FEN standard, this is recorded + only if there is a pawn in position to make an en passant capture, and if + there really is a pawn that might have advanced two squares. 5) Halfmove clock. This is the number of halfmoves since the last pawn advance or capture. This is used to determine if a draw can be claimed under the @@ -587,8 +587,8 @@ bool Position::pseudo_legal(const Move m) const { return false; if ( !(pawn_attacks_bb(us, from) & pieces(~us) & to) // Not a capture - && !((from + pawn_push(us) == to) && empty(to)) // Not a single push - && !( (from + 2 * pawn_push(us) == to) // Not a double push + && !((from + pawn_push(us) == to) && empty(to)) // Not a single push + && !( (from + 2 * pawn_push(us) == to) // Not a double push && (relative_rank(us, from) == RANK_2) && empty(to) && empty(to - pawn_push(us)))) @@ -959,7 +959,7 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ // Remove both pieces first since squares could overlap in Chess960 remove_piece(Do ? from : to); remove_piece(Do ? rfrom : rto); - board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do this for us + board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // remove_piece does not do this for us put_piece(make_piece(us, KING), Do ? to : from); put_piece(make_piece(us, ROOK), Do ? rto : rfrom); } diff --git a/src/search.cpp b/src/search.cpp index 1b019a08e82..2d4a3f3d35b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -100,12 +100,12 @@ namespace { return VALUE_DRAW - 1 + Value(thisThread->nodes & 0x2); } - // Skill structure is used to implement strength limit. - // If we have a UCI_Elo, we convert it to an appropriate skill level, anchored to the Stash engine. - // This method is based on a fit of the Elo results for games played between the master at various - // skill levels and various versions of the Stash engine, all ranked at CCRL. + // Skill structure is used to implement strength limit. If we have a UCI_Elo, + // we convert it to an appropriate skill level, anchored to the Stash engine. + // This method is based on a fit of the Elo results for games played between + // Stockfish at various skill levels and various versions of the Stash engine. // Skill 0 .. 19 now covers CCRL Blitz Elo from 1320 to 3190, approximately - // Reference: https://github.com/vondele/Stockfish/commit/a08b8d4e9711c20acedbfe17d618c3c384b339ec + // Reference: https://github.com/vondele/Stockfish/commit/a08b8d4e9711c2 struct Skill { Skill(int skill_level, int uci_elo) { if (uci_elo) @@ -274,9 +274,9 @@ void MainThread::search() { void Thread::search() { - // Allocate stack with extra size to allow access from (ss-7) to (ss+2) - // (ss-7) is needed for update_continuation_histories(ss-1, ...) which accesses (ss-6) - // (ss+2) is needed for initialization of statScore and killers + // Allocate stack with extra size to allow access from (ss-7) to (ss+2): + // (ss-7) is needed for update_continuation_histories(ss-1) which accesses (ss-6), + // (ss+2) is needed for initialization of statScore and killers. Stack stack[MAX_PLY+10], *ss = stack+7; Move pv[MAX_PLY+1]; Value alpha, beta, delta; @@ -478,8 +478,7 @@ void Thread::search() { double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability; - // Cap used time in case of a single legal move for a better viewer experience in tournaments - // yielding correct scores and sufficiently fast moves. + // Cap used time in case of a single legal move for a better viewer experience if (rootMoves.size() == 1) totalTime = std::min(500.0, totalTime); @@ -574,7 +573,8 @@ namespace { static_cast(thisThread)->check_time(); // Used to send selDepth info to GUI (selDepth counts from 1, ply from 0) - if (PvNode && thisThread->selDepth < ss->ply + 1) + if ( PvNode + && thisThread->selDepth < ss->ply + 1) thisThread->selDepth = ss->ply + 1; if (!rootNode) @@ -640,7 +640,9 @@ namespace { update_quiet_stats(pos, ss, ttMove, stat_bonus(depth)); // Extra penalty for early quiet moves of the previous ply (~0 Elo on STC, ~2 Elo on LTC) - if (prevSq != SQ_NONE && (ss-1)->moveCount <= 2 && !priorCapture) + if ( prevSq != SQ_NONE + && (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 (~1 Elo) @@ -748,7 +750,9 @@ namespace { } // Use static evaluation difference to improve quiet move ordering (~4 Elo) - if (is_ok((ss-1)->currentMove) && !(ss-1)->inCheck && !priorCapture) + if ( is_ok((ss-1)->currentMove) + && !(ss-1)->inCheck + && !priorCapture) { int bonus = std::clamp(-18 * int((ss-1)->staticEval + ss->staticEval), -1812, 1812); thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus; @@ -979,7 +983,8 @@ namespace { Depth r = reduction(improving, depth, moveCount, delta, thisThread->rootDelta); - // Step 14. Pruning at shallow depth (~120 Elo). Depth conditions are important for mate finding. + // Step 14. Pruning at shallow depth (~120 Elo). + // Depth conditions are important for mate finding. if ( !rootNode && pos.non_pawn_material(us) && bestValue > VALUE_TB_LOSS_IN_MAX_PLY) @@ -1052,7 +1057,6 @@ namespace { && depth >= 4 - (thisThread->completedDepth > 24) + 2 * (PvNode && tte->is_pv()) && move == ttMove && !excludedMove // Avoid recursive singular search - /* && ttValue != VALUE_NONE Already implicit in the next condition */ && abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) @@ -1130,8 +1134,7 @@ namespace { // Step 16. Make the move pos.do_move(move, st, givesCheck); - // Decrease reduction if position is or has been on the PV and not likely to fail low. (~3 Elo) - // Decrease further on cutNodes. (~1 Elo) + // Decrease reduction if position is or has been on the PV (~4 Elo) if ( ss->ttPv && !likelyFailLow) r -= cutNode && tte->depth() >= depth ? 3 : 2; @@ -1196,10 +1199,11 @@ namespace { value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); // Do a full-depth search when reduced LMR search fails high - if (value > alpha && d < newDepth) + if ( value > alpha + && d < newDepth) { // Adjust full-depth search based on LMR results - if the result - // was good enough search deeper, if it was bad enough search shallower + // was good enough search deeper, if it was bad enough search shallower. const bool doDeeperSearch = value > (bestValue + 51 + 10 * (newDepth - d)); const bool doEvenDeeperSearch = value > alpha + 700 && ss->doubleExtensions <= 6; const bool doShallowerSearch = value < bestValue + newDepth; @@ -1219,19 +1223,22 @@ namespace { } } - // Step 18. Full-depth search when LMR is skipped. If expected reduction is high, reduce its depth by 1. + // Step 18. Full-depth search when LMR is skipped else if (!PvNode || moveCount > 1) { // Increase reduction for cut nodes and not ttMove (~1 Elo) - if (!ttMove && cutNode) + if ( !ttMove + && cutNode) r += 2; + // Note that if expected reduction is high, we reduce search depth by 1 here value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth - (r > 3), !cutNode); } // For PV nodes only, do a full PV search on the first move or after a fail high, // otherwise let the parent node fail low with value <= alpha and try another move. - if (PvNode && (moveCount == 1 || value > alpha)) + if ( PvNode + && (moveCount == 1 || value > alpha)) { (ss+1)->pv = pv; (ss+1)->pv[0] = MOVE_NONE; @@ -1329,8 +1336,8 @@ namespace { } } - - // If the move is worse than some previously searched move, remember it, to update its stats later + // If the move is worse than some previously searched move, + // remember it, to update its stats later. if (move != bestMove && moveCount <= 32) { if (capture) @@ -1341,14 +1348,6 @@ namespace { } } - // The following condition would detect a stop only after move loop has been - // completed. But in this case, bestValue is valid because we have fully - // searched our subtree, and we can anyhow save the result in TT. - /* - if (Threads.stop) - return VALUE_DRAW; - */ - // Step 21. Check for mate and stalemate // All legal moves have been searched and if there are no legal moves, it // must be a mate or a stalemate. If we are in a singular extension search then @@ -1494,7 +1493,6 @@ namespace { // Stand pat. Return immediately if static value is at least beta if (bestValue >= beta) { - // Save gathered info in transposition table if (!ss->ttHit) tte->save(posKey, value_to_tt(bestValue, ss->ply), false, BOUND_LOWER, DEPTH_NONE, MOVE_NONE, ss->staticEval); @@ -1539,8 +1537,9 @@ namespace { moveCount++; - // Step 6. Pruning. - if (bestValue > VALUE_TB_LOSS_IN_MAX_PLY && pos.non_pawn_material(us)) + // Step 6. Pruning + if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY + && pos.non_pawn_material(us)) { // Futility pruning and moveCount pruning (~10 Elo) if ( !givesCheck @@ -1554,7 +1553,7 @@ namespace { futilityValue = futilityBase + PieceValue[pos.piece_on(to_sq(move))]; // If static eval + value of piece we are going to capture is much lower - // than alpha we can prune this move + // than alpha we can prune this move. if (futilityValue <= alpha) { bestValue = std::max(bestValue, futilityValue); @@ -1562,15 +1561,16 @@ namespace { } // If static eval is much lower than alpha and move is not winning material - // we can prune this move - if (futilityBase <= alpha && !pos.see_ge(move, VALUE_ZERO + 1)) + // we can prune this move. + if ( futilityBase <= alpha + && !pos.see_ge(move, VALUE_ZERO + 1)) { bestValue = std::max(bestValue, futilityBase); continue; } // If static exchange evaluation is much worse than what is needed to not - // fall below alpha we can prune this move + // fall below alpha we can prune this move. if (futilityBase > alpha && !pos.see_ge(move, (alpha - futilityBase) * 4)) { bestValue = alpha; @@ -1655,8 +1655,8 @@ namespace { } - // value_to_tt() adjusts a mate or TB score from "plies to mate from the root" to - // "plies to mate from the current position". Standard scores are unchanged. + // value_to_tt() adjusts a mate or TB score from "plies to mate from the root" + // to "plies to mate from the current position". Standard scores are unchanged. // The function is called before storing a value in the transposition table. Value value_to_tt(Value v, int ply) { @@ -1670,9 +1670,9 @@ namespace { // value_from_tt() is the inverse of value_to_tt(): it adjusts a mate or TB score // from the transposition table (which refers to the plies to mate/be mated from - // current position) to "plies to mate/be mated (TB win/loss) from the root". However, - // for mate scores, to avoid potentially false mate scores related to the 50 moves rule - // and the graph history interaction, we return an optimal TB score instead. + // current position) to "plies to mate/be mated (TB win/loss) from the root". + // However, to avoid potentially false mate scores related to the 50 moves rule + // and the graph history interaction problem, we return an optimal TB score instead. Value value_from_tt(Value v, int ply, int r50c) { From edb4ab924f09abd7c6836c7017365dceccd76b80 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Wed, 4 Oct 2023 18:14:40 +0300 Subject: [PATCH 1225/1766] Standardize Comments use double slashes (//) only for comments. closes #4820 No functional change. --- src/benchmark.cpp | 22 ++++---- src/bitboard.cpp | 12 ++--- src/bitboard.h | 66 +++++++++++------------ src/evaluate.cpp | 34 ++++++------ src/misc.cpp | 88 +++++++++++++++---------------- src/misc.h | 44 ++++++++-------- src/movegen.cpp | 18 +++---- src/movegen.h | 6 +-- src/movepick.cpp | 34 ++++++------ src/movepick.h | 60 ++++++++++----------- src/nnue/evaluate_nnue.cpp | 2 +- src/position.cpp | 104 ++++++++++++++++++------------------- src/position.h | 26 +++++----- src/search.cpp | 30 +++++------ src/search.h | 16 +++--- src/syzygy/tbprobe.cpp | 95 +++++++++++++++++---------------- src/thread.cpp | 40 +++++++------- src/thread.h | 16 +++--- src/thread_win32_osx.h | 10 ++-- src/timeman.cpp | 6 +-- src/timeman.h | 4 +- src/tt.cpp | 28 +++++----- src/tt.h | 30 +++++------ src/tune.h | 51 +++++++++--------- src/types.h | 74 +++++++++++++------------- src/uci.cpp | 42 +++++++-------- src/uci.h | 6 +-- src/ucioption.cpp | 20 +++---- 28 files changed, 491 insertions(+), 493 deletions(-) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 8e28184a3cd..d67e37f66ed 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -95,17 +95,17 @@ const std::vector Defaults = { namespace Stockfish { -/// setup_bench() builds a list of UCI commands to be run by bench. There -/// are five parameters: TT size in MB, number of search threads that -/// should be used, the limit value spent for each position, a file name -/// where to look for positions in FEN format, and the type of the limit: -/// depth, perft, nodes and movetime (in milliseconds). Examples: -/// -/// bench : search default positions up to depth 13 -/// bench 64 1 15 : search default positions up to depth 15 (TT = 64MB) -/// bench 64 1 100000 default nodes : search default positions for 100K nodes each -/// bench 64 4 5000 current movetime : search current position with 4 threads for 5 sec -/// bench 16 1 5 blah perft : run a perft 5 on positions in file "blah" +// setup_bench() builds a list of UCI commands to be run by bench. There +// are five parameters: TT size in MB, number of search threads that +// should be used, the limit value spent for each position, a file name +// where to look for positions in FEN format, and the type of the limit: +// depth, perft, nodes and movetime (in milliseconds). Examples: +// +// bench : search default positions up to depth 13 +// bench 64 1 15 : search default positions up to depth 15 (TT = 64MB) +// bench 64 1 100000 default nodes : search default positions for 100K nodes each +// bench 64 4 5000 current movetime : search current position with 4 threads for 5 sec +// bench 16 1 5 blah perft : run a perft 5 on positions in file "blah" std::vector setup_bench(const Position& current, std::istream& is) { diff --git a/src/bitboard.cpp b/src/bitboard.cpp index bed2b3ee309..89eeee611f8 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -46,8 +46,8 @@ namespace { } -/// safe_destination() returns the bitboard of target square for the given step -/// from the given square. If the step is off the board, returns empty bitboard. +// safe_destination() returns the bitboard of target square for the given step +// from the given square. If the step is off the board, returns empty bitboard. inline Bitboard safe_destination(Square s, int step) { Square to = Square(s + step); @@ -55,8 +55,8 @@ inline Bitboard safe_destination(Square s, int step) { } -/// Bitboards::pretty() returns an ASCII representation of a bitboard suitable -/// to be printed to standard output. Useful for debugging. +// Bitboards::pretty() returns an ASCII representation of a bitboard suitable +// to be printed to standard output. Useful for debugging. std::string Bitboards::pretty(Bitboard b) { @@ -75,8 +75,8 @@ std::string Bitboards::pretty(Bitboard b) { } -/// Bitboards::init() initializes various bitboard tables. It is called at -/// startup and relies on global objects to be already zero-initialized. +// Bitboards::init() initializes various bitboard tables. It is called at +// startup and relies on global objects to be already zero-initialized. void Bitboards::init() { diff --git a/src/bitboard.h b/src/bitboard.h index eb2f949d5aa..0908c957058 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -64,7 +64,7 @@ extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; -/// Magic holds all magic bitboards relevant data for a single square +// Magic holds all magic bitboards relevant data for a single square struct Magic { Bitboard mask; Bitboard magic; @@ -95,8 +95,8 @@ inline Bitboard square_bb(Square s) { } -/// Overloads of bitwise operators between a Bitboard and a Square for testing -/// whether a given bit is set in a bitboard, and for setting and clearing bits. +// Overloads of bitwise operators between a Bitboard and a Square for testing +// whether a given bit is set in a bitboard, and for setting and clearing bits. inline Bitboard operator&( Bitboard b, Square s) { return b & square_bb(s); } inline Bitboard operator|( Bitboard b, Square s) { return b | square_bb(s); } @@ -115,8 +115,8 @@ constexpr bool more_than_one(Bitboard b) { } -/// rank_bb() and file_bb() return a bitboard representing all the squares on -/// the given file or rank. +// rank_bb() and file_bb() return a bitboard representing all the squares on +// the given file or rank. constexpr Bitboard rank_bb(Rank r) { return Rank1BB << (8 * r); @@ -135,7 +135,7 @@ constexpr Bitboard file_bb(Square s) { } -/// shift() moves a bitboard one or two steps as specified by the direction D +// shift() moves a bitboard one or two steps as specified by the direction D template constexpr Bitboard shift(Bitboard b) { @@ -148,8 +148,8 @@ constexpr Bitboard shift(Bitboard b) { } -/// pawn_attacks_bb() returns the squares attacked by pawns of the given color -/// from the squares in the given bitboard. +// pawn_attacks_bb() returns the squares attacked by pawns of the given color +// from the squares in the given bitboard. template constexpr Bitboard pawn_attacks_bb(Bitboard b) { @@ -163,10 +163,10 @@ inline Bitboard pawn_attacks_bb(Color c, Square s) { return PawnAttacks[c][s]; } -/// line_bb() returns a bitboard representing an entire line (from board edge -/// to board edge) that intersects the two given squares. If the given squares -/// are not on a same file/rank/diagonal, the function returns 0. For instance, -/// line_bb(SQ_C4, SQ_F7) will return a bitboard with the A2-G8 diagonal. +// line_bb() returns a bitboard representing an entire line (from board edge +// to board edge) that intersects the two given squares. If the given squares +// are not on a same file/rank/diagonal, the function returns 0. For instance, +// line_bb(SQ_C4, SQ_F7) will return a bitboard with the A2-G8 diagonal. inline Bitboard line_bb(Square s1, Square s2) { @@ -176,13 +176,13 @@ inline Bitboard line_bb(Square s1, Square s2) { } -/// between_bb(s1, s2) returns a bitboard representing the squares in the semi-open -/// segment between the squares s1 and s2 (excluding s1 but including s2). If the -/// given squares are not on a same file/rank/diagonal, it returns s2. For instance, -/// between_bb(SQ_C4, SQ_F7) will return a bitboard with squares D5, E6 and F7, but -/// between_bb(SQ_E6, SQ_F8) will return a bitboard with the square F8. This trick -/// allows to generate non-king evasion moves faster: the defending piece must either -/// interpose itself to cover the check or capture the checking piece. +// between_bb(s1, s2) returns a bitboard representing the squares in the semi-open +// segment between the squares s1 and s2 (excluding s1 but including s2). If the +// given squares are not on a same file/rank/diagonal, it returns s2. For instance, +// between_bb(SQ_C4, SQ_F7) will return a bitboard with squares D5, E6 and F7, but +// between_bb(SQ_E6, SQ_F8) will return a bitboard with the square F8. This trick +// allows to generate non-king evasion moves faster: the defending piece must either +// interpose itself to cover the check or capture the checking piece. inline Bitboard between_bb(Square s1, Square s2) { @@ -191,16 +191,16 @@ inline Bitboard between_bb(Square s1, Square s2) { return BetweenBB[s1][s2]; } -/// aligned() returns true if the squares s1, s2 and s3 are aligned either on a -/// straight or on a diagonal line. +// aligned() returns true if the squares s1, s2 and s3 are aligned either on a +// straight or on a diagonal line. inline bool aligned(Square s1, Square s2, Square s3) { return line_bb(s1, s2) & s3; } -/// distance() functions return the distance between x and y, defined as the -/// number of steps for a king in x to reach y. +// distance() functions return the distance between x and y, defined as the +// number of steps for a king in x to reach y. template inline int distance(Square x, Square y); template<> inline int distance(Square x, Square y) { return std::abs(file_of(x) - file_of(y)); } @@ -209,8 +209,8 @@ template<> inline int distance(Square x, Square y) { return SquareDistan inline int edge_distance(File f) { return std::min(f, File(FILE_H - f)); } -/// attacks_bb(Square) returns the pseudo attacks of the given piece type -/// assuming an empty board. +// attacks_bb(Square) returns the pseudo attacks of the given piece type +// assuming an empty board. template inline Bitboard attacks_bb(Square s) { @@ -221,9 +221,9 @@ inline Bitboard attacks_bb(Square s) { } -/// attacks_bb(Square, Bitboard) returns the attacks by the given piece -/// assuming the board is occupied according to the passed Bitboard. -/// Sliding piece attacks do not continue passed an occupied square. +// attacks_bb(Square, Bitboard) returns the attacks by the given piece +// assuming the board is occupied according to the passed Bitboard. +// Sliding piece attacks do not continue passed an occupied square. template inline Bitboard attacks_bb(Square s, Bitboard occupied) { @@ -253,7 +253,7 @@ inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) { } -/// popcount() counts the number of non-zero bits in a bitboard +// popcount() counts the number of non-zero bits in a bitboard inline int popcount(Bitboard b) { @@ -274,7 +274,7 @@ inline int popcount(Bitboard b) { } -/// lsb() and msb() return the least/most significant bit in a non-zero bitboard +// lsb() and msb() return the least/most significant bit in a non-zero bitboard #if defined(__GNUC__) // GCC, Clang, ICX @@ -342,15 +342,15 @@ inline Square msb(Bitboard b) { #endif -/// least_significant_square_bb() returns the bitboard of the least significant -/// square of a non-zero bitboard. It is equivalent to square_bb(lsb(bb)). +// least_significant_square_bb() returns the bitboard of the least significant +// square of a non-zero bitboard. It is equivalent to square_bb(lsb(bb)). inline Bitboard least_significant_square_bb(Bitboard b) { assert(b); return b & -b; } -/// pop_lsb() finds and clears the least significant bit in a non-zero bitboard +// pop_lsb() finds and clears the least significant bit in a non-zero bitboard inline Square pop_lsb(Bitboard& b) { assert(b); diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 208e3ed5ba4..3eb7ee850a3 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -57,13 +57,13 @@ namespace Eval { std::string currentEvalFileName = "None"; - /// NNUE::init() tries to load a NNUE network at startup time, or when the engine - /// receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue" - /// The name of the NNUE network is always retrieved from the EvalFile option. - /// We search the given network in three locations: internally (the default - /// network may be embedded in the binary), in the active working directory and - /// in the engine directory. Distro packagers may define the DEFAULT_NNUE_DIRECTORY - /// variable to have the engine search in a special directory in their distro. + // NNUE::init() tries to load a NNUE network at startup time, or when the engine + // receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue" + // The name of the NNUE network is always retrieved from the EvalFile option. + // We search the given network in three locations: internally (the default + // network may be embedded in the binary), in the active working directory and + // in the engine directory. Distro packagers may define the DEFAULT_NNUE_DIRECTORY + // variable to have the engine search in a special directory in their distro. void NNUE::init() { @@ -105,7 +105,7 @@ namespace Eval { } } - /// NNUE::verify() verifies that the last net used was loaded successfully + // NNUE::verify() verifies that the last net used was loaded successfully void NNUE::verify() { std::string eval_file = std::string(Options["EvalFile"]); @@ -135,9 +135,9 @@ namespace Eval { } -/// simple_eval() returns a static, purely materialistic evaluation of the position -/// from the point of view of the given color. It can be divided by PawnValue to get -/// an approximation of the material advantage on the board in terms of pawns. +// simple_eval() returns a static, purely materialistic evaluation of the position +// from the point of view of the given color. It can be divided by PawnValue to get +// an approximation of the material advantage on the board in terms of pawns. Value Eval::simple_eval(const Position& pos, Color c) { return PawnValue * (pos.count(c) - pos.count(~c)) @@ -145,8 +145,8 @@ Value Eval::simple_eval(const Position& pos, Color c) { } -/// evaluate() is the evaluator for the outer world. It returns a static evaluation -/// of the position from the point of view of the side to move. +// evaluate() is the evaluator for the outer world. It returns a static evaluation +// of the position from the point of view of the side to move. Value Eval::evaluate(const Position& pos) { @@ -189,10 +189,10 @@ Value Eval::evaluate(const Position& pos) { return v; } -/// trace() is like evaluate(), but instead of returning a value, it returns -/// a string (suitable for outputting to stdout) that contains the detailed -/// descriptions and values of each evaluation term. Useful for debugging. -/// Trace scores are from white's point of view +// trace() is like evaluate(), but instead of returning a value, it returns +// a string (suitable for outputting to stdout) that contains the detailed +// descriptions and values of each evaluation term. Useful for debugging. +// Trace scores are from white's point of view std::string Eval::trace(Position& pos) { diff --git a/src/misc.cpp b/src/misc.cpp index 2f6ffd28e70..5abdaf07a31 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -71,14 +71,14 @@ namespace Stockfish { namespace { -/// Version number or dev. +// Version number or dev. constexpr std::string_view version = "dev"; -/// Our fancy logging facility. The trick here is to replace cin.rdbuf() and -/// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We -/// can toggle the logging of std::cout and std:cin at runtime whilst preserving -/// usual I/O functionality, all without changing a single line of code! -/// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81 +// Our fancy logging facility. The trick here is to replace cin.rdbuf() and +// cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We +// can toggle the logging of std::cout and std:cin at runtime whilst preserving +// usual I/O functionality, all without changing a single line of code! +// Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81 struct Tie: public std::streambuf { // MSVC requires split streambuf for cin and cout @@ -141,15 +141,15 @@ class Logger { } // namespace -/// engine_info() returns the full name of the current Stockfish version. -/// For local dev compiles we try to append the commit sha and commit date -/// from git if that fails only the local compilation date is set and "nogit" is specified: -/// Stockfish dev-YYYYMMDD-SHA -/// or -/// Stockfish dev-YYYYMMDD-nogit -/// -/// For releases (non dev builds) we only include the version number: -/// Stockfish version +// engine_info() returns the full name of the current Stockfish version. +// For local dev compiles we try to append the commit sha and commit date +// from git if that fails only the local compilation date is set and "nogit" is specified: +// Stockfish dev-YYYYMMDD-SHA +// or +// Stockfish dev-YYYYMMDD-nogit +// +// For releases (non-dev builds) we only include the version number: +// Stockfish version std::string engine_info(bool to_uci) { std::stringstream ss; @@ -185,20 +185,20 @@ std::string engine_info(bool to_uci) { } -/// compiler_info() returns a string trying to describe the compiler we use +// compiler_info() returns a string trying to describe the compiler we use std::string compiler_info() { #define make_version_string(major, minor, patch) stringify(major) "." stringify(minor) "." stringify(patch) -/// Predefined macros hell: -/// -/// __GNUC__ Compiler is GCC, Clang or ICX -/// __clang__ Compiler is Clang or ICX -/// __INTEL_LLVM_COMPILER Compiler is ICX -/// _MSC_VER Compiler is MSVC -/// _WIN32 Building on Windows (any) -/// _WIN64 Building on Windows 64 bit +// Predefined macros hell: +// +// __GNUC__ Compiler is GCC, Clang or ICX +// __clang__ Compiler is Clang or ICX +// __INTEL_LLVM_COMPILER Compiler is ICX +// _MSC_VER Compiler is MSVC +// _WIN32 Building on Windows (any) +// _WIN64 Building on Windows 64 bit std::string compiler = "\nCompiled by : "; @@ -305,7 +305,7 @@ std::string compiler_info() { } -/// Debug functions used mainly to collect run-time statistics +// Debug functions used mainly to collect run-time statistics constexpr int MaxDebugSlots = 32; namespace { @@ -397,8 +397,8 @@ void dbg_print() { } -/// Used to serialize access to std::cout to avoid multiple threads writing at -/// the same time. +// Used to serialize access to std::cout to avoid multiple threads writing at +// the same time. std::ostream& operator<<(std::ostream& os, SyncCout sc) { @@ -414,13 +414,13 @@ std::ostream& operator<<(std::ostream& os, SyncCout sc) { } -/// Trampoline helper to avoid moving Logger to misc.h +// Trampoline helper to avoid moving Logger to misc.h void start_logger(const std::string& fname) { Logger::start(fname); } -/// prefetch() preloads the given address in L1/L2 cache. This is a non-blocking -/// function that doesn't stall the CPU waiting for data to be loaded from memory, -/// which can be quite slow. +// prefetch() preloads the given address in L1/L2 cache. This is a non-blocking +// function that doesn't stall the CPU waiting for data to be loaded from memory, +// which can be quite slow. #ifdef NO_PREFETCH void prefetch(void*) {} @@ -439,9 +439,9 @@ void prefetch(void* addr) { #endif -/// std_aligned_alloc() is our wrapper for systems where the c++17 implementation -/// does not guarantee the availability of aligned_alloc(). Memory allocated with -/// std_aligned_alloc() must be freed with std_aligned_free(). +// std_aligned_alloc() is our wrapper for systems where the c++17 implementation +// does not guarantee the availability of aligned_alloc(). Memory allocated with +// std_aligned_alloc() must be freed with std_aligned_free(). void* std_aligned_alloc(size_t alignment, size_t size) { @@ -470,7 +470,7 @@ void std_aligned_free(void* ptr) { #endif } -/// aligned_large_pages_alloc() will return suitably aligned memory, if possible using large pages. +// aligned_large_pages_alloc() will return suitably aligned memory, if possible using large pages. #if defined(_WIN32) @@ -550,7 +550,7 @@ void* aligned_large_pages_alloc(size_t allocSize) { // Try to allocate large pages void* mem = aligned_large_pages_alloc_windows(allocSize); - // Fall back to regular, page aligned, allocation if necessary + // Fall back to regular, page-aligned, allocation if necessary if (!mem) mem = VirtualAlloc(nullptr, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); @@ -579,7 +579,7 @@ void* aligned_large_pages_alloc(size_t allocSize) { #endif -/// aligned_large_pages_free() will free the previously allocated ttmem +// aligned_large_pages_free() will free the previously allocated ttmem #if defined(_WIN32) @@ -612,9 +612,9 @@ void bindThisThread(size_t) {} #else -/// best_node() retrieves logical processor information using Windows specific -/// API and returns the best node id for the thread with index idx. Original -/// code from Texel by Peter Österlund. +// best_node() retrieves logical processor information using Windows specific +// API and returns the best node id for the thread with index idx. Original +// code from Texel by Peter Österlund. static int best_node(size_t idx) { @@ -666,8 +666,8 @@ static int best_node(size_t idx) { std::vector groups; - // Run as many threads as possible on the same node until core limit is - // reached, then move on filling the next node. + // Run as many threads as possible on the same node until the core limit is + // reached, then move on to filling the next node. for (int n = 0; n < nodes; n++) for (int i = 0; i < cores / nodes; i++) groups.push_back(n); @@ -684,7 +684,7 @@ static int best_node(size_t idx) { } -/// bindThisThread() set the group affinity of the current thread +// bindThisThread() sets the group affinity of the current thread void bindThisThread(size_t idx) { @@ -751,7 +751,7 @@ void init([[maybe_unused]] int argc, char* argv[]) { pathSeparator = "\\"; #ifdef _MSC_VER // Under windows argv[0] may not have the extension. Also _get_pgmptr() had - // issues in some windows 10 versions, so check returned values carefully. + // issues in some Windows 10 versions, so check returned values carefully. char* pgmptr = nullptr; if (!_get_pgmptr(&pgmptr) && pgmptr != nullptr && *pgmptr) argv0 = pgmptr; diff --git a/src/misc.h b/src/misc.h index 52595fb906b..60602048b9c 100644 --- a/src/misc.h +++ b/src/misc.h @@ -74,7 +74,7 @@ T* align_ptr_up(T* ptr) } -// IsLittleEndian : true if and only if the binary is compiled on a little endian machine +// IsLittleEndian : true if and only if the binary is compiled on a little-endian machine static inline const union { uint32_t i; char c[4]; } Le = { 0x01020304 }; static inline const bool IsLittleEndian = (Le.c[0] == 4); @@ -95,20 +95,20 @@ class ValueList { }; -/// xorshift64star Pseudo-Random Number Generator -/// This class is based on original code written and dedicated -/// to the public domain by Sebastiano Vigna (2014). -/// It has the following characteristics: -/// -/// - Outputs 64-bit numbers -/// - Passes Dieharder and SmallCrush test batteries -/// - Does not require warm-up, no zeroland to escape -/// - Internal state is a single 64-bit integer -/// - Period is 2^64 - 1 -/// - Speed: 1.60 ns/call (Core i7 @3.40GHz) -/// -/// For further analysis see -/// +// xorshift64star Pseudo-Random Number Generator +// This class is based on original code written and dedicated +// to the public domain by Sebastiano Vigna (2014). +// It has the following characteristics: +// +// - Outputs 64-bit numbers +// - Passes Dieharder and SmallCrush test batteries +// - Does not require warm-up, no zeroland to escape +// - Internal state is a single 64-bit integer +// - Period is 2^64 - 1 +// - Speed: 1.60 ns/call (Core i7 @3.40GHz) +// +// For further analysis see +// class PRNG { @@ -125,8 +125,8 @@ class PRNG { template T rand() { return T(rand64()); } - /// Special generator used to fast init magic numbers. - /// Output values only have 1/8th of their bits set on average. + // Special generator used to fast init magic numbers. + // Output values only have 1/8th of their bits set on average. template T sparse_rand() { return T(rand64() & rand64() & rand64()); } }; @@ -145,11 +145,11 @@ inline uint64_t mul_hi64(uint64_t a, uint64_t b) { #endif } -/// Under Windows it is not possible for a process to run on more than one -/// logical processor group. This usually means to be limited to use max 64 -/// cores. To overcome this, some special platform specific API should be -/// called to set group affinity for each thread. Original code from Texel by -/// Peter Österlund. +// Under Windows it is not possible for a process to run on more than one +// logical processor group. This usually means being limited to using max 64 +// cores. To overcome this, some special platform-specific API should be +// called to set group affinity for each thread. Original code from Texel by +// Peter Österlund. namespace WinProcGroup { void bindThisThread(size_t idx); diff --git a/src/movegen.cpp b/src/movegen.cpp index cda43b3a582..82ad60613c2 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -234,14 +234,14 @@ namespace { } // namespace -/// Generates all pseudo-legal captures plus queen promotions -/// Generates all pseudo-legal non-captures and underpromotions -/// Generates all pseudo-legal check evasions -/// Generates all pseudo-legal captures and non-captures -/// Generates all pseudo-legal non-captures giving check, -/// except castling and promotions -/// -/// Returns a pointer to the end of the move list. +// Generates all pseudo-legal captures plus queen promotions +// Generates all pseudo-legal non-captures and underpromotions +// Generates all pseudo-legal check evasions +// Generates all pseudo-legal captures and non-captures +// Generates all pseudo-legal non-captures giving check, +// except castling and promotions +// +// Returns a pointer to the end of the move list. template ExtMove* generate(const Position& pos, ExtMove* moveList) { @@ -263,7 +263,7 @@ template ExtMove* generate(const Position&, ExtMove*); template ExtMove* generate(const Position&, ExtMove*); -/// generate generates all the legal moves in the given position +// generate generates all the legal moves in the given position template<> ExtMove* generate(const Position& pos, ExtMove* moveList) { diff --git a/src/movegen.h b/src/movegen.h index 5eee2f1acf8..e913a13ea13 100644 --- a/src/movegen.h +++ b/src/movegen.h @@ -56,9 +56,9 @@ inline bool operator<(const ExtMove& f, const ExtMove& s) { template ExtMove* generate(const Position& pos, ExtMove* moveList); -/// The MoveList struct wraps the generate() function and returns a convenient -/// list of moves. Using MoveList is sometimes preferable to directly calling -/// the lower level generate() function. +// The MoveList struct wraps the generate() function and returns a convenient +// list of moves. Using MoveList is sometimes preferable to directly calling +// the lower level generate() function. template struct MoveList { diff --git a/src/movepick.cpp b/src/movepick.cpp index bc3fcf7ed6b..5bb0fd6c274 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -55,13 +55,13 @@ namespace { } // namespace -/// Constructors of the MovePicker class. As arguments, we pass information -/// to help it return the (presumably) good moves first, to decide which -/// moves to return (in the quiescence search, for instance, we only want to -/// search captures, promotions, and some checks) and how important a good -/// move ordering is at the current node. +// Constructors of the MovePicker class. As arguments, we pass information +// to help it return the (presumably) good moves first, to decide which +// moves to return (in the quiescence search, for instance, we only want to +// search captures, promotions, and some checks) and how important a good +// move ordering is at the current node. -/// MovePicker constructor for the main search +// MovePicker constructor for the main search MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const CapturePieceToHistory* cph, const PieceToHistory** ch, @@ -76,7 +76,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist !(ttm && pos.pseudo_legal(ttm)); } -/// MovePicker constructor for quiescence search +// MovePicker constructor for quiescence search MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, const CapturePieceToHistory* cph, const PieceToHistory** ch, @@ -90,8 +90,8 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHist && pos.pseudo_legal(ttm)); } -/// MovePicker constructor for ProbCut: we generate captures with SEE greater -/// than or equal to the given threshold. +// MovePicker constructor for ProbCut: we generate captures with SEE greater +// than or equal to the given threshold. MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph) : pos(p), captureHistory(cph), ttMove(ttm), threshold(th) { @@ -102,9 +102,9 @@ MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePiece && pos.see_ge(ttm, threshold)); } -/// MovePicker::score() assigns a numerical value to each move in a list, used -/// for sorting. Captures are ordered by Most Valuable Victim (MVV), preferring -/// captures with a good history. Quiets moves are ordered using the history tables. +// MovePicker::score() assigns a numerical value to each move in a list, used +// for sorting. Captures are ordered by Most Valuable Victim (MVV), preferring +// captures with a good history. Quiets moves are ordered using the history tables. template void MovePicker::score() { @@ -180,8 +180,8 @@ void MovePicker::score() { } } -/// MovePicker::select() returns the next move satisfying a predicate function. -/// It never returns the TT move. +// MovePicker::select() returns the next move satisfying a predicate function. +// It never returns the TT move. template Move MovePicker::select(Pred filter) { @@ -198,9 +198,9 @@ Move MovePicker::select(Pred filter) { return MOVE_NONE; } -/// MovePicker::next_move() is the most important method of the MovePicker class. It -/// returns a new pseudo-legal move every time it is called until there are no more -/// moves left, picking the move with the highest score from a list of generated moves. +// MovePicker::next_move() is the most important method of the MovePicker class. It +// returns a new pseudo-legal move every time it is called until there are no more +// moves left, picking the move with the highest score from a list of generated moves. Move MovePicker::next_move(bool skipQuiets) { top: diff --git a/src/movepick.h b/src/movepick.h index dd9de0b2161..652ef16166d 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -32,10 +32,10 @@ namespace Stockfish { class Position; -/// StatsEntry stores the stat table value. It is usually a number but could -/// be a move or even a nested history. We use a class instead of naked value -/// to directly call history update operator<<() on the entry so to use stats -/// tables at caller sites as simple multi-dim arrays. +// StatsEntry stores the stat table value. It is usually a number but could +// be a move or even a nested history. We use a class instead of a naked value +// to directly call history update operator<<() on the entry so to use stats +// tables at caller sites as simple multi-dim arrays. template class StatsEntry { @@ -57,11 +57,11 @@ class StatsEntry { } }; -/// Stats is a generic N-dimensional array used to store various statistics. -/// The first template parameter T is the base type of the array, the second -/// template parameter D limits the range of updates in [-D, D] when we update -/// values with the << operator, while the last parameters (Size and Sizes) -/// encode the dimensions of the array. +// Stats is a generic N-dimensional array used to store various statistics. +// The first template parameter T is the base type of the array, and the second +// template parameter D limits the range of updates in [-D, D] when we update +// values with the << operator, while the last parameters (Size and Sizes) +// encode the dimensions of the array. template struct Stats : public std::array, Size> { @@ -69,7 +69,7 @@ struct Stats : public std::array, Size> void fill(const T& v) { - // For standard-layout 'this' points to first struct member + // For standard-layout 'this' points to the first struct member assert(std::is_standard_layout_v); using entry = StatsEntry; @@ -81,40 +81,40 @@ struct Stats : public std::array, Size> template struct Stats : public std::array, Size> {}; -/// In stats table, D=0 means that the template parameter is not used +// 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 -/// ordering decisions. It uses 2 tables (one for each color) indexed by -/// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards -/// (~11 elo) +// ButterflyHistory records how often quiet moves have been successful or +// unsuccessful during the current search, and is used for reduction and move +// ordering decisions. It uses 2 tables (one for each color) indexed by +// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards +// (~11 elo) using ButterflyHistory = Stats; -/// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous -/// move, see www.chessprogramming.org/Countermove_Heuristic +// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous +// move, see www.chessprogramming.org/Countermove_Heuristic using CounterMoveHistory = Stats; -/// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type] +// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type] using CapturePieceToHistory = Stats; -/// PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to] +// PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to] using PieceToHistory = Stats; -/// ContinuationHistory is the combined history of a given pair of moves, usually -/// the current one given a previous one. The nested history table is based on -/// PieceToHistory instead of ButterflyBoards. -/// (~63 elo) +// ContinuationHistory is the combined history of a given pair of moves, usually +// the current one given a previous one. The nested history table is based on +// PieceToHistory instead of ButterflyBoards. +// (~63 elo) using ContinuationHistory = Stats; -/// MovePicker class is used to pick one pseudo-legal move at a time from the -/// current position. The most important method is next_move(), which returns a -/// new pseudo-legal move each time it is called, until there are no moves left, -/// when MOVE_NONE is returned. In order to improve the efficiency of the -/// alpha-beta algorithm, MovePicker attempts to return the moves which are most -/// likely to get a cut-off first. +// MovePicker class is used to pick one pseudo-legal move at a time from the +// current position. The most important method is next_move(), which returns a +// new pseudo-legal move each time it is called, until there are no moves left, +// when MOVE_NONE is returned. In order to improve the efficiency of the +// alpha-beta algorithm, MovePicker attempts to return the moves which are most +// likely to get a cut-off first. class MovePicker { enum PickType { Next, Best }; diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index e1fa3b814a1..1f821cf9a3b 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -375,7 +375,7 @@ namespace Stockfish::Eval::NNUE { return write_parameters(stream); } - /// Save eval, to a file given by its name + // Save eval, to a file given by its name bool save_eval(const std::optional& filename) { std::string actualFilename; diff --git a/src/position.cpp b/src/position.cpp index 0d7d957141f..ada371eb951 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -61,7 +61,7 @@ constexpr Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING } // namespace -/// operator<<(Position) returns an ASCII representation of the position +// operator<<(Position) returns an ASCII representation of the position std::ostream& operator<<(std::ostream& os, const Position& pos) { @@ -116,7 +116,7 @@ Key cuckoo[8192]; Move cuckooMove[8192]; -/// Position::init() initializes at startup the various arrays used to compute hash keys +// Position::init() initializes at startup the various arrays used to compute hash keys void Position::init() { @@ -160,9 +160,9 @@ void Position::init() { } -/// Position::set() initializes the position object with the given FEN string. -/// This function is not very robust - make sure that input FENs are correct, -/// this is assumed to be the responsibility of the GUI. +// Position::set() initializes the position object with the given FEN string. +// This function is not very robust - make sure that input FENs are correct, +// this is assumed to be the responsibility of the GUI. Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Thread* th) { /* @@ -297,8 +297,8 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th } -/// Position::set_castling_right() is a helper function used to set castling -/// rights given the corresponding color and the rook starting square. +// Position::set_castling_right() is a helper function used to set castling +// rights given the corresponding color and the rook starting square. void Position::set_castling_right(Color c, Square rfrom) { @@ -318,7 +318,7 @@ void Position::set_castling_right(Color c, Square rfrom) { } -/// Position::set_check_info() sets king attacks to detect if a move gives check +// Position::set_check_info() sets king attacks to detect if a move gives check void Position::set_check_info() const { @@ -336,9 +336,9 @@ void Position::set_check_info() const { } -/// Position::set_state() computes the hash keys of the position, and other -/// data that once computed is updated incrementally as moves are made. -/// The function is only used when a new position is set up +// Position::set_state() computes the hash keys of the position, and other +// data that once computed is updated incrementally as moves are made. +// The function is only used when a new position is set up void Position::set_state() const { @@ -372,9 +372,9 @@ void Position::set_state() const { } -/// Position::set() is an overload to initialize the position object with -/// the given endgame code string like "KBPKN". It is mainly a helper to -/// get the material key out of an endgame code. +// Position::set() is an overload to initialize the position object with +// the given endgame code string like "KBPKN". It is mainly a helper to +// get the material key out of an endgame code. Position& Position::set(const string& code, Color c, StateInfo* si) { @@ -395,8 +395,8 @@ Position& Position::set(const string& code, Color c, StateInfo* si) { } -/// Position::fen() returns a FEN representation of the position. In case of -/// Chess960 the Shredder-FEN notation is used. This is mainly a debugging function. +// Position::fen() returns a FEN representation of the position. In case of +// Chess960 the Shredder-FEN notation is used. This is mainly a debugging function. string Position::fen() const { @@ -444,9 +444,9 @@ string Position::fen() const { return ss.str(); } -/// update_slider_blockers() calculates st->blockersForKing[c] and st->pinners[~c], -/// which store respectively the pieces preventing king of color c from being in check -/// and the slider pieces of color ~c pinning pieces of color c to the king. +// update_slider_blockers() calculates st->blockersForKing[c] and st->pinners[~c], +// which store respectively the pieces preventing king of color c from being in check +// and the slider pieces of color ~c pinning pieces of color c to the king. void Position::update_slider_blockers(Color c) const { Square ksq = square(c); @@ -474,8 +474,8 @@ void Position::update_slider_blockers(Color c) const { } -/// Position::attackers_to() computes a bitboard of all pieces which attack a -/// given square. Slider attacks use the occupied bitboard to indicate occupancy. +// Position::attackers_to() computes a bitboard of all pieces which attack a +// given square. Slider attacks use the occupied bitboard to indicate occupancy. Bitboard Position::attackers_to(Square s, Bitboard occupied) const { @@ -488,7 +488,7 @@ Bitboard Position::attackers_to(Square s, Bitboard occupied) const { } -/// Position::legal() tests whether a pseudo-legal move is legal +// Position::legal() tests whether a pseudo-legal move is legal bool Position::legal(Move m) const { @@ -532,7 +532,7 @@ bool Position::legal(Move m) const { if (attackers_to(s) & pieces(~us)) return false; - // In case of Chess960, verify if the Rook blocks some checks + // In case of Chess960, verify if the Rook blocks some checks. // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1. return !chess960 || !(blockers_for_king(us) & to_sq(m)); } @@ -549,9 +549,9 @@ bool Position::legal(Move m) const { } -/// Position::pseudo_legal() takes a random move and tests whether the move is -/// pseudo-legal. It is used to validate moves from TT that can be corrupted -/// due to SMP concurrent access or hash position key aliasing. +// Position::pseudo_legal() takes a random move and tests whether the move is +// pseudo-legal. It is used to validate moves from TT that can be corrupted +// due to SMP concurrent access or hash position key aliasing. bool Position::pseudo_legal(const Move m) const { @@ -622,7 +622,7 @@ bool Position::pseudo_legal(const Move m) const { } -/// Position::gives_check() tests whether a pseudo-legal move gives a check +// Position::gives_check() tests whether a pseudo-legal move gives a check bool Position::gives_check(Move m) const { @@ -672,9 +672,9 @@ bool Position::gives_check(Move m) const { } -/// Position::do_move() makes a move, and saves all information necessary -/// to a StateInfo object. The move is assumed to be legal. Pseudo-legal -/// moves should be filtered out before this function is called. +// Position::do_move() makes a move, and saves all information necessary +// to a StateInfo object. The move is assumed to be legal. Pseudo-legal +// moves should be filtered out before this function is called. void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { @@ -870,8 +870,8 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { } -/// Position::undo_move() unmakes a move. When it returns, the position should -/// be restored to exactly the same state as before the move was made. +// Position::undo_move() unmakes a move. When it returns, the position should +// be restored to exactly the same state as before the move was made. void Position::undo_move(Move m) { @@ -934,8 +934,8 @@ void Position::undo_move(Move m) { } -/// Position::do_castling() is a helper used to do/undo a castling move. This -/// is a bit tricky in Chess960 where from/to squares can overlap. +// Position::do_castling() is a helper used to do/undo a castling move. This +// is a bit tricky in Chess960 where from/to squares can overlap. template void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto) { @@ -965,8 +965,8 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ } -/// Position::do_null_move() is used to do a "null move": it flips -/// the side to move without executing any move on the board. +// Position::do_null_move() is used to do a "null move": it flips +// the side to move without executing any move on the board. void Position::do_null_move(StateInfo& newSt) { @@ -1005,7 +1005,7 @@ void Position::do_null_move(StateInfo& newSt) { } -/// Position::undo_null_move() must be used to undo a "null move" +// Position::undo_null_move() must be used to undo a "null move" void Position::undo_null_move() { @@ -1016,9 +1016,9 @@ void Position::undo_null_move() { } -/// Position::key_after() computes the new hash key after the given move. Needed -/// for speculative prefetch. It doesn't recognize special moves like castling, -/// en passant and promotions. +// Position::key_after() computes the new hash key after the given move. Needed +// for speculative prefetch. It doesn't recognize special moves like castling, +// en passant and promotions. Key Position::key_after(Move m) const { @@ -1038,9 +1038,9 @@ Key Position::key_after(Move m) const { } -/// Position::see_ge (Static Exchange Evaluation Greater or Equal) tests if the -/// SEE value of move is greater or equal to the given threshold. We'll use an -/// algorithm similar to alpha-beta pruning with a null window. +// Position::see_ge (Static Exchange Evaluation Greater or Equal) tests if the +// SEE value of move is greater or equal to the given threshold. We'll use an +// algorithm similar to alpha-beta pruning with a null window. bool Position::see_ge(Move m, Value threshold) const { @@ -1143,8 +1143,8 @@ bool Position::see_ge(Move m, Value threshold) const { return bool(res); } -/// Position::is_draw() tests whether the position is drawn by 50-move rule -/// or by repetition. It does not detect stalemates. +// Position::is_draw() tests whether the position is drawn by 50-move rule +// or by repetition. It does not detect stalemates. bool Position::is_draw(int ply) const { @@ -1175,8 +1175,8 @@ bool Position::has_repeated() const { } -/// Position::has_game_cycle() tests if the position has a move which draws by repetition, -/// or an earlier position has a move that directly reaches the current position. +// Position::has_game_cycle() tests if the position has a move which draws by repetition, +// or an earlier position has a move that directly reaches the current position. bool Position::has_game_cycle(int ply) const { @@ -1224,8 +1224,8 @@ bool Position::has_game_cycle(int ply) const { } -/// Position::flip() flips position with the white and black sides reversed. This -/// is only useful for debugging e.g. for finding evaluation symmetry bugs. +// Position::flip() flips position with the white and black sides reversed. This +// is only useful for debugging e.g. for finding evaluation symmetry bugs. void Position::flip() { @@ -1259,9 +1259,9 @@ void Position::flip() { } -/// Position::pos_is_ok() performs some consistency checks for the -/// position object and raise an assert if something wrong is detected. -/// This is meant to be helpful when debugging. +// Position::pos_is_ok() performs some consistency checks for the +// position object and raise an assert if something wrong is detected. +// This is meant to be helpful when debugging. bool Position::pos_is_ok() const { diff --git a/src/position.h b/src/position.h index aae4db94a9c..23fd5bf5688 100644 --- a/src/position.h +++ b/src/position.h @@ -31,9 +31,9 @@ namespace Stockfish { -/// StateInfo struct stores information needed to restore a Position object to -/// its previous state when we retract a move. Whenever a move is made on the -/// board (by calling Position::do_move), a StateInfo object must be passed. +// StateInfo struct stores information needed to restore a Position object to +// its previous state when we retract a move. Whenever a move is made on the +// board (by calling Position::do_move), a StateInfo object must be passed. struct StateInfo { @@ -61,17 +61,17 @@ struct StateInfo { }; -/// A list to keep track of the position states along the setup moves (from the -/// start position to the position just before the search starts). Needed by -/// 'draw by repetition' detection. Use a std::deque because pointers to -/// elements are not invalidated upon list resizing. +// A list to keep track of the position states along the setup moves (from the +// start position to the position just before the search starts). Needed by +// 'draw by repetition' detection. Use a std::deque because pointers to +// elements are not invalidated upon list resizing. using StateListPtr = std::unique_ptr>; -/// Position class stores information regarding the board representation as -/// pieces, side to move, hash keys, castling info, etc. Important methods are -/// do_move() and undo_move(), used by the search to update node info when -/// traversing the search tree. +// Position class stores information regarding the board representation as +// pieces, side to move, hash keys, castling info, etc. Important methods are +// do_move() and undo_move(), used by the search to update node info when +// traversing the search tree. class Thread; class Position { @@ -342,8 +342,8 @@ inline bool Position::capture(Move m) const { || type_of(m) == EN_PASSANT; } -// returns true if a move is generated from the capture stage -// having also queen promotions covered, i.e. consistency with the capture stage move generation +// Returns true if a move is generated from the capture stage, having also +// queen promotions covered, i.e. consistency with the capture stage move generation // is needed to avoid the generation of duplicate moves. inline bool Position::capture_stage(Move m) const { assert(is_ok(m)); diff --git a/src/search.cpp b/src/search.cpp index 2d4a3f3d35b..16c6b0f38f8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -169,7 +169,7 @@ namespace { } // namespace -/// Search::init() is called at startup to initialize various lookup tables +// Search::init() is called at startup to initialize various lookup tables void Search::init() { @@ -178,7 +178,7 @@ void Search::init() { } -/// Search::clear() resets search state to its initial value +// Search::clear() resets search state to its initial value void Search::clear() { @@ -191,8 +191,8 @@ void Search::clear() { } -/// MainThread::search() is started when the program receives the UCI 'go' -/// command. It searches from the root position and outputs the "bestmove". +// MainThread::search() is started when the program receives the UCI 'go' +// command. It searches from the root position and outputs the "bestmove". void MainThread::search() { @@ -268,9 +268,9 @@ void MainThread::search() { } -/// Thread::search() is the main iterative deepening loop. It calls search() -/// repeatedly with increasing depth until the allocated thinking time has been -/// consumed, the user stops the search, or the maximum search depth is reached. +// Thread::search() is the main iterative deepening loop. It calls search() +// repeatedly with increasing depth until the allocated thinking time has been +// consumed, the user stops the search, or the maximum search depth is reached. void Thread::search() { @@ -1837,8 +1837,8 @@ namespace { } // namespace -/// MainThread::check_time() is used to print debug info and, more importantly, -/// to detect when we are out of available time and thus stop the search. +// MainThread::check_time() is used to print debug info and, more importantly, +// to detect when we are out of available time and thus stop the search. void MainThread::check_time() { @@ -1870,8 +1870,8 @@ void MainThread::check_time() { } -/// UCI::pv() formats PV information according to the UCI protocol. UCI requires -/// that all (if any) unsearched PV lines are sent using a previous search score. +// UCI::pv() formats PV information according to the UCI protocol. UCI requires +// that all (if any) unsearched PV lines are sent using a previous search score. string UCI::pv(const Position& pos, Depth depth) { @@ -1929,10 +1929,10 @@ string UCI::pv(const Position& pos, Depth depth) { } -/// RootMove::extract_ponder_from_tt() is called in case we have no ponder move -/// before exiting the search, for instance, in case we stop the search during a -/// fail high at root. We try hard to have a ponder move to return to the GUI, -/// otherwise in case of 'ponder on' we have nothing to think about. +// RootMove::extract_ponder_from_tt() is called in case we have no ponder move +// before exiting the search, for instance, in case we stop the search during a +// fail high at root. We try hard to have a ponder move to return to the GUI, +// otherwise in case of 'ponder on' we have nothing to think about. bool RootMove::extract_ponder_from_tt(Position& pos) { diff --git a/src/search.h b/src/search.h index c6dbffce0c7..c434ba752d6 100644 --- a/src/search.h +++ b/src/search.h @@ -33,9 +33,9 @@ class Position; namespace Search { -/// Stack struct keeps track of the information we need to remember from nodes -/// shallower and deeper in the tree during the search. Each search thread has -/// its own array of Stack objects, indexed by the current ply. +// Stack struct keeps track of the information we need to remember from nodes +// shallower and deeper in the tree during the search. Each search thread has +// its own array of Stack objects, indexed by the current ply. struct Stack { Move* pv; @@ -55,9 +55,9 @@ struct Stack { }; -/// RootMove struct is used for moves at the root of the tree. For each root move -/// we store a score and a PV (really a refutation in the case of moves which -/// fail low). Score is normally set at -VALUE_INFINITE for all non-pv moves. +// RootMove struct is used for moves at the root of the tree. For each root move +// we store a score and a PV (really a refutation in the case of moves which +// fail low). Score is normally set at -VALUE_INFINITE for all non-pv moves. struct RootMove { @@ -84,8 +84,8 @@ struct RootMove { using RootMoves = std::vector; -/// LimitsType struct stores information sent by GUI about available time to -/// search the current move, maximum depth/time, or if we are in analysis mode. +// LimitsType struct stores information sent by GUI about available time to +// search the current move, maximum depth/time, or if we are in analysis mode. struct LimitsType { diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index ffe29ce1626..4114db605d2 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -139,7 +139,7 @@ template int sign_of(T val) { return (T(0) < val) - (val < T(0)); } -// Numbers in little endian used by sparseIndex[] to point into blockLength[] +// Numbers in little-endian used by sparseIndex[] to point into blockLength[] struct SparseEntry { char block[4]; // Number of block char offset[2]; // Offset within the block @@ -153,7 +153,7 @@ struct LR { enum Side { Left, Right }; uint8_t lr[3]; // The first 12 bits is the left-hand symbol, the second 12 - // bits is the right-hand symbol. If symbol has length 1, + // bits is the right-hand symbol. If the symbol has length 1, // then the left-hand symbol is the stored value. template Sym get() { @@ -301,9 +301,9 @@ class TBFile : public std::ifstream { std::string TBFile::Paths; -// struct PairsData contains low level indexing information to access TB data. -// There are 8, 4 or 2 PairsData records for each TBTable, according to type of -// table and if positions have pawns or not. It is populated at first access. +// struct PairsData contains low-level indexing information to access TB data. +// There are 8, 4, or 2 PairsData records for each TBTable, according to the type +// of table and if positions have pawns or not. It is populated at first access. struct PairsData { uint8_t flags; // Table flags, see enum TBFlag uint8_t maxSymLen; // Maximum length in bits of the Huffman symbols @@ -379,7 +379,7 @@ TBTable::TBTable(const std::string& code) : TBTable() { hasUniquePieces = true; // Set the leading color. In case both sides have pawns the leading color - // is the side with less pawns because this leads to better compression. + // is the side with fewer pawns because this leads to better compression. bool c = !pos.count(BLACK) || ( pos.count(WHITE) && pos.count(BLACK) >= pos.count(WHITE)); @@ -404,7 +404,7 @@ TBTable::TBTable(const TBTable& wdl) : TBTable() { } // class TBTables creates and keeps ownership of the TBTable objects, one for -// each TB file found. It supports a fast, hash based, table lookup. Populated +// each TB file found. It supports a fast, hash-based, table lookup. Populated // at init time, accessed at probe time. class TBTables { @@ -511,9 +511,9 @@ void TBTables::add(const std::vector& pieces) { // mostly-draw or mostly-win tables this can leave many 64-byte blocks only half-filled, so // in such cases blocks are 32 bytes long. The blocks of DTZ tables are up to 1024 bytes long. // The generator picks the size that leads to the smallest table. The "book" of symbols and -// Huffman codes is the same for all blocks in the table. A non-symmetric pawnless TB file +// Huffman codes are the same for all blocks in the table. A non-symmetric pawnless TB file // will have one table for wtm and one for btm, a TB file with pawns will have tables per -// file a,b,c,d also in this case one set for wtm and one for btm. +// file a,b,c,d also, in this case, one set for wtm and one for btm. int decompress_pairs(PairsData* d, uint64_t idx) { // Special case where all table positions store the same value @@ -541,7 +541,7 @@ int decompress_pairs(PairsData* d, uint64_t idx) { uint32_t block = number(&d->sparseIndex[k].block); int offset = number(&d->sparseIndex[k].offset); - // Now compute the difference idx - I(k). From definition of k we know that + // Now compute the difference idx - I(k). From the definition of k, we know that // // idx = k * d->span + idx % d->span (2) // @@ -551,7 +551,7 @@ int decompress_pairs(PairsData* d, uint64_t idx) { // Sum the above to offset to find the offset corresponding to our idx offset += diff; - // Move to previous/next block, until we reach the correct block that contains idx, + // Move to the previous/next block, until we reach the correct block that contains idx, // that is when 0 <= offset <= d->blockLength[block] while (offset < 0) offset += d->blockLength[--block] + 1; @@ -564,7 +564,7 @@ int decompress_pairs(PairsData* d, uint64_t idx) { // Read the first 64 bits in our block, this is a (truncated) sequence of // unknown number of symbols of unknown length but we know the first one - // is at the beginning of this 64 bits sequence. + // is at the beginning of this 64-bit sequence. uint64_t buf64 = number(ptr); ptr += 2; int buf64Size = 64; Sym sym; @@ -587,8 +587,8 @@ int decompress_pairs(PairsData* d, uint64_t idx) { // Now add the value of the lowest symbol of length len to get our symbol sym += number(&d->lowestSym[len]); - // If our offset is within the number of values represented by symbol sym - // we are done... + // If our offset is within the number of values represented by symbol sym, + // we are done. if (offset < d->symlen[sym] + 1) break; @@ -604,7 +604,7 @@ int decompress_pairs(PairsData* d, uint64_t idx) { } } - // Ok, now we have our symbol that expands into d->symlen[sym] + 1 symbols. + // Now we have our symbol that expands into d->symlen[sym] + 1 symbols. // We binary-search for our value recursively expanding into the left and // right child symbols until we reach a leaf node where symlen[sym] + 1 == 1 // that will store the value we need. @@ -614,7 +614,7 @@ int decompress_pairs(PairsData* d, uint64_t idx) { // If a symbol contains 36 sub-symbols (d->symlen[sym] + 1 = 36) and // expands in a pair (d->symlen[left] = 23, d->symlen[right] = 11), then - // we know that, for instance the ten-th value (offset = 10) will be on + // we know that, for instance, the tenth value (offset = 10) will be on // the left side because in Recursive Pairing child symbols are adjacent. if (offset < d->symlen[left] + 1) sym = left; @@ -639,7 +639,7 @@ bool check_dtz_stm(TBTable* entry, int stm, File f) { // DTZ scores are sorted by frequency of occurrence and then assigned the // values 0, 1, 2, ... in order of decreasing frequency. This is done for each // of the four WDLScore values. The mapping information necessary to reconstruct -// the original values is stored in the TB file and read during map[] init. +// the original values are stored in the TB file and read during map[] init. WDLScore map_score(TBTable*, File, int value, WDLScore) { return WDLScore(value - 2); } int map_score(TBTable* entry, File f, int value, WDLScore wdl) { @@ -658,7 +658,7 @@ int map_score(TBTable* entry, File f, int value, WDLScore wdl) { } // DTZ tables store distance to zero in number of moves or plies. We - // want to return plies, so we have convert to plies when needed. + // want to return plies, so we have to convert to plies when needed. if ( (wdl == WDLWin && !(flags & TBFlag::WinPlies)) || (wdl == WDLLoss && !(flags & TBFlag::LossPlies)) || wdl == WDLCursedWin @@ -669,7 +669,7 @@ int map_score(TBTable* entry, File f, int value, WDLScore wdl) { } // Compute a unique index out of a position and use it to probe the TB file. To -// encode k pieces of same type and color, first sort the pieces by square in +// encode k pieces of the same type and color, first sort the pieces by square in // ascending order s1 <= s2 <= ... <= sk then compute the unique index as: // // idx = Binomial[1][s1] + Binomial[2][s2] + ... + Binomial[k][sk] @@ -687,13 +687,13 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu // A given TB entry like KRK has associated two material keys: KRvk and Kvkr. // If both sides have the same pieces keys are equal. In this case TB tables - // only store the 'white to move' case, so if the position to lookup has black + // only stores the 'white to move' case, so if the position to lookup has black // to move, we need to switch the color and flip the squares before to lookup. bool symmetricBlackToMove = (entry->key == entry->key2 && pos.side_to_move()); - // TB files are calculated for white as stronger side. For instance we have - // KRvK, not KvKR. A position where stronger side is white will have its - // material key == entry->key, otherwise we have to switch the color and + // TB files are calculated for white as the stronger side. For instance, we + // have KRvK, not KvKR. A position where the stronger side is white will have + // its material key == entry->key, otherwise we have to switch the color and // flip the squares before to lookup. bool blackStronger = (pos.material_key() != entry->key); @@ -816,7 +816,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu // Rs "together" in 62 * 61 / 2 ways (we divide by 2 because rooks can be // swapped and still get the same position.) // - // In case we have at least 3 unique pieces (included kings) we encode them + // In case we have at least 3 unique pieces (including kings) we encode them // together. if (entry->hasUniquePieces) { @@ -861,7 +861,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu idx *= d->groupIdx[0]; Square* groupSq = squares + d->groupLen[0]; - // Encode remaining pawns then pieces according to square, in ascending order + // Encode remaining pawns and then pieces according to square, in ascending order bool remainingPawns = entry->hasPawns && entry->pawnCount[1]; while (d->groupLen[++next]) @@ -870,7 +870,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu uint64_t n = 0; // Map down a square if "comes later" than a square in the previous - // groups (similar to what done earlier for leading group pieces). + // groups (similar to what was done earlier for leading group pieces). for (int i = 0; i < d->groupLen[next]; ++i) { auto f = [&](Square s) { return groupSq[i] > s; }; @@ -888,7 +888,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu } // Group together pieces that will be encoded together. The general rule is that -// a group contains pieces of same type and color. The exception is the leading +// a group contains pieces of the same type and color. The exception is the leading // group that, in case of positions without pawns, can be formed by 3 different // pieces (default) or by the king pair when there is not a unique piece apart // from the kings. When there are pawns, pawns are always first in pieces[]. @@ -953,7 +953,7 @@ void set_groups(T& e, PairsData* d, int order[], File f) { // In Recursive Pairing each symbol represents a pair of children symbols. So // read d->btree[] symbols data and expand each one in his left and right child -// symbol until reaching the leafs that represent the symbol value. +// symbol until reaching the leaves that represent the symbol value. uint8_t set_symlen(PairsData* d, Sym s, std::vector& visited) { visited[s] = true; // We can set it now because tree is acyclic @@ -1002,7 +1002,7 @@ uint8_t* set_sizes(PairsData* d, uint8_t* data) { // See https://en.wikipedia.org/wiki/Huffman_coding // The canonical code is ordered such that longer symbols (in terms of - // the number of bits of their Huffman code) have lower numeric value, + // the number of bits of their Huffman code) have a lower numeric value, // so that d->lowestSym[i] >= d->lowestSym[i+1] (when read as LittleEndian). // Starting from this we compute a base64[] table indexed by symbol length // and containing 64 bit values so that d->base64[i] >= d->base64[i+1]. @@ -1072,7 +1072,7 @@ uint8_t* set_dtz_map(TBTable& e, uint8_t* data, File maxFile) { return data += uintptr_t(data) & 1; // Word alignment } -// Populate entry's PairsData records with data from the just memory mapped file. +// Populate entry's PairsData records with data from the just memory-mapped file. // Called at first access. template void set(T& e, uint8_t* data) { @@ -1138,9 +1138,9 @@ void set(T& e, uint8_t* data) { } } -// If the TB file corresponding to the given position is already memory mapped -// then return its base address, otherwise try to memory map and init it. Called -// at every probe, memory map and init only at first access. Function is thread +// If the TB file corresponding to the given position is already memory-mapped +// then return its base address, otherwise, try to memory map and init it. Called +// at every probe, memory map, and init only at first access. Function is thread // safe and can be called concurrently. template void* mapped(TBTable& e, const Position& pos) { @@ -1191,7 +1191,7 @@ Ret probe_table(const Position& pos, ProbeState* result, WDLScore wdl = WDLDraw) } // For a position where the side to move has a winning capture it is not necessary -// to store a winning value so the generator treats such positions as "don't cares" +// to store a winning value so the generator treats such positions as "don't care" // and tries to assign to it a value that improves the compression ratio. Similarly, // if the side to move has a drawing capture, then the position is at least drawn. // If the position is won, then the TB needs to store a win value. But if the @@ -1200,7 +1200,7 @@ Ret probe_table(const Position& pos, ProbeState* result, WDLScore wdl = WDLDraw) // their results and must probe the position itself. The "best" result of these // probes is the correct result for the position. // DTZ tables do not store values when a following move is a zeroing winning move -// (winning capture or winning pawn move). Also DTZ store wrong values for positions +// (winning capture or winning pawn move). Also, DTZ store wrong values for positions // where the best move is an ep-move (even if losing). So in all these cases set // the state to ZEROING_BEST_MOVE. template @@ -1268,9 +1268,9 @@ WDLScore search(Position& pos, ProbeState* result) { } // namespace -/// Tablebases::init() is called at startup and after every change to -/// "SyzygyPath" UCI option to (re)create the various tables. It is not thread -/// safe, nor it needs to be. +// Tablebases::init() is called at startup and after every change to +// "SyzygyPath" UCI option to (re)create the various tables. It is not thread +// safe, nor it needs to be. void Tablebases::init(const std::string& paths) { TBTables.clear(); @@ -1302,7 +1302,7 @@ void Tablebases::init(const std::string& paths) { // MapKK[] encodes all the 462 possible legal positions of two kings where // the first is in the a1-d1-d4 triangle. If the first king is on the a1-d4 - // diagonal, the other one shall not to be above the a1-h8 diagonal. + // diagonal, the other one shall not be above the a1-h8 diagonal. std::vector> bothOnDiagonal; code = 0; for (int idx = 0; idx < 10; idx++) @@ -1323,7 +1323,7 @@ void Tablebases::init(const std::string& paths) { MapKK[idx][s2] = code++; } - // Legal positions with both kings on diagonal are encoded as last ones + // Legal positions with both kings on a diagonal are encoded as last ones for (auto p : bothOnDiagonal) MapKK[p.first][p.second] = code++; @@ -1338,8 +1338,8 @@ void Tablebases::init(const std::string& paths) { // MapPawns[s] encodes squares a2-h7 to 0..47. This is the number of possible // available squares when the leading one is in 's'. Moreover the pawn with - // highest MapPawns[] is the leading pawn, the one nearest the edge and, - // among pawns with same file, the one with lowest rank. + // highest MapPawns[] is the leading pawn, the one nearest the edge, and + // among pawns with the same file, the one with the lowest rank. int availableSquares = 47; // Available squares when lead pawn is in a2 // Init the tables for the encoding of leading pawns group: with 7-men TB we @@ -1463,7 +1463,7 @@ int Tablebases::probe_dtz(Position& pos, ProbeState* result) { if (*result == FAIL || wdl == WDLDraw) // DTZ tables don't store draws return 0; - // DTZ stores a 'don't care' value in this case, or even a plain wrong + // DTZ stores a 'don't care value in this case, or even a plain wrong // one as in case the best move is a losing ep, so it cannot be probed. if (*result == ZEROING_BEST_MOVE) return dtz_before_zeroing(wdl); @@ -1490,7 +1490,7 @@ int Tablebases::probe_dtz(Position& pos, ProbeState* result) { // For zeroing moves we want the dtz of the move _before_ doing it, // otherwise we will get the dtz of the next move sequence. Search the // position after the move to get the score sign (because even in a - // winning position we could make a losing capture or going for a draw). + // winning position we could make a losing capture or go for a draw). dtz = zeroing ? -dtz_before_zeroing(search(pos, result)) : -probe_dtz(pos, result); @@ -1548,10 +1548,9 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves) { } else if (pos.is_draw(1)) { - // In case a root move leads to a draw by repetition or - // 50-move rule, we set dtz to zero. Note: since we are - // only 1 ply from the root, this must be a true 3-fold - // repetition inside the game history. + // In case a root move leads to a draw by repetition or 50-move rule, + // we set dtz to zero. Note: since we are only 1 ply from the root, + // this must be a true 3-fold repetition inside the game history. dtz = 0; } else diff --git a/src/thread.cpp b/src/thread.cpp index 60f760ed46e..c752e7326cd 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -40,8 +40,8 @@ namespace Stockfish { ThreadPool Threads; // Global object -/// Thread constructor launches the thread and waits until it goes to sleep -/// in idle_loop(). Note that 'searching' and 'exit' should be already set. +// Thread constructor launches the thread and waits until it goes to sleep +// in idle_loop(). Note that 'searching' and 'exit' should be already set. Thread::Thread(size_t n) : idx(n), stdThread(&Thread::idle_loop, this) { @@ -49,8 +49,8 @@ Thread::Thread(size_t n) : idx(n), stdThread(&Thread::idle_loop, this) { } -/// Thread destructor wakes up the thread in idle_loop() and waits -/// for its termination. Thread should be already waiting. +// Thread destructor wakes up the thread in idle_loop() and waits +// for its termination. Thread should be already waiting. Thread::~Thread() { @@ -62,7 +62,7 @@ Thread::~Thread() { } -/// Thread::clear() reset histories, usually before a new game +// Thread::clear() reset histories, usually before a new game void Thread::clear() { @@ -78,7 +78,7 @@ void Thread::clear() { } -/// Thread::start_searching() wakes up the thread that will start the search +// Thread::start_searching() wakes up the thread that will start the search void Thread::start_searching() { mutex.lock(); @@ -88,8 +88,8 @@ void Thread::start_searching() { } -/// Thread::wait_for_search_finished() blocks on the condition variable -/// until the thread has finished searching. +// Thread::wait_for_search_finished() blocks on the condition variable +// until the thread has finished searching. void Thread::wait_for_search_finished() { @@ -98,15 +98,15 @@ void Thread::wait_for_search_finished() { } -/// Thread::idle_loop() is where the thread is parked, blocked on the -/// condition variable, when it has no work to do. +// Thread::idle_loop() is where the thread is parked, blocked on the +// condition variable, when it has no work to do. void Thread::idle_loop() { // If OS already scheduled us on a different group than 0 then don't overwrite // the choice, eventually we are one of many one-threaded processes running on // some Windows NUMA hardware, for instance in fishtest. To make it simple, - // just check if running threads are below a threshold, in this case all this + // just check if running threads are below a threshold, in this case, all this // NUMA machinery is not needed. if (Options["Threads"] > 8) WinProcGroup::bindThisThread(idx); @@ -127,9 +127,9 @@ void Thread::idle_loop() { } } -/// ThreadPool::set() creates/destroys threads to match the requested number. -/// Created and launched threads will immediately go to sleep in idle_loop. -/// Upon resizing, threads are recreated to allow for binding if necessary. +// ThreadPool::set() creates/destroys threads to match the requested number. +// Created and launched threads will immediately go to sleep in idle_loop. +// Upon resizing, threads are recreated to allow for binding if necessary. void ThreadPool::set(size_t requested) { @@ -158,7 +158,7 @@ void ThreadPool::set(size_t requested) { } -/// ThreadPool::clear() sets threadPool data to initial values +// ThreadPool::clear() sets threadPool data to initial values void ThreadPool::clear() { @@ -172,8 +172,8 @@ void ThreadPool::clear() { } -/// ThreadPool::start_thinking() wakes up main thread waiting in idle_loop() and -/// returns immediately. Main thread will wake up other threads and start the search. +// ThreadPool::start_thinking() wakes up main thread waiting in idle_loop() and +// returns immediately. Main thread will wake up other threads and start the search. void ThreadPool::start_thinking(Position& pos, StateListPtr& states, const Search::LimitsType& limits, bool ponderMode) { @@ -225,7 +225,7 @@ Thread* ThreadPool::get_best_thread() const { std::map votes; Value minScore = VALUE_NONE; - // Find minimum score of all threads + // Find the minimum score of all threads for (Thread* th: threads) minScore = std::min(minScore, th->rootMoves[0].score); @@ -256,7 +256,7 @@ Thread* ThreadPool::get_best_thread() const { } -/// Start non-main threads +// Start non-main threads void ThreadPool::start_searching() { @@ -266,7 +266,7 @@ void ThreadPool::start_searching() { } -/// Wait for non-main threads +// Wait for non-main threads void ThreadPool::wait_for_search_finished() const { diff --git a/src/thread.h b/src/thread.h index 8d0adcf0340..44cc56729e3 100644 --- a/src/thread.h +++ b/src/thread.h @@ -34,10 +34,10 @@ namespace Stockfish { -/// Thread class keeps together all the thread-related stuff. We use -/// per-thread pawn and material hash tables so that once we get a -/// pointer to an entry its life time is unlimited and we don't have -/// to care about someone changing the entry under our feet. +// Thread class keeps together all the thread-related stuff. We use +// per-thread pawn and material hash tables so that once we get a +// pointer to an entry its lifetime is unlimited and we don't have +// to care about someone changing the entry under our feet. class Thread { @@ -75,7 +75,7 @@ class Thread { }; -/// MainThread is a derived class specific for main thread +// MainThread is a derived class specific for main thread struct MainThread : public Thread { @@ -94,9 +94,9 @@ struct MainThread : public Thread { }; -/// ThreadPool struct handles all the threads-related stuff like init, starting, -/// parking and, most importantly, launching a thread. All the access to threads -/// is done through this class. +// ThreadPool struct handles all the threads-related stuff like init, starting, +// parking and, most importantly, launching a thread. All the access to threads +// is done through this class. struct ThreadPool { diff --git a/src/thread_win32_osx.h b/src/thread_win32_osx.h index 330a8341dd8..77352aa087a 100644 --- a/src/thread_win32_osx.h +++ b/src/thread_win32_osx.h @@ -21,11 +21,11 @@ #include -/// On OSX threads other than the main thread are created with a reduced stack -/// 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. +// On OSX threads other than the main thread are created with a reduced stack +// 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__) || defined(USE_PTHREADS) diff --git a/src/timeman.cpp b/src/timeman.cpp index 5e57f8f98c5..74f59d90574 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -29,14 +29,14 @@ namespace Stockfish { TimeManagement Time; // Our global time management object -/// TimeManagement::init() is called at the beginning of the search and calculates -/// the bounds of time allowed for the current game ply. We currently support: +// TimeManagement::init() is called at the beginning of the search and calculates +// the bounds of time allowed for the current game ply. We currently support: // 1) x basetime (+ z increment) // 2) x moves in y seconds (+ z increment) void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { - // if we have no time, no need to initialize TM, except for the start time, + // If we have no time, no need to initialize TM, except for the start time, // which is used by movetime. startTime = limits.startTime; if (limits.time[us] == 0) diff --git a/src/timeman.h b/src/timeman.h index 9ad6bdcccf9..6acdf0ac6db 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -28,8 +28,8 @@ namespace Stockfish { -/// The TimeManagement class computes the optimal time to think depending on -/// the maximum available time, the game move number and other parameters. +// The TimeManagement class computes the optimal time to think depending on +// the maximum available time, the game move number, and other parameters. class TimeManagement { public: diff --git a/src/tt.cpp b/src/tt.cpp index adcfe6289a0..c3aec8d3e7c 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -33,8 +33,8 @@ namespace Stockfish { TranspositionTable TT; // Our global transposition table -/// TTEntry::save() populates the TTEntry with a new node's data, possibly -/// overwriting an old position. Update is not atomic and can be racy. +// TTEntry::save() populates the TTEntry with a new node's data, possibly +// overwriting an old position. The update is not atomic and can be racy. void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) { @@ -59,9 +59,9 @@ void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) } -/// TranspositionTable::resize() sets the size of the transposition table, -/// measured in megabytes. Transposition table consists of a power of 2 number -/// of clusters and each cluster consists of ClusterSize number of TTEntry. +// TranspositionTable::resize() sets the size of the transposition table, +// measured in megabytes. Transposition table consists of a power of 2 number +// of clusters and each cluster consists of ClusterSize number of TTEntry. void TranspositionTable::resize(size_t mbSize) { @@ -83,7 +83,7 @@ void TranspositionTable::resize(size_t mbSize) { } -/// TranspositionTable::clear() initializes the entire transposition table to zero, +// TranspositionTable::clear() initializes the entire transposition table to zero, // in a multi-threaded way. void TranspositionTable::clear() { @@ -113,12 +113,12 @@ void TranspositionTable::clear() { } -/// TranspositionTable::probe() looks up the current position in the transposition -/// table. It returns true and a pointer to the TTEntry if the position is found. -/// Otherwise, it returns false and a pointer to an empty or least valuable TTEntry -/// to be replaced later. The replace value of an entry is calculated as its depth -/// minus 8 times its relative age. TTEntry t1 is considered more valuable than -/// TTEntry t2 if its replace value is greater than that of t2. +// TranspositionTable::probe() looks up the current position in the transposition +// table. It returns true and a pointer to the TTEntry if the position is found. +// Otherwise, it returns false and a pointer to an empty or least valuable TTEntry +// to be replaced later. The replace value of an entry is calculated as its depth +// minus 8 times its relative age. TTEntry t1 is considered more valuable than +// TTEntry t2 if its replace value is greater than that of t2. TTEntry* TranspositionTable::probe(const Key key, bool& found) const { @@ -149,8 +149,8 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const { } -/// TranspositionTable::hashfull() returns an approximation of the hashtable -/// occupation during a search. The hash is x permill full, as per UCI protocol. +// TranspositionTable::hashfull() returns an approximation of the hashtable +// occupation during a search. The hash is x permill full, as per UCI protocol. int TranspositionTable::hashfull() const { diff --git a/src/tt.h b/src/tt.h index c11cf085220..fdea4933507 100644 --- a/src/tt.h +++ b/src/tt.h @@ -27,16 +27,16 @@ namespace Stockfish { -/// TTEntry struct is the 10 bytes transposition table entry, defined as below: -/// -/// key 16 bit -/// depth 8 bit -/// generation 5 bit -/// pv node 1 bit -/// bound type 2 bit -/// move 16 bit -/// value 16 bit -/// eval value 16 bit +// TTEntry struct is the 10 bytes transposition table entry, defined as below: +// +// key 16 bit +// depth 8 bit +// generation 5 bit +// pv node 1 bit +// bound type 2 bit +// move 16 bit +// value 16 bit +// eval value 16 bit struct TTEntry { @@ -60,11 +60,11 @@ struct TTEntry { }; -/// A TranspositionTable is an array of Cluster, of size clusterCount. Each -/// cluster consists of ClusterSize number of TTEntry. Each non-empty TTEntry -/// contains information on exactly one position. The size of a Cluster should -/// divide the size of a cache line for best performance, as the cacheline is -/// prefetched when possible. +// A TranspositionTable is an array of Cluster, of size clusterCount. Each +// cluster consists of ClusterSize number of TTEntry. Each non-empty TTEntry +// contains information on exactly one position. The size of a Cluster should +// divide the size of a cache line for best performance, as the cacheline is +// prefetched when possible. class TranspositionTable { diff --git a/src/tune.h b/src/tune.h index dde03b324ea..a9a7331e566 100644 --- a/src/tune.h +++ b/src/tune.h @@ -49,31 +49,30 @@ struct SetRange { #define SetDefaultRange SetRange(default_range) -/// Tune class implements the 'magic' code that makes the setup of a fishtest -/// tuning session as easy as it can be. Mainly you have just to remove const -/// qualifiers from the variables you want to tune and flag them for tuning, so -/// if you have: -/// -/// const Value myValue[][2] = { { V(100), V(20) }, { V(7), V(78) } }; -/// -/// If you have a my_post_update() function to run after values have been updated, -/// and a my_range() function to set custom Option's min-max values, then you just -/// remove the 'const' qualifiers and write somewhere below in the file: -/// -/// TUNE(SetRange(my_range), myValue, my_post_update); -/// -/// You can also set the range directly, and restore the default at the end -/// -/// TUNE(SetRange(-100, 100), myValue, SetDefaultRange); -/// -/// In case update function is slow and you have many parameters, you can add: -/// -/// UPDATE_ON_LAST(); -/// -/// And the values update, including post update function call, will be done only -/// once, after the engine receives the last UCI option, that is the one defined -/// and created as the last one, so the GUI should send the options in the same -/// order in which have been defined. +// Tune class implements the 'magic' code that makes the setup of a fishtest tuning +// session as easy as it can be. Mainly you have just to remove const qualifiers +// from the variables you want to tune and flag them for tuning, so if you have: +// +// const Value myValue[][2] = { { V(100), V(20) }, { V(7), V(78) } }; +// +// If you have a my_post_update() function to run after values have been updated, +// and a my_range() function to set custom Option's min-max values, then you just +// remove the 'const' qualifiers and write somewhere below in the file: +// +// TUNE(SetRange(my_range), myValue, my_post_update); +// +// You can also set the range directly, and restore the default at the end +// +// TUNE(SetRange(-100, 100), myValue, SetDefaultRange); +// +// In case update function is slow and you have many parameters, you can add: +// +// UPDATE_ON_LAST(); +// +// And the values update, including post update function call, will be done only +// once, after the engine receives the last UCI option, that is the one defined +// and created as the last one, so the GUI should send the options in the same +// order in which have been defined. class Tune { @@ -151,7 +150,7 @@ class Tune { static bool update_on_last; }; -// Some macro magic :-) we define a dummy int variable that compiler initializes calling Tune::add() +// Some macro magic :-) we define a dummy int variable that the compiler initializes calling Tune::add() #define STRINGIFY(x) #x #define UNIQUE2(x, y) x ## y #define UNIQUE(x, y) UNIQUE2(x, y) // Two indirection levels to expand __LINE__ diff --git a/src/types.h b/src/types.h index f682e764f17..1fc4d33a95f 100644 --- a/src/types.h +++ b/src/types.h @@ -19,22 +19,22 @@ #ifndef TYPES_H_INCLUDED #define TYPES_H_INCLUDED -/// When compiling with provided Makefile (e.g. for Linux and OSX), configuration -/// is done automatically. To get started type 'make help'. -/// -/// When Makefile is not used (e.g. with Microsoft Visual Studio) some switches -/// need to be set manually: -/// -/// -DNDEBUG | Disable debugging mode. Always use this for release. -/// -/// -DNO_PREFETCH | Disable use of prefetch asm-instruction. You may need this to -/// | run on some very old machines. -/// -/// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction. Works -/// | only in 64-bit mode and requires hardware with popcnt support. -/// -/// -DUSE_PEXT | Add runtime support for use of pext asm-instruction. Works -/// | only in 64-bit mode and requires hardware with pext support. +// When compiling with provided Makefile (e.g. for Linux and OSX), configuration +// is done automatically. To get started type 'make help'. +// +// When Makefile is not used (e.g. with Microsoft Visual Studio) some switches +// need to be set manually: +// +// -DNDEBUG | Disable debugging mode. Always use this for release. +// +// -DNO_PREFETCH | Disable use of prefetch asm-instruction. You may need this to +// | run on some very old machines. +// +// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction. Works +// | only in 64-bit mode and requires hardware with popcnt support. +// +// -DUSE_PEXT | Add runtime support for use of pext asm-instruction. Works +// | only in 64-bit mode and requires hardware with pext support. #include #include @@ -46,14 +46,14 @@ #pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false' #endif -/// Predefined macros hell: -/// -/// __GNUC__ Compiler is GCC, Clang or ICX -/// __clang__ Compiler is Clang or ICX -/// __INTEL_LLVM_COMPILER Compiler is ICX -/// _MSC_VER Compiler is MSVC -/// _WIN32 Building on Windows (any) -/// _WIN64 Building on Windows 64 bit +// Predefined macros hell: +// +// __GNUC__ Compiler is GCC, Clang or ICX +// __clang__ Compiler is Clang or ICX +// __INTEL_LLVM_COMPILER Compiler is ICX +// _MSC_VER Compiler is MSVC +// _WIN32 Building on Windows (any) +// _WIN64 Building on Windows 64 bit #if defined(__GNUC__ ) && (__GNUC__ < 9 || (__GNUC__ == 9 && __GNUC_MINOR__ <= 2)) && defined(_WIN32) && !defined(__clang__) #define ALIGNAS_ON_STACK_VARIABLES_BROKEN @@ -107,17 +107,17 @@ using Bitboard = uint64_t; constexpr int MAX_MOVES = 256; constexpr int MAX_PLY = 246; -/// A move needs 16 bits to be stored -/// -/// bit 0- 5: destination square (from 0 to 63) -/// bit 6-11: origin square (from 0 to 63) -/// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2) -/// bit 14-15: special move flag: promotion (1), en passant (2), castling (3) -/// NOTE: en passant bit is set only when a pawn can be captured -/// -/// Special cases are MOVE_NONE and MOVE_NULL. We can sneak these in because in -/// any normal move destination square is always different from origin square -/// while MOVE_NONE and MOVE_NULL have the same origin and destination square. +// A move needs 16 bits to be stored +// +// bit 0- 5: destination square (from 0 to 63) +// bit 6-11: origin square (from 0 to 63) +// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2) +// bit 14-15: special move flag: promotion (1), en passant (2), castling (3) +// NOTE: en passant bit is set only when a pawn can be captured +// +// Special cases are MOVE_NONE and MOVE_NULL. We can sneak these in because in +// any normal move destination square is always different from origin square +// while MOVE_NONE and MOVE_NULL have the same origin and destination square. enum Move : int { MOVE_NONE, @@ -291,7 +291,7 @@ ENABLE_INCR_OPERATORS_ON(Rank) #undef ENABLE_INCR_OPERATORS_ON #undef ENABLE_BASE_OPERATORS_ON -/// Additional operators to add a Direction to a Square +// Additional operators to add a Direction to a Square constexpr Square operator+(Square s, Direction d) { return Square(int(s) + int(d)); } constexpr Square operator-(Square s, Direction d) { return Square(int(s) - int(d)); } inline Square& operator+=(Square& s, Direction d) { return s = s + d; } @@ -405,7 +405,7 @@ constexpr Move make(Square from, Square to, PieceType pt = KNIGHT) { return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to); } -/// Based on a congruential pseudo-random number generator +// Based on a congruential pseudo-random number generator constexpr Key make_key(uint64_t seed) { return seed * 6364136223846793005ULL + 1442695040888963407ULL; } diff --git a/src/uci.cpp b/src/uci.cpp index f62bb8bf4e0..81bf7aff768 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -233,11 +233,11 @@ namespace { } // namespace -/// UCI::loop() waits for a command from the stdin, parses it, and then calls the appropriate -/// function. It also intercepts an end-of-file (EOF) indication from the stdin to ensure a -/// graceful exit if the GUI dies unexpectedly. When called with some command-line arguments, -/// like running 'bench', the function returns immediately after the command is executed. -/// In addition to the UCI ones, some additional debug commands are also supported. +// UCI::loop() waits for a command from the stdin, parses it, and then calls the appropriate +// function. It also intercepts an end-of-file (EOF) indication from the stdin to ensure a +// graceful exit if the GUI dies unexpectedly. When called with some command-line arguments, +// like running 'bench', the function returns immediately after the command is executed. +// In addition to the UCI ones, some additional debug commands are also supported. void UCI::loop(int argc, char* argv[]) { @@ -310,18 +310,18 @@ void UCI::loop(int argc, char* argv[]) { } -/// Turns a Value to an integer centipawn number, -/// without treatment of mate and similar special scores. +// Turns a Value to an integer centipawn number, +// without treatment of mate and similar special scores. int UCI::to_cp(Value v) { return 100 * v / UCI::NormalizeToPawnValue; } -/// UCI::value() converts a Value to a string by adhering to the UCI protocol specification: -/// -/// cp The score from the engine's point of view in centipawns. -/// mate Mate in 'y' moves (not plies). If the engine is getting mated, -/// uses negative values for 'y'. +// UCI::value() converts a Value to a string by adhering to the UCI protocol specification: +// +// cp The score from the engine's point of view in centipawns. +// mate Mate in 'y' moves (not plies). If the engine is getting mated, +// uses negative values for 'y'. std::string UCI::value(Value v) { @@ -343,8 +343,8 @@ std::string UCI::value(Value v) { } -/// UCI::wdl() reports the win-draw-loss (WDL) statistics given an evaluation -/// and a game ply based on the data gathered for fishtest LTC games. +// UCI::wdl() reports the win-draw-loss (WDL) statistics given an evaluation +// and a game ply based on the data gathered for fishtest LTC games. std::string UCI::wdl(Value v, int ply) { @@ -359,17 +359,17 @@ std::string UCI::wdl(Value v, int ply) { } -/// UCI::square() converts a Square to a string in algebraic notation (g1, a7, etc.) +// UCI::square() converts a Square to a string in algebraic notation (g1, a7, etc.) std::string UCI::square(Square s) { return std::string{ char('a' + file_of(s)), char('1' + rank_of(s)) }; } -/// UCI::move() converts a Move to a string in coordinate notation (g1f3, a7a8q). -/// The only special case is castling where the e1g1 notation is printed in -/// standard chess mode and in e1h1 notation it is printed in Chess960 mode. -/// Internally, all castling moves are always encoded as 'king captures rook'. +// UCI::move() converts a Move to a string in coordinate notation (g1f3, a7a8q). +// The only special case is castling where the e1g1 notation is printed in +// standard chess mode and in e1h1 notation it is printed in Chess960 mode. +// Internally, all castling moves are always encoded as 'king captures rook'. std::string UCI::move(Move m, bool chess960) { @@ -394,8 +394,8 @@ std::string UCI::move(Move m, bool chess960) { } -/// UCI::to_move() converts a string representing a move in coordinate notation -/// (g1f3, a7a8q) to the corresponding legal Move, if any. +// UCI::to_move() converts a string representing a move in coordinate notation +// (g1f3, a7a8q) to the corresponding legal Move, if any. Move UCI::to_move(const Position& pos, std::string& str) { diff --git a/src/uci.h b/src/uci.h index 7ca97d5c6bc..048f8c1166e 100644 --- a/src/uci.h +++ b/src/uci.h @@ -41,15 +41,15 @@ const int NormalizeToPawnValue = 328; class Option; -/// Define a custom comparator, because the UCI options should be case-insensitive +// Define a custom comparator, because the UCI options should be case-insensitive struct CaseInsensitiveLess { bool operator() (const std::string&, const std::string&) const; }; -/// The options container is defined as a std::map +// The options container is defined as a std::map using OptionsMap = std::map; -/// The Option class implements each option as specified by the UCI protocol +// The Option class implements each option as specified by the UCI protocol class Option { using OnChange = void (*)(const Option&); diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 8d2c5c098ed..b822ccf936f 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -44,7 +44,7 @@ UCI::OptionsMap Options; // Global object namespace UCI { -/// 'On change' actions, triggered by an option's value change +// 'On change' actions, triggered by an option's value change static void on_clear_hash(const Option&) { Search::clear(); } static void on_hash_size(const Option& o) { TT.resize(size_t(o)); } static void on_logger(const Option& o) { start_logger(o); } @@ -52,7 +52,7 @@ static void on_threads(const Option& o) { Threads.set(size_t(o)); } static void on_tb_path(const Option& o) { Tablebases::init(o); } static void on_eval_file(const Option&) { Eval::NNUE::init(); } -/// Our case insensitive less() function as required by UCI protocol +// Our case insensitive less() function as required by UCI protocol bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const { return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), @@ -60,7 +60,7 @@ bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const } -/// UCI::init() initializes the UCI options to their hard-coded default values +// UCI::init() initializes the UCI options to their hard-coded default values void init(OptionsMap& o) { @@ -89,8 +89,8 @@ void init(OptionsMap& o) { } -/// operator<<() is used to print all the options default values in chronological -/// insertion order (the idx field) and in the format defined by the UCI protocol. +// operator<<() is used to print all the options default values in chronological +// insertion order (the idx field) and in the format defined by the UCI protocol. std::ostream& operator<<(std::ostream& os, const OptionsMap& om) { @@ -116,7 +116,7 @@ std::ostream& operator<<(std::ostream& os, const OptionsMap& om) { } -/// Option class constructors and conversion operators +// Option class constructors and conversion operators Option::Option(const char* v, OnChange f) : type("string"), min(0), max(0), on_change(f) { defaultValue = currentValue = v; } @@ -150,7 +150,7 @@ bool Option::operator==(const char* s) const { } -/// operator<<() inits options and assigns idx in the correct printing order +// operator<<() inits options and assigns idx in the correct printing order void Option::operator<<(const Option& o) { @@ -161,9 +161,9 @@ void Option::operator<<(const Option& o) { } -/// operator=() updates currentValue and triggers on_change() action. It's up to -/// the GUI to check for option's limits, but we could receive the new value -/// from the user by console window, so let's check the bounds anyway. +// operator=() updates currentValue and triggers on_change() action. It's up to +// the GUI to check for option's limits, but we could receive the new value +// from the user by console window, so let's check the bounds anyway. Option& Option::operator=(const string& v) { From 057046cc9a114e38d9f616fa58cf230e9b15b9a7 Mon Sep 17 00:00:00 2001 From: "Shahin M. Shahin" <41402573+peregrineshahin@users.noreply.github.com> Date: Sat, 14 Oct 2023 18:08:51 +0300 Subject: [PATCH 1226/1766] Cleanup qsearch continuation histories Only (ss-1) and (ss-2) are used in qsearch. closes https://github.com/official-stockfish/Stockfish/pull/4828 No functional change --- src/search.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 16c6b0f38f8..0ebf4e20276 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1506,9 +1506,7 @@ namespace { futilityBase = std::min(ss->staticEval, bestValue) + 200; } - const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, - (ss-3)->continuationHistory, (ss-4)->continuationHistory, - nullptr , (ss-6)->continuationHistory }; + const PieceToHistory* contHist[] = {(ss-1)->continuationHistory, (ss-2)->continuationHistory}; // Initialize a MovePicker object for the current position, and prepare // to search the moves. Because the depth is <= 0 here, only captures, From d3d0c69dc1baf03c93252da3583b1b746c5a7ab6 Mon Sep 17 00:00:00 2001 From: mstembera Date: Wed, 18 Oct 2023 19:25:09 -0700 Subject: [PATCH 1227/1766] Remove outdated Tile naming. cleanup variable naming after #4816 closes #4833 No functional change --- src/nnue/nnue_feature_transformer.h | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 25f686dacbc..9f02830a63e 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -395,9 +395,9 @@ namespace Stockfish::Eval::NNUE { { assert(states_to_update[0]); - auto accTileIn = reinterpret_cast( + auto accIn = reinterpret_cast( &st->accumulator.accumulation[Perspective][0]); - auto accTileOut = reinterpret_cast( + auto accOut = reinterpret_cast( &states_to_update[0]->accumulator.accumulation[Perspective][0]); const IndexType offsetR0 = HalfDimensions * removed[0][0]; @@ -408,7 +408,7 @@ namespace Stockfish::Eval::NNUE { if (removed[0].size() == 1) { for (IndexType k = 0; k < HalfDimensions * sizeof(std::int16_t) / sizeof(vec_t); ++k) - accTileOut[k] = vec_add_16(vec_sub_16(accTileIn[k], columnR0[k]), columnA[k]); + accOut[k] = vec_add_16(vec_sub_16(accIn[k], columnR0[k]), columnA[k]); } else { @@ -416,14 +416,14 @@ namespace Stockfish::Eval::NNUE { auto columnR1 = reinterpret_cast(&weights[offsetR1]); for (IndexType k = 0; k < HalfDimensions * sizeof(std::int16_t) / sizeof(vec_t); ++k) - accTileOut[k] = vec_sub_16( - vec_add_16(accTileIn[k], columnA[k]), - vec_add_16(columnR0[k], columnR1[k])); + accOut[k] = vec_sub_16( + vec_add_16(accIn[k], columnA[k]), + vec_add_16(columnR0[k], columnR1[k])); } - auto accTilePsqtIn = reinterpret_cast( + auto accPsqtIn = reinterpret_cast( &st->accumulator.psqtAccumulation[Perspective][0]); - auto accTilePsqtOut = reinterpret_cast( + auto accPsqtOut = reinterpret_cast( &states_to_update[0]->accumulator.psqtAccumulation[Perspective][0]); const IndexType offsetPsqtR0 = PSQTBuckets * removed[0][0]; @@ -434,8 +434,8 @@ namespace Stockfish::Eval::NNUE { if (removed[0].size() == 1) { for (std::size_t k = 0; k < PSQTBuckets * sizeof(std::int32_t) / sizeof(psqt_vec_t); ++k) - accTilePsqtOut[k] = vec_add_psqt_32(vec_sub_psqt_32( - accTilePsqtIn[k], columnPsqtR0[k]), columnPsqtA[k]); + accPsqtOut[k] = vec_add_psqt_32(vec_sub_psqt_32( + accPsqtIn[k], columnPsqtR0[k]), columnPsqtA[k]); } else { @@ -443,9 +443,9 @@ namespace Stockfish::Eval::NNUE { auto columnPsqtR1 = reinterpret_cast(&psqtWeights[offsetPsqtR1]); for (std::size_t k = 0; k < PSQTBuckets * sizeof(std::int32_t) / sizeof(psqt_vec_t); ++k) - accTilePsqtOut[k] = vec_sub_psqt_32( - vec_add_psqt_32(accTilePsqtIn[k], columnPsqtA[k]), - vec_add_psqt_32(columnPsqtR0[k], columnPsqtR1[k])); + accPsqtOut[k] = vec_sub_psqt_32( + vec_add_psqt_32(accPsqtIn[k], columnPsqtA[k]), + vec_add_psqt_32(columnPsqtR0[k], columnPsqtR1[k])); } } else From 90c18b0b500a5226717353a37a82cd026d71b616 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Thu, 19 Oct 2023 14:38:07 +0300 Subject: [PATCH 1228/1766] Removing history condition Removing the bad history condition from the skip futility pruning formula. Passed STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 142688 W: 36420 L: 36317 D: 69951 Ptnml(0-2): 481, 16653, 36970, 16762, 478 https://tests.stockfishchess.org/tests/view/65270a663125598fc7eb8c67 Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 435378 W: 110723 L: 110925 D: 213730 Ptnml(0-2): 278, 47251, 122788, 47139, 233 https://tests.stockfishchess.org/tests/view/6528595f3125598fc7eba8f5 closes https://github.com/official-stockfish/Stockfish/pull/4834 Bench: 1110579 --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 0ebf4e20276..6214fb5b39b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -786,8 +786,7 @@ namespace { && eval >= beta && eval < 29462 // smaller than TB wins && !( !ttCapture - && ttMove - && thisThread->mainHistory[us][from_to(ttMove)] < 989)) + && ttMove)) return eval; // Step 9. Null move search with verification search (~35 Elo) From e18619d07802882136b583a01e56791b61abede6 Mon Sep 17 00:00:00 2001 From: Taras Vuk <117687515+TarasVuk@users.noreply.github.com> Date: Thu, 19 Oct 2023 14:31:23 +0200 Subject: [PATCH 1229/1766] Subtract the margin from the value returned by ProbCut This patch subtracts the margin from the value returned by ProbCut. Passed STC: LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 46112 W: 11940 L: 11610 D: 22562 Ptnml(0-2): 131, 5318, 11842, 5620, 145 https://tests.stockfishchess.org/tests/view/652ea42ade6d262d08d329dd Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 86880 W: 22192 L: 21776 D: 42912 Ptnml(0-2): 43, 9213, 24510, 9633, 41 https://tests.stockfishchess.org/tests/view/652f70ffde6d262d08d33e8d closes https://github.com/official-stockfish/Stockfish/pull/4835 bench: 1135313 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 6214fb5b39b..baf819687c5 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -897,7 +897,7 @@ namespace { { // Save ProbCut data into transposition table tte->save(posKey, value_to_tt(value, ss->ply), ss->ttPv, BOUND_LOWER, depth - 3, move, ss->staticEval); - return value; + return value - (probCutBeta - beta); } } From 8366ec48ae6fc57dffad849b20844d5b07f963b4 Mon Sep 17 00:00:00 2001 From: mstembera Date: Fri, 20 Oct 2023 02:23:46 -0700 Subject: [PATCH 1230/1766] Scale down stat bonus STC https://tests.stockfishchess.org/tests/view/652eff58de6d262d08d33353 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 88224 W: 22618 L: 22228 D: 43378 Ptnml(0-2): 282, 10177, 22783, 10609, 261 LTC https://tests.stockfishchess.org/tests/view/652fd13bde6d262d08d3481a LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 143508 W: 36674 L: 36142 D: 70692 Ptnml(0-2): 92, 15240, 40534, 15820, 68 Reduces the stat bonus by 20%. Maybe future patches can tune the actual bonus calculations for different histories. closes https://github.com/official-stockfish/Stockfish/pull/4836 bench: 1185932 --- src/movepick.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/movepick.h b/src/movepick.h index 652ef16166d..457defa525a 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -51,7 +51,7 @@ class StatsEntry { assert(abs(bonus) <= D); // Ensure range is [-D, D] static_assert(D <= std::numeric_limits::max(), "D overflows T"); - entry += bonus - entry * abs(bonus) / D; + entry += (bonus * D - entry * abs(bonus)) / (D * 5 / 4); assert(abs(entry) <= D); } From 2d0237db3f0e596fb06e3ffbadba84dcc4e018f6 Mon Sep 17 00:00:00 2001 From: Disservin Date: Sat, 21 Oct 2023 11:40:56 +0200 Subject: [PATCH 1231/1766] add clang-format This introduces clang-format to enforce a consistent code style for Stockfish. Having a documented and consistent style across the code will make contributing easier for new developers, and will make larger changes to the codebase easier to make. To facilitate formatting, this PR includes a Makefile target (`make format`) to format the code, this requires clang-format (version 17 currently) to be installed locally. Installing clang-format is straightforward on most OS and distros (e.g. with https://apt.llvm.org/, brew install clang-format, etc), as this is part of quite commonly used suite of tools and compilers (llvm / clang). Additionally, a CI action is present that will verify if the code requires formatting, and comment on the PR as needed. Initially, correct formatting is not required, it will be done by maintainers as part of the merge or in later commits, but obviously this is encouraged. fixes https://github.com/official-stockfish/Stockfish/issues/3608 closes https://github.com/official-stockfish/Stockfish/pull/4790 Co-Authored-By: Joost VandeVondele --- .clang-format | 44 + .github/workflows/stockfish_format_check.yml | 51 + CONTRIBUTING.md | 5 +- src/Makefile | 17 + src/benchmark.cpp | 84 +- src/benchmark.h | 4 +- src/bitboard.cpp | 136 +- src/bitboard.h | 280 ++- src/evaluate.cpp | 172 +- src/evaluate.h | 30 +- src/main.cpp | 24 +- src/misc.cpp | 852 ++++--- src/misc.h | 106 +- src/movegen.cpp | 127 +- src/movegen.h | 53 +- src/movepick.cpp | 520 ++-- src/movepick.h | 143 +- src/nnue/evaluate_nnue.cpp | 358 +-- src/nnue/evaluate_nnue.h | 54 +- src/nnue/features/half_ka_v2_hm.cpp | 93 +- src/nnue/features/half_ka_v2_hm.h | 70 +- src/nnue/layers/affine_transform.h | 430 ++-- .../layers/affine_transform_sparse_input.h | 322 +-- src/nnue/layers/clipped_relu.h | 214 +- src/nnue/layers/simd.h | 312 ++- src/nnue/layers/sqr_clipped_relu.h | 103 +- src/nnue/nnue_accumulator.h | 10 +- src/nnue/nnue_architecture.h | 163 +- src/nnue/nnue_common.h | 451 ++-- src/nnue/nnue_feature_transformer.h | 1041 ++++---- src/position.cpp | 1829 +++++++------- src/position.h | 485 ++-- src/search.cpp | 2176 ++++++++--------- src/search.h | 92 +- src/syzygy/tbprobe.cpp | 613 ++--- src/syzygy/tbprobe.h | 30 +- src/thread.cpp | 237 +- src/thread.h | 150 +- src/thread_win32_osx.h | 45 +- src/timeman.cpp | 138 +- src/timeman.h | 31 +- src/tt.cpp | 156 +- src/tt.h | 106 +- src/tune.cpp | 93 +- src/tune.h | 191 +- src/types.h | 432 ++-- src/uci.cpp | 405 +-- src/uci.h | 52 +- src/ucioption.cpp | 190 +- 49 files changed, 6963 insertions(+), 6757 deletions(-) create mode 100644 .clang-format create mode 100644 .github/workflows/stockfish_format_check.yml diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000000..c71f0368ed5 --- /dev/null +++ b/.clang-format @@ -0,0 +1,44 @@ +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: Consecutive +AlignConsecutiveDeclarations: Consecutive +AlignEscapedNewlines: DontAlign +AlignOperands: AlignAfterOperator +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: false +AlwaysBreakTemplateDeclarations: Yes +BasedOnStyle: WebKit +BitFieldColonSpacing: After +BinPackParameters: false +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Custom +BraceWrapping: + AfterFunction: false + AfterClass: false + AfterControlStatement: true + BeforeElse: true +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: AfterColon +BreakStringLiterals: false +ColumnLimit: 100 +ContinuationIndentWidth: 2 +Cpp11BracedListStyle: true +IndentGotoLabels: false +IndentPPDirectives: BeforeHash +IndentWidth: 4 +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: None +PackConstructorInitializers: Never +ReflowComments: false +SortIncludes: false +SortUsingDeclarations: false +SpaceAfterCStyleCast: true +SpaceAfterTemplateKeyword: false +SpaceBeforeCaseColon: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeInheritanceColon: false +SpaceInEmptyBlock: false +SpacesBeforeTrailingComments: 2 diff --git a/.github/workflows/stockfish_format_check.yml b/.github/workflows/stockfish_format_check.yml new file mode 100644 index 00000000000..cb16b327871 --- /dev/null +++ b/.github/workflows/stockfish_format_check.yml @@ -0,0 +1,51 @@ +# This workflow will run clang-format and comment on the PR. +# Because of security reasons, it is crucial that this workflow +# executes no shell script nor runs make. +# Read this before editing: https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ + +name: Stockfish +on: + pull_request_target: + branches: + - 'master' + paths: + - '**.cpp' + - '**.h' +jobs: + Stockfish: + name: clang-format check + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Run clang-format style check + uses: jidicula/clang-format-action@f62da5e3d3a2d88ff364771d9d938773a618ab5e + id: clang-format + continue-on-error: true + with: + clang-format-version: '17' + exclude-regex: 'incbin' + + - name: Comment on PR + if: steps.clang-format.outcome == 'failure' + uses: thollander/actions-comment-pull-request@1d3973dc4b8e1399c0620d3f2b1aa5e795465308 + with: + message: | + clang-format 17 needs to be run on this PR. + If you do not have clang-format installed, the maintainer will run it when merging. + For the exact version please see https://packages.ubuntu.com/mantic/clang-format-17. + + _(execution **${{ github.run_id }}** / attempt **${{ github.run_attempt }}**)_ + comment_tag: execution + + - name: Comment on PR + if: steps.clang-format.outcome != 'failure' + uses: thollander/actions-comment-pull-request@1d3973dc4b8e1399c0620d3f2b1aa5e795465308 + with: + message: | + _(execution **${{ github.run_id }}** / attempt **${{ github.run_attempt }}**)_ + create_if_not_exists: false + comment_tag: execution + mode: delete diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7667a942325..9e72e1db6bc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -57,8 +57,9 @@ discussion._ ## Code Style -We do not have a strict code style. But it is best to stick to the existing -style of the file you are editing. +Changes to Stockfish C++ code should respect our coding style defined by +[.clang-format](.clang-format). You can format your changes by running +`make format`. This requires clang-format version 17 to be installed on your system. ## Community and Communication diff --git a/src/Makefile b/src/Makefile index 5b43c35fdd7..7b7ee41b654 100644 --- a/src/Makefile +++ b/src/Makefile @@ -57,6 +57,14 @@ SRCS = benchmark.cpp bitboard.cpp evaluate.cpp main.cpp \ search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \ nnue/evaluate_nnue.cpp nnue/features/half_ka_v2_hm.cpp +HEADERS = benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h \ + nnue/evaluate_nnue.h nnue/features/half_ka_v2_hm.h nnue/layers/affine_transform.h \ + nnue/layers/affine_transform_sparse_input.h nnue/layers/clipped_relu.h nnue/layers/simd.h \ + nnue/layers/sqr_clipped_relu.h nnue/nnue_accumulator.h nnue/nnue_architecture.h \ + nnue/nnue_common.h nnue/nnue_feature_transformer.h position.h \ + search.h syzygy/tbprobe.h thread.h thread_win32_osx.h timeman.h \ + tt.h tune.h types.h uci.h + OBJS = $(notdir $(SRCS:.cpp=.o)) VPATH = syzygy:nnue:nnue/features @@ -145,6 +153,12 @@ dotprod = no arm_version = 0 STRIP = strip +ifneq ($(shell command -v clang-format-17),) + CLANG-FORMAT = clang-format-17 +else + CLANG-FORMAT = clang-format +endif + ### 2.2 Architecture specific ifeq ($(findstring x86,$(ARCH)),x86) @@ -936,6 +950,9 @@ net: netvariables fi; \ fi; \ +format: + $(CLANG-FORMAT) -i $(SRCS) $(HEADERS) -style=file:../.clang-format + # default target default: help diff --git a/src/benchmark.cpp b/src/benchmark.cpp index d67e37f66ed..63598e750e8 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -27,6 +27,7 @@ namespace { +// clang-format off const std::vector Defaults = { "setoption name UCI_Chess960 value false", "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", @@ -90,8 +91,9 @@ const std::vector Defaults = { "nqbnrkrb/pppppppp/8/8/8/8/PPPPPPPP/NQBNRKRB w KQkq - 0 1", "setoption name UCI_Chess960 value false" }; +// clang-format on -} // namespace +} // namespace namespace Stockfish { @@ -109,56 +111,56 @@ namespace Stockfish { std::vector setup_bench(const Position& current, std::istream& is) { - std::vector fens, list; - std::string go, token; + std::vector fens, list; + std::string go, token; - // Assign default values to missing arguments - std::string ttSize = (is >> token) ? token : "16"; - std::string threads = (is >> token) ? token : "1"; - std::string limit = (is >> token) ? token : "13"; - std::string fenFile = (is >> token) ? token : "default"; - std::string limitType = (is >> token) ? token : "depth"; + // Assign default values to missing arguments + std::string ttSize = (is >> token) ? token : "16"; + std::string threads = (is >> token) ? token : "1"; + std::string limit = (is >> token) ? token : "13"; + std::string fenFile = (is >> token) ? token : "default"; + std::string limitType = (is >> token) ? token : "depth"; - go = limitType == "eval" ? "eval" : "go " + limitType + " " + limit; + go = limitType == "eval" ? "eval" : "go " + limitType + " " + limit; - if (fenFile == "default") - fens = Defaults; + if (fenFile == "default") + fens = Defaults; - else if (fenFile == "current") - fens.push_back(current.fen()); + else if (fenFile == "current") + fens.push_back(current.fen()); - else - { - std::string fen; - std::ifstream file(fenFile); + else + { + std::string fen; + std::ifstream file(fenFile); - if (!file.is_open()) - { - std::cerr << "Unable to open file " << fenFile << std::endl; - exit(EXIT_FAILURE); - } + if (!file.is_open()) + { + std::cerr << "Unable to open file " << fenFile << std::endl; + exit(EXIT_FAILURE); + } - while (getline(file, fen)) - if (!fen.empty()) - fens.push_back(fen); + while (getline(file, fen)) + if (!fen.empty()) + fens.push_back(fen); - file.close(); - } + file.close(); + } - list.emplace_back("setoption name Threads value " + threads); - list.emplace_back("setoption name Hash value " + ttSize); - list.emplace_back("ucinewgame"); + list.emplace_back("setoption name Threads value " + threads); + list.emplace_back("setoption name Hash value " + ttSize); + list.emplace_back("ucinewgame"); - for (const std::string& fen : fens) - if (fen.find("setoption") != std::string::npos) - list.emplace_back(fen); - else - { - list.emplace_back("position fen " + fen); - list.emplace_back(go); - } + for (const std::string& fen : fens) + if (fen.find("setoption") != std::string::npos) + list.emplace_back(fen); + else + { + list.emplace_back("position fen " + fen); + list.emplace_back(go); + } - return list; + return list; } -} // namespace Stockfish +} // namespace Stockfish \ No newline at end of file diff --git a/src/benchmark.h b/src/benchmark.h index 64acf833ac0..e6206d19349 100644 --- a/src/benchmark.h +++ b/src/benchmark.h @@ -29,6 +29,6 @@ class Position; std::vector setup_bench(const Position&, std::istream&); -} // namespace Stockfish +} // namespace Stockfish -#endif // #ifndef BENCHMARK_H_INCLUDED +#endif // #ifndef BENCHMARK_H_INCLUDED diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 89eeee611f8..fff7eba9e6f 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -39,10 +39,10 @@ Magic BishopMagics[SQUARE_NB]; namespace { - Bitboard RookTable[0x19000]; // To store rook attacks - Bitboard BishopTable[0x1480]; // To store bishop attacks +Bitboard RookTable[0x19000]; // To store rook attacks +Bitboard BishopTable[0x1480]; // To store bishop attacks - void init_magics(PieceType pt, Bitboard table[], Magic magics[]); +void init_magics(PieceType pt, Bitboard table[], Magic magics[]); } @@ -60,18 +60,18 @@ inline Bitboard safe_destination(Square s, int step) { std::string Bitboards::pretty(Bitboard b) { - std::string s = "+---+---+---+---+---+---+---+---+\n"; + std::string s = "+---+---+---+---+---+---+---+---+\n"; - for (Rank r = RANK_8; r >= RANK_1; --r) - { - for (File f = FILE_A; f <= FILE_H; ++f) - s += b & make_square(f, r) ? "| X " : "| "; + for (Rank r = RANK_8; r >= RANK_1; --r) + { + for (File f = FILE_A; f <= FILE_H; ++f) + s += b & make_square(f, r) ? "| X " : "| "; - s += "| " + std::to_string(1 + r) + "\n+---+---+---+---+---+---+---+---+\n"; - } - s += " a b c d e f g h\n"; + s += "| " + std::to_string(1 + r) + "\n+---+---+---+---+---+---+---+---+\n"; + } + s += " a b c d e f g h\n"; - return s; + return s; } @@ -80,49 +80,50 @@ std::string Bitboards::pretty(Bitboard b) { void Bitboards::init() { - for (unsigned i = 0; i < (1 << 16); ++i) - PopCnt16[i] = uint8_t(std::bitset<16>(i).count()); - - for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) - for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) - SquareDistance[s1][s2] = std::max(distance(s1, s2), distance(s1, s2)); - - init_magics(ROOK, RookTable, RookMagics); - init_magics(BISHOP, BishopTable, BishopMagics); - - for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) - { - PawnAttacks[WHITE][s1] = pawn_attacks_bb(square_bb(s1)); - PawnAttacks[BLACK][s1] = pawn_attacks_bb(square_bb(s1)); - - for (int step : {-9, -8, -7, -1, 1, 7, 8, 9} ) - PseudoAttacks[KING][s1] |= safe_destination(s1, step); - - for (int step : {-17, -15, -10, -6, 6, 10, 15, 17} ) - PseudoAttacks[KNIGHT][s1] |= safe_destination(s1, step); - - PseudoAttacks[QUEEN][s1] = PseudoAttacks[BISHOP][s1] = attacks_bb(s1, 0); - PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ ROOK][s1] = attacks_bb< ROOK>(s1, 0); - - for (PieceType pt : { BISHOP, ROOK }) - for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) - { - if (PseudoAttacks[pt][s1] & s2) - { - LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2; - BetweenBB[s1][s2] = (attacks_bb(pt, s1, square_bb(s2)) & attacks_bb(pt, s2, square_bb(s1))); - } - BetweenBB[s1][s2] |= s2; - } - } + for (unsigned i = 0; i < (1 << 16); ++i) + PopCnt16[i] = uint8_t(std::bitset<16>(i).count()); + + for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) + for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) + SquareDistance[s1][s2] = std::max(distance(s1, s2), distance(s1, s2)); + + init_magics(ROOK, RookTable, RookMagics); + init_magics(BISHOP, BishopTable, BishopMagics); + + for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) + { + PawnAttacks[WHITE][s1] = pawn_attacks_bb(square_bb(s1)); + PawnAttacks[BLACK][s1] = pawn_attacks_bb(square_bb(s1)); + + for (int step : {-9, -8, -7, -1, 1, 7, 8, 9}) + PseudoAttacks[KING][s1] |= safe_destination(s1, step); + + for (int step : {-17, -15, -10, -6, 6, 10, 15, 17}) + PseudoAttacks[KNIGHT][s1] |= safe_destination(s1, step); + + PseudoAttacks[QUEEN][s1] = PseudoAttacks[BISHOP][s1] = attacks_bb(s1, 0); + PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ROOK][s1] = attacks_bb(s1, 0); + + for (PieceType pt : {BISHOP, ROOK}) + for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) + { + if (PseudoAttacks[pt][s1] & s2) + { + LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2; + BetweenBB[s1][s2] = + (attacks_bb(pt, s1, square_bb(s2)) & attacks_bb(pt, s2, square_bb(s1))); + } + BetweenBB[s1][s2] |= s2; + } + } } namespace { - Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) { +Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) { - Bitboard attacks = 0; - Direction RookDirections[4] = {NORTH, SOUTH, EAST, WEST}; + Bitboard attacks = 0; + Direction RookDirections[4] = {NORTH, SOUTH, EAST, WEST}; Direction BishopDirections[4] = {NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST}; for (Direction d : (pt == ROOK ? RookDirections : BishopDirections)) @@ -133,22 +134,22 @@ namespace { } return attacks; - } +} - // init_magics() computes all rook and bishop attacks at startup. Magic - // bitboards are used to look up attacks of sliding pieces. As a reference see - // www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so - // called "fancy" approach. +// init_magics() computes all rook and bishop attacks at startup. Magic +// bitboards are used to look up attacks of sliding pieces. As a reference see +// www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so +// called "fancy" approach. - void init_magics(PieceType pt, Bitboard table[], Magic magics[]) { +void init_magics(PieceType pt, Bitboard table[], Magic magics[]) { // Optimal PRNG seeds to pick the correct magics in the shortest time - int seeds[][RANK_NB] = { { 8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020 }, - { 728, 10316, 55013, 32803, 12281, 15100, 16645, 255 } }; + int seeds[][RANK_NB] = {{8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020}, + {728, 10316, 55013, 32803, 12281, 15100, 16645, 255}}; Bitboard occupancy[4096], reference[4096], edges, b; - int epoch[4096] = {}, cnt = 0, size = 0; + int epoch[4096] = {}, cnt = 0, size = 0; for (Square s = SQ_A1; s <= SQ_H8; ++s) { @@ -161,8 +162,8 @@ namespace { // the number of 1s of the mask. Hence we deduce the size of the shift to // apply to the 64 or 32 bits word to get the index. Magic& m = magics[s]; - m.mask = sliding_attack(pt, s, 0) & ~edges; - m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask); + m.mask = sliding_attack(pt, s, 0) & ~edges; + m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask); // Set the offset for the attacks table of the square. We have individual // table sizes for each square with "Fancy Magic Bitboards". @@ -171,7 +172,8 @@ namespace { // Use Carry-Rippler trick to enumerate all subsets of masks[s] and // store the corresponding sliding attack bitboard in reference[]. b = size = 0; - do { + do + { occupancy[size] = b; reference[size] = sliding_attack(pt, s, b); @@ -189,9 +191,9 @@ namespace { // Find a magic for square 's' picking up an (almost) random number // until we find the one that passes the verification test. - for (int i = 0; i < size; ) + for (int i = 0; i < size;) { - for (m.magic = 0; popcount((m.magic * m.mask) >> 56) < 6; ) + for (m.magic = 0; popcount((m.magic * m.mask) >> 56) < 6;) m.magic = rng.sparse_rand(); // A good magic must map every possible occupancy to an index that @@ -206,7 +208,7 @@ namespace { if (epoch[idx] < cnt) { - epoch[idx] = cnt; + epoch[idx] = cnt; m.attacks[idx] = reference[i]; } else if (m.attacks[idx] != reference[i]) @@ -214,7 +216,7 @@ namespace { } } } - } +} } -} // namespace Stockfish +} // namespace Stockfish diff --git a/src/bitboard.h b/src/bitboard.h index 0908c957058..03a511361f4 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -32,10 +32,10 @@ namespace Stockfish { namespace Bitboards { -void init(); +void init(); std::string pretty(Bitboard b); -} // namespace Stockfish::Bitboards +} // namespace Stockfish::Bitboards constexpr Bitboard FileABB = 0x0101010101010101ULL; constexpr Bitboard FileBBB = FileABB << 1; @@ -66,85 +66,80 @@ extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; // Magic holds all magic bitboards relevant data for a single square struct Magic { - Bitboard mask; - Bitboard magic; - Bitboard* attacks; - unsigned shift; + Bitboard mask; + Bitboard magic; + Bitboard* attacks; + unsigned shift; - // Compute the attack's index using the 'magic bitboards' approach - unsigned index(Bitboard occupied) const { + // Compute the attack's index using the 'magic bitboards' approach + unsigned index(Bitboard occupied) const { - if (HasPext) - return unsigned(pext(occupied, mask)); + if (HasPext) + return unsigned(pext(occupied, mask)); - if (Is64Bit) - return unsigned(((occupied & mask) * magic) >> shift); + if (Is64Bit) + return unsigned(((occupied & mask) * magic) >> shift); - unsigned lo = unsigned(occupied) & unsigned(mask); - unsigned hi = unsigned(occupied >> 32) & unsigned(mask >> 32); - return (lo * unsigned(magic) ^ hi * unsigned(magic >> 32)) >> shift; - } + unsigned lo = unsigned(occupied) & unsigned(mask); + unsigned hi = unsigned(occupied >> 32) & unsigned(mask >> 32); + return (lo * unsigned(magic) ^ hi * unsigned(magic >> 32)) >> shift; + } }; extern Magic RookMagics[SQUARE_NB]; extern Magic BishopMagics[SQUARE_NB]; inline Bitboard square_bb(Square s) { - assert(is_ok(s)); - return (1ULL << s); + assert(is_ok(s)); + return (1ULL << s); } // Overloads of bitwise operators between a Bitboard and a Square for testing // whether a given bit is set in a bitboard, and for setting and clearing bits. -inline Bitboard operator&( Bitboard b, Square s) { return b & square_bb(s); } -inline Bitboard operator|( Bitboard b, Square s) { return b | square_bb(s); } -inline Bitboard operator^( Bitboard b, Square s) { return b ^ square_bb(s); } +inline Bitboard operator&(Bitboard b, Square s) { return b & square_bb(s); } +inline Bitboard operator|(Bitboard b, Square s) { return b | square_bb(s); } +inline Bitboard operator^(Bitboard b, Square s) { return b ^ square_bb(s); } inline Bitboard& operator|=(Bitboard& b, Square s) { return b |= square_bb(s); } inline Bitboard& operator^=(Bitboard& b, Square s) { return b ^= square_bb(s); } -inline Bitboard operator&(Square s, Bitboard b) { return b & s; } -inline Bitboard operator|(Square s, Bitboard b) { return b | s; } -inline Bitboard operator^(Square s, Bitboard b) { return b ^ s; } +inline Bitboard operator&(Square s, Bitboard b) { return b & s; } +inline Bitboard operator|(Square s, Bitboard b) { return b | s; } +inline Bitboard operator^(Square s, Bitboard b) { return b ^ s; } -inline Bitboard operator|(Square s1, Square s2) { return square_bb(s1) | s2; } +inline Bitboard operator|(Square s1, Square s2) { return square_bb(s1) | s2; } -constexpr bool more_than_one(Bitboard b) { - return b & (b - 1); -} +constexpr bool more_than_one(Bitboard b) { return b & (b - 1); } // rank_bb() and file_bb() return a bitboard representing all the squares on // the given file or rank. -constexpr Bitboard rank_bb(Rank r) { - return Rank1BB << (8 * r); -} +constexpr Bitboard rank_bb(Rank r) { return Rank1BB << (8 * r); } -constexpr Bitboard rank_bb(Square s) { - return rank_bb(rank_of(s)); -} +constexpr Bitboard rank_bb(Square s) { return rank_bb(rank_of(s)); } -constexpr Bitboard file_bb(File f) { - return FileABB << f; -} +constexpr Bitboard file_bb(File f) { return FileABB << f; } -constexpr Bitboard file_bb(Square s) { - return file_bb(file_of(s)); -} +constexpr Bitboard file_bb(Square s) { return file_bb(file_of(s)); } // shift() moves a bitboard one or two steps as specified by the direction D template constexpr Bitboard shift(Bitboard b) { - return D == NORTH ? b << 8 : D == SOUTH ? b >> 8 - : D == NORTH+NORTH? b <<16 : D == SOUTH+SOUTH? b >>16 - : D == EAST ? (b & ~FileHBB) << 1 : D == WEST ? (b & ~FileABB) >> 1 - : D == NORTH_EAST ? (b & ~FileHBB) << 9 : D == NORTH_WEST ? (b & ~FileABB) << 7 - : D == SOUTH_EAST ? (b & ~FileHBB) >> 7 : D == SOUTH_WEST ? (b & ~FileABB) >> 9 - : 0; + return D == NORTH ? b << 8 + : D == SOUTH ? b >> 8 + : D == NORTH + NORTH ? b << 16 + : D == SOUTH + SOUTH ? b >> 16 + : D == EAST ? (b & ~FileHBB) << 1 + : D == WEST ? (b & ~FileABB) >> 1 + : D == NORTH_EAST ? (b & ~FileHBB) << 9 + : D == NORTH_WEST ? (b & ~FileABB) << 7 + : D == SOUTH_EAST ? (b & ~FileHBB) >> 7 + : D == SOUTH_WEST ? (b & ~FileABB) >> 9 + : 0; } @@ -153,14 +148,14 @@ constexpr Bitboard shift(Bitboard b) { template constexpr Bitboard pawn_attacks_bb(Bitboard b) { - return C == WHITE ? shift(b) | shift(b) - : shift(b) | shift(b); + return C == WHITE ? shift(b) | shift(b) + : shift(b) | shift(b); } inline Bitboard pawn_attacks_bb(Color c, Square s) { - assert(is_ok(s)); - return PawnAttacks[c][s]; + assert(is_ok(s)); + return PawnAttacks[c][s]; } // line_bb() returns a bitboard representing an entire line (from board edge @@ -170,9 +165,9 @@ inline Bitboard pawn_attacks_bb(Color c, Square s) { inline Bitboard line_bb(Square s1, Square s2) { - assert(is_ok(s1) && is_ok(s2)); + assert(is_ok(s1) && is_ok(s2)); - return LineBB[s1][s2]; + return LineBB[s1][s2]; } @@ -186,26 +181,34 @@ inline Bitboard line_bb(Square s1, Square s2) { inline Bitboard between_bb(Square s1, Square s2) { - assert(is_ok(s1) && is_ok(s2)); + assert(is_ok(s1) && is_ok(s2)); - return BetweenBB[s1][s2]; + return BetweenBB[s1][s2]; } // aligned() returns true if the squares s1, s2 and s3 are aligned either on a // straight or on a diagonal line. -inline bool aligned(Square s1, Square s2, Square s3) { - return line_bb(s1, s2) & s3; -} +inline bool aligned(Square s1, Square s2, Square s3) { return line_bb(s1, s2) & s3; } // distance() functions return the distance between x and y, defined as the // number of steps for a king in x to reach y. -template inline int distance(Square x, Square y); -template<> inline int distance(Square x, Square y) { return std::abs(file_of(x) - file_of(y)); } -template<> inline int distance(Square x, Square y) { return std::abs(rank_of(x) - rank_of(y)); } -template<> inline int distance(Square x, Square y) { return SquareDistance[x][y]; } +template +inline int distance(Square x, Square y); +template<> +inline int distance(Square x, Square y) { + return std::abs(file_of(x) - file_of(y)); +} +template<> +inline int distance(Square x, Square y) { + return std::abs(rank_of(x) - rank_of(y)); +} +template<> +inline int distance(Square x, Square y) { + return SquareDistance[x][y]; +} inline int edge_distance(File f) { return std::min(f, File(FILE_H - f)); } @@ -215,9 +218,9 @@ inline int edge_distance(File f) { return std::min(f, File(FILE_H - f)); } template inline Bitboard attacks_bb(Square s) { - assert((Pt != PAWN) && (is_ok(s))); + assert((Pt != PAWN) && (is_ok(s))); - return PseudoAttacks[Pt][s]; + return PseudoAttacks[Pt][s]; } @@ -228,28 +231,36 @@ inline Bitboard attacks_bb(Square s) { template inline Bitboard attacks_bb(Square s, Bitboard occupied) { - assert((Pt != PAWN) && (is_ok(s))); - - switch (Pt) - { - case BISHOP: return BishopMagics[s].attacks[BishopMagics[s].index(occupied)]; - case ROOK : return RookMagics[s].attacks[ RookMagics[s].index(occupied)]; - case QUEEN : return attacks_bb(s, occupied) | attacks_bb(s, occupied); - default : return PseudoAttacks[Pt][s]; - } + assert((Pt != PAWN) && (is_ok(s))); + + switch (Pt) + { + case BISHOP : + return BishopMagics[s].attacks[BishopMagics[s].index(occupied)]; + case ROOK : + return RookMagics[s].attacks[RookMagics[s].index(occupied)]; + case QUEEN : + return attacks_bb(s, occupied) | attacks_bb(s, occupied); + default : + return PseudoAttacks[Pt][s]; + } } inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) { - assert((pt != PAWN) && (is_ok(s))); - - switch (pt) - { - case BISHOP: return attacks_bb(s, occupied); - case ROOK : return attacks_bb< ROOK>(s, occupied); - case QUEEN : return attacks_bb(s, occupied) | attacks_bb(s, occupied); - default : return PseudoAttacks[pt][s]; - } + assert((pt != PAWN) && (is_ok(s))); + + switch (pt) + { + case BISHOP : + return attacks_bb(s, occupied); + case ROOK : + return attacks_bb(s, occupied); + case QUEEN : + return attacks_bb(s, occupied) | attacks_bb(s, occupied); + default : + return PseudoAttacks[pt][s]; + } } @@ -259,16 +270,19 @@ inline int popcount(Bitboard b) { #ifndef USE_POPCNT - union { Bitboard bb; uint16_t u[4]; } v = { b }; - return PopCnt16[v.u[0]] + PopCnt16[v.u[1]] + PopCnt16[v.u[2]] + PopCnt16[v.u[3]]; + union { + Bitboard bb; + uint16_t u[4]; + } v = {b}; + return PopCnt16[v.u[0]] + PopCnt16[v.u[1]] + PopCnt16[v.u[2]] + PopCnt16[v.u[3]]; #elif defined(_MSC_VER) - return int(_mm_popcnt_u64(b)); + return int(_mm_popcnt_u64(b)); -#else // Assumed gcc or compatible compiler +#else // Assumed gcc or compatible compiler - return __builtin_popcountll(b); + return __builtin_popcountll(b); #endif } @@ -279,66 +293,72 @@ inline int popcount(Bitboard b) { #if defined(__GNUC__) // GCC, Clang, ICX inline Square lsb(Bitboard b) { - assert(b); - return Square(__builtin_ctzll(b)); + assert(b); + return Square(__builtin_ctzll(b)); } inline Square msb(Bitboard b) { - assert(b); - return Square(63 ^ __builtin_clzll(b)); + assert(b); + return Square(63 ^ __builtin_clzll(b)); } #elif defined(_MSC_VER) // MSVC -#ifdef _WIN64 // MSVC, WIN64 + #ifdef _WIN64 // MSVC, WIN64 inline Square lsb(Bitboard b) { - assert(b); - unsigned long idx; - _BitScanForward64(&idx, b); - return (Square) idx; + assert(b); + unsigned long idx; + _BitScanForward64(&idx, b); + return (Square) idx; } inline Square msb(Bitboard b) { - assert(b); - unsigned long idx; - _BitScanReverse64(&idx, b); - return (Square) idx; + assert(b); + unsigned long idx; + _BitScanReverse64(&idx, b); + return (Square) idx; } -#else // MSVC, WIN32 + #else // MSVC, WIN32 inline Square lsb(Bitboard b) { - assert(b); - unsigned long idx; - - if (b & 0xffffffff) { - _BitScanForward(&idx, int32_t(b)); - return Square(idx); - } else { - _BitScanForward(&idx, int32_t(b >> 32)); - return Square(idx + 32); - } + assert(b); + unsigned long idx; + + if (b & 0xffffffff) + { + _BitScanForward(&idx, int32_t(b)); + return Square(idx); + } + else + { + _BitScanForward(&idx, int32_t(b >> 32)); + return Square(idx + 32); + } } inline Square msb(Bitboard b) { - assert(b); - unsigned long idx; - - if (b >> 32) { - _BitScanReverse(&idx, int32_t(b >> 32)); - return Square(idx + 32); - } else { - _BitScanReverse(&idx, int32_t(b)); - return Square(idx); - } + assert(b); + unsigned long idx; + + if (b >> 32) + { + _BitScanReverse(&idx, int32_t(b >> 32)); + return Square(idx + 32); + } + else + { + _BitScanReverse(&idx, int32_t(b)); + return Square(idx); + } } -#endif + #endif #else // Compiler is neither GCC nor MSVC compatible -#error "Compiler not supported." + #error "Compiler not supported." #endif @@ -346,19 +366,19 @@ inline Square msb(Bitboard b) { // square of a non-zero bitboard. It is equivalent to square_bb(lsb(bb)). inline Bitboard least_significant_square_bb(Bitboard b) { - assert(b); - return b & -b; + assert(b); + return b & -b; } // pop_lsb() finds and clears the least significant bit in a non-zero bitboard inline Square pop_lsb(Bitboard& b) { - assert(b); - const Square s = lsb(b); - b &= b - 1; - return s; + assert(b); + const Square s = lsb(b); + b &= b - 1; + return s; } -} // namespace Stockfish +} // namespace Stockfish -#endif // #ifndef BITBOARD_H_INCLUDED +#endif // #ifndef BITBOARD_H_INCLUDED diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 3eb7ee850a3..00498bf02f0 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -43,11 +43,11 @@ // const unsigned int gEmbeddedNNUESize; // the size of the embedded file // Note that this does not work in Microsoft Visual Studio. #if !defined(_MSC_VER) && !defined(NNUE_EMBEDDING_OFF) - INCBIN(EmbeddedNNUE, EvalFileDefaultName); +INCBIN(EmbeddedNNUE, EvalFileDefaultName); #else - const unsigned char gEmbeddedNNUEData[1] = {0x0}; - const unsigned char *const gEmbeddedNNUEEnd = &gEmbeddedNNUEData[1]; - const unsigned int gEmbeddedNNUESize = 1; +const unsigned char gEmbeddedNNUEData[1] = {0x0}; +const unsigned char* const gEmbeddedNNUEEnd = &gEmbeddedNNUEData[1]; +const unsigned int gEmbeddedNNUESize = 1; #endif @@ -55,27 +55,28 @@ namespace Stockfish { namespace Eval { - std::string currentEvalFileName = "None"; +std::string currentEvalFileName = "None"; - // NNUE::init() tries to load a NNUE network at startup time, or when the engine - // receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue" - // The name of the NNUE network is always retrieved from the EvalFile option. - // We search the given network in three locations: internally (the default - // network may be embedded in the binary), in the active working directory and - // in the engine directory. Distro packagers may define the DEFAULT_NNUE_DIRECTORY - // variable to have the engine search in a special directory in their distro. +// NNUE::init() tries to load a NNUE network at startup time, or when the engine +// receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue" +// The name of the NNUE network is always retrieved from the EvalFile option. +// We search the given network in three locations: internally (the default +// network may be embedded in the binary), in the active working directory and +// in the engine directory. Distro packagers may define the DEFAULT_NNUE_DIRECTORY +// variable to have the engine search in a special directory in their distro. - void NNUE::init() { +void NNUE::init() { std::string eval_file = std::string(Options["EvalFile"]); if (eval_file.empty()) eval_file = EvalFileDefaultName; - #if defined(DEFAULT_NNUE_DIRECTORY) - std::vector dirs = { "" , "" , CommandLine::binaryDirectory , stringify(DEFAULT_NNUE_DIRECTORY) }; - #else - std::vector dirs = { "" , "" , CommandLine::binaryDirectory }; - #endif +#if defined(DEFAULT_NNUE_DIRECTORY) + std::vector dirs = {"", "", CommandLine::binaryDirectory, + stringify(DEFAULT_NNUE_DIRECTORY)}; +#else + std::vector dirs = {"", "", CommandLine::binaryDirectory}; +#endif for (const std::string& directory : dirs) if (currentEvalFileName != eval_file) @@ -90,23 +91,28 @@ namespace Eval { if (directory == "" && eval_file == EvalFileDefaultName) { // C++ way to prepare a buffer for a memory stream - class MemoryBuffer : public std::basic_streambuf { - public: MemoryBuffer(char* p, size_t n) { setg(p, p, p + n); setp(p, p + n); } + class MemoryBuffer: public std::basic_streambuf { + public: + MemoryBuffer(char* p, size_t n) { + setg(p, p, p + n); + setp(p, p + n); + } }; - MemoryBuffer buffer(const_cast(reinterpret_cast(gEmbeddedNNUEData)), - size_t(gEmbeddedNNUESize)); - (void) gEmbeddedNNUEEnd; // Silence warning on unused variable + MemoryBuffer buffer( + const_cast(reinterpret_cast(gEmbeddedNNUEData)), + size_t(gEmbeddedNNUESize)); + (void) gEmbeddedNNUEEnd; // Silence warning on unused variable std::istream stream(&buffer); if (NNUE::load_eval(eval_file, stream)) currentEvalFileName = eval_file; } } - } +} - // NNUE::verify() verifies that the last net used was loaded successfully - void NNUE::verify() { +// NNUE::verify() verifies that the last net used was loaded successfully +void NNUE::verify() { std::string eval_file = std::string(Options["EvalFile"]); if (eval_file.empty()) @@ -115,10 +121,14 @@ namespace Eval { if (currentEvalFileName != eval_file) { - std::string msg1 = "Network evaluation parameters compatible with the engine must be available."; + std::string msg1 = + "Network evaluation parameters compatible with the engine must be available."; std::string msg2 = "The network file " + eval_file + " was not loaded successfully."; - std::string msg3 = "The UCI option EvalFile might need to specify the full path, including the directory name, to the network file."; - std::string msg4 = "The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/" + std::string(EvalFileDefaultName); + std::string msg3 = + "The UCI option EvalFile might need to specify the full path, including the directory name, to the network file."; + std::string msg4 = + "The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/" + + std::string(EvalFileDefaultName); std::string msg5 = "The engine will be terminated now."; sync_cout << "info string ERROR: " << msg1 << sync_endl; @@ -131,7 +141,7 @@ namespace Eval { } sync_cout << "info string NNUE evaluation using " << eval_file << sync_endl; - } +} } @@ -140,8 +150,8 @@ namespace Eval { // an approximation of the material advantage on the board in terms of pawns. Value Eval::simple_eval(const Position& pos, Color c) { - return PawnValue * (pos.count(c) - pos.count(~c)) - + (pos.non_pawn_material(c) - pos.non_pawn_material(~c)); + return PawnValue * (pos.count(c) - pos.count(~c)) + + (pos.non_pawn_material(c) - pos.non_pawn_material(~c)); } @@ -150,43 +160,41 @@ Value Eval::simple_eval(const Position& pos, Color c) { Value Eval::evaluate(const Position& pos) { - assert(!pos.checkers()); + assert(!pos.checkers()); - Value v; - Color stm = pos.side_to_move(); - int shuffling = pos.rule50_count(); - int simpleEval = simple_eval(pos, stm) + (int(pos.key() & 7) - 3); + Value v; + Color stm = pos.side_to_move(); + int shuffling = pos.rule50_count(); + int simpleEval = simple_eval(pos, stm) + (int(pos.key() & 7) - 3); - bool lazy = abs(simpleEval) >= RookValue + KnightValue - + 16 * shuffling * shuffling - + abs(pos.this_thread()->bestValue) - + abs(pos.this_thread()->rootSimpleEval); + bool lazy = abs(simpleEval) >= RookValue + KnightValue + 16 * shuffling * shuffling + + abs(pos.this_thread()->bestValue) + + abs(pos.this_thread()->rootSimpleEval); - if (lazy) - v = Value(simpleEval); - else - { - int nnueComplexity; - Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); + if (lazy) + v = Value(simpleEval); + else + { + int nnueComplexity; + Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); - Value optimism = pos.this_thread()->optimism[stm]; + Value optimism = pos.this_thread()->optimism[stm]; - // Blend optimism and eval with nnue complexity and material imbalance - optimism += optimism * (nnueComplexity + abs(simpleEval - nnue)) / 512; - nnue -= nnue * (nnueComplexity + abs(simpleEval - nnue)) / 32768; + // Blend optimism and eval with nnue complexity and material imbalance + optimism += optimism * (nnueComplexity + abs(simpleEval - nnue)) / 512; + nnue -= nnue * (nnueComplexity + abs(simpleEval - nnue)) / 32768; - int npm = pos.non_pawn_material() / 64; - v = ( nnue * (915 + npm + 9 * pos.count()) - + optimism * (154 + npm )) / 1024; - } + int npm = pos.non_pawn_material() / 64; + v = (nnue * (915 + npm + 9 * pos.count()) + optimism * (154 + npm)) / 1024; + } - // Damp down the evaluation linearly when shuffling - v = v * (200 - shuffling) / 214; + // Damp down the evaluation linearly when shuffling + v = v * (200 - shuffling) / 214; - // Guarantee evaluation does not hit the tablebase range - v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); + // Guarantee evaluation does not hit the tablebase range + v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); - return v; + return v; } // trace() is like evaluate(), but instead of returning a value, it returns @@ -196,33 +204,33 @@ Value Eval::evaluate(const Position& pos) { std::string Eval::trace(Position& pos) { - if (pos.checkers()) - return "Final evaluation: none (in check)"; + if (pos.checkers()) + return "Final evaluation: none (in check)"; - // Reset any global variable used in eval - pos.this_thread()->bestValue = VALUE_ZERO; - pos.this_thread()->rootSimpleEval = VALUE_ZERO; - pos.this_thread()->optimism[WHITE] = VALUE_ZERO; - pos.this_thread()->optimism[BLACK] = VALUE_ZERO; + // Reset any global variable used in eval + pos.this_thread()->bestValue = VALUE_ZERO; + pos.this_thread()->rootSimpleEval = VALUE_ZERO; + pos.this_thread()->optimism[WHITE] = VALUE_ZERO; + pos.this_thread()->optimism[BLACK] = VALUE_ZERO; - std::stringstream ss; - ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2); - ss << '\n' << NNUE::trace(pos) << '\n'; + std::stringstream ss; + ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2); + ss << '\n' << NNUE::trace(pos) << '\n'; - ss << std::showpoint << std::showpos << std::fixed << std::setprecision(2) << std::setw(15); + ss << std::showpoint << std::showpos << std::fixed << std::setprecision(2) << std::setw(15); - Value v; - v = NNUE::evaluate(pos, false); - v = pos.side_to_move() == WHITE ? v : -v; - ss << "NNUE evaluation " << 0.01 * UCI::to_cp(v) << " (white side)\n"; + Value v; + v = NNUE::evaluate(pos, false); + v = pos.side_to_move() == WHITE ? v : -v; + ss << "NNUE evaluation " << 0.01 * UCI::to_cp(v) << " (white side)\n"; - v = evaluate(pos); - v = pos.side_to_move() == WHITE ? v : -v; - ss << "Final evaluation " << 0.01 * UCI::to_cp(v) << " (white side)"; - ss << " [with scaled NNUE, ...]"; - ss << "\n"; + v = evaluate(pos); + v = pos.side_to_move() == WHITE ? v : -v; + ss << "Final evaluation " << 0.01 * UCI::to_cp(v) << " (white side)"; + ss << " [with scaled NNUE, ...]"; + ss << "\n"; - return ss.str(); + return ss.str(); } -} // namespace Stockfish +} // namespace Stockfish diff --git a/src/evaluate.h b/src/evaluate.h index 26f2fc4f985..2ab477eced2 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -29,27 +29,27 @@ class Position; namespace Eval { - std::string trace(Position& pos); +std::string trace(Position& pos); - Value simple_eval(const Position& pos, Color c); - Value evaluate(const Position& pos); +Value simple_eval(const Position& pos, Color c); +Value evaluate(const Position& pos); - extern std::string currentEvalFileName; +extern std::string currentEvalFileName; - // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue - // for the build process (profile-build and fishtest) to work. Do not change the - // name of the macro, as it is used in the Makefile. - #define EvalFileDefaultName "nn-0000000000a0.nnue" +// The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue +// for the build process (profile-build and fishtest) to work. Do not change the +// name of the macro, as it is used in the Makefile. +#define EvalFileDefaultName "nn-0000000000a0.nnue" - namespace NNUE { +namespace NNUE { - void init(); - void verify(); +void init(); +void verify(); - } // namespace NNUE +} // namespace NNUE -} // namespace Eval +} // namespace Eval -} // namespace Stockfish +} // namespace Stockfish -#endif // #ifndef EVALUATE_H_INCLUDED +#endif // #ifndef EVALUATE_H_INCLUDED diff --git a/src/main.cpp b/src/main.cpp index eee149fb455..04879cc4673 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -33,19 +33,19 @@ using namespace Stockfish; int main(int argc, char* argv[]) { - std::cout << engine_info() << std::endl; + std::cout << engine_info() << std::endl; - CommandLine::init(argc, argv); - UCI::init(Options); - Tune::init(); - Bitboards::init(); - Position::init(); - Threads.set(size_t(Options["Threads"])); - Search::clear(); // After threads are up - Eval::NNUE::init(); + CommandLine::init(argc, argv); + UCI::init(Options); + Tune::init(); + Bitboards::init(); + Position::init(); + Threads.set(size_t(Options["Threads"])); + Search::clear(); // After threads are up + Eval::NNUE::init(); - UCI::loop(argc, argv); + UCI::loop(argc, argv); - Threads.set(0); - return 0; + Threads.set(0); + return 0; } diff --git a/src/misc.cpp b/src/misc.cpp index 5abdaf07a31..05181325ece 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -19,30 +19,31 @@ #include "misc.h" #ifdef _WIN32 -#if _WIN32_WINNT < 0x0601 -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x0601 // Force to include needed API prototypes -#endif + #if _WIN32_WINNT < 0x0601 + #undef _WIN32_WINNT + #define _WIN32_WINNT 0x0601 // Force to include needed API prototypes + #endif -#ifndef NOMINMAX -#define NOMINMAX -#endif + #ifndef NOMINMAX + #define NOMINMAX + #endif -#include + #include // The needed Windows API for processor groups could be missed from old Windows // versions, so instead of calling them directly (forcing the linker to resolve // the calls at compile time), try to load them at runtime. To do this we need // first to define the corresponding function pointers. extern "C" { -using fun1_t = bool(*)(LOGICAL_PROCESSOR_RELATIONSHIP, - PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, PDWORD); -using fun2_t = bool(*)(USHORT, PGROUP_AFFINITY); -using fun3_t = bool(*)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY); -using fun4_t = bool(*)(USHORT, PGROUP_AFFINITY, USHORT, PUSHORT); -using fun5_t = WORD(*)(); -using fun6_t = bool(*)(HANDLE, DWORD, PHANDLE); -using fun7_t = bool(*)(LPCSTR, LPCSTR, PLUID); -using fun8_t = bool(*)(HANDLE, BOOL, PTOKEN_PRIVILEGES, DWORD, PTOKEN_PRIVILEGES, PDWORD); +using fun1_t = bool (*)(LOGICAL_PROCESSOR_RELATIONSHIP, + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, + PDWORD); +using fun2_t = bool (*)(USHORT, PGROUP_AFFINITY); +using fun3_t = bool (*)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY); +using fun4_t = bool (*)(USHORT, PGROUP_AFFINITY, USHORT, PUSHORT); +using fun5_t = WORD (*)(); +using fun6_t = bool (*)(HANDLE, DWORD, PHANDLE); +using fun7_t = bool (*)(LPCSTR, LPCSTR, PLUID); +using fun8_t = bool (*)(HANDLE, BOOL, PTOKEN_PRIVILEGES, DWORD, PTOKEN_PRIVILEGES, PDWORD); } #endif @@ -59,12 +60,14 @@ using fun8_t = bool(*)(HANDLE, BOOL, PTOKEN_PRIVILEGES, DWORD, PTOKEN_PRIVILEGES #include "types.h" #if defined(__linux__) && !defined(__ANDROID__) -#include + #include #endif -#if defined(__APPLE__) || defined(__ANDROID__) || defined(__OpenBSD__) || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) && !defined(_WIN32)) || defined(__e2k__) -#define POSIXALIGNEDALLOC -#include +#if defined(__APPLE__) || defined(__ANDROID__) || defined(__OpenBSD__) \ + || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) && !defined(_WIN32)) \ + || defined(__e2k__) + #define POSIXALIGNEDALLOC + #include #endif namespace Stockfish { @@ -80,65 +83,69 @@ constexpr std::string_view version = "dev"; // usual I/O functionality, all without changing a single line of code! // Idea from http://groups.google.com/group/comp.lang.c++/msg/1d941c0f26ea0d81 -struct Tie: public std::streambuf { // MSVC requires split streambuf for cin and cout +struct Tie: public std::streambuf { // MSVC requires split streambuf for cin and cout - Tie(std::streambuf* b, std::streambuf* l) : buf(b), logBuf(l) {} + Tie(std::streambuf* b, std::streambuf* l) : + buf(b), + logBuf(l) {} - int sync() override { return logBuf->pubsync(), buf->pubsync(); } - int overflow(int c) override { return log(buf->sputc(char(c)), "<< "); } - int underflow() override { return buf->sgetc(); } - int uflow() override { return log(buf->sbumpc(), ">> "); } + int sync() override { return logBuf->pubsync(), buf->pubsync(); } + int overflow(int c) override { return log(buf->sputc(char(c)), "<< "); } + int underflow() override { return buf->sgetc(); } + int uflow() override { return log(buf->sbumpc(), ">> "); } - std::streambuf *buf, *logBuf; + std::streambuf *buf, *logBuf; - int log(int c, const char* prefix) { + int log(int c, const char* prefix) { - static int last = '\n'; // Single log file + static int last = '\n'; // Single log file - if (last == '\n') - logBuf->sputn(prefix, 3); + if (last == '\n') + logBuf->sputn(prefix, 3); - return last = logBuf->sputc(char(c)); - } + return last = logBuf->sputc(char(c)); + } }; class Logger { - Logger() : in(std::cin.rdbuf(), file.rdbuf()), out(std::cout.rdbuf(), file.rdbuf()) {} - ~Logger() { start(""); } + Logger() : + in(std::cin.rdbuf(), file.rdbuf()), + out(std::cout.rdbuf(), file.rdbuf()) {} + ~Logger() { start(""); } - std::ofstream file; - Tie in, out; + std::ofstream file; + Tie in, out; -public: - static void start(const std::string& fname) { - - static Logger l; - - if (l.file.is_open()) - { - std::cout.rdbuf(l.out.buf); - std::cin.rdbuf(l.in.buf); - l.file.close(); - } + public: + static void start(const std::string& fname) { - if (!fname.empty()) - { - l.file.open(fname, std::ifstream::out); + static Logger l; - if (!l.file.is_open()) + if (l.file.is_open()) { - std::cerr << "Unable to open debug log file " << fname << std::endl; - exit(EXIT_FAILURE); + std::cout.rdbuf(l.out.buf); + std::cin.rdbuf(l.in.buf); + l.file.close(); } - std::cin.rdbuf(&l.in); - std::cout.rdbuf(&l.out); + if (!fname.empty()) + { + l.file.open(fname, std::ifstream::out); + + if (!l.file.is_open()) + { + std::cerr << "Unable to open debug log file " << fname << std::endl; + exit(EXIT_FAILURE); + } + + std::cin.rdbuf(&l.in); + std::cout.rdbuf(&l.out); + } } - } }; -} // namespace +} // namespace // engine_info() returns the full name of the current Stockfish version. @@ -152,36 +159,36 @@ class Logger { // Stockfish version std::string engine_info(bool to_uci) { - std::stringstream ss; - ss << "Stockfish " << version << std::setfill('0'); - - if constexpr (version == "dev") - { - ss << "-"; - #ifdef GIT_DATE - ss << stringify(GIT_DATE); - #else - constexpr std::string_view months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); - std::string month, day, year; - std::stringstream date(__DATE__); // From compiler, format is "Sep 21 2008" - - date >> month >> day >> year; - ss << year << std::setw(2) << std::setfill('0') << (1 + months.find(month) / 4) << std::setw(2) << std::setfill('0') << day; - #endif - - ss << "-"; - - #ifdef GIT_SHA - ss << stringify(GIT_SHA); - #else - ss << "nogit"; - #endif - } - - ss << (to_uci ? "\nid author ": " by ") - << "the Stockfish developers (see AUTHORS file)"; - - return ss.str(); + std::stringstream ss; + ss << "Stockfish " << version << std::setfill('0'); + + if constexpr (version == "dev") + { + ss << "-"; +#ifdef GIT_DATE + ss << stringify(GIT_DATE); +#else + constexpr std::string_view months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); + std::string month, day, year; + std::stringstream date(__DATE__); // From compiler, format is "Sep 21 2008" + + date >> month >> day >> year; + ss << year << std::setw(2) << std::setfill('0') << (1 + months.find(month) / 4) + << std::setw(2) << std::setfill('0') << day; +#endif + + ss << "-"; + +#ifdef GIT_SHA + ss << stringify(GIT_SHA); +#else + ss << "nogit"; +#endif + } + + ss << (to_uci ? "\nid author " : " by ") << "the Stockfish developers (see AUTHORS file)"; + + return ss.str(); } @@ -189,119 +196,118 @@ std::string engine_info(bool to_uci) { std::string compiler_info() { - #define make_version_string(major, minor, patch) stringify(major) "." stringify(minor) "." stringify(patch) - -// Predefined macros hell: -// -// __GNUC__ Compiler is GCC, Clang or ICX -// __clang__ Compiler is Clang or ICX -// __INTEL_LLVM_COMPILER Compiler is ICX -// _MSC_VER Compiler is MSVC -// _WIN32 Building on Windows (any) -// _WIN64 Building on Windows 64 bit - - std::string compiler = "\nCompiled by : "; - - #if defined(__INTEL_LLVM_COMPILER) - compiler += "ICX "; - compiler += stringify(__INTEL_LLVM_COMPILER); - #elif defined(__clang__) - compiler += "clang++ "; - compiler += make_version_string(__clang_major__, __clang_minor__, __clang_patchlevel__); - #elif _MSC_VER - compiler += "MSVC "; - compiler += "(version "; - compiler += stringify(_MSC_FULL_VER) "." stringify(_MSC_BUILD); - compiler += ")"; - #elif defined(__e2k__) && defined(__LCC__) +#define make_version_string(major, minor, patch) \ + stringify(major) "." stringify(minor) "." stringify(patch) + + // Predefined macros hell: + // + // __GNUC__ Compiler is GCC, Clang or ICX + // __clang__ Compiler is Clang or ICX + // __INTEL_LLVM_COMPILER Compiler is ICX + // _MSC_VER Compiler is MSVC + // _WIN32 Building on Windows (any) + // _WIN64 Building on Windows 64 bit + + std::string compiler = "\nCompiled by : "; + +#if defined(__INTEL_LLVM_COMPILER) + compiler += "ICX "; + compiler += stringify(__INTEL_LLVM_COMPILER); +#elif defined(__clang__) + compiler += "clang++ "; + compiler += make_version_string(__clang_major__, __clang_minor__, __clang_patchlevel__); +#elif _MSC_VER + compiler += "MSVC "; + compiler += "(version "; + compiler += stringify(_MSC_FULL_VER) "." stringify(_MSC_BUILD); + compiler += ")"; +#elif defined(__e2k__) && defined(__LCC__) #define dot_ver2(n) \ - compiler += char('.'); \ - compiler += char('0' + (n) / 10); \ - compiler += char('0' + (n) % 10); - - compiler += "MCST LCC "; - compiler += "(version "; - compiler += std::to_string(__LCC__ / 100); - dot_ver2(__LCC__ % 100) - dot_ver2(__LCC_MINOR__) - compiler += ")"; - #elif __GNUC__ - compiler += "g++ (GNUC) "; - compiler += make_version_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 += "\nCompilation architecture : "; - #if defined(ARCH) - compiler += stringify(ARCH); - #else - compiler += "(undefined architecture)"; - #endif - - compiler += "\nCompilation settings : "; - compiler += (Is64Bit ? "64bit" : "32bit"); - #if defined(USE_VNNI) + compiler += char('.'); \ + compiler += char('0' + (n) / 10); \ + compiler += char('0' + (n) % 10); + + compiler += "MCST LCC "; + compiler += "(version "; + compiler += std::to_string(__LCC__ / 100); + dot_ver2(__LCC__ % 100) dot_ver2(__LCC_MINOR__) compiler += ")"; +#elif __GNUC__ + compiler += "g++ (GNUC) "; + compiler += make_version_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 += "\nCompilation architecture : "; +#if defined(ARCH) + compiler += stringify(ARCH); +#else + compiler += "(undefined architecture)"; +#endif + + compiler += "\nCompilation settings : "; + compiler += (Is64Bit ? "64bit" : "32bit"); +#if defined(USE_VNNI) compiler += " VNNI"; - #endif - #if defined(USE_AVX512) +#endif +#if defined(USE_AVX512) compiler += " AVX512"; - #endif - compiler += (HasPext ? " BMI2" : ""); - #if defined(USE_AVX2) +#endif + compiler += (HasPext ? " BMI2" : ""); +#if defined(USE_AVX2) compiler += " AVX2"; - #endif - #if defined(USE_SSE41) +#endif +#if defined(USE_SSE41) compiler += " SSE41"; - #endif - #if defined(USE_SSSE3) +#endif +#if defined(USE_SSSE3) compiler += " SSSE3"; - #endif - #if defined(USE_SSE2) +#endif +#if defined(USE_SSE2) compiler += " SSE2"; - #endif - compiler += (HasPopCnt ? " POPCNT" : ""); - #if defined(USE_NEON_DOTPROD) +#endif + compiler += (HasPopCnt ? " POPCNT" : ""); +#if defined(USE_NEON_DOTPROD) compiler += " NEON_DOTPROD"; - #elif defined(USE_NEON) +#elif defined(USE_NEON) compiler += " NEON"; - #endif +#endif - #if !defined(NDEBUG) +#if !defined(NDEBUG) compiler += " DEBUG"; - #endif +#endif - compiler += "\nCompiler __VERSION__ macro : "; - #ifdef __VERSION__ - compiler += __VERSION__; - #else - compiler += "(undefined macro)"; - #endif + compiler += "\nCompiler __VERSION__ macro : "; +#ifdef __VERSION__ + compiler += __VERSION__; +#else + compiler += "(undefined macro)"; +#endif - compiler += "\n"; + compiler += "\n"; - return compiler; + return compiler; } @@ -312,7 +318,7 @@ namespace { template struct DebugInfo { - std::atomic data[N] = { 0 }; + std::atomic data[N] = {0}; constexpr inline std::atomic& operator[](int index) { return data[index]; } }; @@ -357,42 +363,34 @@ void dbg_correl_of(int64_t value1, int64_t value2, int slot) { void dbg_print() { int64_t n; - auto E = [&n](int64_t x) { return double(x) / n; }; - auto sqr = [](double x) { return x * x; }; + auto E = [&n](int64_t x) { return double(x) / n; }; + auto sqr = [](double x) { return x * x; }; for (int i = 0; i < MaxDebugSlots; ++i) if ((n = hit[i][0])) - std::cerr << "Hit #" << i - << ": Total " << n << " Hits " << hit[i][1] - << " Hit Rate (%) " << 100.0 * E(hit[i][1]) - << std::endl; + std::cerr << "Hit #" << i << ": Total " << n << " Hits " << hit[i][1] + << " Hit Rate (%) " << 100.0 * E(hit[i][1]) << std::endl; for (int i = 0; i < MaxDebugSlots; ++i) if ((n = mean[i][0])) { - std::cerr << "Mean #" << i - << ": Total " << n << " Mean " << E(mean[i][1]) - << std::endl; + std::cerr << "Mean #" << i << ": Total " << n << " Mean " << E(mean[i][1]) << std::endl; } for (int i = 0; i < MaxDebugSlots; ++i) if ((n = stdev[i][0])) { double r = sqrt(E(stdev[i][2]) - sqr(E(stdev[i][1]))); - std::cerr << "Stdev #" << i - << ": Total " << n << " Stdev " << r - << std::endl; + std::cerr << "Stdev #" << i << ": Total " << n << " Stdev " << r << std::endl; } for (int i = 0; i < MaxDebugSlots; ++i) if ((n = correl[i][0])) { double r = (E(correl[i][5]) - E(correl[i][1]) * E(correl[i][3])) - / ( sqrt(E(correl[i][2]) - sqr(E(correl[i][1]))) - * sqrt(E(correl[i][4]) - sqr(E(correl[i][3])))); - std::cerr << "Correl. #" << i - << ": Total " << n << " Coefficient " << r - << std::endl; + / (sqrt(E(correl[i][2]) - sqr(E(correl[i][1]))) + * sqrt(E(correl[i][4]) - sqr(E(correl[i][3])))); + std::cerr << "Correl. #" << i << ": Total " << n << " Coefficient " << r << std::endl; } } @@ -402,15 +400,15 @@ void dbg_print() { std::ostream& operator<<(std::ostream& os, SyncCout sc) { - static std::mutex m; + static std::mutex m; - if (sc == IO_LOCK) - m.lock(); + if (sc == IO_LOCK) + m.lock(); - if (sc == IO_UNLOCK) - m.unlock(); + if (sc == IO_UNLOCK) + m.unlock(); - return os; + return os; } @@ -429,11 +427,11 @@ void prefetch(void*) {} void prefetch(void* addr) { -# if defined(_MSC_VER) - _mm_prefetch((char*)addr, _MM_HINT_T0); -# else - __builtin_prefetch(addr); -# endif + #if defined(_MSC_VER) + _mm_prefetch((char*) addr, _MM_HINT_T0); + #else + __builtin_prefetch(addr); + #endif } #endif @@ -446,27 +444,27 @@ void prefetch(void* addr) { void* std_aligned_alloc(size_t alignment, size_t size) { #if defined(POSIXALIGNEDALLOC) - void *mem; - return posix_memalign(&mem, alignment, size) ? nullptr : mem; + void* mem; + return posix_memalign(&mem, alignment, size) ? nullptr : mem; #elif defined(_WIN32) && !defined(_M_ARM) && !defined(_M_ARM64) - return _mm_malloc(size, alignment); + return _mm_malloc(size, alignment); #elif defined(_WIN32) - return _aligned_malloc(size, alignment); + return _aligned_malloc(size, alignment); #else - return std::aligned_alloc(alignment, size); + return std::aligned_alloc(alignment, size); #endif } void std_aligned_free(void* ptr) { #if defined(POSIXALIGNEDALLOC) - free(ptr); + free(ptr); #elif defined(_WIN32) && !defined(_M_ARM) && !defined(_M_ARM64) - _mm_free(ptr); + _mm_free(ptr); #elif defined(_WIN32) - _aligned_free(ptr); + _aligned_free(ptr); #else - free(ptr); + free(ptr); #endif } @@ -476,104 +474,104 @@ void std_aligned_free(void* ptr) { static void* aligned_large_pages_alloc_windows([[maybe_unused]] size_t allocSize) { - #if !defined(_WIN64) + #if !defined(_WIN64) return nullptr; - #else - - HANDLE hProcessToken { }; - LUID luid { }; - void* mem = nullptr; - - const size_t largePageSize = GetLargePageMinimum(); - if (!largePageSize) - return nullptr; - - // Dynamically link OpenProcessToken, LookupPrivilegeValue and AdjustTokenPrivileges - - HMODULE hAdvapi32 = GetModuleHandle(TEXT("advapi32.dll")); - - if (!hAdvapi32) - hAdvapi32 = LoadLibrary(TEXT("advapi32.dll")); - - auto fun6 = fun6_t((void(*)())GetProcAddress(hAdvapi32, "OpenProcessToken")); - if (!fun6) - return nullptr; - auto fun7 = fun7_t((void(*)())GetProcAddress(hAdvapi32, "LookupPrivilegeValueA")); - if (!fun7) - return nullptr; - auto fun8 = fun8_t((void(*)())GetProcAddress(hAdvapi32, "AdjustTokenPrivileges")); - if (!fun8) - return nullptr; - - // We need SeLockMemoryPrivilege, so try to enable it for the process - if (!fun6( // OpenProcessToken() - GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken)) - return nullptr; - - if (fun7( // LookupPrivilegeValue(nullptr, SE_LOCK_MEMORY_NAME, &luid) - nullptr, "SeLockMemoryPrivilege", &luid)) - { - TOKEN_PRIVILEGES tp { }; - TOKEN_PRIVILEGES prevTp { }; - DWORD prevTpLen = 0; - - tp.PrivilegeCount = 1; - tp.Privileges[0].Luid = luid; - tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - - // Try to enable SeLockMemoryPrivilege. Note that even if AdjustTokenPrivileges() succeeds, - // we still need to query GetLastError() to ensure that the privileges were actually obtained. - if (fun8( // AdjustTokenPrivileges() - hProcessToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &prevTp, &prevTpLen) && - GetLastError() == ERROR_SUCCESS) - { - // Round up size to full pages and allocate - allocSize = (allocSize + largePageSize - 1) & ~size_t(largePageSize - 1); - mem = VirtualAlloc( - nullptr, allocSize, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); - - // Privilege no longer needed, restore previous state - fun8( // AdjustTokenPrivileges () + #else + + HANDLE hProcessToken{}; + LUID luid{}; + void* mem = nullptr; + + const size_t largePageSize = GetLargePageMinimum(); + if (!largePageSize) + return nullptr; + + // Dynamically link OpenProcessToken, LookupPrivilegeValue and AdjustTokenPrivileges + + HMODULE hAdvapi32 = GetModuleHandle(TEXT("advapi32.dll")); + + if (!hAdvapi32) + hAdvapi32 = LoadLibrary(TEXT("advapi32.dll")); + + auto fun6 = fun6_t((void (*)()) GetProcAddress(hAdvapi32, "OpenProcessToken")); + if (!fun6) + return nullptr; + auto fun7 = fun7_t((void (*)()) GetProcAddress(hAdvapi32, "LookupPrivilegeValueA")); + if (!fun7) + return nullptr; + auto fun8 = fun8_t((void (*)()) GetProcAddress(hAdvapi32, "AdjustTokenPrivileges")); + if (!fun8) + return nullptr; + + // We need SeLockMemoryPrivilege, so try to enable it for the process + if (!fun6( // OpenProcessToken() + GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken)) + return nullptr; + + if (fun7( // LookupPrivilegeValue(nullptr, SE_LOCK_MEMORY_NAME, &luid) + nullptr, "SeLockMemoryPrivilege", &luid)) + { + TOKEN_PRIVILEGES tp{}; + TOKEN_PRIVILEGES prevTp{}; + DWORD prevTpLen = 0; + + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = luid; + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + // Try to enable SeLockMemoryPrivilege. Note that even if AdjustTokenPrivileges() succeeds, + // we still need to query GetLastError() to ensure that the privileges were actually obtained. + if (fun8( // AdjustTokenPrivileges() + hProcessToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &prevTp, &prevTpLen) + && GetLastError() == ERROR_SUCCESS) + { + // Round up size to full pages and allocate + allocSize = (allocSize + largePageSize - 1) & ~size_t(largePageSize - 1); + mem = VirtualAlloc(nullptr, allocSize, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, + PAGE_READWRITE); + + // Privilege no longer needed, restore previous state + fun8( // AdjustTokenPrivileges () hProcessToken, FALSE, &prevTp, 0, nullptr, nullptr); - } - } + } + } - CloseHandle(hProcessToken); + CloseHandle(hProcessToken); - return mem; + return mem; - #endif + #endif } void* aligned_large_pages_alloc(size_t allocSize) { - // Try to allocate large pages - void* mem = aligned_large_pages_alloc_windows(allocSize); + // Try to allocate large pages + void* mem = aligned_large_pages_alloc_windows(allocSize); - // Fall back to regular, page-aligned, allocation if necessary - if (!mem) - mem = VirtualAlloc(nullptr, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + // Fall back to regular, page-aligned, allocation if necessary + if (!mem) + mem = VirtualAlloc(nullptr, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); - return mem; + return mem; } #else void* aligned_large_pages_alloc(size_t allocSize) { -#if defined(__linux__) - constexpr size_t alignment = 2 * 1024 * 1024; // assumed 2MB page size -#else - constexpr size_t alignment = 4096; // assumed small page size -#endif - - // round up to multiples of alignment - size_t size = ((allocSize + alignment - 1) / alignment) * alignment; - void *mem = std_aligned_alloc(alignment, size); -#if defined(MADV_HUGEPAGE) - madvise(mem, size, MADV_HUGEPAGE); -#endif - return mem; + #if defined(__linux__) + constexpr size_t alignment = 2 * 1024 * 1024; // assumed 2MB page size + #else + constexpr size_t alignment = 4096; // assumed small page size + #endif + + // round up to multiples of alignment + size_t size = ((allocSize + alignment - 1) / alignment) * alignment; + void* mem = std_aligned_alloc(alignment, size); + #if defined(MADV_HUGEPAGE) + madvise(mem, size, MADV_HUGEPAGE); + #endif + return mem; } #endif @@ -585,21 +583,18 @@ void* aligned_large_pages_alloc(size_t allocSize) { void aligned_large_pages_free(void* mem) { - if (mem && !VirtualFree(mem, 0, MEM_RELEASE)) - { - DWORD err = GetLastError(); - std::cerr << "Failed to free large page memory. Error code: 0x" - << std::hex << err - << std::dec << std::endl; - exit(EXIT_FAILURE); - } + if (mem && !VirtualFree(mem, 0, MEM_RELEASE)) + { + DWORD err = GetLastError(); + std::cerr << "Failed to free large page memory. Error code: 0x" << std::hex << err + << std::dec << std::endl; + exit(EXIT_FAILURE); + } } #else -void aligned_large_pages_free(void *mem) { - std_aligned_free(mem); -} +void aligned_large_pages_free(void* mem) { std_aligned_free(mem); } #endif @@ -618,69 +613,69 @@ void bindThisThread(size_t) {} static int best_node(size_t idx) { - int threads = 0; - int nodes = 0; - int cores = 0; - DWORD returnLength = 0; - DWORD byteOffset = 0; - - // Early exit if the needed API is not available at runtime - HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll")); - auto fun1 = (fun1_t)(void(*)())GetProcAddress(k32, "GetLogicalProcessorInformationEx"); - if (!fun1) - return -1; - - // First call to GetLogicalProcessorInformationEx() to get returnLength. - // We expect the call to fail due to null buffer. - if (fun1(RelationAll, nullptr, &returnLength)) - return -1; - - // Once we know returnLength, allocate the buffer - SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *buffer, *ptr; - ptr = buffer = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)malloc(returnLength); - - // Second call to GetLogicalProcessorInformationEx(), now we expect to succeed - if (!fun1(RelationAll, buffer, &returnLength)) - { - free(buffer); - return -1; - } - - while (byteOffset < returnLength) - { - if (ptr->Relationship == RelationNumaNode) - nodes++; - - else if (ptr->Relationship == RelationProcessorCore) - { - cores++; - threads += (ptr->Processor.Flags == LTP_PC_SMT) ? 2 : 1; - } - - assert(ptr->Size); - byteOffset += ptr->Size; - ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)(((char*)ptr) + ptr->Size); - } - - free(buffer); - - std::vector groups; - - // Run as many threads as possible on the same node until the core limit is - // reached, then move on to filling the next node. - for (int n = 0; n < nodes; n++) - for (int i = 0; i < cores / nodes; i++) - groups.push_back(n); - - // In case a core has more than one logical processor (we assume 2) and we - // have still threads to allocate, then spread them evenly across available - // nodes. - for (int t = 0; t < threads - cores; t++) - groups.push_back(t % nodes); - - // If we still have more threads than the total number of logical processors - // then return -1 and let the OS to decide what to do. - return idx < groups.size() ? groups[idx] : -1; + int threads = 0; + int nodes = 0; + int cores = 0; + DWORD returnLength = 0; + DWORD byteOffset = 0; + + // Early exit if the needed API is not available at runtime + HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll")); + auto fun1 = (fun1_t) (void (*)()) GetProcAddress(k32, "GetLogicalProcessorInformationEx"); + if (!fun1) + return -1; + + // First call to GetLogicalProcessorInformationEx() to get returnLength. + // We expect the call to fail due to null buffer. + if (fun1(RelationAll, nullptr, &returnLength)) + return -1; + + // Once we know returnLength, allocate the buffer + SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *buffer, *ptr; + ptr = buffer = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*) malloc(returnLength); + + // Second call to GetLogicalProcessorInformationEx(), now we expect to succeed + if (!fun1(RelationAll, buffer, &returnLength)) + { + free(buffer); + return -1; + } + + while (byteOffset < returnLength) + { + if (ptr->Relationship == RelationNumaNode) + nodes++; + + else if (ptr->Relationship == RelationProcessorCore) + { + cores++; + threads += (ptr->Processor.Flags == LTP_PC_SMT) ? 2 : 1; + } + + assert(ptr->Size); + byteOffset += ptr->Size; + ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*) (((char*) ptr) + ptr->Size); + } + + free(buffer); + + std::vector groups; + + // Run as many threads as possible on the same node until the core limit is + // reached, then move on to filling the next node. + for (int n = 0; n < nodes; n++) + for (int i = 0; i < cores / nodes; i++) + groups.push_back(n); + + // In case a core has more than one logical processor (we assume 2) and we + // have still threads to allocate, then spread them evenly across available + // nodes. + for (int t = 0; t < threads - cores; t++) + groups.push_back(t % nodes); + + // If we still have more threads than the total number of logical processors + // then return -1 and let the OS to decide what to do. + return idx < groups.size() ? groups[idx] : -1; } @@ -688,58 +683,59 @@ static int best_node(size_t idx) { void bindThisThread(size_t idx) { - // Use only local variables to be thread-safe - int node = best_node(idx); - - if (node == -1) - return; - - // Early exit if the needed API are not available at runtime - HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll")); - auto fun2 = fun2_t((void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMaskEx")); - auto fun3 = fun3_t((void(*)())GetProcAddress(k32, "SetThreadGroupAffinity")); - auto fun4 = fun4_t((void(*)())GetProcAddress(k32, "GetNumaNodeProcessorMask2")); - auto fun5 = fun5_t((void(*)())GetProcAddress(k32, "GetMaximumProcessorGroupCount")); - - if (!fun2 || !fun3) - return; - - if (!fun4 || !fun5) - { - GROUP_AFFINITY affinity; - if (fun2(node, &affinity)) // GetNumaNodeProcessorMaskEx - fun3(GetCurrentThread(), &affinity, nullptr); // SetThreadGroupAffinity - } - else - { - // If a numa node has more than one processor group, we assume they are - // sized equal and we spread threads evenly across the groups. - USHORT elements, returnedElements; - elements = fun5(); // GetMaximumProcessorGroupCount - GROUP_AFFINITY *affinity = (GROUP_AFFINITY*)malloc(elements * sizeof(GROUP_AFFINITY)); - if (fun4(node, affinity, elements, &returnedElements)) // GetNumaNodeProcessorMask2 - fun3(GetCurrentThread(), &affinity[idx % returnedElements], nullptr); // SetThreadGroupAffinity - free(affinity); - } + // Use only local variables to be thread-safe + int node = best_node(idx); + + if (node == -1) + return; + + // Early exit if the needed API are not available at runtime + HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll")); + auto fun2 = fun2_t((void (*)()) GetProcAddress(k32, "GetNumaNodeProcessorMaskEx")); + auto fun3 = fun3_t((void (*)()) GetProcAddress(k32, "SetThreadGroupAffinity")); + auto fun4 = fun4_t((void (*)()) GetProcAddress(k32, "GetNumaNodeProcessorMask2")); + auto fun5 = fun5_t((void (*)()) GetProcAddress(k32, "GetMaximumProcessorGroupCount")); + + if (!fun2 || !fun3) + return; + + if (!fun4 || !fun5) + { + GROUP_AFFINITY affinity; + if (fun2(node, &affinity)) // GetNumaNodeProcessorMaskEx + fun3(GetCurrentThread(), &affinity, nullptr); // SetThreadGroupAffinity + } + else + { + // If a numa node has more than one processor group, we assume they are + // sized equal and we spread threads evenly across the groups. + USHORT elements, returnedElements; + elements = fun5(); // GetMaximumProcessorGroupCount + GROUP_AFFINITY* affinity = (GROUP_AFFINITY*) malloc(elements * sizeof(GROUP_AFFINITY)); + if (fun4(node, affinity, elements, &returnedElements)) // GetNumaNodeProcessorMask2 + fun3(GetCurrentThread(), &affinity[idx % returnedElements], + nullptr); // SetThreadGroupAffinity + free(affinity); + } } #endif -} // namespace WinProcGroup +} // namespace WinProcGroup #ifdef _WIN32 -#include -#define GETCWD _getcwd + #include + #define GETCWD _getcwd #else -#include -#define GETCWD getcwd + #include + #define GETCWD getcwd #endif namespace CommandLine { -std::string argv0; // path+name of the executable binary, as given by argv[0] -std::string binaryDirectory; // path of the executable directory -std::string workingDirectory; // path of the working directory +std::string argv0; // path+name of the executable binary, as given by argv[0] +std::string binaryDirectory; // path of the executable directory +std::string workingDirectory; // path of the working directory void init([[maybe_unused]] int argc, char* argv[]) { std::string pathSeparator; @@ -749,27 +745,27 @@ void init([[maybe_unused]] int argc, char* argv[]) { #ifdef _WIN32 pathSeparator = "\\"; - #ifdef _MSC_VER + #ifdef _MSC_VER // Under windows argv[0] may not have the extension. Also _get_pgmptr() had // issues in some Windows 10 versions, so check returned values carefully. char* pgmptr = nullptr; if (!_get_pgmptr(&pgmptr) && pgmptr != nullptr && *pgmptr) argv0 = pgmptr; - #endif + #endif #else pathSeparator = "/"; #endif // extract the working directory workingDirectory = ""; - char buff[40000]; + char buff[40000]; char* cwd = GETCWD(buff, 40000); if (cwd) workingDirectory = cwd; // extract the binary directory path from argv0 binaryDirectory = argv0; - size_t pos = binaryDirectory.find_last_of("\\/"); + size_t pos = binaryDirectory.find_last_of("\\/"); if (pos == std::string::npos) binaryDirectory = "." + pathSeparator; else @@ -781,6 +777,6 @@ void init([[maybe_unused]] int argc, char* argv[]) { } -} // namespace CommandLine +} // namespace CommandLine -} // namespace Stockfish +} // namespace Stockfish diff --git a/src/misc.h b/src/misc.h index 60602048b9c..3cd3315a8ed 100644 --- a/src/misc.h +++ b/src/misc.h @@ -33,12 +33,13 @@ namespace Stockfish { std::string engine_info(bool to_uci = false); std::string compiler_info(); -void prefetch(void* addr); -void start_logger(const std::string& fname); -void* std_aligned_alloc(size_t alignment, size_t size); -void std_aligned_free(void* ptr); -void* aligned_large_pages_alloc(size_t size); // memory aligned by page size, min alignment: 4096 bytes -void aligned_large_pages_free(void* mem); // nop if mem == nullptr +void prefetch(void* addr); +void start_logger(const std::string& fname); +void* std_aligned_alloc(size_t alignment, size_t size); +void std_aligned_free(void* ptr); +void* aligned_large_pages_alloc( + size_t size); // memory aligned by page size, min alignment: 4096 bytes +void aligned_large_pages_free(void* mem); // nop if mem == nullptr void dbg_hit_on(bool cond, int slot = 0); void dbg_mean_of(int64_t value, int slot = 0); @@ -46,15 +47,19 @@ void dbg_stdev_of(int64_t value, int slot = 0); void dbg_correl_of(int64_t value1, int64_t value2, int slot = 0); void dbg_print(); -using TimePoint = std::chrono::milliseconds::rep; // A value in milliseconds +using TimePoint = std::chrono::milliseconds::rep; // A value in milliseconds static_assert(sizeof(TimePoint) == sizeof(int64_t), "TimePoint should be 64 bits"); inline TimePoint now() { - return std::chrono::duration_cast - (std::chrono::steady_clock::now().time_since_epoch()).count(); + return std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()) + .count(); } -enum SyncCout { IO_LOCK, IO_UNLOCK }; +enum SyncCout { + IO_LOCK, + IO_UNLOCK +}; std::ostream& operator<<(std::ostream&, SyncCout); #define sync_cout std::cout << IO_LOCK @@ -64,34 +69,37 @@ std::ostream& operator<<(std::ostream&, SyncCout); // align_ptr_up() : get the first aligned element of an array. // ptr must point to an array of size at least `sizeof(T) * N + alignment` bytes, // where N is the number of elements in the array. -template -T* align_ptr_up(T* ptr) -{ - static_assert(alignof(T) < Alignment); +template +T* align_ptr_up(T* ptr) { + static_assert(alignof(T) < Alignment); - const uintptr_t ptrint = reinterpret_cast(reinterpret_cast(ptr)); - return reinterpret_cast(reinterpret_cast((ptrint + (Alignment - 1)) / Alignment * Alignment)); + const uintptr_t ptrint = reinterpret_cast(reinterpret_cast(ptr)); + return reinterpret_cast( + reinterpret_cast((ptrint + (Alignment - 1)) / Alignment * Alignment)); } // IsLittleEndian : true if and only if the binary is compiled on a little-endian machine -static inline const union { uint32_t i; char c[4]; } Le = { 0x01020304 }; +static inline const union { + uint32_t i; + char c[4]; +} Le = {0x01020304}; static inline const bool IsLittleEndian = (Le.c[0] == 4); -template +template class ValueList { -public: - std::size_t size() const { return size_; } - void push_back(const T& value) { values_[size_++] = value; } - const T* begin() const { return values_; } - const T* end() const { return values_ + size_; } - const T& operator[](int index) const { return values_[index]; } + public: + std::size_t size() const { return size_; } + void push_back(const T& value) { values_[size_++] = value; } + const T* begin() const { return values_; } + const T* end() const { return values_ + size_; } + const T& operator[](int index) const { return values_[index]; } -private: - T values_[MaxSize]; - std::size_t size_ = 0; + private: + T values_[MaxSize]; + std::size_t size_ = 0; }; @@ -112,23 +120,31 @@ class ValueList { class PRNG { - uint64_t s; + uint64_t s; - uint64_t rand64() { + uint64_t rand64() { - s ^= s >> 12, s ^= s << 25, s ^= s >> 27; - return s * 2685821657736338717LL; - } + s ^= s >> 12, s ^= s << 25, s ^= s >> 27; + return s * 2685821657736338717LL; + } -public: - PRNG(uint64_t seed) : s(seed) { assert(seed); } + public: + PRNG(uint64_t seed) : + s(seed) { + assert(seed); + } - template T rand() { return T(rand64()); } + template + T rand() { + return T(rand64()); + } - // Special generator used to fast init magic numbers. - // Output values only have 1/8th of their bits set on average. - template T sparse_rand() - { return T(rand64() & rand64() & rand64()); } + // Special generator used to fast init magic numbers. + // Output values only have 1/8th of their bits set on average. + template + T sparse_rand() { + return T(rand64() & rand64() & rand64()); + } }; inline uint64_t mul_hi64(uint64_t a, uint64_t b) { @@ -152,16 +168,16 @@ inline uint64_t mul_hi64(uint64_t a, uint64_t b) { // Peter Österlund. namespace WinProcGroup { - void bindThisThread(size_t idx); +void bindThisThread(size_t idx); } namespace CommandLine { - void init(int argc, char* argv[]); +void init(int argc, char* argv[]); - extern std::string binaryDirectory; // path of the executable directory - extern std::string workingDirectory; // path of the working directory +extern std::string binaryDirectory; // path of the executable directory +extern std::string workingDirectory; // path of the working directory } -} // namespace Stockfish +} // namespace Stockfish -#endif // #ifndef MISC_H_INCLUDED +#endif // #ifndef MISC_H_INCLUDED diff --git a/src/movegen.cpp b/src/movegen.cpp index 82ad60613c2..cf457d1176c 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -28,8 +28,8 @@ namespace Stockfish { namespace { - template - ExtMove* make_promotions(ExtMove* moveList, [[maybe_unused]] Square to) { +template +ExtMove* make_promotions(ExtMove* moveList, [[maybe_unused]] Square to) { if constexpr (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) { @@ -50,33 +50,32 @@ namespace { } return moveList; - } +} - template - ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) { +template +ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) { constexpr Color Them = ~Us; - constexpr Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); - constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); + constexpr Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); + constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); constexpr Direction Up = pawn_push(Us); constexpr Direction UpRight = (Us == WHITE ? NORTH_EAST : SOUTH_WEST); constexpr Direction UpLeft = (Us == WHITE ? NORTH_WEST : SOUTH_EAST); const Bitboard emptySquares = ~pos.pieces(); - const Bitboard enemies = Type == EVASIONS ? pos.checkers() - : pos.pieces(Them); + const Bitboard enemies = Type == EVASIONS ? pos.checkers() : pos.pieces(Them); - Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB; + Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB; Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB; // Single and double pawn pushes, no promotions if constexpr (Type != CAPTURES) { - Bitboard b1 = shift(pawnsNotOn7) & emptySquares; + Bitboard b1 = shift(pawnsNotOn7) & emptySquares; Bitboard b2 = shift(b1 & TRank3BB) & emptySquares; - if constexpr (Type == EVASIONS) // Consider only blocking squares + if constexpr (Type == EVASIONS) // Consider only blocking squares { b1 &= target; b2 &= target; @@ -87,21 +86,21 @@ namespace { // To make a quiet check, you either make a direct check by pushing a pawn // or push a blocker pawn that is not on the same file as the enemy king. // Discovered check promotion has been already generated amongst the captures. - Square ksq = pos.square(Them); + Square ksq = pos.square(Them); Bitboard dcCandidatePawns = pos.blockers_for_king(Them) & ~file_bb(ksq); - b1 &= pawn_attacks_bb(Them, ksq) | shift< Up>(dcCandidatePawns); - b2 &= pawn_attacks_bb(Them, ksq) | shift(dcCandidatePawns); + b1 &= pawn_attacks_bb(Them, ksq) | shift(dcCandidatePawns); + b2 &= pawn_attacks_bb(Them, ksq) | shift(dcCandidatePawns); } while (b1) { - Square to = pop_lsb(b1); + Square to = pop_lsb(b1); *moveList++ = make_move(to - Up, to); } while (b2) { - Square to = pop_lsb(b2); + Square to = pop_lsb(b2); *moveList++ = make_move(to - Up - Up, to); } } @@ -110,8 +109,8 @@ namespace { if (pawnsOn7) { Bitboard b1 = shift(pawnsOn7) & enemies; - Bitboard b2 = shift(pawnsOn7) & enemies; - Bitboard b3 = shift(pawnsOn7) & emptySquares; + Bitboard b2 = shift(pawnsOn7) & enemies; + Bitboard b3 = shift(pawnsOn7) & emptySquares; if constexpr (Type == EVASIONS) b3 &= target; @@ -123,24 +122,24 @@ namespace { moveList = make_promotions(moveList, pop_lsb(b2)); while (b3) - moveList = make_promotions(moveList, pop_lsb(b3)); + moveList = make_promotions(moveList, pop_lsb(b3)); } // Standard and en passant captures if constexpr (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) { Bitboard b1 = shift(pawnsNotOn7) & enemies; - Bitboard b2 = shift(pawnsNotOn7) & enemies; + Bitboard b2 = shift(pawnsNotOn7) & enemies; while (b1) { - Square to = pop_lsb(b1); + Square to = pop_lsb(b1); *moveList++ = make_move(to - UpRight, to); } while (b2) { - Square to = pop_lsb(b2); + Square to = pop_lsb(b2); *moveList++ = make_move(to - UpLeft, to); } @@ -162,11 +161,11 @@ namespace { } return moveList; - } +} - template - ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) { +template +ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) { static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()"); @@ -174,8 +173,8 @@ namespace { while (bb) { - Square from = pop_lsb(bb); - Bitboard b = attacks_bb(from, pos.pieces()) & target; + Square from = pop_lsb(bb); + Bitboard b = attacks_bb(from, pos.pieces()) & target; // To check, you either move freely a blocker or make a direct check. if (Checks && (Pt == QUEEN || !(pos.blockers_for_king(~Us) & from))) @@ -186,31 +185,31 @@ namespace { } return moveList; - } +} - template - ExtMove* generate_all(const Position& pos, ExtMove* moveList) { +template +ExtMove* generate_all(const Position& pos, ExtMove* moveList) { static_assert(Type != LEGAL, "Unsupported type in generate_all()"); - constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantiations - const Square ksq = pos.square(Us); - Bitboard target; + constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantiations + const Square ksq = pos.square(Us); + Bitboard target; // Skip generating non-king moves when in double check if (Type != EVASIONS || !more_than_one(pos.checkers())) { - target = Type == EVASIONS ? between_bb(ksq, lsb(pos.checkers())) - : Type == NON_EVASIONS ? ~pos.pieces( Us) - : Type == CAPTURES ? pos.pieces(~Us) - : ~pos.pieces( ); // QUIETS || QUIET_CHECKS + target = Type == EVASIONS ? between_bb(ksq, lsb(pos.checkers())) + : Type == NON_EVASIONS ? ~pos.pieces(Us) + : Type == CAPTURES ? pos.pieces(~Us) + : ~pos.pieces(); // QUIETS || QUIET_CHECKS moveList = generate_pawn_moves(pos, moveList, target); moveList = generate_moves(pos, moveList, target); moveList = generate_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); } if (!Checks || pos.blockers_for_king(~Us) & ksq) @@ -223,15 +222,15 @@ namespace { *moveList++ = make_move(ksq, pop_lsb(b)); if ((Type == QUIETS || Type == NON_EVASIONS) && pos.can_castle(Us & ANY_CASTLING)) - for (CastlingRights cr : { Us & KING_SIDE, Us & QUEEN_SIDE } ) + for (CastlingRights cr : {Us & KING_SIDE, Us & QUEEN_SIDE}) if (!pos.castling_impeded(cr) && pos.can_castle(cr)) *moveList++ = make(ksq, pos.castling_rook_square(cr)); } return moveList; - } +} -} // namespace +} // namespace // Generates all pseudo-legal captures plus queen promotions @@ -246,13 +245,13 @@ namespace { template ExtMove* generate(const Position& pos, ExtMove* moveList) { - static_assert(Type != LEGAL, "Unsupported type in generate()"); - assert((Type == EVASIONS) == bool(pos.checkers())); + static_assert(Type != LEGAL, "Unsupported type in generate()"); + assert((Type == EVASIONS) == bool(pos.checkers())); - Color us = pos.side_to_move(); + Color us = pos.side_to_move(); - return us == WHITE ? generate_all(pos, moveList) - : generate_all(pos, moveList); + return us == WHITE ? generate_all(pos, moveList) + : generate_all(pos, moveList); } // Explicit template instantiations @@ -268,21 +267,21 @@ template ExtMove* generate(const Position&, ExtMove*); template<> ExtMove* generate(const Position& pos, ExtMove* moveList) { - Color us = pos.side_to_move(); - Bitboard pinned = pos.blockers_for_king(us) & pos.pieces(us); - Square ksq = pos.square(us); - ExtMove* cur = moveList; - - moveList = pos.checkers() ? generate(pos, moveList) - : generate(pos, moveList); - while (cur != moveList) - if ( ((pinned & from_sq(*cur)) || from_sq(*cur) == ksq || type_of(*cur) == EN_PASSANT) - && !pos.legal(*cur)) - *cur = (--moveList)->move; - else - ++cur; - - return moveList; + Color us = pos.side_to_move(); + Bitboard pinned = pos.blockers_for_king(us) & pos.pieces(us); + Square ksq = pos.square(us); + ExtMove* cur = moveList; + + moveList = + pos.checkers() ? generate(pos, moveList) : generate(pos, moveList); + while (cur != moveList) + if (((pinned & from_sq(*cur)) || from_sq(*cur) == ksq || type_of(*cur) == EN_PASSANT) + && !pos.legal(*cur)) + *cur = (--moveList)->move; + else + ++cur; + + return moveList; } -} // namespace Stockfish +} // namespace Stockfish diff --git a/src/movegen.h b/src/movegen.h index e913a13ea13..9a39d1c50ea 100644 --- a/src/movegen.h +++ b/src/movegen.h @@ -19,7 +19,7 @@ #ifndef MOVEGEN_H_INCLUDED #define MOVEGEN_H_INCLUDED -#include // IWYU pragma: keep +#include // IWYU pragma: keep #include #include "types.h" @@ -29,29 +29,27 @@ namespace Stockfish { class Position; enum GenType { - CAPTURES, - QUIETS, - QUIET_CHECKS, - EVASIONS, - NON_EVASIONS, - LEGAL + CAPTURES, + QUIETS, + QUIET_CHECKS, + EVASIONS, + NON_EVASIONS, + LEGAL }; struct ExtMove { - Move move; - int value; + Move move; + int value; - operator Move() const { return move; } - void operator=(Move m) { move = m; } + operator Move() const { return move; } + void operator=(Move m) { move = m; } - // Inhibit unwanted implicit conversions to Move - // with an ambiguity that yields to a compile error. - operator float() const = delete; + // Inhibit unwanted implicit conversions to Move + // with an ambiguity that yields to a compile error. + operator float() const = delete; }; -inline bool operator<(const ExtMove& f, const ExtMove& s) { - return f.value < s.value; -} +inline bool operator<(const ExtMove& f, const ExtMove& s) { return f.value < s.value; } template ExtMove* generate(const Position& pos, ExtMove* moveList); @@ -62,18 +60,17 @@ ExtMove* generate(const Position& pos, ExtMove* moveList); template struct MoveList { - explicit MoveList(const Position& pos) : last(generate(pos, moveList)) {} - const ExtMove* begin() const { return moveList; } - const ExtMove* end() const { return last; } - size_t size() const { return last - moveList; } - bool contains(Move move) const { - return std::find(begin(), end(), move) != end(); - } + explicit MoveList(const Position& pos) : + last(generate(pos, moveList)) {} + const ExtMove* begin() const { return moveList; } + const ExtMove* end() const { return last; } + size_t size() const { return last - moveList; } + bool contains(Move move) const { return std::find(begin(), end(), move) != end(); } -private: - ExtMove moveList[MAX_MOVES], *last; + private: + ExtMove moveList[MAX_MOVES], *last; }; -} // namespace Stockfish +} // namespace Stockfish -#endif // #ifndef MOVEGEN_H_INCLUDED +#endif // #ifndef MOVEGEN_H_INCLUDED diff --git a/src/movepick.cpp b/src/movepick.cpp index 5bb0fd6c274..41ad0dd6e8d 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -30,29 +30,50 @@ namespace Stockfish { namespace { - enum Stages { - MAIN_TT, CAPTURE_INIT, GOOD_CAPTURE, REFUTATION, QUIET_INIT, QUIET, BAD_CAPTURE, - EVASION_TT, EVASION_INIT, EVASION, - PROBCUT_TT, PROBCUT_INIT, PROBCUT, - QSEARCH_TT, QCAPTURE_INIT, QCAPTURE, QCHECK_INIT, QCHECK - }; - - // partial_insertion_sort() sorts moves in descending order up to and including - // a given limit. The order of moves smaller than the limit is left unspecified. - void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) { +enum Stages { + // generate main search moves + MAIN_TT, + CAPTURE_INIT, + GOOD_CAPTURE, + REFUTATION, + QUIET_INIT, + QUIET, + BAD_CAPTURE, + + // generate evasion moves + EVASION_TT, + EVASION_INIT, + EVASION, + + // generate probcut moves + PROBCUT_TT, + PROBCUT_INIT, + PROBCUT, + + // generate qsearch moves + QSEARCH_TT, + QCAPTURE_INIT, + QCAPTURE, + QCHECK_INIT, + QCHECK +}; + +// partial_insertion_sort() sorts moves in descending order up to and including +// a given limit. The order of moves smaller than the limit is left unspecified. +void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) { for (ExtMove *sortedEnd = begin, *p = begin + 1; p < end; ++p) if (p->value >= limit) { ExtMove tmp = *p, *q; - *p = *++sortedEnd; + *p = *++sortedEnd; for (q = sortedEnd; q != begin && *(q - 1) < tmp; --q) *q = *(q - 1); *q = tmp; } - } +} -} // namespace +} // namespace // Constructors of the MovePicker class. As arguments, we pass information @@ -62,44 +83,57 @@ namespace { // move ordering is at the current node. // MovePicker constructor for the main search -MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, - const CapturePieceToHistory* cph, - const PieceToHistory** ch, - Move cm, - const Move* killers) - : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), - ttMove(ttm), refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d) -{ - assert(d > 0); - - stage = (pos.checkers() ? EVASION_TT : MAIN_TT) + - !(ttm && pos.pseudo_legal(ttm)); +MovePicker::MovePicker(const Position& p, + Move ttm, + Depth d, + const ButterflyHistory* mh, + const CapturePieceToHistory* cph, + const PieceToHistory** ch, + Move cm, + const Move* killers) : + pos(p), + mainHistory(mh), + captureHistory(cph), + continuationHistory(ch), + ttMove(ttm), + refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, + depth(d) { + assert(d > 0); + + stage = (pos.checkers() ? EVASION_TT : MAIN_TT) + !(ttm && pos.pseudo_legal(ttm)); } // MovePicker constructor for quiescence search -MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, - const CapturePieceToHistory* cph, - const PieceToHistory** ch, - Square rs) - : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), ttMove(ttm), recaptureSquare(rs), depth(d) -{ - assert(d <= 0); - - stage = (pos.checkers() ? EVASION_TT : QSEARCH_TT) + - !( ttm - && pos.pseudo_legal(ttm)); +MovePicker::MovePicker(const Position& p, + Move ttm, + Depth d, + const ButterflyHistory* mh, + const CapturePieceToHistory* cph, + const PieceToHistory** ch, + Square rs) : + pos(p), + mainHistory(mh), + captureHistory(cph), + continuationHistory(ch), + ttMove(ttm), + recaptureSquare(rs), + depth(d) { + assert(d <= 0); + + stage = (pos.checkers() ? EVASION_TT : QSEARCH_TT) + !(ttm && pos.pseudo_legal(ttm)); } // MovePicker constructor for ProbCut: we generate captures with SEE greater // than or equal to the given threshold. -MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph) - : pos(p), captureHistory(cph), ttMove(ttm), threshold(th) -{ - assert(!pos.checkers()); - - stage = PROBCUT_TT + !(ttm && pos.capture_stage(ttm) - && pos.pseudo_legal(ttm) - && pos.see_ge(ttm, threshold)); +MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph) : + pos(p), + captureHistory(cph), + ttMove(ttm), + threshold(th) { + assert(!pos.checkers()); + + stage = PROBCUT_TT + + !(ttm && pos.capture_stage(ttm) && pos.pseudo_legal(ttm) && pos.see_ge(ttm, threshold)); } // MovePicker::score() assigns a numerical value to each move in a list, used @@ -108,76 +142,78 @@ MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePiece template void MovePicker::score() { - static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type"); - - [[maybe_unused]] Bitboard threatenedByPawn, threatenedByMinor, threatenedByRook, threatenedPieces; - if constexpr (Type == QUIETS) - { - Color us = pos.side_to_move(); - - threatenedByPawn = pos.attacks_by(~us); - threatenedByMinor = pos.attacks_by(~us) | pos.attacks_by(~us) | threatenedByPawn; - threatenedByRook = pos.attacks_by(~us) | threatenedByMinor; - - // Pieces threatened by pieces of lesser material value - threatenedPieces = (pos.pieces(us, QUEEN) & threatenedByRook) - | (pos.pieces(us, ROOK) & threatenedByMinor) - | (pos.pieces(us, KNIGHT, BISHOP) & threatenedByPawn); - } - - for (auto& m : *this) - if constexpr (Type == CAPTURES) - m.value = (7 * int(PieceValue[pos.piece_on(to_sq(m))]) - + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]) / 16; - - else if constexpr (Type == QUIETS) - { - Piece pc = pos.moved_piece(m); - PieceType pt = type_of(pos.moved_piece(m)); - Square from = from_sq(m); - Square to = to_sq(m); - - // histories - m.value = 2 * (*mainHistory)[pos.side_to_move()][from_to(m)]; - m.value += 2 * (*continuationHistory[0])[pc][to]; - m.value += (*continuationHistory[1])[pc][to]; - m.value += (*continuationHistory[2])[pc][to] / 4; - m.value += (*continuationHistory[3])[pc][to]; - m.value += (*continuationHistory[5])[pc][to]; - - // bonus for checks - m.value += bool(pos.check_squares(pt) & to) * 16384; - - // bonus for escaping from capture - m.value += threatenedPieces & from ? - (pt == QUEEN && !(to & threatenedByRook) ? 50000 - : pt == ROOK && !(to & threatenedByMinor) ? 25000 - : !(to & threatenedByPawn) ? 15000 - : 0 ) - : 0 ; - - // malus for putting piece en prise - m.value -= !(threatenedPieces & from) ? - (pt == QUEEN ? bool(to & threatenedByRook) * 50000 - + bool(to & threatenedByMinor) * 10000 - + bool(to & threatenedByPawn) * 20000 - : pt == ROOK ? bool(to & threatenedByMinor) * 25000 - + bool(to & threatenedByPawn) * 10000 - : pt != PAWN ? bool(to & threatenedByPawn) * 15000 - : 0 ) - : 0 ; - } - - else // Type == EVASIONS - { - if (pos.capture_stage(m)) - m.value = PieceValue[pos.piece_on(to_sq(m))] - - Value(type_of(pos.moved_piece(m))) - + (1 << 28); - else - m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] - + (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]; - } + static_assert(Type == CAPTURES || Type == QUIETS || Type == EVASIONS, "Wrong type"); + + [[maybe_unused]] Bitboard threatenedByPawn, threatenedByMinor, threatenedByRook, + threatenedPieces; + if constexpr (Type == QUIETS) + { + Color us = pos.side_to_move(); + + threatenedByPawn = pos.attacks_by(~us); + threatenedByMinor = + pos.attacks_by(~us) | pos.attacks_by(~us) | threatenedByPawn; + threatenedByRook = pos.attacks_by(~us) | threatenedByMinor; + + // Pieces threatened by pieces of lesser material value + threatenedPieces = (pos.pieces(us, QUEEN) & threatenedByRook) + | (pos.pieces(us, ROOK) & threatenedByMinor) + | (pos.pieces(us, KNIGHT, BISHOP) & threatenedByPawn); + } + + for (auto& m : *this) + if constexpr (Type == CAPTURES) + m.value = + (7 * int(PieceValue[pos.piece_on(to_sq(m))]) + + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]) + / 16; + + else if constexpr (Type == QUIETS) + { + Piece pc = pos.moved_piece(m); + PieceType pt = type_of(pos.moved_piece(m)); + Square from = from_sq(m); + Square to = to_sq(m); + + // histories + m.value = 2 * (*mainHistory)[pos.side_to_move()][from_to(m)]; + m.value += 2 * (*continuationHistory[0])[pc][to]; + m.value += (*continuationHistory[1])[pc][to]; + m.value += (*continuationHistory[2])[pc][to] / 4; + m.value += (*continuationHistory[3])[pc][to]; + m.value += (*continuationHistory[5])[pc][to]; + + // bonus for checks + m.value += bool(pos.check_squares(pt) & to) * 16384; + + // bonus for escaping from capture + m.value += threatenedPieces & from ? (pt == QUEEN && !(to & threatenedByRook) ? 50000 + : pt == ROOK && !(to & threatenedByMinor) ? 25000 + : !(to & threatenedByPawn) ? 15000 + : 0) + : 0; + + // malus for putting piece en prise + m.value -= !(threatenedPieces & from) + ? (pt == QUEEN ? bool(to & threatenedByRook) * 50000 + + bool(to & threatenedByMinor) * 10000 + + bool(to & threatenedByPawn) * 20000 + : pt == ROOK ? bool(to & threatenedByMinor) * 25000 + + bool(to & threatenedByPawn) * 10000 + : pt != PAWN ? bool(to & threatenedByPawn) * 15000 + : 0) + : 0; + } + + else // Type == EVASIONS + { + if (pos.capture_stage(m)) + m.value = PieceValue[pos.piece_on(to_sq(m))] - Value(type_of(pos.moved_piece(m))) + + (1 << 28); + else + m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] + + (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]; + } } // MovePicker::select() returns the next move satisfying a predicate function. @@ -185,17 +221,17 @@ void MovePicker::score() { template Move MovePicker::select(Pred filter) { - while (cur < endMoves) - { - if constexpr (T == Best) - std::swap(*cur, *std::max_element(cur, endMoves)); + while (cur < endMoves) + { + if constexpr (T == Best) + std::swap(*cur, *std::max_element(cur, endMoves)); - if (*cur != ttMove && filter()) - return *cur++; + if (*cur != ttMove && filter()) + return *cur++; - cur++; - } - return MOVE_NONE; + cur++; + } + return MOVE_NONE; } // MovePicker::next_move() is the most important method of the MovePicker class. It @@ -204,122 +240,126 @@ Move MovePicker::select(Pred filter) { Move MovePicker::next_move(bool skipQuiets) { top: - switch (stage) { - - case MAIN_TT: - case EVASION_TT: - case QSEARCH_TT: - case PROBCUT_TT: - ++stage; - return ttMove; - - case CAPTURE_INIT: - case PROBCUT_INIT: - case QCAPTURE_INIT: - cur = endBadCaptures = moves; - endMoves = generate(pos, cur); - - score(); - partial_insertion_sort(cur, endMoves, std::numeric_limits::min()); - ++stage; - goto top; - - case GOOD_CAPTURE: - if (select([&](){ - return pos.see_ge(*cur, Value(-cur->value)) ? - // Move losing capture to endBadCaptures to be tried later - true : (*endBadCaptures++ = *cur, false); })) - return *(cur - 1); - - // Prepare the pointers to loop over the refutations array - cur = std::begin(refutations); - endMoves = std::end(refutations); - - // If the countermove is the same as a killer, skip it - if ( refutations[0].move == refutations[2].move - || refutations[1].move == refutations[2].move) - --endMoves; - - ++stage; - [[fallthrough]]; - - case REFUTATION: - if (select([&](){ return *cur != MOVE_NONE - && !pos.capture_stage(*cur) - && pos.pseudo_legal(*cur); })) - return *(cur - 1); - ++stage; - [[fallthrough]]; - - case QUIET_INIT: - if (!skipQuiets) - { - cur = endBadCaptures; - endMoves = generate(pos, cur); - - score(); - partial_insertion_sort(cur, endMoves, -3000 * depth); - } - - ++stage; - [[fallthrough]]; - - case QUIET: - if ( !skipQuiets - && select([&](){return *cur != refutations[0].move - && *cur != refutations[1].move - && *cur != refutations[2].move;})) - return *(cur - 1); - - // Prepare the pointers to loop over the bad captures - cur = moves; - endMoves = endBadCaptures; - - ++stage; - [[fallthrough]]; - - case BAD_CAPTURE: - return select([](){ return true; }); - - case EVASION_INIT: - cur = moves; - endMoves = generate(pos, cur); - - score(); - ++stage; - [[fallthrough]]; - - case EVASION: - return select([](){ return true; }); - - case PROBCUT: - return select([&](){ return pos.see_ge(*cur, threshold); }); - - case QCAPTURE: - if (select([&](){ return depth > DEPTH_QS_RECAPTURES - || to_sq(*cur) == recaptureSquare; })) - return *(cur - 1); - - // If we did not find any move and we do not try checks, we have finished - if (depth != DEPTH_QS_CHECKS) - return MOVE_NONE; - - ++stage; - [[fallthrough]]; - - case QCHECK_INIT: - cur = moves; - endMoves = generate(pos, cur); - - ++stage; - [[fallthrough]]; - - case QCHECK: - return select([](){ return true; }); - } - - assert(false); - return MOVE_NONE; // Silence warning + switch (stage) + { + + case MAIN_TT : + case EVASION_TT : + case QSEARCH_TT : + case PROBCUT_TT : + ++stage; + return ttMove; + + case CAPTURE_INIT : + case PROBCUT_INIT : + case QCAPTURE_INIT : + cur = endBadCaptures = moves; + endMoves = generate(pos, cur); + + score(); + partial_insertion_sort(cur, endMoves, std::numeric_limits::min()); + ++stage; + goto top; + + case GOOD_CAPTURE : + if (select([&]() { + return pos.see_ge(*cur, Value(-cur->value)) + ? + // Move losing capture to endBadCaptures to be tried later + true + : (*endBadCaptures++ = *cur, false); + })) + return *(cur - 1); + + // Prepare the pointers to loop over the refutations array + cur = std::begin(refutations); + endMoves = std::end(refutations); + + // If the countermove is the same as a killer, skip it + if (refutations[0].move == refutations[2].move + || refutations[1].move == refutations[2].move) + --endMoves; + + ++stage; + [[fallthrough]]; + + case REFUTATION : + if (select([&]() { + return *cur != MOVE_NONE && !pos.capture_stage(*cur) && pos.pseudo_legal(*cur); + })) + return *(cur - 1); + ++stage; + [[fallthrough]]; + + case QUIET_INIT : + if (!skipQuiets) + { + cur = endBadCaptures; + endMoves = generate(pos, cur); + + score(); + partial_insertion_sort(cur, endMoves, -3000 * depth); + } + + ++stage; + [[fallthrough]]; + + case QUIET : + if (!skipQuiets && select([&]() { + return *cur != refutations[0].move && *cur != refutations[1].move + && *cur != refutations[2].move; + })) + return *(cur - 1); + + // Prepare the pointers to loop over the bad captures + cur = moves; + endMoves = endBadCaptures; + + ++stage; + [[fallthrough]]; + + case BAD_CAPTURE : + return select([]() { return true; }); + + case EVASION_INIT : + cur = moves; + endMoves = generate(pos, cur); + + score(); + ++stage; + [[fallthrough]]; + + case EVASION : + return select([]() { return true; }); + + case PROBCUT : + return select([&]() { return pos.see_ge(*cur, threshold); }); + + case QCAPTURE : + if (select( + [&]() { return depth > DEPTH_QS_RECAPTURES || to_sq(*cur) == recaptureSquare; })) + return *(cur - 1); + + // If we did not find any move and we do not try checks, we have finished + if (depth != DEPTH_QS_CHECKS) + return MOVE_NONE; + + ++stage; + [[fallthrough]]; + + case QCHECK_INIT : + cur = moves; + endMoves = generate(pos, cur); + + ++stage; + [[fallthrough]]; + + case QCHECK : + return select([]() { return true; }); + } + + assert(false); + return MOVE_NONE; // Silence warning } -} // namespace Stockfish +} // namespace Stockfish diff --git a/src/movepick.h b/src/movepick.h index 457defa525a..65e93dda6fe 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -24,7 +24,7 @@ #include #include #include -#include // IWYU pragma: keep +#include // IWYU pragma: keep #include "movegen.h" #include "types.h" @@ -39,22 +39,22 @@ class Position; template class StatsEntry { - T entry; + T entry; -public: - void operator=(const T& v) { entry = v; } - T* operator&() { return &entry; } - T* operator->() { return &entry; } - operator const T&() const { return entry; } + public: + void operator=(const T& v) { entry = v; } + T* operator&() { return &entry; } + T* operator->() { return &entry; } + operator const T&() const { return entry; } - void operator<<(int bonus) { - assert(abs(bonus) <= D); // Ensure range is [-D, D] - static_assert(D <= std::numeric_limits::max(), "D overflows T"); + void operator<<(int bonus) { + assert(abs(bonus) <= D); // Ensure range is [-D, D] + static_assert(D <= std::numeric_limits::max(), "D overflows T"); - entry += (bonus * D - entry * abs(bonus)) / (D * 5 / 4); + entry += (bonus * D - entry * abs(bonus)) / (D * 5 / 4); - assert(abs(entry) <= D); - } + assert(abs(entry) <= D); + } }; // Stats is a generic N-dimensional array used to store various statistics. @@ -62,28 +62,32 @@ class StatsEntry { // template parameter D limits the range of updates in [-D, D] when we update // values with the << operator, while the last parameters (Size and Sizes) // encode the dimensions of the array. -template -struct Stats : public std::array, Size> -{ - using stats = Stats; +template +struct Stats: public std::array, Size> { + using stats = Stats; - void fill(const T& v) { + void fill(const T& v) { - // For standard-layout 'this' points to the first struct member - assert(std::is_standard_layout_v); + // For standard-layout 'this' points to the first struct member + assert(std::is_standard_layout_v); - using entry = StatsEntry; - entry* p = reinterpret_cast(this); - std::fill(p, p + sizeof(*this) / sizeof(entry), v); - } + using entry = StatsEntry; + entry* p = reinterpret_cast(this); + std::fill(p, p + sizeof(*this) / sizeof(entry), v); + } }; -template -struct Stats : public std::array, Size> {}; +template +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 }; +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 @@ -117,42 +121,53 @@ using ContinuationHistory = Stats // likely to get a cut-off first. class MovePicker { - enum PickType { Next, Best }; - -public: - MovePicker(const MovePicker&) = delete; - MovePicker& operator=(const MovePicker&) = delete; - MovePicker(const Position&, Move, Depth, const ButterflyHistory*, - const CapturePieceToHistory*, - const PieceToHistory**, - Move, - const Move*); - MovePicker(const Position&, Move, Depth, const ButterflyHistory*, - const CapturePieceToHistory*, - const PieceToHistory**, - Square); - MovePicker(const Position&, Move, Value, const CapturePieceToHistory*); - Move next_move(bool skipQuiets = false); - -private: - template Move select(Pred); - template void score(); - ExtMove* begin() { return cur; } - ExtMove* end() { return endMoves; } - - const Position& pos; - const ButterflyHistory* mainHistory; - const CapturePieceToHistory* captureHistory; - const PieceToHistory** continuationHistory; - Move ttMove; - ExtMove refutations[3], *cur, *endMoves, *endBadCaptures; - int stage; - Square recaptureSquare; - Value threshold; - Depth depth; - ExtMove moves[MAX_MOVES]; + enum PickType { + Next, + Best + }; + + public: + MovePicker(const MovePicker&) = delete; + MovePicker& operator=(const MovePicker&) = delete; + MovePicker(const Position&, + Move, + Depth, + const ButterflyHistory*, + const CapturePieceToHistory*, + const PieceToHistory**, + Move, + const Move*); + MovePicker(const Position&, + Move, + Depth, + const ButterflyHistory*, + const CapturePieceToHistory*, + const PieceToHistory**, + Square); + MovePicker(const Position&, Move, Value, const CapturePieceToHistory*); + Move next_move(bool skipQuiets = false); + + private: + template + Move select(Pred); + template + void score(); + ExtMove* begin() { return cur; } + ExtMove* end() { return endMoves; } + + const Position& pos; + const ButterflyHistory* mainHistory; + const CapturePieceToHistory* captureHistory; + const PieceToHistory** continuationHistory; + Move ttMove; + ExtMove refutations[3], *cur, *endMoves, *endBadCaptures; + int stage; + Square recaptureSquare; + Value threshold; + Depth depth; + ExtMove moves[MAX_MOVES]; }; -} // namespace Stockfish +} // namespace Stockfish -#endif // #ifndef MOVEPICK_H_INCLUDED +#endif // #ifndef MOVEPICK_H_INCLUDED diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 1f821cf9a3b..679192d4710 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -39,136 +39,144 @@ namespace Stockfish::Eval::NNUE { - // Input feature converter - LargePagePtr featureTransformer; +// Input feature converter +LargePagePtr featureTransformer; - // Evaluation function - AlignedPtr network[LayerStacks]; +// Evaluation function +AlignedPtr network[LayerStacks]; - // Evaluation function file name - std::string fileName; - std::string netDescription; +// Evaluation function file name +std::string fileName; +std::string netDescription; - namespace Detail { +namespace Detail { - // Initialize the evaluation function parameters - template - void initialize(AlignedPtr& pointer) { +// Initialize the evaluation function parameters +template +void initialize(AlignedPtr& pointer) { pointer.reset(reinterpret_cast(std_aligned_alloc(alignof(T), sizeof(T)))); std::memset(pointer.get(), 0, sizeof(T)); - } +} - template - void initialize(LargePagePtr& pointer) { +template +void initialize(LargePagePtr& pointer) { - static_assert(alignof(T) <= 4096, "aligned_large_pages_alloc() may fail for such a big alignment requirement of T"); + static_assert(alignof(T) <= 4096, + "aligned_large_pages_alloc() may fail for such a big alignment requirement of T"); pointer.reset(reinterpret_cast(aligned_large_pages_alloc(sizeof(T)))); std::memset(pointer.get(), 0, sizeof(T)); - } +} - // Read evaluation function parameters - template - bool read_parameters(std::istream& stream, T& reference) { +// Read evaluation function parameters +template +bool read_parameters(std::istream& stream, T& reference) { std::uint32_t header; header = read_little_endian(stream); - if (!stream || header != T::get_hash_value()) return false; + if (!stream || header != T::get_hash_value()) + return false; return reference.read_parameters(stream); - } +} - // Write evaluation function parameters - template - bool write_parameters(std::ostream& stream, const T& reference) { +// Write evaluation function parameters +template +bool write_parameters(std::ostream& stream, const T& reference) { write_little_endian(stream, T::get_hash_value()); return reference.write_parameters(stream); - } +} - } // namespace Detail +} // namespace Detail - // Initialize the evaluation function parameters - static void initialize() { +// Initialize the evaluation function parameters +static void initialize() { Detail::initialize(featureTransformer); for (std::size_t i = 0; i < LayerStacks; ++i) - Detail::initialize(network[i]); - } + Detail::initialize(network[i]); +} - // Read network header - static bool read_header(std::istream& stream, std::uint32_t* hashValue, std::string* desc) - { +// Read network header +static bool read_header(std::istream& stream, std::uint32_t* hashValue, std::string* desc) { std::uint32_t version, size; - version = read_little_endian(stream); - *hashValue = read_little_endian(stream); - size = read_little_endian(stream); - if (!stream || version != Version) return false; + version = read_little_endian(stream); + *hashValue = read_little_endian(stream); + size = read_little_endian(stream); + if (!stream || version != Version) + return false; desc->resize(size); stream.read(&(*desc)[0], size); return !stream.fail(); - } +} - // Write network header - static bool write_header(std::ostream& stream, std::uint32_t hashValue, const std::string& desc) - { +// Write network header +static bool write_header(std::ostream& stream, std::uint32_t hashValue, const std::string& desc) { write_little_endian(stream, Version); write_little_endian(stream, hashValue); - write_little_endian(stream, (std::uint32_t)desc.size()); + write_little_endian(stream, (std::uint32_t) desc.size()); stream.write(&desc[0], desc.size()); return !stream.fail(); - } +} - // Read network parameters - static bool read_parameters(std::istream& stream) { +// Read network parameters +static bool read_parameters(std::istream& stream) { std::uint32_t hashValue; - if (!read_header(stream, &hashValue, &netDescription)) return false; - if (hashValue != HashValue) return false; - if (!Detail::read_parameters(stream, *featureTransformer)) return false; + if (!read_header(stream, &hashValue, &netDescription)) + return false; + if (hashValue != HashValue) + return false; + if (!Detail::read_parameters(stream, *featureTransformer)) + return false; for (std::size_t i = 0; i < LayerStacks; ++i) - if (!Detail::read_parameters(stream, *(network[i]))) return false; + if (!Detail::read_parameters(stream, *(network[i]))) + return false; return stream && stream.peek() == std::ios::traits_type::eof(); - } +} - // Write network parameters - static bool write_parameters(std::ostream& stream) { +// Write network parameters +static bool write_parameters(std::ostream& stream) { - if (!write_header(stream, HashValue, netDescription)) return false; - if (!Detail::write_parameters(stream, *featureTransformer)) return false; + if (!write_header(stream, HashValue, netDescription)) + return false; + if (!Detail::write_parameters(stream, *featureTransformer)) + return false; for (std::size_t i = 0; i < LayerStacks; ++i) - if (!Detail::write_parameters(stream, *(network[i]))) return false; + if (!Detail::write_parameters(stream, *(network[i]))) + return false; return bool(stream); - } +} - void hint_common_parent_position(const Position& pos) { +void hint_common_parent_position(const Position& pos) { featureTransformer->hint_common_access(pos); - } +} - // Evaluation function. Perform differential calculation. - Value evaluate(const Position& pos, bool adjusted, int* complexity) { +// Evaluation function. Perform differential calculation. +Value evaluate(const Position& pos, bool adjusted, int* complexity) { // We manually align the arrays on the stack because with gcc < 9.3 // overaligning stack variables with alignas() doesn't work correctly. constexpr uint64_t alignment = CacheLineSize; - constexpr int delta = 24; + constexpr int delta = 24; #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) - TransformedFeatureType transformedFeaturesUnaligned[ - FeatureTransformer::BufferSize + alignment / sizeof(TransformedFeatureType)]; + TransformedFeatureType + transformedFeaturesUnaligned[FeatureTransformer::BufferSize + + alignment / sizeof(TransformedFeatureType)]; auto* transformedFeatures = align_ptr_up(&transformedFeaturesUnaligned[0]); #else - alignas(alignment) - TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize]; + alignas(alignment) TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize]; #endif ASSERT_ALIGNED(transformedFeatures, alignment); - const int bucket = (pos.count() - 1) / 4; - const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket); + const int bucket = (pos.count() - 1) / 4; + const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket); const auto positional = network[bucket]->propagate(transformedFeatures); if (complexity) @@ -176,158 +184,164 @@ namespace Stockfish::Eval::NNUE { // Give more value to positional evaluation when adjusted flag is set if (adjusted) - return static_cast(((1024 - delta) * psqt + (1024 + delta) * positional) / (1024 * OutputScale)); + return static_cast(((1024 - delta) * psqt + (1024 + delta) * positional) + / (1024 * OutputScale)); else return static_cast((psqt + positional) / OutputScale); - } +} - struct NnueEvalTrace { +struct NnueEvalTrace { static_assert(LayerStacks == PSQTBuckets); - Value psqt[LayerStacks]; - Value positional[LayerStacks]; + Value psqt[LayerStacks]; + Value positional[LayerStacks]; std::size_t correctBucket; - }; +}; - static NnueEvalTrace trace_evaluate(const Position& pos) { +static NnueEvalTrace trace_evaluate(const Position& pos) { // We manually align the arrays on the stack because with gcc < 9.3 // overaligning stack variables with alignas() doesn't work correctly. constexpr uint64_t alignment = CacheLineSize; #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) - TransformedFeatureType transformedFeaturesUnaligned[ - FeatureTransformer::BufferSize + alignment / sizeof(TransformedFeatureType)]; + TransformedFeatureType + transformedFeaturesUnaligned[FeatureTransformer::BufferSize + + alignment / sizeof(TransformedFeatureType)]; auto* transformedFeatures = align_ptr_up(&transformedFeaturesUnaligned[0]); #else - alignas(alignment) - TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize]; + alignas(alignment) TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize]; #endif ASSERT_ALIGNED(transformedFeatures, alignment); NnueEvalTrace t{}; t.correctBucket = (pos.count() - 1) / 4; - for (IndexType bucket = 0; bucket < LayerStacks; ++bucket) { - const auto materialist = featureTransformer->transform(pos, transformedFeatures, bucket); - const auto positional = network[bucket]->propagate(transformedFeatures); + for (IndexType bucket = 0; bucket < LayerStacks; ++bucket) + { + const auto materialist = featureTransformer->transform(pos, transformedFeatures, bucket); + const auto positional = network[bucket]->propagate(transformedFeatures); - t.psqt[bucket] = static_cast( materialist / OutputScale ); - t.positional[bucket] = static_cast( positional / OutputScale ); + t.psqt[bucket] = static_cast(materialist / OutputScale); + t.positional[bucket] = static_cast(positional / OutputScale); } return t; - } +} - constexpr std::string_view PieceToChar(" PNBRQK pnbrqk"); +constexpr std::string_view PieceToChar(" PNBRQK pnbrqk"); - // format_cp_compact() converts a Value into (centi)pawns and writes it in a buffer. - // The buffer must have capacity for at least 5 chars. - static void format_cp_compact(Value v, char* buffer) { +// format_cp_compact() converts a Value into (centi)pawns and writes it in a buffer. +// The buffer must have capacity for at least 5 chars. +static void format_cp_compact(Value v, char* buffer) { buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' '); int cp = std::abs(UCI::to_cp(v)); if (cp >= 10000) { - buffer[1] = '0' + cp / 10000; cp %= 10000; - buffer[2] = '0' + cp / 1000; cp %= 1000; + buffer[1] = '0' + cp / 10000; + cp %= 10000; + buffer[2] = '0' + cp / 1000; + cp %= 1000; buffer[3] = '0' + cp / 100; buffer[4] = ' '; } else if (cp >= 1000) { - buffer[1] = '0' + cp / 1000; cp %= 1000; - buffer[2] = '0' + cp / 100; cp %= 100; + buffer[1] = '0' + cp / 1000; + cp %= 1000; + buffer[2] = '0' + cp / 100; + cp %= 100; buffer[3] = '.'; buffer[4] = '0' + cp / 10; } else { - buffer[1] = '0' + cp / 100; cp %= 100; + buffer[1] = '0' + cp / 100; + cp %= 100; buffer[2] = '.'; - buffer[3] = '0' + cp / 10; cp %= 10; + buffer[3] = '0' + cp / 10; + cp %= 10; buffer[4] = '0' + cp / 1; } - } +} - // format_cp_aligned_dot() converts a Value into pawns, always keeping two decimals - static void format_cp_aligned_dot(Value v, std::stringstream &stream) { +// format_cp_aligned_dot() converts a Value into pawns, always keeping two decimals +static void format_cp_aligned_dot(Value v, std::stringstream& stream) { const double pawns = std::abs(0.01 * UCI::to_cp(v)); - stream << (v < 0 ? '-' : v > 0 ? '+' : ' ') - << std::setiosflags(std::ios::fixed) - << std::setw(6) - << std::setprecision(2) - << pawns; - } + stream << (v < 0 ? '-' + : v > 0 ? '+' + : ' ') + << std::setiosflags(std::ios::fixed) << std::setw(6) << std::setprecision(2) << pawns; +} - // trace() returns a string with the value of each piece on a board, - // and a table for (PSQT, Layers) values bucket by bucket. - std::string trace(Position& pos) { +// trace() returns a string with the value of each piece on a board, +// and a table for (PSQT, Layers) values bucket by bucket. +std::string trace(Position& pos) { std::stringstream ss; - char board[3*8+1][8*8+2]; + char board[3 * 8 + 1][8 * 8 + 2]; std::memset(board, ' ', sizeof(board)); - for (int row = 0; row < 3*8+1; ++row) - board[row][8*8+1] = '\0'; + for (int row = 0; row < 3 * 8 + 1; ++row) + board[row][8 * 8 + 1] = '\0'; // A lambda to output one box of the board auto writeSquare = [&board](File file, Rank rank, Piece pc, Value value) { - - const int x = int(file) * 8; - const int y = (7 - int(rank)) * 3; - for (int i = 1; i < 8; ++i) - board[y][x+i] = board[y+3][x+i] = '-'; - for (int i = 1; i < 3; ++i) - board[y+i][x] = board[y+i][x+8] = '|'; - board[y][x] = board[y][x+8] = board[y+3][x+8] = board[y+3][x] = '+'; - if (pc != NO_PIECE) - board[y+1][x+4] = PieceToChar[pc]; - if (value != VALUE_NONE) - format_cp_compact(value, &board[y+2][x+2]); + const int x = int(file) * 8; + const int y = (7 - int(rank)) * 3; + for (int i = 1; i < 8; ++i) + board[y][x + i] = board[y + 3][x + i] = '-'; + for (int i = 1; i < 3; ++i) + board[y + i][x] = board[y + i][x + 8] = '|'; + board[y][x] = board[y][x + 8] = board[y + 3][x + 8] = board[y + 3][x] = '+'; + if (pc != NO_PIECE) + board[y + 1][x + 4] = PieceToChar[pc]; + if (value != VALUE_NONE) + format_cp_compact(value, &board[y + 2][x + 2]); }; // We estimate the value of each piece by doing a differential evaluation from // the current base eval, simulating the removal of the piece from its square. Value base = evaluate(pos); - base = pos.side_to_move() == WHITE ? base : -base; + base = pos.side_to_move() == WHITE ? base : -base; for (File f = FILE_A; f <= FILE_H; ++f) - for (Rank r = RANK_1; r <= RANK_8; ++r) - { - Square sq = make_square(f, r); - Piece pc = pos.piece_on(sq); - Value v = VALUE_NONE; - - if (pc != NO_PIECE && type_of(pc) != KING) + for (Rank r = RANK_1; r <= RANK_8; ++r) { - auto st = pos.state(); + Square sq = make_square(f, r); + Piece pc = pos.piece_on(sq); + Value v = VALUE_NONE; - pos.remove_piece(sq); - st->accumulator.computed[WHITE] = false; - st->accumulator.computed[BLACK] = false; + if (pc != NO_PIECE && type_of(pc) != KING) + { + auto st = pos.state(); - Value eval = evaluate(pos); - eval = pos.side_to_move() == WHITE ? eval : -eval; - v = base - eval; + pos.remove_piece(sq); + st->accumulator.computed[WHITE] = false; + st->accumulator.computed[BLACK] = false; - pos.put_piece(pc, sq); - st->accumulator.computed[WHITE] = false; - st->accumulator.computed[BLACK] = false; - } + Value eval = evaluate(pos); + eval = pos.side_to_move() == WHITE ? eval : -eval; + v = base - eval; + + pos.put_piece(pc, sq); + st->accumulator.computed[WHITE] = false; + st->accumulator.computed[BLACK] = false; + } - writeSquare(f, r, pc, v); - } + writeSquare(f, r, pc, v); + } ss << " NNUE derived piece values:\n"; - for (int row = 0; row < 3*8+1; ++row) + for (int row = 0; row < 3 * 8 + 1; ++row) ss << board[row] << '\n'; ss << '\n'; @@ -342,41 +356,47 @@ namespace Stockfish::Eval::NNUE { for (std::size_t bucket = 0; bucket < LayerStacks; ++bucket) { - ss << "| " << bucket << " "; - ss << " | "; format_cp_aligned_dot(t.psqt[bucket], ss); ss << " " - << " | "; format_cp_aligned_dot(t.positional[bucket], ss); ss << " " - << " | "; format_cp_aligned_dot(t.psqt[bucket] + t.positional[bucket], ss); ss << " " - << " |"; - if (bucket == t.correctBucket) - ss << " <-- this bucket is used"; - ss << '\n'; + ss << "| " << bucket << " "; + ss << " | "; + format_cp_aligned_dot(t.psqt[bucket], ss); + ss << " " + << " | "; + format_cp_aligned_dot(t.positional[bucket], ss); + ss << " " + << " | "; + format_cp_aligned_dot(t.psqt[bucket] + t.positional[bucket], ss); + ss << " " + << " |"; + if (bucket == t.correctBucket) + ss << " <-- this bucket is used"; + ss << '\n'; } ss << "+------------+------------+------------+------------+\n"; return ss.str(); - } +} - // Load eval, from a file stream or a memory stream - bool load_eval(std::string name, std::istream& stream) { +// Load eval, from a file stream or a memory stream +bool load_eval(std::string name, std::istream& stream) { initialize(); fileName = name; return read_parameters(stream); - } +} - // Save eval, to a file stream or a memory stream - bool save_eval(std::ostream& stream) { +// Save eval, to a file stream or a memory stream +bool save_eval(std::ostream& stream) { if (fileName.empty()) - return false; + return false; return write_parameters(stream); - } +} - // Save eval, to a file given by its name - bool save_eval(const std::optional& filename) { +// Save eval, to a file given by its name +bool save_eval(const std::optional& filename) { std::string actualFilename; std::string msg; @@ -387,23 +407,23 @@ namespace Stockfish::Eval::NNUE { { if (currentEvalFileName != EvalFileDefaultName) { - msg = "Failed to export a net. A non-embedded net can only be saved if the filename is specified"; + msg = + "Failed to export a net. A non-embedded net can only be saved if the filename is specified"; - sync_cout << msg << sync_endl; - return false; + sync_cout << msg << sync_endl; + return false; } actualFilename = EvalFileDefaultName; } std::ofstream stream(actualFilename, std::ios_base::binary); - bool saved = save_eval(stream); + bool saved = save_eval(stream); - msg = saved ? "Network saved successfully to " + actualFilename - : "Failed to export a net"; + msg = saved ? "Network saved successfully to " + actualFilename : "Failed to export a net"; sync_cout << msg << sync_endl; return saved; - } +} -} // namespace Stockfish::Eval::NNUE +} // namespace Stockfish::Eval::NNUE diff --git a/src/nnue/evaluate_nnue.h b/src/nnue/evaluate_nnue.h index 8faec6cce43..6edc212f4d7 100644 --- a/src/nnue/evaluate_nnue.h +++ b/src/nnue/evaluate_nnue.h @@ -32,48 +32,48 @@ #include "nnue_feature_transformer.h" namespace Stockfish { - class Position; - enum Value : int; +class Position; +enum Value : int; } namespace Stockfish::Eval::NNUE { - // Hash value of evaluation function structure - constexpr std::uint32_t HashValue = - FeatureTransformer::get_hash_value() ^ Network::get_hash_value(); +// Hash value of evaluation function structure +constexpr std::uint32_t HashValue = + FeatureTransformer::get_hash_value() ^ Network::get_hash_value(); - // Deleter for automating release of memory area - template - struct AlignedDeleter { +// Deleter for automating release of memory area +template +struct AlignedDeleter { void operator()(T* ptr) const { - ptr->~T(); - std_aligned_free(ptr); + ptr->~T(); + std_aligned_free(ptr); } - }; +}; - template - struct LargePageDeleter { +template +struct LargePageDeleter { void operator()(T* ptr) const { - ptr->~T(); - aligned_large_pages_free(ptr); + ptr->~T(); + aligned_large_pages_free(ptr); } - }; +}; - template - using AlignedPtr = std::unique_ptr>; +template +using AlignedPtr = std::unique_ptr>; - template - using LargePagePtr = std::unique_ptr>; +template +using LargePagePtr = std::unique_ptr>; - std::string trace(Position& pos); - Value evaluate(const Position& pos, bool adjusted = false, int* complexity = nullptr); - void hint_common_parent_position(const Position& pos); +std::string trace(Position& pos); +Value evaluate(const Position& pos, bool adjusted = false, int* complexity = nullptr); +void hint_common_parent_position(const Position& pos); - bool load_eval(std::string name, std::istream& stream); - bool save_eval(std::ostream& stream); - bool save_eval(const std::optional& filename); +bool load_eval(std::string name, std::istream& stream); +bool save_eval(std::ostream& stream); +bool save_eval(const std::optional& filename); } // namespace Stockfish::Eval::NNUE -#endif // #ifndef NNUE_EVALUATE_NNUE_H_INCLUDED +#endif // #ifndef NNUE_EVALUATE_NNUE_H_INCLUDED diff --git a/src/nnue/features/half_ka_v2_hm.cpp b/src/nnue/features/half_ka_v2_hm.cpp index 016934b8c4d..6c3fdfdb60b 100644 --- a/src/nnue/features/half_ka_v2_hm.cpp +++ b/src/nnue/features/half_ka_v2_hm.cpp @@ -27,61 +27,60 @@ namespace Stockfish::Eval::NNUE::Features { - // Index of a feature for a given king position and another piece on some square - template - inline IndexType HalfKAv2_hm::make_index(Square s, Piece pc, Square ksq) { - return IndexType((int(s) ^ OrientTBL[Perspective][ksq]) + PieceSquareIndex[Perspective][pc] + KingBuckets[Perspective][ksq]); - } - - // Get a list of indices for active features - template - void HalfKAv2_hm::append_active_indices( - const Position& pos, - IndexList& active - ) { - Square ksq = pos.square(Perspective); - Bitboard bb = pos.pieces(); +// Index of a feature for a given king position and another piece on some square +template +inline IndexType HalfKAv2_hm::make_index(Square s, Piece pc, Square ksq) { + return IndexType((int(s) ^ OrientTBL[Perspective][ksq]) + PieceSquareIndex[Perspective][pc] + + KingBuckets[Perspective][ksq]); +} + +// Get a list of indices for active features +template +void HalfKAv2_hm::append_active_indices(const Position& pos, IndexList& active) { + Square ksq = pos.square(Perspective); + Bitboard bb = pos.pieces(); while (bb) { - Square s = pop_lsb(bb); - active.push_back(make_index(s, pos.piece_on(s), ksq)); + Square s = pop_lsb(bb); + active.push_back(make_index(s, pos.piece_on(s), ksq)); } - } - - // Explicit template instantiations - template void HalfKAv2_hm::append_active_indices(const Position& pos, IndexList& active); - template void HalfKAv2_hm::append_active_indices(const Position& pos, IndexList& active); - - // append_changed_indices() : get a list of indices for recently changed features - template - void HalfKAv2_hm::append_changed_indices( - Square ksq, - const DirtyPiece& dp, - IndexList& removed, - IndexList& added - ) { - for (int i = 0; i < dp.dirty_num; ++i) { - if (dp.from[i] != SQ_NONE) - removed.push_back(make_index(dp.from[i], dp.piece[i], ksq)); - if (dp.to[i] != SQ_NONE) - added.push_back(make_index(dp.to[i], dp.piece[i], ksq)); +} + +// Explicit template instantiations +template void HalfKAv2_hm::append_active_indices(const Position& pos, IndexList& active); +template void HalfKAv2_hm::append_active_indices(const Position& pos, IndexList& active); + +// append_changed_indices() : get a list of indices for recently changed features +template +void HalfKAv2_hm::append_changed_indices(Square ksq, + const DirtyPiece& dp, + IndexList& removed, + IndexList& added) { + for (int i = 0; i < dp.dirty_num; ++i) + { + if (dp.from[i] != SQ_NONE) + removed.push_back(make_index(dp.from[i], dp.piece[i], ksq)); + if (dp.to[i] != SQ_NONE) + added.push_back(make_index(dp.to[i], dp.piece[i], ksq)); } - } +} - // Explicit template instantiations - template void HalfKAv2_hm::append_changed_indices(Square ksq, const DirtyPiece& dp, IndexList& removed, IndexList& added); - template void HalfKAv2_hm::append_changed_indices(Square ksq, const DirtyPiece& dp, IndexList& removed, IndexList& added); +// Explicit template instantiations +template void HalfKAv2_hm::append_changed_indices(Square ksq, + const DirtyPiece& dp, + IndexList& removed, + IndexList& added); +template void HalfKAv2_hm::append_changed_indices(Square ksq, + const DirtyPiece& dp, + IndexList& removed, + IndexList& added); - int HalfKAv2_hm::update_cost(const StateInfo* st) { - return st->dirtyPiece.dirty_num; - } +int HalfKAv2_hm::update_cost(const StateInfo* st) { return st->dirtyPiece.dirty_num; } - int HalfKAv2_hm::refresh_cost(const Position& pos) { - return pos.count(); - } +int HalfKAv2_hm::refresh_cost(const Position& pos) { return pos.count(); } - bool HalfKAv2_hm::requires_refresh(const StateInfo* st, Color perspective) { +bool HalfKAv2_hm::requires_refresh(const StateInfo* st, Color perspective) { return st->dirtyPiece.piece[0] == make_piece(perspective, KING); - } +} } // namespace Stockfish::Eval::NNUE::Features diff --git a/src/nnue/features/half_ka_v2_hm.h b/src/nnue/features/half_ka_v2_hm.h index 9da1cc05531..540ff895a5a 100644 --- a/src/nnue/features/half_ka_v2_hm.h +++ b/src/nnue/features/half_ka_v2_hm.h @@ -28,41 +28,40 @@ #include "../nnue_common.h" namespace Stockfish { - struct StateInfo; - class Position; +struct StateInfo; +class Position; } namespace Stockfish::Eval::NNUE::Features { - // Feature HalfKAv2_hm: Combination of the position of own king - // and the position of pieces. Position mirrored such that king always on e..h files. - class HalfKAv2_hm { +// Feature HalfKAv2_hm: Combination of the position of own king +// and the position of pieces. Position mirrored such that king always on e..h files. +class HalfKAv2_hm { // unique number for each piece type on each square enum { - PS_NONE = 0, - PS_W_PAWN = 0, - PS_B_PAWN = 1 * SQUARE_NB, - PS_W_KNIGHT = 2 * SQUARE_NB, - PS_B_KNIGHT = 3 * SQUARE_NB, - PS_W_BISHOP = 4 * SQUARE_NB, - PS_B_BISHOP = 5 * SQUARE_NB, - PS_W_ROOK = 6 * SQUARE_NB, - PS_B_ROOK = 7 * SQUARE_NB, - PS_W_QUEEN = 8 * SQUARE_NB, - PS_B_QUEEN = 9 * SQUARE_NB, - PS_KING = 10 * SQUARE_NB, - PS_NB = 11 * SQUARE_NB + PS_NONE = 0, + PS_W_PAWN = 0, + PS_B_PAWN = 1 * SQUARE_NB, + PS_W_KNIGHT = 2 * SQUARE_NB, + PS_B_KNIGHT = 3 * SQUARE_NB, + PS_W_BISHOP = 4 * SQUARE_NB, + PS_B_BISHOP = 5 * SQUARE_NB, + PS_W_ROOK = 6 * SQUARE_NB, + PS_B_ROOK = 7 * SQUARE_NB, + PS_W_QUEEN = 8 * SQUARE_NB, + PS_B_QUEEN = 9 * SQUARE_NB, + PS_KING = 10 * SQUARE_NB, + PS_NB = 11 * SQUARE_NB }; static constexpr IndexType PieceSquareIndex[COLOR_NB][PIECE_NB] = { // convention: W - us, B - them // viewed from other side, W and B are reversed - { PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE, - PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE }, - { PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE, - PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE } - }; + {PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE, + PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE}, + {PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE, + PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE}}; // Index of a feature for a given king position and another piece on some square template @@ -77,9 +76,10 @@ namespace Stockfish::Eval::NNUE::Features { // Number of feature dimensions static constexpr IndexType Dimensions = - static_cast(SQUARE_NB) * static_cast(PS_NB) / 2; + static_cast(SQUARE_NB) * static_cast(PS_NB) / 2; #define B(v) (v * PS_NB) + // clang-format off static constexpr int KingBuckets[COLOR_NB][SQUARE_NB] = { { B(28), B(29), B(30), B(31), B(31), B(30), B(29), B(28), B(24), B(25), B(26), B(27), B(27), B(26), B(25), B(24), @@ -98,8 +98,9 @@ namespace Stockfish::Eval::NNUE::Features { B(24), B(25), B(26), B(27), B(27), B(26), B(25), B(24), B(28), B(29), B(30), B(31), B(31), B(30), B(29), B(28) } }; + // clang-format on #undef B - + // clang-format off // Orient a square according to perspective (rotates by 180 for black) static constexpr int OrientTBL[COLOR_NB][SQUARE_NB] = { { SQ_H1, SQ_H1, SQ_H1, SQ_H1, SQ_A1, SQ_A1, SQ_A1, SQ_A1, @@ -119,25 +120,20 @@ namespace Stockfish::Eval::NNUE::Features { SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8, SQ_H8, SQ_H8, SQ_H8, SQ_H8, SQ_A8, SQ_A8, SQ_A8, SQ_A8 } }; + // clang-format on // Maximum number of simultaneously active features. static constexpr IndexType MaxActiveDimensions = 32; - using IndexList = ValueList; + using IndexList = ValueList; // Get a list of indices for active features template - static void append_active_indices( - const Position& pos, - IndexList& active); + static void append_active_indices(const Position& pos, IndexList& active); // Get a list of indices for recently changed features template - static void append_changed_indices( - Square ksq, - const DirtyPiece& dp, - IndexList& removed, - IndexList& added - ); + static void + append_changed_indices(Square ksq, const DirtyPiece& dp, IndexList& removed, IndexList& added); // Returns the cost of updating one perspective, the most costly one. // Assumes no refresh needed. @@ -147,8 +143,8 @@ namespace Stockfish::Eval::NNUE::Features { // Returns whether the change stored in this StateInfo means that // a full accumulator refresh is required. static bool requires_refresh(const StateInfo* st, Color perspective); - }; +}; } // namespace Stockfish::Eval::NNUE::Features -#endif // #ifndef NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED +#endif // #ifndef NNUE_FEATURES_HALF_KA_V2_HM_H_INCLUDED diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index fc65c34339f..3fba45ed87d 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -42,95 +42,102 @@ namespace Stockfish::Eval::NNUE::Layers { // Fallback implementation for older/other architectures. // Requires the input to be padded to at least 16 values. #if !defined(USE_SSSE3) - template - static void affine_transform_non_ssse3(std::int32_t* output, const std::int8_t* weights, const std::int32_t* biases, const std::uint8_t* input) - { -# if defined(USE_SSE2) || defined(USE_NEON_DOTPROD) || defined(USE_NEON) -# if defined(USE_SSE2) +template +static void affine_transform_non_ssse3(std::int32_t* output, + const std::int8_t* weights, + const std::int32_t* biases, + const std::uint8_t* input) { + #if defined(USE_SSE2) || defined(USE_NEON_DOTPROD) || defined(USE_NEON) + #if defined(USE_SSE2) // At least a multiple of 16, with SSE2. - constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 16) / 16; - const __m128i Zeros = _mm_setzero_si128(); - const auto inputVector = reinterpret_cast(input); - -# elif defined(USE_NEON_DOTPROD) - constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 16) / 16; - const auto inputVector = reinterpret_cast(input); - -# elif defined(USE_NEON) - constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 16) / 16; - const auto inputVector = reinterpret_cast(input); -# endif - - for (IndexType i = 0; i < OutputDimensions; ++i) { - const IndexType offset = i * PaddedInputDimensions; - -# if defined(USE_SSE2) - __m128i sumLo = _mm_cvtsi32_si128(biases[i]); - __m128i sumHi = Zeros; - const auto row = reinterpret_cast(&weights[offset]); - for (IndexType j = 0; j < NumChunks; ++j) { - __m128i row_j = _mm_load_si128(&row[j]); - __m128i input_j = _mm_load_si128(&inputVector[j]); - __m128i extendedRowLo = _mm_srai_epi16(_mm_unpacklo_epi8(row_j, row_j), 8); - __m128i extendedRowHi = _mm_srai_epi16(_mm_unpackhi_epi8(row_j, row_j), 8); - __m128i extendedInputLo = _mm_unpacklo_epi8(input_j, Zeros); - __m128i extendedInputHi = _mm_unpackhi_epi8(input_j, Zeros); - __m128i productLo = _mm_madd_epi16(extendedRowLo, extendedInputLo); - __m128i productHi = _mm_madd_epi16(extendedRowHi, extendedInputHi); - sumLo = _mm_add_epi32(sumLo, productLo); - sumHi = _mm_add_epi32(sumHi, productHi); - } - __m128i sum = _mm_add_epi32(sumLo, sumHi); - __m128i sumHigh_64 = _mm_shuffle_epi32(sum, _MM_SHUFFLE(1, 0, 3, 2)); - sum = _mm_add_epi32(sum, sumHigh_64); - __m128i sum_second_32 = _mm_shufflelo_epi16(sum, _MM_SHUFFLE(1, 0, 3, 2)); - sum = _mm_add_epi32(sum, sum_second_32); - output[i] = _mm_cvtsi128_si32(sum); - -# elif defined(USE_NEON_DOTPROD) - int32x4_t sum = {biases[i]}; - const auto row = reinterpret_cast(&weights[offset]); - for (IndexType j = 0; j < NumChunks; ++j) { - sum = vdotq_s32(sum, inputVector[j], row[j]); - } - output[i] = vaddvq_s32(sum); - -# elif defined(USE_NEON) - int32x4_t sum = {biases[i]}; - const auto row = reinterpret_cast(&weights[offset]); - for (IndexType j = 0; j < NumChunks; ++j) { - int16x8_t product = vmull_s8(inputVector[j * 2], row[j * 2]); - product = vmlal_s8(product, inputVector[j * 2 + 1], row[j * 2 + 1]); - sum = vpadalq_s16(sum, product); - } - output[i] = sum[0] + sum[1] + sum[2] + sum[3]; - -# endif + constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 16) / 16; + const __m128i Zeros = _mm_setzero_si128(); + const auto inputVector = reinterpret_cast(input); + + #elif defined(USE_NEON_DOTPROD) + constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 16) / 16; + const auto inputVector = reinterpret_cast(input); + + #elif defined(USE_NEON) + constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 16) / 16; + const auto inputVector = reinterpret_cast(input); + #endif + + for (IndexType i = 0; i < OutputDimensions; ++i) + { + const IndexType offset = i * PaddedInputDimensions; + + #if defined(USE_SSE2) + __m128i sumLo = _mm_cvtsi32_si128(biases[i]); + __m128i sumHi = Zeros; + const auto row = reinterpret_cast(&weights[offset]); + for (IndexType j = 0; j < NumChunks; ++j) + { + __m128i row_j = _mm_load_si128(&row[j]); + __m128i input_j = _mm_load_si128(&inputVector[j]); + __m128i extendedRowLo = _mm_srai_epi16(_mm_unpacklo_epi8(row_j, row_j), 8); + __m128i extendedRowHi = _mm_srai_epi16(_mm_unpackhi_epi8(row_j, row_j), 8); + __m128i extendedInputLo = _mm_unpacklo_epi8(input_j, Zeros); + __m128i extendedInputHi = _mm_unpackhi_epi8(input_j, Zeros); + __m128i productLo = _mm_madd_epi16(extendedRowLo, extendedInputLo); + __m128i productHi = _mm_madd_epi16(extendedRowHi, extendedInputHi); + sumLo = _mm_add_epi32(sumLo, productLo); + sumHi = _mm_add_epi32(sumHi, productHi); + } + __m128i sum = _mm_add_epi32(sumLo, sumHi); + __m128i sumHigh_64 = _mm_shuffle_epi32(sum, _MM_SHUFFLE(1, 0, 3, 2)); + sum = _mm_add_epi32(sum, sumHigh_64); + __m128i sum_second_32 = _mm_shufflelo_epi16(sum, _MM_SHUFFLE(1, 0, 3, 2)); + sum = _mm_add_epi32(sum, sum_second_32); + output[i] = _mm_cvtsi128_si32(sum); + + #elif defined(USE_NEON_DOTPROD) + int32x4_t sum = {biases[i]}; + const auto row = reinterpret_cast(&weights[offset]); + for (IndexType j = 0; j < NumChunks; ++j) + { + sum = vdotq_s32(sum, inputVector[j], row[j]); + } + output[i] = vaddvq_s32(sum); + + #elif defined(USE_NEON) + int32x4_t sum = {biases[i]}; + const auto row = reinterpret_cast(&weights[offset]); + for (IndexType j = 0; j < NumChunks; ++j) + { + int16x8_t product = vmull_s8(inputVector[j * 2], row[j * 2]); + product = vmlal_s8(product, inputVector[j * 2 + 1], row[j * 2 + 1]); + sum = vpadalq_s16(sum, product); + } + output[i] = sum[0] + sum[1] + sum[2] + sum[3]; + + #endif } -# else - std::memcpy(output, biases, sizeof(std::int32_t) * OutputDimensions); - - // Traverse weights in transpose order to take advantage of input sparsity - for (IndexType i = 0; i < InputDimensions; ++i) - if (input[i]) { - const std::int8_t* w = &weights[i]; - const int in = input[i]; - for (IndexType j = 0; j < OutputDimensions; ++j) - output[j] += w[j * PaddedInputDimensions] * in; - } -# endif - } + #else + std::memcpy(output, biases, sizeof(std::int32_t) * OutputDimensions); + + // Traverse weights in transpose order to take advantage of input sparsity + for (IndexType i = 0; i < InputDimensions; ++i) + if (input[i]) + { + const std::int8_t* w = &weights[i]; + const int in = input[i]; + for (IndexType j = 0; j < OutputDimensions; ++j) + output[j] += w[j * PaddedInputDimensions] * in; + } + #endif +} #endif - template - class AffineTransform { +template +class AffineTransform { public: // Input/output type - using InputType = std::uint8_t; + using InputType = std::uint8_t; using OutputType = std::int32_t; // Number of input/output dimensions - static constexpr IndexType InputDimensions = InDims; + static constexpr IndexType InputDimensions = InDims; static constexpr IndexType OutputDimensions = OutDims; static constexpr IndexType PaddedInputDimensions = @@ -142,175 +149,168 @@ namespace Stockfish::Eval::NNUE::Layers { // Hash value embedded in the evaluation file static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) { - std::uint32_t hashValue = 0xCC03DAE4u; - hashValue += OutputDimensions; - hashValue ^= prevHash >> 1; - hashValue ^= prevHash << 31; - return hashValue; + std::uint32_t hashValue = 0xCC03DAE4u; + hashValue += OutputDimensions; + hashValue ^= prevHash >> 1; + hashValue ^= prevHash << 31; + return hashValue; } - static constexpr IndexType get_weight_index_scrambled(IndexType i) - { - return - (i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 + - i / PaddedInputDimensions * 4 + - i % 4; + static constexpr IndexType get_weight_index_scrambled(IndexType i) { + return (i / 4) % (PaddedInputDimensions / 4) * OutputDimensions * 4 + + i / PaddedInputDimensions * 4 + i % 4; } - static constexpr IndexType get_weight_index(IndexType i) - { -#if defined (USE_SSSE3) - return get_weight_index_scrambled(i); + static constexpr IndexType get_weight_index(IndexType i) { +#if defined(USE_SSSE3) + return get_weight_index_scrambled(i); #else - return i; + return i; #endif } // Read network parameters bool read_parameters(std::istream& stream) { - read_little_endian(stream, biases, OutputDimensions); - for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) - weights[get_weight_index(i)] = read_little_endian(stream); + read_little_endian(stream, biases, OutputDimensions); + for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) + weights[get_weight_index(i)] = read_little_endian(stream); - return !stream.fail(); + return !stream.fail(); } // Write network parameters bool write_parameters(std::ostream& stream) const { - write_little_endian(stream, biases, OutputDimensions); + write_little_endian(stream, biases, OutputDimensions); - for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) - write_little_endian(stream, weights[get_weight_index(i)]); + for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) + write_little_endian(stream, weights[get_weight_index(i)]); - return !stream.fail(); + return !stream.fail(); } // Forward propagation - void propagate( - const InputType* input, OutputType* output) const { - -#if defined (USE_SSSE3) - - if constexpr (OutputDimensions > 1) - { - -#if defined (USE_AVX512) - using vec_t = __m512i; - #define vec_setzero _mm512_setzero_si512 - #define vec_set_32 _mm512_set1_epi32 - #define vec_add_dpbusd_32 Simd::m512_add_dpbusd_epi32 - #define vec_add_dpbusd_32x2 Simd::m512_add_dpbusd_epi32x2 - #define vec_hadd Simd::m512_hadd -#elif defined (USE_AVX2) - using vec_t = __m256i; - #define vec_setzero _mm256_setzero_si256 - #define vec_set_32 _mm256_set1_epi32 - #define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32 - #define vec_add_dpbusd_32x2 Simd::m256_add_dpbusd_epi32x2 - #define vec_hadd Simd::m256_hadd -#elif defined (USE_SSSE3) - using vec_t = __m128i; - #define vec_setzero _mm_setzero_si128 - #define vec_set_32 _mm_set1_epi32 - #define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32 - #define vec_add_dpbusd_32x2 Simd::m128_add_dpbusd_epi32x2 - #define vec_hadd Simd::m128_hadd -#endif - - static constexpr IndexType OutputSimdWidth = sizeof(vec_t) / sizeof(OutputType); - - static_assert(OutputDimensions % OutputSimdWidth == 0); + void propagate(const InputType* input, OutputType* output) const { - constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 8) / 4; - constexpr IndexType NumRegs = OutputDimensions / OutputSimdWidth; +#if defined(USE_SSSE3) - const auto input32 = reinterpret_cast(input); - const vec_t* biasvec = reinterpret_cast(biases); - vec_t acc[NumRegs]; - for (IndexType k = 0; k < NumRegs; ++k) - acc[k] = biasvec[k]; - - for (IndexType i = 0; i < NumChunks; i += 2) + if constexpr (OutputDimensions > 1) { - const vec_t in0 = vec_set_32(input32[i + 0]); - const vec_t in1 = vec_set_32(input32[i + 1]); - const auto col0 = reinterpret_cast(&weights[(i + 0) * OutputDimensions * 4]); - const auto col1 = reinterpret_cast(&weights[(i + 1) * OutputDimensions * 4]); - for (IndexType k = 0; k < NumRegs; ++k) - vec_add_dpbusd_32x2(acc[k], in0, col0[k], in1, col1[k]); - } - - vec_t* outptr = reinterpret_cast(output); - for (IndexType k = 0; k < NumRegs; ++k) - outptr[k] = acc[k]; - -# undef vec_setzero -# undef vec_set_32 -# undef vec_add_dpbusd_32 -# undef vec_add_dpbusd_32x2 -# undef vec_hadd - - } - else if constexpr (OutputDimensions == 1) - { - -// We cannot use AVX512 for the last layer because there's only 32 inputs and the buffer is not padded to 64 elements. -#if defined (USE_AVX2) - using vec_t = __m256i; - #define vec_setzero _mm256_setzero_si256 - #define vec_set_32 _mm256_set1_epi32 - #define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32 - #define vec_add_dpbusd_32x2 Simd::m256_add_dpbusd_epi32x2 - #define vec_hadd Simd::m256_hadd -#elif defined (USE_SSSE3) - using vec_t = __m128i; - #define vec_setzero _mm_setzero_si128 - #define vec_set_32 _mm_set1_epi32 - #define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32 - #define vec_add_dpbusd_32x2 Simd::m128_add_dpbusd_epi32x2 - #define vec_hadd Simd::m128_hadd -#endif - const auto inputVector = reinterpret_cast(input); - - static constexpr IndexType InputSimdWidth = sizeof(vec_t) / sizeof(InputType); - - static_assert(PaddedInputDimensions % InputSimdWidth == 0); - - constexpr IndexType NumChunks = PaddedInputDimensions / InputSimdWidth; - vec_t sum0 = vec_setzero(); - const auto row0 = reinterpret_cast(&weights[0]); - - for (int j = 0; j < int(NumChunks); ++j) - { - const vec_t in = inputVector[j]; - vec_add_dpbusd_32(sum0, in, row0[j]); + #if defined(USE_AVX512) + using vec_t = __m512i; + #define vec_setzero _mm512_setzero_si512 + #define vec_set_32 _mm512_set1_epi32 + #define vec_add_dpbusd_32 Simd::m512_add_dpbusd_epi32 + #define vec_add_dpbusd_32x2 Simd::m512_add_dpbusd_epi32x2 + #define vec_hadd Simd::m512_hadd + #elif defined(USE_AVX2) + using vec_t = __m256i; + #define vec_setzero _mm256_setzero_si256 + #define vec_set_32 _mm256_set1_epi32 + #define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32 + #define vec_add_dpbusd_32x2 Simd::m256_add_dpbusd_epi32x2 + #define vec_hadd Simd::m256_hadd + #elif defined(USE_SSSE3) + using vec_t = __m128i; + #define vec_setzero _mm_setzero_si128 + #define vec_set_32 _mm_set1_epi32 + #define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32 + #define vec_add_dpbusd_32x2 Simd::m128_add_dpbusd_epi32x2 + #define vec_hadd Simd::m128_hadd + #endif + + static constexpr IndexType OutputSimdWidth = sizeof(vec_t) / sizeof(OutputType); + + static_assert(OutputDimensions % OutputSimdWidth == 0); + + constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 8) / 4; + constexpr IndexType NumRegs = OutputDimensions / OutputSimdWidth; + + const auto input32 = reinterpret_cast(input); + const vec_t* biasvec = reinterpret_cast(biases); + vec_t acc[NumRegs]; + for (IndexType k = 0; k < NumRegs; ++k) + acc[k] = biasvec[k]; + + for (IndexType i = 0; i < NumChunks; i += 2) + { + const vec_t in0 = vec_set_32(input32[i + 0]); + const vec_t in1 = vec_set_32(input32[i + 1]); + const auto col0 = + reinterpret_cast(&weights[(i + 0) * OutputDimensions * 4]); + const auto col1 = + reinterpret_cast(&weights[(i + 1) * OutputDimensions * 4]); + for (IndexType k = 0; k < NumRegs; ++k) + vec_add_dpbusd_32x2(acc[k], in0, col0[k], in1, col1[k]); + } + + vec_t* outptr = reinterpret_cast(output); + for (IndexType k = 0; k < NumRegs; ++k) + outptr[k] = acc[k]; + + #undef vec_setzero + #undef vec_set_32 + #undef vec_add_dpbusd_32 + #undef vec_add_dpbusd_32x2 + #undef vec_hadd } - output[0] = vec_hadd(sum0, biases[0]); - -# undef vec_setzero -# undef vec_set_32 -# undef vec_add_dpbusd_32 -# undef vec_add_dpbusd_32x2 -# undef vec_hadd + else if constexpr (OutputDimensions == 1) + { - } + // We cannot use AVX512 for the last layer because there's only 32 inputs and the buffer is not padded to 64 elements. + #if defined(USE_AVX2) + using vec_t = __m256i; + #define vec_setzero _mm256_setzero_si256 + #define vec_set_32 _mm256_set1_epi32 + #define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32 + #define vec_add_dpbusd_32x2 Simd::m256_add_dpbusd_epi32x2 + #define vec_hadd Simd::m256_hadd + #elif defined(USE_SSSE3) + using vec_t = __m128i; + #define vec_setzero _mm_setzero_si128 + #define vec_set_32 _mm_set1_epi32 + #define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32 + #define vec_add_dpbusd_32x2 Simd::m128_add_dpbusd_epi32x2 + #define vec_hadd Simd::m128_hadd + #endif + + const auto inputVector = reinterpret_cast(input); + + static constexpr IndexType InputSimdWidth = sizeof(vec_t) / sizeof(InputType); + + static_assert(PaddedInputDimensions % InputSimdWidth == 0); + + constexpr IndexType NumChunks = PaddedInputDimensions / InputSimdWidth; + vec_t sum0 = vec_setzero(); + const auto row0 = reinterpret_cast(&weights[0]); + + for (int j = 0; j < int(NumChunks); ++j) + { + const vec_t in = inputVector[j]; + vec_add_dpbusd_32(sum0, in, row0[j]); + } + output[0] = vec_hadd(sum0, biases[0]); + + #undef vec_setzero + #undef vec_set_32 + #undef vec_add_dpbusd_32 + #undef vec_add_dpbusd_32x2 + #undef vec_hadd + } #else - // Use old implementation for the other architectures. - affine_transform_non_ssse3< - InputDimensions, - PaddedInputDimensions, - OutputDimensions>(output, weights, biases, input); + // Use old implementation for the other architectures. + affine_transform_non_ssse3( + output, weights, biases, input); #endif } private: - using BiasType = OutputType; + using BiasType = OutputType; using WeightType = std::int8_t; alignas(CacheLineSize) BiasType biases[OutputDimensions]; alignas(CacheLineSize) WeightType weights[OutputDimensions * PaddedInputDimensions]; - }; +}; } // namespace Stockfish::Eval::NNUE::Layers -#endif // #ifndef NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED +#endif // #ifndef NNUE_LAYERS_AFFINE_TRANSFORM_H_INCLUDED diff --git a/src/nnue/layers/affine_transform_sparse_input.h b/src/nnue/layers/affine_transform_sparse_input.h index 1dc42109844..6cb4d1a9347 100644 --- a/src/nnue/layers/affine_transform_sparse_input.h +++ b/src/nnue/layers/affine_transform_sparse_input.h @@ -38,104 +38,110 @@ namespace Stockfish::Eval::NNUE::Layers { #if (USE_SSSE3 | (USE_NEON >= 8)) - alignas(CacheLineSize) static inline const std::array, 256> lookup_indices = [](){ - std::array, 256> v{}; - for (unsigned i = 0; i < 256; ++i) - { - std::uint64_t j = i, k = 0; - while(j) - v[i][k++] = pop_lsb(j); - } - return v; +alignas(CacheLineSize) static inline const + std::array, 256> lookup_indices = []() { + std::array, 256> v{}; + for (unsigned i = 0; i < 256; ++i) + { + std::uint64_t j = i, k = 0; + while (j) + v[i][k++] = pop_lsb(j); + } + return v; }(); - // Find indices of nonzero numbers in an int32_t array - template - void find_nnz(const std::int32_t* input, std::uint16_t* out, IndexType& count_out) { -#if defined (USE_SSSE3) - #if defined (USE_AVX512) - using vec_t = __m512i; - #define vec_nnz(a) _mm512_cmpgt_epi32_mask(a, _mm512_setzero_si512()) - #elif defined (USE_AVX2) - using vec_t = __m256i; - #if defined(USE_VNNI) && !defined(USE_AVXVNNI) - #define vec_nnz(a) _mm256_cmpgt_epi32_mask(a, _mm256_setzero_si256()) - #else - #define vec_nnz(a) _mm256_movemask_ps(_mm256_castsi256_ps(_mm256_cmpgt_epi32(a, _mm256_setzero_si256()))) +// Find indices of nonzero numbers in an int32_t array +template +void find_nnz(const std::int32_t* input, std::uint16_t* out, IndexType& count_out) { + #if defined(USE_SSSE3) + #if defined(USE_AVX512) + using vec_t = __m512i; + #define vec_nnz(a) _mm512_cmpgt_epi32_mask(a, _mm512_setzero_si512()) + #elif defined(USE_AVX2) + using vec_t = __m256i; + #if defined(USE_VNNI) && !defined(USE_AVXVNNI) + #define vec_nnz(a) _mm256_cmpgt_epi32_mask(a, _mm256_setzero_si256()) + #else + #define vec_nnz(a) \ + _mm256_movemask_ps( \ + _mm256_castsi256_ps(_mm256_cmpgt_epi32(a, _mm256_setzero_si256()))) + #endif + #elif defined(USE_SSSE3) + using vec_t = __m128i; + #define vec_nnz(a) \ + _mm_movemask_ps(_mm_castsi128_ps(_mm_cmpgt_epi32(a, _mm_setzero_si128()))) #endif - #elif defined (USE_SSSE3) - using vec_t = __m128i; - #define vec_nnz(a) _mm_movemask_ps(_mm_castsi128_ps(_mm_cmpgt_epi32(a, _mm_setzero_si128()))) - #endif using vec128_t = __m128i; - #define vec128_zero _mm_setzero_si128() - #define vec128_set_16(a) _mm_set1_epi16(a) - #define vec128_load(a) _mm_load_si128(a) - #define vec128_storeu(a, b) _mm_storeu_si128(a, b) - #define vec128_add(a, b) _mm_add_epi16(a, b) -#elif defined (USE_NEON) - using vec_t = uint32x4_t; + #define vec128_zero _mm_setzero_si128() + #define vec128_set_16(a) _mm_set1_epi16(a) + #define vec128_load(a) _mm_load_si128(a) + #define vec128_storeu(a, b) _mm_storeu_si128(a, b) + #define vec128_add(a, b) _mm_add_epi16(a, b) + #elif defined(USE_NEON) + using vec_t = uint32x4_t; static const std::uint32_t Mask[4] = {1, 2, 4, 8}; - #define vec_nnz(a) vaddvq_u32(vandq_u32(vtstq_u32(a, a), vld1q_u32(Mask))) - using vec128_t = uint16x8_t; - #define vec128_zero vdupq_n_u16(0) - #define vec128_set_16(a) vdupq_n_u16(a) - #define vec128_load(a) vld1q_u16(reinterpret_cast(a)) - #define vec128_storeu(a, b) vst1q_u16(reinterpret_cast(a), b) - #define vec128_add(a, b) vaddq_u16(a, b) -#endif + #define vec_nnz(a) vaddvq_u32(vandq_u32(vtstq_u32(a, a), vld1q_u32(Mask))) + using vec128_t = uint16x8_t; + #define vec128_zero vdupq_n_u16(0) + #define vec128_set_16(a) vdupq_n_u16(a) + #define vec128_load(a) vld1q_u16(reinterpret_cast(a)) + #define vec128_storeu(a, b) vst1q_u16(reinterpret_cast(a), b) + #define vec128_add(a, b) vaddq_u16(a, b) + #endif constexpr IndexType InputSimdWidth = sizeof(vec_t) / sizeof(std::int32_t); // Inputs are processed InputSimdWidth at a time and outputs are processed 8 at a time so we process in chunks of max(InputSimdWidth, 8) - constexpr IndexType ChunkSize = std::max(InputSimdWidth, 8); - constexpr IndexType NumChunks = InputDimensions / ChunkSize; - constexpr IndexType InputsPerChunk = ChunkSize / InputSimdWidth; + constexpr IndexType ChunkSize = std::max(InputSimdWidth, 8); + constexpr IndexType NumChunks = InputDimensions / ChunkSize; + constexpr IndexType InputsPerChunk = ChunkSize / InputSimdWidth; constexpr IndexType OutputsPerChunk = ChunkSize / 8; - const auto inputVector = reinterpret_cast(input); - IndexType count = 0; - vec128_t base = vec128_zero; - const vec128_t increment = vec128_set_16(8); + const auto inputVector = reinterpret_cast(input); + IndexType count = 0; + vec128_t base = vec128_zero; + const vec128_t increment = vec128_set_16(8); for (IndexType i = 0; i < NumChunks; ++i) { - // bitmask of nonzero values in this chunk - unsigned nnz = 0; - for (IndexType j = 0; j < InputsPerChunk; ++j) - { - const vec_t inputChunk = inputVector[i * InputsPerChunk + j]; - nnz |= unsigned(vec_nnz(inputChunk)) << (j * InputSimdWidth); - } - for (IndexType j = 0; j < OutputsPerChunk; ++j) - { - const auto lookup = (nnz >> (j * 8)) & 0xFF; - const auto offsets = vec128_load(reinterpret_cast(&lookup_indices[lookup])); - vec128_storeu(reinterpret_cast(out + count), vec128_add(base, offsets)); - count += popcount(lookup); - base = vec128_add(base, increment); - } + // bitmask of nonzero values in this chunk + unsigned nnz = 0; + for (IndexType j = 0; j < InputsPerChunk; ++j) + { + const vec_t inputChunk = inputVector[i * InputsPerChunk + j]; + nnz |= unsigned(vec_nnz(inputChunk)) << (j * InputSimdWidth); + } + for (IndexType j = 0; j < OutputsPerChunk; ++j) + { + const auto lookup = (nnz >> (j * 8)) & 0xFF; + const auto offsets = + vec128_load(reinterpret_cast(&lookup_indices[lookup])); + vec128_storeu(reinterpret_cast(out + count), vec128_add(base, offsets)); + count += popcount(lookup); + base = vec128_add(base, increment); + } } count_out = count; - } -# undef vec_nnz -# undef vec128_zero -# undef vec128_set_16 -# undef vec128_load -# undef vec128_storeu -# undef vec128_add +} + #undef vec_nnz + #undef vec128_zero + #undef vec128_set_16 + #undef vec128_load + #undef vec128_storeu + #undef vec128_add #endif - // Sparse input implementation - template - class AffineTransformSparseInput { +// Sparse input implementation +template +class AffineTransformSparseInput { public: // Input/output type - using InputType = std::uint8_t; + using InputType = std::uint8_t; using OutputType = std::int32_t; // Number of input/output dimensions - static constexpr IndexType InputDimensions = InDims; + static constexpr IndexType InputDimensions = InDims; static constexpr IndexType OutputDimensions = OutDims; - static_assert(OutputDimensions % 16 == 0, "Only implemented for OutputDimensions divisible by 16."); + static_assert(OutputDimensions % 16 == 0, + "Only implemented for OutputDimensions divisible by 16."); static constexpr IndexType PaddedInputDimensions = ceil_to_multiple(InputDimensions, MaxSimdWidth); @@ -152,127 +158,121 @@ namespace Stockfish::Eval::NNUE::Layers { // Hash value embedded in the evaluation file static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) { - std::uint32_t hashValue = 0xCC03DAE4u; - hashValue += OutputDimensions; - hashValue ^= prevHash >> 1; - hashValue ^= prevHash << 31; - return hashValue; + std::uint32_t hashValue = 0xCC03DAE4u; + hashValue += OutputDimensions; + hashValue ^= prevHash >> 1; + hashValue ^= prevHash << 31; + return hashValue; } - static constexpr IndexType get_weight_index_scrambled(IndexType i) - { - return - (i / ChunkSize) % (PaddedInputDimensions / ChunkSize) * OutputDimensions * ChunkSize + - i / PaddedInputDimensions * ChunkSize + - i % ChunkSize; + static constexpr IndexType get_weight_index_scrambled(IndexType i) { + return (i / ChunkSize) % (PaddedInputDimensions / ChunkSize) * OutputDimensions * ChunkSize + + i / PaddedInputDimensions * ChunkSize + i % ChunkSize; } - static constexpr IndexType get_weight_index(IndexType i) - { + static constexpr IndexType get_weight_index(IndexType i) { #if (USE_SSSE3 | (USE_NEON >= 8)) - return get_weight_index_scrambled(i); + return get_weight_index_scrambled(i); #else - return i; + return i; #endif } // Read network parameters bool read_parameters(std::istream& stream) { - read_little_endian(stream, biases, OutputDimensions); - for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) - weights[get_weight_index(i)] = read_little_endian(stream); + read_little_endian(stream, biases, OutputDimensions); + for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) + weights[get_weight_index(i)] = read_little_endian(stream); - return !stream.fail(); + return !stream.fail(); } // Write network parameters bool write_parameters(std::ostream& stream) const { - write_little_endian(stream, biases, OutputDimensions); + write_little_endian(stream, biases, OutputDimensions); - for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) - write_little_endian(stream, weights[get_weight_index(i)]); + for (IndexType i = 0; i < OutputDimensions * PaddedInputDimensions; ++i) + write_little_endian(stream, weights[get_weight_index(i)]); - return !stream.fail(); + return !stream.fail(); } // Forward propagation - void propagate( - const InputType* input, OutputType* output) const { + void propagate(const InputType* input, OutputType* output) const { #if (USE_SSSE3 | (USE_NEON >= 8)) -#if defined (USE_AVX512) - using invec_t = __m512i; - using outvec_t = __m512i; - #define vec_set_32 _mm512_set1_epi32 - #define vec_add_dpbusd_32 Simd::m512_add_dpbusd_epi32 -#elif defined (USE_AVX2) - using invec_t = __m256i; - using outvec_t = __m256i; - #define vec_set_32 _mm256_set1_epi32 - #define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32 -#elif defined (USE_SSSE3) - using invec_t = __m128i; - using outvec_t = __m128i; - #define vec_set_32 _mm_set1_epi32 - #define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32 -#elif defined (USE_NEON_DOTPROD) - using invec_t = int8x16_t; - using outvec_t = int32x4_t; - #define vec_set_32(a) vreinterpretq_s8_u32(vdupq_n_u32(a)) - #define vec_add_dpbusd_32 Simd::dotprod_m128_add_dpbusd_epi32 -#elif defined (USE_NEON) - using invec_t = int8x16_t; - using outvec_t = int32x4_t; - #define vec_set_32(a) vreinterpretq_s8_u32(vdupq_n_u32(a)) - #define vec_add_dpbusd_32 Simd::neon_m128_add_dpbusd_epi32 -#endif - static constexpr IndexType OutputSimdWidth = sizeof(outvec_t) / sizeof(OutputType); - - constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 8) / ChunkSize; - constexpr IndexType NumRegs = OutputDimensions / OutputSimdWidth; - std::uint16_t nnz[NumChunks]; - IndexType count; + #if defined(USE_AVX512) + using invec_t = __m512i; + using outvec_t = __m512i; + #define vec_set_32 _mm512_set1_epi32 + #define vec_add_dpbusd_32 Simd::m512_add_dpbusd_epi32 + #elif defined(USE_AVX2) + using invec_t = __m256i; + using outvec_t = __m256i; + #define vec_set_32 _mm256_set1_epi32 + #define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32 + #elif defined(USE_SSSE3) + using invec_t = __m128i; + using outvec_t = __m128i; + #define vec_set_32 _mm_set1_epi32 + #define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32 + #elif defined(USE_NEON_DOTPROD) + using invec_t = int8x16_t; + using outvec_t = int32x4_t; + #define vec_set_32(a) vreinterpretq_s8_u32(vdupq_n_u32(a)) + #define vec_add_dpbusd_32 Simd::dotprod_m128_add_dpbusd_epi32 + #elif defined(USE_NEON) + using invec_t = int8x16_t; + using outvec_t = int32x4_t; + #define vec_set_32(a) vreinterpretq_s8_u32(vdupq_n_u32(a)) + #define vec_add_dpbusd_32 Simd::neon_m128_add_dpbusd_epi32 + #endif + static constexpr IndexType OutputSimdWidth = sizeof(outvec_t) / sizeof(OutputType); - const auto input32 = reinterpret_cast(input); + constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 8) / ChunkSize; + constexpr IndexType NumRegs = OutputDimensions / OutputSimdWidth; + std::uint16_t nnz[NumChunks]; + IndexType count; - // Find indices of nonzero 32bit blocks - find_nnz(input32, nnz, count); + const auto input32 = reinterpret_cast(input); - const outvec_t* biasvec = reinterpret_cast(biases); - outvec_t acc[NumRegs]; - for (IndexType k = 0; k < NumRegs; ++k) - acc[k] = biasvec[k]; + // Find indices of nonzero 32bit blocks + find_nnz(input32, nnz, count); - for (IndexType j = 0; j < count; ++j) - { - const auto i = nnz[j]; - const invec_t in = vec_set_32(input32[i]); - const auto col = reinterpret_cast(&weights[i * OutputDimensions * ChunkSize]); + const outvec_t* biasvec = reinterpret_cast(biases); + outvec_t acc[NumRegs]; for (IndexType k = 0; k < NumRegs; ++k) - vec_add_dpbusd_32(acc[k], in, col[k]); - } - - outvec_t* outptr = reinterpret_cast(output); - for (IndexType k = 0; k < NumRegs; ++k) - outptr[k] = acc[k]; -# undef vec_set_32 -# undef vec_add_dpbusd_32 + acc[k] = biasvec[k]; + + for (IndexType j = 0; j < count; ++j) + { + const auto i = nnz[j]; + const invec_t in = vec_set_32(input32[i]); + const auto col = + reinterpret_cast(&weights[i * OutputDimensions * ChunkSize]); + for (IndexType k = 0; k < NumRegs; ++k) + vec_add_dpbusd_32(acc[k], in, col[k]); + } + + outvec_t* outptr = reinterpret_cast(output); + for (IndexType k = 0; k < NumRegs; ++k) + outptr[k] = acc[k]; + #undef vec_set_32 + #undef vec_add_dpbusd_32 #else - // Use dense implementation for the other architectures. - affine_transform_non_ssse3< - InputDimensions, - PaddedInputDimensions, - OutputDimensions>(output, weights, biases, input); + // Use dense implementation for the other architectures. + affine_transform_non_ssse3( + output, weights, biases, input); #endif } private: - using BiasType = OutputType; + using BiasType = OutputType; using WeightType = std::int8_t; alignas(CacheLineSize) BiasType biases[OutputDimensions]; alignas(CacheLineSize) WeightType weights[OutputDimensions * PaddedInputDimensions]; - }; +}; } // namespace Stockfish::Eval::NNUE::Layers -#endif // #ifndef NNUE_LAYERS_AFFINE_TRANSFORM_SPARSE_INPUT_H_INCLUDED +#endif // #ifndef NNUE_LAYERS_AFFINE_TRANSFORM_SPARSE_INPUT_H_INCLUDED diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index 48cd6c69345..a3a0c1ede9e 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -29,136 +29,140 @@ namespace Stockfish::Eval::NNUE::Layers { - // Clipped ReLU - template - class ClippedReLU { +// Clipped ReLU +template +class ClippedReLU { public: // Input/output type - using InputType = std::int32_t; + using InputType = std::int32_t; using OutputType = std::uint8_t; // Number of input/output dimensions - static constexpr IndexType InputDimensions = InDims; + static constexpr IndexType InputDimensions = InDims; static constexpr IndexType OutputDimensions = InputDimensions; static constexpr IndexType PaddedOutputDimensions = - ceil_to_multiple(OutputDimensions, 32); + ceil_to_multiple(OutputDimensions, 32); using OutputBuffer = OutputType[PaddedOutputDimensions]; // Hash value embedded in the evaluation file static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) { - std::uint32_t hashValue = 0x538D24C7u; - hashValue += prevHash; - return hashValue; + std::uint32_t hashValue = 0x538D24C7u; + hashValue += prevHash; + return hashValue; } // Read network parameters - bool read_parameters(std::istream&) { - return true; - } + bool read_parameters(std::istream&) { return true; } // Write network parameters - bool write_parameters(std::ostream&) const { - return true; - } + bool write_parameters(std::ostream&) const { return true; } // Forward propagation - void propagate( - const InputType* input, OutputType* output) const { + void propagate(const InputType* input, OutputType* output) const { + +#if defined(USE_AVX2) + if constexpr (InputDimensions % SimdWidth == 0) + { + constexpr IndexType NumChunks = InputDimensions / SimdWidth; + const __m256i Zero = _mm256_setzero_si256(); + const __m256i Offsets = _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0); + const auto in = reinterpret_cast(input); + const auto out = reinterpret_cast<__m256i*>(output); + for (IndexType i = 0; i < NumChunks; ++i) + { + const __m256i words0 = + _mm256_srai_epi16(_mm256_packs_epi32(_mm256_load_si256(&in[i * 4 + 0]), + _mm256_load_si256(&in[i * 4 + 1])), + WeightScaleBits); + const __m256i words1 = + _mm256_srai_epi16(_mm256_packs_epi32(_mm256_load_si256(&in[i * 4 + 2]), + _mm256_load_si256(&in[i * 4 + 3])), + WeightScaleBits); + _mm256_store_si256( + &out[i], _mm256_permutevar8x32_epi32( + _mm256_max_epi8(_mm256_packs_epi16(words0, words1), Zero), Offsets)); + } + } + else + { + constexpr IndexType NumChunks = InputDimensions / (SimdWidth / 2); + const __m128i Zero = _mm_setzero_si128(); + const auto in = reinterpret_cast(input); + const auto out = reinterpret_cast<__m128i*>(output); + for (IndexType i = 0; i < NumChunks; ++i) + { + const __m128i words0 = _mm_srai_epi16( + _mm_packs_epi32(_mm_load_si128(&in[i * 4 + 0]), _mm_load_si128(&in[i * 4 + 1])), + WeightScaleBits); + const __m128i words1 = _mm_srai_epi16( + _mm_packs_epi32(_mm_load_si128(&in[i * 4 + 2]), _mm_load_si128(&in[i * 4 + 3])), + WeightScaleBits); + const __m128i packedbytes = _mm_packs_epi16(words0, words1); + _mm_store_si128(&out[i], _mm_max_epi8(packedbytes, Zero)); + } + } + constexpr IndexType Start = InputDimensions % SimdWidth == 0 + ? InputDimensions / SimdWidth * SimdWidth + : InputDimensions / (SimdWidth / 2) * (SimdWidth / 2); - #if defined(USE_AVX2) - if constexpr (InputDimensions % SimdWidth == 0) { +#elif defined(USE_SSE2) constexpr IndexType NumChunks = InputDimensions / SimdWidth; - const __m256i Zero = _mm256_setzero_si256(); - const __m256i Offsets = _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0); - const auto in = reinterpret_cast(input); - const auto out = reinterpret_cast<__m256i*>(output); - for (IndexType i = 0; i < NumChunks; ++i) { - const __m256i words0 = _mm256_srai_epi16(_mm256_packs_epi32( - _mm256_load_si256(&in[i * 4 + 0]), - _mm256_load_si256(&in[i * 4 + 1])), WeightScaleBits); - const __m256i words1 = _mm256_srai_epi16(_mm256_packs_epi32( - _mm256_load_si256(&in[i * 4 + 2]), - _mm256_load_si256(&in[i * 4 + 3])), WeightScaleBits); - _mm256_store_si256(&out[i], _mm256_permutevar8x32_epi32(_mm256_max_epi8( - _mm256_packs_epi16(words0, words1), Zero), Offsets)); - } - } else { - constexpr IndexType NumChunks = InputDimensions / (SimdWidth / 2); + + #ifdef USE_SSE41 const __m128i Zero = _mm_setzero_si128(); - const auto in = reinterpret_cast(input); + #else + const __m128i k0x80s = _mm_set1_epi8(-128); + #endif + + const auto in = reinterpret_cast(input); const auto out = reinterpret_cast<__m128i*>(output); - for (IndexType i = 0; i < NumChunks; ++i) { - const __m128i words0 = _mm_srai_epi16(_mm_packs_epi32( - _mm_load_si128(&in[i * 4 + 0]), - _mm_load_si128(&in[i * 4 + 1])), WeightScaleBits); - const __m128i words1 = _mm_srai_epi16(_mm_packs_epi32( - _mm_load_si128(&in[i * 4 + 2]), - _mm_load_si128(&in[i * 4 + 3])), WeightScaleBits); - const __m128i packedbytes = _mm_packs_epi16(words0, words1); - _mm_store_si128(&out[i], _mm_max_epi8(packedbytes, Zero)); + for (IndexType i = 0; i < NumChunks; ++i) + { + const __m128i words0 = _mm_srai_epi16( + _mm_packs_epi32(_mm_load_si128(&in[i * 4 + 0]), _mm_load_si128(&in[i * 4 + 1])), + WeightScaleBits); + const __m128i words1 = _mm_srai_epi16( + _mm_packs_epi32(_mm_load_si128(&in[i * 4 + 2]), _mm_load_si128(&in[i * 4 + 3])), + WeightScaleBits); + const __m128i packedbytes = _mm_packs_epi16(words0, words1); + _mm_store_si128(&out[i], + + #ifdef USE_SSE41 + _mm_max_epi8(packedbytes, Zero) + #else + _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s) + #endif + + ); + } + constexpr IndexType Start = NumChunks * SimdWidth; + +#elif defined(USE_NEON) + constexpr IndexType NumChunks = InputDimensions / (SimdWidth / 2); + const int8x8_t Zero = {0}; + const auto in = reinterpret_cast(input); + const auto out = reinterpret_cast(output); + for (IndexType i = 0; i < NumChunks; ++i) + { + int16x8_t shifted; + const auto pack = reinterpret_cast(&shifted); + pack[0] = vqshrn_n_s32(in[i * 2 + 0], WeightScaleBits); + pack[1] = vqshrn_n_s32(in[i * 2 + 1], WeightScaleBits); + out[i] = vmax_s8(vqmovn_s16(shifted), Zero); + } + constexpr IndexType Start = NumChunks * (SimdWidth / 2); +#else + constexpr IndexType Start = 0; +#endif + + for (IndexType i = Start; i < InputDimensions; ++i) + { + output[i] = static_cast(std::clamp(input[i] >> WeightScaleBits, 0, 127)); } - } - constexpr IndexType Start = - InputDimensions % SimdWidth == 0 - ? InputDimensions / SimdWidth * SimdWidth - : InputDimensions / (SimdWidth / 2) * (SimdWidth / 2); - - #elif defined(USE_SSE2) - constexpr IndexType NumChunks = InputDimensions / SimdWidth; - - #ifdef USE_SSE41 - const __m128i Zero = _mm_setzero_si128(); - #else - const __m128i k0x80s = _mm_set1_epi8(-128); - #endif - - const auto in = reinterpret_cast(input); - const auto out = reinterpret_cast<__m128i*>(output); - for (IndexType i = 0; i < NumChunks; ++i) { - const __m128i words0 = _mm_srai_epi16(_mm_packs_epi32( - _mm_load_si128(&in[i * 4 + 0]), - _mm_load_si128(&in[i * 4 + 1])), WeightScaleBits); - const __m128i words1 = _mm_srai_epi16(_mm_packs_epi32( - _mm_load_si128(&in[i * 4 + 2]), - _mm_load_si128(&in[i * 4 + 3])), WeightScaleBits); - const __m128i packedbytes = _mm_packs_epi16(words0, words1); - _mm_store_si128(&out[i], - - #ifdef USE_SSE41 - _mm_max_epi8(packedbytes, Zero) - #else - _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s) - #endif - - ); - } - constexpr IndexType Start = NumChunks * SimdWidth; - - #elif defined(USE_NEON) - constexpr IndexType NumChunks = InputDimensions / (SimdWidth / 2); - const int8x8_t Zero = {0}; - const auto in = reinterpret_cast(input); - const auto out = reinterpret_cast(output); - for (IndexType i = 0; i < NumChunks; ++i) { - int16x8_t shifted; - const auto pack = reinterpret_cast(&shifted); - pack[0] = vqshrn_n_s32(in[i * 2 + 0], WeightScaleBits); - pack[1] = vqshrn_n_s32(in[i * 2 + 1], WeightScaleBits); - out[i] = vmax_s8(vqmovn_s16(shifted), Zero); - } - constexpr IndexType Start = NumChunks * (SimdWidth / 2); - #else - constexpr IndexType Start = 0; - #endif - - for (IndexType i = Start; i < InputDimensions; ++i) { - output[i] = static_cast( - std::clamp(input[i] >> WeightScaleBits, 0, 127)); - } } - }; +}; } // namespace Stockfish::Eval::NNUE::Layers -#endif // NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED +#endif // NNUE_LAYERS_CLIPPED_RELU_H_INCLUDED diff --git a/src/nnue/layers/simd.h b/src/nnue/layers/simd.h index 349217edb7a..5425ca192bc 100644 --- a/src/nnue/layers/simd.h +++ b/src/nnue/layers/simd.h @@ -20,30 +20,30 @@ #define STOCKFISH_SIMD_H_INCLUDED #if defined(USE_AVX2) -# include + #include #elif defined(USE_SSE41) -# include + #include #elif defined(USE_SSSE3) -# include + #include #elif defined(USE_SSE2) -# include + #include #elif defined(USE_NEON) -# include + #include #endif namespace Stockfish::Simd { -#if defined (USE_AVX512) +#if defined(USE_AVX512) - [[maybe_unused]] static int m512_hadd(__m512i sum, int bias) { - return _mm512_reduce_add_epi32(sum) + bias; - } +[[maybe_unused]] static int m512_hadd(__m512i sum, int bias) { + return _mm512_reduce_add_epi32(sum) + bias; +} - /* +/* Parameters: sum0 = [zmm0.i128[0], zmm0.i128[1], zmm0.i128[2], zmm0.i128[3]] sum1 = [zmm1.i128[0], zmm1.i128[1], zmm1.i128[2], zmm1.i128[3]] @@ -58,186 +58,164 @@ namespace Stockfish::Simd { reduce_add_epi32(zmm0.i128[3]), reduce_add_epi32(zmm1.i128[3]), reduce_add_epi32(zmm2.i128[3]), reduce_add_epi32(zmm3.i128[3]) ] */ - [[maybe_unused]] static __m512i m512_hadd128x16_interleave( - __m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3) { - - __m512i sum01a = _mm512_unpacklo_epi32(sum0, sum1); - __m512i sum01b = _mm512_unpackhi_epi32(sum0, sum1); - - __m512i sum23a = _mm512_unpacklo_epi32(sum2, sum3); - __m512i sum23b = _mm512_unpackhi_epi32(sum2, sum3); - - __m512i sum01 = _mm512_add_epi32(sum01a, sum01b); - __m512i sum23 = _mm512_add_epi32(sum23a, sum23b); - - __m512i sum0123a = _mm512_unpacklo_epi64(sum01, sum23); - __m512i sum0123b = _mm512_unpackhi_epi64(sum01, sum23); - - return _mm512_add_epi32(sum0123a, sum0123b); - } - - [[maybe_unused]] static void m512_add_dpbusd_epi32( - __m512i& acc, - __m512i a, - __m512i b) { - -# if defined (USE_VNNI) - acc = _mm512_dpbusd_epi32(acc, a, b); -# else - __m512i product0 = _mm512_maddubs_epi16(a, b); - product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1)); - acc = _mm512_add_epi32(acc, product0); -# endif - } - - [[maybe_unused]] static void m512_add_dpbusd_epi32x2( - __m512i& acc, - __m512i a0, __m512i b0, - __m512i a1, __m512i b1) { - -# if defined (USE_VNNI) - acc = _mm512_dpbusd_epi32(acc, a0, b0); - acc = _mm512_dpbusd_epi32(acc, a1, b1); -# else - __m512i product0 = _mm512_maddubs_epi16(a0, b0); - __m512i product1 = _mm512_maddubs_epi16(a1, b1); - product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1)); - product1 = _mm512_madd_epi16(product1, _mm512_set1_epi16(1)); - acc = _mm512_add_epi32(acc, _mm512_add_epi32(product0, product1)); -# endif - } +[[maybe_unused]] static __m512i +m512_hadd128x16_interleave(__m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3) { + + __m512i sum01a = _mm512_unpacklo_epi32(sum0, sum1); + __m512i sum01b = _mm512_unpackhi_epi32(sum0, sum1); + + __m512i sum23a = _mm512_unpacklo_epi32(sum2, sum3); + __m512i sum23b = _mm512_unpackhi_epi32(sum2, sum3); + + __m512i sum01 = _mm512_add_epi32(sum01a, sum01b); + __m512i sum23 = _mm512_add_epi32(sum23a, sum23b); + + __m512i sum0123a = _mm512_unpacklo_epi64(sum01, sum23); + __m512i sum0123b = _mm512_unpackhi_epi64(sum01, sum23); + + return _mm512_add_epi32(sum0123a, sum0123b); +} + +[[maybe_unused]] static void m512_add_dpbusd_epi32(__m512i& acc, __m512i a, __m512i b) { + + #if defined(USE_VNNI) + acc = _mm512_dpbusd_epi32(acc, a, b); + #else + __m512i product0 = _mm512_maddubs_epi16(a, b); + product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1)); + acc = _mm512_add_epi32(acc, product0); + #endif +} + +[[maybe_unused]] static void +m512_add_dpbusd_epi32x2(__m512i& acc, __m512i a0, __m512i b0, __m512i a1, __m512i b1) { + + #if defined(USE_VNNI) + acc = _mm512_dpbusd_epi32(acc, a0, b0); + acc = _mm512_dpbusd_epi32(acc, a1, b1); + #else + __m512i product0 = _mm512_maddubs_epi16(a0, b0); + __m512i product1 = _mm512_maddubs_epi16(a1, b1); + product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1)); + product1 = _mm512_madd_epi16(product1, _mm512_set1_epi16(1)); + acc = _mm512_add_epi32(acc, _mm512_add_epi32(product0, product1)); + #endif +} #endif -#if defined (USE_AVX2) - - [[maybe_unused]] static int m256_hadd(__m256i sum, int bias) { - __m128i sum128 = _mm_add_epi32(_mm256_castsi256_si128(sum), _mm256_extracti128_si256(sum, 1)); - sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_BADC)); - sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_CDAB)); - return _mm_cvtsi128_si32(sum128) + bias; - } - - [[maybe_unused]] static void m256_add_dpbusd_epi32( - __m256i& acc, - __m256i a, - __m256i b) { - -# if defined (USE_VNNI) - acc = _mm256_dpbusd_epi32(acc, a, b); -# else - __m256i product0 = _mm256_maddubs_epi16(a, b); - product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1)); - acc = _mm256_add_epi32(acc, product0); -# endif - } - - [[maybe_unused]] static void m256_add_dpbusd_epi32x2( - __m256i& acc, - __m256i a0, __m256i b0, - __m256i a1, __m256i b1) { - -# if defined (USE_VNNI) - acc = _mm256_dpbusd_epi32(acc, a0, b0); - acc = _mm256_dpbusd_epi32(acc, a1, b1); -# else - __m256i product0 = _mm256_maddubs_epi16(a0, b0); - __m256i product1 = _mm256_maddubs_epi16(a1, b1); - product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1)); - product1 = _mm256_madd_epi16(product1, _mm256_set1_epi16(1)); - acc = _mm256_add_epi32(acc, _mm256_add_epi32(product0, product1)); -# endif - } +#if defined(USE_AVX2) + +[[maybe_unused]] static int m256_hadd(__m256i sum, int bias) { + __m128i sum128 = _mm_add_epi32(_mm256_castsi256_si128(sum), _mm256_extracti128_si256(sum, 1)); + sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_BADC)); + sum128 = _mm_add_epi32(sum128, _mm_shuffle_epi32(sum128, _MM_PERM_CDAB)); + return _mm_cvtsi128_si32(sum128) + bias; +} + +[[maybe_unused]] static void m256_add_dpbusd_epi32(__m256i& acc, __m256i a, __m256i b) { + + #if defined(USE_VNNI) + acc = _mm256_dpbusd_epi32(acc, a, b); + #else + __m256i product0 = _mm256_maddubs_epi16(a, b); + product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1)); + acc = _mm256_add_epi32(acc, product0); + #endif +} + +[[maybe_unused]] static void +m256_add_dpbusd_epi32x2(__m256i& acc, __m256i a0, __m256i b0, __m256i a1, __m256i b1) { + + #if defined(USE_VNNI) + acc = _mm256_dpbusd_epi32(acc, a0, b0); + acc = _mm256_dpbusd_epi32(acc, a1, b1); + #else + __m256i product0 = _mm256_maddubs_epi16(a0, b0); + __m256i product1 = _mm256_maddubs_epi16(a1, b1); + product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1)); + product1 = _mm256_madd_epi16(product1, _mm256_set1_epi16(1)); + acc = _mm256_add_epi32(acc, _mm256_add_epi32(product0, product1)); + #endif +} #endif -#if defined (USE_SSSE3) +#if defined(USE_SSSE3) - [[maybe_unused]] static int m128_hadd(__m128i sum, int bias) { - sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0x4E)); //_MM_PERM_BADC - sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0xB1)); //_MM_PERM_CDAB - return _mm_cvtsi128_si32(sum) + bias; - } +[[maybe_unused]] static int m128_hadd(__m128i sum, int bias) { + sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0x4E)); //_MM_PERM_BADC + sum = _mm_add_epi32(sum, _mm_shuffle_epi32(sum, 0xB1)); //_MM_PERM_CDAB + return _mm_cvtsi128_si32(sum) + bias; +} - [[maybe_unused]] static void m128_add_dpbusd_epi32( - __m128i& acc, - __m128i a, - __m128i b) { +[[maybe_unused]] static void m128_add_dpbusd_epi32(__m128i& acc, __m128i a, __m128i b) { - __m128i product0 = _mm_maddubs_epi16(a, b); - product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1)); - acc = _mm_add_epi32(acc, product0); - } + __m128i product0 = _mm_maddubs_epi16(a, b); + product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1)); + acc = _mm_add_epi32(acc, product0); +} - [[maybe_unused]] static void m128_add_dpbusd_epi32x2( - __m128i& acc, - __m128i a0, __m128i b0, - __m128i a1, __m128i b1) { +[[maybe_unused]] static void +m128_add_dpbusd_epi32x2(__m128i& acc, __m128i a0, __m128i b0, __m128i a1, __m128i b1) { - __m128i product0 = _mm_maddubs_epi16(a0, b0); - __m128i product1 = _mm_maddubs_epi16(a1, b1); - product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1)); - product1 = _mm_madd_epi16(product1, _mm_set1_epi16(1)); - acc = _mm_add_epi32(acc, _mm_add_epi32(product0, product1)); - } + __m128i product0 = _mm_maddubs_epi16(a0, b0); + __m128i product1 = _mm_maddubs_epi16(a1, b1); + product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1)); + product1 = _mm_madd_epi16(product1, _mm_set1_epi16(1)); + acc = _mm_add_epi32(acc, _mm_add_epi32(product0, product1)); +} #endif -#if defined (USE_NEON_DOTPROD) +#if defined(USE_NEON_DOTPROD) - [[maybe_unused]] static void dotprod_m128_add_dpbusd_epi32x2( - int32x4_t& acc, - int8x16_t a0, int8x16_t b0, - int8x16_t a1, int8x16_t b1) { +[[maybe_unused]] static void dotprod_m128_add_dpbusd_epi32x2( + int32x4_t& acc, int8x16_t a0, int8x16_t b0, int8x16_t a1, int8x16_t b1) { - acc = vdotq_s32(acc, a0, b0); - acc = vdotq_s32(acc, a1, b1); - } + acc = vdotq_s32(acc, a0, b0); + acc = vdotq_s32(acc, a1, b1); +} - [[maybe_unused]] static void dotprod_m128_add_dpbusd_epi32( - int32x4_t& acc, - int8x16_t a, int8x16_t b) { +[[maybe_unused]] static void +dotprod_m128_add_dpbusd_epi32(int32x4_t& acc, int8x16_t a, int8x16_t b) { - acc = vdotq_s32(acc, a, b); - } + acc = vdotq_s32(acc, a, b); +} #endif -#if defined (USE_NEON) - - [[maybe_unused]] static int neon_m128_reduce_add_epi32(int32x4_t s) { -# if USE_NEON >= 8 - return vaddvq_s32(s); -# else - return s[0] + s[1] + s[2] + s[3]; -# endif - } - - [[maybe_unused]] static int neon_m128_hadd(int32x4_t sum, int bias) { - return neon_m128_reduce_add_epi32(sum) + bias; - } - - [[maybe_unused]] static void neon_m128_add_dpbusd_epi32x2( - int32x4_t& acc, - int8x8_t a0, int8x8_t b0, - int8x8_t a1, int8x8_t b1) { - - int16x8_t product = vmull_s8(a0, b0); - product = vmlal_s8(product, a1, b1); - acc = vpadalq_s16(acc, product); - } +#if defined(USE_NEON) + +[[maybe_unused]] static int neon_m128_reduce_add_epi32(int32x4_t s) { + #if USE_NEON >= 8 + return vaddvq_s32(s); + #else + return s[0] + s[1] + s[2] + s[3]; + #endif +} + +[[maybe_unused]] static int neon_m128_hadd(int32x4_t sum, int bias) { + return neon_m128_reduce_add_epi32(sum) + bias; +} + +[[maybe_unused]] static void +neon_m128_add_dpbusd_epi32x2(int32x4_t& acc, int8x8_t a0, int8x8_t b0, int8x8_t a1, int8x8_t b1) { + + int16x8_t product = vmull_s8(a0, b0); + product = vmlal_s8(product, a1, b1); + acc = vpadalq_s16(acc, product); +} #endif #if USE_NEON >= 8 - [[maybe_unused]] static void neon_m128_add_dpbusd_epi32( - int32x4_t& acc, - int8x16_t a, int8x16_t b) { - - int16x8_t product0 = vmull_s8(vget_low_s8(a), vget_low_s8(b)); - int16x8_t product1 = vmull_high_s8(a, b); - int16x8_t sum = vpaddq_s16(product0, product1); - acc = vpadalq_s16(acc, sum); - } +[[maybe_unused]] static void neon_m128_add_dpbusd_epi32(int32x4_t& acc, int8x16_t a, int8x16_t b) { + + int16x8_t product0 = vmull_s8(vget_low_s8(a), vget_low_s8(b)); + int16x8_t product1 = vmull_high_s8(a, b); + int16x8_t sum = vpaddq_s16(product0, product1); + acc = vpadalq_s16(acc, sum); +} #endif } -#endif // STOCKFISH_SIMD_H_INCLUDED +#endif // STOCKFISH_SIMD_H_INCLUDED diff --git a/src/nnue/layers/sqr_clipped_relu.h b/src/nnue/layers/sqr_clipped_relu.h index a3d2059b4de..987de892f3d 100644 --- a/src/nnue/layers/sqr_clipped_relu.h +++ b/src/nnue/layers/sqr_clipped_relu.h @@ -29,80 +29,75 @@ namespace Stockfish::Eval::NNUE::Layers { - // Clipped ReLU - template - class SqrClippedReLU { +// Clipped ReLU +template +class SqrClippedReLU { public: // Input/output type - using InputType = std::int32_t; + using InputType = std::int32_t; using OutputType = std::uint8_t; // Number of input/output dimensions - static constexpr IndexType InputDimensions = InDims; + static constexpr IndexType InputDimensions = InDims; static constexpr IndexType OutputDimensions = InputDimensions; static constexpr IndexType PaddedOutputDimensions = - ceil_to_multiple(OutputDimensions, 32); + ceil_to_multiple(OutputDimensions, 32); using OutputBuffer = OutputType[PaddedOutputDimensions]; // Hash value embedded in the evaluation file static constexpr std::uint32_t get_hash_value(std::uint32_t prevHash) { - std::uint32_t hashValue = 0x538D24C7u; - hashValue += prevHash; - return hashValue; + std::uint32_t hashValue = 0x538D24C7u; + hashValue += prevHash; + return hashValue; } // Read network parameters - bool read_parameters(std::istream&) { - return true; - } + bool read_parameters(std::istream&) { return true; } // Write network parameters - bool write_parameters(std::ostream&) const { - return true; - } + bool write_parameters(std::ostream&) const { return true; } // Forward propagation - void propagate( - const InputType* input, OutputType* output) const { - - #if defined(USE_SSE2) - constexpr IndexType NumChunks = InputDimensions / 16; - - static_assert(WeightScaleBits == 6); - const auto in = reinterpret_cast(input); - const auto out = reinterpret_cast<__m128i*>(output); - for (IndexType i = 0; i < NumChunks; ++i) { - __m128i words0 = _mm_packs_epi32( - _mm_load_si128(&in[i * 4 + 0]), - _mm_load_si128(&in[i * 4 + 1])); - __m128i words1 = _mm_packs_epi32( - _mm_load_si128(&in[i * 4 + 2]), - _mm_load_si128(&in[i * 4 + 3])); - - // We shift by WeightScaleBits * 2 = 12 and divide by 128 - // which is an additional shift-right of 7, meaning 19 in total. - // MulHi strips the lower 16 bits so we need to shift out 3 more to match. - words0 = _mm_srli_epi16(_mm_mulhi_epi16(words0, words0), 3); - words1 = _mm_srli_epi16(_mm_mulhi_epi16(words1, words1), 3); - - _mm_store_si128(&out[i], _mm_packs_epi16(words0, words1)); - } - constexpr IndexType Start = NumChunks * 16; - - #else - constexpr IndexType Start = 0; - #endif - - for (IndexType i = Start; i < InputDimensions; ++i) { - output[i] = static_cast( - // Really should be /127 but we need to make it fast so we right shift - // by an extra 7 bits instead. Needs to be accounted for in the trainer. - std::min(127ll, ((long long)input[i] * input[i]) >> (2 * WeightScaleBits + 7))); - } + void propagate(const InputType* input, OutputType* output) const { + +#if defined(USE_SSE2) + constexpr IndexType NumChunks = InputDimensions / 16; + + static_assert(WeightScaleBits == 6); + const auto in = reinterpret_cast(input); + const auto out = reinterpret_cast<__m128i*>(output); + for (IndexType i = 0; i < NumChunks; ++i) + { + __m128i words0 = + _mm_packs_epi32(_mm_load_si128(&in[i * 4 + 0]), _mm_load_si128(&in[i * 4 + 1])); + __m128i words1 = + _mm_packs_epi32(_mm_load_si128(&in[i * 4 + 2]), _mm_load_si128(&in[i * 4 + 3])); + + // We shift by WeightScaleBits * 2 = 12 and divide by 128 + // which is an additional shift-right of 7, meaning 19 in total. + // MulHi strips the lower 16 bits so we need to shift out 3 more to match. + words0 = _mm_srli_epi16(_mm_mulhi_epi16(words0, words0), 3); + words1 = _mm_srli_epi16(_mm_mulhi_epi16(words1, words1), 3); + + _mm_store_si128(&out[i], _mm_packs_epi16(words0, words1)); + } + constexpr IndexType Start = NumChunks * 16; + +#else + constexpr IndexType Start = 0; +#endif + + for (IndexType i = Start; i < InputDimensions; ++i) + { + output[i] = static_cast( + // Really should be /127 but we need to make it fast so we right shift + // by an extra 7 bits instead. Needs to be accounted for in the trainer. + std::min(127ll, ((long long) input[i] * input[i]) >> (2 * WeightScaleBits + 7))); + } } - }; +}; } // namespace Stockfish::Eval::NNUE::Layers -#endif // NNUE_LAYERS_SQR_CLIPPED_RELU_H_INCLUDED +#endif // NNUE_LAYERS_SQR_CLIPPED_RELU_H_INCLUDED diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index 03fc3bd5cd8..2f1b1d35e52 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -28,13 +28,13 @@ namespace Stockfish::Eval::NNUE { - // Class that holds the result of affine transformation of input features - struct alignas(CacheLineSize) Accumulator { +// Class that holds the result of affine transformation of input features +struct alignas(CacheLineSize) Accumulator { std::int16_t accumulation[2][TransformedFeatureDimensions]; std::int32_t psqtAccumulation[2][PSQTBuckets]; - bool computed[2]; - }; + bool computed[2]; +}; } // namespace Stockfish::Eval::NNUE -#endif // NNUE_ACCUMULATOR_H_INCLUDED +#endif // NNUE_ACCUMULATOR_H_INCLUDED diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index 2a7f064bbaa..be0138f14bd 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -39,97 +39,90 @@ using FeatureSet = Features::HalfKAv2_hm; // Number of input feature dimensions after conversion constexpr IndexType TransformedFeatureDimensions = 2560; -constexpr IndexType PSQTBuckets = 8; -constexpr IndexType LayerStacks = 8; - -struct Network -{ - static constexpr int FC_0_OUTPUTS = 15; - static constexpr int FC_1_OUTPUTS = 32; - - Layers::AffineTransformSparseInput fc_0; - Layers::SqrClippedReLU ac_sqr_0; - Layers::ClippedReLU ac_0; - Layers::AffineTransform fc_1; - Layers::ClippedReLU ac_1; - Layers::AffineTransform fc_2; - - // Hash value embedded in the evaluation file - static constexpr std::uint32_t get_hash_value() { - // input slice hash - std::uint32_t hashValue = 0xEC42E90Du; - hashValue ^= TransformedFeatureDimensions * 2; - - hashValue = decltype(fc_0)::get_hash_value(hashValue); - hashValue = decltype(ac_0)::get_hash_value(hashValue); - hashValue = decltype(fc_1)::get_hash_value(hashValue); - hashValue = decltype(ac_1)::get_hash_value(hashValue); - hashValue = decltype(fc_2)::get_hash_value(hashValue); - - return hashValue; - } - - // Read network parameters - bool read_parameters(std::istream& stream) { - return fc_0.read_parameters(stream) - && ac_0.read_parameters(stream) - && fc_1.read_parameters(stream) - && ac_1.read_parameters(stream) - && fc_2.read_parameters(stream); - } - - // Write network parameters - bool write_parameters(std::ostream& stream) const { - return fc_0.write_parameters(stream) - && ac_0.write_parameters(stream) - && fc_1.write_parameters(stream) - && ac_1.write_parameters(stream) - && fc_2.write_parameters(stream); - } - - std::int32_t propagate(const TransformedFeatureType* transformedFeatures) - { - struct alignas(CacheLineSize) Buffer - { - alignas(CacheLineSize) decltype(fc_0)::OutputBuffer fc_0_out; - alignas(CacheLineSize) decltype(ac_sqr_0)::OutputType ac_sqr_0_out[ceil_to_multiple(FC_0_OUTPUTS * 2, 32)]; - alignas(CacheLineSize) decltype(ac_0)::OutputBuffer ac_0_out; - alignas(CacheLineSize) decltype(fc_1)::OutputBuffer fc_1_out; - alignas(CacheLineSize) decltype(ac_1)::OutputBuffer ac_1_out; - alignas(CacheLineSize) decltype(fc_2)::OutputBuffer fc_2_out; - - Buffer() - { - std::memset(this, 0, sizeof(*this)); - } - }; +constexpr IndexType PSQTBuckets = 8; +constexpr IndexType LayerStacks = 8; + +struct Network { + static constexpr int FC_0_OUTPUTS = 15; + static constexpr int FC_1_OUTPUTS = 32; + + Layers::AffineTransformSparseInput fc_0; + Layers::SqrClippedReLU ac_sqr_0; + Layers::ClippedReLU ac_0; + Layers::AffineTransform fc_1; + Layers::ClippedReLU ac_1; + Layers::AffineTransform fc_2; + + // Hash value embedded in the evaluation file + static constexpr std::uint32_t get_hash_value() { + // input slice hash + std::uint32_t hashValue = 0xEC42E90Du; + hashValue ^= TransformedFeatureDimensions * 2; + + hashValue = decltype(fc_0)::get_hash_value(hashValue); + hashValue = decltype(ac_0)::get_hash_value(hashValue); + hashValue = decltype(fc_1)::get_hash_value(hashValue); + hashValue = decltype(ac_1)::get_hash_value(hashValue); + hashValue = decltype(fc_2)::get_hash_value(hashValue); + + return hashValue; + } + + // Read network parameters + bool read_parameters(std::istream& stream) { + return fc_0.read_parameters(stream) && ac_0.read_parameters(stream) + && fc_1.read_parameters(stream) && ac_1.read_parameters(stream) + && fc_2.read_parameters(stream); + } + + // Write network parameters + bool write_parameters(std::ostream& stream) const { + return fc_0.write_parameters(stream) && ac_0.write_parameters(stream) + && fc_1.write_parameters(stream) && ac_1.write_parameters(stream) + && fc_2.write_parameters(stream); + } + + std::int32_t propagate(const TransformedFeatureType* transformedFeatures) { + struct alignas(CacheLineSize) Buffer { + alignas(CacheLineSize) decltype(fc_0)::OutputBuffer fc_0_out; + alignas(CacheLineSize) decltype(ac_sqr_0)::OutputType + ac_sqr_0_out[ceil_to_multiple(FC_0_OUTPUTS * 2, 32)]; + alignas(CacheLineSize) decltype(ac_0)::OutputBuffer ac_0_out; + alignas(CacheLineSize) decltype(fc_1)::OutputBuffer fc_1_out; + alignas(CacheLineSize) decltype(ac_1)::OutputBuffer ac_1_out; + alignas(CacheLineSize) decltype(fc_2)::OutputBuffer fc_2_out; + + Buffer() { std::memset(this, 0, sizeof(*this)); } + }; #if defined(__clang__) && (__APPLE__) - // workaround for a bug reported with xcode 12 - static thread_local auto tlsBuffer = std::make_unique(); - // Access TLS only once, cache result. - Buffer& buffer = *tlsBuffer; + // workaround for a bug reported with xcode 12 + static thread_local auto tlsBuffer = std::make_unique(); + // Access TLS only once, cache result. + Buffer& buffer = *tlsBuffer; #else - alignas(CacheLineSize) static thread_local Buffer buffer; + alignas(CacheLineSize) static thread_local Buffer buffer; #endif - fc_0.propagate(transformedFeatures, buffer.fc_0_out); - ac_sqr_0.propagate(buffer.fc_0_out, buffer.ac_sqr_0_out); - ac_0.propagate(buffer.fc_0_out, buffer.ac_0_out); - std::memcpy(buffer.ac_sqr_0_out + FC_0_OUTPUTS, buffer.ac_0_out, FC_0_OUTPUTS * sizeof(decltype(ac_0)::OutputType)); - fc_1.propagate(buffer.ac_sqr_0_out, buffer.fc_1_out); - ac_1.propagate(buffer.fc_1_out, buffer.ac_1_out); - fc_2.propagate(buffer.ac_1_out, buffer.fc_2_out); - - // buffer.fc_0_out[FC_0_OUTPUTS] is such that 1.0 is equal to 127*(1< + #include #elif defined(USE_SSE41) -#include + #include #elif defined(USE_SSSE3) -#include + #include #elif defined(USE_SSE2) -#include + #include #elif defined(USE_NEON) -#include + #include #endif namespace Stockfish::Eval::NNUE { - // Version of the evaluation file - constexpr std::uint32_t Version = 0x7AF32F20u; +// Version of the evaluation file +constexpr std::uint32_t Version = 0x7AF32F20u; - // Constant used in evaluation value calculation - constexpr int OutputScale = 16; - constexpr int WeightScaleBits = 6; +// Constant used in evaluation value calculation +constexpr int OutputScale = 16; +constexpr int WeightScaleBits = 6; - // Size of cache line (in bytes) - constexpr std::size_t CacheLineSize = 64; +// Size of cache line (in bytes) +constexpr std::size_t CacheLineSize = 64; - constexpr const char Leb128MagicString[] = "COMPRESSED_LEB128"; - constexpr const std::size_t Leb128MagicStringSize = sizeof(Leb128MagicString) - 1; - - // SIMD width (in bytes) - #if defined(USE_AVX2) - constexpr std::size_t SimdWidth = 32; - - #elif defined(USE_SSE2) - constexpr std::size_t SimdWidth = 16; - - #elif defined(USE_NEON) - constexpr std::size_t SimdWidth = 16; - #endif - - constexpr std::size_t MaxSimdWidth = 32; - - // Type of input feature after conversion - using TransformedFeatureType = std::uint8_t; - using IndexType = std::uint32_t; - - // Round n up to be a multiple of base - template - constexpr IntType ceil_to_multiple(IntType n, IntType base) { - return (n + base - 1) / base * base; - } - - - // read_little_endian() is our utility to read an integer (signed or unsigned, any size) - // from a stream in little-endian order. We swap the byte order after the read if - // necessary to return a result with the byte ordering of the compiling machine. - template - inline IntType read_little_endian(std::istream& stream) { - IntType result; - - if (IsLittleEndian) - stream.read(reinterpret_cast(&result), sizeof(IntType)); - else - { - std::uint8_t u[sizeof(IntType)]; - std::make_unsigned_t v = 0; - - stream.read(reinterpret_cast(u), sizeof(IntType)); - for (std::size_t i = 0; i < sizeof(IntType); ++i) - v = (v << 8) | u[sizeof(IntType) - i - 1]; - - std::memcpy(&result, &v, sizeof(IntType)); - } - - return result; - } +constexpr const char Leb128MagicString[] = "COMPRESSED_LEB128"; +constexpr const std::size_t Leb128MagicStringSize = sizeof(Leb128MagicString) - 1; +// SIMD width (in bytes) +#if defined(USE_AVX2) +constexpr std::size_t SimdWidth = 32; - // write_little_endian() is our utility to write an integer (signed or unsigned, any size) - // to a stream in little-endian order. We swap the byte order before the write if - // necessary to always write in little endian order, independently of the byte - // ordering of the compiling machine. - template - inline void write_little_endian(std::ostream& stream, IntType value) { +#elif defined(USE_SSE2) +constexpr std::size_t SimdWidth = 16; - if (IsLittleEndian) - stream.write(reinterpret_cast(&value), sizeof(IntType)); - else - { - std::uint8_t u[sizeof(IntType)]; - std::make_unsigned_t v = value; +#elif defined(USE_NEON) +constexpr std::size_t SimdWidth = 16; +#endif - std::size_t i = 0; - // if constexpr to silence the warning about shift by 8 - if constexpr (sizeof(IntType) > 1) - { +constexpr std::size_t MaxSimdWidth = 32; + +// Type of input feature after conversion +using TransformedFeatureType = std::uint8_t; +using IndexType = std::uint32_t; + +// Round n up to be a multiple of base +template +constexpr IntType ceil_to_multiple(IntType n, IntType base) { + return (n + base - 1) / base * base; +} + + +// read_little_endian() is our utility to read an integer (signed or unsigned, any size) +// from a stream in little-endian order. We swap the byte order after the read if +// necessary to return a result with the byte ordering of the compiling machine. +template +inline IntType read_little_endian(std::istream& stream) { + IntType result; + + if (IsLittleEndian) + stream.read(reinterpret_cast(&result), sizeof(IntType)); + else + { + std::uint8_t u[sizeof(IntType)]; + std::make_unsigned_t v = 0; + + stream.read(reinterpret_cast(u), sizeof(IntType)); + for (std::size_t i = 0; i < sizeof(IntType); ++i) + v = (v << 8) | u[sizeof(IntType) - i - 1]; + + std::memcpy(&result, &v, sizeof(IntType)); + } + + return result; +} + + +// write_little_endian() is our utility to write an integer (signed or unsigned, any size) +// to a stream in little-endian order. We swap the byte order before the write if +// necessary to always write in little endian order, independently of the byte +// ordering of the compiling machine. +template +inline void write_little_endian(std::ostream& stream, IntType value) { + + if (IsLittleEndian) + stream.write(reinterpret_cast(&value), sizeof(IntType)); + else + { + std::uint8_t u[sizeof(IntType)]; + std::make_unsigned_t v = value; + + std::size_t i = 0; + // if constexpr to silence the warning about shift by 8 + if constexpr (sizeof(IntType) > 1) + { for (; i + 1 < sizeof(IntType); ++i) { - u[i] = (std::uint8_t)v; + u[i] = (std::uint8_t) v; v >>= 8; } - } - u[i] = (std::uint8_t)v; - - stream.write(reinterpret_cast(u), sizeof(IntType)); - } - } - - - // read_little_endian(s, out, N) : read integers in bulk from a little indian stream. - // This reads N integers from stream s and put them in array out. - template - inline void read_little_endian(std::istream& stream, IntType* out, std::size_t count) { - if (IsLittleEndian) - stream.read(reinterpret_cast(out), sizeof(IntType) * count); - else - for (std::size_t i = 0; i < count; ++i) - out[i] = read_little_endian(stream); - } - - - // write_little_endian(s, values, N) : write integers in bulk to a little indian stream. - // This takes N integers from array values and writes them on stream s. - template - inline void write_little_endian(std::ostream& stream, const IntType* values, std::size_t count) { - if (IsLittleEndian) - stream.write(reinterpret_cast(values), sizeof(IntType) * count); - else - for (std::size_t i = 0; i < count; ++i) - write_little_endian(stream, values[i]); - } - - - // read_leb_128(s, out, N) : read N signed integers from the stream s, putting them in - // the array out. The stream is assumed to be compressed using the signed LEB128 format. - // See https://en.wikipedia.org/wiki/LEB128 for a description of the compression scheme. - template - inline void read_leb_128(std::istream& stream, IntType* out, std::size_t count) { - - // Check the presence of our LEB128 magic string - char leb128MagicString[Leb128MagicStringSize]; - stream.read(leb128MagicString, Leb128MagicStringSize); - assert(strncmp(Leb128MagicString, leb128MagicString, Leb128MagicStringSize) == 0); - - static_assert(std::is_signed_v, "Not implemented for unsigned types"); - - const std::uint32_t BUF_SIZE = 4096; - std::uint8_t buf[BUF_SIZE]; - - auto bytes_left = read_little_endian(stream); - - std::uint32_t buf_pos = BUF_SIZE; - for (std::size_t i = 0; i < count; ++i) - { - IntType result = 0; - size_t shift = 0; - do - { - if (buf_pos == BUF_SIZE) - { - stream.read(reinterpret_cast(buf), std::min(bytes_left, BUF_SIZE)); - buf_pos = 0; - } - - std::uint8_t byte = buf[buf_pos++]; - --bytes_left; - result |= (byte & 0x7f) << shift; - shift += 7; - - if ((byte & 0x80) == 0) - { - out[i] = (sizeof(IntType) * 8 <= shift || (byte & 0x40) == 0) ? result - : result | ~((1 << shift) - 1); - break; - } - } - while (shift < sizeof(IntType) * 8); - } - - assert(bytes_left == 0); - } - - - // write_leb_128(s, values, N) : write signed integers to a stream with LEB128 compression. - // This takes N integers from array values, compress them with the LEB128 algorithm and - // writes the result on the stream s. - // See https://en.wikipedia.org/wiki/LEB128 for a description of the compression scheme. - template - inline void write_leb_128(std::ostream& stream, const IntType* values, std::size_t count) { - - // Write our LEB128 magic string - stream.write(Leb128MagicString, Leb128MagicStringSize); - - static_assert(std::is_signed_v, "Not implemented for unsigned types"); - - std::uint32_t byte_count = 0; - for (std::size_t i = 0; i < count; ++i) - { - IntType value = values[i]; - std::uint8_t byte; - do - { - byte = value & 0x7f; - value >>= 7; - ++byte_count; - } - while ((byte & 0x40) == 0 ? value != 0 : value != -1); - } - - write_little_endian(stream, byte_count); - - const std::uint32_t BUF_SIZE = 4096; - std::uint8_t buf[BUF_SIZE]; - std::uint32_t buf_pos = 0; - - auto flush = [&]() { - if (buf_pos > 0) - { - stream.write(reinterpret_cast(buf), buf_pos); - buf_pos = 0; - } - }; - - auto write = [&](std::uint8_t byte) { - buf[buf_pos++] = byte; - if (buf_pos == BUF_SIZE) - flush(); - }; - - for (std::size_t i = 0; i < count; ++i) - { - IntType value = values[i]; - while (true) - { - std::uint8_t byte = value & 0x7f; - value >>= 7; - if ((byte & 0x40) == 0 ? value == 0 : value == -1) - { - write(byte); - break; - } - write(byte | 0x80); - } - } - - flush(); - } + } + u[i] = (std::uint8_t) v; + + stream.write(reinterpret_cast(u), sizeof(IntType)); + } +} + + +// read_little_endian(s, out, N) : read integers in bulk from a little indian stream. +// This reads N integers from stream s and put them in array out. +template +inline void read_little_endian(std::istream& stream, IntType* out, std::size_t count) { + if (IsLittleEndian) + stream.read(reinterpret_cast(out), sizeof(IntType) * count); + else + for (std::size_t i = 0; i < count; ++i) + out[i] = read_little_endian(stream); +} + + +// write_little_endian(s, values, N) : write integers in bulk to a little indian stream. +// This takes N integers from array values and writes them on stream s. +template +inline void write_little_endian(std::ostream& stream, const IntType* values, std::size_t count) { + if (IsLittleEndian) + stream.write(reinterpret_cast(values), sizeof(IntType) * count); + else + for (std::size_t i = 0; i < count; ++i) + write_little_endian(stream, values[i]); +} + + +// read_leb_128(s, out, N) : read N signed integers from the stream s, putting them in +// the array out. The stream is assumed to be compressed using the signed LEB128 format. +// See https://en.wikipedia.org/wiki/LEB128 for a description of the compression scheme. +template +inline void read_leb_128(std::istream& stream, IntType* out, std::size_t count) { + + // Check the presence of our LEB128 magic string + char leb128MagicString[Leb128MagicStringSize]; + stream.read(leb128MagicString, Leb128MagicStringSize); + assert(strncmp(Leb128MagicString, leb128MagicString, Leb128MagicStringSize) == 0); + + static_assert(std::is_signed_v, "Not implemented for unsigned types"); + + const std::uint32_t BUF_SIZE = 4096; + std::uint8_t buf[BUF_SIZE]; + + auto bytes_left = read_little_endian(stream); + + std::uint32_t buf_pos = BUF_SIZE; + for (std::size_t i = 0; i < count; ++i) + { + IntType result = 0; + size_t shift = 0; + do + { + if (buf_pos == BUF_SIZE) + { + stream.read(reinterpret_cast(buf), std::min(bytes_left, BUF_SIZE)); + buf_pos = 0; + } + + std::uint8_t byte = buf[buf_pos++]; + --bytes_left; + result |= (byte & 0x7f) << shift; + shift += 7; + + if ((byte & 0x80) == 0) + { + out[i] = (sizeof(IntType) * 8 <= shift || (byte & 0x40) == 0) + ? result + : result | ~((1 << shift) - 1); + break; + } + } while (shift < sizeof(IntType) * 8); + } + + assert(bytes_left == 0); +} + + +// write_leb_128(s, values, N) : write signed integers to a stream with LEB128 compression. +// This takes N integers from array values, compress them with the LEB128 algorithm and +// writes the result on the stream s. +// See https://en.wikipedia.org/wiki/LEB128 for a description of the compression scheme. +template +inline void write_leb_128(std::ostream& stream, const IntType* values, std::size_t count) { + + // Write our LEB128 magic string + stream.write(Leb128MagicString, Leb128MagicStringSize); + + static_assert(std::is_signed_v, "Not implemented for unsigned types"); + + std::uint32_t byte_count = 0; + for (std::size_t i = 0; i < count; ++i) + { + IntType value = values[i]; + std::uint8_t byte; + do + { + byte = value & 0x7f; + value >>= 7; + ++byte_count; + } while ((byte & 0x40) == 0 ? value != 0 : value != -1); + } + + write_little_endian(stream, byte_count); + + const std::uint32_t BUF_SIZE = 4096; + std::uint8_t buf[BUF_SIZE]; + std::uint32_t buf_pos = 0; + + auto flush = [&]() { + if (buf_pos > 0) + { + stream.write(reinterpret_cast(buf), buf_pos); + buf_pos = 0; + } + }; + + auto write = [&](std::uint8_t byte) { + buf[buf_pos++] = byte; + if (buf_pos == BUF_SIZE) + flush(); + }; + + for (std::size_t i = 0; i < count; ++i) + { + IntType value = values[i]; + while (true) + { + std::uint8_t byte = value & 0x7f; + value >>= 7; + if ((byte & 0x40) == 0 ? value == 0 : value == -1) + { + write(byte); + break; + } + write(byte | 0x80); + } + } + + flush(); +} } // namespace Stockfish::Eval::NNUE -#endif // #ifndef NNUE_COMMON_H_INCLUDED +#endif // #ifndef NNUE_COMMON_H_INCLUDED diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 9f02830a63e..9cb14187df4 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -36,303 +36,304 @@ namespace Stockfish::Eval::NNUE { - using BiasType = std::int16_t; - using WeightType = std::int16_t; - using PSQTWeightType = std::int32_t; - - // If vector instructions are enabled, we update and refresh the - // accumulator tile by tile such that each tile fits in the CPU's - // vector registers. - #define VECTOR - - static_assert(PSQTBuckets % 8 == 0, - "Per feature PSQT values cannot be processed at granularity lower than 8 at a time."); - - #ifdef USE_AVX512 - using vec_t = __m512i; - using psqt_vec_t = __m256i; - #define vec_load(a) _mm512_load_si512(a) - #define vec_store(a,b) _mm512_store_si512(a,b) - #define vec_add_16(a,b) _mm512_add_epi16(a,b) - #define vec_sub_16(a,b) _mm512_sub_epi16(a,b) - #define vec_mul_16(a,b) _mm512_mullo_epi16(a,b) - #define vec_zero() _mm512_setzero_epi32() - #define vec_set_16(a) _mm512_set1_epi16(a) - #define vec_max_16(a,b) _mm512_max_epi16(a,b) - #define vec_min_16(a,b) _mm512_min_epi16(a,b) - inline vec_t vec_msb_pack_16(vec_t a, vec_t b){ - vec_t compacted = _mm512_packs_epi16(_mm512_srli_epi16(a,7),_mm512_srli_epi16(b,7)); +using BiasType = std::int16_t; +using WeightType = std::int16_t; +using PSQTWeightType = std::int32_t; + +// If vector instructions are enabled, we update and refresh the +// accumulator tile by tile such that each tile fits in the CPU's +// vector registers. +#define VECTOR + +static_assert(PSQTBuckets % 8 == 0, + "Per feature PSQT values cannot be processed at granularity lower than 8 at a time."); + +#ifdef USE_AVX512 +using vec_t = __m512i; +using psqt_vec_t = __m256i; + #define vec_load(a) _mm512_load_si512(a) + #define vec_store(a, b) _mm512_store_si512(a, b) + #define vec_add_16(a, b) _mm512_add_epi16(a, b) + #define vec_sub_16(a, b) _mm512_sub_epi16(a, b) + #define vec_mul_16(a, b) _mm512_mullo_epi16(a, b) + #define vec_zero() _mm512_setzero_epi32() + #define vec_set_16(a) _mm512_set1_epi16(a) + #define vec_max_16(a, b) _mm512_max_epi16(a, b) + #define vec_min_16(a, b) _mm512_min_epi16(a, b) +inline vec_t vec_msb_pack_16(vec_t a, vec_t b) { + vec_t compacted = _mm512_packs_epi16(_mm512_srli_epi16(a, 7), _mm512_srli_epi16(b, 7)); return _mm512_permutexvar_epi64(_mm512_setr_epi64(0, 2, 4, 6, 1, 3, 5, 7), compacted); - } - #define vec_load_psqt(a) _mm256_load_si256(a) - #define vec_store_psqt(a,b) _mm256_store_si256(a,b) - #define vec_add_psqt_32(a,b) _mm256_add_epi32(a,b) - #define vec_sub_psqt_32(a,b) _mm256_sub_epi32(a,b) - #define vec_zero_psqt() _mm256_setzero_si256() - #define NumRegistersSIMD 16 - #define MaxChunkSize 64 - - #elif USE_AVX2 - using vec_t = __m256i; - using psqt_vec_t = __m256i; - #define vec_load(a) _mm256_load_si256(a) - #define vec_store(a,b) _mm256_store_si256(a,b) - #define vec_add_16(a,b) _mm256_add_epi16(a,b) - #define vec_sub_16(a,b) _mm256_sub_epi16(a,b) - #define vec_mul_16(a,b) _mm256_mullo_epi16(a,b) - #define vec_zero() _mm256_setzero_si256() - #define vec_set_16(a) _mm256_set1_epi16(a) - #define vec_max_16(a,b) _mm256_max_epi16(a,b) - #define vec_min_16(a,b) _mm256_min_epi16(a,b) - inline vec_t vec_msb_pack_16(vec_t a, vec_t b){ - vec_t compacted = _mm256_packs_epi16(_mm256_srli_epi16(a,7), _mm256_srli_epi16(b,7)); +} + #define vec_load_psqt(a) _mm256_load_si256(a) + #define vec_store_psqt(a, b) _mm256_store_si256(a, b) + #define vec_add_psqt_32(a, b) _mm256_add_epi32(a, b) + #define vec_sub_psqt_32(a, b) _mm256_sub_epi32(a, b) + #define vec_zero_psqt() _mm256_setzero_si256() + #define NumRegistersSIMD 16 + #define MaxChunkSize 64 + +#elif USE_AVX2 +using vec_t = __m256i; +using psqt_vec_t = __m256i; + #define vec_load(a) _mm256_load_si256(a) + #define vec_store(a, b) _mm256_store_si256(a, b) + #define vec_add_16(a, b) _mm256_add_epi16(a, b) + #define vec_sub_16(a, b) _mm256_sub_epi16(a, b) + #define vec_mul_16(a, b) _mm256_mullo_epi16(a, b) + #define vec_zero() _mm256_setzero_si256() + #define vec_set_16(a) _mm256_set1_epi16(a) + #define vec_max_16(a, b) _mm256_max_epi16(a, b) + #define vec_min_16(a, b) _mm256_min_epi16(a, b) +inline vec_t vec_msb_pack_16(vec_t a, vec_t b) { + vec_t compacted = _mm256_packs_epi16(_mm256_srli_epi16(a, 7), _mm256_srli_epi16(b, 7)); return _mm256_permute4x64_epi64(compacted, 0b11011000); - } - #define vec_load_psqt(a) _mm256_load_si256(a) - #define vec_store_psqt(a,b) _mm256_store_si256(a,b) - #define vec_add_psqt_32(a,b) _mm256_add_epi32(a,b) - #define vec_sub_psqt_32(a,b) _mm256_sub_epi32(a,b) - #define vec_zero_psqt() _mm256_setzero_si256() - #define NumRegistersSIMD 16 - #define MaxChunkSize 32 - - #elif USE_SSE2 - using vec_t = __m128i; - using psqt_vec_t = __m128i; - #define vec_load(a) (*(a)) - #define vec_store(a,b) *(a)=(b) - #define vec_add_16(a,b) _mm_add_epi16(a,b) - #define vec_sub_16(a,b) _mm_sub_epi16(a,b) - #define vec_mul_16(a,b) _mm_mullo_epi16(a,b) - #define vec_zero() _mm_setzero_si128() - #define vec_set_16(a) _mm_set1_epi16(a) - #define vec_max_16(a,b) _mm_max_epi16(a,b) - #define vec_min_16(a,b) _mm_min_epi16(a,b) - #define vec_msb_pack_16(a,b) _mm_packs_epi16(_mm_srli_epi16(a,7),_mm_srli_epi16(b,7)) - #define vec_load_psqt(a) (*(a)) - #define vec_store_psqt(a,b) *(a)=(b) - #define vec_add_psqt_32(a,b) _mm_add_epi32(a,b) - #define vec_sub_psqt_32(a,b) _mm_sub_epi32(a,b) - #define vec_zero_psqt() _mm_setzero_si128() - #define NumRegistersSIMD (Is64Bit ? 16 : 8) - #define MaxChunkSize 16 - - #elif USE_NEON - using vec_t = int16x8_t; - using psqt_vec_t = int32x4_t; - #define vec_load(a) (*(a)) - #define vec_store(a,b) *(a)=(b) - #define vec_add_16(a,b) vaddq_s16(a,b) - #define vec_sub_16(a,b) vsubq_s16(a,b) - #define vec_mul_16(a,b) vmulq_s16(a,b) - #define vec_zero() vec_t{0} - #define vec_set_16(a) vdupq_n_s16(a) - #define vec_max_16(a,b) vmaxq_s16(a,b) - #define vec_min_16(a,b) vminq_s16(a,b) - inline vec_t vec_msb_pack_16(vec_t a, vec_t b){ - const int8x8_t shifta = vshrn_n_s16(a, 7); - const int8x8_t shiftb = vshrn_n_s16(b, 7); - const int8x16_t compacted = vcombine_s8(shifta,shiftb); - return *reinterpret_cast (&compacted); - } - #define vec_load_psqt(a) (*(a)) - #define vec_store_psqt(a,b) *(a)=(b) - #define vec_add_psqt_32(a,b) vaddq_s32(a,b) - #define vec_sub_psqt_32(a,b) vsubq_s32(a,b) - #define vec_zero_psqt() psqt_vec_t{0} - #define NumRegistersSIMD 16 - #define MaxChunkSize 16 - - #else - #undef VECTOR - - #endif - - - #ifdef VECTOR - - // Compute optimal SIMD register count for feature transformer accumulation. - - // We use __m* types as template arguments, which causes GCC to emit warnings - // about losing some attribute information. This is irrelevant to us as we - // only take their size, so the following pragma are harmless. - #if defined(__GNUC__) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wignored-attributes" - #endif - - template - static constexpr int BestRegisterCount() - { - #define RegisterSize sizeof(SIMDRegisterType) - #define LaneSize sizeof(LaneType) - - static_assert(RegisterSize >= LaneSize); - static_assert(MaxRegisters <= NumRegistersSIMD); - static_assert(MaxRegisters > 0); - static_assert(NumRegistersSIMD > 0); - static_assert(RegisterSize % LaneSize == 0); - static_assert((NumLanes * LaneSize) % RegisterSize == 0); - - const int ideal = (NumLanes * LaneSize) / RegisterSize; - if (ideal <= MaxRegisters) - return ideal; - - // Look for the largest divisor of the ideal register count that is smaller than MaxRegisters - for (int divisor = MaxRegisters; divisor > 1; --divisor) - if (ideal % divisor == 0) - return divisor; - - return 1; - } - - static constexpr int NumRegs = BestRegisterCount(); - static constexpr int NumPsqtRegs = BestRegisterCount(); - #if defined(__GNUC__) - #pragma GCC diagnostic pop - #endif - #endif - - - - // Input feature converter - class FeatureTransformer { +} + #define vec_load_psqt(a) _mm256_load_si256(a) + #define vec_store_psqt(a, b) _mm256_store_si256(a, b) + #define vec_add_psqt_32(a, b) _mm256_add_epi32(a, b) + #define vec_sub_psqt_32(a, b) _mm256_sub_epi32(a, b) + #define vec_zero_psqt() _mm256_setzero_si256() + #define NumRegistersSIMD 16 + #define MaxChunkSize 32 + +#elif USE_SSE2 +using vec_t = __m128i; +using psqt_vec_t = __m128i; + #define vec_load(a) (*(a)) + #define vec_store(a, b) *(a) = (b) + #define vec_add_16(a, b) _mm_add_epi16(a, b) + #define vec_sub_16(a, b) _mm_sub_epi16(a, b) + #define vec_mul_16(a, b) _mm_mullo_epi16(a, b) + #define vec_zero() _mm_setzero_si128() + #define vec_set_16(a) _mm_set1_epi16(a) + #define vec_max_16(a, b) _mm_max_epi16(a, b) + #define vec_min_16(a, b) _mm_min_epi16(a, b) + #define vec_msb_pack_16(a, b) _mm_packs_epi16(_mm_srli_epi16(a, 7), _mm_srli_epi16(b, 7)) + #define vec_load_psqt(a) (*(a)) + #define vec_store_psqt(a, b) *(a) = (b) + #define vec_add_psqt_32(a, b) _mm_add_epi32(a, b) + #define vec_sub_psqt_32(a, b) _mm_sub_epi32(a, b) + #define vec_zero_psqt() _mm_setzero_si128() + #define NumRegistersSIMD (Is64Bit ? 16 : 8) + #define MaxChunkSize 16 + +#elif USE_NEON +using vec_t = int16x8_t; +using psqt_vec_t = int32x4_t; + #define vec_load(a) (*(a)) + #define vec_store(a, b) *(a) = (b) + #define vec_add_16(a, b) vaddq_s16(a, b) + #define vec_sub_16(a, b) vsubq_s16(a, b) + #define vec_mul_16(a, b) vmulq_s16(a, b) + #define vec_zero() \ + vec_t { 0 } + #define vec_set_16(a) vdupq_n_s16(a) + #define vec_max_16(a, b) vmaxq_s16(a, b) + #define vec_min_16(a, b) vminq_s16(a, b) +inline vec_t vec_msb_pack_16(vec_t a, vec_t b) { + const int8x8_t shifta = vshrn_n_s16(a, 7); + const int8x8_t shiftb = vshrn_n_s16(b, 7); + const int8x16_t compacted = vcombine_s8(shifta, shiftb); + return *reinterpret_cast(&compacted); +} + #define vec_load_psqt(a) (*(a)) + #define vec_store_psqt(a, b) *(a) = (b) + #define vec_add_psqt_32(a, b) vaddq_s32(a, b) + #define vec_sub_psqt_32(a, b) vsubq_s32(a, b) + #define vec_zero_psqt() \ + psqt_vec_t { 0 } + #define NumRegistersSIMD 16 + #define MaxChunkSize 16 + +#else + #undef VECTOR + +#endif + + +#ifdef VECTOR + + // Compute optimal SIMD register count for feature transformer accumulation. + + // We use __m* types as template arguments, which causes GCC to emit warnings + // about losing some attribute information. This is irrelevant to us as we + // only take their size, so the following pragma are harmless. + #if defined(__GNUC__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wignored-attributes" + #endif + +template +static constexpr int BestRegisterCount() { + #define RegisterSize sizeof(SIMDRegisterType) + #define LaneSize sizeof(LaneType) + + static_assert(RegisterSize >= LaneSize); + static_assert(MaxRegisters <= NumRegistersSIMD); + static_assert(MaxRegisters > 0); + static_assert(NumRegistersSIMD > 0); + static_assert(RegisterSize % LaneSize == 0); + static_assert((NumLanes * LaneSize) % RegisterSize == 0); + + const int ideal = (NumLanes * LaneSize) / RegisterSize; + if (ideal <= MaxRegisters) + return ideal; + + // Look for the largest divisor of the ideal register count that is smaller than MaxRegisters + for (int divisor = MaxRegisters; divisor > 1; --divisor) + if (ideal % divisor == 0) + return divisor; + + return 1; +} + +static constexpr int NumRegs = + BestRegisterCount(); +static constexpr int NumPsqtRegs = + BestRegisterCount(); + #if defined(__GNUC__) + #pragma GCC diagnostic pop + #endif +#endif + + +// Input feature converter +class FeatureTransformer { private: // Number of output dimensions for one side static constexpr IndexType HalfDimensions = TransformedFeatureDimensions; - #ifdef VECTOR - static constexpr IndexType TileHeight = NumRegs * sizeof(vec_t) / 2; +#ifdef VECTOR + static constexpr IndexType TileHeight = NumRegs * sizeof(vec_t) / 2; static constexpr IndexType PsqtTileHeight = NumPsqtRegs * sizeof(psqt_vec_t) / 4; static_assert(HalfDimensions % TileHeight == 0, "TileHeight must divide HalfDimensions"); static_assert(PSQTBuckets % PsqtTileHeight == 0, "PsqtTileHeight must divide PSQTBuckets"); - #endif +#endif public: // Output type using OutputType = TransformedFeatureType; // Number of input/output dimensions - static constexpr IndexType InputDimensions = FeatureSet::Dimensions; + static constexpr IndexType InputDimensions = FeatureSet::Dimensions; static constexpr IndexType OutputDimensions = HalfDimensions; // Size of forward propagation buffer - static constexpr std::size_t BufferSize = - OutputDimensions * sizeof(OutputType); + static constexpr std::size_t BufferSize = OutputDimensions * sizeof(OutputType); // Hash value embedded in the evaluation file static constexpr std::uint32_t get_hash_value() { - return FeatureSet::HashValue ^ (OutputDimensions * 2); + return FeatureSet::HashValue ^ (OutputDimensions * 2); } // Read network parameters bool read_parameters(std::istream& stream) { - read_leb_128(stream, biases , HalfDimensions ); - read_leb_128(stream, weights , HalfDimensions * InputDimensions); - read_leb_128(stream, psqtWeights, PSQTBuckets * InputDimensions); + read_leb_128(stream, biases, HalfDimensions); + read_leb_128(stream, weights, HalfDimensions * InputDimensions); + read_leb_128(stream, psqtWeights, PSQTBuckets * InputDimensions); - return !stream.fail(); + return !stream.fail(); } // Write network parameters bool write_parameters(std::ostream& stream) const { - write_leb_128(stream, biases , HalfDimensions ); - write_leb_128(stream, weights , HalfDimensions * InputDimensions); - write_leb_128(stream, psqtWeights, PSQTBuckets * InputDimensions); + write_leb_128(stream, biases, HalfDimensions); + write_leb_128(stream, weights, HalfDimensions * InputDimensions); + write_leb_128(stream, psqtWeights, PSQTBuckets * InputDimensions); - return !stream.fail(); + return !stream.fail(); } // Convert input features std::int32_t transform(const Position& pos, OutputType* output, int bucket) const { - update_accumulator(pos); - update_accumulator(pos); + update_accumulator(pos); + update_accumulator(pos); - const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()}; - const auto& accumulation = pos.state()->accumulator.accumulation; - const auto& psqtAccumulation = pos.state()->accumulator.psqtAccumulation; + const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()}; + const auto& accumulation = pos.state()->accumulator.accumulation; + const auto& psqtAccumulation = pos.state()->accumulator.psqtAccumulation; - const auto psqt = ( - psqtAccumulation[perspectives[0]][bucket] - - psqtAccumulation[perspectives[1]][bucket] - ) / 2; + const auto psqt = + (psqtAccumulation[perspectives[0]][bucket] - psqtAccumulation[perspectives[1]][bucket]) + / 2; - for (IndexType p = 0; p < 2; ++p) - { - const IndexType offset = (HalfDimensions / 2) * p; + for (IndexType p = 0; p < 2; ++p) + { + const IndexType offset = (HalfDimensions / 2) * p; #if defined(VECTOR) - constexpr IndexType OutputChunkSize = MaxChunkSize; - static_assert((HalfDimensions / 2) % OutputChunkSize == 0); - constexpr IndexType NumOutputChunks = HalfDimensions / 2 / OutputChunkSize; + constexpr IndexType OutputChunkSize = MaxChunkSize; + static_assert((HalfDimensions / 2) % OutputChunkSize == 0); + constexpr IndexType NumOutputChunks = HalfDimensions / 2 / OutputChunkSize; - vec_t Zero = vec_zero(); - vec_t One = vec_set_16(127); + vec_t Zero = vec_zero(); + vec_t One = vec_set_16(127); - const vec_t* in0 = reinterpret_cast(&(accumulation[perspectives[p]][0])); - const vec_t* in1 = reinterpret_cast(&(accumulation[perspectives[p]][HalfDimensions / 2])); - vec_t* out = reinterpret_cast< vec_t*>(output + offset); + const vec_t* in0 = reinterpret_cast(&(accumulation[perspectives[p]][0])); + const vec_t* in1 = + reinterpret_cast(&(accumulation[perspectives[p]][HalfDimensions / 2])); + vec_t* out = reinterpret_cast(output + offset); - for (IndexType j = 0; j < NumOutputChunks; j += 1) - { - const vec_t sum0a = vec_max_16(vec_min_16(in0[j * 2 + 0], One), Zero); - const vec_t sum0b = vec_max_16(vec_min_16(in0[j * 2 + 1], One), Zero); - const vec_t sum1a = vec_max_16(vec_min_16(in1[j * 2 + 0], One), Zero); - const vec_t sum1b = vec_max_16(vec_min_16(in1[j * 2 + 1], One), Zero); + for (IndexType j = 0; j < NumOutputChunks; j += 1) + { + const vec_t sum0a = vec_max_16(vec_min_16(in0[j * 2 + 0], One), Zero); + const vec_t sum0b = vec_max_16(vec_min_16(in0[j * 2 + 1], One), Zero); + const vec_t sum1a = vec_max_16(vec_min_16(in1[j * 2 + 0], One), Zero); + const vec_t sum1b = vec_max_16(vec_min_16(in1[j * 2 + 1], One), Zero); - const vec_t pa = vec_mul_16(sum0a, sum1a); - const vec_t pb = vec_mul_16(sum0b, sum1b); + const vec_t pa = vec_mul_16(sum0a, sum1a); + const vec_t pb = vec_mul_16(sum0b, sum1b); - out[j] = vec_msb_pack_16(pa, pb); - } + out[j] = vec_msb_pack_16(pa, pb); + } #else - for (IndexType j = 0; j < HalfDimensions / 2; ++j) { - BiasType sum0 = accumulation[static_cast(perspectives[p])][j + 0]; - BiasType sum1 = accumulation[static_cast(perspectives[p])][j + HalfDimensions / 2]; - sum0 = std::clamp(sum0, 0, 127); - sum1 = std::clamp(sum1, 0, 127); - output[offset + j] = static_cast(unsigned(sum0 * sum1) / 128); - } + for (IndexType j = 0; j < HalfDimensions / 2; ++j) + { + BiasType sum0 = accumulation[static_cast(perspectives[p])][j + 0]; + BiasType sum1 = + accumulation[static_cast(perspectives[p])][j + HalfDimensions / 2]; + sum0 = std::clamp(sum0, 0, 127); + sum1 = std::clamp(sum1, 0, 127); + output[offset + j] = static_cast(unsigned(sum0 * sum1) / 128); + } #endif - } + } - return psqt; - } // end of function transform() + return psqt; + } // end of function transform() void hint_common_access(const Position& pos) const { - hint_common_access_for_perspective(pos); - hint_common_access_for_perspective(pos); + hint_common_access_for_perspective(pos); + hint_common_access_for_perspective(pos); } private: template - [[nodiscard]] std::pair try_find_computed_accumulator(const Position& pos) const { - // Look for a usable accumulator of an earlier position. We keep track - // of the estimated gain in terms of features to be added/subtracted. - StateInfo *st = pos.state(), *next = nullptr; - int gain = FeatureSet::refresh_cost(pos); - while (st->previous && !st->accumulator.computed[Perspective]) - { - // This governs when a full feature refresh is needed and how many - // updates are better than just one full refresh. - if ( FeatureSet::requires_refresh(st, Perspective) - || (gain -= FeatureSet::update_cost(st) + 1) < 0) - break; - next = st; - st = st->previous; - } - return { st, next }; + [[nodiscard]] std::pair + try_find_computed_accumulator(const Position& pos) const { + // Look for a usable accumulator of an earlier position. We keep track + // of the estimated gain in terms of features to be added/subtracted. + StateInfo *st = pos.state(), *next = nullptr; + int gain = FeatureSet::refresh_cost(pos); + while (st->previous && !st->accumulator.computed[Perspective]) + { + // This governs when a full feature refresh is needed and how many + // updates are better than just one full refresh. + if (FeatureSet::requires_refresh(st, Perspective) + || (gain -= FeatureSet::update_cost(st) + 1) < 0) + break; + next = st; + st = st->previous; + } + return {st, next}; } // NOTE: The parameter states_to_update is an array of position states, ending with nullptr. @@ -340,364 +341,374 @@ namespace Stockfish::Eval::NNUE { // by repeatedly applying ->previous from states_to_update[i+1] or states_to_update[i] == nullptr. // computed_st must be reachable by repeatedly applying ->previous on states_to_update[0], if not nullptr. template - void update_accumulator_incremental(const Position& pos, StateInfo* computed_st, StateInfo* states_to_update[N]) const { - static_assert(N > 0); - assert(states_to_update[N-1] == nullptr); + void update_accumulator_incremental(const Position& pos, + StateInfo* computed_st, + StateInfo* states_to_update[N]) const { + static_assert(N > 0); + assert(states_to_update[N - 1] == nullptr); - #ifdef VECTOR - // Gcc-10.2 unnecessarily spills AVX2 registers if this array - // is defined in the VECTOR code below, once in each branch - vec_t acc[NumRegs]; - psqt_vec_t psqt[NumPsqtRegs]; - #endif +#ifdef VECTOR + // Gcc-10.2 unnecessarily spills AVX2 registers if this array + // is defined in the VECTOR code below, once in each branch + vec_t acc[NumRegs]; + psqt_vec_t psqt[NumPsqtRegs]; +#endif - if (states_to_update[0] == nullptr) - return; + if (states_to_update[0] == nullptr) + return; - // Update incrementally going back through states_to_update. + // Update incrementally going back through states_to_update. - // Gather all features to be updated. - const Square ksq = pos.square(Perspective); + // Gather all features to be updated. + const Square ksq = pos.square(Perspective); - // The size must be enough to contain the largest possible update. - // That might depend on the feature set and generally relies on the - // feature set's update cost calculation to be correct and never - // allow updates with more added/removed features than MaxActiveDimensions. - FeatureSet::IndexList removed[N-1], added[N-1]; + // The size must be enough to contain the largest possible update. + // That might depend on the feature set and generally relies on the + // feature set's update cost calculation to be correct and never + // allow updates with more added/removed features than MaxActiveDimensions. + FeatureSet::IndexList removed[N - 1], added[N - 1]; - { - int i = N-2; // last potential state to update. Skip last element because it must be nullptr. - while (states_to_update[i] == nullptr) - --i; + { + int i = + N + - 2; // last potential state to update. Skip last element because it must be nullptr. + while (states_to_update[i] == nullptr) + --i; - StateInfo* st2 = states_to_update[i]; + StateInfo* st2 = states_to_update[i]; - for (; i >= 0; --i) - { - states_to_update[i]->accumulator.computed[Perspective] = true; + for (; i >= 0; --i) + { + states_to_update[i]->accumulator.computed[Perspective] = true; - const StateInfo* end_state = i == 0 ? computed_st : states_to_update[i - 1]; + const StateInfo* end_state = i == 0 ? computed_st : states_to_update[i - 1]; - for (; st2 != end_state; st2 = st2->previous) - FeatureSet::append_changed_indices( - ksq, st2->dirtyPiece, removed[i], added[i]); + for (; st2 != end_state; st2 = st2->previous) + FeatureSet::append_changed_indices(ksq, st2->dirtyPiece, + removed[i], added[i]); + } } - } - StateInfo* st = computed_st; + StateInfo* st = computed_st; - // Now update the accumulators listed in states_to_update[], where the last element is a sentinel. + // Now update the accumulators listed in states_to_update[], where the last element is a sentinel. #ifdef VECTOR - if ( states_to_update[1] == nullptr - && (removed[0].size() == 1 || removed[0].size() == 2) - && added[0].size() == 1) - { - assert(states_to_update[0]); + if (states_to_update[1] == nullptr && (removed[0].size() == 1 || removed[0].size() == 2) + && added[0].size() == 1) + { + assert(states_to_update[0]); - auto accIn = reinterpret_cast( - &st->accumulator.accumulation[Perspective][0]); - auto accOut = reinterpret_cast( + auto accIn = + reinterpret_cast(&st->accumulator.accumulation[Perspective][0]); + auto accOut = reinterpret_cast( &states_to_update[0]->accumulator.accumulation[Perspective][0]); - const IndexType offsetR0 = HalfDimensions * removed[0][0]; - auto columnR0 = reinterpret_cast(&weights[offsetR0]); - const IndexType offsetA = HalfDimensions * added[0][0]; - auto columnA = reinterpret_cast(&weights[offsetA]); - - if (removed[0].size() == 1) - { - for (IndexType k = 0; k < HalfDimensions * sizeof(std::int16_t) / sizeof(vec_t); ++k) - accOut[k] = vec_add_16(vec_sub_16(accIn[k], columnR0[k]), columnA[k]); - } - else - { - const IndexType offsetR1 = HalfDimensions * removed[0][1]; - auto columnR1 = reinterpret_cast(&weights[offsetR1]); - - for (IndexType k = 0; k < HalfDimensions * sizeof(std::int16_t) / sizeof(vec_t); ++k) - accOut[k] = vec_sub_16( - vec_add_16(accIn[k], columnA[k]), - vec_add_16(columnR0[k], columnR1[k])); - } - - auto accPsqtIn = reinterpret_cast( + const IndexType offsetR0 = HalfDimensions * removed[0][0]; + auto columnR0 = reinterpret_cast(&weights[offsetR0]); + const IndexType offsetA = HalfDimensions * added[0][0]; + auto columnA = reinterpret_cast(&weights[offsetA]); + + if (removed[0].size() == 1) + { + for (IndexType k = 0; k < HalfDimensions * sizeof(std::int16_t) / sizeof(vec_t); + ++k) + accOut[k] = vec_add_16(vec_sub_16(accIn[k], columnR0[k]), columnA[k]); + } + else + { + const IndexType offsetR1 = HalfDimensions * removed[0][1]; + auto columnR1 = reinterpret_cast(&weights[offsetR1]); + + for (IndexType k = 0; k < HalfDimensions * sizeof(std::int16_t) / sizeof(vec_t); + ++k) + accOut[k] = vec_sub_16(vec_add_16(accIn[k], columnA[k]), + vec_add_16(columnR0[k], columnR1[k])); + } + + auto accPsqtIn = reinterpret_cast( &st->accumulator.psqtAccumulation[Perspective][0]); - auto accPsqtOut = reinterpret_cast( + auto accPsqtOut = reinterpret_cast( &states_to_update[0]->accumulator.psqtAccumulation[Perspective][0]); - const IndexType offsetPsqtR0 = PSQTBuckets * removed[0][0]; - auto columnPsqtR0 = reinterpret_cast(&psqtWeights[offsetPsqtR0]); - const IndexType offsetPsqtA = PSQTBuckets * added[0][0]; - auto columnPsqtA = reinterpret_cast(&psqtWeights[offsetPsqtA]); - - if (removed[0].size() == 1) - { - for (std::size_t k = 0; k < PSQTBuckets * sizeof(std::int32_t) / sizeof(psqt_vec_t); ++k) - accPsqtOut[k] = vec_add_psqt_32(vec_sub_psqt_32( - accPsqtIn[k], columnPsqtR0[k]), columnPsqtA[k]); - } - else - { - const IndexType offsetPsqtR1 = PSQTBuckets * removed[0][1]; - auto columnPsqtR1 = reinterpret_cast(&psqtWeights[offsetPsqtR1]); - - for (std::size_t k = 0; k < PSQTBuckets * sizeof(std::int32_t) / sizeof(psqt_vec_t); ++k) - accPsqtOut[k] = vec_sub_psqt_32( - vec_add_psqt_32(accPsqtIn[k], columnPsqtA[k]), - vec_add_psqt_32(columnPsqtR0[k], columnPsqtR1[k])); - } - } - else - { - for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) - { - // Load accumulator - auto accTileIn = reinterpret_cast( - &st->accumulator.accumulation[Perspective][j * TileHeight]); - for (IndexType k = 0; k < NumRegs; ++k) - acc[k] = vec_load(&accTileIn[k]); + const IndexType offsetPsqtR0 = PSQTBuckets * removed[0][0]; + auto columnPsqtR0 = reinterpret_cast(&psqtWeights[offsetPsqtR0]); + const IndexType offsetPsqtA = PSQTBuckets * added[0][0]; + auto columnPsqtA = reinterpret_cast(&psqtWeights[offsetPsqtA]); - for (IndexType i = 0; states_to_update[i]; ++i) + if (removed[0].size() == 1) { - // Difference calculation for the deactivated features - for (const auto index : removed[i]) - { - const IndexType offset = HalfDimensions * index + j * TileHeight; - auto column = reinterpret_cast(&weights[offset]); - for (IndexType k = 0; k < NumRegs; ++k) - acc[k] = vec_sub_16(acc[k], column[k]); - } + for (std::size_t k = 0; k < PSQTBuckets * sizeof(std::int32_t) / sizeof(psqt_vec_t); + ++k) + accPsqtOut[k] = vec_add_psqt_32(vec_sub_psqt_32(accPsqtIn[k], columnPsqtR0[k]), + columnPsqtA[k]); + } + else + { + const IndexType offsetPsqtR1 = PSQTBuckets * removed[0][1]; + auto columnPsqtR1 = reinterpret_cast(&psqtWeights[offsetPsqtR1]); - // Difference calculation for the activated features - for (const auto index : added[i]) - { - const IndexType offset = HalfDimensions * index + j * TileHeight; - auto column = reinterpret_cast(&weights[offset]); + for (std::size_t k = 0; k < PSQTBuckets * sizeof(std::int32_t) / sizeof(psqt_vec_t); + ++k) + accPsqtOut[k] = + vec_sub_psqt_32(vec_add_psqt_32(accPsqtIn[k], columnPsqtA[k]), + vec_add_psqt_32(columnPsqtR0[k], columnPsqtR1[k])); + } + } + else + { + for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) + { + // Load accumulator + auto accTileIn = reinterpret_cast( + &st->accumulator.accumulation[Perspective][j * TileHeight]); for (IndexType k = 0; k < NumRegs; ++k) - acc[k] = vec_add_16(acc[k], column[k]); - } - - // Store accumulator - auto accTileOut = reinterpret_cast( - &states_to_update[i]->accumulator.accumulation[Perspective][j * TileHeight]); - for (IndexType k = 0; k < NumRegs; ++k) - vec_store(&accTileOut[k], acc[k]); + acc[k] = vec_load(&accTileIn[k]); + + for (IndexType i = 0; states_to_update[i]; ++i) + { + // Difference calculation for the deactivated features + for (const auto index : removed[i]) + { + const IndexType offset = HalfDimensions * index + j * TileHeight; + auto column = reinterpret_cast(&weights[offset]); + for (IndexType k = 0; k < NumRegs; ++k) + acc[k] = vec_sub_16(acc[k], column[k]); + } + + // Difference calculation for the activated features + for (const auto index : added[i]) + { + const IndexType offset = HalfDimensions * index + j * TileHeight; + auto column = reinterpret_cast(&weights[offset]); + for (IndexType k = 0; k < NumRegs; ++k) + acc[k] = vec_add_16(acc[k], column[k]); + } + + // Store accumulator + auto accTileOut = reinterpret_cast( + &states_to_update[i]->accumulator.accumulation[Perspective][j * TileHeight]); + for (IndexType k = 0; k < NumRegs; ++k) + vec_store(&accTileOut[k], acc[k]); + } } - } - - for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j) - { - // Load accumulator - auto accTilePsqtIn = reinterpret_cast( - &st->accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); - for (std::size_t k = 0; k < NumPsqtRegs; ++k) - psqt[k] = vec_load_psqt(&accTilePsqtIn[k]); - for (IndexType i = 0; states_to_update[i]; ++i) + for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j) { - // Difference calculation for the deactivated features - for (const auto index : removed[i]) - { - const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; - auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); + // Load accumulator + auto accTilePsqtIn = reinterpret_cast( + &st->accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); for (std::size_t k = 0; k < NumPsqtRegs; ++k) - psqt[k] = vec_sub_psqt_32(psqt[k], columnPsqt[k]); - } - - // Difference calculation for the activated features - for (const auto index : added[i]) - { - const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; - auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); - for (std::size_t k = 0; k < NumPsqtRegs; ++k) - psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]); - } - - // Store accumulator - auto accTilePsqtOut = reinterpret_cast( - &states_to_update[i]->accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); - for (std::size_t k = 0; k < NumPsqtRegs; ++k) - vec_store_psqt(&accTilePsqtOut[k], psqt[k]); + psqt[k] = vec_load_psqt(&accTilePsqtIn[k]); + + for (IndexType i = 0; states_to_update[i]; ++i) + { + // Difference calculation for the deactivated features + for (const auto index : removed[i]) + { + const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; + auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + psqt[k] = vec_sub_psqt_32(psqt[k], columnPsqt[k]); + } + + // Difference calculation for the activated features + for (const auto index : added[i]) + { + const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; + auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]); + } + + // Store accumulator + auto accTilePsqtOut = reinterpret_cast( + &states_to_update[i] + ->accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + vec_store_psqt(&accTilePsqtOut[k], psqt[k]); + } } - } - } + } #else - for (IndexType i = 0; states_to_update[i]; ++i) - { - std::memcpy(states_to_update[i]->accumulator.accumulation[Perspective], - st->accumulator.accumulation[Perspective], - HalfDimensions * sizeof(BiasType)); + for (IndexType i = 0; states_to_update[i]; ++i) + { + std::memcpy(states_to_update[i]->accumulator.accumulation[Perspective], + st->accumulator.accumulation[Perspective], + HalfDimensions * sizeof(BiasType)); - for (std::size_t k = 0; k < PSQTBuckets; ++k) - states_to_update[i]->accumulator.psqtAccumulation[Perspective][k] = st->accumulator.psqtAccumulation[Perspective][k]; + for (std::size_t k = 0; k < PSQTBuckets; ++k) + states_to_update[i]->accumulator.psqtAccumulation[Perspective][k] = + st->accumulator.psqtAccumulation[Perspective][k]; - st = states_to_update[i]; + st = states_to_update[i]; - // Difference calculation for the deactivated features - for (const auto index : removed[i]) - { - const IndexType offset = HalfDimensions * index; + // Difference calculation for the deactivated features + for (const auto index : removed[i]) + { + const IndexType offset = HalfDimensions * index; - for (IndexType j = 0; j < HalfDimensions; ++j) - st->accumulator.accumulation[Perspective][j] -= weights[offset + j]; + for (IndexType j = 0; j < HalfDimensions; ++j) + st->accumulator.accumulation[Perspective][j] -= weights[offset + j]; - for (std::size_t k = 0; k < PSQTBuckets; ++k) - st->accumulator.psqtAccumulation[Perspective][k] -= psqtWeights[index * PSQTBuckets + k]; - } + for (std::size_t k = 0; k < PSQTBuckets; ++k) + st->accumulator.psqtAccumulation[Perspective][k] -= + psqtWeights[index * PSQTBuckets + k]; + } - // Difference calculation for the activated features - for (const auto index : added[i]) - { - const IndexType offset = HalfDimensions * index; + // Difference calculation for the activated features + for (const auto index : added[i]) + { + const IndexType offset = HalfDimensions * index; - for (IndexType j = 0; j < HalfDimensions; ++j) - st->accumulator.accumulation[Perspective][j] += weights[offset + j]; + for (IndexType j = 0; j < HalfDimensions; ++j) + st->accumulator.accumulation[Perspective][j] += weights[offset + j]; - for (std::size_t k = 0; k < PSQTBuckets; ++k) - st->accumulator.psqtAccumulation[Perspective][k] += psqtWeights[index * PSQTBuckets + k]; + for (std::size_t k = 0; k < PSQTBuckets; ++k) + st->accumulator.psqtAccumulation[Perspective][k] += + psqtWeights[index * PSQTBuckets + k]; + } } - } #endif } template void update_accumulator_refresh(const Position& pos) const { - #ifdef VECTOR - // Gcc-10.2 unnecessarily spills AVX2 registers if this array - // is defined in the VECTOR code below, once in each branch - vec_t acc[NumRegs]; - psqt_vec_t psqt[NumPsqtRegs]; - #endif - - // Refresh the accumulator - // Could be extracted to a separate function because it's done in 2 places, - // but it's unclear if compilers would correctly handle register allocation. - auto& accumulator = pos.state()->accumulator; - accumulator.computed[Perspective] = true; - FeatureSet::IndexList active; - FeatureSet::append_active_indices(pos, active); - #ifdef VECTOR - for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) - { - auto biasesTile = reinterpret_cast( - &biases[j * TileHeight]); - for (IndexType k = 0; k < NumRegs; ++k) - acc[k] = biasesTile[k]; + // Gcc-10.2 unnecessarily spills AVX2 registers if this array + // is defined in the VECTOR code below, once in each branch + vec_t acc[NumRegs]; + psqt_vec_t psqt[NumPsqtRegs]; +#endif - for (const auto index : active) + // Refresh the accumulator + // Could be extracted to a separate function because it's done in 2 places, + // but it's unclear if compilers would correctly handle register allocation. + auto& accumulator = pos.state()->accumulator; + accumulator.computed[Perspective] = true; + FeatureSet::IndexList active; + FeatureSet::append_active_indices(pos, active); + +#ifdef VECTOR + for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) { - const IndexType offset = HalfDimensions * index + j * TileHeight; - auto column = reinterpret_cast(&weights[offset]); + auto biasesTile = reinterpret_cast(&biases[j * TileHeight]); + for (IndexType k = 0; k < NumRegs; ++k) + acc[k] = biasesTile[k]; - for (unsigned k = 0; k < NumRegs; ++k) - acc[k] = vec_add_16(acc[k], column[k]); - } + for (const auto index : active) + { + const IndexType offset = HalfDimensions * index + j * TileHeight; + auto column = reinterpret_cast(&weights[offset]); - auto accTile = reinterpret_cast( - &accumulator.accumulation[Perspective][j * TileHeight]); - for (unsigned k = 0; k < NumRegs; k++) - vec_store(&accTile[k], acc[k]); - } + for (unsigned k = 0; k < NumRegs; ++k) + acc[k] = vec_add_16(acc[k], column[k]); + } - for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j) - { - for (std::size_t k = 0; k < NumPsqtRegs; ++k) - psqt[k] = vec_zero_psqt(); + auto accTile = + reinterpret_cast(&accumulator.accumulation[Perspective][j * TileHeight]); + for (unsigned k = 0; k < NumRegs; k++) + vec_store(&accTile[k], acc[k]); + } - for (const auto index : active) + for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j) { - const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; - auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + psqt[k] = vec_zero_psqt(); - for (std::size_t k = 0; k < NumPsqtRegs; ++k) - psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]); - } + for (const auto index : active) + { + const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; + auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); + + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]); + } - auto accTilePsqt = reinterpret_cast( - &accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); - for (std::size_t k = 0; k < NumPsqtRegs; ++k) - vec_store_psqt(&accTilePsqt[k], psqt[k]); - } + auto accTilePsqt = reinterpret_cast( + &accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + vec_store_psqt(&accTilePsqt[k], psqt[k]); + } #else - std::memcpy(accumulator.accumulation[Perspective], biases, - HalfDimensions * sizeof(BiasType)); + std::memcpy(accumulator.accumulation[Perspective], biases, + HalfDimensions * sizeof(BiasType)); - for (std::size_t k = 0; k < PSQTBuckets; ++k) - accumulator.psqtAccumulation[Perspective][k] = 0; + for (std::size_t k = 0; k < PSQTBuckets; ++k) + accumulator.psqtAccumulation[Perspective][k] = 0; - for (const auto index : active) - { - const IndexType offset = HalfDimensions * index; + for (const auto index : active) + { + const IndexType offset = HalfDimensions * index; - for (IndexType j = 0; j < HalfDimensions; ++j) - accumulator.accumulation[Perspective][j] += weights[offset + j]; + for (IndexType j = 0; j < HalfDimensions; ++j) + accumulator.accumulation[Perspective][j] += weights[offset + j]; - for (std::size_t k = 0; k < PSQTBuckets; ++k) - accumulator.psqtAccumulation[Perspective][k] += psqtWeights[index * PSQTBuckets + k]; - } + for (std::size_t k = 0; k < PSQTBuckets; ++k) + accumulator.psqtAccumulation[Perspective][k] += + psqtWeights[index * PSQTBuckets + k]; + } #endif } template void hint_common_access_for_perspective(const Position& pos) const { - // Works like update_accumulator, but performs less work. - // Updates ONLY the accumulator for pos. - - // Look for a usable accumulator of an earlier position. We keep track - // of the estimated gain in terms of features to be added/subtracted. - // Fast early exit. - if (pos.state()->accumulator.computed[Perspective]) - return; - - auto [oldest_st, _] = try_find_computed_accumulator(pos); - - if (oldest_st->accumulator.computed[Perspective]) - { - // Only update current position accumulator to minimize work. - StateInfo* states_to_update[2] = { pos.state(), nullptr }; - update_accumulator_incremental(pos, oldest_st, states_to_update); - } - else - { - update_accumulator_refresh(pos); - } + // Works like update_accumulator, but performs less work. + // Updates ONLY the accumulator for pos. + + // Look for a usable accumulator of an earlier position. We keep track + // of the estimated gain in terms of features to be added/subtracted. + // Fast early exit. + if (pos.state()->accumulator.computed[Perspective]) + return; + + auto [oldest_st, _] = try_find_computed_accumulator(pos); + + if (oldest_st->accumulator.computed[Perspective]) + { + // Only update current position accumulator to minimize work. + StateInfo* states_to_update[2] = {pos.state(), nullptr}; + update_accumulator_incremental(pos, oldest_st, states_to_update); + } + else + { + update_accumulator_refresh(pos); + } } template void update_accumulator(const Position& pos) const { - auto [oldest_st, next] = try_find_computed_accumulator(pos); + auto [oldest_st, next] = try_find_computed_accumulator(pos); - if (oldest_st->accumulator.computed[Perspective]) - { - if (next == nullptr) - return; - - // Now update the accumulators listed in states_to_update[], where the last element is a sentinel. - // Currently we update 2 accumulators. - // 1. for the current position - // 2. the next accumulator after the computed one - // The heuristic may change in the future. - StateInfo *states_to_update[3] = - { next, next == pos.state() ? nullptr : pos.state(), nullptr }; - - update_accumulator_incremental(pos, oldest_st, states_to_update); - } - else - { - update_accumulator_refresh(pos); - } + if (oldest_st->accumulator.computed[Perspective]) + { + if (next == nullptr) + return; + + // Now update the accumulators listed in states_to_update[], where the last element is a sentinel. + // Currently we update 2 accumulators. + // 1. for the current position + // 2. the next accumulator after the computed one + // The heuristic may change in the future. + StateInfo* states_to_update[3] = {next, next == pos.state() ? nullptr : pos.state(), + nullptr}; + + update_accumulator_incremental(pos, oldest_st, states_to_update); + } + else + { + update_accumulator_refresh(pos); + } } alignas(CacheLineSize) BiasType biases[HalfDimensions]; alignas(CacheLineSize) WeightType weights[HalfDimensions * InputDimensions]; alignas(CacheLineSize) PSQTWeightType psqtWeights[InputDimensions * PSQTBuckets]; - }; +}; } // namespace Stockfish::Eval::NNUE -#endif // #ifndef NNUE_FEATURE_TRANSFORMER_H_INCLUDED +#endif // #ifndef NNUE_FEATURE_TRANSFORMER_H_INCLUDED diff --git a/src/position.cpp b/src/position.cpp index ada371eb951..f7354b3d77c 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -46,59 +46,57 @@ namespace Stockfish { namespace Zobrist { - Key psq[PIECE_NB][SQUARE_NB]; - Key enpassant[FILE_NB]; - Key castling[CASTLING_RIGHT_NB]; - Key side; +Key psq[PIECE_NB][SQUARE_NB]; +Key enpassant[FILE_NB]; +Key castling[CASTLING_RIGHT_NB]; +Key side; } namespace { constexpr std::string_view PieceToChar(" PNBRQK pnbrqk"); -constexpr Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, - B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING }; -} // namespace +constexpr Piece Pieces[] = {W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, + B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING}; +} // namespace // operator<<(Position) returns an ASCII representation of the position std::ostream& operator<<(std::ostream& os, const Position& pos) { - os << "\n +---+---+---+---+---+---+---+---+\n"; + os << "\n +---+---+---+---+---+---+---+---+\n"; - for (Rank r = RANK_8; r >= RANK_1; --r) - { - for (File f = FILE_A; f <= FILE_H; ++f) - os << " | " << PieceToChar[pos.piece_on(make_square(f, r))]; - - os << " | " << (1 + r) << "\n +---+---+---+---+---+---+---+---+\n"; - } + for (Rank r = RANK_8; r >= RANK_1; --r) + { + for (File f = FILE_A; f <= FILE_H; ++f) + os << " | " << PieceToChar[pos.piece_on(make_square(f, r))]; - os << " a b c d e f g h\n" - << "\nFen: " << pos.fen() << "\nKey: " << std::hex << std::uppercase - << std::setfill('0') << std::setw(16) << pos.key() - << std::setfill(' ') << std::dec << "\nCheckers: "; + os << " | " << (1 + r) << "\n +---+---+---+---+---+---+---+---+\n"; + } - for (Bitboard b = pos.checkers(); b; ) - os << UCI::square(pop_lsb(b)) << " "; + os << " a b c d e f g h\n" + << "\nFen: " << pos.fen() << "\nKey: " << std::hex << std::uppercase << std::setfill('0') + << std::setw(16) << pos.key() << std::setfill(' ') << std::dec << "\nCheckers: "; - if ( int(Tablebases::MaxCardinality) >= popcount(pos.pieces()) - && !pos.can_castle(ANY_CASTLING)) - { - StateInfo st; - ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize); + for (Bitboard b = pos.checkers(); b;) + os << UCI::square(pop_lsb(b)) << " "; - Position p; - p.set(pos.fen(), pos.is_chess960(), &st, pos.this_thread()); - Tablebases::ProbeState s1, s2; - Tablebases::WDLScore wdl = Tablebases::probe_wdl(p, &s1); - int dtz = Tablebases::probe_dtz(p, &s2); - os << "\nTablebases WDL: " << std::setw(4) << wdl << " (" << s1 << ")" - << "\nTablebases DTZ: " << std::setw(4) << dtz << " (" << s2 << ")"; - } + if (int(Tablebases::MaxCardinality) >= popcount(pos.pieces()) && !pos.can_castle(ANY_CASTLING)) + { + StateInfo st; + ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize); + + Position p; + p.set(pos.fen(), pos.is_chess960(), &st, pos.this_thread()); + Tablebases::ProbeState s1, s2; + Tablebases::WDLScore wdl = Tablebases::probe_wdl(p, &s1); + int dtz = Tablebases::probe_dtz(p, &s2); + os << "\nTablebases WDL: " << std::setw(4) << wdl << " (" << s1 << ")" + << "\nTablebases DTZ: " << std::setw(4) << dtz << " (" << s2 << ")"; + } - return os; + return os; } @@ -112,7 +110,7 @@ inline int H1(Key h) { return h & 0x1fff; } inline int H2(Key h) { return (h >> 16) & 0x1fff; } // Cuckoo tables with Zobrist hashes of valid reversible moves, and the moves themselves -Key cuckoo[8192]; +Key cuckoo[8192]; Move cuckooMove[8192]; @@ -120,43 +118,43 @@ Move cuckooMove[8192]; void Position::init() { - PRNG rng(1070372); - - for (Piece pc : Pieces) - for (Square s = SQ_A1; s <= SQ_H8; ++s) - Zobrist::psq[pc][s] = rng.rand(); - - for (File f = FILE_A; f <= FILE_H; ++f) - Zobrist::enpassant[f] = rng.rand(); - - for (int cr = NO_CASTLING; cr <= ANY_CASTLING; ++cr) - Zobrist::castling[cr] = rng.rand(); - - Zobrist::side = rng.rand(); - - // Prepare the cuckoo tables - std::memset(cuckoo, 0, sizeof(cuckoo)); - std::memset(cuckooMove, 0, sizeof(cuckooMove)); - [[maybe_unused]] int count = 0; - for (Piece pc : Pieces) - for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) - for (Square s2 = Square(s1 + 1); s2 <= SQ_H8; ++s2) - if ((type_of(pc) != PAWN) && (attacks_bb(type_of(pc), s1, 0) & s2)) - { - Move move = make_move(s1, s2); - Key key = Zobrist::psq[pc][s1] ^ Zobrist::psq[pc][s2] ^ Zobrist::side; - int i = H1(key); - while (true) - { - std::swap(cuckoo[i], key); - std::swap(cuckooMove[i], move); - if (move == MOVE_NONE) // Arrived at empty slot? - break; - i = (i == H1(key)) ? H2(key) : H1(key); // Push victim to alternative slot - } - count++; - } - assert(count == 3668); + PRNG rng(1070372); + + for (Piece pc : Pieces) + for (Square s = SQ_A1; s <= SQ_H8; ++s) + Zobrist::psq[pc][s] = rng.rand(); + + for (File f = FILE_A; f <= FILE_H; ++f) + Zobrist::enpassant[f] = rng.rand(); + + for (int cr = NO_CASTLING; cr <= ANY_CASTLING; ++cr) + Zobrist::castling[cr] = rng.rand(); + + Zobrist::side = rng.rand(); + + // Prepare the cuckoo tables + std::memset(cuckoo, 0, sizeof(cuckoo)); + std::memset(cuckooMove, 0, sizeof(cuckooMove)); + [[maybe_unused]] int count = 0; + for (Piece pc : Pieces) + for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) + for (Square s2 = Square(s1 + 1); s2 <= SQ_H8; ++s2) + if ((type_of(pc) != PAWN) && (attacks_bb(type_of(pc), s1, 0) & s2)) + { + Move move = make_move(s1, s2); + Key key = Zobrist::psq[pc][s1] ^ Zobrist::psq[pc][s2] ^ Zobrist::side; + int i = H1(key); + while (true) + { + std::swap(cuckoo[i], key); + std::swap(cuckooMove[i], move); + if (move == MOVE_NONE) // Arrived at empty slot? + break; + i = (i == H1(key)) ? H2(key) : H1(key); // Push victim to alternative slot + } + count++; + } + assert(count == 3668); } @@ -165,7 +163,7 @@ void Position::init() { // this is assumed to be the responsibility of the GUI. Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Thread* th) { -/* + /* A FEN string defines a particular position using only the ASCII character set. A FEN string contains six fields separated by a space. The fields are: @@ -200,100 +198,103 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th incremented after Black's move. */ - unsigned char col, row, token; - size_t idx; - Square sq = SQ_A8; - std::istringstream ss(fenStr); - - std::memset(this, 0, sizeof(Position)); - std::memset(si, 0, sizeof(StateInfo)); - st = si; + unsigned char col, row, token; + size_t idx; + Square sq = SQ_A8; + std::istringstream ss(fenStr); - ss >> std::noskipws; + std::memset(this, 0, sizeof(Position)); + std::memset(si, 0, sizeof(StateInfo)); + st = si; - // 1. Piece placement - while ((ss >> token) && !isspace(token)) - { - if (isdigit(token)) - sq += (token - '0') * EAST; // Advance the given number of files + ss >> std::noskipws; - else if (token == '/') - sq += 2 * SOUTH; - - else if ((idx = PieceToChar.find(token)) != string::npos) { - put_piece(Piece(idx), sq); - ++sq; - } - } + // 1. Piece placement + while ((ss >> token) && !isspace(token)) + { + if (isdigit(token)) + sq += (token - '0') * EAST; // Advance the given number of files - // 2. Active color - ss >> token; - sideToMove = (token == 'w' ? WHITE : BLACK); - ss >> token; + else if (token == '/') + sq += 2 * SOUTH; - // 3. Castling availability. Compatible with 3 standards: Normal FEN standard, - // Shredder-FEN that uses the letters of the columns on which the rooks began - // the game instead of KQkq and also X-FEN standard that, in case of Chess960, - // if an inner rook is associated with the castling right, the castling tag is - // replaced by the file letter of the involved rook, as for the Shredder-FEN. - while ((ss >> token) && !isspace(token)) - { - Square rsq; - Color c = islower(token) ? BLACK : WHITE; - Piece rook = make_piece(c, ROOK); + else if ((idx = PieceToChar.find(token)) != string::npos) + { + put_piece(Piece(idx), sq); + ++sq; + } + } - token = char(toupper(token)); + // 2. Active color + ss >> token; + sideToMove = (token == 'w' ? WHITE : BLACK); + ss >> token; + + // 3. Castling availability. Compatible with 3 standards: Normal FEN standard, + // Shredder-FEN that uses the letters of the columns on which the rooks began + // the game instead of KQkq and also X-FEN standard that, in case of Chess960, + // if an inner rook is associated with the castling right, the castling tag is + // replaced by the file letter of the involved rook, as for the Shredder-FEN. + while ((ss >> token) && !isspace(token)) + { + Square rsq; + Color c = islower(token) ? BLACK : WHITE; + Piece rook = make_piece(c, ROOK); - if (token == 'K') - for (rsq = relative_square(c, SQ_H1); piece_on(rsq) != rook; --rsq) {} + token = char(toupper(token)); - else if (token == 'Q') - for (rsq = relative_square(c, SQ_A1); piece_on(rsq) != rook; ++rsq) {} + if (token == 'K') + for (rsq = relative_square(c, SQ_H1); piece_on(rsq) != rook; --rsq) + {} - else if (token >= 'A' && token <= 'H') - rsq = make_square(File(token - 'A'), relative_rank(c, RANK_1)); + else if (token == 'Q') + for (rsq = relative_square(c, SQ_A1); piece_on(rsq) != rook; ++rsq) + {} - else - continue; + else if (token >= 'A' && token <= 'H') + rsq = make_square(File(token - 'A'), relative_rank(c, RANK_1)); - set_castling_right(c, rsq); - } + else + continue; - // 4. En passant square. - // Ignore if square is invalid or not on side to move relative rank 6. - bool enpassant = false; + set_castling_right(c, rsq); + } - if ( ((ss >> col) && (col >= 'a' && col <= 'h')) - && ((ss >> row) && (row == (sideToMove == WHITE ? '6' : '3')))) - { - st->epSquare = make_square(File(col - 'a'), Rank(row - '1')); + // 4. En passant square. + // Ignore if square is invalid or not on side to move relative rank 6. + bool enpassant = false; - // En passant square will be considered only if - // a) side to move have a pawn threatening epSquare - // b) there is an enemy pawn in front of epSquare - // c) there is no piece on epSquare or behind epSquare - enpassant = pawn_attacks_bb(~sideToMove, st->epSquare) & pieces(sideToMove, PAWN) - && (pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove))) - && !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove)))); - } + if (((ss >> col) && (col >= 'a' && col <= 'h')) + && ((ss >> row) && (row == (sideToMove == WHITE ? '6' : '3')))) + { + st->epSquare = make_square(File(col - 'a'), Rank(row - '1')); + + // En passant square will be considered only if + // a) side to move have a pawn threatening epSquare + // b) there is an enemy pawn in front of epSquare + // c) there is no piece on epSquare or behind epSquare + enpassant = pawn_attacks_bb(~sideToMove, st->epSquare) & pieces(sideToMove, PAWN) + && (pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove))) + && !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove)))); + } - if (!enpassant) - st->epSquare = SQ_NONE; + if (!enpassant) + st->epSquare = SQ_NONE; - // 5-6. Halfmove clock and fullmove number - ss >> std::skipws >> st->rule50 >> gamePly; + // 5-6. Halfmove clock and fullmove number + ss >> std::skipws >> st->rule50 >> gamePly; - // Convert from fullmove starting from 1 to gamePly starting from 0, - // handle also common incorrect FEN with fullmove = 0. - gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK); + // Convert from fullmove starting from 1 to gamePly starting from 0, + // handle also common incorrect FEN with fullmove = 0. + gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK); - chess960 = isChess960; - thisThread = th; - set_state(); + chess960 = isChess960; + thisThread = th; + set_state(); - assert(pos_is_ok()); + assert(pos_is_ok()); - return *this; + return *this; } @@ -302,19 +303,18 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th void Position::set_castling_right(Color c, Square rfrom) { - Square kfrom = square(c); - CastlingRights cr = c & (kfrom < rfrom ? KING_SIDE: QUEEN_SIDE); + Square kfrom = square(c); + CastlingRights cr = c & (kfrom < rfrom ? KING_SIDE : QUEEN_SIDE); - st->castlingRights |= cr; - castlingRightsMask[kfrom] |= cr; - castlingRightsMask[rfrom] |= cr; - castlingRookSquare[cr] = rfrom; + st->castlingRights |= cr; + castlingRightsMask[kfrom] |= cr; + castlingRightsMask[rfrom] |= cr; + castlingRookSquare[cr] = rfrom; - Square kto = relative_square(c, cr & KING_SIDE ? SQ_G1 : SQ_C1); - Square rto = relative_square(c, cr & 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)) - & ~(kfrom | rfrom); + castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto)) & ~(kfrom | rfrom); } @@ -322,17 +322,17 @@ void Position::set_castling_right(Color c, Square rfrom) { void Position::set_check_info() const { - update_slider_blockers(WHITE); - update_slider_blockers(BLACK); + update_slider_blockers(WHITE); + update_slider_blockers(BLACK); - Square ksq = square(~sideToMove); + Square ksq = square(~sideToMove); - st->checkSquares[PAWN] = pawn_attacks_bb(~sideToMove, ksq); - st->checkSquares[KNIGHT] = attacks_bb(ksq); - st->checkSquares[BISHOP] = attacks_bb(ksq, pieces()); - st->checkSquares[ROOK] = attacks_bb(ksq, pieces()); - st->checkSquares[QUEEN] = st->checkSquares[BISHOP] | st->checkSquares[ROOK]; - st->checkSquares[KING] = 0; + st->checkSquares[PAWN] = pawn_attacks_bb(~sideToMove, ksq); + st->checkSquares[KNIGHT] = attacks_bb(ksq); + st->checkSquares[BISHOP] = attacks_bb(ksq, pieces()); + st->checkSquares[ROOK] = attacks_bb(ksq, pieces()); + st->checkSquares[QUEEN] = st->checkSquares[BISHOP] | st->checkSquares[ROOK]; + st->checkSquares[KING] = 0; } @@ -342,33 +342,33 @@ void Position::set_check_info() const { void Position::set_state() const { - st->key = st->materialKey = 0; - st->nonPawnMaterial[WHITE] = st->nonPawnMaterial[BLACK] = VALUE_ZERO; - st->checkersBB = attackers_to(square(sideToMove)) & pieces(~sideToMove); + st->key = st->materialKey = 0; + st->nonPawnMaterial[WHITE] = st->nonPawnMaterial[BLACK] = VALUE_ZERO; + st->checkersBB = attackers_to(square(sideToMove)) & pieces(~sideToMove); - set_check_info(); + set_check_info(); - for (Bitboard b = pieces(); b; ) - { - Square s = pop_lsb(b); - Piece pc = piece_on(s); - st->key ^= Zobrist::psq[pc][s]; + for (Bitboard b = pieces(); b;) + { + Square s = pop_lsb(b); + Piece pc = piece_on(s); + st->key ^= Zobrist::psq[pc][s]; - if (type_of(pc) != KING && type_of(pc) != PAWN) - st->nonPawnMaterial[color_of(pc)] += PieceValue[pc]; - } + if (type_of(pc) != KING && type_of(pc) != PAWN) + st->nonPawnMaterial[color_of(pc)] += PieceValue[pc]; + } - if (st->epSquare != SQ_NONE) - st->key ^= Zobrist::enpassant[file_of(st->epSquare)]; + if (st->epSquare != SQ_NONE) + st->key ^= Zobrist::enpassant[file_of(st->epSquare)]; - if (sideToMove == BLACK) - st->key ^= Zobrist::side; + if (sideToMove == BLACK) + st->key ^= Zobrist::side; - st->key ^= Zobrist::castling[st->castlingRights]; + st->key ^= Zobrist::castling[st->castlingRights]; - for (Piece pc : Pieces) - for (int cnt = 0; cnt < pieceCount[pc]; ++cnt) - st->materialKey ^= Zobrist::psq[pc][cnt]; + for (Piece pc : Pieces) + for (int cnt = 0; cnt < pieceCount[pc]; ++cnt) + st->materialKey ^= Zobrist::psq[pc][cnt]; } @@ -378,20 +378,20 @@ void Position::set_state() const { Position& Position::set(const string& code, Color c, StateInfo* si) { - assert(code[0] == 'K'); + assert(code[0] == 'K'); - string sides[] = { code.substr(code.find('K', 1)), // Weak - code.substr(0, std::min(code.find('v'), code.find('K', 1))) }; // Strong + string sides[] = {code.substr(code.find('K', 1)), // Weak + code.substr(0, std::min(code.find('v'), code.find('K', 1)))}; // Strong - assert(sides[0].length() > 0 && sides[0].length() < 8); - assert(sides[1].length() > 0 && sides[1].length() < 8); + assert(sides[0].length() > 0 && sides[0].length() < 8); + assert(sides[1].length() > 0 && sides[1].length() < 8); - std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower); + std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower); - string fenStr = "8/" + sides[0] + char(8 - sides[0].length() + '0') + "/8/8/8/8/" - + sides[1] + char(8 - sides[1].length() + '0') + "/8 w - - 0 10"; + string fenStr = "8/" + sides[0] + char(8 - sides[0].length() + '0') + "/8/8/8/8/" + sides[1] + + char(8 - sides[1].length() + '0') + "/8 w - - 0 10"; - return set(fenStr, false, si, nullptr); + return set(fenStr, false, si, nullptr); } @@ -400,48 +400,48 @@ Position& Position::set(const string& code, Color c, StateInfo* si) { string Position::fen() const { - int emptyCnt; - std::ostringstream ss; + int emptyCnt; + std::ostringstream ss; - for (Rank r = RANK_8; r >= RANK_1; --r) - { - for (File f = FILE_A; f <= FILE_H; ++f) - { - for (emptyCnt = 0; f <= FILE_H && empty(make_square(f, r)); ++f) - ++emptyCnt; + for (Rank r = RANK_8; r >= RANK_1; --r) + { + for (File f = FILE_A; f <= FILE_H; ++f) + { + for (emptyCnt = 0; f <= FILE_H && empty(make_square(f, r)); ++f) + ++emptyCnt; - if (emptyCnt) - ss << emptyCnt; + if (emptyCnt) + ss << emptyCnt; - if (f <= FILE_H) - ss << PieceToChar[piece_on(make_square(f, r))]; - } + if (f <= FILE_H) + ss << PieceToChar[piece_on(make_square(f, r))]; + } - if (r > RANK_1) - ss << '/'; - } + if (r > RANK_1) + ss << '/'; + } - ss << (sideToMove == WHITE ? " w " : " b "); + ss << (sideToMove == WHITE ? " w " : " b "); - if (can_castle(WHITE_OO)) - ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE_OO ))) : 'K'); + if (can_castle(WHITE_OO)) + ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE_OO))) : 'K'); - if (can_castle(WHITE_OOO)) - ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE_OOO))) : 'Q'); + if (can_castle(WHITE_OOO)) + ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE_OOO))) : 'Q'); - if (can_castle(BLACK_OO)) - ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK_OO ))) : 'k'); + if (can_castle(BLACK_OO)) + ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK_OO))) : 'k'); - if (can_castle(BLACK_OOO)) - ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK_OOO))) : 'q'); + if (can_castle(BLACK_OOO)) + ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK_OOO))) : 'q'); - if (!can_castle(ANY_CASTLING)) - ss << '-'; + if (!can_castle(ANY_CASTLING)) + ss << '-'; - ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(ep_square()) + " ") - << st->rule50 << " " << 1 + (gamePly - (sideToMove == BLACK)) / 2; + ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(ep_square()) + " ") << st->rule50 + << " " << 1 + (gamePly - (sideToMove == BLACK)) / 2; - return ss.str(); + return ss.str(); } // update_slider_blockers() calculates st->blockersForKing[c] and st->pinners[~c], @@ -449,28 +449,29 @@ string Position::fen() const { // and the slider pieces of color ~c pinning pieces of color c to the king. void Position::update_slider_blockers(Color c) const { - Square ksq = square(c); - - st->blockersForKing[c] = 0; - st->pinners[~c] = 0; + Square ksq = square(c); - // Snipers are sliders that attack 's' when a piece and other snipers are removed - Bitboard snipers = ( (attacks_bb< ROOK>(ksq) & pieces(QUEEN, ROOK)) - | (attacks_bb(ksq) & pieces(QUEEN, BISHOP))) & pieces(~c); - Bitboard occupancy = pieces() ^ snipers; + st->blockersForKing[c] = 0; + st->pinners[~c] = 0; - while (snipers) - { - Square sniperSq = pop_lsb(snipers); - Bitboard b = between_bb(ksq, sniperSq) & occupancy; + // Snipers are sliders that attack 's' when a piece and other snipers are removed + Bitboard snipers = ((attacks_bb(ksq) & pieces(QUEEN, ROOK)) + | (attacks_bb(ksq) & pieces(QUEEN, BISHOP))) + & pieces(~c); + Bitboard occupancy = pieces() ^ snipers; - if (b && !more_than_one(b)) + while (snipers) { - st->blockersForKing[c] |= b; - if (b & pieces(c)) - st->pinners[~c] |= sniperSq; + Square sniperSq = pop_lsb(snipers); + Bitboard b = between_bb(ksq, sniperSq) & occupancy; + + if (b && !more_than_one(b)) + { + st->blockersForKing[c] |= b; + if (b & pieces(c)) + st->pinners[~c] |= sniperSq; + } } - } } @@ -479,12 +480,12 @@ void Position::update_slider_blockers(Color c) const { Bitboard Position::attackers_to(Square s, Bitboard occupied) const { - return (pawn_attacks_bb(BLACK, s) & pieces(WHITE, PAWN)) - | (pawn_attacks_bb(WHITE, s) & pieces(BLACK, PAWN)) - | (attacks_bb(s) & pieces(KNIGHT)) - | (attacks_bb< ROOK>(s, occupied) & pieces( ROOK, QUEEN)) - | (attacks_bb(s, occupied) & pieces(BISHOP, QUEEN)) - | (attacks_bb(s) & pieces(KING)); + return (pawn_attacks_bb(BLACK, s) & pieces(WHITE, PAWN)) + | (pawn_attacks_bb(WHITE, s) & pieces(BLACK, PAWN)) + | (attacks_bb(s) & pieces(KNIGHT)) + | (attacks_bb(s, occupied) & pieces(ROOK, QUEEN)) + | (attacks_bb(s, occupied) & pieces(BISHOP, QUEEN)) + | (attacks_bb(s) & pieces(KING)); } @@ -492,60 +493,59 @@ Bitboard Position::attackers_to(Square s, Bitboard occupied) const { bool Position::legal(Move m) const { - assert(is_ok(m)); + assert(is_ok(m)); - Color us = sideToMove; - Square from = from_sq(m); - Square to = to_sq(m); + Color us = sideToMove; + Square from = from_sq(m); + Square to = to_sq(m); - assert(color_of(moved_piece(m)) == us); - assert(piece_on(square(us)) == make_piece(us, KING)); + assert(color_of(moved_piece(m)) == us); + assert(piece_on(square(us)) == make_piece(us, KING)); - // En passant captures are a tricky special case. Because they are rather - // uncommon, we do it simply by testing whether the king is attacked after - // the move is made. - if (type_of(m) == EN_PASSANT) - { - Square ksq = square(us); - Square capsq = to - pawn_push(us); - Bitboard occupied = (pieces() ^ from ^ capsq) | to; + // En passant captures are a tricky special case. Because they are rather + // uncommon, we do it simply by testing whether the king is attacked after + // the move is made. + if (type_of(m) == EN_PASSANT) + { + Square ksq = square(us); + Square capsq = to - pawn_push(us); + Bitboard occupied = (pieces() ^ from ^ capsq) | to; - assert(to == ep_square()); - assert(moved_piece(m) == make_piece(us, PAWN)); - assert(piece_on(capsq) == make_piece(~us, PAWN)); - assert(piece_on(to) == NO_PIECE); + assert(to == ep_square()); + assert(moved_piece(m) == make_piece(us, PAWN)); + assert(piece_on(capsq) == make_piece(~us, PAWN)); + assert(piece_on(to) == NO_PIECE); - return !(attacks_bb< ROOK>(ksq, occupied) & pieces(~us, QUEEN, ROOK)) + return !(attacks_bb(ksq, occupied) & pieces(~us, QUEEN, ROOK)) && !(attacks_bb(ksq, occupied) & pieces(~us, QUEEN, BISHOP)); - } - - // Castling moves generation does not check if the castling path is clear of - // enemy attacks, it is delayed at a later time: now! - if (type_of(m) == CASTLING) - { - // After castling, the rook and king final positions are the same in - // Chess960 as they would be in standard chess. - to = relative_square(us, to > from ? SQ_G1 : SQ_C1); - Direction step = to > from ? WEST : EAST; - - for (Square s = to; s != from; s += step) - if (attackers_to(s) & pieces(~us)) - return false; - - // In case of Chess960, verify if the Rook blocks some checks. - // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1. - return !chess960 || !(blockers_for_king(us) & to_sq(m)); - } - - // If the moving piece is a king, check whether the destination square is - // attacked by the opponent. - if (type_of(piece_on(from)) == KING) - return !(attackers_to(to, pieces() ^ from) & pieces(~us)); - - // A non-king move is legal if and only if it is not pinned or it - // is moving along the ray towards or away from the king. - return !(blockers_for_king(us) & from) - || aligned(from, to, square(us)); + } + + // Castling moves generation does not check if the castling path is clear of + // enemy attacks, it is delayed at a later time: now! + if (type_of(m) == CASTLING) + { + // After castling, the rook and king final positions are the same in + // Chess960 as they would be in standard chess. + to = relative_square(us, to > from ? SQ_G1 : SQ_C1); + Direction step = to > from ? WEST : EAST; + + for (Square s = to; s != from; s += step) + if (attackers_to(s) & pieces(~us)) + return false; + + // In case of Chess960, verify if the Rook blocks some checks. + // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1. + return !chess960 || !(blockers_for_king(us) & to_sq(m)); + } + + // If the moving piece is a king, check whether the destination square is + // attacked by the opponent. + if (type_of(piece_on(from)) == KING) + return !(attackers_to(to, pieces() ^ from) & pieces(~us)); + + // A non-king move is legal if and only if it is not pinned or it + // is moving along the ray towards or away from the king. + return !(blockers_for_king(us) & from) || aligned(from, to, square(us)); } @@ -555,70 +555,68 @@ bool Position::legal(Move m) const { bool Position::pseudo_legal(const Move m) const { - Color us = sideToMove; - Square from = from_sq(m); - Square to = to_sq(m); - Piece pc = moved_piece(m); - - // Use a slower but simpler function for uncommon cases - // yet we skip the legality check of MoveList(). - if (type_of(m) != NORMAL) - return checkers() ? MoveList< EVASIONS>(*this).contains(m) - : MoveList(*this).contains(m); - - // Is not a promotion, so the promotion piece must be empty - assert(promotion_type(m) - KNIGHT == NO_PIECE_TYPE); - - // If the 'from' square is not occupied by a piece belonging to the side to - // move, the move is obviously not legal. - if (pc == NO_PIECE || color_of(pc) != us) - return false; - - // The destination square cannot be occupied by a friendly piece - if (pieces(us) & to) - return false; - - // Handle the special case of a pawn move - if (type_of(pc) == PAWN) - { - // We have already handled promotion moves, so destination - // cannot be on the 8th/1st rank. - if ((Rank8BB | Rank1BB) & to) - return false; - - if ( !(pawn_attacks_bb(us, from) & pieces(~us) & to) // Not a capture - && !((from + pawn_push(us) == to) && empty(to)) // Not a single push - && !( (from + 2 * pawn_push(us) == to) // Not a double push - && (relative_rank(us, from) == RANK_2) - && empty(to) - && empty(to - pawn_push(us)))) - return false; - } - else if (!(attacks_bb(type_of(pc), from, pieces()) & to)) - return false; - - // Evasions generator already takes care to avoid some kind of illegal moves - // and legal() relies on this. We therefore have to take care that the same - // kind of moves are filtered out here. - if (checkers()) - { - if (type_of(pc) != KING) - { - // Double check? In this case, a king move is required - if (more_than_one(checkers())) - return false; - - // Our move must be a blocking interposition or a capture of the checking piece - if (!(between_bb(square(us), lsb(checkers())) & to)) - return false; - } - // In case of king moves under check we have to remove the king so as to catch - // invalid moves like b1a1 when opposite queen is on c1. - else if (attackers_to(to, pieces() ^ from) & pieces(~us)) - return false; - } - - return true; + Color us = sideToMove; + Square from = from_sq(m); + Square to = to_sq(m); + Piece pc = moved_piece(m); + + // Use a slower but simpler function for uncommon cases + // yet we skip the legality check of MoveList(). + if (type_of(m) != NORMAL) + return checkers() ? MoveList(*this).contains(m) + : MoveList(*this).contains(m); + + // Is not a promotion, so the promotion piece must be empty + assert(promotion_type(m) - KNIGHT == NO_PIECE_TYPE); + + // If the 'from' square is not occupied by a piece belonging to the side to + // move, the move is obviously not legal. + if (pc == NO_PIECE || color_of(pc) != us) + return false; + + // The destination square cannot be occupied by a friendly piece + if (pieces(us) & to) + return false; + + // Handle the special case of a pawn move + if (type_of(pc) == PAWN) + { + // We have already handled promotion moves, so destination + // cannot be on the 8th/1st rank. + if ((Rank8BB | Rank1BB) & to) + return false; + + if (!(pawn_attacks_bb(us, from) & pieces(~us) & to) // Not a capture + && !((from + pawn_push(us) == to) && empty(to)) // Not a single push + && !((from + 2 * pawn_push(us) == to) // Not a double push + && (relative_rank(us, from) == RANK_2) && empty(to) && empty(to - pawn_push(us)))) + return false; + } + else if (!(attacks_bb(type_of(pc), from, pieces()) & to)) + return false; + + // Evasions generator already takes care to avoid some kind of illegal moves + // and legal() relies on this. We therefore have to take care that the same + // kind of moves are filtered out here. + if (checkers()) + { + if (type_of(pc) != KING) + { + // Double check? In this case, a king move is required + if (more_than_one(checkers())) + return false; + + // Our move must be a blocking interposition or a capture of the checking piece + if (!(between_bb(square(us), lsb(checkers())) & to)) + return false; + } + // In case of king moves under check we have to remove the king so as to catch + // invalid moves like b1a1 when opposite queen is on c1. + else if (attackers_to(to, pieces() ^ from) & pieces(~us)) + return false; + } + + return true; } @@ -626,49 +624,48 @@ bool Position::pseudo_legal(const Move m) const { bool Position::gives_check(Move m) const { - assert(is_ok(m)); - assert(color_of(moved_piece(m)) == sideToMove); - - Square from = from_sq(m); - Square to = to_sq(m); - - // Is there a direct check? - if (check_squares(type_of(piece_on(from))) & to) - return true; - - // Is there a discovered check? - if (blockers_for_king(~sideToMove) & from) - return !aligned(from, to, square(~sideToMove)) - || type_of(m) == CASTLING; - - switch (type_of(m)) - { - case NORMAL: - return false; - - case PROMOTION: - return attacks_bb(promotion_type(m), to, pieces() ^ from) & square(~sideToMove); - - // En passant capture with check? We have already handled the case - // of direct checks and ordinary discovered check, so the only case we - // need to handle is the unusual case of a discovered check through - // the captured pawn. - case EN_PASSANT: - { - Square capsq = make_square(file_of(to), rank_of(from)); - Bitboard b = (pieces() ^ from ^ capsq) | to; - - return (attacks_bb< ROOK>(square(~sideToMove), b) & pieces(sideToMove, QUEEN, ROOK)) - | (attacks_bb(square(~sideToMove), b) & pieces(sideToMove, QUEEN, BISHOP)); - } - default: //CASTLING - { - // Castling is encoded as 'king captures the rook' - Square rto = relative_square(sideToMove, to > from ? SQ_F1 : SQ_D1); - - return check_squares(ROOK) & rto; - } - } + assert(is_ok(m)); + assert(color_of(moved_piece(m)) == sideToMove); + + Square from = from_sq(m); + Square to = to_sq(m); + + // Is there a direct check? + if (check_squares(type_of(piece_on(from))) & to) + return true; + + // Is there a discovered check? + if (blockers_for_king(~sideToMove) & from) + return !aligned(from, to, square(~sideToMove)) || type_of(m) == CASTLING; + + switch (type_of(m)) + { + case NORMAL : + return false; + + case PROMOTION : + return attacks_bb(promotion_type(m), to, pieces() ^ from) & square(~sideToMove); + + // En passant capture with check? We have already handled the case + // of direct checks and ordinary discovered check, so the only case we + // need to handle is the unusual case of a discovered check through + // the captured pawn. + case EN_PASSANT : { + Square capsq = make_square(file_of(to), rank_of(from)); + Bitboard b = (pieces() ^ from ^ capsq) | to; + + return (attacks_bb(square(~sideToMove), b) & pieces(sideToMove, QUEEN, ROOK)) + | (attacks_bb(square(~sideToMove), b) + & pieces(sideToMove, QUEEN, BISHOP)); + } + default : //CASTLING + { + // Castling is encoded as 'king captures the rook' + Square rto = relative_square(sideToMove, to > from ? SQ_F1 : SQ_D1); + + return check_squares(ROOK) & rto; + } + } } @@ -678,195 +675,195 @@ bool Position::gives_check(Move m) const { void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { - assert(is_ok(m)); - assert(&newSt != st); - - thisThread->nodes.fetch_add(1, std::memory_order_relaxed); - Key k = st->key ^ Zobrist::side; - - // Copy some fields of the old state to our new StateInfo object except the - // ones which are going to be recalculated from scratch anyway and then switch - // our state pointer to point to the new (ready to be updated) state. - std::memcpy(&newSt, st, offsetof(StateInfo, key)); - newSt.previous = st; - st = &newSt; - - // Increment ply counters. In particular, rule50 will be reset to zero later on - // in case of a capture or a pawn move. - ++gamePly; - ++st->rule50; - ++st->pliesFromNull; - - // Used by NNUE - st->accumulator.computed[WHITE] = false; - st->accumulator.computed[BLACK] = false; - auto& dp = st->dirtyPiece; - dp.dirty_num = 1; - - Color us = sideToMove; - Color them = ~us; - Square from = from_sq(m); - Square to = to_sq(m); - Piece pc = piece_on(from); - Piece captured = type_of(m) == EN_PASSANT ? make_piece(them, PAWN) : piece_on(to); - - assert(color_of(pc) == us); - assert(captured == NO_PIECE || color_of(captured) == (type_of(m) != CASTLING ? them : us)); - assert(type_of(captured) != KING); - - if (type_of(m) == CASTLING) - { - assert(pc == make_piece(us, KING)); - assert(captured == make_piece(us, ROOK)); - - Square rfrom, rto; - do_castling(us, from, to, rfrom, rto); - - k ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto]; - captured = NO_PIECE; - } - - if (captured) - { - Square capsq = to; - - // If the captured piece is a pawn, update pawn hash key, otherwise - // update non-pawn material. - if (type_of(captured) == PAWN) - { - if (type_of(m) == EN_PASSANT) - { - capsq -= pawn_push(us); - - assert(pc == make_piece(us, PAWN)); - assert(to == st->epSquare); - assert(relative_rank(us, to) == RANK_6); - assert(piece_on(to) == NO_PIECE); - assert(piece_on(capsq) == make_piece(them, PAWN)); - } - } - else - st->nonPawnMaterial[them] -= PieceValue[captured]; - - dp.dirty_num = 2; // 1 piece moved, 1 piece captured - dp.piece[1] = captured; - dp.from[1] = capsq; - dp.to[1] = SQ_NONE; - - // Update board and piece lists - remove_piece(capsq); - - // Update material hash key and prefetch access to materialTable - k ^= Zobrist::psq[captured][capsq]; - st->materialKey ^= Zobrist::psq[captured][pieceCount[captured]]; - - // Reset rule 50 counter - st->rule50 = 0; - } - - // Update hash key - k ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; - - // Reset en passant square - if (st->epSquare != SQ_NONE) - { - k ^= Zobrist::enpassant[file_of(st->epSquare)]; - st->epSquare = SQ_NONE; - } - - // Update castling rights if needed - if (st->castlingRights && (castlingRightsMask[from] | castlingRightsMask[to])) - { - k ^= Zobrist::castling[st->castlingRights]; - st->castlingRights &= ~(castlingRightsMask[from] | castlingRightsMask[to]); - k ^= Zobrist::castling[st->castlingRights]; - } - - // Move the piece. The tricky Chess960 castling is handled earlier - if (type_of(m) != CASTLING) - { - dp.piece[0] = pc; - dp.from[0] = from; - dp.to[0] = to; - - move_piece(from, to); - } - - // If the moving piece is a pawn do some special extra work - if (type_of(pc) == PAWN) - { - // Set en passant square if the moved pawn can be captured - if ( (int(to) ^ int(from)) == 16 - && (pawn_attacks_bb(us, to - pawn_push(us)) & pieces(them, PAWN))) - { - st->epSquare = to - pawn_push(us); - k ^= Zobrist::enpassant[file_of(st->epSquare)]; - } - - else if (type_of(m) == PROMOTION) - { - Piece promotion = make_piece(us, promotion_type(m)); - - assert(relative_rank(us, to) == RANK_8); - assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN); - - remove_piece(to); - put_piece(promotion, to); - - // Promoting pawn to SQ_NONE, promoted piece from SQ_NONE - dp.to[0] = SQ_NONE; - dp.piece[dp.dirty_num] = promotion; - dp.from[dp.dirty_num] = SQ_NONE; - dp.to[dp.dirty_num] = to; - dp.dirty_num++; - - // Update hash keys - k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[promotion][to]; - st->materialKey ^= Zobrist::psq[promotion][pieceCount[promotion]-1] - ^ Zobrist::psq[pc][pieceCount[pc]]; - - // Update material - st->nonPawnMaterial[us] += PieceValue[promotion]; - } - - // Reset rule 50 draw counter - st->rule50 = 0; - } - - // Set capture piece - st->capturedPiece = captured; - - // Update the key with the final value - st->key = k; - - // Calculate checkers bitboard (if move gives check) - st->checkersBB = givesCheck ? attackers_to(square(them)) & pieces(us) : 0; - - sideToMove = ~sideToMove; - - // Update king attacks used for fast check detection - set_check_info(); - - // Calculate the repetition info. It is the ply distance from the previous - // occurrence of the same position, negative in the 3-fold case, or zero - // if the position was not repeated. - st->repetition = 0; - int end = std::min(st->rule50, st->pliesFromNull); - if (end >= 4) - { - StateInfo* stp = st->previous->previous; - for (int i = 4; i <= end; i += 2) - { - stp = stp->previous->previous; - if (stp->key == st->key) - { - st->repetition = stp->repetition ? -i : i; - break; - } - } - } - - assert(pos_is_ok()); + assert(is_ok(m)); + assert(&newSt != st); + + thisThread->nodes.fetch_add(1, std::memory_order_relaxed); + Key k = st->key ^ Zobrist::side; + + // Copy some fields of the old state to our new StateInfo object except the + // ones which are going to be recalculated from scratch anyway and then switch + // our state pointer to point to the new (ready to be updated) state. + std::memcpy(&newSt, st, offsetof(StateInfo, key)); + newSt.previous = st; + st = &newSt; + + // Increment ply counters. In particular, rule50 will be reset to zero later on + // in case of a capture or a pawn move. + ++gamePly; + ++st->rule50; + ++st->pliesFromNull; + + // Used by NNUE + st->accumulator.computed[WHITE] = false; + st->accumulator.computed[BLACK] = false; + auto& dp = st->dirtyPiece; + dp.dirty_num = 1; + + Color us = sideToMove; + Color them = ~us; + Square from = from_sq(m); + Square to = to_sq(m); + Piece pc = piece_on(from); + Piece captured = type_of(m) == EN_PASSANT ? make_piece(them, PAWN) : piece_on(to); + + assert(color_of(pc) == us); + assert(captured == NO_PIECE || color_of(captured) == (type_of(m) != CASTLING ? them : us)); + assert(type_of(captured) != KING); + + if (type_of(m) == CASTLING) + { + assert(pc == make_piece(us, KING)); + assert(captured == make_piece(us, ROOK)); + + Square rfrom, rto; + do_castling(us, from, to, rfrom, rto); + + k ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto]; + captured = NO_PIECE; + } + + if (captured) + { + Square capsq = to; + + // If the captured piece is a pawn, update pawn hash key, otherwise + // update non-pawn material. + if (type_of(captured) == PAWN) + { + if (type_of(m) == EN_PASSANT) + { + capsq -= pawn_push(us); + + assert(pc == make_piece(us, PAWN)); + assert(to == st->epSquare); + assert(relative_rank(us, to) == RANK_6); + assert(piece_on(to) == NO_PIECE); + assert(piece_on(capsq) == make_piece(them, PAWN)); + } + } + else + st->nonPawnMaterial[them] -= PieceValue[captured]; + + dp.dirty_num = 2; // 1 piece moved, 1 piece captured + dp.piece[1] = captured; + dp.from[1] = capsq; + dp.to[1] = SQ_NONE; + + // Update board and piece lists + remove_piece(capsq); + + // Update material hash key and prefetch access to materialTable + k ^= Zobrist::psq[captured][capsq]; + st->materialKey ^= Zobrist::psq[captured][pieceCount[captured]]; + + // Reset rule 50 counter + st->rule50 = 0; + } + + // Update hash key + k ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; + + // Reset en passant square + if (st->epSquare != SQ_NONE) + { + k ^= Zobrist::enpassant[file_of(st->epSquare)]; + st->epSquare = SQ_NONE; + } + + // Update castling rights if needed + if (st->castlingRights && (castlingRightsMask[from] | castlingRightsMask[to])) + { + k ^= Zobrist::castling[st->castlingRights]; + st->castlingRights &= ~(castlingRightsMask[from] | castlingRightsMask[to]); + k ^= Zobrist::castling[st->castlingRights]; + } + + // Move the piece. The tricky Chess960 castling is handled earlier + if (type_of(m) != CASTLING) + { + dp.piece[0] = pc; + dp.from[0] = from; + dp.to[0] = to; + + move_piece(from, to); + } + + // If the moving piece is a pawn do some special extra work + if (type_of(pc) == PAWN) + { + // Set en passant square if the moved pawn can be captured + if ((int(to) ^ int(from)) == 16 + && (pawn_attacks_bb(us, to - pawn_push(us)) & pieces(them, PAWN))) + { + st->epSquare = to - pawn_push(us); + k ^= Zobrist::enpassant[file_of(st->epSquare)]; + } + + else if (type_of(m) == PROMOTION) + { + Piece promotion = make_piece(us, promotion_type(m)); + + assert(relative_rank(us, to) == RANK_8); + assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN); + + remove_piece(to); + put_piece(promotion, to); + + // Promoting pawn to SQ_NONE, promoted piece from SQ_NONE + dp.to[0] = SQ_NONE; + dp.piece[dp.dirty_num] = promotion; + dp.from[dp.dirty_num] = SQ_NONE; + dp.to[dp.dirty_num] = to; + dp.dirty_num++; + + // Update hash keys + k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[promotion][to]; + st->materialKey ^= + Zobrist::psq[promotion][pieceCount[promotion] - 1] ^ Zobrist::psq[pc][pieceCount[pc]]; + + // Update material + st->nonPawnMaterial[us] += PieceValue[promotion]; + } + + // Reset rule 50 draw counter + st->rule50 = 0; + } + + // Set capture piece + st->capturedPiece = captured; + + // Update the key with the final value + st->key = k; + + // Calculate checkers bitboard (if move gives check) + st->checkersBB = givesCheck ? attackers_to(square(them)) & pieces(us) : 0; + + sideToMove = ~sideToMove; + + // Update king attacks used for fast check detection + set_check_info(); + + // Calculate the repetition info. It is the ply distance from the previous + // occurrence of the same position, negative in the 3-fold case, or zero + // if the position was not repeated. + st->repetition = 0; + int end = std::min(st->rule50, st->pliesFromNull); + if (end >= 4) + { + StateInfo* stp = st->previous->previous; + for (int i = 4; i <= end; i += 2) + { + stp = stp->previous->previous; + if (stp->key == st->key) + { + st->repetition = stp->repetition ? -i : i; + break; + } + } + } + + assert(pos_is_ok()); } @@ -875,62 +872,62 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { void Position::undo_move(Move m) { - assert(is_ok(m)); - - sideToMove = ~sideToMove; - - Color us = sideToMove; - Square from = from_sq(m); - Square to = to_sq(m); - Piece pc = piece_on(to); - - assert(empty(from) || type_of(m) == CASTLING); - assert(type_of(st->capturedPiece) != KING); - - if (type_of(m) == PROMOTION) - { - assert(relative_rank(us, to) == RANK_8); - assert(type_of(pc) == promotion_type(m)); - assert(type_of(pc) >= KNIGHT && type_of(pc) <= QUEEN); - - remove_piece(to); - pc = make_piece(us, PAWN); - put_piece(pc, to); - } - - if (type_of(m) == CASTLING) - { - Square rfrom, rto; - do_castling(us, from, to, rfrom, rto); - } - else - { - move_piece(to, from); // Put the piece back at the source square - - if (st->capturedPiece) - { - Square capsq = to; - - if (type_of(m) == EN_PASSANT) - { - capsq -= pawn_push(us); - - assert(type_of(pc) == PAWN); - assert(to == st->previous->epSquare); - assert(relative_rank(us, to) == RANK_6); - assert(piece_on(capsq) == NO_PIECE); - assert(st->capturedPiece == make_piece(~us, PAWN)); - } - - put_piece(st->capturedPiece, capsq); // Restore the captured piece - } - } - - // Finally point our state pointer back to the previous state - st = st->previous; - --gamePly; - - assert(pos_is_ok()); + assert(is_ok(m)); + + sideToMove = ~sideToMove; + + Color us = sideToMove; + Square from = from_sq(m); + Square to = to_sq(m); + Piece pc = piece_on(to); + + assert(empty(from) || type_of(m) == CASTLING); + assert(type_of(st->capturedPiece) != KING); + + if (type_of(m) == PROMOTION) + { + assert(relative_rank(us, to) == RANK_8); + assert(type_of(pc) == promotion_type(m)); + assert(type_of(pc) >= KNIGHT && type_of(pc) <= QUEEN); + + remove_piece(to); + pc = make_piece(us, PAWN); + put_piece(pc, to); + } + + if (type_of(m) == CASTLING) + { + Square rfrom, rto; + do_castling(us, from, to, rfrom, rto); + } + else + { + move_piece(to, from); // Put the piece back at the source square + + if (st->capturedPiece) + { + Square capsq = to; + + if (type_of(m) == EN_PASSANT) + { + capsq -= pawn_push(us); + + assert(type_of(pc) == PAWN); + assert(to == st->previous->epSquare); + assert(relative_rank(us, to) == RANK_6); + assert(piece_on(capsq) == NO_PIECE); + assert(st->capturedPiece == make_piece(~us, PAWN)); + } + + put_piece(st->capturedPiece, capsq); // Restore the captured piece + } + } + + // Finally point our state pointer back to the previous state + st = st->previous; + --gamePly; + + assert(pos_is_ok()); } @@ -939,29 +936,30 @@ void Position::undo_move(Move m) { template void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto) { - bool kingSide = to > from; - rfrom = to; // Castling is encoded as "king captures friendly rook" - rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1); - to = relative_square(us, kingSide ? SQ_G1 : SQ_C1); - - if (Do) - { - auto& dp = st->dirtyPiece; - dp.piece[0] = make_piece(us, KING); - dp.from[0] = from; - dp.to[0] = to; - dp.piece[1] = make_piece(us, ROOK); - dp.from[1] = rfrom; - dp.to[1] = rto; - dp.dirty_num = 2; - } - - // Remove both pieces first since squares could overlap in Chess960 - remove_piece(Do ? from : to); - remove_piece(Do ? rfrom : rto); - board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // remove_piece does not do this for us - put_piece(make_piece(us, KING), Do ? to : from); - put_piece(make_piece(us, ROOK), Do ? rto : rfrom); + bool kingSide = to > from; + rfrom = to; // Castling is encoded as "king captures friendly rook" + rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1); + to = relative_square(us, kingSide ? SQ_G1 : SQ_C1); + + if (Do) + { + auto& dp = st->dirtyPiece; + dp.piece[0] = make_piece(us, KING); + dp.from[0] = from; + dp.to[0] = to; + dp.piece[1] = make_piece(us, ROOK); + dp.from[1] = rfrom; + dp.to[1] = rto; + dp.dirty_num = 2; + } + + // Remove both pieces first since squares could overlap in Chess960 + remove_piece(Do ? from : to); + remove_piece(Do ? rfrom : rto); + board[Do ? from : to] = board[Do ? rfrom : rto] = + NO_PIECE; // remove_piece does not do this for us + put_piece(make_piece(us, KING), Do ? to : from); + put_piece(make_piece(us, ROOK), Do ? rto : rfrom); } @@ -970,38 +968,38 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ void Position::do_null_move(StateInfo& newSt) { - assert(!checkers()); - assert(&newSt != st); + assert(!checkers()); + assert(&newSt != st); - std::memcpy(&newSt, st, offsetof(StateInfo, accumulator)); + std::memcpy(&newSt, st, offsetof(StateInfo, accumulator)); - newSt.previous = st; - st = &newSt; + newSt.previous = st; + st = &newSt; - st->dirtyPiece.dirty_num = 0; - st->dirtyPiece.piece[0] = NO_PIECE; // Avoid checks in UpdateAccumulator() - st->accumulator.computed[WHITE] = false; - st->accumulator.computed[BLACK] = false; + st->dirtyPiece.dirty_num = 0; + st->dirtyPiece.piece[0] = NO_PIECE; // Avoid checks in UpdateAccumulator() + st->accumulator.computed[WHITE] = false; + st->accumulator.computed[BLACK] = false; - if (st->epSquare != SQ_NONE) - { - st->key ^= Zobrist::enpassant[file_of(st->epSquare)]; - st->epSquare = SQ_NONE; - } + if (st->epSquare != SQ_NONE) + { + st->key ^= Zobrist::enpassant[file_of(st->epSquare)]; + st->epSquare = SQ_NONE; + } - st->key ^= Zobrist::side; - ++st->rule50; - prefetch(TT.first_entry(key())); + st->key ^= Zobrist::side; + ++st->rule50; + prefetch(TT.first_entry(key())); - st->pliesFromNull = 0; + st->pliesFromNull = 0; - sideToMove = ~sideToMove; + sideToMove = ~sideToMove; - set_check_info(); + set_check_info(); - st->repetition = 0; + st->repetition = 0; - assert(pos_is_ok()); + assert(pos_is_ok()); } @@ -1009,10 +1007,10 @@ void Position::do_null_move(StateInfo& newSt) { void Position::undo_null_move() { - assert(!checkers()); + assert(!checkers()); - st = st->previous; - sideToMove = ~sideToMove; + st = st->previous; + sideToMove = ~sideToMove; } @@ -1022,19 +1020,18 @@ void Position::undo_null_move() { Key Position::key_after(Move m) const { - Square from = from_sq(m); - Square to = to_sq(m); - Piece pc = piece_on(from); - Piece captured = piece_on(to); - Key k = st->key ^ Zobrist::side; + Square from = from_sq(m); + Square to = to_sq(m); + Piece pc = piece_on(from); + Piece captured = piece_on(to); + Key k = st->key ^ Zobrist::side; - if (captured) - k ^= Zobrist::psq[captured][to]; + if (captured) + k ^= Zobrist::psq[captured][to]; - k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[pc][from]; + k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[pc][from]; - return (captured || type_of(pc) == PAWN) - ? k : adjust_key50(k); + return (captured || type_of(pc) == PAWN) ? k : adjust_key50(k); } @@ -1044,103 +1041,103 @@ Key Position::key_after(Move m) const { bool Position::see_ge(Move m, Value threshold) const { - assert(is_ok(m)); - - // Only deal with normal moves, assume others pass a simple SEE - if (type_of(m) != NORMAL) - return VALUE_ZERO >= threshold; - - Square from = from_sq(m), to = to_sq(m); - - int swap = PieceValue[piece_on(to)] - threshold; - if (swap < 0) - return false; - - swap = PieceValue[piece_on(from)] - swap; - if (swap <= 0) - return true; - - assert(color_of(piece_on(from)) == sideToMove); - Bitboard occupied = pieces() ^ from ^ to; // xoring to is important for pinned piece logic - Color stm = sideToMove; - Bitboard attackers = attackers_to(to, occupied); - Bitboard stmAttackers, bb; - int res = 1; - - while (true) - { - stm = ~stm; - attackers &= occupied; - - // If stm has no more attackers then give up: stm loses - if (!(stmAttackers = attackers & pieces(stm))) - break; - - // Don't allow pinned pieces to attack as long as there are - // pinners on their original square. - if (pinners(~stm) & occupied) - { - stmAttackers &= ~blockers_for_king(stm); - - if (!stmAttackers) - break; - } - - res ^= 1; - - // Locate and remove the next least valuable attacker, and add to - // the bitboard 'attackers' any X-ray attackers behind it. - if ((bb = stmAttackers & pieces(PAWN))) - { - if ((swap = PawnValue - swap) < res) - break; - occupied ^= least_significant_square_bb(bb); - - attackers |= attacks_bb(to, occupied) & pieces(BISHOP, QUEEN); - } - - else if ((bb = stmAttackers & pieces(KNIGHT))) - { - if ((swap = KnightValue - swap) < res) - break; - occupied ^= least_significant_square_bb(bb); - } - - else if ((bb = stmAttackers & pieces(BISHOP))) - { - if ((swap = BishopValue - swap) < res) - break; - occupied ^= least_significant_square_bb(bb); - - attackers |= attacks_bb(to, occupied) & pieces(BISHOP, QUEEN); - } - - else if ((bb = stmAttackers & pieces(ROOK))) - { - if ((swap = RookValue - swap) < res) - break; - occupied ^= least_significant_square_bb(bb); - - attackers |= attacks_bb(to, occupied) & pieces(ROOK, QUEEN); - } - - else if ((bb = stmAttackers & pieces(QUEEN))) - { - if ((swap = QueenValue - swap) < res) - break; - occupied ^= least_significant_square_bb(bb); - - attackers |= (attacks_bb(to, occupied) & pieces(BISHOP, QUEEN)) - | (attacks_bb(to, occupied) & pieces(ROOK , QUEEN)); - } - - else // KING - // If we "capture" with the king but the opponent still has attackers, - // reverse the result. - return (attackers & ~pieces(stm)) ? res ^ 1 : res; - } - - return bool(res); + assert(is_ok(m)); + + // Only deal with normal moves, assume others pass a simple SEE + if (type_of(m) != NORMAL) + return VALUE_ZERO >= threshold; + + Square from = from_sq(m), to = to_sq(m); + + int swap = PieceValue[piece_on(to)] - threshold; + if (swap < 0) + return false; + + swap = PieceValue[piece_on(from)] - swap; + if (swap <= 0) + return true; + + assert(color_of(piece_on(from)) == sideToMove); + Bitboard occupied = pieces() ^ from ^ to; // xoring to is important for pinned piece logic + Color stm = sideToMove; + Bitboard attackers = attackers_to(to, occupied); + Bitboard stmAttackers, bb; + int res = 1; + + while (true) + { + stm = ~stm; + attackers &= occupied; + + // If stm has no more attackers then give up: stm loses + if (!(stmAttackers = attackers & pieces(stm))) + break; + + // Don't allow pinned pieces to attack as long as there are + // pinners on their original square. + if (pinners(~stm) & occupied) + { + stmAttackers &= ~blockers_for_king(stm); + + if (!stmAttackers) + break; + } + + res ^= 1; + + // Locate and remove the next least valuable attacker, and add to + // the bitboard 'attackers' any X-ray attackers behind it. + if ((bb = stmAttackers & pieces(PAWN))) + { + if ((swap = PawnValue - swap) < res) + break; + occupied ^= least_significant_square_bb(bb); + + attackers |= attacks_bb(to, occupied) & pieces(BISHOP, QUEEN); + } + + else if ((bb = stmAttackers & pieces(KNIGHT))) + { + if ((swap = KnightValue - swap) < res) + break; + occupied ^= least_significant_square_bb(bb); + } + + else if ((bb = stmAttackers & pieces(BISHOP))) + { + if ((swap = BishopValue - swap) < res) + break; + occupied ^= least_significant_square_bb(bb); + + attackers |= attacks_bb(to, occupied) & pieces(BISHOP, QUEEN); + } + + else if ((bb = stmAttackers & pieces(ROOK))) + { + if ((swap = RookValue - swap) < res) + break; + occupied ^= least_significant_square_bb(bb); + + attackers |= attacks_bb(to, occupied) & pieces(ROOK, QUEEN); + } + + else if ((bb = stmAttackers & pieces(QUEEN))) + { + if ((swap = QueenValue - swap) < res) + break; + occupied ^= least_significant_square_bb(bb); + + attackers |= (attacks_bb(to, occupied) & pieces(BISHOP, QUEEN)) + | (attacks_bb(to, occupied) & pieces(ROOK, QUEEN)); + } + + else // KING + // If we "capture" with the king but the opponent still has attackers, + // reverse the result. + return (attackers & ~pieces(stm)) ? res ^ 1 : res; + } + + return bool(res); } // Position::is_draw() tests whether the position is drawn by 50-move rule @@ -1148,12 +1145,12 @@ bool Position::see_ge(Move m, Value threshold) const { bool Position::is_draw(int ply) const { - if (st->rule50 > 99 && (!checkers() || MoveList(*this).size())) - return true; + if (st->rule50 > 99 && (!checkers() || MoveList(*this).size())) + return true; - // Return a draw score if a position repeats once earlier but strictly - // after the root, or repeats twice before or at the root. - return st->repetition && st->repetition < ply; + // Return a draw score if a position repeats once earlier but strictly + // after the root, or repeats twice before or at the root. + return st->repetition && st->repetition < ply; } @@ -1163,7 +1160,7 @@ bool Position::is_draw(int ply) const { bool Position::has_repeated() const { StateInfo* stc = st; - int end = std::min(st->rule50, st->pliesFromNull); + int end = std::min(st->rule50, st->pliesFromNull); while (end-- >= 4) { if (stc->repetition) @@ -1180,47 +1177,46 @@ bool Position::has_repeated() const { bool Position::has_game_cycle(int ply) const { - int j; + int j; + + int end = std::min(st->rule50, st->pliesFromNull); - int end = std::min(st->rule50, st->pliesFromNull); + if (end < 3) + return false; - if (end < 3) - return false; + Key originalKey = st->key; + StateInfo* stp = st->previous; - Key originalKey = st->key; - StateInfo* stp = st->previous; - - for (int i = 3; i <= end; i += 2) - { - stp = stp->previous->previous; - - Key moveKey = originalKey ^ stp->key; - if ( (j = H1(moveKey), cuckoo[j] == moveKey) - || (j = H2(moveKey), cuckoo[j] == moveKey)) - { - Move move = cuckooMove[j]; - Square s1 = from_sq(move); - Square s2 = to_sq(move); - - if (!((between_bb(s1, s2) ^ s2) & pieces())) - { - if (ply > i) - return true; - - // For nodes before or at the root, check that the move is a - // repetition rather than a move to the current position. - // In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in - // the same location, so we have to select which square to check. - if (color_of(piece_on(empty(s1) ? s2 : s1)) != side_to_move()) - continue; - - // For repetitions before or at the root, require one more - if (stp->repetition) - return true; - } - } - } - return false; + for (int i = 3; i <= end; i += 2) + { + stp = stp->previous->previous; + + Key moveKey = originalKey ^ stp->key; + if ((j = H1(moveKey), cuckoo[j] == moveKey) || (j = H2(moveKey), cuckoo[j] == moveKey)) + { + Move move = cuckooMove[j]; + Square s1 = from_sq(move); + Square s2 = to_sq(move); + + if (!((between_bb(s1, s2) ^ s2) & pieces())) + { + if (ply > i) + return true; + + // For nodes before or at the root, check that the move is a + // repetition rather than a move to the current position. + // In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in + // the same location, so we have to select which square to check. + if (color_of(piece_on(empty(s1) ? s2 : s1)) != side_to_move()) + continue; + + // For repetitions before or at the root, require one more + if (stp->repetition) + return true; + } + } + } + return false; } @@ -1229,33 +1225,33 @@ bool Position::has_game_cycle(int ply) const { void Position::flip() { - string f, token; - std::stringstream ss(fen()); + string f, token; + std::stringstream ss(fen()); - for (Rank r = RANK_8; r >= RANK_1; --r) // Piece placement - { - std::getline(ss, token, r > RANK_1 ? '/' : ' '); - f.insert(0, token + (f.empty() ? " " : "/")); - } + for (Rank r = RANK_8; r >= RANK_1; --r) // Piece placement + { + std::getline(ss, token, r > RANK_1 ? '/' : ' '); + f.insert(0, token + (f.empty() ? " " : "/")); + } - ss >> token; // Active color - f += (token == "w" ? "B " : "W "); // Will be lowercased later + ss >> token; // Active color + f += (token == "w" ? "B " : "W "); // Will be lowercased later - ss >> token; // Castling availability - f += token + " "; + ss >> token; // Castling availability + f += token + " "; - std::transform(f.begin(), f.end(), f.begin(), - [](char c) { return char(islower(c) ? toupper(c) : tolower(c)); }); + std::transform(f.begin(), f.end(), f.begin(), + [](char c) { return char(islower(c) ? toupper(c) : tolower(c)); }); - ss >> token; // En passant square - f += (token == "-" ? token : token.replace(1, 1, token[1] == '3' ? "6" : "3")); + ss >> token; // En passant square + f += (token == "-" ? token : token.replace(1, 1, token[1] == '3' ? "6" : "3")); - std::getline(ss, token); // Half and full moves - f += token; + std::getline(ss, token); // Half and full moves + f += token; - set(f, is_chess960(), st, this_thread()); + set(f, is_chess960(), st, this_thread()); - assert(pos_is_ok()); + assert(pos_is_ok()); } @@ -1265,58 +1261,51 @@ void Position::flip() { bool Position::pos_is_ok() const { - constexpr bool Fast = true; // Quick (default) or full check? - - if ( (sideToMove != WHITE && sideToMove != BLACK) - || piece_on(square(WHITE)) != W_KING - || piece_on(square(BLACK)) != B_KING - || ( ep_square() != SQ_NONE - && relative_rank(sideToMove, ep_square()) != RANK_6)) - assert(0 && "pos_is_ok: Default"); - - if (Fast) - return true; - - if ( pieceCount[W_KING] != 1 - || pieceCount[B_KING] != 1 - || attackers_to(square(~sideToMove)) & pieces(sideToMove)) - assert(0 && "pos_is_ok: Kings"); - - if ( (pieces(PAWN) & (Rank1BB | Rank8BB)) - || pieceCount[W_PAWN] > 8 - || pieceCount[B_PAWN] > 8) - assert(0 && "pos_is_ok: Pawns"); - - if ( (pieces(WHITE) & pieces(BLACK)) - || (pieces(WHITE) | pieces(BLACK)) != pieces() - || popcount(pieces(WHITE)) > 16 - || popcount(pieces(BLACK)) > 16) - assert(0 && "pos_is_ok: Bitboards"); - - for (PieceType p1 = PAWN; p1 <= KING; ++p1) - for (PieceType p2 = PAWN; p2 <= KING; ++p2) - if (p1 != p2 && (pieces(p1) & pieces(p2))) - assert(0 && "pos_is_ok: Bitboards"); - - - for (Piece pc : Pieces) - if ( pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc))) - || pieceCount[pc] != std::count(board, board + SQUARE_NB, pc)) - assert(0 && "pos_is_ok: Pieces"); - - for (Color c : { WHITE, BLACK }) - for (CastlingRights cr : {c & KING_SIDE, c & QUEEN_SIDE}) - { - if (!can_castle(cr)) - continue; - - if ( piece_on(castlingRookSquare[cr]) != make_piece(c, ROOK) - || castlingRightsMask[castlingRookSquare[cr]] != cr - || (castlingRightsMask[square(c)] & cr) != cr) - assert(0 && "pos_is_ok: Castling"); - } - - return true; + constexpr bool Fast = true; // Quick (default) or full check? + + if ((sideToMove != WHITE && sideToMove != BLACK) || piece_on(square(WHITE)) != W_KING + || piece_on(square(BLACK)) != B_KING + || (ep_square() != SQ_NONE && relative_rank(sideToMove, ep_square()) != RANK_6)) + assert(0 && "pos_is_ok: Default"); + + if (Fast) + return true; + + if (pieceCount[W_KING] != 1 || pieceCount[B_KING] != 1 + || attackers_to(square(~sideToMove)) & pieces(sideToMove)) + assert(0 && "pos_is_ok: Kings"); + + if ((pieces(PAWN) & (Rank1BB | Rank8BB)) || pieceCount[W_PAWN] > 8 || pieceCount[B_PAWN] > 8) + assert(0 && "pos_is_ok: Pawns"); + + if ((pieces(WHITE) & pieces(BLACK)) || (pieces(WHITE) | pieces(BLACK)) != pieces() + || popcount(pieces(WHITE)) > 16 || popcount(pieces(BLACK)) > 16) + assert(0 && "pos_is_ok: Bitboards"); + + for (PieceType p1 = PAWN; p1 <= KING; ++p1) + for (PieceType p2 = PAWN; p2 <= KING; ++p2) + if (p1 != p2 && (pieces(p1) & pieces(p2))) + assert(0 && "pos_is_ok: Bitboards"); + + + for (Piece pc : Pieces) + if (pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc))) + || pieceCount[pc] != std::count(board, board + SQUARE_NB, pc)) + assert(0 && "pos_is_ok: Pieces"); + + for (Color c : {WHITE, BLACK}) + for (CastlingRights cr : {c & KING_SIDE, c & QUEEN_SIDE}) + { + if (!can_castle(cr)) + continue; + + if (piece_on(castlingRookSquare[cr]) != make_piece(c, ROOK) + || castlingRightsMask[castlingRookSquare[cr]] != cr + || (castlingRightsMask[square(c)] & cr) != cr) + assert(0 && "pos_is_ok: Castling"); + } + + return true; } -} // namespace Stockfish +} // namespace Stockfish diff --git a/src/position.h b/src/position.h index 23fd5bf5688..2aeb8fcd575 100644 --- a/src/position.h +++ b/src/position.h @@ -37,27 +37,27 @@ namespace Stockfish { struct StateInfo { - // Copied when making a move - Key materialKey; - Value nonPawnMaterial[COLOR_NB]; - int castlingRights; - int rule50; - int pliesFromNull; - Square epSquare; - - // Not copied when making a move (will be recomputed anyhow) - Key key; - Bitboard checkersBB; - StateInfo* previous; - Bitboard blockersForKing[COLOR_NB]; - Bitboard pinners[COLOR_NB]; - Bitboard checkSquares[PIECE_TYPE_NB]; - Piece capturedPiece; - int repetition; - - // Used by NNUE - Eval::NNUE::Accumulator accumulator; - DirtyPiece dirtyPiece; + // Copied when making a move + Key materialKey; + Value nonPawnMaterial[COLOR_NB]; + int castlingRights; + int rule50; + int pliesFromNull; + Square epSquare; + + // Not copied when making a move (will be recomputed anyhow) + Key key; + Bitboard checkersBB; + StateInfo* previous; + Bitboard blockersForKing[COLOR_NB]; + Bitboard pinners[COLOR_NB]; + Bitboard checkSquares[PIECE_TYPE_NB]; + Piece capturedPiece; + int repetition; + + // Used by NNUE + Eval::NNUE::Accumulator accumulator; + DirtyPiece dirtyPiece; }; @@ -75,329 +75,290 @@ using StateListPtr = std::unique_ptr>; class Thread; class Position { -public: - static void init(); - - Position() = default; - Position(const Position&) = delete; - Position& operator=(const Position&) = delete; - - // FEN string input/output - Position& set(const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th); - Position& set(const std::string& code, Color c, StateInfo* si); - std::string fen() const; - - // Position representation - Bitboard pieces(PieceType pt = ALL_PIECES) const; - template Bitboard pieces(PieceType pt, PieceTypes... pts) const; - Bitboard pieces(Color c) const; - template Bitboard pieces(Color c, PieceTypes... pts) const; - Piece piece_on(Square s) const; - Square ep_square() const; - bool empty(Square s) const; - template int count(Color c) const; - template int count() const; - template Square square(Color c) const; - - // Castling - CastlingRights castling_rights(Color c) const; - bool can_castle(CastlingRights cr) const; - bool castling_impeded(CastlingRights cr) const; - Square castling_rook_square(CastlingRights cr) const; - - // Checking - Bitboard checkers() const; - Bitboard blockers_for_king(Color c) const; - Bitboard check_squares(PieceType pt) const; - Bitboard pinners(Color c) const; - - // Attacks to/from a given square - Bitboard attackers_to(Square s) const; - Bitboard attackers_to(Square s, Bitboard occupied) const; - void update_slider_blockers(Color c) const; - template Bitboard attacks_by(Color c) const; - - // Properties of moves - bool legal(Move m) const; - bool pseudo_legal(const Move m) const; - bool capture(Move m) const; - bool capture_stage(Move m) const; - bool gives_check(Move m) const; - Piece moved_piece(Move m) const; - Piece captured_piece() const; - - // Doing and undoing moves - void do_move(Move m, StateInfo& newSt); - void do_move(Move m, StateInfo& newSt, bool givesCheck); - void undo_move(Move m); - void do_null_move(StateInfo& newSt); - void undo_null_move(); - - // Static Exchange Evaluation - bool see_ge(Move m, Value threshold = VALUE_ZERO) const; - - // Accessing hash keys - Key key() const; - Key key_after(Move m) const; - Key material_key() const; - - // Other properties of the position - Color side_to_move() const; - int game_ply() const; - bool is_chess960() const; - Thread* this_thread() const; - bool is_draw(int ply) const; - bool has_game_cycle(int ply) const; - bool has_repeated() const; - int rule50_count() const; - Value non_pawn_material(Color c) const; - Value non_pawn_material() const; - - // Position consistency check, for debugging - bool pos_is_ok() const; - void flip(); - - // Used by NNUE - StateInfo* state() const; - - void put_piece(Piece pc, Square s); - void remove_piece(Square s); - -private: - // Initialization helpers (used while setting up a position) - void set_castling_right(Color c, Square rfrom); - void set_state() const; - void set_check_info() const; - - // Other helpers - void move_piece(Square from, Square to); - template - void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto); - template - Key adjust_key50(Key k) const; - - // Data members - Piece board[SQUARE_NB]; - Bitboard byTypeBB[PIECE_TYPE_NB]; - Bitboard byColorBB[COLOR_NB]; - int pieceCount[PIECE_NB]; - int castlingRightsMask[SQUARE_NB]; - Square castlingRookSquare[CASTLING_RIGHT_NB]; - Bitboard castlingPath[CASTLING_RIGHT_NB]; - Thread* thisThread; - StateInfo* st; - int gamePly; - Color sideToMove; - bool chess960; + public: + static void init(); + + Position() = default; + Position(const Position&) = delete; + Position& operator=(const Position&) = delete; + + // FEN string input/output + Position& set(const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th); + Position& set(const std::string& code, Color c, StateInfo* si); + std::string fen() const; + + // Position representation + Bitboard pieces(PieceType pt = ALL_PIECES) const; + template + Bitboard pieces(PieceType pt, PieceTypes... pts) const; + Bitboard pieces(Color c) const; + template + Bitboard pieces(Color c, PieceTypes... pts) const; + Piece piece_on(Square s) const; + Square ep_square() const; + bool empty(Square s) const; + template + int count(Color c) const; + template + int count() const; + template + Square square(Color c) const; + + // Castling + CastlingRights castling_rights(Color c) const; + bool can_castle(CastlingRights cr) const; + bool castling_impeded(CastlingRights cr) const; + Square castling_rook_square(CastlingRights cr) const; + + // Checking + Bitboard checkers() const; + Bitboard blockers_for_king(Color c) const; + Bitboard check_squares(PieceType pt) const; + Bitboard pinners(Color c) const; + + // Attacks to/from a given square + Bitboard attackers_to(Square s) const; + Bitboard attackers_to(Square s, Bitboard occupied) const; + void update_slider_blockers(Color c) const; + template + Bitboard attacks_by(Color c) const; + + // Properties of moves + bool legal(Move m) const; + bool pseudo_legal(const Move m) const; + bool capture(Move m) const; + bool capture_stage(Move m) const; + bool gives_check(Move m) const; + Piece moved_piece(Move m) const; + Piece captured_piece() const; + + // Doing and undoing moves + void do_move(Move m, StateInfo& newSt); + void do_move(Move m, StateInfo& newSt, bool givesCheck); + void undo_move(Move m); + void do_null_move(StateInfo& newSt); + void undo_null_move(); + + // Static Exchange Evaluation + bool see_ge(Move m, Value threshold = VALUE_ZERO) const; + + // Accessing hash keys + Key key() const; + Key key_after(Move m) const; + Key material_key() const; + + // Other properties of the position + Color side_to_move() const; + int game_ply() const; + bool is_chess960() const; + Thread* this_thread() const; + bool is_draw(int ply) const; + bool has_game_cycle(int ply) const; + bool has_repeated() const; + int rule50_count() const; + Value non_pawn_material(Color c) const; + Value non_pawn_material() const; + + // Position consistency check, for debugging + bool pos_is_ok() const; + void flip(); + + // Used by NNUE + StateInfo* state() const; + + void put_piece(Piece pc, Square s); + void remove_piece(Square s); + + private: + // Initialization helpers (used while setting up a position) + void set_castling_right(Color c, Square rfrom); + void set_state() const; + void set_check_info() const; + + // Other helpers + void move_piece(Square from, Square to); + template + void do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto); + template + Key adjust_key50(Key k) const; + + // Data members + Piece board[SQUARE_NB]; + Bitboard byTypeBB[PIECE_TYPE_NB]; + Bitboard byColorBB[COLOR_NB]; + int pieceCount[PIECE_NB]; + int castlingRightsMask[SQUARE_NB]; + Square castlingRookSquare[CASTLING_RIGHT_NB]; + Bitboard castlingPath[CASTLING_RIGHT_NB]; + Thread* thisThread; + StateInfo* st; + int gamePly; + Color sideToMove; + bool chess960; }; std::ostream& operator<<(std::ostream& os, const Position& pos); -inline Color Position::side_to_move() const { - return sideToMove; -} +inline Color Position::side_to_move() const { return sideToMove; } inline Piece Position::piece_on(Square s) const { - assert(is_ok(s)); - return board[s]; + assert(is_ok(s)); + return board[s]; } -inline bool Position::empty(Square s) const { - return piece_on(s) == NO_PIECE; -} +inline bool Position::empty(Square s) const { return piece_on(s) == NO_PIECE; } -inline Piece Position::moved_piece(Move m) const { - return piece_on(from_sq(m)); -} +inline Piece Position::moved_piece(Move m) const { return piece_on(from_sq(m)); } -inline Bitboard Position::pieces(PieceType pt) const { - return byTypeBB[pt]; -} +inline Bitboard Position::pieces(PieceType pt) const { return byTypeBB[pt]; } -template +template inline Bitboard Position::pieces(PieceType pt, PieceTypes... pts) const { - return pieces(pt) | pieces(pts...); + return pieces(pt) | pieces(pts...); } -inline Bitboard Position::pieces(Color c) const { - return byColorBB[c]; -} +inline Bitboard Position::pieces(Color c) const { return byColorBB[c]; } -template +template inline Bitboard Position::pieces(Color c, PieceTypes... pts) const { - return pieces(c) & pieces(pts...); + return pieces(c) & pieces(pts...); } -template inline int Position::count(Color c) const { - return pieceCount[make_piece(c, Pt)]; +template +inline int Position::count(Color c) const { + return pieceCount[make_piece(c, Pt)]; } -template inline int Position::count() const { - return count(WHITE) + count(BLACK); +template +inline int Position::count() const { + return count(WHITE) + count(BLACK); } -template inline Square Position::square(Color c) const { - assert(count(c) == 1); - return lsb(pieces(c, Pt)); +template +inline Square Position::square(Color c) const { + assert(count(c) == 1); + return lsb(pieces(c, Pt)); } -inline Square Position::ep_square() const { - return st->epSquare; -} +inline Square Position::ep_square() const { return st->epSquare; } -inline bool Position::can_castle(CastlingRights cr) const { - return st->castlingRights & cr; -} +inline bool Position::can_castle(CastlingRights cr) const { return st->castlingRights & cr; } inline CastlingRights Position::castling_rights(Color c) const { - return c & CastlingRights(st->castlingRights); + return c & CastlingRights(st->castlingRights); } inline bool Position::castling_impeded(CastlingRights cr) const { - assert(cr == WHITE_OO || cr == WHITE_OOO || cr == BLACK_OO || cr == BLACK_OOO); + assert(cr == WHITE_OO || cr == WHITE_OOO || cr == BLACK_OO || cr == BLACK_OOO); - return pieces() & castlingPath[cr]; + return pieces() & castlingPath[cr]; } inline Square Position::castling_rook_square(CastlingRights cr) const { - assert(cr == WHITE_OO || cr == WHITE_OOO || cr == BLACK_OO || cr == BLACK_OOO); + assert(cr == WHITE_OO || cr == WHITE_OOO || cr == BLACK_OO || cr == BLACK_OOO); - return castlingRookSquare[cr]; + return castlingRookSquare[cr]; } -inline Bitboard Position::attackers_to(Square s) const { - return attackers_to(s, pieces()); -} +inline Bitboard Position::attackers_to(Square s) const { return attackers_to(s, pieces()); } template inline Bitboard Position::attacks_by(Color c) const { - if constexpr (Pt == PAWN) - return c == WHITE ? pawn_attacks_bb(pieces(WHITE, PAWN)) - : pawn_attacks_bb(pieces(BLACK, PAWN)); - else - { - Bitboard threats = 0; - Bitboard attackers = pieces(c, Pt); - while (attackers) - threats |= attacks_bb(pop_lsb(attackers), pieces()); - return threats; - } + if constexpr (Pt == PAWN) + return c == WHITE ? pawn_attacks_bb(pieces(WHITE, PAWN)) + : pawn_attacks_bb(pieces(BLACK, PAWN)); + else + { + Bitboard threats = 0; + Bitboard attackers = pieces(c, Pt); + while (attackers) + threats |= attacks_bb(pop_lsb(attackers), pieces()); + return threats; + } } -inline Bitboard Position::checkers() const { - return st->checkersBB; -} +inline Bitboard Position::checkers() const { return st->checkersBB; } -inline Bitboard Position::blockers_for_king(Color c) const { - return st->blockersForKing[c]; -} +inline Bitboard Position::blockers_for_king(Color c) const { return st->blockersForKing[c]; } -inline Bitboard Position::pinners(Color c) const { - return st->pinners[c]; -} +inline Bitboard Position::pinners(Color c) const { return st->pinners[c]; } -inline Bitboard Position::check_squares(PieceType pt) const { - return st->checkSquares[pt]; -} +inline Bitboard Position::check_squares(PieceType pt) const { return st->checkSquares[pt]; } -inline Key Position::key() const { - return adjust_key50(st->key); -} +inline Key Position::key() const { return adjust_key50(st->key); } template -inline Key Position::adjust_key50(Key k) const -{ - return st->rule50 < 14 - AfterMove - ? k : k ^ make_key((st->rule50 - (14 - AfterMove)) / 8); +inline Key Position::adjust_key50(Key k) const { + return st->rule50 < 14 - AfterMove ? k : k ^ make_key((st->rule50 - (14 - AfterMove)) / 8); } -inline Key Position::material_key() const { - return st->materialKey; -} +inline Key Position::material_key() const { return st->materialKey; } -inline Value Position::non_pawn_material(Color c) const { - return st->nonPawnMaterial[c]; -} +inline Value Position::non_pawn_material(Color c) const { return st->nonPawnMaterial[c]; } inline Value Position::non_pawn_material() const { - return non_pawn_material(WHITE) + non_pawn_material(BLACK); + return non_pawn_material(WHITE) + non_pawn_material(BLACK); } -inline int Position::game_ply() const { - return gamePly; -} +inline int Position::game_ply() const { return gamePly; } -inline int Position::rule50_count() const { - return st->rule50; -} +inline int Position::rule50_count() const { return st->rule50; } -inline bool Position::is_chess960() const { - return chess960; -} +inline bool Position::is_chess960() const { return chess960; } inline bool Position::capture(Move m) const { - assert(is_ok(m)); - return (!empty(to_sq(m)) && type_of(m) != CASTLING) - || type_of(m) == EN_PASSANT; + assert(is_ok(m)); + return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == EN_PASSANT; } // Returns true if a move is generated from the capture stage, having also // queen promotions covered, i.e. consistency with the capture stage move generation // is needed to avoid the generation of duplicate moves. inline bool Position::capture_stage(Move m) const { - assert(is_ok(m)); - return capture(m) || promotion_type(m) == QUEEN; + assert(is_ok(m)); + return capture(m) || promotion_type(m) == QUEEN; } -inline Piece Position::captured_piece() const { - return st->capturedPiece; -} +inline Piece Position::captured_piece() const { return st->capturedPiece; } -inline Thread* Position::this_thread() const { - return thisThread; -} +inline Thread* Position::this_thread() const { return thisThread; } inline void Position::put_piece(Piece pc, Square s) { - board[s] = pc; - byTypeBB[ALL_PIECES] |= byTypeBB[type_of(pc)] |= s; - byColorBB[color_of(pc)] |= s; - pieceCount[pc]++; - pieceCount[make_piece(color_of(pc), ALL_PIECES)]++; + board[s] = pc; + byTypeBB[ALL_PIECES] |= byTypeBB[type_of(pc)] |= s; + byColorBB[color_of(pc)] |= s; + pieceCount[pc]++; + pieceCount[make_piece(color_of(pc), ALL_PIECES)]++; } inline void Position::remove_piece(Square s) { - Piece pc = board[s]; - byTypeBB[ALL_PIECES] ^= s; - byTypeBB[type_of(pc)] ^= s; - byColorBB[color_of(pc)] ^= s; - board[s] = NO_PIECE; - pieceCount[pc]--; - pieceCount[make_piece(color_of(pc), ALL_PIECES)]--; + Piece pc = board[s]; + byTypeBB[ALL_PIECES] ^= s; + byTypeBB[type_of(pc)] ^= s; + byColorBB[color_of(pc)] ^= s; + board[s] = NO_PIECE; + pieceCount[pc]--; + pieceCount[make_piece(color_of(pc), ALL_PIECES)]--; } inline void Position::move_piece(Square from, Square to) { - Piece pc = board[from]; - Bitboard fromTo = from | to; - byTypeBB[ALL_PIECES] ^= fromTo; - byTypeBB[type_of(pc)] ^= fromTo; - byColorBB[color_of(pc)] ^= fromTo; - board[from] = NO_PIECE; - board[to] = pc; + Piece pc = board[from]; + Bitboard fromTo = from | to; + byTypeBB[ALL_PIECES] ^= fromTo; + byTypeBB[type_of(pc)] ^= fromTo; + byColorBB[color_of(pc)] ^= fromTo; + board[from] = NO_PIECE; + board[to] = pc; } -inline void Position::do_move(Move m, StateInfo& newSt) { - do_move(m, newSt, gives_check(m)); -} - -inline StateInfo* Position::state() const { +inline void Position::do_move(Move m, StateInfo& newSt) { do_move(m, newSt, gives_check(m)); } - return st; -} +inline StateInfo* Position::state() const { return st; } -} // namespace Stockfish +} // namespace Stockfish -#endif // #ifndef POSITION_H_INCLUDED +#endif // #ifndef POSITION_H_INCLUDED diff --git a/src/search.cpp b/src/search.cpp index baf819687c5..43f0c8726e3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -49,15 +49,15 @@ namespace Stockfish { namespace Search { - LimitsType Limits; +LimitsType Limits; } namespace Tablebases { - int Cardinality; - bool RootInTB; - bool UseRule50; - Depth ProbeDepth; +int Cardinality; +bool RootInTB; +bool UseRule50; +Depth ProbeDepth; } namespace TB = Tablebases; @@ -68,45 +68,46 @@ using namespace Search; namespace { - // Different node types, used as a template parameter - enum NodeType { NonPV, PV, Root }; +// Different node types, used as a template parameter +enum NodeType { + NonPV, + PV, + Root +}; - // Futility margin - Value futility_margin(Depth d, bool noTtCutNode, bool improving) { +// Futility margin +Value futility_margin(Depth d, bool noTtCutNode, bool improving) { return Value((126 - 42 * noTtCutNode) * (d - improving)); - } +} - // Reductions lookup table initialized at startup - int Reductions[MAX_MOVES]; // [depth or moveNumber] +// Reductions lookup table initialized at startup +int Reductions[MAX_MOVES]; // [depth or moveNumber] - Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) { +Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) { int reductionScale = Reductions[d] * Reductions[mn]; - return (reductionScale + 1560 - int(delta) * 945 / int(rootDelta)) / 1024 - + (!i && reductionScale > 791); - } - - constexpr int futility_move_count(bool improving, Depth depth) { - return improving ? (3 + depth * depth) - : (3 + depth * depth) / 2; - } - - // History and stats update bonus, based on depth - int stat_bonus(Depth d) { - return std::min(334 * d - 531, 1538); - } - - // Add a small random component to draw evaluations to avoid 3-fold blindness - Value value_draw(const Thread* thisThread) { + return (reductionScale + 1560 - int(delta) * 945 / int(rootDelta)) / 1024 + + (!i && reductionScale > 791); +} + +constexpr int futility_move_count(bool improving, Depth depth) { + return improving ? (3 + depth * depth) : (3 + depth * depth) / 2; +} + +// History and stats update bonus, based on depth +int stat_bonus(Depth d) { return std::min(334 * d - 531, 1538); } + +// Add a small random component to draw evaluations to avoid 3-fold blindness +Value value_draw(const Thread* thisThread) { return VALUE_DRAW - 1 + Value(thisThread->nodes & 0x2); - } - - // Skill structure is used to implement strength limit. If we have a UCI_Elo, - // we convert it to an appropriate skill level, anchored to the Stash engine. - // This method is based on a fit of the Elo results for games played between - // Stockfish at various skill levels and various versions of the Stash engine. - // Skill 0 .. 19 now covers CCRL Blitz Elo from 1320 to 3190, approximately - // Reference: https://github.com/vondele/Stockfish/commit/a08b8d4e9711c2 - struct Skill { +} + +// Skill structure is used to implement strength limit. If we have a UCI_Elo, +// we convert it to an appropriate skill level, anchored to the Stash engine. +// This method is based on a fit of the Elo results for games played between +// Stockfish at various skill levels and various versions of the Stash engine. +// Skill 0 .. 19 now covers CCRL Blitz Elo from 1320 to 3190, approximately +// Reference: https://github.com/vondele/Stockfish/commit/a08b8d4e9711c2 +struct Skill { Skill(int skill_level, int uci_elo) { if (uci_elo) { @@ -121,32 +122,41 @@ namespace { Move pick_best(size_t multiPV); double level; - Move best = MOVE_NONE; - }; - - template - 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 = 0); - - Value value_to_tt(Value v, int ply); - Value value_from_tt(Value v, int ply, int r50c); - void update_pv(Move* pv, Move move, const Move* childPv); - void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus); - void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus); - void update_all_stats(const Position& pos, Stack* ss, Move bestMove, Value bestValue, Value beta, Square prevSq, - Move* quietsSearched, int quietCount, Move* capturesSearched, int captureCount, Depth depth); - - // perft() is our utility to verify move generation. All the leaf nodes up - // to the given depth are generated and counted, and the sum is returned. - template - uint64_t perft(Position& pos, Depth depth) { + Move best = MOVE_NONE; +}; + +template +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 = 0); + +Value value_to_tt(Value v, int ply); +Value value_from_tt(Value v, int ply, int r50c); +void update_pv(Move* pv, Move move, const Move* childPv); +void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus); +void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus); +void update_all_stats(const Position& pos, + Stack* ss, + Move bestMove, + Value bestValue, + Value beta, + Square prevSq, + Move* quietsSearched, + int quietCount, + Move* capturesSearched, + int captureCount, + Depth depth); + +// perft() is our utility to verify move generation. All the leaf nodes up +// to the given depth are generated and counted, and the sum is returned. +template +uint64_t perft(Position& pos, Depth depth) { StateInfo st; ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize); - uint64_t cnt, nodes = 0; + uint64_t cnt, nodes = 0; const bool leaf = (depth == 2); for (const auto& m : MoveList(pos)) @@ -164,17 +174,17 @@ namespace { sync_cout << UCI::move(m, pos.is_chess960()) << ": " << cnt << sync_endl; } return nodes; - } +} -} // namespace +} // namespace // Search::init() is called at startup to initialize various lookup tables void Search::init() { - for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int((20.37 + std::log(Threads.size()) / 2) * std::log(i)); + for (int i = 1; i < MAX_MOVES; ++i) + Reductions[i] = int((20.37 + std::log(Threads.size()) / 2) * std::log(i)); } @@ -182,12 +192,12 @@ void Search::init() { void Search::clear() { - Threads.main()->wait_for_search_finished(); + Threads.main()->wait_for_search_finished(); - Time.availableNodes = 0; - TT.clear(); - Threads.clear(); - Tablebases::init(Options["SyzygyPath"]); // Free mapped files + Time.availableNodes = 0; + TT.clear(); + Threads.clear(); + Tablebases::init(Options["SyzygyPath"]); // Free mapped files } @@ -196,75 +206,74 @@ void Search::clear() { void MainThread::search() { - if (Limits.perft) - { - nodes = perft(rootPos, Limits.perft); - sync_cout << "\nNodes searched: " << nodes << "\n" << sync_endl; - return; - } - - Color us = rootPos.side_to_move(); - Time.init(Limits, us, rootPos.game_ply()); - TT.new_search(); - - Eval::NNUE::verify(); - - if (rootMoves.empty()) - { - rootMoves.emplace_back(MOVE_NONE); - sync_cout << "info depth 0 score " - << UCI::value(rootPos.checkers() ? -VALUE_MATE : VALUE_DRAW) - << sync_endl; - } - else - { - Threads.start_searching(); // start non-main threads - Thread::search(); // main thread start searching - } - - // When we reach the maximum depth, we can arrive here without a raise of - // Threads.stop. However, if we are pondering or in an infinite search, - // the UCI protocol states that we shouldn't print the best move before the - // GUI sends a "stop" or "ponderhit" command. We therefore simply wait here - // until the GUI sends one of those commands. - - while (!Threads.stop && (ponder || Limits.infinite)) - {} // Busy wait for a stop or a ponder reset - - // Stop the threads if not already stopped (also raise the stop if - // "ponderhit" just reset Threads.ponder). - Threads.stop = true; - - // Wait until all threads have finished - Threads.wait_for_search_finished(); - - // When playing in 'nodes as time' mode, subtract the searched nodes from - // the available ones before exiting. - if (Limits.npmsec) - Time.availableNodes += Limits.inc[us] - Threads.nodes_searched(); - - Thread* bestThread = this; - Skill skill = Skill(Options["Skill Level"], Options["UCI_LimitStrength"] ? int(Options["UCI_Elo"]) : 0); - - if ( int(Options["MultiPV"]) == 1 - && !Limits.depth - && !skill.enabled() - && rootMoves[0].pv[0] != MOVE_NONE) - bestThread = Threads.get_best_thread(); - - bestPreviousScore = bestThread->rootMoves[0].score; - bestPreviousAverageScore = bestThread->rootMoves[0].averageScore; - - // Send again PV info if we have a new best thread - if (bestThread != this) - sync_cout << UCI::pv(bestThread->rootPos, bestThread->completedDepth) << sync_endl; - - sync_cout << "bestmove " << UCI::move(bestThread->rootMoves[0].pv[0], rootPos.is_chess960()); - - if (bestThread->rootMoves[0].pv.size() > 1 || bestThread->rootMoves[0].extract_ponder_from_tt(rootPos)) - std::cout << " ponder " << UCI::move(bestThread->rootMoves[0].pv[1], rootPos.is_chess960()); - - std::cout << sync_endl; + if (Limits.perft) + { + nodes = perft(rootPos, Limits.perft); + sync_cout << "\nNodes searched: " << nodes << "\n" << sync_endl; + return; + } + + Color us = rootPos.side_to_move(); + Time.init(Limits, us, rootPos.game_ply()); + TT.new_search(); + + Eval::NNUE::verify(); + + if (rootMoves.empty()) + { + rootMoves.emplace_back(MOVE_NONE); + sync_cout << "info depth 0 score " + << UCI::value(rootPos.checkers() ? -VALUE_MATE : VALUE_DRAW) << sync_endl; + } + else + { + Threads.start_searching(); // start non-main threads + Thread::search(); // main thread start searching + } + + // When we reach the maximum depth, we can arrive here without a raise of + // Threads.stop. However, if we are pondering or in an infinite search, + // the UCI protocol states that we shouldn't print the best move before the + // GUI sends a "stop" or "ponderhit" command. We therefore simply wait here + // until the GUI sends one of those commands. + + while (!Threads.stop && (ponder || Limits.infinite)) + {} // Busy wait for a stop or a ponder reset + + // Stop the threads if not already stopped (also raise the stop if + // "ponderhit" just reset Threads.ponder). + Threads.stop = true; + + // Wait until all threads have finished + Threads.wait_for_search_finished(); + + // When playing in 'nodes as time' mode, subtract the searched nodes from + // the available ones before exiting. + if (Limits.npmsec) + Time.availableNodes += Limits.inc[us] - Threads.nodes_searched(); + + Thread* bestThread = this; + Skill skill = + Skill(Options["Skill Level"], Options["UCI_LimitStrength"] ? int(Options["UCI_Elo"]) : 0); + + if (int(Options["MultiPV"]) == 1 && !Limits.depth && !skill.enabled() + && rootMoves[0].pv[0] != MOVE_NONE) + bestThread = Threads.get_best_thread(); + + bestPreviousScore = bestThread->rootMoves[0].score; + bestPreviousAverageScore = bestThread->rootMoves[0].averageScore; + + // Send again PV info if we have a new best thread + if (bestThread != this) + sync_cout << UCI::pv(bestThread->rootPos, bestThread->completedDepth) << sync_endl; + + sync_cout << "bestmove " << UCI::move(bestThread->rootMoves[0].pv[0], rootPos.is_chess960()); + + if (bestThread->rootMoves[0].pv.size() > 1 + || bestThread->rootMoves[0].extract_ponder_from_tt(rootPos)) + std::cout << " ponder " << UCI::move(bestThread->rootMoves[0].pv[1], rootPos.is_chess960()); + + std::cout << sync_endl; } @@ -274,266 +283,259 @@ void MainThread::search() { void Thread::search() { - // Allocate stack with extra size to allow access from (ss-7) to (ss+2): - // (ss-7) is needed for update_continuation_histories(ss-1) which accesses (ss-6), - // (ss+2) is needed for initialization of statScore and killers. - Stack stack[MAX_PLY+10], *ss = stack+7; - Move pv[MAX_PLY+1]; - Value alpha, beta, delta; - Move lastBestMove = MOVE_NONE; - Depth lastBestMoveDepth = 0; - MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr); - double timeReduction = 1, totBestMoveChanges = 0; - Color us = rootPos.side_to_move(); - int iterIdx = 0; - - std::memset(ss-7, 0, 10 * sizeof(Stack)); - for (int i = 7; i > 0; --i) - { - (ss-i)->continuationHistory = &this->continuationHistory[0][0][NO_PIECE][0]; // Use as a sentinel - (ss-i)->staticEval = VALUE_NONE; - } - - for (int i = 0; i <= MAX_PLY + 2; ++i) - (ss+i)->ply = i; - - ss->pv = pv; - - bestValue = -VALUE_INFINITE; - - if (mainThread) - { - if (mainThread->bestPreviousScore == VALUE_INFINITE) - for (int i = 0; i < 4; ++i) - mainThread->iterValue[i] = VALUE_ZERO; - else - for (int i = 0; i < 4; ++i) - mainThread->iterValue[i] = mainThread->bestPreviousScore; - } - - size_t multiPV = size_t(Options["MultiPV"]); - Skill skill(Options["Skill Level"], Options["UCI_LimitStrength"] ? int(Options["UCI_Elo"]) : 0); - - // When playing with strength handicap enable MultiPV search that we will - // use behind-the-scenes to retrieve a set of possible moves. - if (skill.enabled()) - multiPV = std::max(multiPV, size_t(4)); - - multiPV = std::min(multiPV, rootMoves.size()); - - int searchAgainCounter = 0; - - // Iterative deepening loop until requested to stop or the target depth is reached - while ( ++rootDepth < MAX_PLY - && !Threads.stop - && !(Limits.depth && mainThread && rootDepth > Limits.depth)) - { - // Age out PV variability metric - if (mainThread) - totBestMoveChanges /= 2; - - // Save the last iteration's scores before the first PV line is searched and - // all the move scores except the (new) PV are set to -VALUE_INFINITE. - for (RootMove& rm : rootMoves) - rm.previousScore = rm.score; - - size_t pvFirst = 0; - pvLast = 0; - - if (!Threads.increaseDepth) - searchAgainCounter++; - - // MultiPV loop. We perform a full root search for each PV line - for (pvIdx = 0; pvIdx < multiPV && !Threads.stop; ++pvIdx) - { - if (pvIdx == pvLast) - { - pvFirst = pvLast; - for (pvLast++; pvLast < rootMoves.size(); pvLast++) - if (rootMoves[pvLast].tbRank != rootMoves[pvFirst].tbRank) - break; - } - - // Reset UCI info selDepth for each depth and each PV line - selDepth = 0; - - // Reset aspiration window starting size - Value prev = rootMoves[pvIdx].averageScore; - delta = Value(10) + int(prev) * prev / 17470; - alpha = std::max(prev - delta,-VALUE_INFINITE); - beta = std::min(prev + delta, VALUE_INFINITE); - - // Adjust optimism based on root move's previousScore (~4 Elo) - int opt = 113 * prev / (std::abs(prev) + 109); - optimism[ us] = Value(opt); - optimism[~us] = -optimism[us]; - - // Start with a small aspiration window and, in the case of a fail - // high/low, re-search with a bigger window until we don't fail - // high/low anymore. - int failedHighCnt = 0; - while (true) - { - // Adjust the effective depth searched, but ensure at least one effective increment for every - // four searchAgain steps (see issue #2717). - Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt - 3 * (searchAgainCounter + 1) / 4); - bestValue = Stockfish::search(rootPos, ss, alpha, beta, adjustedDepth, false); - - // Bring the best move to the front. It is critical that sorting - // is done with a stable algorithm because all the values but the - // first and eventually the new best one is set to -VALUE_INFINITE - // and we want to keep the same order for all the moves except the - // new PV that goes to the front. Note that in the case of MultiPV - // search the already searched PV lines are preserved. - std::stable_sort(rootMoves.begin() + pvIdx, rootMoves.begin() + pvLast); - - // If search has been stopped, we break immediately. Sorting is - // safe because RootMoves is still valid, although it refers to - // the previous iteration. - if (Threads.stop) - break; - - // When failing high/low give some update (without cluttering - // the UI) before a re-search. - if ( mainThread - && multiPV == 1 - && (bestValue <= alpha || bestValue >= beta) - && Time.elapsed() > 3000) - sync_cout << UCI::pv(rootPos, rootDepth) << sync_endl; - - // In case of failing low/high increase aspiration window and - // re-search, otherwise exit the loop. - if (bestValue <= alpha) - { - beta = (alpha + beta) / 2; - alpha = std::max(bestValue - delta, -VALUE_INFINITE); - - failedHighCnt = 0; - if (mainThread) - mainThread->stopOnPonderhit = false; - } - else if (bestValue >= beta) - { - beta = std::min(bestValue + delta, VALUE_INFINITE); - ++failedHighCnt; - } - else - break; - - delta += delta / 3; - - assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE); - } - - // Sort the PV lines searched so far and update the GUI - std::stable_sort(rootMoves.begin() + pvFirst, rootMoves.begin() + pvIdx + 1); - - if ( mainThread - && (Threads.stop || pvIdx + 1 == multiPV || Time.elapsed() > 3000)) - sync_cout << UCI::pv(rootPos, rootDepth) << sync_endl; - } - - if (!Threads.stop) - completedDepth = rootDepth; - - if (rootMoves[0].pv[0] != lastBestMove) - { - lastBestMove = rootMoves[0].pv[0]; - lastBestMoveDepth = rootDepth; - } - - // Have we found a "mate in x"? - if ( Limits.mate - && bestValue >= VALUE_MATE_IN_MAX_PLY - && VALUE_MATE - bestValue <= 2 * Limits.mate) - Threads.stop = true; - - if (!mainThread) - continue; - - // If the skill level is enabled and time is up, pick a sub-optimal best move - if (skill.enabled() && skill.time_to_pick(rootDepth)) - skill.pick_best(multiPV); - - // Use part of the gained time from a previous stable move for the current move - for (Thread* th : Threads) - { - totBestMoveChanges += th->bestMoveChanges; - th->bestMoveChanges = 0; - } - - // Do we have time for the next iteration? Can we stop searching now? - if ( Limits.use_time_management() - && !Threads.stop - && !mainThread->stopOnPonderhit) - { - double fallingEval = (69 + 13 * (mainThread->bestPreviousAverageScore - bestValue) - + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 619.6; - fallingEval = std::clamp(fallingEval, 0.5, 1.5); - - // If the bestMove is stable over several iterations, reduce time accordingly - timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.57 : 0.65; - double reduction = (1.4 + mainThread->previousTimeReduction) / (2.08 * timeReduction); - double bestMoveInstability = 1 + 1.8 * totBestMoveChanges / Threads.size(); - - double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability; - - // Cap used time in case of a single legal move for a better viewer experience - if (rootMoves.size() == 1) - totalTime = std::min(500.0, totalTime); - - // Stop the search if we have exceeded the totalTime - if (Time.elapsed() > totalTime) - { - // If we are allowed to ponder do not stop the search now but - // keep pondering until the GUI sends "ponderhit" or "stop". - if (mainThread->ponder) - mainThread->stopOnPonderhit = true; - else - Threads.stop = true; - } - else if ( !mainThread->ponder - && Time.elapsed() > totalTime * 0.50) - Threads.increaseDepth = false; - else - Threads.increaseDepth = true; - } - - mainThread->iterValue[iterIdx] = bestValue; - iterIdx = (iterIdx + 1) & 3; - } - - if (!mainThread) - return; - - mainThread->previousTimeReduction = timeReduction; - - // If the skill level is enabled, swap the best PV line with the sub-optimal one - if (skill.enabled()) - std::swap(rootMoves[0], *std::find(rootMoves.begin(), rootMoves.end(), - skill.best ? skill.best : skill.pick_best(multiPV))); + // Allocate stack with extra size to allow access from (ss-7) to (ss+2): + // (ss-7) is needed for update_continuation_histories(ss-1) which accesses (ss-6), + // (ss+2) is needed for initialization of statScore and killers. + Stack stack[MAX_PLY + 10], *ss = stack + 7; + Move pv[MAX_PLY + 1]; + Value alpha, beta, delta; + Move lastBestMove = MOVE_NONE; + Depth lastBestMoveDepth = 0; + MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr); + double timeReduction = 1, totBestMoveChanges = 0; + Color us = rootPos.side_to_move(); + int iterIdx = 0; + + std::memset(ss - 7, 0, 10 * sizeof(Stack)); + for (int i = 7; i > 0; --i) + { + (ss - i)->continuationHistory = + &this->continuationHistory[0][0][NO_PIECE][0]; // Use as a sentinel + (ss - i)->staticEval = VALUE_NONE; + } + + for (int i = 0; i <= MAX_PLY + 2; ++i) + (ss + i)->ply = i; + + ss->pv = pv; + + bestValue = -VALUE_INFINITE; + + if (mainThread) + { + if (mainThread->bestPreviousScore == VALUE_INFINITE) + for (int i = 0; i < 4; ++i) + mainThread->iterValue[i] = VALUE_ZERO; + else + for (int i = 0; i < 4; ++i) + mainThread->iterValue[i] = mainThread->bestPreviousScore; + } + + size_t multiPV = size_t(Options["MultiPV"]); + Skill skill(Options["Skill Level"], Options["UCI_LimitStrength"] ? int(Options["UCI_Elo"]) : 0); + + // When playing with strength handicap enable MultiPV search that we will + // use behind-the-scenes to retrieve a set of possible moves. + if (skill.enabled()) + multiPV = std::max(multiPV, size_t(4)); + + multiPV = std::min(multiPV, rootMoves.size()); + + int searchAgainCounter = 0; + + // Iterative deepening loop until requested to stop or the target depth is reached + while (++rootDepth < MAX_PLY && !Threads.stop + && !(Limits.depth && mainThread && rootDepth > Limits.depth)) + { + // Age out PV variability metric + if (mainThread) + totBestMoveChanges /= 2; + + // Save the last iteration's scores before the first PV line is searched and + // all the move scores except the (new) PV are set to -VALUE_INFINITE. + for (RootMove& rm : rootMoves) + rm.previousScore = rm.score; + + size_t pvFirst = 0; + pvLast = 0; + + if (!Threads.increaseDepth) + searchAgainCounter++; + + // MultiPV loop. We perform a full root search for each PV line + for (pvIdx = 0; pvIdx < multiPV && !Threads.stop; ++pvIdx) + { + if (pvIdx == pvLast) + { + pvFirst = pvLast; + for (pvLast++; pvLast < rootMoves.size(); pvLast++) + if (rootMoves[pvLast].tbRank != rootMoves[pvFirst].tbRank) + break; + } + + // Reset UCI info selDepth for each depth and each PV line + selDepth = 0; + + // Reset aspiration window starting size + Value prev = rootMoves[pvIdx].averageScore; + delta = Value(10) + int(prev) * prev / 17470; + alpha = std::max(prev - delta, -VALUE_INFINITE); + beta = std::min(prev + delta, VALUE_INFINITE); + + // Adjust optimism based on root move's previousScore (~4 Elo) + int opt = 113 * prev / (std::abs(prev) + 109); + optimism[us] = Value(opt); + optimism[~us] = -optimism[us]; + + // Start with a small aspiration window and, in the case of a fail + // high/low, re-search with a bigger window until we don't fail + // high/low anymore. + int failedHighCnt = 0; + while (true) + { + // Adjust the effective depth searched, but ensure at least one effective increment for every + // four searchAgain steps (see issue #2717). + Depth adjustedDepth = + std::max(1, rootDepth - failedHighCnt - 3 * (searchAgainCounter + 1) / 4); + bestValue = Stockfish::search(rootPos, ss, alpha, beta, adjustedDepth, false); + + // Bring the best move to the front. It is critical that sorting + // is done with a stable algorithm because all the values but the + // first and eventually the new best one is set to -VALUE_INFINITE + // and we want to keep the same order for all the moves except the + // new PV that goes to the front. Note that in the case of MultiPV + // search the already searched PV lines are preserved. + std::stable_sort(rootMoves.begin() + pvIdx, rootMoves.begin() + pvLast); + + // If search has been stopped, we break immediately. Sorting is + // safe because RootMoves is still valid, although it refers to + // the previous iteration. + if (Threads.stop) + break; + + // When failing high/low give some update (without cluttering + // the UI) before a re-search. + if (mainThread && multiPV == 1 && (bestValue <= alpha || bestValue >= beta) + && Time.elapsed() > 3000) + sync_cout << UCI::pv(rootPos, rootDepth) << sync_endl; + + // In case of failing low/high increase aspiration window and + // re-search, otherwise exit the loop. + if (bestValue <= alpha) + { + beta = (alpha + beta) / 2; + alpha = std::max(bestValue - delta, -VALUE_INFINITE); + + failedHighCnt = 0; + if (mainThread) + mainThread->stopOnPonderhit = false; + } + else if (bestValue >= beta) + { + beta = std::min(bestValue + delta, VALUE_INFINITE); + ++failedHighCnt; + } + else + break; + + delta += delta / 3; + + assert(alpha >= -VALUE_INFINITE && beta <= VALUE_INFINITE); + } + + // Sort the PV lines searched so far and update the GUI + std::stable_sort(rootMoves.begin() + pvFirst, rootMoves.begin() + pvIdx + 1); + + if (mainThread && (Threads.stop || pvIdx + 1 == multiPV || Time.elapsed() > 3000)) + sync_cout << UCI::pv(rootPos, rootDepth) << sync_endl; + } + + if (!Threads.stop) + completedDepth = rootDepth; + + if (rootMoves[0].pv[0] != lastBestMove) + { + lastBestMove = rootMoves[0].pv[0]; + lastBestMoveDepth = rootDepth; + } + + // Have we found a "mate in x"? + if (Limits.mate && bestValue >= VALUE_MATE_IN_MAX_PLY + && VALUE_MATE - bestValue <= 2 * Limits.mate) + Threads.stop = true; + + if (!mainThread) + continue; + + // If the skill level is enabled and time is up, pick a sub-optimal best move + if (skill.enabled() && skill.time_to_pick(rootDepth)) + skill.pick_best(multiPV); + + // Use part of the gained time from a previous stable move for the current move + for (Thread* th : Threads) + { + totBestMoveChanges += th->bestMoveChanges; + th->bestMoveChanges = 0; + } + + // Do we have time for the next iteration? Can we stop searching now? + if (Limits.use_time_management() && !Threads.stop && !mainThread->stopOnPonderhit) + { + double fallingEval = (69 + 13 * (mainThread->bestPreviousAverageScore - bestValue) + + 6 * (mainThread->iterValue[iterIdx] - bestValue)) + / 619.6; + fallingEval = std::clamp(fallingEval, 0.5, 1.5); + + // If the bestMove is stable over several iterations, reduce time accordingly + timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.57 : 0.65; + double reduction = (1.4 + mainThread->previousTimeReduction) / (2.08 * timeReduction); + double bestMoveInstability = 1 + 1.8 * totBestMoveChanges / Threads.size(); + + double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability; + + // Cap used time in case of a single legal move for a better viewer experience + if (rootMoves.size() == 1) + totalTime = std::min(500.0, totalTime); + + // Stop the search if we have exceeded the totalTime + if (Time.elapsed() > totalTime) + { + // If we are allowed to ponder do not stop the search now but + // keep pondering until the GUI sends "ponderhit" or "stop". + if (mainThread->ponder) + mainThread->stopOnPonderhit = true; + else + Threads.stop = true; + } + else if (!mainThread->ponder && Time.elapsed() > totalTime * 0.50) + Threads.increaseDepth = false; + else + Threads.increaseDepth = true; + } + + mainThread->iterValue[iterIdx] = bestValue; + iterIdx = (iterIdx + 1) & 3; + } + + if (!mainThread) + return; + + mainThread->previousTimeReduction = timeReduction; + + // If the skill level is enabled, swap the best PV line with the sub-optimal one + if (skill.enabled()) + std::swap(rootMoves[0], *std::find(rootMoves.begin(), rootMoves.end(), + skill.best ? skill.best : skill.pick_best(multiPV))); } namespace { - // search<>() is the main search function for both PV and non-PV nodes +// search<>() is the main search function for both PV and non-PV nodes - template - Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) { +template +Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) { - constexpr bool PvNode = nodeType != NonPV; + constexpr bool PvNode = nodeType != NonPV; constexpr bool rootNode = nodeType == Root; // Dive into quiescence search when the depth reaches zero if (depth <= 0) - return qsearch(pos, ss, alpha, beta); + return qsearch < PvNode ? PV : NonPV > (pos, ss, alpha, beta); // Check if we have an upcoming move that draws by repetition, or // if the opponent had an alternative move earlier to this position. - if ( !rootNode - && alpha < VALUE_DRAW - && pos.has_game_cycle(ss->ply)) + if (!rootNode && alpha < VALUE_DRAW && pos.has_game_cycle(ss->ply)) { alpha = value_draw(pos.this_thread()); if (alpha >= beta) @@ -545,43 +547,41 @@ namespace { assert(0 < depth && depth < MAX_PLY); assert(!(PvNode && cutNode)); - Move pv[MAX_PLY+1], capturesSearched[32], quietsSearched[32]; + Move pv[MAX_PLY + 1], capturesSearched[32], quietsSearched[32]; StateInfo st; ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize); TTEntry* tte; - Key posKey; - Move ttMove, move, excludedMove, bestMove; - Depth extension, newDepth; - Value bestValue, value, ttValue, eval, maxValue, probCutBeta; - bool givesCheck, improving, priorCapture, singularQuietLMR; - bool capture, moveCountPruning, ttCapture; - Piece movedPiece; - int moveCount, captureCount, quietCount; + Key posKey; + Move ttMove, move, excludedMove, bestMove; + Depth extension, newDepth; + Value bestValue, value, ttValue, eval, maxValue, probCutBeta; + bool givesCheck, improving, priorCapture, singularQuietLMR; + bool capture, moveCountPruning, ttCapture; + Piece movedPiece; + int moveCount, captureCount, quietCount; // Step 1. Initialize node Thread* thisThread = pos.this_thread(); ss->inCheck = pos.checkers(); priorCapture = pos.captured_piece(); Color us = pos.side_to_move(); - moveCount = captureCount = quietCount = ss->moveCount = 0; - bestValue = -VALUE_INFINITE; - maxValue = VALUE_INFINITE; + moveCount = captureCount = quietCount = ss->moveCount = 0; + bestValue = -VALUE_INFINITE; + maxValue = VALUE_INFINITE; // Check for the available remaining time if (thisThread == Threads.main()) static_cast(thisThread)->check_time(); // Used to send selDepth info to GUI (selDepth counts from 1, ply from 0) - if ( PvNode - && thisThread->selDepth < ss->ply + 1) + if (PvNode && thisThread->selDepth < ss->ply + 1) thisThread->selDepth = ss->ply + 1; if (!rootNode) { // Step 2. Check for aborted search and immediate draw - if ( Threads.stop.load(std::memory_order_relaxed) - || pos.is_draw(ss->ply) + if (Threads.stop.load(std::memory_order_relaxed) || pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos) : value_draw(pos.this_thread()); @@ -593,7 +593,7 @@ namespace { // signs apply also in the opposite condition of being mated instead of giving // mate. In this case, return a fail-high score. alpha = std::max(mated_in(ss->ply), alpha); - beta = std::min(mate_in(ss->ply+1), beta); + beta = std::min(mate_in(ss->ply + 1), beta); if (alpha >= beta) return alpha; } @@ -602,20 +602,21 @@ namespace { assert(0 <= ss->ply && ss->ply < MAX_PLY); - (ss+1)->excludedMove = bestMove = MOVE_NONE; - (ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE; - (ss+2)->cutoffCnt = 0; - ss->doubleExtensions = (ss-1)->doubleExtensions; - Square prevSq = is_ok((ss-1)->currentMove) ? to_sq((ss-1)->currentMove) : SQ_NONE; - ss->statScore = 0; + (ss + 1)->excludedMove = bestMove = MOVE_NONE; + (ss + 2)->killers[0] = (ss + 2)->killers[1] = MOVE_NONE; + (ss + 2)->cutoffCnt = 0; + ss->doubleExtensions = (ss - 1)->doubleExtensions; + Square prevSq = is_ok((ss - 1)->currentMove) ? to_sq((ss - 1)->currentMove) : SQ_NONE; + ss->statScore = 0; // Step 4. Transposition table lookup. excludedMove = ss->excludedMove; - posKey = pos.key(); - tte = TT.probe(posKey, ss->ttHit); - ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; - ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] - : ss->ttHit ? tte->move() : MOVE_NONE; + posKey = pos.key(); + tte = TT.probe(posKey, ss->ttHit); + ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; + ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] + : ss->ttHit ? tte->move() + : MOVE_NONE; ttCapture = ttMove && pos.capture_stage(ttMove); // At this point, if excluded, skip straight to step 6, static eval. However, @@ -624,10 +625,8 @@ namespace { ss->ttPv = PvNode || (ss->ttHit && tte->is_pv()); // At non-PV nodes we check for an early TT cutoff - if ( !PvNode - && !excludedMove - && tte->depth() > depth - && ttValue != VALUE_NONE // Possible in case of TT access race or if !ttHit + if (!PvNode && !excludedMove && tte->depth() > depth + && ttValue != VALUE_NONE // Possible in case of TT access race or if !ttHit && (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER))) { // If ttMove is quiet, update move sorting heuristics on TT hit (~2 Elo) @@ -640,10 +639,9 @@ namespace { update_quiet_stats(pos, ss, ttMove, stat_bonus(depth)); // Extra penalty for early quiet moves of the previous ply (~0 Elo on STC, ~2 Elo on LTC) - if ( prevSq != SQ_NONE - && (ss-1)->moveCount <= 2 - && !priorCapture) - update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + 1)); + if (prevSq != SQ_NONE && (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 (~1 Elo) else if (!ttCapture) @@ -665,13 +663,12 @@ namespace { { int piecesCount = pos.count(); - if ( piecesCount <= TB::Cardinality - && (piecesCount < TB::Cardinality || depth >= TB::ProbeDepth) - && pos.rule50_count() == 0 + if (piecesCount <= TB::Cardinality + && (piecesCount < TB::Cardinality || depth >= TB::ProbeDepth) && pos.rule50_count() == 0 && !pos.can_castle(ANY_CASTLING)) { TB::ProbeState err; - TB::WDLScore wdl = Tablebases::probe_wdl(pos, &err); + TB::WDLScore wdl = Tablebases::probe_wdl(pos, &err); // Force check of time on the next occasion if (thisThread == Threads.main()) @@ -684,19 +681,18 @@ namespace { int drawScore = TB::UseRule50 ? 1 : 0; // use the range VALUE_MATE_IN_MAX_PLY to VALUE_TB_WIN_IN_MAX_PLY to score - value = wdl < -drawScore ? VALUE_MATED_IN_MAX_PLY + ss->ply + 1 - : wdl > drawScore ? VALUE_MATE_IN_MAX_PLY - ss->ply - 1 - : VALUE_DRAW + 2 * wdl * drawScore; + value = wdl < -drawScore ? VALUE_MATED_IN_MAX_PLY + ss->ply + 1 + : wdl > drawScore ? VALUE_MATE_IN_MAX_PLY - ss->ply - 1 + : VALUE_DRAW + 2 * wdl * drawScore; - Bound b = wdl < -drawScore ? BOUND_UPPER - : wdl > drawScore ? BOUND_LOWER : BOUND_EXACT; + Bound b = wdl < -drawScore ? BOUND_UPPER + : wdl > drawScore ? BOUND_LOWER + : BOUND_EXACT; - if ( b == BOUND_EXACT - || (b == BOUND_LOWER ? value >= beta : value <= alpha)) + if (b == BOUND_EXACT || (b == BOUND_LOWER ? value >= beta : value <= alpha)) { tte->save(posKey, value_to_tt(value, ss->ply), ss->ttPv, b, - std::min(MAX_PLY - 1, depth + 6), - MOVE_NONE, VALUE_NONE); + std::min(MAX_PLY - 1, depth + 6), MOVE_NONE, VALUE_NONE); return value; } @@ -719,7 +715,7 @@ namespace { { // Skip early pruning when in check ss->staticEval = eval = VALUE_NONE; - improving = false; + improving = false; goto moves_loop; } else if (excludedMove) @@ -738,8 +734,7 @@ namespace { Eval::NNUE::hint_common_parent_position(pos); // ttValue can be used as a better position evaluation (~7 Elo) - if ( ttValue != VALUE_NONE - && (tte->bound() & (ttValue > eval ? BOUND_LOWER : BOUND_UPPER))) + if (ttValue != VALUE_NONE && (tte->bound() & (ttValue > eval ? BOUND_LOWER : BOUND_UPPER))) eval = ttValue; } else @@ -750,12 +745,10 @@ namespace { } // Use static evaluation difference to improve quiet move ordering (~4 Elo) - if ( is_ok((ss-1)->currentMove) - && !(ss-1)->inCheck - && !priorCapture) + if (is_ok((ss - 1)->currentMove) && !(ss - 1)->inCheck && !priorCapture) { - int bonus = std::clamp(-18 * int((ss-1)->staticEval + ss->staticEval), -1812, 1812); - thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << bonus; + int bonus = std::clamp(-18 * int((ss - 1)->staticEval + ss->staticEval), -1812, 1812); + thisThread->mainHistory[~us][from_to((ss - 1)->currentMove)] << bonus; } // Set up the improving flag, which is true if current static evaluation is @@ -763,15 +756,15 @@ namespace { // check at our previous move we look at static evaluation at move prior to it // and if we were in check at move prior to it flag is set to true) and is // false otherwise. The improving flag is used in various pruning heuristics. - improving = (ss-2)->staticEval != VALUE_NONE ? ss->staticEval > (ss-2)->staticEval - : (ss-4)->staticEval != VALUE_NONE ? ss->staticEval > (ss-4)->staticEval - : true; + improving = (ss - 2)->staticEval != VALUE_NONE ? ss->staticEval > (ss - 2)->staticEval + : (ss - 4)->staticEval != VALUE_NONE ? ss->staticEval > (ss - 4)->staticEval + : true; // Step 7. Razoring (~1 Elo) // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. // Adjust razor margin according to cutoffCnt. (~1 Elo) - if (eval < alpha - 492 - (257 - 200 * ((ss+1)->cutoffCnt > 3)) * depth * depth) + if (eval < alpha - 492 - (257 - 200 * ((ss + 1)->cutoffCnt > 3)) * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -780,38 +773,31 @@ namespace { // Step 8. Futility pruning: child node (~40 Elo) // The depth condition is important for mate finding. - if ( !ss->ttPv - && depth < 9 - && eval - futility_margin(depth, cutNode && !ss->ttHit, improving) - (ss-1)->statScore / 321 >= beta - && eval >= beta - && eval < 29462 // smaller than TB wins - && !( !ttCapture - && ttMove)) + if (!ss->ttPv && depth < 9 + && eval - futility_margin(depth, cutNode && !ss->ttHit, improving) + - (ss - 1)->statScore / 321 + >= beta + && eval >= beta && eval < 29462 // smaller than TB wins + && !(!ttCapture && ttMove)) return eval; // Step 9. Null move search with verification search (~35 Elo) - if ( !PvNode - && (ss-1)->currentMove != MOVE_NULL - && (ss-1)->statScore < 17257 - && eval >= beta - && eval >= ss->staticEval - && ss->staticEval >= beta - 24 * depth + 281 - && !excludedMove - && pos.non_pawn_material(us) - && ss->ply >= thisThread->nmpMinPly - && beta > VALUE_TB_LOSS_IN_MAX_PLY) + if (!PvNode && (ss - 1)->currentMove != MOVE_NULL && (ss - 1)->statScore < 17257 && eval >= beta + && eval >= ss->staticEval && ss->staticEval >= beta - 24 * depth + 281 && !excludedMove + && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly + && beta > VALUE_TB_LOSS_IN_MAX_PLY) { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and eval Depth R = std::min(int(eval - beta) / 152, 6) + depth / 3 + 4; - ss->currentMove = MOVE_NULL; + ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; pos.do_null_move(st); - Value nullValue = -search(pos, ss+1, -beta, -beta+1, depth-R, !cutNode); + Value nullValue = -search(pos, ss + 1, -beta, -beta + 1, depth - R, !cutNode); pos.undo_null_move(); @@ -821,13 +807,13 @@ namespace { if (thisThread->nmpMinPly || depth < 14) return nullValue; - assert(!thisThread->nmpMinPly); // Recursive verification is not allowed + assert(!thisThread->nmpMinPly); // Recursive verification is not allowed // Do verification search at high depths, with null move pruning disabled // until ply exceeds nmpMinPly. - thisThread->nmpMinPly = ss->ply + 3 * (depth-R) / 4; + thisThread->nmpMinPly = ss->ply + 3 * (depth - R) / 4; - Value v = search(pos, ss, beta-1, beta, depth-R, false); + Value v = search(pos, ss, beta - 1, beta, depth - R, false); thisThread->nmpMinPly = 0; @@ -839,16 +825,13 @@ namespace { // Step 10. If the position doesn't have a ttMove, decrease depth by 2 // (or by 4 if the TT entry for the current position was hit and the stored depth is greater than or equal to the current depth). // Use qsearch if depth is equal or below zero (~9 Elo) - if ( PvNode - && !ttMove) + if (PvNode && !ttMove) depth -= 2 + 2 * (ss->ttHit && tte->depth() >= depth); if (depth <= 0) return qsearch(pos, ss, alpha, beta); - if ( cutNode - && depth >= 8 - && !ttMove) + if (cutNode && depth >= 8 && !ttMove) depth -= 2; probCutBeta = beta + 168 - 70 * improving; @@ -856,16 +839,14 @@ namespace { // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search returns a value // much above beta, we can (almost) safely prune the previous move. - if ( !PvNode - && depth > 3 - && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY - // If value from transposition table is lower than probCutBeta, don't attempt probCut - // there and in further interactions with transposition table cutoff depth is set to depth - 3 - // because probCut search has depth set to depth - 4 but we also do a move before it - // So effective depth is equal to depth - 3 - && !( tte->depth() >= depth - 3 - && ttValue != VALUE_NONE - && ttValue < probCutBeta)) + if ( + !PvNode && depth > 3 + && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY + // If value from transposition table is lower than probCutBeta, don't attempt probCut + // there and in further interactions with transposition table cutoff depth is set to depth - 3 + // because probCut search has depth set to depth - 4 but we also do a move before it + // So effective depth is equal to depth - 3 + && !(tte->depth() >= depth - 3 && ttValue != VALUE_NONE && ttValue < probCutBeta)) { assert(probCutBeta < VALUE_INFINITE); @@ -877,26 +858,27 @@ namespace { assert(pos.capture_stage(move)); ss->currentMove = move; - ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck] - [true] - [pos.moved_piece(move)] - [to_sq(move)]; + ss->continuationHistory = + &thisThread + ->continuationHistory[ss->inCheck][true][pos.moved_piece(move)][to_sq(move)]; pos.do_move(move, st); // Perform a preliminary qsearch to verify that the move holds - value = -qsearch(pos, ss+1, -probCutBeta, -probCutBeta+1); + value = -qsearch(pos, ss + 1, -probCutBeta, -probCutBeta + 1); // If the qsearch held, perform the regular search if (value >= probCutBeta) - value = -search(pos, ss+1, -probCutBeta, -probCutBeta+1, depth - 4, !cutNode); + value = -search(pos, ss + 1, -probCutBeta, -probCutBeta + 1, depth - 4, + !cutNode); pos.undo_move(move); if (value >= probCutBeta) { // Save ProbCut data into transposition table - tte->save(posKey, value_to_tt(value, ss->ply), ss->ttPv, BOUND_LOWER, depth - 3, move, ss->staticEval); + tte->save(posKey, value_to_tt(value, ss->ply), ss->ttPv, BOUND_LOWER, depth - 3, + move, ss->staticEval); return value - (probCutBeta - beta); } } @@ -904,447 +886,416 @@ namespace { Eval::NNUE::hint_common_parent_position(pos); } -moves_loop: // When in check, search starts here +moves_loop: // When in check, search starts here // Step 12. A small Probcut idea, when we are in check (~4 Elo) probCutBeta = beta + 416; - if ( ss->inCheck - && !PvNode - && ttCapture - && (tte->bound() & BOUND_LOWER) - && tte->depth() >= depth - 4 - && ttValue >= probCutBeta - && abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY - && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) + if (ss->inCheck && !PvNode && ttCapture && (tte->bound() & BOUND_LOWER) + && tte->depth() >= depth - 4 && ttValue >= probCutBeta + && abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) return probCutBeta; - const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory, - (ss-3)->continuationHistory, (ss-4)->continuationHistory, - nullptr , (ss-6)->continuationHistory }; + const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, + (ss - 2)->continuationHistory, + (ss - 3)->continuationHistory, + (ss - 4)->continuationHistory, + nullptr, + (ss - 6)->continuationHistory}; - Move countermove = prevSq != SQ_NONE ? thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] : MOVE_NONE; + Move countermove = + prevSq != SQ_NONE ? thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] : MOVE_NONE; - MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, - &captureHistory, - contHist, - countermove, - ss->killers); + MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &captureHistory, contHist, + countermove, ss->killers); - value = bestValue; + value = bestValue; moveCountPruning = singularQuietLMR = false; // Indicate PvNodes that will probably fail low if the node was searched // at a depth equal to or greater than the current depth, and the result // of this search was a fail low. - bool likelyFailLow = PvNode - && ttMove - && (tte->bound() & BOUND_UPPER) - && tte->depth() >= depth; + bool likelyFailLow = PvNode && ttMove && (tte->bound() & BOUND_UPPER) && tte->depth() >= depth; // Step 13. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. while ((move = mp.next_move(moveCountPruning)) != MOVE_NONE) { - assert(is_ok(move)); - - if (move == excludedMove) - continue; - - // Check for legality - if (!pos.legal(move)) - continue; - - // At root obey the "searchmoves" option and skip moves not listed in Root - // Move List. In MultiPV mode we also skip PV moves that have been already - // searched and those of lower "TB rank" if we are in a TB root position. - if (rootNode && !std::count(thisThread->rootMoves.begin() + thisThread->pvIdx, - thisThread->rootMoves.begin() + thisThread->pvLast, move)) - continue; - - ss->moveCount = ++moveCount; - - if (rootNode && thisThread == Threads.main() && Time.elapsed() > 3000) - 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 = 0; - capture = pos.capture_stage(move); - movedPiece = pos.moved_piece(move); - givesCheck = pos.gives_check(move); - - // Calculate new depth for this move - newDepth = depth - 1; - - Value delta = beta - alpha; - - Depth r = reduction(improving, depth, moveCount, delta, thisThread->rootDelta); - - // Step 14. Pruning at shallow depth (~120 Elo). - // Depth conditions are important for mate finding. - if ( !rootNode - && pos.non_pawn_material(us) - && bestValue > VALUE_TB_LOSS_IN_MAX_PLY) - { - // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold (~8 Elo) - if (!moveCountPruning) - moveCountPruning = moveCount >= futility_move_count(improving, depth); - - // Reduced depth of the next LMR search - int lmrDepth = newDepth - r; - - if ( capture - || givesCheck) - { - // Futility pruning for captures (~2 Elo) - if ( !givesCheck - && lmrDepth < 7 - && !ss->inCheck - && ss->staticEval + 188 + 206 * lmrDepth + PieceValue[pos.piece_on(to_sq(move))] - + captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] / 7 < alpha) - continue; - - // SEE based pruning for captures and checks (~11 Elo) - if (!pos.see_ge(move, Value(-185) * depth)) - continue; - } - else - { - int history = (*contHist[0])[movedPiece][to_sq(move)] + assert(is_ok(move)); + + if (move == excludedMove) + continue; + + // Check for legality + if (!pos.legal(move)) + continue; + + // At root obey the "searchmoves" option and skip moves not listed in Root + // Move List. In MultiPV mode we also skip PV moves that have been already + // searched and those of lower "TB rank" if we are in a TB root position. + if (rootNode + && !std::count(thisThread->rootMoves.begin() + thisThread->pvIdx, + thisThread->rootMoves.begin() + thisThread->pvLast, move)) + continue; + + ss->moveCount = ++moveCount; + + if (rootNode && thisThread == Threads.main() && Time.elapsed() > 3000) + 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 = 0; + capture = pos.capture_stage(move); + movedPiece = pos.moved_piece(move); + givesCheck = pos.gives_check(move); + + // Calculate new depth for this move + newDepth = depth - 1; + + Value delta = beta - alpha; + + Depth r = reduction(improving, depth, moveCount, delta, thisThread->rootDelta); + + // Step 14. Pruning at shallow depth (~120 Elo). + // Depth conditions are important for mate finding. + if (!rootNode && pos.non_pawn_material(us) && bestValue > VALUE_TB_LOSS_IN_MAX_PLY) + { + // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold (~8 Elo) + if (!moveCountPruning) + moveCountPruning = moveCount >= futility_move_count(improving, depth); + + // Reduced depth of the next LMR search + int lmrDepth = newDepth - r; + + if (capture || givesCheck) + { + // Futility pruning for captures (~2 Elo) + if (!givesCheck && lmrDepth < 7 && !ss->inCheck + && ss->staticEval + 188 + 206 * lmrDepth + PieceValue[pos.piece_on(to_sq(move))] + + captureHistory[movedPiece][to_sq(move)] + [type_of(pos.piece_on(to_sq(move)))] + / 7 + < alpha) + continue; + + // SEE based pruning for captures and checks (~11 Elo) + if (!pos.see_ge(move, Value(-185) * depth)) + continue; + } + else + { + int history = (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)]; - // Continuation history based pruning (~2 Elo) - if ( lmrDepth < 6 - && history < -3232 * depth) - continue; - - history += 2 * thisThread->mainHistory[us][from_to(move)]; - - lmrDepth += history / 5793; - lmrDepth = std::max(lmrDepth, -2); - - // Futility pruning: parent node (~13 Elo) - if ( !ss->inCheck - && lmrDepth < 13 - && ss->staticEval + 115 + 122 * lmrDepth <= alpha) - continue; - - lmrDepth = std::max(lmrDepth, 0); - - // Prune moves with negative SEE (~4 Elo) - if (!pos.see_ge(move, Value(-27 * lmrDepth * lmrDepth))) - continue; - } - } - - // Step 15. Extensions (~100 Elo) - // We take care to not overdo to avoid search getting stuck. - if (ss->ply < thisThread->rootDepth * 2) - { - // Singular extension search (~94 Elo). If all moves but one fail low on a - // search of (alpha-s, beta-s), and just one fails high on (alpha, beta), - // 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. Note - // that depth margin and singularBeta margin are known for having non-linear - // scaling. Their values are optimized to time controls of 180+1.8 and longer - // so changing them requires tests at this type of time controls. - if ( !rootNode - && depth >= 4 - (thisThread->completedDepth > 24) + 2 * (PvNode && tte->is_pv()) - && move == ttMove - && !excludedMove // Avoid recursive singular search - && abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY - && (tte->bound() & BOUND_LOWER) - && tte->depth() >= depth - 3) - { - Value singularBeta = ttValue - (64 + 57 * (ss->ttPv && !PvNode)) * depth / 64; - Depth singularDepth = (depth - 1) / 2; - - ss->excludedMove = move; - value = search(pos, ss, singularBeta - 1, singularBeta, singularDepth, cutNode); - ss->excludedMove = MOVE_NONE; - - if (value < singularBeta) - { - extension = 1; - singularQuietLMR = !ttCapture; - - // Avoid search explosion by limiting the number of double extensions - if ( !PvNode - && value < singularBeta - 18 - && ss->doubleExtensions <= 11) - { - extension = 2; - depth += depth < 15; - } - } - - // Multi-cut pruning - // Our ttMove is assumed to fail high, and now we failed high also on a - // reduced search without the ttMove. So we assume this expected cut-node - // is not singular, that multiple moves fail high, and we can prune the - // whole subtree by returning a softbound. - else if (singularBeta >= beta) - return singularBeta; - - // If the eval of ttMove is greater than beta, we reduce it (negative extension) (~7 Elo) - else if (ttValue >= beta) - extension = -2 - !PvNode; - - // If we are on a cutNode, reduce it based on depth (negative extension) (~1 Elo) - else if (cutNode) - extension = depth < 19 ? -2 : -1; - - // If the eval of ttMove is less than value, we reduce it (negative extension) (~1 Elo) - else if (ttValue <= value) - extension = -1; - } - - // Check extensions (~1 Elo) - else if ( givesCheck - && depth > 9) - extension = 1; - - // Quiet ttMove extensions (~1 Elo) - else if ( PvNode - && move == ttMove - && move == ss->killers[0] - && (*contHist[0])[movedPiece][to_sq(move)] >= 4194) - extension = 1; - } - - // Add extension to new depth - newDepth += extension; - ss->doubleExtensions = (ss-1)->doubleExtensions + (extension == 2); - - // Speculative prefetch as early as possible - prefetch(TT.first_entry(pos.key_after(move))); - - // Update the current move (this must be done after singular extension search) - ss->currentMove = move; - ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck] - [capture] - [movedPiece] - [to_sq(move)]; - - // Step 16. Make the move - pos.do_move(move, st, givesCheck); - - // Decrease reduction if position is or has been on the PV (~4 Elo) - if ( ss->ttPv - && !likelyFailLow) - r -= cutNode && tte->depth() >= depth ? 3 : 2; - - // Decrease reduction if opponent's move count is high (~1 Elo) - if ((ss-1)->moveCount > 7) - r--; - - // Increase reduction for cut nodes (~3 Elo) - if (cutNode) - r += 2; - - // Increase reduction if ttMove is a capture (~3 Elo) - if (ttCapture) - r++; - - // Decrease reduction for PvNodes (~2 Elo) - if (PvNode) - r--; - - // Decrease reduction if ttMove has been singularly extended (~1 Elo) - if (singularQuietLMR) - r--; - - // Increase reduction on repetition (~1 Elo) - if ( move == (ss-4)->currentMove - && pos.has_repeated()) - r += 2; - - // Increase reduction if next ply has a lot of fail high (~5 Elo) - if ((ss+1)->cutoffCnt > 3) - r++; - - // Decrease reduction for first generated move (ttMove) - else if (move == ttMove) - r--; - - ss->statScore = 2 * thisThread->mainHistory[us][from_to(move)] - + (*contHist[0])[movedPiece][to_sq(move)] - + (*contHist[1])[movedPiece][to_sq(move)] - + (*contHist[3])[movedPiece][to_sq(move)] - - 3848; - - // Decrease/increase reduction for moves with a good/bad history (~25 Elo) - r -= ss->statScore / (10216 + 3855 * (depth > 5 && depth < 23)); - - // Step 17. Late moves reduction / extension (LMR, ~117 Elo) - // We use various heuristics for the sons of a node after the first son has - // been searched. In general, we would like to reduce them, but there are many - // cases where we extend a son if it has good chances to be "interesting". - if ( depth >= 2 - && moveCount > 1 + (PvNode && ss->ply <= 1) - && ( !ss->ttPv - || !capture - || (cutNode && (ss-1)->moveCount > 1))) - { - // In general we want to cap the LMR depth search at newDepth, but when - // reduction is negative, we allow this move a limited search extension - // beyond the first move depth. This may lead to hidden double extensions. - Depth d = std::clamp(newDepth - r, 1, newDepth + 1); - - value = -search(pos, ss+1, -(alpha+1), -alpha, d, true); - - // Do a full-depth search when reduced LMR search fails high - if ( value > alpha - && d < newDepth) - { - // Adjust full-depth search based on LMR results - if the result - // was good enough search deeper, if it was bad enough search shallower. - const bool doDeeperSearch = value > (bestValue + 51 + 10 * (newDepth - d)); - const bool doEvenDeeperSearch = value > alpha + 700 && ss->doubleExtensions <= 6; - const bool doShallowerSearch = value < bestValue + newDepth; - - ss->doubleExtensions = ss->doubleExtensions + doEvenDeeperSearch; - - newDepth += doDeeperSearch - doShallowerSearch + doEvenDeeperSearch; - - if (newDepth > d) - value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth, !cutNode); - - int bonus = value <= alpha ? -stat_bonus(newDepth) - : value >= beta ? stat_bonus(newDepth) - : 0; - - update_continuation_histories(ss, movedPiece, to_sq(move), bonus); - } - } - - // Step 18. Full-depth search when LMR is skipped - else if (!PvNode || moveCount > 1) - { - // Increase reduction for cut nodes and not ttMove (~1 Elo) - if ( !ttMove - && cutNode) - r += 2; - - // Note that if expected reduction is high, we reduce search depth by 1 here - value = -search(pos, ss+1, -(alpha+1), -alpha, newDepth - (r > 3), !cutNode); - } - - // For PV nodes only, do a full PV search on the first move or after a fail high, - // otherwise let the parent node fail low with value <= alpha and try another move. - if ( PvNode - && (moveCount == 1 || value > alpha)) - { - (ss+1)->pv = pv; - (ss+1)->pv[0] = MOVE_NONE; - - value = -search(pos, ss+1, -beta, -alpha, newDepth, false); - } - - // Step 19. Undo move - pos.undo_move(move); - - assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); - - // Step 20. Check for a new best move - // Finished searching the move. If a stop occurred, the return value of - // the search cannot be trusted, and we return immediately without - // updating best move, PV and TT. - if (Threads.stop.load(std::memory_order_relaxed)) - return VALUE_ZERO; - - if (rootNode) - { - RootMove& rm = *std::find(thisThread->rootMoves.begin(), - thisThread->rootMoves.end(), move); - - rm.averageScore = rm.averageScore != -VALUE_INFINITE ? (2 * value + rm.averageScore) / 3 : value; - - // PV move or new best move? - if (moveCount == 1 || value > alpha) - { - rm.score = rm.uciScore = value; - rm.selDepth = thisThread->selDepth; - rm.scoreLowerbound = rm.scoreUpperbound = false; - - if (value >= beta) - { - rm.scoreLowerbound = true; - rm.uciScore = beta; - } - else if (value <= alpha) - { - rm.scoreUpperbound = true; - rm.uciScore = alpha; - } - - rm.pv.resize(1); - - assert((ss+1)->pv); - - for (Move* m = (ss+1)->pv; *m != MOVE_NONE; ++m) - rm.pv.push_back(*m); - - // We record how often the best move has been changed in each iteration. - // This information is used for time management. In MultiPV mode, - // we must take care to only do this for the first PV line. - if ( moveCount > 1 - && !thisThread->pvIdx) - ++thisThread->bestMoveChanges; - } - else - // All other moves but the PV, are set to the lowest value: this - // is not a problem when sorting because the sort is stable and the - // move position in the list is preserved - just the PV is pushed up. - rm.score = -VALUE_INFINITE; - } - - if (value > bestValue) - { - bestValue = value; - - if (value > alpha) - { - bestMove = move; - - if (PvNode && !rootNode) // Update pv even in fail-high case - update_pv(ss->pv, move, (ss+1)->pv); - - if (value >= beta) - { - ss->cutoffCnt += 1 + !ttMove; - assert(value >= beta); // Fail high - break; - } - else - { - // Reduce other moves if we have found at least one score improvement (~2 Elo) - if ( depth > 2 - && depth < 12 - && beta < 13828 - && value > -11369) - depth -= 2; - - assert(depth > 0); - alpha = value; // Update alpha! Always alpha < beta - } - } - } - - // If the move is worse than some previously searched move, - // remember it, to update its stats later. - if (move != bestMove && moveCount <= 32) - { - if (capture) - capturesSearched[captureCount++] = move; - - else - quietsSearched[quietCount++] = move; - } + // Continuation history based pruning (~2 Elo) + if (lmrDepth < 6 && history < -3232 * depth) + continue; + + history += 2 * thisThread->mainHistory[us][from_to(move)]; + + lmrDepth += history / 5793; + lmrDepth = std::max(lmrDepth, -2); + + // Futility pruning: parent node (~13 Elo) + if (!ss->inCheck && lmrDepth < 13 && ss->staticEval + 115 + 122 * lmrDepth <= alpha) + continue; + + lmrDepth = std::max(lmrDepth, 0); + + // Prune moves with negative SEE (~4 Elo) + if (!pos.see_ge(move, Value(-27 * lmrDepth * lmrDepth))) + continue; + } + } + + // Step 15. Extensions (~100 Elo) + // We take care to not overdo to avoid search getting stuck. + if (ss->ply < thisThread->rootDepth * 2) + { + // Singular extension search (~94 Elo). If all moves but one fail low on a + // search of (alpha-s, beta-s), and just one fails high on (alpha, beta), + // 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. Note + // that depth margin and singularBeta margin are known for having non-linear + // scaling. Their values are optimized to time controls of 180+1.8 and longer + // so changing them requires tests at this type of time controls. + if (!rootNode + && depth >= 4 - (thisThread->completedDepth > 24) + 2 * (PvNode && tte->is_pv()) + && move == ttMove && !excludedMove // Avoid recursive singular search + && abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && (tte->bound() & BOUND_LOWER) + && tte->depth() >= depth - 3) + { + Value singularBeta = ttValue - (64 + 57 * (ss->ttPv && !PvNode)) * depth / 64; + Depth singularDepth = (depth - 1) / 2; + + ss->excludedMove = move; + value = + search(pos, ss, singularBeta - 1, singularBeta, singularDepth, cutNode); + ss->excludedMove = MOVE_NONE; + + if (value < singularBeta) + { + extension = 1; + singularQuietLMR = !ttCapture; + + // Avoid search explosion by limiting the number of double extensions + if (!PvNode && value < singularBeta - 18 && ss->doubleExtensions <= 11) + { + extension = 2; + depth += depth < 15; + } + } + + // Multi-cut pruning + // Our ttMove is assumed to fail high, and now we failed high also on a + // reduced search without the ttMove. So we assume this expected cut-node + // is not singular, that multiple moves fail high, and we can prune the + // whole subtree by returning a softbound. + else if (singularBeta >= beta) + return singularBeta; + + // If the eval of ttMove is greater than beta, we reduce it (negative extension) (~7 Elo) + else if (ttValue >= beta) + extension = -2 - !PvNode; + + // If we are on a cutNode, reduce it based on depth (negative extension) (~1 Elo) + else if (cutNode) + extension = depth < 19 ? -2 : -1; + + // If the eval of ttMove is less than value, we reduce it (negative extension) (~1 Elo) + else if (ttValue <= value) + extension = -1; + } + + // Check extensions (~1 Elo) + else if (givesCheck && depth > 9) + extension = 1; + + // Quiet ttMove extensions (~1 Elo) + else if (PvNode && move == ttMove && move == ss->killers[0] + && (*contHist[0])[movedPiece][to_sq(move)] >= 4194) + extension = 1; + } + + // Add extension to new depth + newDepth += extension; + ss->doubleExtensions = (ss - 1)->doubleExtensions + (extension == 2); + + // Speculative prefetch as early as possible + prefetch(TT.first_entry(pos.key_after(move))); + + // Update the current move (this must be done after singular extension search) + ss->currentMove = move; + ss->continuationHistory = + &thisThread->continuationHistory[ss->inCheck][capture][movedPiece][to_sq(move)]; + + // Step 16. Make the move + pos.do_move(move, st, givesCheck); + + // Decrease reduction if position is or has been on the PV (~4 Elo) + if (ss->ttPv && !likelyFailLow) + r -= cutNode && tte->depth() >= depth ? 3 : 2; + + // Decrease reduction if opponent's move count is high (~1 Elo) + if ((ss - 1)->moveCount > 7) + r--; + + // Increase reduction for cut nodes (~3 Elo) + if (cutNode) + r += 2; + + // Increase reduction if ttMove is a capture (~3 Elo) + if (ttCapture) + r++; + + // Decrease reduction for PvNodes (~2 Elo) + if (PvNode) + r--; + + // Decrease reduction if ttMove has been singularly extended (~1 Elo) + if (singularQuietLMR) + r--; + + // Increase reduction on repetition (~1 Elo) + if (move == (ss - 4)->currentMove && pos.has_repeated()) + r += 2; + + // Increase reduction if next ply has a lot of fail high (~5 Elo) + if ((ss + 1)->cutoffCnt > 3) + r++; + + // Decrease reduction for first generated move (ttMove) + else if (move == ttMove) + r--; + + ss->statScore = 2 * thisThread->mainHistory[us][from_to(move)] + + (*contHist[0])[movedPiece][to_sq(move)] + + (*contHist[1])[movedPiece][to_sq(move)] + + (*contHist[3])[movedPiece][to_sq(move)] - 3848; + + // Decrease/increase reduction for moves with a good/bad history (~25 Elo) + r -= ss->statScore / (10216 + 3855 * (depth > 5 && depth < 23)); + + // Step 17. Late moves reduction / extension (LMR, ~117 Elo) + // We use various heuristics for the sons of a node after the first son has + // been searched. In general, we would like to reduce them, but there are many + // cases where we extend a son if it has good chances to be "interesting". + if (depth >= 2 && moveCount > 1 + (PvNode && ss->ply <= 1) + && (!ss->ttPv || !capture || (cutNode && (ss - 1)->moveCount > 1))) + { + // In general we want to cap the LMR depth search at newDepth, but when + // reduction is negative, we allow this move a limited search extension + // beyond the first move depth. This may lead to hidden double extensions. + Depth d = std::clamp(newDepth - r, 1, newDepth + 1); + + value = -search(pos, ss + 1, -(alpha + 1), -alpha, d, true); + + // Do a full-depth search when reduced LMR search fails high + if (value > alpha && d < newDepth) + { + // Adjust full-depth search based on LMR results - if the result + // was good enough search deeper, if it was bad enough search shallower. + const bool doDeeperSearch = value > (bestValue + 51 + 10 * (newDepth - d)); + const bool doEvenDeeperSearch = value > alpha + 700 && ss->doubleExtensions <= 6; + const bool doShallowerSearch = value < bestValue + newDepth; + + ss->doubleExtensions = ss->doubleExtensions + doEvenDeeperSearch; + + newDepth += doDeeperSearch - doShallowerSearch + doEvenDeeperSearch; + + if (newDepth > d) + value = -search(pos, ss + 1, -(alpha + 1), -alpha, newDepth, !cutNode); + + int bonus = value <= alpha ? -stat_bonus(newDepth) + : value >= beta ? stat_bonus(newDepth) + : 0; + + update_continuation_histories(ss, movedPiece, to_sq(move), bonus); + } + } + + // Step 18. Full-depth search when LMR is skipped + else if (!PvNode || moveCount > 1) + { + // Increase reduction for cut nodes and not ttMove (~1 Elo) + if (!ttMove && cutNode) + r += 2; + + // Note that if expected reduction is high, we reduce search depth by 1 here + value = -search(pos, ss + 1, -(alpha + 1), -alpha, newDepth - (r > 3), !cutNode); + } + + // For PV nodes only, do a full PV search on the first move or after a fail high, + // otherwise let the parent node fail low with value <= alpha and try another move. + if (PvNode && (moveCount == 1 || value > alpha)) + { + (ss + 1)->pv = pv; + (ss + 1)->pv[0] = MOVE_NONE; + + value = -search(pos, ss + 1, -beta, -alpha, newDepth, false); + } + + // Step 19. Undo move + pos.undo_move(move); + + assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); + + // Step 20. Check for a new best move + // Finished searching the move. If a stop occurred, the return value of + // the search cannot be trusted, and we return immediately without + // updating best move, PV and TT. + if (Threads.stop.load(std::memory_order_relaxed)) + return VALUE_ZERO; + + if (rootNode) + { + RootMove& rm = + *std::find(thisThread->rootMoves.begin(), thisThread->rootMoves.end(), move); + + rm.averageScore = + rm.averageScore != -VALUE_INFINITE ? (2 * value + rm.averageScore) / 3 : value; + + // PV move or new best move? + if (moveCount == 1 || value > alpha) + { + rm.score = rm.uciScore = value; + rm.selDepth = thisThread->selDepth; + rm.scoreLowerbound = rm.scoreUpperbound = false; + + if (value >= beta) + { + rm.scoreLowerbound = true; + rm.uciScore = beta; + } + else if (value <= alpha) + { + rm.scoreUpperbound = true; + rm.uciScore = alpha; + } + + rm.pv.resize(1); + + assert((ss + 1)->pv); + + for (Move* m = (ss + 1)->pv; *m != MOVE_NONE; ++m) + rm.pv.push_back(*m); + + // We record how often the best move has been changed in each iteration. + // This information is used for time management. In MultiPV mode, + // we must take care to only do this for the first PV line. + if (moveCount > 1 && !thisThread->pvIdx) + ++thisThread->bestMoveChanges; + } + else + // All other moves but the PV, are set to the lowest value: this + // is not a problem when sorting because the sort is stable and the + // move position in the list is preserved - just the PV is pushed up. + rm.score = -VALUE_INFINITE; + } + + if (value > bestValue) + { + bestValue = value; + + if (value > alpha) + { + bestMove = move; + + if (PvNode && !rootNode) // Update pv even in fail-high case + update_pv(ss->pv, move, (ss + 1)->pv); + + if (value >= beta) + { + ss->cutoffCnt += 1 + !ttMove; + assert(value >= beta); // Fail high + break; + } + else + { + // Reduce other moves if we have found at least one score improvement (~2 Elo) + if (depth > 2 && depth < 12 && beta < 13828 && value > -11369) + depth -= 2; + + assert(depth > 0); + alpha = value; // Update alpha! Always alpha < beta + } + } + } + + // If the move is worse than some previously searched move, + // remember it, to update its stats later. + if (move != bestMove && moveCount <= 32) + { + if (capture) + capturesSearched[captureCount++] = move; + + else + quietsSearched[quietCount++] = move; + } } // Step 21. Check for mate and stalemate @@ -1355,21 +1306,22 @@ namespace { assert(moveCount || !ss->inCheck || excludedMove || !MoveList(pos).size()); if (!moveCount) - bestValue = excludedMove ? alpha : - ss->inCheck ? mated_in(ss->ply) - : VALUE_DRAW; + bestValue = excludedMove ? alpha : ss->inCheck ? mated_in(ss->ply) : VALUE_DRAW; // If there is a move that produces search value greater than alpha we update the stats of searched moves else if (bestMove) - update_all_stats(pos, ss, bestMove, bestValue, beta, prevSq, - quietsSearched, quietCount, capturesSearched, captureCount, depth); + update_all_stats(pos, ss, bestMove, bestValue, beta, prevSq, quietsSearched, quietCount, + capturesSearched, captureCount, depth); // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (depth > 6) + (PvNode || cutNode) + (bestValue < alpha - 653) + ((ss-1)->moveCount > 11); - update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); - thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << stat_bonus(depth) * bonus / 2; + int bonus = (depth > 6) + (PvNode || cutNode) + (bestValue < alpha - 653) + + ((ss - 1)->moveCount > 11); + update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, + stat_bonus(depth) * bonus); + thisThread->mainHistory[~us][from_to((ss - 1)->currentMove)] + << stat_bonus(depth) * bonus / 2; } if (PvNode) @@ -1378,26 +1330,27 @@ namespace { // If no good move is found and the previous position was ttPv, then the previous // opponent move is probably good and the new position is added to the search tree. (~7 Elo) if (bestValue <= alpha) - ss->ttPv = ss->ttPv || ((ss-1)->ttPv && depth > 3); + ss->ttPv = ss->ttPv || ((ss - 1)->ttPv && depth > 3); // Write gathered information in transposition table if (!excludedMove && !(rootNode && thisThread->pvIdx)) tte->save(posKey, value_to_tt(bestValue, ss->ply), ss->ttPv, - bestValue >= beta ? BOUND_LOWER : - PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER, + bestValue >= beta ? BOUND_LOWER + : PvNode && bestMove ? BOUND_EXACT + : BOUND_UPPER, depth, bestMove, ss->staticEval); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); return bestValue; - } +} - // qsearch() is the quiescence search function, which is called by the main search - // function with zero depth, or recursively with further decreasing depth per call. - // (~155 Elo) - template - Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { +// qsearch() is the quiescence search function, which is called by the main search +// function with zero depth, or recursively with further decreasing depth per call. +// (~155 Elo) +template +Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { static_assert(nodeType != Root); constexpr bool PvNode = nodeType == PV; @@ -1408,42 +1361,40 @@ namespace { // Check if we have an upcoming move that draws by repetition, or // if the opponent had an alternative move earlier to this position. - if ( alpha < VALUE_DRAW - && pos.has_game_cycle(ss->ply)) + if (alpha < VALUE_DRAW && pos.has_game_cycle(ss->ply)) { alpha = value_draw(pos.this_thread()); if (alpha >= beta) return alpha; } - Move pv[MAX_PLY+1]; + Move pv[MAX_PLY + 1]; StateInfo st; ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize); TTEntry* tte; - Key posKey; - Move ttMove, move, bestMove; - Depth ttDepth; - Value bestValue, value, ttValue, futilityValue, futilityBase; - bool pvHit, givesCheck, capture; - int moveCount; - Color us = pos.side_to_move(); + Key posKey; + Move ttMove, move, bestMove; + Depth ttDepth; + Value bestValue, value, ttValue, futilityValue, futilityBase; + bool pvHit, givesCheck, capture; + int moveCount; + Color us = pos.side_to_move(); // Step 1. Initialize node if (PvNode) { - (ss+1)->pv = pv; - ss->pv[0] = MOVE_NONE; + (ss + 1)->pv = pv; + ss->pv[0] = MOVE_NONE; } Thread* thisThread = pos.this_thread(); - bestMove = MOVE_NONE; - ss->inCheck = pos.checkers(); - moveCount = 0; + bestMove = MOVE_NONE; + ss->inCheck = pos.checkers(); + moveCount = 0; // Step 2. Check for an immediate draw or maximum ply reached - if ( pos.is_draw(ss->ply) - || ss->ply >= MAX_PLY) + if (pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos) : VALUE_DRAW; assert(0 <= ss->ply && ss->ply < MAX_PLY); @@ -1451,20 +1402,18 @@ namespace { // Decide whether or not to include checks: this fixes also the type of // TT entry depth that we are going to use. Note that in qsearch we use // only two types of depth in TT: DEPTH_QS_CHECKS or DEPTH_QS_NO_CHECKS. - ttDepth = ss->inCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS - : DEPTH_QS_NO_CHECKS; + ttDepth = ss->inCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS : DEPTH_QS_NO_CHECKS; // Step 3. Transposition table lookup - posKey = pos.key(); - tte = TT.probe(posKey, ss->ttHit); + posKey = pos.key(); + tte = TT.probe(posKey, ss->ttHit); ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; - ttMove = ss->ttHit ? tte->move() : MOVE_NONE; - pvHit = ss->ttHit && tte->is_pv(); + ttMove = ss->ttHit ? tte->move() : MOVE_NONE; + pvHit = ss->ttHit && tte->is_pv(); // At non-PV nodes we check for an early TT cutoff - if ( !PvNode - && tte->depth() >= ttDepth - && ttValue != VALUE_NONE // Only in case of TT access race or if !ttHit + if (!PvNode && tte->depth() >= ttDepth + && ttValue != VALUE_NONE // Only in case of TT access race or if !ttHit && (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER))) return ttValue; @@ -1480,21 +1429,21 @@ namespace { ss->staticEval = bestValue = evaluate(pos); // ttValue can be used as a better position evaluation (~13 Elo) - if ( ttValue != VALUE_NONE + if (ttValue != VALUE_NONE && (tte->bound() & (ttValue > bestValue ? BOUND_LOWER : BOUND_UPPER))) bestValue = ttValue; } else // In case of null move search use previous static eval with a different sign - ss->staticEval = bestValue = (ss-1)->currentMove != MOVE_NULL ? evaluate(pos) - : -(ss-1)->staticEval; + ss->staticEval = bestValue = + (ss - 1)->currentMove != MOVE_NULL ? evaluate(pos) : -(ss - 1)->staticEval; // Stand pat. Return immediately if static value is at least beta if (bestValue >= beta) { if (!ss->ttHit) - tte->save(posKey, value_to_tt(bestValue, ss->ply), false, BOUND_LOWER, - DEPTH_NONE, MOVE_NONE, ss->staticEval); + tte->save(posKey, value_to_tt(bestValue, ss->ply), false, BOUND_LOWER, DEPTH_NONE, + MOVE_NONE, ss->staticEval); return bestValue; } @@ -1505,17 +1454,16 @@ namespace { futilityBase = std::min(ss->staticEval, bestValue) + 200; } - const PieceToHistory* contHist[] = {(ss-1)->continuationHistory, (ss-2)->continuationHistory}; + const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, + (ss - 2)->continuationHistory}; // Initialize a MovePicker object for the current position, and prepare // to search the moves. Because the depth is <= 0 here, only captures, // queen promotions, and other checks (only if depth >= DEPTH_QS_CHECKS) // will be generated. - Square prevSq = is_ok((ss-1)->currentMove) ? to_sq((ss-1)->currentMove) : SQ_NONE; - MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, - &thisThread->captureHistory, - contHist, - prevSq); + Square prevSq = is_ok((ss - 1)->currentMove) ? to_sq((ss - 1)->currentMove) : SQ_NONE; + MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory, + contHist, prevSq); int quietCheckEvasions = 0; @@ -1530,19 +1478,16 @@ namespace { continue; givesCheck = pos.gives_check(move); - capture = pos.capture_stage(move); + capture = pos.capture_stage(move); moveCount++; // Step 6. Pruning - if ( bestValue > VALUE_TB_LOSS_IN_MAX_PLY - && pos.non_pawn_material(us)) + if (bestValue > VALUE_TB_LOSS_IN_MAX_PLY && pos.non_pawn_material(us)) { // Futility pruning and moveCount pruning (~10 Elo) - if ( !givesCheck - && to_sq(move) != prevSq - && futilityBase > VALUE_TB_LOSS_IN_MAX_PLY - && type_of(move) != PROMOTION) + if (!givesCheck && to_sq(move) != prevSq && futilityBase > VALUE_TB_LOSS_IN_MAX_PLY + && type_of(move) != PROMOTION) { if (moveCount > 2) continue; @@ -1559,8 +1504,7 @@ namespace { // If static eval is much lower than alpha and move is not winning material // we can prune this move. - if ( futilityBase <= alpha - && !pos.see_ge(move, VALUE_ZERO + 1)) + if (futilityBase <= alpha && !pos.see_ge(move, VALUE_ZERO + 1)) { bestValue = std::max(bestValue, futilityBase); continue; @@ -1582,8 +1526,7 @@ namespace { break; // Continuation history based pruning (~3 Elo) - if ( !capture - && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < 0 + if (!capture && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < 0 && (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < 0) continue; @@ -1597,16 +1540,15 @@ namespace { // Update the current move ss->currentMove = move; - ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck] - [capture] - [pos.moved_piece(move)] - [to_sq(move)]; + ss->continuationHistory = + &thisThread + ->continuationHistory[ss->inCheck][capture][pos.moved_piece(move)][to_sq(move)]; quietCheckEvasions += !capture && ss->inCheck; // Step 7. Make and search the move pos.do_move(move, st, givesCheck); - value = -qsearch(pos, ss+1, -beta, -alpha, depth - 1); + value = -qsearch(pos, ss + 1, -beta, -alpha, depth - 1); pos.undo_move(move); assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); @@ -1620,13 +1562,13 @@ namespace { { bestMove = move; - if (PvNode) // Update pv even in fail-high case - update_pv(ss->pv, move, (ss+1)->pv); + if (PvNode) // Update pv even in fail-high case + update_pv(ss->pv, move, (ss + 1)->pv); - if (value < beta) // Update alpha here! + if (value < beta) // Update alpha here! alpha = value; else - break; // Fail high + break; // Fail high } } } @@ -1638,40 +1580,38 @@ namespace { { assert(!MoveList(pos).size()); - return mated_in(ss->ply); // Plies to mate from the root + return mated_in(ss->ply); // Plies to mate from the root } // Save gathered info in transposition table tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit, - bestValue >= beta ? BOUND_LOWER : BOUND_UPPER, - ttDepth, bestMove, ss->staticEval); + bestValue >= beta ? BOUND_LOWER : BOUND_UPPER, ttDepth, bestMove, ss->staticEval); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); return bestValue; - } +} - // value_to_tt() adjusts a mate or TB score from "plies to mate from the root" - // to "plies to mate from the current position". Standard scores are unchanged. - // The function is called before storing a value in the transposition table. +// value_to_tt() adjusts a mate or TB score from "plies to mate from the root" +// to "plies to mate from the current position". Standard scores are unchanged. +// The function is called before storing a value in the transposition table. - Value value_to_tt(Value v, int ply) { +Value value_to_tt(Value v, int ply) { assert(v != VALUE_NONE); - return v >= VALUE_TB_WIN_IN_MAX_PLY ? v + ply - : v <= VALUE_TB_LOSS_IN_MAX_PLY ? v - ply : v; - } + return v >= VALUE_TB_WIN_IN_MAX_PLY ? v + ply : v <= VALUE_TB_LOSS_IN_MAX_PLY ? v - ply : v; +} - // value_from_tt() is the inverse of value_to_tt(): it adjusts a mate or TB score - // from the transposition table (which refers to the plies to mate/be mated from - // current position) to "plies to mate/be mated (TB win/loss) from the root". - // However, to avoid potentially false mate scores related to the 50 moves rule - // and the graph history interaction problem, we return an optimal TB score instead. +// value_from_tt() is the inverse of value_to_tt(): it adjusts a mate or TB score +// from the transposition table (which refers to the plies to mate/be mated from +// current position) to "plies to mate/be mated (TB win/loss) from the root". +// However, to avoid potentially false mate scores related to the 50 moves rule +// and the graph history interaction problem, we return an optimal TB score instead. - Value value_from_tt(Value v, int ply, int r50c) { +Value value_from_tt(Value v, int ply, int r50c) { if (v == VALUE_NONE) return VALUE_NONE; @@ -1679,50 +1619,59 @@ namespace { if (v >= VALUE_TB_WIN_IN_MAX_PLY) // TB win or better { if (v >= VALUE_MATE_IN_MAX_PLY && VALUE_MATE - v > 99 - r50c) - return VALUE_MATE_IN_MAX_PLY - 1; // do not return a potentially false mate score + return VALUE_MATE_IN_MAX_PLY - 1; // do not return a potentially false mate score return v - ply; } - if (v <= VALUE_TB_LOSS_IN_MAX_PLY) // TB loss or worse + if (v <= VALUE_TB_LOSS_IN_MAX_PLY) // TB loss or worse { if (v <= VALUE_MATED_IN_MAX_PLY && VALUE_MATE + v > 99 - r50c) - return VALUE_MATED_IN_MAX_PLY + 1; // do not return a potentially false mate score + return VALUE_MATED_IN_MAX_PLY + 1; // do not return a potentially false mate score return v + ply; } return v; - } +} - // update_pv() adds current move and appends child pv[] +// update_pv() adds current move and appends child pv[] - void update_pv(Move* pv, Move move, const Move* childPv) { +void update_pv(Move* pv, Move move, const Move* childPv) { - for (*pv++ = move; childPv && *childPv != MOVE_NONE; ) + for (*pv++ = move; childPv && *childPv != MOVE_NONE;) *pv++ = *childPv++; *pv = MOVE_NONE; - } +} - // update_all_stats() updates stats at the end of search() when a bestMove is found +// update_all_stats() updates stats at the end of search() when a bestMove is found - void update_all_stats(const Position& pos, Stack* ss, Move bestMove, Value bestValue, Value beta, Square prevSq, - Move* quietsSearched, int quietCount, Move* capturesSearched, int captureCount, Depth depth) { +void update_all_stats(const Position& pos, + Stack* ss, + Move bestMove, + Value bestValue, + Value beta, + Square prevSq, + Move* quietsSearched, + int quietCount, + Move* capturesSearched, + int captureCount, + Depth depth) { - Color us = pos.side_to_move(); - Thread* thisThread = pos.this_thread(); + Color us = pos.side_to_move(); + Thread* thisThread = pos.this_thread(); CapturePieceToHistory& captureHistory = thisThread->captureHistory; - Piece moved_piece = pos.moved_piece(bestMove); - PieceType captured; + Piece moved_piece = pos.moved_piece(bestMove); + PieceType captured; int quietMoveBonus = stat_bonus(depth + 1); if (!pos.capture_stage(bestMove)) { - int bestMoveBonus = bestValue > beta + 168 ? quietMoveBonus // larger bonus - : stat_bonus(depth); // smaller bonus + int bestMoveBonus = bestValue > beta + 168 ? quietMoveBonus // larger bonus + : stat_bonus(depth); // smaller bonus // Increase stats for the best move in case it was a quiet move update_quiet_stats(pos, ss, bestMove, bestMoveBonus); @@ -1731,7 +1680,8 @@ namespace { for (int i = 0; i < quietCount; ++i) { thisThread->mainHistory[us][from_to(quietsSearched[i])] << -bestMoveBonus; - update_continuation_histories(ss, pos.moved_piece(quietsSearched[i]), to_sq(quietsSearched[i]), -bestMoveBonus); + update_continuation_histories(ss, pos.moved_piece(quietsSearched[i]), + to_sq(quietsSearched[i]), -bestMoveBonus); } } else @@ -1743,40 +1693,41 @@ namespace { // Extra penalty for a quiet early move that was not a TT move or // main killer move in previous ply when it gets refuted. - if ( prevSq != SQ_NONE - && ((ss-1)->moveCount == 1 + (ss-1)->ttHit || ((ss-1)->currentMove == (ss-1)->killers[0])) + if (prevSq != SQ_NONE + && ((ss - 1)->moveCount == 1 + (ss - 1)->ttHit + || ((ss - 1)->currentMove == (ss - 1)->killers[0])) && !pos.captured_piece()) - update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -quietMoveBonus); + update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, -quietMoveBonus); // Decrease stats for all non-best capture moves for (int i = 0; i < captureCount; ++i) { moved_piece = pos.moved_piece(capturesSearched[i]); - captured = type_of(pos.piece_on(to_sq(capturesSearched[i]))); + captured = type_of(pos.piece_on(to_sq(capturesSearched[i]))); captureHistory[moved_piece][to_sq(capturesSearched[i])][captured] << -quietMoveBonus; } - } +} - // update_continuation_histories() updates histories of the move pairs formed - // by moves at ply -1, -2, -4, and -6 with current move. +// update_continuation_histories() updates histories of the move pairs formed +// by moves at ply -1, -2, -4, and -6 with current move. - void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { +void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { for (int i : {1, 2, 3, 4, 6}) { // Only update the first 2 continuation histories if we are in check if (ss->inCheck && i > 2) break; - if (is_ok((ss-i)->currentMove)) - (*(ss-i)->continuationHistory)[pc][to] << bonus / (1 + 3 * (i == 3)); + if (is_ok((ss - i)->currentMove)) + (*(ss - i)->continuationHistory)[pc][to] << bonus / (1 + 3 * (i == 3)); } - } +} - // update_quiet_stats() updates move sorting heuristics +// update_quiet_stats() updates move sorting heuristics - void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus) { +void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus) { // Update killers if (ss->killers[0] != move) @@ -1785,31 +1736,31 @@ namespace { ss->killers[0] = move; } - Color us = pos.side_to_move(); + Color us = pos.side_to_move(); Thread* thisThread = pos.this_thread(); thisThread->mainHistory[us][from_to(move)] << bonus; update_continuation_histories(ss, pos.moved_piece(move), to_sq(move), bonus); // Update countermove history - if (is_ok((ss-1)->currentMove)) + if (is_ok((ss - 1)->currentMove)) { - Square prevSq = to_sq((ss-1)->currentMove); + Square prevSq = to_sq((ss - 1)->currentMove); thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] = move; } - } +} - // When playing with strength handicap, choose the best move among a set of RootMoves - // using a statistical rule dependent on 'level'. Idea by Heinz van Saanen. +// When playing with strength handicap, choose the best move among a set of RootMoves +// using a statistical rule dependent on 'level'. Idea by Heinz van Saanen. - Move Skill::pick_best(size_t multiPV) { +Move Skill::pick_best(size_t multiPV) { const RootMoves& rootMoves = Threads.main()->rootMoves; - static PRNG rng(now()); // PRNG sequence should be non-deterministic + static PRNG rng(now()); // PRNG sequence should be non-deterministic // RootMoves are already sorted by score in descending order - Value topScore = rootMoves[0].score; - int delta = std::min(topScore - rootMoves[multiPV - 1].score, PawnValue); - int maxScore = -VALUE_INFINITE; + Value topScore = rootMoves[0].score; + int delta = std::min(topScore - rootMoves[multiPV - 1].score, PawnValue); + int maxScore = -VALUE_INFINITE; double weakness = 120 - 2 * level; // Choose best move. For each move score we add two terms, both dependent on @@ -1818,20 +1769,21 @@ namespace { for (size_t i = 0; i < multiPV; ++i) { // This is our magic formula - int push = int(( weakness * int(topScore - rootMoves[i].score) - + delta * (rng.rand() % int(weakness))) / 128); + int push = int((weakness * int(topScore - rootMoves[i].score) + + delta * (rng.rand() % int(weakness))) + / 128); if (rootMoves[i].score + push >= maxScore) { maxScore = rootMoves[i].score + push; - best = rootMoves[i].pv[0]; + best = rootMoves[i].pv[0]; } } return best; - } +} -} // namespace +} // namespace // MainThread::check_time() is used to print debug info and, more importantly, @@ -1839,31 +1791,31 @@ namespace { void MainThread::check_time() { - if (--callsCnt > 0) - return; + if (--callsCnt > 0) + return; - // When using nodes, ensure checking rate is not lower than 0.1% of nodes - callsCnt = Limits.nodes ? std::min(512, int(Limits.nodes / 1024)) : 512; + // When using nodes, ensure checking rate is not lower than 0.1% of nodes + callsCnt = Limits.nodes ? std::min(512, int(Limits.nodes / 1024)) : 512; - static TimePoint lastInfoTime = now(); + static TimePoint lastInfoTime = now(); - TimePoint elapsed = Time.elapsed(); - TimePoint tick = Limits.startTime + elapsed; + TimePoint elapsed = Time.elapsed(); + TimePoint tick = Limits.startTime + elapsed; - if (tick - lastInfoTime >= 1000) - { - lastInfoTime = tick; - dbg_print(); - } + if (tick - lastInfoTime >= 1000) + { + lastInfoTime = tick; + dbg_print(); + } - // We should not stop pondering until told so by the GUI - if (ponder) - return; + // We should not stop pondering until told so by the GUI + if (ponder) + return; - if ( (Limits.use_time_management() && (elapsed > Time.maximum() || stopOnPonderhit)) - || (Limits.movetime && elapsed >= Limits.movetime) - || (Limits.nodes && Threads.nodes_searched() >= uint64_t(Limits.nodes))) - Threads.stop = true; + if ((Limits.use_time_management() && (elapsed > Time.maximum() || stopOnPonderhit)) + || (Limits.movetime && elapsed >= Limits.movetime) + || (Limits.nodes && Threads.nodes_searched() >= uint64_t(Limits.nodes))) + Threads.stop = true; } @@ -1872,57 +1824,53 @@ void MainThread::check_time() { string UCI::pv(const Position& pos, Depth depth) { - std::stringstream ss; - TimePoint elapsed = Time.elapsed() + 1; - const RootMoves& rootMoves = pos.this_thread()->rootMoves; - size_t pvIdx = pos.this_thread()->pvIdx; - size_t multiPV = std::min(size_t(Options["MultiPV"]), rootMoves.size()); - uint64_t nodesSearched = Threads.nodes_searched(); - uint64_t tbHits = Threads.tb_hits() + (TB::RootInTB ? rootMoves.size() : 0); + std::stringstream ss; + TimePoint elapsed = Time.elapsed() + 1; + const RootMoves& rootMoves = pos.this_thread()->rootMoves; + size_t pvIdx = pos.this_thread()->pvIdx; + size_t multiPV = std::min(size_t(Options["MultiPV"]), rootMoves.size()); + uint64_t nodesSearched = Threads.nodes_searched(); + uint64_t tbHits = Threads.tb_hits() + (TB::RootInTB ? rootMoves.size() : 0); - for (size_t i = 0; i < multiPV; ++i) - { - bool updated = rootMoves[i].score != -VALUE_INFINITE; + for (size_t i = 0; i < multiPV; ++i) + { + bool updated = rootMoves[i].score != -VALUE_INFINITE; - if (depth == 1 && !updated && i > 0) - continue; + if (depth == 1 && !updated && i > 0) + continue; - Depth d = updated ? depth : std::max(1, depth - 1); - Value v = updated ? rootMoves[i].uciScore : rootMoves[i].previousScore; + Depth d = updated ? depth : std::max(1, depth - 1); + Value v = updated ? rootMoves[i].uciScore : rootMoves[i].previousScore; - if (v == -VALUE_INFINITE) - v = VALUE_ZERO; + if (v == -VALUE_INFINITE) + v = VALUE_ZERO; - bool tb = TB::RootInTB && abs(v) < VALUE_MATE_IN_MAX_PLY; - v = tb ? rootMoves[i].tbScore : v; + bool tb = TB::RootInTB && abs(v) < VALUE_MATE_IN_MAX_PLY; + v = tb ? rootMoves[i].tbScore : v; - if (ss.rdbuf()->in_avail()) // Not at first line - ss << "\n"; + if (ss.rdbuf()->in_avail()) // Not at first line + ss << "\n"; - ss << "info" - << " depth " << d - << " seldepth " << rootMoves[i].selDepth - << " multipv " << i + 1 - << " score " << UCI::value(v); + ss << "info" + << " depth " << d << " seldepth " << rootMoves[i].selDepth << " multipv " << i + 1 + << " score " << UCI::value(v); - if (Options["UCI_ShowWDL"]) - ss << UCI::wdl(v, pos.game_ply()); + if (Options["UCI_ShowWDL"]) + ss << UCI::wdl(v, pos.game_ply()); - if (i == pvIdx && !tb && updated) // tablebase- and previous-scores are exact - ss << (rootMoves[i].scoreLowerbound ? " lowerbound" : (rootMoves[i].scoreUpperbound ? " upperbound" : "")); + if (i == pvIdx && !tb && updated) // tablebase- and previous-scores are exact + ss << (rootMoves[i].scoreLowerbound + ? " lowerbound" + : (rootMoves[i].scoreUpperbound ? " upperbound" : "")); - ss << " nodes " << nodesSearched - << " nps " << nodesSearched * 1000 / elapsed - << " hashfull " << TT.hashfull() - << " tbhits " << tbHits - << " time " << elapsed - << " pv"; + ss << " nodes " << nodesSearched << " nps " << nodesSearched * 1000 / elapsed + << " hashfull " << TT.hashfull() << " tbhits " << tbHits << " time " << elapsed << " pv"; - for (Move m : rootMoves[i].pv) - ss << " " << UCI::move(m, pos.is_chess960()); - } + for (Move m : rootMoves[i].pv) + ss << " " << UCI::move(m, pos.is_chess960()); + } - return ss.str(); + return ss.str(); } @@ -1948,7 +1896,7 @@ bool RootMove::extract_ponder_from_tt(Position& pos) { if (ttHit) { - Move m = tte->move(); // Local copy to be SMP safe + Move m = tte->move(); // Local copy to be SMP safe if (MoveList(pos).contains(m)) pv.push_back(m); } @@ -1959,10 +1907,10 @@ bool RootMove::extract_ponder_from_tt(Position& pos) { void Tablebases::rank_root_moves(Position& pos, Search::RootMoves& rootMoves) { - RootInTB = false; - UseRule50 = bool(Options["Syzygy50MoveRule"]); - ProbeDepth = int(Options["SyzygyProbeDepth"]); - Cardinality = int(Options["SyzygyProbeLimit"]); + RootInTB = false; + UseRule50 = bool(Options["Syzygy50MoveRule"]); + ProbeDepth = int(Options["SyzygyProbeDepth"]); + Cardinality = int(Options["SyzygyProbeLimit"]); bool dtz_available = true; // Tables with fewer pieces than SyzygyProbeLimit are searched with @@ -1970,7 +1918,7 @@ void Tablebases::rank_root_moves(Position& pos, Search::RootMoves& rootMoves) { if (Cardinality > MaxCardinality) { Cardinality = MaxCardinality; - ProbeDepth = 0; + ProbeDepth = 0; } if (Cardinality >= popcount(pos.pieces()) && !pos.can_castle(ANY_CASTLING)) @@ -1982,7 +1930,7 @@ void Tablebases::rank_root_moves(Position& pos, Search::RootMoves& rootMoves) { { // DTZ tables are missing; try to rank moves using WDL tables dtz_available = false; - RootInTB = root_probe_wdl(pos, rootMoves); + RootInTB = root_probe_wdl(pos, rootMoves); } } @@ -1990,7 +1938,7 @@ void Tablebases::rank_root_moves(Position& pos, Search::RootMoves& rootMoves) { { // Sort moves according to TB rank std::stable_sort(rootMoves.begin(), rootMoves.end(), - [](const RootMove &a, const RootMove &b) { return a.tbRank > b.tbRank; } ); + [](const RootMove& a, const RootMove& b) { return a.tbRank > b.tbRank; }); // Probe during search only if DTZ is not available and we are winning if (dtz_available || rootMoves[0].tbScore <= VALUE_DRAW) @@ -2004,4 +1952,4 @@ void Tablebases::rank_root_moves(Position& pos, Search::RootMoves& rootMoves) { } } -} // namespace Stockfish +} // namespace Stockfish diff --git a/src/search.h b/src/search.h index c434ba752d6..37cd5e5a686 100644 --- a/src/search.h +++ b/src/search.h @@ -38,20 +38,20 @@ namespace Search { // its own array of Stack objects, indexed by the current ply. struct Stack { - Move* pv; - PieceToHistory* continuationHistory; - int ply; - Move currentMove; - Move excludedMove; - Move killers[2]; - Value staticEval; - int statScore; - int moveCount; - bool inCheck; - bool ttPv; - bool ttHit; - int doubleExtensions; - int cutoffCnt; + Move* pv; + PieceToHistory* continuationHistory; + int ply; + Move currentMove; + Move excludedMove; + Move killers[2]; + Value staticEval; + int statScore; + int moveCount; + bool inCheck; + bool ttPv; + bool ttHit; + int doubleExtensions; + int cutoffCnt; }; @@ -61,24 +61,24 @@ struct Stack { struct RootMove { - explicit RootMove(Move m) : pv(1, m) {} - bool extract_ponder_from_tt(Position& pos); - bool operator==(const Move& m) const { return pv[0] == m; } - bool operator<(const RootMove& m) const { // Sort in descending order - return m.score != score ? m.score < score - : m.previousScore < previousScore; - } - - Value score = -VALUE_INFINITE; - Value previousScore = -VALUE_INFINITE; - Value averageScore = -VALUE_INFINITE; - Value uciScore = -VALUE_INFINITE; - bool scoreLowerbound = false; - bool scoreUpperbound = false; - int selDepth = 0; - int tbRank = 0; - Value tbScore; - std::vector pv; + explicit RootMove(Move m) : + pv(1, m) {} + bool extract_ponder_from_tt(Position& pos); + bool operator==(const Move& m) const { return pv[0] == m; } + bool operator<(const RootMove& m) const { // Sort in descending order + return m.score != score ? m.score < score : m.previousScore < previousScore; + } + + Value score = -VALUE_INFINITE; + Value previousScore = -VALUE_INFINITE; + Value averageScore = -VALUE_INFINITE; + Value uciScore = -VALUE_INFINITE; + bool scoreLowerbound = false; + bool scoreUpperbound = false; + int selDepth = 0; + int tbRank = 0; + Value tbScore; + std::vector pv; }; using RootMoves = std::vector; @@ -89,20 +89,18 @@ using RootMoves = std::vector; struct LimitsType { - LimitsType() { // Init explicitly due to broken value-initialization of non POD in MSVC - time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = npmsec = movetime = TimePoint(0); - movestogo = depth = mate = perft = infinite = 0; - nodes = 0; - } + LimitsType() { // Init explicitly due to broken value-initialization of non POD in MSVC + time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = npmsec = movetime = TimePoint(0); + movestogo = depth = mate = perft = infinite = 0; + nodes = 0; + } - bool use_time_management() const { - return time[WHITE] || time[BLACK]; - } + bool use_time_management() const { return time[WHITE] || time[BLACK]; } - std::vector searchmoves; - TimePoint time[COLOR_NB], inc[COLOR_NB], npmsec, movetime, startTime; - int movestogo, depth, mate, perft, infinite; - int64_t nodes; + std::vector searchmoves; + TimePoint time[COLOR_NB], inc[COLOR_NB], npmsec, movetime, startTime; + int movestogo, depth, mate, perft, infinite; + int64_t nodes; }; extern LimitsType Limits; @@ -110,8 +108,8 @@ extern LimitsType Limits; void init(); void clear(); -} // namespace Search +} // namespace Search -} // namespace Stockfish +} // namespace Stockfish -#endif // #ifndef SEARCH_H_INCLUDED +#endif // #ifndef SEARCH_H_INCLUDED diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 4114db605d2..c8e60ab6c02 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -45,15 +45,15 @@ #include "../uci.h" #ifndef _WIN32 -#include -#include -#include + #include + #include + #include #else -#define WIN32_LEAN_AND_MEAN -#ifndef NOMINMAX -# define NOMINMAX // Disable macros min() and max() -#endif -#include + #define WIN32_LEAN_AND_MEAN + #ifndef NOMINMAX + #define NOMINMAX // Disable macros min() and max() + #endif + #include #endif using namespace Stockfish::Tablebases; @@ -64,60 +64,69 @@ namespace Stockfish { namespace { -constexpr int TBPIECES = 7; // Max number of supported pieces -constexpr int MAX_DTZ = 1 << 18; // Max DTZ supported, large enough to deal with the syzygy TB limit. +constexpr int TBPIECES = 7; // Max number of supported pieces +constexpr int MAX_DTZ = + 1 << 18; // Max DTZ supported, large enough to deal with the syzygy TB limit. -enum { BigEndian, LittleEndian }; -enum TBType { WDL, DTZ }; // Used as template parameter +enum { + BigEndian, + LittleEndian +}; +enum TBType { + WDL, + DTZ +}; // Used as template parameter // Each table has a set of flags: all of them refer to DTZ tables, the last one to WDL tables -enum TBFlag { STM = 1, Mapped = 2, WinPlies = 4, LossPlies = 8, Wide = 16, SingleValue = 128 }; +enum TBFlag { + STM = 1, + Mapped = 2, + WinPlies = 4, + LossPlies = 8, + Wide = 16, + SingleValue = 128 +}; inline WDLScore operator-(WDLScore d) { return WDLScore(-int(d)); } -inline Square operator^(Square s, int i) { return Square(int(s) ^ i); } +inline Square operator^(Square s, int i) { return Square(int(s) ^ i); } constexpr std::string_view PieceToChar = " PNBRQK pnbrqk"; int MapPawns[SQUARE_NB]; int MapB1H1H7[SQUARE_NB]; int MapA1D1D4[SQUARE_NB]; -int MapKK[10][SQUARE_NB]; // [MapA1D1D4][SQUARE_NB] +int MapKK[10][SQUARE_NB]; // [MapA1D1D4][SQUARE_NB] -int Binomial[6][SQUARE_NB]; // [k][n] k elements from a set of n elements -int LeadPawnIdx[6][SQUARE_NB]; // [leadPawnsCnt][SQUARE_NB] -int LeadPawnsSize[6][4]; // [leadPawnsCnt][FILE_A..FILE_D] +int Binomial[6][SQUARE_NB]; // [k][n] k elements from a set of n elements +int LeadPawnIdx[6][SQUARE_NB]; // [leadPawnsCnt][SQUARE_NB] +int LeadPawnsSize[6][4]; // [leadPawnsCnt][FILE_A..FILE_D] // Comparison function to sort leading pawns in ascending MapPawns[] order bool pawns_comp(Square i, Square j) { return MapPawns[i] < MapPawns[j]; } -int off_A1H8(Square sq) { return int(rank_of(sq)) - file_of(sq); } - -constexpr Value WDL_to_value[] = { - -VALUE_MATE + MAX_PLY + 1, - VALUE_DRAW - 2, - VALUE_DRAW, - VALUE_DRAW + 2, - VALUE_MATE - MAX_PLY - 1 -}; +int off_A1H8(Square sq) { return int(rank_of(sq)) - file_of(sq); } + +constexpr Value WDL_to_value[] = {-VALUE_MATE + MAX_PLY + 1, VALUE_DRAW - 2, VALUE_DRAW, + VALUE_DRAW + 2, VALUE_MATE - MAX_PLY - 1}; template -inline void swap_endian(T& x) -{ +inline void swap_endian(T& x) { static_assert(std::is_unsigned_v, "Argument of swap_endian not unsigned"); - uint8_t tmp, *c = (uint8_t*)&x; + uint8_t tmp, *c = (uint8_t*) &x; for (int i = 0; i < Half; ++i) tmp = c[i], c[i] = c[End - i], c[End - i] = tmp; } -template<> inline void swap_endian(uint8_t&) {} +template<> +inline void swap_endian(uint8_t&) {} -template T number(void* addr) -{ +template +T number(void* addr) { T v; - if (uintptr_t(addr) & (alignof(T) - 1)) // Unaligned pointer (very rare) + if (uintptr_t(addr) & (alignof(T) - 1)) // Unaligned pointer (very rare) std::memcpy(&v, addr, sizeof(T)); else - v = *((T*)addr); + v = *((T*) addr); if (LE != IsLittleEndian) swap_endian(v); @@ -128,14 +137,16 @@ template T number(void* addr) // like captures and pawn moves but we can easily recover the correct dtz of the // previous move if we know the position's WDL score. int dtz_before_zeroing(WDLScore wdl) { - return wdl == WDLWin ? 1 : - wdl == WDLCursedWin ? 101 : - wdl == WDLBlessedLoss ? -101 : - wdl == WDLLoss ? -1 : 0; + return wdl == WDLWin ? 1 + : wdl == WDLCursedWin ? 101 + : wdl == WDLBlessedLoss ? -101 + : wdl == WDLLoss ? -1 + : 0; } // Return the sign of a number (-1, 0, 1) -template int sign_of(T val) { +template +int sign_of(T val) { return (T(0) < val) - (val < T(0)); } @@ -147,18 +158,22 @@ struct SparseEntry { static_assert(sizeof(SparseEntry) == 6, "SparseEntry must be 6 bytes"); -using Sym = uint16_t; // Huffman symbol +using Sym = uint16_t; // Huffman symbol struct LR { - enum Side { Left, Right }; + enum Side { + Left, + Right + }; - uint8_t lr[3]; // The first 12 bits is the left-hand symbol, the second 12 - // bits is the right-hand symbol. If the symbol has length 1, - // then the left-hand symbol is the stored value. + uint8_t lr[3]; // The first 12 bits is the left-hand symbol, the second 12 + // bits is the right-hand symbol. If the symbol has length 1, + // then the left-hand symbol is the stored value. template Sym get() { - return S == Left ? ((lr[1] & 0xF) << 8) | lr[0] : - S == Right ? (lr[2] << 4) | (lr[1] >> 4) : (assert(false), Sym(-1)); + return S == Left ? ((lr[1] & 0xF) << 8) | lr[0] + : S == Right ? (lr[2] << 4) | (lr[1] >> 4) + : (assert(false), Sym(-1)); } }; @@ -173,11 +188,11 @@ static_assert(sizeof(LR) == 3, "LR tree entry must be 3 bytes"); // class TBFile memory maps/unmaps the single .rtbw and .rtbz files. Files are // memory mapped for best performance. Files are mapped at first access: at init // time only existence of the file is checked. -class TBFile : public std::ifstream { +class TBFile: public std::ifstream { std::string fname; -public: + public: // Look for and open the file among the Paths directories where the .rtbw // and .rtbz files can be found. Multiple directories are separated by ";" // on Windows and by ":" on Unix-based operating systems. @@ -194,7 +209,7 @@ class TBFile : public std::ifstream { constexpr char SepChar = ';'; #endif std::stringstream ss(Paths); - std::string path; + std::string path; while (std::getline(ss, path, SepChar)) { @@ -208,11 +223,11 @@ class TBFile : public std::ifstream { // Memory map the file and check it. uint8_t* map(void** baseAddress, uint64_t* mapping, TBType type) { if (is_open()) - close(); // Need to re-open to get native file descriptor + close(); // Need to re-open to get native file descriptor #ifndef _WIN32 struct stat statbuf; - int fd = ::open(fname.c_str(), O_RDONLY); + int fd = ::open(fname.c_str(), O_RDONLY); if (fd == -1) return *baseAddress = nullptr, nullptr; @@ -225,11 +240,11 @@ class TBFile : public std::ifstream { exit(EXIT_FAILURE); } - *mapping = statbuf.st_size; + *mapping = statbuf.st_size; *baseAddress = mmap(nullptr, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0); -#if defined(MADV_RANDOM) + #if defined(MADV_RANDOM) madvise(*baseAddress, statbuf.st_size, MADV_RANDOM); -#endif + #endif ::close(fd); if (*baseAddress == MAP_FAILED) @@ -240,7 +255,7 @@ class TBFile : public std::ifstream { #else // Note FILE_FLAG_RANDOM_ACCESS is only a hint to Windows and as such may get ignored. HANDLE fd = CreateFileA(fname.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, - OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, nullptr); + OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, nullptr); if (fd == INVALID_HANDLE_VALUE) return *baseAddress = nullptr, nullptr; @@ -263,7 +278,7 @@ class TBFile : public std::ifstream { exit(EXIT_FAILURE); } - *mapping = uint64_t(mmap); + *mapping = uint64_t(mmap); *baseAddress = MapViewOfFile(mmap, FILE_MAP_READ, 0, 0, 0); if (!*baseAddress) @@ -273,10 +288,9 @@ class TBFile : public std::ifstream { exit(EXIT_FAILURE); } #endif - uint8_t* data = (uint8_t*)*baseAddress; + uint8_t* data = (uint8_t*) *baseAddress; - constexpr uint8_t Magics[][4] = { { 0xD7, 0x66, 0x0C, 0xA5 }, - { 0x71, 0xE8, 0x23, 0x5D } }; + constexpr uint8_t Magics[][4] = {{0xD7, 0x66, 0x0C, 0xA5}, {0x71, 0xE8, 0x23, 0x5D}}; if (memcmp(data, Magics[type == WDL], 4)) { @@ -285,7 +299,7 @@ class TBFile : public std::ifstream { return *baseAddress = nullptr, nullptr; } - return data + 4; // Skip Magics's header + return data + 4; // Skip Magics's header } static void unmap(void* baseAddress, uint64_t mapping) { @@ -294,7 +308,7 @@ class TBFile : public std::ifstream { munmap(baseAddress, mapping); #else UnmapViewOfFile(baseAddress); - CloseHandle((HANDLE)mapping); + CloseHandle((HANDLE) mapping); #endif } }; @@ -305,25 +319,27 @@ std::string TBFile::Paths; // There are 8, 4, or 2 PairsData records for each TBTable, according to the type // of table and if positions have pawns or not. It is populated at first access. struct PairsData { - uint8_t flags; // Table flags, see enum TBFlag - uint8_t maxSymLen; // Maximum length in bits of the Huffman symbols - uint8_t minSymLen; // Minimum length in bits of the Huffman symbols - uint32_t blocksNum; // Number of blocks in the TB file - size_t sizeofBlock; // Block size in bytes - size_t span; // About every span values there is a SparseIndex[] entry - Sym* lowestSym; // lowestSym[l] is the symbol of length l with the lowest value - LR* btree; // btree[sym] stores the left and right symbols that expand sym - uint16_t* blockLength; // Number of stored positions (minus one) for each block: 1..65536 - uint32_t blockLengthSize; // Size of blockLength[] table: padded so it's bigger than blocksNum - SparseEntry* sparseIndex; // Partial indices into blockLength[] - size_t sparseIndexSize; // Size of SparseIndex[] table - uint8_t* data; // Start of Huffman compressed data - std::vector base64; // base64[l - min_sym_len] is the 64bit-padded lowest symbol of length l - std::vector symlen; // Number of values (-1) represented by a given Huffman symbol: 1..256 - Piece pieces[TBPIECES]; // Position pieces: the order of pieces defines the groups - uint64_t groupIdx[TBPIECES+1]; // Start index used for the encoding of the group's pieces - int groupLen[TBPIECES+1]; // Number of pieces in a given group: KRKN -> (3, 1) - uint16_t map_idx[4]; // WDLWin, WDLLoss, WDLCursedWin, WDLBlessedLoss (used in DTZ) + uint8_t flags; // Table flags, see enum TBFlag + uint8_t maxSymLen; // Maximum length in bits of the Huffman symbols + uint8_t minSymLen; // Minimum length in bits of the Huffman symbols + uint32_t blocksNum; // Number of blocks in the TB file + size_t sizeofBlock; // Block size in bytes + size_t span; // About every span values there is a SparseIndex[] entry + Sym* lowestSym; // lowestSym[l] is the symbol of length l with the lowest value + LR* btree; // btree[sym] stores the left and right symbols that expand sym + uint16_t* blockLength; // Number of stored positions (minus one) for each block: 1..65536 + uint32_t blockLengthSize; // Size of blockLength[] table: padded so it's bigger than blocksNum + SparseEntry* sparseIndex; // Partial indices into blockLength[] + size_t sparseIndexSize; // Size of SparseIndex[] table + uint8_t* data; // Start of Huffman compressed data + std::vector + base64; // base64[l - min_sym_len] is the 64bit-padded lowest symbol of length l + std::vector + symlen; // Number of values (-1) represented by a given Huffman symbol: 1..256 + Piece pieces[TBPIECES]; // Position pieces: the order of pieces defines the groups + uint64_t groupIdx[TBPIECES + 1]; // Start index used for the encoding of the group's pieces + int groupLen[TBPIECES + 1]; // Number of pieces in a given group: KRKN -> (3, 1) + uint16_t map_idx[4]; // WDLWin, WDLLoss, WDLCursedWin, WDLBlessedLoss (used in DTZ) }; // struct TBTable contains indexing information to access the corresponding TBFile. @@ -337,22 +353,22 @@ struct TBTable { static constexpr int Sides = Type == WDL ? 2 : 1; std::atomic_bool ready; - void* baseAddress; - uint8_t* map; - uint64_t mapping; - Key key; - Key key2; - int pieceCount; - bool hasPawns; - bool hasUniquePieces; - uint8_t pawnCount[2]; // [Lead color / other color] - PairsData items[Sides][4]; // [wtm / btm][FILE_A..FILE_D or 0] - - PairsData* get(int stm, int f) { - return &items[stm % Sides][hasPawns ? f : 0]; - } - - TBTable() : ready(false), baseAddress(nullptr) {} + void* baseAddress; + uint8_t* map; + uint64_t mapping; + Key key; + Key key2; + int pieceCount; + bool hasPawns; + bool hasUniquePieces; + uint8_t pawnCount[2]; // [Lead color / other color] + PairsData items[Sides][4]; // [wtm / btm][FILE_A..FILE_D or 0] + + PairsData* get(int stm, int f) { return &items[stm % Sides][hasPawns ? f : 0]; } + + TBTable() : + ready(false), + baseAddress(nullptr) {} explicit TBTable(const std::string& code); explicit TBTable(const TBTable& wdl); @@ -363,26 +379,26 @@ struct TBTable { }; template<> -TBTable::TBTable(const std::string& code) : TBTable() { +TBTable::TBTable(const std::string& code) : + TBTable() { StateInfo st; - Position pos; + Position pos; - key = pos.set(code, WHITE, &st).material_key(); + key = pos.set(code, WHITE, &st).material_key(); pieceCount = pos.count(); - hasPawns = pos.pieces(PAWN); + 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; // Set the leading color. In case both sides have pawns the leading color // is the side with fewer pawns because this leads to better compression. - bool c = !pos.count(BLACK) - || ( pos.count(WHITE) - && pos.count(BLACK) >= pos.count(WHITE)); + bool c = !pos.count(BLACK) + || (pos.count(WHITE) && pos.count(BLACK) >= pos.count(WHITE)); pawnCount[0] = pos.count(c ? WHITE : BLACK); pawnCount[1] = pos.count(c ? BLACK : WHITE); @@ -391,16 +407,17 @@ TBTable::TBTable(const std::string& code) : TBTable() { } template<> -TBTable::TBTable(const TBTable& wdl) : TBTable() { +TBTable::TBTable(const TBTable& wdl) : + TBTable() { // Use the corresponding WDL table to avoid recalculating all from scratch - key = wdl.key; - key2 = wdl.key2; - pieceCount = wdl.pieceCount; - hasPawns = wdl.hasPawns; + key = wdl.key; + key2 = wdl.key2; + pieceCount = wdl.pieceCount; + hasPawns = wdl.hasPawns; hasUniquePieces = wdl.hasUniquePieces; - pawnCount[0] = wdl.pawnCount[0]; - pawnCount[1] = wdl.pawnCount[1]; + pawnCount[0] = wdl.pawnCount[0]; + pawnCount[1] = wdl.pawnCount[1]; } // class TBTables creates and keeps ownership of the TBTable objects, one for @@ -408,19 +425,18 @@ TBTable::TBTable(const TBTable& wdl) : TBTable() { // at init time, accessed at probe time. class TBTables { - struct Entry - { - Key key; + struct Entry { + Key key; TBTable* wdl; TBTable* dtz; - template + template TBTable* get() const { - return (TBTable*)(Type == WDL ? (void*)wdl : (void*)dtz); + return (TBTable*) (Type == WDL ? (void*) wdl : (void*) dtz); } }; - static constexpr int Size = 1 << 12; // 4K table, indexed by key's 12 lsb + static constexpr int Size = 1 << 12; // 4K table, indexed by key's 12 lsb static constexpr int Overflow = 1; // Number of elements allowed to map to the last bucket Entry hashTable[Size + Overflow]; @@ -430,12 +446,14 @@ class TBTables { void insert(Key key, TBTable* wdl, TBTable* dtz) { uint32_t homeBucket = uint32_t(key) & (Size - 1); - Entry entry{ key, wdl, dtz }; + Entry entry{key, wdl, dtz}; // Ensure last element is empty to avoid overflow when looking up - for (uint32_t bucket = homeBucket; bucket < Size + Overflow - 1; ++bucket) { + for (uint32_t bucket = homeBucket; bucket < Size + Overflow - 1; ++bucket) + { Key otherKey = hashTable[bucket].key; - if (otherKey == key || !hashTable[bucket].get()) { + if (otherKey == key || !hashTable[bucket].get()) + { hashTable[bucket] = entry; return; } @@ -443,9 +461,10 @@ class TBTables { // Robin Hood hashing: If we've probed for longer than this element, // insert here and search for a new spot for the other element instead. uint32_t otherHomeBucket = uint32_t(otherKey) & (Size - 1); - if (otherHomeBucket > homeBucket) { + if (otherHomeBucket > homeBucket) + { std::swap(entry, hashTable[bucket]); - key = otherKey; + key = otherKey; homeBucket = otherHomeBucket; } } @@ -453,10 +472,11 @@ class TBTables { exit(EXIT_FAILURE); } -public: + public: template TBTable* get(Key key) { - for (const Entry* entry = &hashTable[uint32_t(key) & (Size - 1)]; ; ++entry) { + for (const Entry* entry = &hashTable[uint32_t(key) & (Size - 1)];; ++entry) + { if (entry->key == key || !entry->get()) return entry->get(); } @@ -468,7 +488,7 @@ class TBTables { dtzTable.clear(); } size_t size() const { return wdlTable.size(); } - void add(const std::vector& pieces); + void add(const std::vector& pieces); }; TBTables TBTables; @@ -482,9 +502,9 @@ void TBTables::add(const std::vector& pieces) { for (PieceType pt : pieces) code += PieceToChar[pt]; - TBFile file(code.insert(code.find('K', 1), "v") + ".rtbw"); // KRK -> KRvK + TBFile file(code.insert(code.find('K', 1), "v") + ".rtbw"); // KRK -> KRvK - if (!file.is_open()) // Only WDL file is checked + if (!file.is_open()) // Only WDL file is checked return; file.close(); @@ -495,7 +515,7 @@ void TBTables::add(const std::vector& pieces) { dtzTable.emplace_back(wdlTable.back()); // Insert into the hash keys for both colors: KRvK with KR white and black - insert(wdlTable.back().key , &wdlTable.back(), &dtzTable.back()); + insert(wdlTable.back().key, &wdlTable.back(), &dtzTable.back()); insert(wdlTable.back().key2, &wdlTable.back(), &dtzTable.back()); } @@ -538,8 +558,8 @@ int decompress_pairs(PairsData* d, uint64_t idx) { uint32_t k = uint32_t(idx / d->span); // Then we read the corresponding SparseIndex[] entry - uint32_t block = number(&d->sparseIndex[k].block); - int offset = number(&d->sparseIndex[k].offset); + uint32_t block = number(&d->sparseIndex[k].block); + int offset = number(&d->sparseIndex[k].offset); // Now compute the difference idx - I(k). From the definition of k, we know that // @@ -560,18 +580,19 @@ int decompress_pairs(PairsData* d, uint64_t idx) { offset -= d->blockLength[block++] + 1; // Finally, we find the start address of our block of canonical Huffman symbols - uint32_t* ptr = (uint32_t*)(d->data + (uint64_t(block) * d->sizeofBlock)); + uint32_t* ptr = (uint32_t*) (d->data + (uint64_t(block) * d->sizeofBlock)); // Read the first 64 bits in our block, this is a (truncated) sequence of // unknown number of symbols of unknown length but we know the first one // is at the beginning of this 64-bit sequence. - uint64_t buf64 = number(ptr); ptr += 2; + uint64_t buf64 = number(ptr); + ptr += 2; int buf64Size = 64; Sym sym; while (true) { - int len = 0; // This is the symbol length - d->min_sym_len + int len = 0; // This is the symbol length - d->min_sym_len // Now get the symbol length. For any symbol s64 of length l right-padded // to 64 bits we know that d->base64[l-1] >= s64 >= d->base64[l] so we @@ -594,11 +615,12 @@ int decompress_pairs(PairsData* d, uint64_t idx) { // ...otherwise update the offset and continue to iterate offset -= d->symlen[sym] + 1; - len += d->minSymLen; // Get the real length - buf64 <<= len; // Consume the just processed symbol + len += d->minSymLen; // Get the real length + buf64 <<= len; // Consume the just processed symbol buf64Size -= len; - if (buf64Size <= 32) { // Refill the buffer + if (buf64Size <= 32) + { // Refill the buffer buf64Size += 32; buf64 |= uint64_t(number(ptr++)) << (64 - buf64Size); } @@ -618,7 +640,8 @@ int decompress_pairs(PairsData* d, uint64_t idx) { // the left side because in Recursive Pairing child symbols are adjacent. if (offset < d->symlen[left] + 1) sym = left; - else { + else + { offset -= d->symlen[left] + 1; sym = d->btree[sym].get(); } @@ -632,8 +655,7 @@ bool check_dtz_stm(TBTable*, int, File) { return true; } bool check_dtz_stm(TBTable* entry, int stm, File f) { auto flags = entry->get(stm, f)->flags; - return (flags & TBFlag::STM) == stm - || ((entry->key == entry->key2) && !entry->hasPawns); + return (flags & TBFlag::STM) == stm || ((entry->key == entry->key2) && !entry->hasPawns); } // DTZ scores are sorted by frequency of occurrence and then assigned the @@ -644,25 +666,25 @@ WDLScore map_score(TBTable*, File, int value, WDLScore) { return WDLScore(v int map_score(TBTable* entry, File f, int value, WDLScore wdl) { - constexpr int WDLMap[] = { 1, 3, 0, 2, 0 }; + constexpr int WDLMap[] = {1, 3, 0, 2, 0}; auto flags = entry->get(0, f)->flags; - uint8_t* map = entry->map; + uint8_t* map = entry->map; uint16_t* idx = entry->get(0, f)->map_idx; - if (flags & TBFlag::Mapped) { + if (flags & TBFlag::Mapped) + { if (flags & TBFlag::Wide) - value = ((uint16_t *)map)[idx[WDLMap[wdl + 2]] + value]; + value = ((uint16_t*) map)[idx[WDLMap[wdl + 2]] + value]; else value = map[idx[WDLMap[wdl + 2]] + value]; } // DTZ tables store distance to zero in number of moves or plies. We // want to return plies, so we have to convert to plies when needed. - if ( (wdl == WDLWin && !(flags & TBFlag::WinPlies)) - || (wdl == WDLLoss && !(flags & TBFlag::LossPlies)) - || wdl == WDLCursedWin - || wdl == WDLBlessedLoss) + if ((wdl == WDLWin && !(flags & TBFlag::WinPlies)) + || (wdl == WDLLoss && !(flags & TBFlag::LossPlies)) || wdl == WDLCursedWin + || wdl == WDLBlessedLoss) value *= 2; return value + 1; @@ -677,13 +699,13 @@ int map_score(TBTable* entry, File f, int value, WDLScore wdl) { template Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* result) { - Square squares[TBPIECES]; - Piece pieces[TBPIECES]; - uint64_t idx; - int next = 0, size = 0, leadPawnsCnt = 0; + Square squares[TBPIECES]; + Piece pieces[TBPIECES]; + uint64_t idx; + int next = 0, size = 0, leadPawnsCnt = 0; PairsData* d; - Bitboard b, leadPawns = 0; - File tbFile = FILE_A; + Bitboard b, leadPawns = 0; + File tbFile = FILE_A; // A given TB entry like KRK has associated two material keys: KRvk and Kvkr. // If both sides have the same pieces keys are equal. In this case TB tables @@ -704,7 +726,8 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu // For pawns, TB files store 4 separate tables according if leading pawn is on // file a, b, c or d after reordering. The leading pawn is the one with maximum // MapPawns[] value, that is the one most toward the edges and with lowest rank. - if (entry->hasPawns) { + if (entry->hasPawns) + { // In all the 4 tables, pawns are at the beginning of the piece sequence and // their color is the reference one. So we just pick the first one. @@ -733,9 +756,10 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu // Now we are ready to get all the position pieces (but the lead pawns) and // directly map them to the correct color and square. b = pos.pieces() ^ leadPawns; - do { - Square s = pop_lsb(b); - squares[size] = s ^ flipSquares; + do + { + Square s = pop_lsb(b); + squares[size] = s ^ flipSquares; pieces[size++] = Piece(pos.piece_on(s) ^ flipColor); } while (b); @@ -762,7 +786,8 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu // Encode leading pawns starting with the one with minimum MapPawns[] and // proceeding in ascending order. - if (entry->hasPawns) { + if (entry->hasPawns) + { idx = LeadPawnIdx[leadPawnsCnt][squares[0]]; std::stable_sort(squares + 1, squares + leadPawnsCnt, pawns_comp); @@ -770,7 +795,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu for (int i = 1; i < leadPawnsCnt; ++i) idx += Binomial[i][MapPawns[squares[i]]]; - goto encode_remaining; // With pawns we have finished special treatments + goto encode_remaining; // With pawns we have finished special treatments } // In positions without pawns, we further flip the squares to ensure leading @@ -781,11 +806,12 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu // Look for the first piece of the leading group not on the A1-D4 diagonal // and ensure it is mapped below the diagonal. - for (int i = 0; i < d->groupLen[0]; ++i) { + for (int i = 0; i < d->groupLen[0]; ++i) + { if (!off_A1H8(squares[i])) continue; - if (off_A1H8(squares[i]) > 0) // A1-H8 diagonal flip: SQ_A3 -> SQ_C1 + if (off_A1H8(squares[i]) > 0) // A1-H8 diagonal flip: SQ_A3 -> SQ_C1 for (int j = i; j < size; ++j) squares[j] = Square(((squares[j] >> 3) | (squares[j] << 3)) & 63); break; @@ -818,41 +844,36 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu // // In case we have at least 3 unique pieces (including kings) we encode them // together. - if (entry->hasUniquePieces) { + if (entry->hasUniquePieces) + { - int adjust1 = squares[1] > squares[0]; + int adjust1 = squares[1] > squares[0]; int adjust2 = (squares[2] > squares[0]) + (squares[2] > squares[1]); // First piece is below a1-h8 diagonal. MapA1D1D4[] maps the b1-d1-d3 // triangle to 0...5. There are 63 squares for second piece and and 62 // (mapped to 0...61) for the third. if (off_A1H8(squares[0])) - idx = ( MapA1D1D4[squares[0]] * 63 - + (squares[1] - adjust1)) * 62 - + squares[2] - adjust2; + idx = (MapA1D1D4[squares[0]] * 63 + (squares[1] - adjust1)) * 62 + squares[2] - adjust2; // First piece is on a1-h8 diagonal, second below: map this occurrence to // 6 to differentiate from the above case, rank_of() maps a1-d4 diagonal // to 0...3 and finally MapB1H1H7[] maps the b1-h1-h7 triangle to 0..27. else if (off_A1H8(squares[1])) - idx = ( 6 * 63 + rank_of(squares[0]) * 28 - + MapB1H1H7[squares[1]]) * 62 - + squares[2] - adjust2; + idx = (6 * 63 + rank_of(squares[0]) * 28 + MapB1H1H7[squares[1]]) * 62 + squares[2] + - adjust2; // First two pieces are on a1-h8 diagonal, third below else if (off_A1H8(squares[2])) - idx = 6 * 63 * 62 + 4 * 28 * 62 - + rank_of(squares[0]) * 7 * 28 - + (rank_of(squares[1]) - adjust1) * 28 - + MapB1H1H7[squares[2]]; + idx = 6 * 63 * 62 + 4 * 28 * 62 + rank_of(squares[0]) * 7 * 28 + + (rank_of(squares[1]) - adjust1) * 28 + MapB1H1H7[squares[2]]; // All 3 pieces on the diagonal a1-h8 else - idx = 6 * 63 * 62 + 4 * 28 * 62 + 4 * 7 * 28 - + rank_of(squares[0]) * 7 * 6 - + (rank_of(squares[1]) - adjust1) * 6 - + (rank_of(squares[2]) - adjust2); - } else + idx = 6 * 63 * 62 + 4 * 28 * 62 + 4 * 7 * 28 + rank_of(squares[0]) * 7 * 6 + + (rank_of(squares[1]) - adjust1) * 6 + (rank_of(squares[2]) - adjust2); + } + else // We don't have at least 3 unique pieces, like in KRRvKBB, just map // the kings. idx = MapKK[MapA1D1D4[squares[0]]][squares[1]]; @@ -873,7 +894,7 @@ Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* resu // groups (similar to what was done earlier for leading group pieces). for (int i = 0; i < d->groupLen[next]; ++i) { - auto f = [&](Square s) { return groupSq[i] > s; }; + auto f = [&](Square s) { return groupSq[i] > s; }; auto adjust = std::count_if(squares, groupSq, f); n += Binomial[i + 1][groupSq[i] - adjust - 8 * remainingPawns]; } @@ -911,7 +932,7 @@ void set_groups(T& e, PairsData* d, int order[], File f) { else d->groupLen[++n] = 1; - d->groupLen[++n] = 0; // Zero-terminated + d->groupLen[++n] = 0; // Zero-terminated // The sequence in pieces[] defines the groups, but not the order in which // they are encoded. If the pieces in a group g can be combined on the board @@ -924,24 +945,23 @@ void set_groups(T& e, PairsData* d, int order[], File f) { // pawns/pieces -> remaining pawns -> remaining pieces. In particular the // first group is at order[0] position and the remaining pawns, when present, // are at order[1] position. - bool pp = e.hasPawns && e.pawnCount[1]; // Pawns on both sides - int next = pp ? 2 : 1; - int freeSquares = 64 - d->groupLen[0] - (pp ? d->groupLen[1] : 0); - uint64_t idx = 1; + bool pp = e.hasPawns && e.pawnCount[1]; // Pawns on both sides + int next = pp ? 2 : 1; + int freeSquares = 64 - d->groupLen[0] - (pp ? d->groupLen[1] : 0); + uint64_t idx = 1; for (int k = 0; next < n || k == order[0] || k == order[1]; ++k) - if (k == order[0]) // Leading pawns or pieces + if (k == order[0]) // Leading pawns or pieces { d->groupIdx[0] = idx; - idx *= e.hasPawns ? LeadPawnsSize[d->groupLen[0]][f] - : e.hasUniquePieces ? 31332 : 462; + idx *= e.hasPawns ? LeadPawnsSize[d->groupLen[0]][f] : e.hasUniquePieces ? 31332 : 462; } - else if (k == order[1]) // Remaining pawns + else if (k == order[1]) // Remaining pawns { d->groupIdx[1] = idx; idx *= Binomial[d->groupLen[1]][48 - d->groupLen[0]]; } - else // Remaining pieces + else // Remaining pieces { d->groupIdx[next] = idx; idx *= Binomial[d->groupLen[next]][freeSquares]; @@ -956,8 +976,8 @@ void set_groups(T& e, PairsData* d, int order[], File f) { // symbol until reaching the leaves that represent the symbol value. uint8_t set_symlen(PairsData* d, Sym s, std::vector& visited) { - visited[s] = true; // We can set it now because tree is acyclic - Sym sr = d->btree[s].get(); + visited[s] = true; // We can set it now because tree is acyclic + Sym sr = d->btree[s].get(); if (sr == 0xFFF) return 0; @@ -977,10 +997,11 @@ uint8_t* set_sizes(PairsData* d, uint8_t* data) { d->flags = *data++; - if (d->flags & TBFlag::SingleValue) { + if (d->flags & TBFlag::SingleValue) + { d->blocksNum = d->blockLengthSize = 0; - d->span = d->sparseIndexSize = 0; // Broken MSVC zero-init - d->minSymLen = *data++; // Here we store the single value + d->span = d->sparseIndexSize = 0; // Broken MSVC zero-init + d->minSymLen = *data++; // Here we store the single value return data; } @@ -988,16 +1009,17 @@ uint8_t* set_sizes(PairsData* d, uint8_t* data) { // element stores the biggest index that is the tb size. uint64_t tbSize = d->groupIdx[std::find(d->groupLen, d->groupLen + 7, 0) - d->groupLen]; - d->sizeofBlock = 1ULL << *data++; - d->span = 1ULL << *data++; - d->sparseIndexSize = size_t((tbSize + d->span - 1) / d->span); // Round up - auto padding = number(data++); - d->blocksNum = number(data); data += sizeof(uint32_t); - d->blockLengthSize = d->blocksNum + padding; // Padded to ensure SparseIndex[] - // does not point out of range. + d->sizeofBlock = 1ULL << *data++; + d->span = 1ULL << *data++; + d->sparseIndexSize = size_t((tbSize + d->span - 1) / d->span); // Round up + auto padding = number(data++); + d->blocksNum = number(data); + data += sizeof(uint32_t); + d->blockLengthSize = d->blocksNum + padding; // Padded to ensure SparseIndex[] + // does not point out of range. d->maxSymLen = *data++; d->minSymLen = *data++; - d->lowestSym = (Sym*)data; + d->lowestSym = (Sym*) data; d->base64.resize(d->maxSymLen - d->minSymLen + 1); // See https://en.wikipedia.org/wiki/Huffman_coding @@ -1012,11 +1034,13 @@ uint8_t* set_sizes(PairsData* d, uint8_t* data) { // avoiding unsigned overflow warnings. int base64_size = static_cast(d->base64.size()); - for (int i = base64_size - 2; i >= 0; --i) { + for (int i = base64_size - 2; i >= 0; --i) + { d->base64[i] = (d->base64[i + 1] + number(&d->lowestSym[i]) - - number(&d->lowestSym[i + 1])) / 2; + - number(&d->lowestSym[i + 1])) + / 2; - assert(d->base64[i] * 2 >= d->base64[i+1]); + assert(d->base64[i] * 2 >= d->base64[i + 1]); } // Now left-shift by an amount so that d->base64[i] gets shifted 1 bit more @@ -1024,11 +1048,12 @@ uint8_t* set_sizes(PairsData* d, uint8_t* data) { // d->base64[i] >= d->base64[i+1]. Moreover for any symbol s64 of length i // and right-padded to 64 bits holds d->base64[i-1] >= s64 >= d->base64[i]. for (int i = 0; i < base64_size; ++i) - d->base64[i] <<= 64 - i - d->minSymLen; // Right-padding to 64 bits + d->base64[i] <<= 64 - i - d->minSymLen; // Right-padding to 64 bits data += base64_size * sizeof(Sym); - d->symlen.resize(number(data)); data += sizeof(uint16_t); - d->btree = (LR*)data; + d->symlen.resize(number(data)); + data += sizeof(uint16_t); + d->btree = (LR*) data; // The compression scheme used is "Recursive Pairing", that replaces the most // frequent adjacent pair of symbols in the source message by a new symbol, @@ -1050,18 +1075,24 @@ uint8_t* set_dtz_map(TBTable& e, uint8_t* data, File maxFile) { e.map = data; - for (File f = FILE_A; f <= maxFile; ++f) { + for (File f = FILE_A; f <= maxFile; ++f) + { auto flags = e.get(0, f)->flags; - if (flags & TBFlag::Mapped) { - if (flags & TBFlag::Wide) { + if (flags & TBFlag::Mapped) + { + if (flags & TBFlag::Wide) + { data += uintptr_t(data) & 1; // Word alignment, we may have a mixed table - for (int i = 0; i < 4; ++i) { // Sequence like 3,x,x,x,1,x,0,2,x,x - e.get(0, f)->map_idx[i] = uint16_t((uint16_t*)data - (uint16_t*)e.map + 1); + for (int i = 0; i < 4; ++i) + { // Sequence like 3,x,x,x,1,x,0,2,x,x + e.get(0, f)->map_idx[i] = uint16_t((uint16_t*) data - (uint16_t*) e.map + 1); data += 2 * number(data) + 2; } } - else { - for (int i = 0; i < 4; ++i) { + else + { + for (int i = 0; i < 4; ++i) + { e.get(0, f)->map_idx[i] = uint16_t(data - e.map + 1); data += *data + 1; } @@ -1069,7 +1100,7 @@ uint8_t* set_dtz_map(TBTable& e, uint8_t* data, File maxFile) { } } - return data += uintptr_t(data) & 1; // Word alignment + return data += uintptr_t(data) & 1; // Word alignment } // Populate entry's PairsData records with data from the just memory-mapped file. @@ -1079,38 +1110,42 @@ void set(T& e, uint8_t* data) { PairsData* d; - enum { Split = 1, HasPawns = 2 }; + enum { + Split = 1, + HasPawns = 2 + }; - assert(e.hasPawns == bool(*data & HasPawns)); + assert(e.hasPawns == bool(*data & HasPawns)); assert((e.key != e.key2) == bool(*data & Split)); - data++; // First byte stores flags + data++; // First byte stores flags - const int sides = T::Sides == 2 && (e.key != e.key2) ? 2 : 1; + const int sides = T::Sides == 2 && (e.key != e.key2) ? 2 : 1; const File maxFile = e.hasPawns ? FILE_D : FILE_A; - bool pp = e.hasPawns && e.pawnCount[1]; // Pawns on both sides + bool pp = e.hasPawns && e.pawnCount[1]; // Pawns on both sides assert(!pp || e.pawnCount[0]); - for (File f = FILE_A; f <= maxFile; ++f) { + for (File f = FILE_A; f <= maxFile; ++f) + { for (int i = 0; i < sides; i++) *e.get(i, f) = PairsData(); - int order[][2] = { { *data & 0xF, pp ? *(data + 1) & 0xF : 0xF }, - { *data >> 4, pp ? *(data + 1) >> 4 : 0xF } }; + int order[][2] = {{*data & 0xF, pp ? *(data + 1) & 0xF : 0xF}, + {*data >> 4, pp ? *(data + 1) >> 4 : 0xF}}; data += 1 + pp; for (int k = 0; k < e.pieceCount; ++k, ++data) for (int i = 0; i < sides; i++) - e.get(i, f)->pieces[k] = Piece(i ? *data >> 4 : *data & 0xF); + e.get(i, f)->pieces[k] = Piece(i ? *data >> 4 : *data & 0xF); for (int i = 0; i < sides; ++i) set_groups(e, e.get(i, f), order[i], f); } - data += uintptr_t(data) & 1; // Word alignment + data += uintptr_t(data) & 1; // Word alignment for (File f = FILE_A; f <= maxFile; ++f) for (int i = 0; i < sides; i++) @@ -1119,20 +1154,23 @@ void set(T& e, uint8_t* data) { data = set_dtz_map(e, data, maxFile); for (File f = FILE_A; f <= maxFile; ++f) - for (int i = 0; i < sides; i++) { - (d = e.get(i, f))->sparseIndex = (SparseEntry*)data; + for (int i = 0; i < sides; i++) + { + (d = e.get(i, f))->sparseIndex = (SparseEntry*) data; data += d->sparseIndexSize * sizeof(SparseEntry); } for (File f = FILE_A; f <= maxFile; ++f) - for (int i = 0; i < sides; i++) { - (d = e.get(i, f))->blockLength = (uint16_t*)data; + for (int i = 0; i < sides; i++) + { + (d = e.get(i, f))->blockLength = (uint16_t*) data; data += d->blockLengthSize * sizeof(uint16_t); } for (File f = FILE_A; f <= maxFile; ++f) - for (int i = 0; i < sides; i++) { - data = (uint8_t*)((uintptr_t(data) + 0x3F) & ~0x3F); // 64 byte alignment + for (int i = 0; i < sides; i++) + { + data = (uint8_t*) ((uintptr_t(data) + 0x3F) & ~0x3F); // 64 byte alignment (d = e.get(i, f))->data = data; data += d->blocksNum * d->sizeofBlock; } @@ -1150,22 +1188,23 @@ void* mapped(TBTable& e, const Position& pos) { // 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 + return e.baseAddress; // Could be nullptr if file does not exist std::scoped_lock lk(mutex); - if (e.ready.load(std::memory_order_relaxed)) // Recheck under lock + if (e.ready.load(std::memory_order_relaxed)) // Recheck under lock return e.baseAddress; // Pieces strings in decreasing order for each color, like ("KPP","KR") std::string fname, w, b; - for (PieceType pt = KING; pt >= PAWN; --pt) { + for (PieceType pt = KING; pt >= PAWN; --pt) + { w += std::string(popcount(pos.pieces(WHITE, pt)), PieceToChar[pt]); b += std::string(popcount(pos.pieces(BLACK, pt)), PieceToChar[pt]); } - fname = (e.key == pos.material_key() ? w + 'v' + b : b + 'v' + w) - + (Type == WDL ? ".rtbw" : ".rtbz"); + fname = + (e.key == pos.material_key() ? w + 'v' + b : b + 'v' + w) + (Type == WDL ? ".rtbw" : ".rtbz"); uint8_t* data = TBFile(fname).map(&e.baseAddress, &e.mapping, Type); @@ -1179,7 +1218,7 @@ void* mapped(TBTable& e, const Position& pos) { template::Ret> Ret probe_table(const Position& pos, ProbeState* result, WDLScore wdl = WDLDraw) { - if (pos.count() == 2) // KvK + if (pos.count() == 2) // KvK return Ret(WDLDraw); TBTable* entry = TBTables.get(pos.material_key()); @@ -1206,16 +1245,15 @@ Ret probe_table(const Position& pos, ProbeState* result, WDLScore wdl = WDLDraw) template WDLScore search(Position& pos, ProbeState* result) { - WDLScore value, bestValue = WDLLoss; + WDLScore value, bestValue = WDLLoss; StateInfo st; - auto moveList = MoveList(pos); + auto moveList = MoveList(pos); size_t totalCount = moveList.size(), moveCount = 0; for (const Move move : moveList) { - if ( !pos.capture(move) - && (!CheckZeroingMoves || type_of(pos.moved_piece(move)) != PAWN)) + if (!pos.capture(move) && (!CheckZeroingMoves || type_of(pos.moved_piece(move)) != PAWN)) continue; moveCount++; @@ -1233,7 +1271,7 @@ WDLScore search(Position& pos, ProbeState* result) { if (value >= WDLWin) { - *result = ZEROING_BEST_MOVE; // Winning DTZ-zeroing move + *result = ZEROING_BEST_MOVE; // Winning DTZ-zeroing move return value; } } @@ -1259,13 +1297,12 @@ WDLScore search(Position& pos, ProbeState* result) { // DTZ stores a "don't care" value if bestValue is a win if (bestValue >= value) - return *result = ( bestValue > WDLDraw - || noMoreMoves ? ZEROING_BEST_MOVE : OK), bestValue; + return *result = (bestValue > WDLDraw || noMoreMoves ? ZEROING_BEST_MOVE : OK), bestValue; return *result = OK, value; } -} // namespace +} // namespace // Tablebases::init() is called at startup and after every change to @@ -1275,7 +1312,7 @@ void Tablebases::init(const std::string& paths) { TBTables.clear(); MaxCardinality = 0; - TBFile::Paths = paths; + TBFile::Paths = paths; if (paths.empty() || paths == "") return; @@ -1307,14 +1344,14 @@ void Tablebases::init(const std::string& paths) { code = 0; for (int idx = 0; idx < 10; idx++) for (Square s1 = SQ_A1; s1 <= SQ_D4; ++s1) - if (MapA1D1D4[s1] == idx && (idx || s1 == SQ_B1)) // SQ_B1 is mapped to 0 + if (MapA1D1D4[s1] == idx && (idx || s1 == SQ_B1)) // SQ_B1 is mapped to 0 { for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) if ((PseudoAttacks[KING][s1] | s1) & s2) - continue; // Illegal position + continue; // Illegal position else if (!off_A1H8(s1) && off_A1H8(s2) > 0) - continue; // First on diagonal, second above + continue; // First on diagonal, second above else if (!off_A1H8(s1) && !off_A1H8(s2)) bothOnDiagonal.emplace_back(idx, s2); @@ -1331,16 +1368,16 @@ void Tablebases::init(const std::string& paths) { // are Binomial[k][n] ways to choose k elements from a set of n elements. Binomial[0][0] = 1; - for (int n = 1; n < 64; n++) // Squares - for (int k = 0; k < 6 && k <= n; ++k) // Pieces - Binomial[k][n] = (k > 0 ? Binomial[k - 1][n - 1] : 0) - + (k < n ? Binomial[k ][n - 1] : 0); + for (int n = 1; n < 64; n++) // Squares + for (int k = 0; k < 6 && k <= n; ++k) // Pieces + Binomial[k][n] = + (k > 0 ? Binomial[k - 1][n - 1] : 0) + (k < n ? Binomial[k][n - 1] : 0); // MapPawns[s] encodes squares a2-h7 to 0..47. This is the number of possible // available squares when the leading one is in 's'. Moreover the pawn with // highest MapPawns[] is the leading pawn, the one nearest the edge, and // among pawns with the same file, the one with the lowest rank. - int availableSquares = 47; // Available squares when lead pawn is in a2 + int availableSquares = 47; // Available squares when lead pawn is in a2 // Init the tables for the encoding of leading pawns group: with 7-men TB we // can have up to 5 leading pawns (KPPPPPK). @@ -1364,7 +1401,7 @@ void Tablebases::init(const std::string& paths) { // due to mirroring: sq == a3 -> no a2, h2, so MapPawns[a3] = 45 if (leadPawnsCnt == 1) { - MapPawns[sq] = availableSquares--; + MapPawns[sq] = availableSquares--; MapPawns[flip_file(sq)] = availableSquares--; } LeadPawnIdx[leadPawnsCnt][sq] = idx; @@ -1375,20 +1412,24 @@ void Tablebases::init(const std::string& paths) { } // Add entries in TB tables if the corresponding ".rtbw" file exists - for (PieceType p1 = PAWN; p1 < KING; ++p1) { + for (PieceType p1 = PAWN; p1 < KING; ++p1) + { TBTables.add({KING, p1, KING}); - for (PieceType p2 = PAWN; p2 <= p1; ++p2) { + for (PieceType p2 = PAWN; p2 <= p1; ++p2) + { TBTables.add({KING, p1, p2, KING}); TBTables.add({KING, p1, KING, p2}); for (PieceType p3 = PAWN; p3 < KING; ++p3) TBTables.add({KING, p1, p2, KING, p3}); - for (PieceType p3 = PAWN; p3 <= p2; ++p3) { + for (PieceType p3 = PAWN; p3 <= p2; ++p3) + { TBTables.add({KING, p1, p2, p3, KING}); - for (PieceType p4 = PAWN; p4 <= p3; ++p4) { + for (PieceType p4 = PAWN; p4 <= p3; ++p4) + { TBTables.add({KING, p1, p2, p3, p4, KING}); for (PieceType p5 = PAWN; p5 <= p4; ++p5) @@ -1398,7 +1439,8 @@ void Tablebases::init(const std::string& paths) { TBTables.add({KING, p1, p2, p3, p4, KING, p5}); } - for (PieceType p4 = PAWN; p4 < KING; ++p4) { + for (PieceType p4 = PAWN; p4 < KING; ++p4) + { TBTables.add({KING, p1, p2, p3, KING, p4}); for (PieceType p5 = PAWN; p5 <= p4; ++p5) @@ -1457,10 +1499,10 @@ WDLScore Tablebases::probe_wdl(Position& pos, ProbeState* result) { // then do not accept moves leading to dtz + 50-move-counter == 100. int Tablebases::probe_dtz(Position& pos, ProbeState* result) { - *result = OK; + *result = OK; WDLScore wdl = search(pos, result); - if (*result == FAIL || wdl == WDLDraw) // DTZ tables don't store draws + if (*result == FAIL || wdl == WDLDraw) // DTZ tables don't store draws return 0; // DTZ stores a 'don't care value in this case, or even a plain wrong @@ -1479,7 +1521,7 @@ int Tablebases::probe_dtz(Position& pos, ProbeState* result) { // DTZ stores results for the other side, so we need to do a 1-ply search and // find the winning move that minimizes DTZ. StateInfo st; - int minDTZ = 0xFFFF; + int minDTZ = 0xFFFF; for (const Move move : MoveList(pos)) { @@ -1491,8 +1533,7 @@ int Tablebases::probe_dtz(Position& pos, ProbeState* result) { // otherwise we will get the dtz of the next move sequence. Search the // position after the move to get the score sign (because even in a // winning position we could make a losing capture or go for a draw). - dtz = zeroing ? -dtz_before_zeroing(search(pos, result)) - : -probe_dtz(pos, result); + dtz = zeroing ? -dtz_before_zeroing(search(pos, result)) : -probe_dtz(pos, result); // If the move mates, force minDTZ to 1 if (dtz == 1 && pos.checkers() && MoveList(pos).size() == 0) @@ -1524,7 +1565,7 @@ int Tablebases::probe_dtz(Position& pos, ProbeState* result) { bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves) { ProbeState result = OK; - StateInfo st; + StateInfo st; // Obtain 50-move counter for the root position int cnt50 = pos.rule50_count(); @@ -1544,7 +1585,7 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves) { { // In case of a zeroing move, dtz is one of -101/-1/0/1/101 WDLScore wdl = -probe_wdl(pos, &result); - dtz = dtz_before_zeroing(wdl); + dtz = dtz_before_zeroing(wdl); } else if (pos.is_draw(1)) { @@ -1557,14 +1598,11 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves) { { // Otherwise, take dtz for the new position and correct by 1 ply dtz = -probe_dtz(pos, &result); - dtz = dtz > 0 ? dtz + 1 - : dtz < 0 ? dtz - 1 : dtz; + dtz = dtz > 0 ? dtz + 1 : dtz < 0 ? dtz - 1 : dtz; } // Make sure that a mating move is assigned a dtz value of 1 - if ( pos.checkers() - && dtz == 2 - && MoveList(pos).size() == 0) + if (pos.checkers() && dtz == 2 && MoveList(pos).size() == 0) dtz = 1; pos.undo_move(m.pv[0]); @@ -1574,19 +1612,19 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves) { // Better moves are ranked higher. Certain wins are ranked equally. // Losing moves are ranked equally unless a 50-move draw is in sight. - int r = dtz > 0 ? (dtz + cnt50 <= 99 && !rep ? MAX_DTZ : MAX_DTZ - (dtz + cnt50)) - : dtz < 0 ? (-dtz * 2 + cnt50 < 100 ? -MAX_DTZ : -MAX_DTZ + (-dtz + cnt50)) - : 0; + int r = dtz > 0 ? (dtz + cnt50 <= 99 && !rep ? MAX_DTZ : MAX_DTZ - (dtz + cnt50)) + : dtz < 0 ? (-dtz * 2 + cnt50 < 100 ? -MAX_DTZ : -MAX_DTZ + (-dtz + cnt50)) + : 0; m.tbRank = r; // Determine the score to be displayed for this move. Assign at least // 1 cp to cursed wins and let it grow to 49 cp as the positions gets // closer to a real win. - m.tbScore = r >= bound ? VALUE_MATE - MAX_PLY - 1 - : r > 0 ? Value((std::max( 3, r - (MAX_DTZ - 200)) * int(PawnValue)) / 200) - : r == 0 ? VALUE_DRAW - : r > -bound ? Value((std::min(-3, r + (MAX_DTZ - 200)) * int(PawnValue)) / 200) - : -VALUE_MATE + MAX_PLY + 1; + m.tbScore = r >= bound ? VALUE_MATE - MAX_PLY - 1 + : r > 0 ? Value((std::max(3, r - (MAX_DTZ - 200)) * int(PawnValue)) / 200) + : r == 0 ? VALUE_DRAW + : r > -bound ? Value((std::min(-3, r + (MAX_DTZ - 200)) * int(PawnValue)) / 200) + : -VALUE_MATE + MAX_PLY + 1; } return true; @@ -1599,11 +1637,11 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves) { // A return value false indicates that not all probes were successful. bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves) { - static const int WDL_to_rank[] = { -MAX_DTZ, -MAX_DTZ + 101, 0, MAX_DTZ - 101, MAX_DTZ }; + static const int WDL_to_rank[] = {-MAX_DTZ, -MAX_DTZ + 101, 0, MAX_DTZ - 101, MAX_DTZ}; ProbeState result = OK; - StateInfo st; - WDLScore wdl; + StateInfo st; + WDLScore wdl; bool rule50 = Options["Syzygy50MoveRule"]; @@ -1625,12 +1663,11 @@ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves) { m.tbRank = WDL_to_rank[wdl + 2]; if (!rule50) - wdl = wdl > WDLDraw ? WDLWin - : wdl < WDLDraw ? WDLLoss : WDLDraw; + wdl = wdl > WDLDraw ? WDLWin : wdl < WDLDraw ? WDLLoss : WDLDraw; m.tbScore = WDL_to_value[wdl + 2]; } return true; } -} // namespace Stockfish +} // namespace Stockfish diff --git a/src/syzygy/tbprobe.h b/src/syzygy/tbprobe.h index b2ba35ff4b0..3b7c8aa70fd 100644 --- a/src/syzygy/tbprobe.h +++ b/src/syzygy/tbprobe.h @@ -30,30 +30,30 @@ class Position; namespace Stockfish::Tablebases { enum WDLScore { - WDLLoss = -2, // Loss - WDLBlessedLoss = -1, // Loss, but draw under 50-move rule - WDLDraw = 0, // Draw - WDLCursedWin = 1, // Win, but draw under 50-move rule - WDLWin = 2, // Win + WDLLoss = -2, // Loss + WDLBlessedLoss = -1, // Loss, but draw under 50-move rule + WDLDraw = 0, // Draw + WDLCursedWin = 1, // Win, but draw under 50-move rule + WDLWin = 2, // Win }; // Possible states after a probing operation enum ProbeState { - FAIL = 0, // Probe failed (missing file table) - OK = 1, // Probe successful - CHANGE_STM = -1, // DTZ should check the other side - ZEROING_BEST_MOVE = 2 // Best move zeroes DTZ (capture or pawn move) + FAIL = 0, // Probe failed (missing file table) + OK = 1, // Probe successful + CHANGE_STM = -1, // DTZ should check the other side + ZEROING_BEST_MOVE = 2 // Best move zeroes DTZ (capture or pawn move) }; extern int MaxCardinality; -void init(const std::string& paths); +void init(const std::string& paths); WDLScore probe_wdl(Position& pos, ProbeState* result); -int probe_dtz(Position& pos, ProbeState* result); -bool root_probe(Position& pos, Search::RootMoves& rootMoves); -bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves); -void rank_root_moves(Position& pos, Search::RootMoves& rootMoves); +int probe_dtz(Position& pos, ProbeState* result); +bool root_probe(Position& pos, Search::RootMoves& rootMoves); +bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves); +void rank_root_moves(Position& pos, Search::RootMoves& rootMoves); -} // namespace Stockfish::Tablebases +} // namespace Stockfish::Tablebases #endif diff --git a/src/thread.cpp b/src/thread.cpp index c752e7326cd..9f8a63bdc01 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -37,15 +37,17 @@ namespace Stockfish { -ThreadPool Threads; // Global object +ThreadPool Threads; // Global object // Thread constructor launches the thread and waits until it goes to sleep // in idle_loop(). Note that 'searching' and 'exit' should be already set. -Thread::Thread(size_t n) : idx(n), stdThread(&Thread::idle_loop, this) { +Thread::Thread(size_t n) : + idx(n), + stdThread(&Thread::idle_loop, this) { - wait_for_search_finished(); + wait_for_search_finished(); } @@ -54,11 +56,11 @@ Thread::Thread(size_t n) : idx(n), stdThread(&Thread::idle_loop, this) { Thread::~Thread() { - assert(!searching); + assert(!searching); - exit = true; - start_searching(); - stdThread.join(); + exit = true; + start_searching(); + stdThread.join(); } @@ -66,25 +68,25 @@ Thread::~Thread() { void Thread::clear() { - counterMoves.fill(MOVE_NONE); - mainHistory.fill(0); - captureHistory.fill(0); + counterMoves.fill(MOVE_NONE); + mainHistory.fill(0); + captureHistory.fill(0); - for (bool inCheck : { false, true }) - for (StatsType c : { NoCaptures, Captures }) - for (auto& to : continuationHistory[inCheck][c]) - for (auto& h : to) - h->fill(-71); + for (bool inCheck : {false, true}) + for (StatsType c : {NoCaptures, Captures}) + for (auto& to : continuationHistory[inCheck][c]) + for (auto& h : to) + h->fill(-71); } // Thread::start_searching() wakes up the thread that will start the search void Thread::start_searching() { - mutex.lock(); - searching = true; - mutex.unlock(); // Unlock before notifying saves a few CPU-cycles - cv.notify_one(); // Wake up the thread in idle_loop() + mutex.lock(); + searching = true; + mutex.unlock(); // Unlock before notifying saves a few CPU-cycles + cv.notify_one(); // Wake up the thread in idle_loop() } @@ -93,8 +95,8 @@ void Thread::start_searching() { void Thread::wait_for_search_finished() { - std::unique_lock lk(mutex); - cv.wait(lk, [&]{ return !searching; }); + std::unique_lock lk(mutex); + cv.wait(lk, [&] { return !searching; }); } @@ -103,28 +105,28 @@ void Thread::wait_for_search_finished() { void Thread::idle_loop() { - // If OS already scheduled us on a different group than 0 then don't overwrite - // the choice, eventually we are one of many one-threaded processes running on - // some Windows NUMA hardware, for instance in fishtest. To make it simple, - // just check if running threads are below a threshold, in this case, all this - // NUMA machinery is not needed. - if (Options["Threads"] > 8) - WinProcGroup::bindThisThread(idx); + // If OS already scheduled us on a different group than 0 then don't overwrite + // the choice, eventually we are one of many one-threaded processes running on + // some Windows NUMA hardware, for instance in fishtest. To make it simple, + // just check if running threads are below a threshold, in this case, all this + // NUMA machinery is not needed. + if (Options["Threads"] > 8) + WinProcGroup::bindThisThread(idx); - while (true) - { - std::unique_lock lk(mutex); - searching = false; - cv.notify_one(); // Wake up anyone waiting for search finished - cv.wait(lk, [&]{ return searching; }); + while (true) + { + std::unique_lock lk(mutex); + searching = false; + cv.notify_one(); // Wake up anyone waiting for search finished + cv.wait(lk, [&] { return searching; }); - if (exit) - return; + if (exit) + return; - lk.unlock(); + lk.unlock(); - search(); - } + search(); + } } // ThreadPool::set() creates/destroys threads to match the requested number. @@ -133,28 +135,28 @@ void Thread::idle_loop() { void ThreadPool::set(size_t requested) { - if (threads.size() > 0) // destroy any existing thread(s) - { - main()->wait_for_search_finished(); + if (threads.size() > 0) // destroy any existing thread(s) + { + main()->wait_for_search_finished(); - while (threads.size() > 0) - delete threads.back(), threads.pop_back(); - } + while (threads.size() > 0) + delete threads.back(), threads.pop_back(); + } - if (requested > 0) // create new thread(s) - { - threads.push_back(new MainThread(0)); + if (requested > 0) // create new thread(s) + { + threads.push_back(new MainThread(0)); - while (threads.size() < requested) - threads.push_back(new Thread(threads.size())); - clear(); + while (threads.size() < requested) + threads.push_back(new Thread(threads.size())); + clear(); - // Reallocate the hash with the new threadpool size - TT.resize(size_t(Options["Hash"])); + // Reallocate the hash with the new threadpool size + TT.resize(size_t(Options["Hash"])); - // Init thread number dependent search params. - Search::init(); - } + // Init thread number dependent search params. + Search::init(); + } } @@ -162,77 +164,79 @@ void ThreadPool::set(size_t requested) { void ThreadPool::clear() { - for (Thread* th : threads) - th->clear(); + for (Thread* th : threads) + th->clear(); - main()->callsCnt = 0; - main()->bestPreviousScore = VALUE_INFINITE; - main()->bestPreviousAverageScore = VALUE_INFINITE; - main()->previousTimeReduction = 1.0; + main()->callsCnt = 0; + main()->bestPreviousScore = VALUE_INFINITE; + main()->bestPreviousAverageScore = VALUE_INFINITE; + main()->previousTimeReduction = 1.0; } // ThreadPool::start_thinking() wakes up main thread waiting in idle_loop() and // returns immediately. Main thread will wake up other threads and start the search. -void ThreadPool::start_thinking(Position& pos, StateListPtr& states, - const Search::LimitsType& limits, bool ponderMode) { - - main()->wait_for_search_finished(); - - main()->stopOnPonderhit = stop = false; - increaseDepth = true; - main()->ponder = ponderMode; - Search::Limits = limits; - Search::RootMoves rootMoves; - - for (const auto& m : MoveList(pos)) - if ( limits.searchmoves.empty() - || std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m)) - rootMoves.emplace_back(m); - - if (!rootMoves.empty()) - Tablebases::rank_root_moves(pos, rootMoves); - - // After ownership transfer 'states' becomes empty, so if we stop the search - // and call 'go' again without setting a new position states.get() == nullptr. - assert(states.get() || setupStates.get()); - - if (states.get()) - setupStates = std::move(states); // Ownership transfer, states is now empty - - // We use Position::set() to set root position across threads. But there are - // some StateInfo fields (previous, pliesFromNull, capturedPiece) that cannot - // be deduced from a fen string, so set() clears them and they are set from - // setupStates->back() later. The rootState is per thread, earlier states are shared - // since they are read-only. - for (Thread* th : threads) - { - th->nodes = th->tbHits = th->nmpMinPly = th->bestMoveChanges = 0; - th->rootDepth = th->completedDepth = 0; - th->rootMoves = rootMoves; - th->rootPos.set(pos.fen(), pos.is_chess960(), &th->rootState, th); - th->rootState = setupStates->back(); - th->rootSimpleEval = Eval::simple_eval(pos, pos.side_to_move()); - } - - main()->start_searching(); +void ThreadPool::start_thinking(Position& pos, + StateListPtr& states, + const Search::LimitsType& limits, + bool ponderMode) { + + main()->wait_for_search_finished(); + + main()->stopOnPonderhit = stop = false; + increaseDepth = true; + main()->ponder = ponderMode; + Search::Limits = limits; + Search::RootMoves rootMoves; + + for (const auto& m : MoveList(pos)) + if (limits.searchmoves.empty() + || std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m)) + rootMoves.emplace_back(m); + + if (!rootMoves.empty()) + Tablebases::rank_root_moves(pos, rootMoves); + + // After ownership transfer 'states' becomes empty, so if we stop the search + // and call 'go' again without setting a new position states.get() == nullptr. + assert(states.get() || setupStates.get()); + + if (states.get()) + setupStates = std::move(states); // Ownership transfer, states is now empty + + // We use Position::set() to set root position across threads. But there are + // some StateInfo fields (previous, pliesFromNull, capturedPiece) that cannot + // be deduced from a fen string, so set() clears them and they are set from + // setupStates->back() later. The rootState is per thread, earlier states are shared + // since they are read-only. + for (Thread* th : threads) + { + th->nodes = th->tbHits = th->nmpMinPly = th->bestMoveChanges = 0; + th->rootDepth = th->completedDepth = 0; + th->rootMoves = rootMoves; + th->rootPos.set(pos.fen(), pos.is_chess960(), &th->rootState, th); + th->rootState = setupStates->back(); + th->rootSimpleEval = Eval::simple_eval(pos, pos.side_to_move()); + } + + main()->start_searching(); } Thread* ThreadPool::get_best_thread() const { - Thread* bestThread = threads.front(); + Thread* bestThread = threads.front(); std::map votes; - Value minScore = VALUE_NONE; + Value minScore = VALUE_NONE; // Find the minimum score of all threads - for (Thread* th: threads) + for (Thread* th : threads) minScore = std::min(minScore, th->rootMoves[0].score); // Vote according to score and depth, and select the best thread auto thread_value = [minScore](Thread* th) { - return (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth); - }; + return (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth); + }; for (Thread* th : threads) votes[th->rootMoves[0].pv[0]] += thread_value(th); @@ -244,12 +248,13 @@ Thread* ThreadPool::get_best_thread() const { if (th->rootMoves[0].score > bestThread->rootMoves[0].score) bestThread = th; } - else if ( th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY - || ( th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY - && ( votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]] - || ( votes[th->rootMoves[0].pv[0]] == votes[bestThread->rootMoves[0].pv[0]] - && thread_value(th) * int(th->rootMoves[0].pv.size() > 2) - > thread_value(bestThread) * int(bestThread->rootMoves[0].pv.size() > 2))))) + else if (th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY + || (th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY + && (votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]] + || (votes[th->rootMoves[0].pv[0]] == votes[bestThread->rootMoves[0].pv[0]] + && thread_value(th) * int(th->rootMoves[0].pv.size() > 2) + > thread_value(bestThread) + * int(bestThread->rootMoves[0].pv.size() > 2))))) bestThread = th; return bestThread; @@ -275,4 +280,4 @@ void ThreadPool::wait_for_search_finished() const { th->wait_for_search_finished(); } -} // namespace Stockfish +} // namespace Stockfish diff --git a/src/thread.h b/src/thread.h index 44cc56729e3..4a077962661 100644 --- a/src/thread.h +++ b/src/thread.h @@ -41,56 +41,56 @@ namespace Stockfish { class Thread { - std::mutex mutex; - std::condition_variable cv; - size_t idx; - bool exit = false, searching = true; // Set before starting std::thread - NativeThread stdThread; - -public: - explicit Thread(size_t); - virtual ~Thread(); - virtual void search(); - void clear(); - void idle_loop(); - void start_searching(); - void wait_for_search_finished(); - size_t id() const { return idx; } - - size_t pvIdx, pvLast; - std::atomic nodes, tbHits, bestMoveChanges; - int selDepth, nmpMinPly; - Value bestValue, optimism[COLOR_NB]; - - Position rootPos; - StateInfo rootState; - Search::RootMoves rootMoves; - Depth rootDepth, completedDepth; - Value rootDelta; - Value rootSimpleEval; - CounterMoveHistory counterMoves; - ButterflyHistory mainHistory; - CapturePieceToHistory captureHistory; - ContinuationHistory continuationHistory[2][2]; + std::mutex mutex; + std::condition_variable cv; + size_t idx; + bool exit = false, searching = true; // Set before starting std::thread + NativeThread stdThread; + + public: + explicit Thread(size_t); + virtual ~Thread(); + virtual void search(); + void clear(); + void idle_loop(); + void start_searching(); + void wait_for_search_finished(); + size_t id() const { return idx; } + + size_t pvIdx, pvLast; + std::atomic nodes, tbHits, bestMoveChanges; + int selDepth, nmpMinPly; + Value bestValue, optimism[COLOR_NB]; + + Position rootPos; + StateInfo rootState; + Search::RootMoves rootMoves; + Depth rootDepth, completedDepth; + Value rootDelta; + Value rootSimpleEval; + CounterMoveHistory counterMoves; + ButterflyHistory mainHistory; + CapturePieceToHistory captureHistory; + ContinuationHistory continuationHistory[2][2]; }; // MainThread is a derived class specific for main thread -struct MainThread : public Thread { +struct MainThread: public Thread { - using Thread::Thread; + using Thread::Thread; - void search() override; - void check_time(); + void search() override; + void check_time(); - double previousTimeReduction; - Value bestPreviousScore; - Value bestPreviousAverageScore; - Value iterValue[4]; - int callsCnt; - bool stopOnPonderhit; - std::atomic_bool ponder; + double previousTimeReduction; + Value bestPreviousScore; + Value bestPreviousAverageScore; + Value iterValue[4]; + int callsCnt; + bool stopOnPonderhit; + std::atomic_bool ponder; }; @@ -100,41 +100,41 @@ struct MainThread : public Thread { struct ThreadPool { - void start_thinking(Position&, StateListPtr&, const Search::LimitsType&, bool = false); - void clear(); - void set(size_t); - - MainThread* main() const { return static_cast(threads.front()); } - uint64_t nodes_searched() const { return accumulate(&Thread::nodes); } - uint64_t tb_hits() const { return accumulate(&Thread::tbHits); } - Thread* get_best_thread() const; - void start_searching(); - void wait_for_search_finished() const; - - std::atomic_bool stop, increaseDepth; - - auto cbegin() const noexcept { return threads.cbegin(); } - auto begin() noexcept { return threads.begin(); } - auto end() noexcept { return threads.end(); } - auto cend() const noexcept { return threads.cend(); } - auto size() const noexcept { return threads.size(); } - auto empty() const noexcept { return threads.empty(); } - -private: - StateListPtr setupStates; - std::vector threads; - - uint64_t accumulate(std::atomic Thread::* member) const { - - uint64_t sum = 0; - for (Thread* th : threads) - sum += (th->*member).load(std::memory_order_relaxed); - return sum; - } + void start_thinking(Position&, StateListPtr&, const Search::LimitsType&, bool = false); + void clear(); + void set(size_t); + + MainThread* main() const { return static_cast(threads.front()); } + uint64_t nodes_searched() const { return accumulate(&Thread::nodes); } + uint64_t tb_hits() const { return accumulate(&Thread::tbHits); } + Thread* get_best_thread() const; + void start_searching(); + void wait_for_search_finished() const; + + std::atomic_bool stop, increaseDepth; + + auto cbegin() const noexcept { return threads.cbegin(); } + auto begin() noexcept { return threads.begin(); } + auto end() noexcept { return threads.end(); } + auto cend() const noexcept { return threads.cend(); } + auto size() const noexcept { return threads.size(); } + auto empty() const noexcept { return threads.empty(); } + + private: + StateListPtr setupStates; + std::vector threads; + + uint64_t accumulate(std::atomic Thread::*member) const { + + uint64_t sum = 0; + for (Thread* th : threads) + sum += (th->*member).load(std::memory_order_relaxed); + return sum; + } }; extern ThreadPool Threads; -} // namespace Stockfish +} // namespace Stockfish -#endif // #ifndef THREAD_H_INCLUDED +#endif // #ifndef THREAD_H_INCLUDED diff --git a/src/thread_win32_osx.h b/src/thread_win32_osx.h index 77352aa087a..248e4a67450 100644 --- a/src/thread_win32_osx.h +++ b/src/thread_win32_osx.h @@ -29,46 +29,45 @@ #if defined(__APPLE__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(USE_PTHREADS) -#include + #include namespace Stockfish { static const size_t TH_STACK_SIZE = 8 * 1024 * 1024; -template > -void* start_routine(void* ptr) -{ - P* p = reinterpret_cast(ptr); - (p->first->*(p->second))(); // Call member function pointer - delete p; - return nullptr; +template> +void* start_routine(void* ptr) { + P* p = reinterpret_cast(ptr); + (p->first->*(p->second))(); // Call member function pointer + delete p; + return nullptr; } class NativeThread { - pthread_t thread; - -public: - template> - explicit NativeThread(void(T::*fun)(), T* obj) { - pthread_attr_t attr_storage, *attr = &attr_storage; - pthread_attr_init(attr); - pthread_attr_setstacksize(attr, TH_STACK_SIZE); - pthread_create(&thread, attr, start_routine, new P(obj, fun)); - } - void join() { pthread_join(thread, nullptr); } + pthread_t thread; + + public: + template> + explicit NativeThread(void (T::*fun)(), T* obj) { + pthread_attr_t attr_storage, *attr = &attr_storage; + pthread_attr_init(attr); + pthread_attr_setstacksize(attr, TH_STACK_SIZE); + pthread_create(&thread, attr, start_routine, new P(obj, fun)); + } + void join() { pthread_join(thread, nullptr); } }; -} // namespace Stockfish +} // namespace Stockfish -#else // Default case: use STL classes +#else // Default case: use STL classes namespace Stockfish { using NativeThread = std::thread; -} // namespace Stockfish +} // namespace Stockfish #endif -#endif // #ifndef THREAD_WIN32_OSX_H_INCLUDED +#endif // #ifndef THREAD_WIN32_OSX_H_INCLUDED diff --git a/src/timeman.cpp b/src/timeman.cpp index 74f59d90574..cf0e08ed789 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -26,7 +26,7 @@ namespace Stockfish { -TimeManagement Time; // Our global time management object +TimeManagement Time; // Our global time management object // TimeManagement::init() is called at the beginning of the search and calculates @@ -36,74 +36,74 @@ TimeManagement Time; // Our global time management object void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { - // If we have no time, no need to initialize TM, except for the start time, - // which is used by movetime. - startTime = limits.startTime; - if (limits.time[us] == 0) - return; - - TimePoint moveOverhead = TimePoint(Options["Move Overhead"]); - TimePoint slowMover = TimePoint(Options["Slow Mover"]); - TimePoint npmsec = TimePoint(Options["nodestime"]); - - // optScale is a percentage of available time to use for the current move. - // maxScale is a multiplier applied to optimumTime. - double optScale, maxScale; - - // If we have to play in 'nodes as time' mode, then convert from time - // to nodes, and use resulting values in time management formulas. - // WARNING: to avoid time losses, the given npmsec (nodes per millisecond) - // must be much lower than the real engine speed. - if (npmsec) - { - if (!availableNodes) // Only once at game start - availableNodes = npmsec * limits.time[us]; // Time is in msec - - // Convert from milliseconds to nodes - limits.time[us] = TimePoint(availableNodes); - limits.inc[us] *= npmsec; - limits.npmsec = npmsec; - } - - // Maximum move horizon of 50 moves - int mtg = limits.movestogo ? std::min(limits.movestogo, 50) : 50; - - // Make sure timeLeft is > 0 since we may use it as a divisor - TimePoint timeLeft = std::max(TimePoint(1), - limits.time[us] + limits.inc[us] * (mtg - 1) - moveOverhead * (2 + mtg)); - - // Use extra time with larger increments - double optExtra = std::clamp(1.0 + 12.0 * limits.inc[us] / limits.time[us], 1.0, 1.12); - - // A user may scale time usage by setting UCI option "Slow Mover" - // Default is 100 and changing this value will probably lose elo. - timeLeft = slowMover * timeLeft / 100; - - // x basetime (+ z increment) - // If there is a healthy increment, timeLeft can exceed actual available - // game time for the current move, so also cap to 20% of available game time. - if (limits.movestogo == 0) - { - optScale = std::min(0.0120 + std::pow(ply + 3.0, 0.45) * 0.0039, - 0.2 * limits.time[us] / double(timeLeft)) + // If we have no time, no need to initialize TM, except for the start time, + // which is used by movetime. + startTime = limits.startTime; + if (limits.time[us] == 0) + return; + + TimePoint moveOverhead = TimePoint(Options["Move Overhead"]); + TimePoint slowMover = TimePoint(Options["Slow Mover"]); + TimePoint npmsec = TimePoint(Options["nodestime"]); + + // optScale is a percentage of available time to use for the current move. + // maxScale is a multiplier applied to optimumTime. + double optScale, maxScale; + + // If we have to play in 'nodes as time' mode, then convert from time + // to nodes, and use resulting values in time management formulas. + // WARNING: to avoid time losses, the given npmsec (nodes per millisecond) + // must be much lower than the real engine speed. + if (npmsec) + { + if (!availableNodes) // Only once at game start + availableNodes = npmsec * limits.time[us]; // Time is in msec + + // Convert from milliseconds to nodes + limits.time[us] = TimePoint(availableNodes); + limits.inc[us] *= npmsec; + limits.npmsec = npmsec; + } + + // Maximum move horizon of 50 moves + int mtg = limits.movestogo ? std::min(limits.movestogo, 50) : 50; + + // Make sure timeLeft is > 0 since we may use it as a divisor + TimePoint timeLeft = std::max(TimePoint(1), limits.time[us] + limits.inc[us] * (mtg - 1) + - moveOverhead * (2 + mtg)); + + // Use extra time with larger increments + double optExtra = std::clamp(1.0 + 12.0 * limits.inc[us] / limits.time[us], 1.0, 1.12); + + // A user may scale time usage by setting UCI option "Slow Mover" + // Default is 100 and changing this value will probably lose elo. + timeLeft = slowMover * timeLeft / 100; + + // x basetime (+ z increment) + // If there is a healthy increment, timeLeft can exceed actual available + // game time for the current move, so also cap to 20% of available game time. + if (limits.movestogo == 0) + { + optScale = std::min(0.0120 + std::pow(ply + 3.0, 0.45) * 0.0039, + 0.2 * limits.time[us] / double(timeLeft)) * optExtra; - maxScale = std::min(7.0, 4.0 + ply / 12.0); - } - - // x moves in y seconds (+ z increment) - else - { - optScale = std::min((0.88 + ply / 116.4) / mtg, - 0.88 * limits.time[us] / double(timeLeft)); - maxScale = std::min(6.3, 1.5 + 0.11 * mtg); - } - - // Never use more than 80% of the available time for this move - optimumTime = TimePoint(optScale * timeLeft); - maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, maxScale * optimumTime)) - 10; - - if (Options["Ponder"]) - optimumTime += optimumTime / 4; + maxScale = std::min(7.0, 4.0 + ply / 12.0); + } + + // x moves in y seconds (+ z increment) + else + { + optScale = std::min((0.88 + ply / 116.4) / mtg, 0.88 * limits.time[us] / double(timeLeft)); + maxScale = std::min(6.3, 1.5 + 0.11 * mtg); + } + + // Never use more than 80% of the available time for this move + optimumTime = TimePoint(optScale * timeLeft); + maximumTime = + TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, maxScale * optimumTime)) - 10; + + if (Options["Ponder"]) + optimumTime += optimumTime / 4; } -} // namespace Stockfish +} // namespace Stockfish diff --git a/src/timeman.h b/src/timeman.h index 6acdf0ac6db..4b9b62bd2a9 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -32,23 +32,24 @@ namespace Stockfish { // the maximum available time, the game move number, and other parameters. class TimeManagement { -public: - void init(Search::LimitsType& limits, Color us, int ply); - TimePoint optimum() const { return optimumTime; } - TimePoint maximum() const { return maximumTime; } - TimePoint elapsed() const { return Search::Limits.npmsec ? - TimePoint(Threads.nodes_searched()) : now() - startTime; } - - int64_t availableNodes; // When in 'nodes as time' mode - -private: - TimePoint startTime; - TimePoint optimumTime; - TimePoint maximumTime; + public: + void init(Search::LimitsType& limits, Color us, int ply); + TimePoint optimum() const { return optimumTime; } + TimePoint maximum() const { return maximumTime; } + TimePoint elapsed() const { + return Search::Limits.npmsec ? TimePoint(Threads.nodes_searched()) : now() - startTime; + } + + int64_t availableNodes; // When in 'nodes as time' mode + + private: + TimePoint startTime; + TimePoint optimumTime; + TimePoint maximumTime; }; extern TimeManagement Time; -} // namespace Stockfish +} // namespace Stockfish -#endif // #ifndef TIMEMAN_H_INCLUDED +#endif // #ifndef TIMEMAN_H_INCLUDED diff --git a/src/tt.cpp b/src/tt.cpp index c3aec8d3e7c..a3ad0a78d4f 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -31,31 +31,29 @@ namespace Stockfish { -TranspositionTable TT; // Our global transposition table +TranspositionTable TT; // Our global transposition table // TTEntry::save() populates the TTEntry with a new node's data, possibly // overwriting an old position. The update is not atomic and can be racy. void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) { - // Preserve any existing move for the same position - if (m || uint16_t(k) != key16) - move16 = uint16_t(m); - - // Overwrite less valuable entries (cheapest checks first) - if ( b == BOUND_EXACT - || uint16_t(k) != key16 - || d - DEPTH_OFFSET + 2 * pv > depth8 - 4) - { - assert(d > DEPTH_OFFSET); - assert(d < 256 + DEPTH_OFFSET); - - key16 = uint16_t(k); - depth8 = uint8_t(d - DEPTH_OFFSET); - genBound8 = uint8_t(TT.generation8 | uint8_t(pv) << 2 | b); - value16 = int16_t(v); - eval16 = int16_t(ev); - } + // Preserve any existing move for the same position + if (m || uint16_t(k) != key16) + move16 = uint16_t(m); + + // Overwrite less valuable entries (cheapest checks first) + if (b == BOUND_EXACT || uint16_t(k) != key16 || d - DEPTH_OFFSET + 2 * pv > depth8 - 4) + { + assert(d > DEPTH_OFFSET); + assert(d < 256 + DEPTH_OFFSET); + + key16 = uint16_t(k); + depth8 = uint8_t(d - DEPTH_OFFSET); + genBound8 = uint8_t(TT.generation8 | uint8_t(pv) << 2 | b); + value16 = int16_t(v); + eval16 = int16_t(ev); + } } @@ -65,21 +63,20 @@ void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) void TranspositionTable::resize(size_t mbSize) { - Threads.main()->wait_for_search_finished(); + Threads.main()->wait_for_search_finished(); - aligned_large_pages_free(table); + aligned_large_pages_free(table); - clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster); + clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster); - table = static_cast(aligned_large_pages_alloc(clusterCount * sizeof(Cluster))); - if (!table) - { - std::cerr << "Failed to allocate " << mbSize - << "MB for transposition table." << std::endl; - exit(EXIT_FAILURE); - } + table = static_cast(aligned_large_pages_alloc(clusterCount * sizeof(Cluster))); + if (!table) + { + std::cerr << "Failed to allocate " << mbSize << "MB for transposition table." << std::endl; + exit(EXIT_FAILURE); + } - clear(); + clear(); } @@ -88,28 +85,27 @@ void TranspositionTable::resize(size_t mbSize) { void TranspositionTable::clear() { - std::vector threads; + std::vector threads; - for (size_t idx = 0; idx < size_t(Options["Threads"]); ++idx) - { - threads.emplace_back([this, idx]() { + for (size_t idx = 0; idx < size_t(Options["Threads"]); ++idx) + { + threads.emplace_back([this, idx]() { + // Thread binding gives faster search on systems with a first-touch policy + if (Options["Threads"] > 8) + WinProcGroup::bindThisThread(idx); - // Thread binding gives faster search on systems with a first-touch policy - if (Options["Threads"] > 8) - WinProcGroup::bindThisThread(idx); + // Each thread will zero its part of the hash table + const size_t stride = size_t(clusterCount / Options["Threads"]), + start = size_t(stride * idx), + len = + idx != size_t(Options["Threads"]) - 1 ? stride : clusterCount - start; - // Each thread will zero its part of the hash table - const size_t stride = size_t(clusterCount / Options["Threads"]), - start = size_t(stride * idx), - len = idx != size_t(Options["Threads"]) - 1 ? - stride : clusterCount - start; + std::memset(&table[start], 0, len * sizeof(Cluster)); + }); + } - std::memset(&table[start], 0, len * sizeof(Cluster)); - }); - } - - for (std::thread& th : threads) - th.join(); + for (std::thread& th : threads) + th.join(); } @@ -122,30 +118,33 @@ void TranspositionTable::clear() { TTEntry* TranspositionTable::probe(const Key key, bool& found) const { - TTEntry* const tte = first_entry(key); - const uint16_t key16 = uint16_t(key); // Use the low 16 bits as key inside the cluster - - for (int i = 0; i < ClusterSize; ++i) - if (tte[i].key16 == key16 || !tte[i].depth8) - { - tte[i].genBound8 = uint8_t(generation8 | (tte[i].genBound8 & (GENERATION_DELTA - 1))); // Refresh - - return found = bool(tte[i].depth8), &tte[i]; - } - - // Find an entry to be replaced according to the replacement strategy - TTEntry* replace = tte; - for (int i = 1; i < ClusterSize; ++i) - // Due to our packed storage format for generation and its cyclic - // nature we add GENERATION_CYCLE (256 is the modulus, plus what - // is needed to keep the unrelated lowest n bits from affecting - // the result) to calculate the entry age correctly even after - // generation8 overflows into the next cycle. - if ( replace->depth8 - ((GENERATION_CYCLE + generation8 - replace->genBound8) & GENERATION_MASK) - > tte[i].depth8 - ((GENERATION_CYCLE + generation8 - tte[i].genBound8) & GENERATION_MASK)) - replace = &tte[i]; - - return found = false, replace; + TTEntry* const tte = first_entry(key); + const uint16_t key16 = uint16_t(key); // Use the low 16 bits as key inside the cluster + + for (int i = 0; i < ClusterSize; ++i) + if (tte[i].key16 == key16 || !tte[i].depth8) + { + tte[i].genBound8 = + uint8_t(generation8 | (tte[i].genBound8 & (GENERATION_DELTA - 1))); // Refresh + + return found = bool(tte[i].depth8), &tte[i]; + } + + // Find an entry to be replaced according to the replacement strategy + TTEntry* replace = tte; + for (int i = 1; i < ClusterSize; ++i) + // Due to our packed storage format for generation and its cyclic + // nature we add GENERATION_CYCLE (256 is the modulus, plus what + // is needed to keep the unrelated lowest n bits from affecting + // the result) to calculate the entry age correctly even after + // generation8 overflows into the next cycle. + if (replace->depth8 + - ((GENERATION_CYCLE + generation8 - replace->genBound8) & GENERATION_MASK) + > tte[i].depth8 + - ((GENERATION_CYCLE + generation8 - tte[i].genBound8) & GENERATION_MASK)) + replace = &tte[i]; + + return found = false, replace; } @@ -154,12 +153,13 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const { int TranspositionTable::hashfull() const { - int cnt = 0; - for (int i = 0; i < 1000; ++i) - for (int j = 0; j < ClusterSize; ++j) - cnt += table[i].entry[j].depth8 && (table[i].entry[j].genBound8 & GENERATION_MASK) == generation8; + int cnt = 0; + for (int i = 0; i < 1000; ++i) + for (int j = 0; j < ClusterSize; ++j) + cnt += table[i].entry[j].depth8 + && (table[i].entry[j].genBound8 & GENERATION_MASK) == generation8; - return cnt / ClusterSize; + return cnt / ClusterSize; } -} // namespace Stockfish +} // namespace Stockfish diff --git a/src/tt.h b/src/tt.h index fdea4933507..628dfba28f7 100644 --- a/src/tt.h +++ b/src/tt.h @@ -40,23 +40,23 @@ namespace Stockfish { 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 + 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); - -private: - friend class TranspositionTable; - - uint16_t key16; - uint8_t depth8; - uint8_t genBound8; - uint16_t move16; - int16_t value16; - int16_t eval16; + Move move() const { return Move(move16); } + Value value() const { return Value(value16); } + Value eval() const { return Value(eval16); } + 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); + + private: + friend class TranspositionTable; + + uint16_t key16; + uint8_t depth8; + uint8_t genBound8; + uint16_t move16; + int16_t value16; + int16_t eval16; }; @@ -68,43 +68,45 @@ struct TTEntry { class TranspositionTable { - static constexpr int ClusterSize = 3; - - struct Cluster { - TTEntry entry[ClusterSize]; - char padding[2]; // Pad to 32 bytes - }; - - static_assert(sizeof(Cluster) == 32, "Unexpected Cluster size"); - - // Constants used to refresh the hash table periodically - static constexpr unsigned GENERATION_BITS = 3; // nb of bits reserved for other things - static constexpr int GENERATION_DELTA = (1 << GENERATION_BITS); // increment for generation field - static constexpr int GENERATION_CYCLE = 255 + (1 << GENERATION_BITS); // cycle length - static constexpr int GENERATION_MASK = (0xFF << GENERATION_BITS) & 0xFF; // mask to pull out generation number - -public: - ~TranspositionTable() { aligned_large_pages_free(table); } - void new_search() { generation8 += GENERATION_DELTA; } // Lower bits are used for other things - TTEntry* probe(const Key key, bool& found) const; - int hashfull() const; - void resize(size_t mbSize); - void clear(); - - TTEntry* first_entry(const Key key) const { - return &table[mul_hi64(key, clusterCount)].entry[0]; - } - -private: - friend struct TTEntry; - - size_t clusterCount; - Cluster* table; - uint8_t generation8; // Size must be not bigger than TTEntry::genBound8 + static constexpr int ClusterSize = 3; + + struct Cluster { + TTEntry entry[ClusterSize]; + char padding[2]; // Pad to 32 bytes + }; + + static_assert(sizeof(Cluster) == 32, "Unexpected Cluster size"); + + // Constants used to refresh the hash table periodically + static constexpr unsigned GENERATION_BITS = 3; // nb of bits reserved for other things + static constexpr int GENERATION_DELTA = + (1 << GENERATION_BITS); // increment for generation field + static constexpr int GENERATION_CYCLE = 255 + (1 << GENERATION_BITS); // cycle length + static constexpr int GENERATION_MASK = + (0xFF << GENERATION_BITS) & 0xFF; // mask to pull out generation number + + public: + ~TranspositionTable() { aligned_large_pages_free(table); } + void new_search() { generation8 += GENERATION_DELTA; } // Lower bits are used for other things + TTEntry* probe(const Key key, bool& found) const; + int hashfull() const; + void resize(size_t mbSize); + void clear(); + + TTEntry* first_entry(const Key key) const { + return &table[mul_hi64(key, clusterCount)].entry[0]; + } + + private: + friend struct TTEntry; + + size_t clusterCount; + Cluster* table; + uint8_t generation8; // Size must be not bigger than TTEntry::genBound8 }; extern TranspositionTable TT; -} // namespace Stockfish +} // namespace Stockfish -#endif // #ifndef TT_H_INCLUDED +#endif // #ifndef TT_H_INCLUDED diff --git a/src/tune.cpp b/src/tune.cpp index 97baeb784e9..cf80b9d7b70 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -34,75 +34,84 @@ using std::string; namespace Stockfish { -bool Tune::update_on_last; -const UCI::Option* LastOption = nullptr; +bool Tune::update_on_last; +const UCI::Option* LastOption = nullptr; static std::map TuneResults; string Tune::next(string& names, bool pop) { - string name; + string name; - do { - string token = names.substr(0, names.find(',')); + do + { + string token = names.substr(0, names.find(',')); - if (pop) - names.erase(0, token.size() + 1); + if (pop) + names.erase(0, token.size() + 1); - std::stringstream ws(token); - name += (ws >> token, token); // Remove trailing whitespace + std::stringstream ws(token); + name += (ws >> token, token); // Remove trailing whitespace - } while ( std::count(name.begin(), name.end(), '(') - - std::count(name.begin(), name.end(), ')')); + } while (std::count(name.begin(), name.end(), '(') - std::count(name.begin(), name.end(), ')')); - return name; + return name; } static void on_tune(const UCI::Option& o) { - if (!Tune::update_on_last || LastOption == &o) - Tune::read_options(); + if (!Tune::update_on_last || LastOption == &o) + Tune::read_options(); } static void make_option(const string& n, int v, const SetRange& r) { - // Do not generate option when there is nothing to tune (ie. min = max) - if (r(v).first == r(v).second) - return; + // Do not generate option when there is nothing to tune (ie. min = max) + if (r(v).first == r(v).second) + return; - if (TuneResults.count(n)) - v = TuneResults[n]; + if (TuneResults.count(n)) + v = TuneResults[n]; - Options[n] << UCI::Option(v, r(v).first, r(v).second, on_tune); - LastOption = &Options[n]; + Options[n] << UCI::Option(v, r(v).first, r(v).second, on_tune); + LastOption = &Options[n]; - // Print formatted parameters, ready to be copy-pasted in Fishtest - std::cout << n << "," - << v << "," - << r(v).first << "," << r(v).second << "," - << (r(v).second - r(v).first) / 20.0 << "," - << "0.0020" - << std::endl; + // Print formatted parameters, ready to be copy-pasted in Fishtest + std::cout << n << "," << v << "," << r(v).first << "," << r(v).second << "," + << (r(v).second - r(v).first) / 20.0 << "," + << "0.0020" << std::endl; } -template<> void Tune::Entry::init_option() { make_option(name, value, range); } +template<> +void Tune::Entry::init_option() { + make_option(name, value, range); +} -template<> void Tune::Entry::read_option() { - if (Options.count(name)) - value = int(Options[name]); +template<> +void Tune::Entry::read_option() { + if (Options.count(name)) + value = int(Options[name]); } -template<> void Tune::Entry::init_option() { make_option(name, value, range); } +template<> +void Tune::Entry::init_option() { + make_option(name, value, range); +} -template<> void Tune::Entry::read_option() { - if (Options.count(name)) - value = Value(int(Options[name])); +template<> +void Tune::Entry::read_option() { + if (Options.count(name)) + value = Value(int(Options[name])); } // Instead of a variable here we have a PostUpdate function: just call it -template<> void Tune::Entry::init_option() {} -template<> void Tune::Entry::read_option() { value(); } +template<> +void Tune::Entry::init_option() {} +template<> +void Tune::Entry::read_option() { + value(); +} -} // namespace Stockfish +} // namespace Stockfish // Init options with tuning session results instead of default values. Useful to @@ -117,9 +126,7 @@ template<> void Tune::Entry::read_option() { value(); } namespace Stockfish { -void Tune::read_results() { - - /* ...insert your values here... */ +void Tune::read_results() { /* ...insert your values here... */ } -} // namespace Stockfish +} // namespace Stockfish diff --git a/src/tune.h b/src/tune.h index a9a7331e566..480aea165b5 100644 --- a/src/tune.h +++ b/src/tune.h @@ -22,28 +22,29 @@ #include #include #include -#include // IWYU pragma: keep +#include // IWYU pragma: keep #include #include namespace Stockfish { enum Value : int; -using Range = std::pair; // Option's min-max values -using RangeFun = Range (int); +using Range = std::pair; // Option's min-max values +using RangeFun = Range(int); // Default Range function, to calculate Option's min-max values -inline Range default_range(int v) { - return v > 0 ? Range(0, 2 * v) : Range(2 * v, 0); -} +inline Range default_range(int v) { return v > 0 ? Range(0, 2 * v) : Range(2 * v, 0); } struct SetRange { - explicit SetRange(RangeFun f) : fun(f) {} - SetRange(int min, int max) : fun(nullptr), range(min, max) {} - Range operator()(int v) const { return fun ? fun(v) : range; } - - RangeFun* fun; - Range range; + explicit SetRange(RangeFun f) : + fun(f) {} + SetRange(int min, int max) : + fun(nullptr), + range(min, max) {} + Range operator()(int v) const { return fun ? fun(v) : range; } + + RangeFun* fun; + Range range; }; #define SetDefaultRange SetRange(default_range) @@ -76,88 +77,102 @@ struct SetRange { class Tune { - using PostUpdate = void (); // Post-update function - - Tune() { read_results(); } - Tune(const Tune&) = delete; - void operator=(const Tune&) = delete; - void read_results(); - - static Tune& instance() { static Tune t; return t; } // Singleton - - // Use polymorphism to accommodate Entry of different types in the same vector - struct EntryBase { - virtual ~EntryBase() = default; - virtual void init_option() = 0; - virtual void read_option() = 0; - }; - - template - struct Entry : public EntryBase { - - static_assert(!std::is_const_v, "Parameter cannot be const!"); - - static_assert( std::is_same_v - || std::is_same_v - || std::is_same_v, "Parameter type not supported!"); - - Entry(const std::string& n, T& v, const SetRange& r) : name(n), value(v), range(r) {} - void operator=(const Entry&) = delete; // Because 'value' is a reference - void init_option() override; - void read_option() override; - - std::string name; - T& value; - SetRange range; - }; - - // Our facility to fill the container, each Entry corresponds to a parameter - // to tune. We use variadic templates to deal with an unspecified number of - // entries, each one of a possible different type. - static std::string next(std::string& names, bool pop = true); - - int add(const SetRange&, std::string&&) { return 0; } - - template - int add(const SetRange& range, std::string&& names, T& value, Args&&... args) { - list.push_back(std::unique_ptr(new Entry(next(names), value, range))); - return add(range, std::move(names), args...); - } - - // Template specialization for arrays: recursively handle multi-dimensional arrays - template - int add(const SetRange& range, std::string&& names, T (&value)[N], Args&&... args) { - for (size_t i = 0; i < N; i++) - add(range, next(names, i == N - 1) + "[" + std::to_string(i) + "]", value[i]); - return add(range, std::move(names), args...); - } - - // Template specialization for SetRange - template - int add(const SetRange&, std::string&& names, SetRange& value, Args&&... args) { - return add(value, (next(names), std::move(names)), args...); - } - - std::vector> list; - -public: - template - static int add(const std::string& names, Args&&... args) { - return instance().add(SetDefaultRange, names.substr(1, names.size() - 2), args...); // Remove trailing parenthesis - } - static void init() { for (auto& e : instance().list) e->init_option(); read_options(); } // Deferred, due to UCI::Options access - static void read_options() { for (auto& e : instance().list) e->read_option(); } - static bool update_on_last; + using PostUpdate = void(); // Post-update function + + Tune() { read_results(); } + Tune(const Tune&) = delete; + void operator=(const Tune&) = delete; + void read_results(); + + static Tune& instance() { + static Tune t; + return t; + } // Singleton + + // Use polymorphism to accommodate Entry of different types in the same vector + struct EntryBase { + virtual ~EntryBase() = default; + virtual void init_option() = 0; + virtual void read_option() = 0; + }; + + template + struct Entry: public EntryBase { + + static_assert(!std::is_const_v, "Parameter cannot be const!"); + + static_assert(std::is_same_v || std::is_same_v + || std::is_same_v, + "Parameter type not supported!"); + + Entry(const std::string& n, T& v, const SetRange& r) : + name(n), + value(v), + range(r) {} + void operator=(const Entry&) = delete; // Because 'value' is a reference + void init_option() override; + void read_option() override; + + std::string name; + T& value; + SetRange range; + }; + + // Our facility to fill the container, each Entry corresponds to a parameter + // to tune. We use variadic templates to deal with an unspecified number of + // entries, each one of a possible different type. + static std::string next(std::string& names, bool pop = true); + + int add(const SetRange&, std::string&&) { return 0; } + + template + int add(const SetRange& range, std::string&& names, T& value, Args&&... args) { + list.push_back(std::unique_ptr(new Entry(next(names), value, range))); + return add(range, std::move(names), args...); + } + + // Template specialization for arrays: recursively handle multi-dimensional arrays + template + int add(const SetRange& range, std::string&& names, T (&value)[N], Args&&... args) { + for (size_t i = 0; i < N; i++) + add(range, next(names, i == N - 1) + "[" + std::to_string(i) + "]", value[i]); + return add(range, std::move(names), args...); + } + + // Template specialization for SetRange + template + int add(const SetRange&, std::string&& names, SetRange& value, Args&&... args) { + return add(value, (next(names), std::move(names)), args...); + } + + std::vector> list; + + public: + template + static int add(const std::string& names, Args&&... args) { + return instance().add(SetDefaultRange, names.substr(1, names.size() - 2), + args...); // Remove trailing parenthesis + } + static void init() { + for (auto& e : instance().list) + e->init_option(); + read_options(); + } // Deferred, due to UCI::Options access + static void read_options() { + for (auto& e : instance().list) + e->read_option(); + } + static bool update_on_last; }; // Some macro magic :-) we define a dummy int variable that the compiler initializes calling Tune::add() #define STRINGIFY(x) #x -#define UNIQUE2(x, y) x ## y -#define UNIQUE(x, y) UNIQUE2(x, y) // Two indirection levels to expand __LINE__ +#define UNIQUE2(x, y) x##y +#define UNIQUE(x, y) UNIQUE2(x, y) // Two indirection levels to expand __LINE__ #define TUNE(...) int UNIQUE(p, __LINE__) = Tune::add(STRINGIFY((__VA_ARGS__)), __VA_ARGS__) #define UPDATE_ON_LAST() bool UNIQUE(p, __LINE__) = Tune::update_on_last = true -} // namespace Stockfish +} // namespace Stockfish -#endif // #ifndef TUNE_H_INCLUDED +#endif // #ifndef TUNE_H_INCLUDED diff --git a/src/types.h b/src/types.h index 1fc4d33a95f..c76efd077d1 100644 --- a/src/types.h +++ b/src/types.h @@ -17,7 +17,7 @@ */ #ifndef TYPES_H_INCLUDED -#define TYPES_H_INCLUDED + #define TYPES_H_INCLUDED // When compiling with provided Makefile (e.g. for Linux and OSX), configuration // is done automatically. To get started type 'make help'. @@ -36,15 +36,15 @@ // -DUSE_PEXT | Add runtime support for use of pext asm-instruction. Works // | only in 64-bit mode and requires hardware with pext support. -#include -#include + #include + #include -#if defined(_MSC_VER) -// Disable some silly and noisy warnings from MSVC compiler -#pragma warning(disable: 4127) // Conditional expression is constant -#pragma warning(disable: 4146) // Unary minus operator applied to unsigned type -#pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false' -#endif + #if defined(_MSC_VER) + // Disable some silly and noisy warnings from MSVC compiler + #pragma warning(disable: 4127) // Conditional expression is constant + #pragma warning(disable: 4146) // Unary minus operator applied to unsigned type + #pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false' + #endif // Predefined macros hell: // @@ -55,53 +55,54 @@ // _WIN32 Building on Windows (any) // _WIN64 Building on Windows 64 bit -#if defined(__GNUC__ ) && (__GNUC__ < 9 || (__GNUC__ == 9 && __GNUC_MINOR__ <= 2)) && defined(_WIN32) && !defined(__clang__) -#define ALIGNAS_ON_STACK_VARIABLES_BROKEN -#endif + #if defined(__GNUC__) && (__GNUC__ < 9 || (__GNUC__ == 9 && __GNUC_MINOR__ <= 2)) \ + && defined(_WIN32) && !defined(__clang__) + #define ALIGNAS_ON_STACK_VARIABLES_BROKEN + #endif -#define ASSERT_ALIGNED(ptr, alignment) assert(reinterpret_cast(ptr) % alignment == 0) + #define ASSERT_ALIGNED(ptr, alignment) assert(reinterpret_cast(ptr) % alignment == 0) -#if defined(_WIN64) && defined(_MSC_VER) // No Makefile used -# include // Microsoft header for _BitScanForward64() -# define IS_64BIT -#endif + #if defined(_WIN64) && defined(_MSC_VER) // No Makefile used + #include // Microsoft header for _BitScanForward64() + #define IS_64BIT + #endif -#if defined(USE_POPCNT) && defined(_MSC_VER) -# include // Microsoft header for _mm_popcnt_u64() -#endif + #if defined(USE_POPCNT) && defined(_MSC_VER) + #include // Microsoft header for _mm_popcnt_u64() + #endif -#if !defined(NO_PREFETCH) && defined(_MSC_VER) -# include // Microsoft header for _mm_prefetch() -#endif + #if !defined(NO_PREFETCH) && defined(_MSC_VER) + #include // Microsoft header for _mm_prefetch() + #endif -#if defined(USE_PEXT) -# include // Header for _pext_u64() intrinsic -# define pext(b, m) _pext_u64(b, m) -#else -# define pext(b, m) 0 -#endif + #if defined(USE_PEXT) + #include // Header for _pext_u64() intrinsic + #define pext(b, m) _pext_u64(b, m) + #else + #define pext(b, m) 0 + #endif namespace Stockfish { -#ifdef USE_POPCNT + #ifdef USE_POPCNT constexpr bool HasPopCnt = true; -#else + #else constexpr bool HasPopCnt = false; -#endif + #endif -#ifdef USE_PEXT + #ifdef USE_PEXT constexpr bool HasPext = true; -#else + #else constexpr bool HasPext = false; -#endif + #endif -#ifdef IS_64BIT + #ifdef IS_64BIT constexpr bool Is64Bit = true; -#else + #else constexpr bool Is64Bit = false; -#endif + #endif -using Key = uint64_t; +using Key = uint64_t; using Bitboard = uint64_t; constexpr int MAX_MOVES = 256; @@ -120,164 +121,187 @@ constexpr int MAX_PLY = 246; // while MOVE_NONE and MOVE_NULL have the same origin and destination square. enum Move : int { - MOVE_NONE, - MOVE_NULL = 65 + MOVE_NONE, + MOVE_NULL = 65 }; enum MoveType { - NORMAL, - PROMOTION = 1 << 14, - EN_PASSANT = 2 << 14, - CASTLING = 3 << 14 + NORMAL, + PROMOTION = 1 << 14, + EN_PASSANT = 2 << 14, + CASTLING = 3 << 14 }; enum Color { - WHITE, BLACK, COLOR_NB = 2 + WHITE, + BLACK, + COLOR_NB = 2 }; enum CastlingRights { - NO_CASTLING, - WHITE_OO, - WHITE_OOO = WHITE_OO << 1, - BLACK_OO = WHITE_OO << 2, - BLACK_OOO = WHITE_OO << 3, - - 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 + NO_CASTLING, + WHITE_OO, + WHITE_OOO = WHITE_OO << 1, + BLACK_OO = WHITE_OO << 2, + BLACK_OOO = WHITE_OO << 3, + + 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 }; enum Bound { - BOUND_NONE, - BOUND_UPPER, - BOUND_LOWER, - BOUND_EXACT = BOUND_UPPER | BOUND_LOWER + BOUND_NONE, + BOUND_UPPER, + BOUND_LOWER, + BOUND_EXACT = BOUND_UPPER | BOUND_LOWER }; enum Value : int { - VALUE_ZERO = 0, - VALUE_DRAW = 0, - VALUE_MATE = 32000, - VALUE_INFINITE = 32001, - VALUE_NONE = 32002, - - VALUE_TB_WIN_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY, - VALUE_TB_LOSS_IN_MAX_PLY = -VALUE_TB_WIN_IN_MAX_PLY, - VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY, - VALUE_MATED_IN_MAX_PLY = -VALUE_MATE_IN_MAX_PLY, - - // In the code, we make the assumption that these values - // are such that non_pawn_material() can be used to uniquely - // identify the material on the board. - PawnValue = 208, - KnightValue = 781, - BishopValue = 825, - RookValue = 1276, - QueenValue = 2538, + VALUE_ZERO = 0, + VALUE_DRAW = 0, + VALUE_MATE = 32000, + VALUE_INFINITE = 32001, + VALUE_NONE = 32002, + + VALUE_TB_WIN_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY, + VALUE_TB_LOSS_IN_MAX_PLY = -VALUE_TB_WIN_IN_MAX_PLY, + VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY, + VALUE_MATED_IN_MAX_PLY = -VALUE_MATE_IN_MAX_PLY, + + // In the code, we make the assumption that these values + // are such that non_pawn_material() can be used to uniquely + // identify the material on the board. + PawnValue = 208, + KnightValue = 781, + BishopValue = 825, + RookValue = 1276, + QueenValue = 2538, }; +// clang-format off enum PieceType { - NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING, - ALL_PIECES = 0, - PIECE_TYPE_NB = 8 + NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING, + ALL_PIECES = 0, + PIECE_TYPE_NB = 8 }; enum Piece { - NO_PIECE, - W_PAWN = PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, - B_PAWN = PAWN + 8, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING, - PIECE_NB = 16 + NO_PIECE, + W_PAWN = PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, + B_PAWN = PAWN + 8, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING, + PIECE_NB = 16 }; +// clang-format on -constexpr Value PieceValue[PIECE_NB] = { VALUE_ZERO, PawnValue, KnightValue, BishopValue, RookValue, QueenValue, VALUE_ZERO, VALUE_ZERO, - VALUE_ZERO, PawnValue, KnightValue, BishopValue, RookValue, QueenValue, VALUE_ZERO, VALUE_ZERO }; +constexpr Value PieceValue[PIECE_NB] = { + VALUE_ZERO, PawnValue, KnightValue, BishopValue, RookValue, QueenValue, VALUE_ZERO, VALUE_ZERO, + VALUE_ZERO, PawnValue, KnightValue, BishopValue, RookValue, QueenValue, VALUE_ZERO, VALUE_ZERO}; using Depth = int; enum : int { - DEPTH_QS_CHECKS = 0, - DEPTH_QS_NO_CHECKS = -1, - DEPTH_QS_RECAPTURES = -5, + DEPTH_QS_CHECKS = 0, + DEPTH_QS_NO_CHECKS = -1, + DEPTH_QS_RECAPTURES = -5, - DEPTH_NONE = -6, + DEPTH_NONE = -6, - DEPTH_OFFSET = -7 // value used only for TT entry occupancy check + DEPTH_OFFSET = -7 // value used only for TT entry occupancy check }; +// clang-format off 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, - SQ_A3, SQ_B3, SQ_C3, SQ_D3, SQ_E3, SQ_F3, SQ_G3, SQ_H3, - SQ_A4, SQ_B4, SQ_C4, SQ_D4, SQ_E4, SQ_F4, SQ_G4, SQ_H4, - SQ_A5, SQ_B5, SQ_C5, SQ_D5, SQ_E5, SQ_F5, SQ_G5, SQ_H5, - SQ_A6, SQ_B6, SQ_C6, SQ_D6, SQ_E6, SQ_F6, SQ_G6, SQ_H6, - SQ_A7, SQ_B7, SQ_C7, SQ_D7, SQ_E7, SQ_F7, SQ_G7, SQ_H7, - SQ_A8, SQ_B8, SQ_C8, SQ_D8, SQ_E8, SQ_F8, SQ_G8, SQ_H8, - SQ_NONE, - - SQUARE_ZERO = 0, - SQUARE_NB = 64 + 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, + SQ_A3, SQ_B3, SQ_C3, SQ_D3, SQ_E3, SQ_F3, SQ_G3, SQ_H3, + SQ_A4, SQ_B4, SQ_C4, SQ_D4, SQ_E4, SQ_F4, SQ_G4, SQ_H4, + SQ_A5, SQ_B5, SQ_C5, SQ_D5, SQ_E5, SQ_F5, SQ_G5, SQ_H5, + SQ_A6, SQ_B6, SQ_C6, SQ_D6, SQ_E6, SQ_F6, SQ_G6, SQ_H6, + SQ_A7, SQ_B7, SQ_C7, SQ_D7, SQ_E7, SQ_F7, SQ_G7, SQ_H7, + SQ_A8, SQ_B8, SQ_C8, SQ_D8, SQ_E8, SQ_F8, SQ_G8, SQ_H8, + SQ_NONE, + + SQUARE_ZERO = 0, + SQUARE_NB = 64 }; +// clang-format on enum Direction : int { - NORTH = 8, - EAST = 1, - SOUTH = -NORTH, - WEST = -EAST, - - NORTH_EAST = NORTH + EAST, - SOUTH_EAST = SOUTH + EAST, - SOUTH_WEST = SOUTH + WEST, - NORTH_WEST = NORTH + WEST + NORTH = 8, + EAST = 1, + SOUTH = -NORTH, + WEST = -EAST, + + NORTH_EAST = NORTH + EAST, + SOUTH_EAST = SOUTH + EAST, + SOUTH_WEST = SOUTH + WEST, + NORTH_WEST = NORTH + WEST }; enum File : int { - FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H, FILE_NB + FILE_A, + FILE_B, + FILE_C, + FILE_D, + FILE_E, + FILE_F, + FILE_G, + FILE_H, + FILE_NB }; enum Rank : int { - RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8, RANK_NB + RANK_1, + RANK_2, + RANK_3, + RANK_4, + RANK_5, + RANK_6, + RANK_7, + RANK_8, + RANK_NB }; // Keep track of what a move changes on the board (used by NNUE) struct DirtyPiece { - // Number of changed pieces - int dirty_num; + // Number of changed pieces + int dirty_num; - // Max 3 pieces can change in one move. A promotion with capture moves - // both the pawn and the captured piece to SQ_NONE and the piece promoted - // to from SQ_NONE to the capture square. - Piece piece[3]; + // Max 3 pieces can change in one move. A promotion with capture moves + // both the pawn and the captured piece to SQ_NONE and the piece promoted + // to from SQ_NONE to the capture square. + Piece piece[3]; - // From and to squares, which may be SQ_NONE - Square from[3]; - Square to[3]; + // From and to squares, which may be SQ_NONE + Square from[3]; + Square to[3]; }; -#define ENABLE_BASE_OPERATORS_ON(T) \ -constexpr T operator+(T d1, int d2) { return T(int(d1) + d2); } \ -constexpr T operator-(T d1, int d2) { return T(int(d1) - d2); } \ -constexpr T operator-(T d) { return T(-int(d)); } \ -inline T& operator+=(T& d1, int d2) { return d1 = d1 + d2; } \ -inline T& operator-=(T& d1, int d2) { return d1 = d1 - d2; } - -#define ENABLE_INCR_OPERATORS_ON(T) \ -inline T& operator++(T& d) { return d = T(int(d) + 1); } \ -inline T& operator--(T& d) { return d = T(int(d) - 1); } - -#define ENABLE_FULL_OPERATORS_ON(T) \ -ENABLE_BASE_OPERATORS_ON(T) \ -constexpr T operator*(int i, T d) { return T(i * int(d)); } \ -constexpr T operator*(T d, int i) { return T(int(d) * i); } \ -constexpr T operator/(T d, int i) { return T(int(d) / i); } \ -constexpr int operator/(T d1, T d2) { return int(d1) / int(d2); } \ -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); } + #define ENABLE_BASE_OPERATORS_ON(T) \ + constexpr T operator+(T d1, int d2) { return T(int(d1) + d2); } \ + constexpr T operator-(T d1, int d2) { return T(int(d1) - d2); } \ + constexpr T operator-(T d) { return T(-int(d)); } \ + inline T& operator+=(T& d1, int d2) { return d1 = d1 + d2; } \ + inline T& operator-=(T& d1, int d2) { return d1 = d1 - d2; } + + #define ENABLE_INCR_OPERATORS_ON(T) \ + inline T& operator++(T& d) { return d = T(int(d) + 1); } \ + inline T& operator--(T& d) { return d = T(int(d) - 1); } + + #define ENABLE_FULL_OPERATORS_ON(T) \ + ENABLE_BASE_OPERATORS_ON(T) \ + constexpr T operator*(int i, T d) { return T(i * int(d)); } \ + constexpr T operator*(T d, int i) { return T(int(d) * i); } \ + constexpr T operator/(T d, int i) { return T(int(d) / i); } \ + constexpr int operator/(T d1, T d2) { return int(d1) / int(d2); } \ + 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(Direction) @@ -287,131 +311,97 @@ ENABLE_INCR_OPERATORS_ON(Square) ENABLE_INCR_OPERATORS_ON(File) ENABLE_INCR_OPERATORS_ON(Rank) -#undef ENABLE_FULL_OPERATORS_ON -#undef ENABLE_INCR_OPERATORS_ON -#undef ENABLE_BASE_OPERATORS_ON + #undef ENABLE_FULL_OPERATORS_ON + #undef ENABLE_INCR_OPERATORS_ON + #undef ENABLE_BASE_OPERATORS_ON // Additional operators to add a Direction to a Square constexpr Square operator+(Square s, Direction d) { return Square(int(s) + int(d)); } constexpr Square operator-(Square s, Direction d) { return Square(int(s) - int(d)); } -inline Square& operator+=(Square& s, Direction d) { return s = s + d; } -inline Square& operator-=(Square& s, Direction d) { return s = s - d; } +inline Square& operator+=(Square& s, Direction d) { return s = s + d; } +inline Square& operator-=(Square& s, Direction d) { return s = s - d; } constexpr Color operator~(Color c) { - return Color(c ^ BLACK); // Toggle color + return Color(c ^ BLACK); // Toggle color } -constexpr Square flip_rank(Square s) { // Swap A1 <-> A8 - return Square(s ^ SQ_A8); +constexpr Square flip_rank(Square s) { // Swap A1 <-> A8 + return Square(s ^ SQ_A8); } -constexpr Square flip_file(Square s) { // Swap A1 <-> H1 - return Square(s ^ SQ_H1); +constexpr Square flip_file(Square s) { // Swap A1 <-> H1 + return Square(s ^ SQ_H1); } constexpr Piece operator~(Piece pc) { - return Piece(pc ^ 8); // Swap color of piece B_KNIGHT <-> W_KNIGHT + return Piece(pc ^ 8); // Swap color of piece B_KNIGHT <-> W_KNIGHT } constexpr CastlingRights operator&(Color c, CastlingRights cr) { - return CastlingRights((c == WHITE ? WHITE_CASTLING : BLACK_CASTLING) & cr); + return CastlingRights((c == WHITE ? WHITE_CASTLING : BLACK_CASTLING) & cr); } -constexpr Value mate_in(int ply) { - return VALUE_MATE - ply; -} +constexpr Value mate_in(int ply) { return VALUE_MATE - ply; } -constexpr Value mated_in(int ply) { - return -VALUE_MATE + ply; -} +constexpr Value mated_in(int ply) { return -VALUE_MATE + ply; } -constexpr Square make_square(File f, Rank r) { - return Square((r << 3) + f); -} +constexpr Square make_square(File f, Rank r) { return Square((r << 3) + f); } -constexpr Piece make_piece(Color c, PieceType pt) { - return Piece((c << 3) + pt); -} +constexpr Piece make_piece(Color c, PieceType pt) { return Piece((c << 3) + pt); } -constexpr PieceType type_of(Piece pc) { - return PieceType(pc & 7); -} +constexpr PieceType type_of(Piece pc) { return PieceType(pc & 7); } inline Color color_of(Piece pc) { - assert(pc != NO_PIECE); - return Color(pc >> 3); + assert(pc != NO_PIECE); + return Color(pc >> 3); } -constexpr bool is_ok(Move m) { - return m != MOVE_NONE && m != MOVE_NULL; -} +constexpr bool is_ok(Move m) { return m != MOVE_NONE && m != MOVE_NULL; } -constexpr bool is_ok(Square s) { - return s >= SQ_A1 && s <= SQ_H8; -} +constexpr bool is_ok(Square s) { return s >= SQ_A1 && s <= SQ_H8; } -constexpr File file_of(Square s) { - return File(s & 7); -} +constexpr File file_of(Square s) { return File(s & 7); } -constexpr Rank rank_of(Square s) { - return Rank(s >> 3); -} +constexpr Rank rank_of(Square s) { return Rank(s >> 3); } -constexpr Square relative_square(Color c, Square s) { - return Square(s ^ (c * 56)); -} +constexpr Square relative_square(Color c, Square s) { return Square(s ^ (c * 56)); } -constexpr Rank relative_rank(Color c, Rank r) { - return Rank(r ^ (c * 7)); -} +constexpr Rank relative_rank(Color c, Rank r) { return Rank(r ^ (c * 7)); } -constexpr Rank relative_rank(Color c, Square s) { - return relative_rank(c, rank_of(s)); -} +constexpr Rank relative_rank(Color c, Square s) { return relative_rank(c, rank_of(s)); } -constexpr Direction pawn_push(Color c) { - return c == WHITE ? NORTH : SOUTH; -} +constexpr Direction pawn_push(Color c) { return c == WHITE ? NORTH : SOUTH; } constexpr Square from_sq(Move m) { - assert(is_ok(m)); - return Square((m >> 6) & 0x3F); + assert(is_ok(m)); + return Square((m >> 6) & 0x3F); } constexpr Square to_sq(Move m) { - assert(is_ok(m)); - return Square(m & 0x3F); + assert(is_ok(m)); + return Square(m & 0x3F); } -constexpr int from_to(Move m) { - return m & 0xFFF; -} +constexpr int from_to(Move m) { return m & 0xFFF; } -constexpr MoveType type_of(Move m) { - return MoveType(m & (3 << 14)); -} +constexpr MoveType type_of(Move m) { return MoveType(m & (3 << 14)); } -constexpr PieceType promotion_type(Move m) { - return PieceType(((m >> 12) & 3) + KNIGHT); -} +constexpr PieceType promotion_type(Move m) { return PieceType(((m >> 12) & 3) + KNIGHT); } -constexpr Move make_move(Square from, Square to) { - return Move((from << 6) + to); -} +constexpr Move make_move(Square from, Square to) { return Move((from << 6) + to); } template constexpr Move make(Square from, Square to, PieceType pt = KNIGHT) { - return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to); + return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to); } // Based on a congruential pseudo-random number generator constexpr Key make_key(uint64_t seed) { - return seed * 6364136223846793005ULL + 1442695040888963407ULL; + return seed * 6364136223846793005ULL + 1442695040888963407ULL; } -} // namespace Stockfish +} // namespace Stockfish -#endif // #ifndef TYPES_H_INCLUDED +#endif // #ifndef TYPES_H_INCLUDED -#include "tune.h" // Global visibility to tuning setup +#include "tune.h" // Global visibility to tuning setup diff --git a/src/uci.cpp b/src/uci.cpp index 81bf7aff768..0671cb5ff65 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -45,18 +45,18 @@ namespace Stockfish { namespace { - // FEN string for the initial position in standard chess - const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; +// FEN string for the initial position in standard chess +const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; - // position() is called when the engine receives the "position" UCI command. - // It sets up the position that is described in the given FEN string ("fen") or - // the initial position ("startpos") and then makes the moves given in the following - // move list ("moves"). +// position() is called when the engine receives the "position" UCI command. +// It sets up the position that is described in the given FEN string ("fen") or +// the initial position ("startpos") and then makes the moves given in the following +// move list ("moves"). - void position(Position& pos, std::istringstream& is, StateListPtr& states) { +void position(Position& pos, std::istringstream& is, StateListPtr& states) { - Move m; + Move m; std::string token, fen; is >> token; @@ -64,7 +64,7 @@ namespace { if (token == "startpos") { fen = StartFEN; - is >> token; // Consume the "moves" token, if any + is >> token; // Consume the "moves" token, if any } else if (token == "fen") while (is >> token && token != "moves") @@ -72,7 +72,7 @@ namespace { else return; - states = StateListPtr(new std::deque(1)); // Drop the old state and create a new one + states = StateListPtr(new std::deque(1)); // Drop the old state and create a new one pos.set(fen, Options["UCI_Chess960"], &states->back(), Threads.main()); // Parse the move list, if any @@ -81,33 +81,33 @@ namespace { states->emplace_back(); pos.do_move(m, states->back()); } - } +} - // trace_eval() prints the evaluation of the current position, consistent with - // the UCI options set so far. +// trace_eval() prints the evaluation of the current position, consistent with +// the UCI options set so far. - void trace_eval(Position& pos) { +void trace_eval(Position& pos) { StateListPtr states(new std::deque(1)); - Position p; + Position p; p.set(pos.fen(), Options["UCI_Chess960"], &states->back(), Threads.main()); Eval::NNUE::verify(); sync_cout << "\n" << Eval::trace(p) << sync_endl; - } +} - // setoption() is called when the engine receives the "setoption" UCI command. - // The function updates the UCI option ("name") to the given value ("value"). +// setoption() is called when the engine receives the "setoption" UCI command. +// The function updates the UCI option ("name") to the given value ("value"). - void setoption(std::istringstream& is) { +void setoption(std::istringstream& is) { Threads.main()->wait_for_search_finished(); std::string token, name, value; - is >> token; // Consume the "name" token + is >> token; // Consume the "name" token // Read the option name (can contain spaces) while (is >> token && token != "value") @@ -121,54 +121,67 @@ namespace { Options[name] = value; else sync_cout << "No such option: " << name << sync_endl; - } +} - // go() is called when the engine receives the "go" UCI command. The function - // sets the thinking time and other parameters from the input string, then starts - // with a search. +// go() is called when the engine receives the "go" UCI command. The function +// sets the thinking time and other parameters from the input string, then starts +// with a search. - void go(Position& pos, std::istringstream& is, StateListPtr& states) { +void go(Position& pos, std::istringstream& is, StateListPtr& states) { Search::LimitsType limits; - std::string token; - bool ponderMode = false; + std::string token; + bool ponderMode = false; - limits.startTime = now(); // The search starts as early as possible + limits.startTime = now(); // The search starts as early as possible while (is >> token) - if (token == "searchmoves") // Needs to be the last command on the line + if (token == "searchmoves") // Needs to be the last command on the line while (is >> token) limits.searchmoves.push_back(UCI::to_move(pos, token)); - else if (token == "wtime") is >> limits.time[WHITE]; - else if (token == "btime") is >> limits.time[BLACK]; - else if (token == "winc") is >> limits.inc[WHITE]; - else if (token == "binc") is >> limits.inc[BLACK]; - else if (token == "movestogo") is >> limits.movestogo; - else if (token == "depth") is >> limits.depth; - else if (token == "nodes") is >> limits.nodes; - else if (token == "movetime") is >> limits.movetime; - else if (token == "mate") is >> limits.mate; - else if (token == "perft") is >> limits.perft; - else if (token == "infinite") limits.infinite = 1; - else if (token == "ponder") ponderMode = true; + else if (token == "wtime") + is >> limits.time[WHITE]; + else if (token == "btime") + is >> limits.time[BLACK]; + else if (token == "winc") + is >> limits.inc[WHITE]; + else if (token == "binc") + is >> limits.inc[BLACK]; + else if (token == "movestogo") + is >> limits.movestogo; + else if (token == "depth") + is >> limits.depth; + else if (token == "nodes") + is >> limits.nodes; + else if (token == "movetime") + is >> limits.movetime; + else if (token == "mate") + is >> limits.mate; + else if (token == "perft") + is >> limits.perft; + else if (token == "infinite") + limits.infinite = 1; + else if (token == "ponder") + ponderMode = true; Threads.start_thinking(pos, states, limits, ponderMode); - } +} - // bench() is called when the engine receives the "bench" command. - // First, a list of UCI commands is set up according to the bench - // parameters, then it is run one by one, printing a summary at the end. +// bench() is called when the engine receives the "bench" command. +// First, a list of UCI commands is set up according to the bench +// parameters, then it is run one by one, printing a summary at the end. - void bench(Position& pos, std::istream& args, StateListPtr& states) { +void bench(Position& pos, std::istream& args, StateListPtr& states) { std::string token; - uint64_t num, nodes = 0, cnt = 1; + uint64_t num, nodes = 0, cnt = 1; std::vector list = setup_bench(pos, args); - num = count_if(list.begin(), list.end(), [](const std::string& s) { return s.find("go ") == 0 || s.find("eval") == 0; }); + num = count_if(list.begin(), list.end(), + [](const std::string& s) { return s.find("go ") == 0 || s.find("eval") == 0; }); TimePoint elapsed = now(); @@ -179,58 +192,64 @@ namespace { if (token == "go" || token == "eval") { - std::cerr << "\nPosition: " << cnt++ << '/' << num << " (" << pos.fen() << ")" << std::endl; + std::cerr << "\nPosition: " << cnt++ << '/' << num << " (" << pos.fen() << ")" + << std::endl; if (token == "go") { - go(pos, is, states); - Threads.main()->wait_for_search_finished(); - nodes += Threads.nodes_searched(); + go(pos, is, states); + Threads.main()->wait_for_search_finished(); + nodes += Threads.nodes_searched(); } else - trace_eval(pos); + trace_eval(pos); } - else if (token == "setoption") setoption(is); - else if (token == "position") position(pos, is, states); - else if (token == "ucinewgame") { Search::clear(); elapsed = now(); } // Search::clear() may take a while + else if (token == "setoption") + setoption(is); + else if (token == "position") + position(pos, is, states); + else if (token == "ucinewgame") + { + Search::clear(); + elapsed = now(); + } // Search::clear() may take a while } - elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero' + elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero' dbg_print(); std::cerr << "\n===========================" - << "\nTotal time (ms) : " << elapsed - << "\nNodes searched : " << nodes + << "\nTotal time (ms) : " << elapsed << "\nNodes searched : " << nodes << "\nNodes/second : " << 1000 * nodes / elapsed << std::endl; - } +} - // The win rate model returns the probability of winning (in per mille units) given an - // eval and a game ply. It fits the LTC fishtest statistics rather accurately. - int win_rate_model(Value v, int ply) { +// The win rate model returns the probability of winning (in per mille units) given an +// eval and a game ply. It fits the LTC fishtest statistics rather accurately. +int win_rate_model(Value v, int ply) { - // The model only captures up to 240 plies, so limit the input and then rescale - double m = std::min(240, ply) / 64.0; + // The model only captures up to 240 plies, so limit the input and then rescale + double m = std::min(240, ply) / 64.0; - // The coefficients of a third-order polynomial fit is based on the fishtest data - // for two parameters that need to transform eval to the argument of a logistic - // function. - constexpr double as[] = { 0.38036525, -2.82015070, 23.17882135, 307.36768407}; - constexpr double bs[] = { -2.29434733, 13.27689788, -14.26828904, 63.45318330 }; + // The coefficients of a third-order polynomial fit is based on the fishtest data + // for two parameters that need to transform eval to the argument of a logistic + // function. + constexpr double as[] = {0.38036525, -2.82015070, 23.17882135, 307.36768407}; + constexpr double bs[] = {-2.29434733, 13.27689788, -14.26828904, 63.45318330}; - // Enforce that NormalizeToPawnValue corresponds to a 50% win rate at ply 64 - static_assert(UCI::NormalizeToPawnValue == int(as[0] + as[1] + as[2] + as[3])); + // Enforce that NormalizeToPawnValue corresponds to a 50% win rate at ply 64 + static_assert(UCI::NormalizeToPawnValue == int(as[0] + as[1] + as[2] + as[3])); - double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3]; - double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3]; + double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3]; + double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3]; - // Transform the eval to centipawns with limited range - double x = std::clamp(double(v), -4000.0, 4000.0); + // Transform the eval to centipawns with limited range + double x = std::clamp(double(v), -4000.0, 4000.0); - // Return the win rate in per mille units, rounded to the nearest integer - return int(0.5 + 1000 / (1 + std::exp((a - x) / b))); - } + // Return the win rate in per mille units, rounded to the nearest integer + return int(0.5 + 1000 / (1 + std::exp((a - x) / b))); +} -} // namespace +} // namespace // UCI::loop() waits for a command from the stdin, parses it, and then calls the appropriate @@ -241,81 +260,91 @@ namespace { void UCI::loop(int argc, char* argv[]) { - Position pos; - std::string token, cmd; - StateListPtr states(new std::deque(1)); - - pos.set(StartFEN, false, &states->back(), Threads.main()); - - for (int i = 1; i < argc; ++i) - cmd += std::string(argv[i]) + " "; - - do { - if (argc == 1 && !getline(std::cin, cmd)) // Wait for an input or an end-of-file (EOF) indication - cmd = "quit"; - - std::istringstream is(cmd); - - token.clear(); // Avoid a stale if getline() returns nothing or a blank line - is >> std::skipws >> token; - - if ( token == "quit" - || token == "stop") - Threads.stop = true; - - // The GUI sends 'ponderhit' to tell that the user has played the expected move. - // So, 'ponderhit' is sent if pondering was done on the same move that the user - // has played. The search should continue, but should also switch from pondering - // to the normal search. - else if (token == "ponderhit") - Threads.main()->ponder = false; // Switch to the normal search - - else if (token == "uci") - sync_cout << "id name " << engine_info(true) - << "\n" << Options - << "\nuciok" << sync_endl; - - else if (token == "setoption") setoption(is); - else if (token == "go") go(pos, is, states); - else if (token == "position") position(pos, is, states); - else if (token == "ucinewgame") Search::clear(); - else if (token == "isready") sync_cout << "readyok" << sync_endl; - - // Add custom non-UCI commands, mainly for debugging purposes. - // These commands must not be used 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; - else if (token == "eval") trace_eval(pos); - else if (token == "compiler") sync_cout << compiler_info() << sync_endl; - else if (token == "export_net") - { - std::optional filename; - std::string f; - if (is >> std::skipws >> f) - filename = f; - Eval::NNUE::save_eval(filename); - } - else if (token == "--help" || token == "help" || token == "--license" || token == "license") - sync_cout << "\nStockfish is a powerful chess engine for playing and analyzing." - "\nIt is released as free software licensed under the GNU GPLv3 License." - "\nStockfish is normally used with a graphical user interface (GUI) and implements" - "\nthe Universal Chess Interface (UCI) protocol to communicate with a GUI, an API, etc." - "\nFor any further information, visit https://github.com/official-stockfish/Stockfish#readme" - "\nor read the corresponding README.md and Copying.txt files distributed along with this program.\n" << sync_endl; - else if (!token.empty() && token[0] != '#') - sync_cout << "Unknown command: '" << cmd << "'. Type help for more information." << sync_endl; - - } while (token != "quit" && argc == 1); // The command-line arguments are one-shot + Position pos; + std::string token, cmd; + StateListPtr states(new std::deque(1)); + + pos.set(StartFEN, false, &states->back(), Threads.main()); + + for (int i = 1; i < argc; ++i) + cmd += std::string(argv[i]) + " "; + + do + { + if (argc == 1 + && !getline(std::cin, cmd)) // Wait for an input or an end-of-file (EOF) indication + cmd = "quit"; + + std::istringstream is(cmd); + + token.clear(); // Avoid a stale if getline() returns nothing or a blank line + is >> std::skipws >> token; + + if (token == "quit" || token == "stop") + Threads.stop = true; + + // The GUI sends 'ponderhit' to tell that the user has played the expected move. + // So, 'ponderhit' is sent if pondering was done on the same move that the user + // has played. The search should continue, but should also switch from pondering + // to the normal search. + else if (token == "ponderhit") + Threads.main()->ponder = false; // Switch to the normal search + + else if (token == "uci") + sync_cout << "id name " << engine_info(true) << "\n" + << Options << "\nuciok" << sync_endl; + + else if (token == "setoption") + setoption(is); + else if (token == "go") + go(pos, is, states); + else if (token == "position") + position(pos, is, states); + else if (token == "ucinewgame") + Search::clear(); + else if (token == "isready") + sync_cout << "readyok" << sync_endl; + + // Add custom non-UCI commands, mainly for debugging purposes. + // These commands must not be used 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; + else if (token == "eval") + trace_eval(pos); + else if (token == "compiler") + sync_cout << compiler_info() << sync_endl; + else if (token == "export_net") + { + std::optional filename; + std::string f; + if (is >> std::skipws >> f) + filename = f; + Eval::NNUE::save_eval(filename); + } + else if (token == "--help" || token == "help" || token == "--license" || token == "license") + sync_cout + << "\nStockfish is a powerful chess engine for playing and analyzing." + "\nIt is released as free software licensed under the GNU GPLv3 License." + "\nStockfish is normally used with a graphical user interface (GUI) and implements" + "\nthe Universal Chess Interface (UCI) protocol to communicate with a GUI, an API, etc." + "\nFor any further information, visit https://github.com/official-stockfish/Stockfish#readme" + "\nor read the corresponding README.md and Copying.txt files distributed along with this program.\n" + << sync_endl; + else if (!token.empty() && token[0] != '#') + sync_cout << "Unknown command: '" << cmd << "'. Type help for more information." + << sync_endl; + + } while (token != "quit" && argc == 1); // The command-line arguments are one-shot } // Turns a Value to an integer centipawn number, // without treatment of mate and similar special scores. -int UCI::to_cp(Value v) { - - return 100 * v / UCI::NormalizeToPawnValue; -} +int UCI::to_cp(Value v) { return 100 * v / UCI::NormalizeToPawnValue; } // UCI::value() converts a Value to a string by adhering to the UCI protocol specification: // @@ -325,21 +354,21 @@ int UCI::to_cp(Value v) { std::string UCI::value(Value v) { - assert(-VALUE_INFINITE < v && v < VALUE_INFINITE); + assert(-VALUE_INFINITE < v && v < VALUE_INFINITE); - std::stringstream ss; + std::stringstream ss; - if (abs(v) < VALUE_TB_WIN_IN_MAX_PLY) - ss << "cp " << UCI::to_cp(v); - else if (abs(v) < VALUE_MATE_IN_MAX_PLY) - { - const int ply = VALUE_MATE_IN_MAX_PLY - 1 - std::abs(v); // recompute ss->ply - ss << "cp " << (v > 0 ? 20000 - ply : -20000 + ply); - } - else - ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; + if (abs(v) < VALUE_TB_WIN_IN_MAX_PLY) + ss << "cp " << UCI::to_cp(v); + else if (abs(v) < VALUE_MATE_IN_MAX_PLY) + { + const int ply = VALUE_MATE_IN_MAX_PLY - 1 - std::abs(v); // recompute ss->ply + ss << "cp " << (v > 0 ? 20000 - ply : -20000 + ply); + } + else + ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; - return ss.str(); + return ss.str(); } @@ -348,21 +377,21 @@ std::string UCI::value(Value v) { std::string UCI::wdl(Value v, int ply) { - std::stringstream ss; + std::stringstream ss; - int wdl_w = win_rate_model( v, ply); - int wdl_l = win_rate_model(-v, ply); - int wdl_d = 1000 - wdl_w - wdl_l; - ss << " wdl " << wdl_w << " " << wdl_d << " " << wdl_l; + int wdl_w = win_rate_model(v, ply); + int wdl_l = win_rate_model(-v, ply); + int wdl_d = 1000 - wdl_w - wdl_l; + ss << " wdl " << wdl_w << " " << wdl_d << " " << wdl_l; - return ss.str(); + return ss.str(); } // UCI::square() converts a Square to a string in algebraic notation (g1, a7, etc.) std::string UCI::square(Square s) { - return std::string{ char('a' + file_of(s)), char('1' + rank_of(s)) }; + return std::string{char('a' + file_of(s)), char('1' + rank_of(s))}; } @@ -373,24 +402,24 @@ std::string UCI::square(Square s) { std::string UCI::move(Move m, bool chess960) { - if (m == MOVE_NONE) - return "(none)"; + if (m == MOVE_NONE) + return "(none)"; - if (m == MOVE_NULL) - return "0000"; + if (m == MOVE_NULL) + return "0000"; - Square from = from_sq(m); - Square to = to_sq(m); + Square from = from_sq(m); + Square to = to_sq(m); - if (type_of(m) == CASTLING && !chess960) - to = make_square(to > from ? FILE_G : FILE_C, rank_of(from)); + if (type_of(m) == CASTLING && !chess960) + to = make_square(to > from ? FILE_G : FILE_C, rank_of(from)); - std::string move = UCI::square(from) + UCI::square(to); + std::string move = UCI::square(from) + UCI::square(to); - if (type_of(m) == PROMOTION) - move += " pnbrqk"[promotion_type(m)]; + if (type_of(m) == PROMOTION) + move += " pnbrqk"[promotion_type(m)]; - return move; + return move; } @@ -399,14 +428,14 @@ std::string UCI::move(Move m, bool chess960) { Move UCI::to_move(const Position& pos, std::string& str) { - if (str.length() == 5) - str[4] = char(tolower(str[4])); // The promotion piece character must be lowercased + if (str.length() == 5) + str[4] = char(tolower(str[4])); // The promotion piece character must be lowercased - for (const auto& m : MoveList(pos)) - if (str == UCI::move(m, pos.is_chess960())) - return m; + for (const auto& m : MoveList(pos)) + if (str == UCI::move(m, pos.is_chess960())) + return m; - return MOVE_NONE; + return MOVE_NONE; } -} // namespace Stockfish +} // namespace Stockfish diff --git a/src/uci.h b/src/uci.h index 048f8c1166e..be5c70c54ce 100644 --- a/src/uci.h +++ b/src/uci.h @@ -43,7 +43,7 @@ class Option; // Define a custom comparator, because the UCI options should be case-insensitive struct CaseInsensitiveLess { - bool operator() (const std::string&, const std::string&) const; + bool operator()(const std::string&, const std::string&) const; }; // The options container is defined as a std::map @@ -52,44 +52,44 @@ using OptionsMap = std::map; // The Option class implements each option as specified by the UCI protocol class Option { - using OnChange = void (*)(const Option&); + using OnChange = void (*)(const Option&); -public: - Option(OnChange = nullptr); - Option(bool v, OnChange = nullptr); - Option(const char* v, OnChange = nullptr); - Option(double v, int minv, int maxv, OnChange = nullptr); - Option(const char* v, const char* cur, OnChange = nullptr); + public: + Option(OnChange = nullptr); + Option(bool v, OnChange = nullptr); + Option(const char* v, OnChange = nullptr); + Option(double v, int minv, int maxv, OnChange = nullptr); + Option(const char* v, const char* cur, OnChange = nullptr); - Option& operator=(const std::string&); - void operator<<(const Option&); - operator int() const; - operator std::string() const; - bool operator==(const char*) const; + Option& operator=(const std::string&); + void operator<<(const Option&); + operator int() const; + operator std::string() const; + bool operator==(const char*) const; -private: - friend std::ostream& operator<<(std::ostream&, const OptionsMap&); + private: + friend std::ostream& operator<<(std::ostream&, const OptionsMap&); - std::string defaultValue, currentValue, type; - int min, max; - size_t idx; - OnChange on_change; + std::string defaultValue, currentValue, type; + int min, max; + size_t idx; + OnChange on_change; }; -void init(OptionsMap&); -void loop(int argc, char* argv[]); -int to_cp(Value v); +void init(OptionsMap&); +void loop(int argc, char* argv[]); +int to_cp(Value v); std::string value(Value v); std::string square(Square s); std::string move(Move m, bool chess960); std::string pv(const Position& pos, Depth depth); std::string wdl(Value v, int ply); -Move to_move(const Position& pos, std::string& str); +Move to_move(const Position& pos, std::string& str); -} // namespace UCI +} // namespace UCI extern UCI::OptionsMap Options; -} // namespace Stockfish +} // namespace Stockfish -#endif // #ifndef UCI_H_INCLUDED +#endif // #ifndef UCI_H_INCLUDED diff --git a/src/ucioption.cpp b/src/ucioption.cpp index b822ccf936f..8db4233af5f 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -40,7 +40,7 @@ using std::string; namespace Stockfish { -UCI::OptionsMap Options; // Global object +UCI::OptionsMap Options; // Global object namespace UCI { @@ -53,10 +53,10 @@ static void on_tb_path(const Option& o) { Tablebases::init(o); } static void on_eval_file(const Option&) { Eval::NNUE::init(); } // Our case insensitive less() function as required by UCI protocol -bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const { +bool CaseInsensitiveLess::operator()(const string& s1, const string& s2) const { - return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), - [](char c1, char c2) { return tolower(c1) < tolower(c2); }); + return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), + [](char c1, char c2) { return tolower(c1) < tolower(c2); }); } @@ -64,28 +64,28 @@ bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const void init(OptionsMap& o) { - constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048; - - o["Debug Log File"] << Option("", on_logger); - o["Threads"] << Option(1, 1, 1024, on_threads); - o["Hash"] << Option(16, 1, MaxHashMB, on_hash_size); - o["Clear Hash"] << Option(on_clear_hash); - o["Ponder"] << Option(false); - o["MultiPV"] << Option(1, 1, 500); - o["Skill Level"] << Option(20, 0, 20); - o["Move Overhead"] << Option(10, 0, 5000); - o["Slow Mover"] << Option(100, 10, 1000); - 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(1320, 1320, 3190); - o["UCI_ShowWDL"] << Option(false); - o["SyzygyPath"] << Option("", on_tb_path); - o["SyzygyProbeDepth"] << Option(1, 1, 100); - o["Syzygy50MoveRule"] << Option(true); - o["SyzygyProbeLimit"] << Option(7, 0, 7); - o["EvalFile"] << Option(EvalFileDefaultName, on_eval_file); + constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048; + + o["Debug Log File"] << Option("", on_logger); + o["Threads"] << Option(1, 1, 1024, on_threads); + o["Hash"] << Option(16, 1, MaxHashMB, on_hash_size); + o["Clear Hash"] << Option(on_clear_hash); + o["Ponder"] << Option(false); + o["MultiPV"] << Option(1, 1, 500); + o["Skill Level"] << Option(20, 0, 20); + o["Move Overhead"] << Option(10, 0, 5000); + o["Slow Mover"] << Option(100, 10, 1000); + 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(1320, 1320, 3190); + o["UCI_ShowWDL"] << Option(false); + o["SyzygyPath"] << Option("", on_tb_path); + o["SyzygyProbeDepth"] << Option(1, 1, 100); + o["Syzygy50MoveRule"] << Option(true); + o["SyzygyProbeLimit"] << Option(7, 0, 7); + o["EvalFile"] << Option(EvalFileDefaultName, on_eval_file); } @@ -94,59 +94,81 @@ void init(OptionsMap& o) { std::ostream& operator<<(std::ostream& os, const OptionsMap& om) { - for (size_t idx = 0; idx < om.size(); ++idx) - for (const auto& it : om) - if (it.second.idx == idx) - { - const Option& o = it.second; - os << "\noption name " << it.first << " type " << o.type; + for (size_t idx = 0; idx < om.size(); ++idx) + for (const auto& it : om) + if (it.second.idx == idx) + { + const Option& o = it.second; + os << "\noption name " << it.first << " type " << o.type; - if (o.type == "string" || o.type == "check" || o.type == "combo") - os << " default " << o.defaultValue; + if (o.type == "string" || o.type == "check" || o.type == "combo") + os << " default " << o.defaultValue; - if (o.type == "spin") - os << " default " << int(stof(o.defaultValue)) - << " min " << o.min - << " max " << o.max; + if (o.type == "spin") + os << " default " << int(stof(o.defaultValue)) << " min " << o.min << " max " + << o.max; - break; - } + break; + } - return os; + return os; } // Option class constructors and conversion operators -Option::Option(const char* v, OnChange f) : type("string"), min(0), max(0), on_change(f) -{ defaultValue = currentValue = v; } - -Option::Option(bool v, OnChange f) : type("check"), min(0), max(0), on_change(f) -{ defaultValue = currentValue = (v ? "true" : "false"); } +Option::Option(const char* v, OnChange f) : + type("string"), + min(0), + max(0), + on_change(f) { + defaultValue = currentValue = v; +} -Option::Option(OnChange f) : type("button"), min(0), max(0), on_change(f) -{} +Option::Option(bool v, OnChange f) : + type("check"), + min(0), + max(0), + on_change(f) { + defaultValue = currentValue = (v ? "true" : "false"); +} -Option::Option(double v, int minv, int maxv, OnChange f) : type("spin"), min(minv), max(maxv), on_change(f) -{ defaultValue = currentValue = std::to_string(v); } +Option::Option(OnChange f) : + type("button"), + min(0), + max(0), + on_change(f) {} + +Option::Option(double v, int minv, int maxv, OnChange f) : + type("spin"), + min(minv), + max(maxv), + on_change(f) { + defaultValue = currentValue = std::to_string(v); +} -Option::Option(const char* v, const char* cur, OnChange f) : type("combo"), min(0), max(0), on_change(f) -{ defaultValue = v; currentValue = cur; } +Option::Option(const char* v, const char* cur, OnChange f) : + type("combo"), + min(0), + max(0), + on_change(f) { + defaultValue = v; + currentValue = cur; +} Option::operator int() const { - assert(type == "check" || type == "spin"); - return (type == "spin" ? std::stoi(currentValue) : currentValue == "true"); + assert(type == "check" || type == "spin"); + return (type == "spin" ? std::stoi(currentValue) : currentValue == "true"); } Option::operator std::string() const { - assert(type == "string"); - return currentValue; + assert(type == "string"); + return currentValue; } bool Option::operator==(const char* s) const { - assert(type == "combo"); - return !CaseInsensitiveLess()(currentValue, s) - && !CaseInsensitiveLess()(s, currentValue); + assert(type == "combo"); + return !CaseInsensitiveLess()(currentValue, s) && !CaseInsensitiveLess()(s, currentValue); } @@ -154,10 +176,10 @@ bool Option::operator==(const char* s) const { void Option::operator<<(const Option& o) { - static size_t insert_order = 0; + static size_t insert_order = 0; - *this = o; - idx = insert_order++; + *this = o; + idx = insert_order++; } @@ -167,33 +189,33 @@ void Option::operator<<(const Option& o) { Option& Option::operator=(const string& v) { - assert(!type.empty()); + assert(!type.empty()); - if ( (type != "button" && type != "string" && v.empty()) - || (type == "check" && v != "true" && v != "false") - || (type == "spin" && (stof(v) < min || stof(v) > max))) - return *this; + if ((type != "button" && type != "string" && v.empty()) + || (type == "check" && v != "true" && v != "false") + || (type == "spin" && (stof(v) < min || stof(v) > max))) + return *this; - if (type == "combo") - { - OptionsMap comboMap; // To have case insensitive compare - string token; - std::istringstream ss(defaultValue); - while (ss >> token) - comboMap[token] << Option(); - if (!comboMap.count(v) || v == "var") - return *this; - } + if (type == "combo") + { + OptionsMap comboMap; // To have case insensitive compare + string token; + std::istringstream ss(defaultValue); + while (ss >> token) + comboMap[token] << Option(); + if (!comboMap.count(v) || v == "var") + return *this; + } - if (type != "button") - currentValue = v; + if (type != "button") + currentValue = v; - if (on_change) - on_change(*this); + if (on_change) + on_change(*this); - return *this; + return *this; } -} // namespace UCI +} // namespace UCI -} // namespace Stockfish +} // namespace Stockfish From b7b7800e2b752c93131e03c31d7456f18b392a7c Mon Sep 17 00:00:00 2001 From: Taras Vuk <117687515+TarasVuk@users.noreply.github.com> Date: Sun, 22 Oct 2023 08:36:43 +0200 Subject: [PATCH 1232/1766] Simplify futilityBase formula This patch replaces std::min(ss->staticEval, bestValue) with ss->staticEval in the futilityBase formula. Original idea by Vizvezdenec: https://tests.stockfishchess.org/tests/view/64ce66795b17f7c21c0d85f3 Passed STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 116928 W: 29925 L: 29793 D: 57210 Ptnml(0-2): 399, 13558, 30446, 13634, 427 https://tests.stockfishchess.org/tests/view/653285aade6d262d08d385dd Passed LTC: LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 50868 W: 12947 L: 12757 D: 25164 Ptnml(0-2): 30, 5414, 14355, 5606, 29 https://tests.stockfishchess.org/tests/view/65336ffbde6d262d08d39ba0 closes https://github.com/official-stockfish/Stockfish/pull/4837 bench: 1241996 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 43f0c8726e3..43d78892f60 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1451,7 +1451,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { if (bestValue > alpha) alpha = bestValue; - futilityBase = std::min(ss->staticEval, bestValue) + 200; + futilityBase = ss->staticEval + 200; } const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, From 40c6a84434ea4600b5ebdd1020b34516317be1a9 Mon Sep 17 00:00:00 2001 From: MinetaS Date: Wed, 18 Oct 2023 04:03:39 +0900 Subject: [PATCH 1233/1766] Fix a compiler bug on Clang 15+ with AVX-512 fixes https://github.com/official-stockfish/Stockfish/issues/4450 closes https://github.com/official-stockfish/Stockfish/pull/4830 No functional change. --- src/syzygy/tbprobe.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index c8e60ab6c02..31597f835a8 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -690,6 +690,17 @@ int map_score(TBTable* entry, File f, int value, WDLScore wdl) { return value + 1; } +// A temporary fix for the compiler bug with AVX-512. (#4450) +#ifdef USE_AVX512 + #if defined(__clang__) && defined(__clang_major__) && __clang_major__ >= 15 + #define CLANG_AVX512_BUG_FIX __attribute__((optnone)) + #endif +#endif + +#ifndef CLANG_AVX512_BUG_FIX + #define CLANG_AVX512_BUG_FIX +#endif + // Compute a unique index out of a position and use it to probe the TB file. To // encode k pieces of the same type and color, first sort the pieces by square in // ascending order s1 <= s2 <= ... <= sk then compute the unique index as: @@ -697,7 +708,8 @@ int map_score(TBTable* entry, File f, int value, WDLScore wdl) { // idx = Binomial[1][s1] + Binomial[2][s2] + ... + Binomial[k][sk] // template -Ret do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* result) { +CLANG_AVX512_BUG_FIX Ret +do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* result) { Square squares[TBPIECES]; Piece pieces[TBPIECES]; From b1876222335df6581777baadc68fb5b17e5fe656 Mon Sep 17 00:00:00 2001 From: Disservin Date: Sun, 22 Oct 2023 16:43:33 +0200 Subject: [PATCH 1234/1766] use expanded variables for shell commands Performance improvement for the shell commands in the Makefile. By using expanded variables, the shell commands are only evaluated once, instead of every time they are used. closes https://github.com/official-stockfish/Stockfish/pull/4838 No functional change --- src/Makefile | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Makefile b/src/Makefile index 7b7ee41b654..76ef6fdeb8d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -20,9 +20,9 @@ ### ========================================================================== ### Establish the operating system name -KERNEL = $(shell uname -s) +KERNEL := $(shell uname -s) ifeq ($(KERNEL),Linux) - OS = $(shell uname -o) + OS := $(shell uname -o) endif ### Target Windows OS @@ -33,7 +33,7 @@ ifeq ($(OS),Windows_NT) else ifeq ($(COMP),mingw) target_windows = yes ifeq ($(WINE_PATH),) - WINE_PATH = $(shell which wine) + WINE_PATH := $(shell which wine) endif endif @@ -116,7 +116,7 @@ ifeq ($(ARCH),) endif ifeq ($(ARCH), native) - override ARCH = $(shell $(SHELL) ../scripts/get_native_properties.sh | cut -d " " -f 1) + override ARCH := $(shell $(SHELL) ../scripts/get_native_properties.sh | cut -d " " -f 1) endif # explicitly check for the list of supported architectures (as listed with make help), @@ -542,8 +542,8 @@ endif ### Sometimes gcc is really clang ifeq ($(COMP),gcc) - gccversion = $(shell $(CXX) --version 2>/dev/null) - gccisclang = $(findstring clang,$(gccversion)) + gccversion := $(shell $(CXX) --version 2>/dev/null) + gccisclang := $(findstring clang,$(gccversion)) ifneq ($(gccisclang),) profile_make = clang-profile-make profile_use = clang-profile-use @@ -601,7 +601,7 @@ ifeq ($(optimize),yes) endif ifeq ($(comp),clang) - clangmajorversion = $(shell $(CXX) -dumpversion 2>/dev/null | cut -f1 -d.) + clangmajorversion := $(shell $(CXX) -dumpversion 2>/dev/null | cut -f1 -d.) ifeq ($(shell expr $(clangmajorversion) \< 16),1) CXXFLAGS += -fexperimental-new-pass-manager endif @@ -717,13 +717,13 @@ ifeq ($(pext),yes) endif ### 3.8.1 Try to include git commit sha for versioning -GIT_SHA = $(shell git rev-parse HEAD 2>/dev/null | cut -c 1-8) +GIT_SHA := $(shell git rev-parse HEAD 2>/dev/null | cut -c 1-8) ifneq ($(GIT_SHA), ) CXXFLAGS += -DGIT_SHA=$(GIT_SHA) endif ### 3.8.2 Try to include git commit date for versioning -GIT_DATE = $(shell git show -s --date=format:'%Y%m%d' --format=%cd HEAD 2>/dev/null) +GIT_DATE := $(shell git show -s --date=format:'%Y%m%d' --format=%cd HEAD 2>/dev/null) ifneq ($(GIT_DATE), ) CXXFLAGS += -DGIT_DATE=$(GIT_DATE) endif From a105978bbde04508389abad03bd121f817f91646 Mon Sep 17 00:00:00 2001 From: Disservin Date: Sun, 22 Oct 2023 20:20:53 +0200 Subject: [PATCH 1235/1766] remove blank line between function and it's description - remove the blank line between the declaration of the function and it's comment, leads to better IDE support when hovering over a function to see it's description - remove the unnecessary duplication of the function name in the functions description - slightly refactored code for lsb, msb in bitboard.h There are still a few things we can be improved later on, move the description of a function where it was declared (instead of implemented) and add descriptions to functions which are behind macros ifdefs closes https://github.com/official-stockfish/Stockfish/pull/4840 No functional change --- src/benchmark.cpp | 3 +- src/bitboard.cpp | 12 ++-- src/bitboard.h | 90 +++++++++++++---------------- src/evaluate.cpp | 14 ++--- src/misc.cpp | 19 ++---- src/misc.h | 25 ++++---- src/movegen.cpp | 1 - src/movepick.cpp | 12 ++-- src/nnue/evaluate_nnue.cpp | 6 +- src/nnue/features/half_ka_v2_hm.cpp | 2 +- src/nnue/nnue_common.h | 12 ++-- src/position.cpp | 75 +++++++++--------------- src/search.cpp | 47 +++++---------- src/search.h | 8 +-- src/syzygy/tbprobe.cpp | 2 +- src/thread.cpp | 26 +++------ src/thread.h | 3 - src/timeman.cpp | 3 +- src/timeman.h | 1 - src/tt.cpp | 16 ++--- src/tt.h | 2 - src/types.h | 20 +++---- src/uci.cpp | 30 ++++------ src/ucioption.cpp | 11 ++-- 24 files changed, 172 insertions(+), 268 deletions(-) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 63598e750e8..2270dcc3c83 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -97,7 +97,7 @@ const std::vector Defaults = { namespace Stockfish { -// setup_bench() builds a list of UCI commands to be run by bench. There +// Builds a list of UCI commands to be run by bench. There // are five parameters: TT size in MB, number of search threads that // should be used, the limit value spent for each position, a file name // where to look for positions in FEN format, and the type of the limit: @@ -108,7 +108,6 @@ namespace Stockfish { // bench 64 1 100000 default nodes : search default positions for 100K nodes each // bench 64 4 5000 current movetime : search current position with 4 threads for 5 sec // bench 16 1 5 blah perft : run a perft 5 on positions in file "blah" - std::vector setup_bench(const Position& current, std::istream& is) { std::vector fens, list; diff --git a/src/bitboard.cpp b/src/bitboard.cpp index fff7eba9e6f..a8a10cbb874 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -46,18 +46,16 @@ void init_magics(PieceType pt, Bitboard table[], Magic magics[]); } -// safe_destination() returns the bitboard of target square for the given step +// Returns the bitboard of target square for the given step // from the given square. If the step is off the board, returns empty bitboard. - inline Bitboard safe_destination(Square s, int step) { Square to = Square(s + step); return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0); } -// Bitboards::pretty() returns an ASCII representation of a bitboard suitable +// Returns an ASCII representation of a bitboard suitable // to be printed to standard output. Useful for debugging. - std::string Bitboards::pretty(Bitboard b) { std::string s = "+---+---+---+---+---+---+---+---+\n"; @@ -75,9 +73,8 @@ std::string Bitboards::pretty(Bitboard b) { } -// Bitboards::init() initializes various bitboard tables. It is called at +// Initializes various bitboard tables. It is called at // startup and relies on global objects to be already zero-initialized. - void Bitboards::init() { for (unsigned i = 0; i < (1 << 16); ++i) @@ -137,11 +134,10 @@ Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) { } -// init_magics() computes all rook and bishop attacks at startup. Magic +// Computes all rook and bishop attacks at startup. Magic // bitboards are used to look up attacks of sliding pieces. As a reference see // www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so // called "fancy" approach. - void init_magics(PieceType pt, Bitboard table[], Magic magics[]) { // Optimal PRNG seeds to pick the correct magics in the shortest time diff --git a/src/bitboard.h b/src/bitboard.h index 03a511361f4..24f6deca840 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -125,8 +125,7 @@ constexpr Bitboard file_bb(File f) { return FileABB << f; } constexpr Bitboard file_bb(Square s) { return file_bb(file_of(s)); } -// shift() moves a bitboard one or two steps as specified by the direction D - +// Moves a bitboard one or two steps as specified by the direction D template constexpr Bitboard shift(Bitboard b) { return D == NORTH ? b << 8 @@ -143,9 +142,8 @@ constexpr Bitboard shift(Bitboard b) { } -// pawn_attacks_bb() returns the squares attacked by pawns of the given color +// Returns the squares attacked by pawns of the given color // from the squares in the given bitboard. - template constexpr Bitboard pawn_attacks_bb(Bitboard b) { return C == WHITE ? shift(b) | shift(b) @@ -158,11 +156,10 @@ inline Bitboard pawn_attacks_bb(Color c, Square s) { return PawnAttacks[c][s]; } -// line_bb() returns a bitboard representing an entire line (from board edge +// Returns a bitboard representing an entire line (from board edge // to board edge) that intersects the two given squares. If the given squares // are not on a same file/rank/diagonal, the function returns 0. For instance, // line_bb(SQ_C4, SQ_F7) will return a bitboard with the A2-G8 diagonal. - inline Bitboard line_bb(Square s1, Square s2) { assert(is_ok(s1) && is_ok(s2)); @@ -171,14 +168,13 @@ inline Bitboard line_bb(Square s1, Square s2) { } -// between_bb(s1, s2) returns a bitboard representing the squares in the semi-open +// Returns a bitboard representing the squares in the semi-open // segment between the squares s1 and s2 (excluding s1 but including s2). If the // given squares are not on a same file/rank/diagonal, it returns s2. For instance, // between_bb(SQ_C4, SQ_F7) will return a bitboard with squares D5, E6 and F7, but // between_bb(SQ_E6, SQ_F8) will return a bitboard with the square F8. This trick // allows to generate non-king evasion moves faster: the defending piece must either // interpose itself to cover the check or capture the checking piece. - inline Bitboard between_bb(Square s1, Square s2) { assert(is_ok(s1) && is_ok(s2)); @@ -186,9 +182,8 @@ inline Bitboard between_bb(Square s1, Square s2) { return BetweenBB[s1][s2]; } -// aligned() returns true if the squares s1, s2 and s3 are aligned either on a +// Returns true if the squares s1, s2 and s3 are aligned either on a // straight or on a diagonal line. - inline bool aligned(Square s1, Square s2, Square s3) { return line_bb(s1, s2) & s3; } @@ -197,14 +192,17 @@ inline bool aligned(Square s1, Square s2, Square s3) { return line_bb(s1, s2) & template inline int distance(Square x, Square y); + template<> inline int distance(Square x, Square y) { return std::abs(file_of(x) - file_of(y)); } + template<> inline int distance(Square x, Square y) { return std::abs(rank_of(x) - rank_of(y)); } + template<> inline int distance(Square x, Square y) { return SquareDistance[x][y]; @@ -212,9 +210,8 @@ inline int distance(Square x, Square y) { inline int edge_distance(File f) { return std::min(f, File(FILE_H - f)); } -// attacks_bb(Square) returns the pseudo attacks of the given piece type +// Returns the pseudo attacks of the given piece type // assuming an empty board. - template inline Bitboard attacks_bb(Square s) { @@ -224,10 +221,9 @@ inline Bitboard attacks_bb(Square s) { } -// attacks_bb(Square, Bitboard) returns the attacks by the given piece +// Returns the attacks by the given piece // assuming the board is occupied according to the passed Bitboard. // Sliding piece attacks do not continue passed an occupied square. - template inline Bitboard attacks_bb(Square s, Bitboard occupied) { @@ -246,6 +242,9 @@ inline Bitboard attacks_bb(Square s, Bitboard occupied) { } } +// Returns the attacks by the given piece +// assuming the board is occupied according to the passed Bitboard. +// Sliding piece attacks do not continue passed an occupied square. inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) { assert((pt != PAWN) && (is_ok(s))); @@ -264,8 +263,7 @@ inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) { } -// popcount() counts the number of non-zero bits in a bitboard - +// Counts the number of non-zero bits in a bitboard. inline int popcount(Bitboard b) { #ifndef USE_POPCNT @@ -287,43 +285,22 @@ inline int popcount(Bitboard b) { #endif } - -// lsb() and msb() return the least/most significant bit in a non-zero bitboard - -#if defined(__GNUC__) // GCC, Clang, ICX - +// Returns the least significant bit in a non-zero bitboard. inline Square lsb(Bitboard b) { assert(b); - return Square(__builtin_ctzll(b)); -} -inline Square msb(Bitboard b) { - assert(b); - return Square(63 ^ __builtin_clzll(b)); -} +#if defined(__GNUC__) // GCC, Clang, ICX -#elif defined(_MSC_VER) // MSVC + return Square(__builtin_ctzll(b)); +#elif defined(_MSC_VER) #ifdef _WIN64 // MSVC, WIN64 -inline Square lsb(Bitboard b) { - assert(b); unsigned long idx; _BitScanForward64(&idx, b); return (Square) idx; -} - -inline Square msb(Bitboard b) { - assert(b); - unsigned long idx; - _BitScanReverse64(&idx, b); - return (Square) idx; -} #else // MSVC, WIN32 - -inline Square lsb(Bitboard b) { - assert(b); unsigned long idx; if (b & 0xffffffff) @@ -336,10 +313,29 @@ inline Square lsb(Bitboard b) { _BitScanForward(&idx, int32_t(b >> 32)); return Square(idx + 32); } + #endif +#else // Compiler is neither GCC nor MSVC compatible + #error "Compiler not supported." +#endif } +// Returns the most significant bit in a non-zero bitboard. inline Square msb(Bitboard b) { assert(b); + +#if defined(__GNUC__) // GCC, Clang, ICX + + return Square(63 ^ __builtin_clzll(b)); + +#elif defined(_MSC_VER) + #ifdef _WIN64 // MSVC, WIN64 + + unsigned long idx; + _BitScanReverse64(&idx, b); + return (Square) idx; + + #else // MSVC, WIN32 + unsigned long idx; if (b >> 32) @@ -352,26 +348,20 @@ inline Square msb(Bitboard b) { _BitScanReverse(&idx, int32_t(b)); return Square(idx); } -} - #endif - #else // Compiler is neither GCC nor MSVC compatible - #error "Compiler not supported." - #endif +} -// least_significant_square_bb() returns the bitboard of the least significant +// Returns the bitboard of the least significant // square of a non-zero bitboard. It is equivalent to square_bb(lsb(bb)). - inline Bitboard least_significant_square_bb(Bitboard b) { assert(b); return b & -b; } -// pop_lsb() finds and clears the least significant bit in a non-zero bitboard - +// Finds and clears the least significant bit in a non-zero bitboard. inline Square pop_lsb(Bitboard& b) { assert(b); const Square s = lsb(b); diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 00498bf02f0..4ee3e6fd8b4 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -57,14 +57,13 @@ namespace Eval { std::string currentEvalFileName = "None"; -// NNUE::init() tries to load a NNUE network at startup time, or when the engine +// Tries to load a NNUE network at startup time, or when the engine // receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue" // The name of the NNUE network is always retrieved from the EvalFile option. // We search the given network in three locations: internally (the default // network may be embedded in the binary), in the active working directory and // in the engine directory. Distro packagers may define the DEFAULT_NNUE_DIRECTORY // variable to have the engine search in a special directory in their distro. - void NNUE::init() { std::string eval_file = std::string(Options["EvalFile"]); @@ -111,7 +110,7 @@ void NNUE::init() { } } -// NNUE::verify() verifies that the last net used was loaded successfully +// Verifies that the last net used was loaded successfully void NNUE::verify() { std::string eval_file = std::string(Options["EvalFile"]); @@ -145,19 +144,17 @@ void NNUE::verify() { } -// simple_eval() returns a static, purely materialistic evaluation of the position +// Returns a static, purely materialistic evaluation of the position // from the point of view of the given color. It can be divided by PawnValue to get // an approximation of the material advantage on the board in terms of pawns. - Value Eval::simple_eval(const Position& pos, Color c) { return PawnValue * (pos.count(c) - pos.count(~c)) + (pos.non_pawn_material(c) - pos.non_pawn_material(~c)); } -// evaluate() is the evaluator for the outer world. It returns a static evaluation +// Evaluate is the evaluator for the outer world. It returns a static evaluation // of the position from the point of view of the side to move. - Value Eval::evaluate(const Position& pos) { assert(!pos.checkers()); @@ -197,11 +194,10 @@ Value Eval::evaluate(const Position& pos) { return v; } -// trace() is like evaluate(), but instead of returning a value, it returns +// Like evaluate(), but instead of returning a value, it returns // a string (suitable for outputting to stdout) that contains the detailed // descriptions and values of each evaluation term. Useful for debugging. // Trace scores are from white's point of view - std::string Eval::trace(Position& pos) { if (pos.checkers()) diff --git a/src/misc.cpp b/src/misc.cpp index 05181325ece..3e9006156c6 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -148,7 +148,7 @@ class Logger { } // namespace -// engine_info() returns the full name of the current Stockfish version. +// Returns the full name of the current Stockfish version. // For local dev compiles we try to append the commit sha and commit date // from git if that fails only the local compilation date is set and "nogit" is specified: // Stockfish dev-YYYYMMDD-SHA @@ -157,7 +157,6 @@ class Logger { // // For releases (non-dev builds) we only include the version number: // Stockfish version - std::string engine_info(bool to_uci) { std::stringstream ss; ss << "Stockfish " << version << std::setfill('0'); @@ -192,8 +191,7 @@ std::string engine_info(bool to_uci) { } -// compiler_info() returns a string trying to describe the compiler we use - +// Returns a string trying to describe the compiler we use std::string compiler_info() { #define make_version_string(major, minor, patch) \ @@ -397,7 +395,6 @@ void dbg_print() { // Used to serialize access to std::cout to avoid multiple threads writing at // the same time. - std::ostream& operator<<(std::ostream& os, SyncCout sc) { static std::mutex m; @@ -416,9 +413,6 @@ std::ostream& operator<<(std::ostream& os, SyncCout sc) { void start_logger(const std::string& fname) { Logger::start(fname); } -// prefetch() preloads the given address in L1/L2 cache. This is a non-blocking -// function that doesn't stall the CPU waiting for data to be loaded from memory, -// which can be quite slow. #ifdef NO_PREFETCH void prefetch(void*) {} @@ -437,10 +431,9 @@ void prefetch(void* addr) { #endif -// std_aligned_alloc() is our wrapper for systems where the c++17 implementation +// Wrapper for systems where the c++17 implementation // does not guarantee the availability of aligned_alloc(). Memory allocated with // std_aligned_alloc() must be freed with std_aligned_free(). - void* std_aligned_alloc(size_t alignment, size_t size) { #if defined(POSIXALIGNEDALLOC) @@ -607,10 +600,9 @@ void bindThisThread(size_t) {} #else -// best_node() retrieves logical processor information using Windows specific +// Retrieves logical processor information using Windows specific // API and returns the best node id for the thread with index idx. Original // code from Texel by Peter Österlund. - static int best_node(size_t idx) { int threads = 0; @@ -679,8 +671,7 @@ static int best_node(size_t idx) { } -// bindThisThread() sets the group affinity of the current thread - +// Sets the group affinity of the current thread void bindThisThread(size_t idx) { // Use only local variables to be thread-safe diff --git a/src/misc.h b/src/misc.h index 3cd3315a8ed..91fdb72f1b1 100644 --- a/src/misc.h +++ b/src/misc.h @@ -33,13 +33,19 @@ namespace Stockfish { std::string engine_info(bool to_uci = false); std::string compiler_info(); -void prefetch(void* addr); -void start_logger(const std::string& fname); -void* std_aligned_alloc(size_t alignment, size_t size); -void std_aligned_free(void* ptr); -void* aligned_large_pages_alloc( - size_t size); // memory aligned by page size, min alignment: 4096 bytes -void aligned_large_pages_free(void* mem); // nop if mem == nullptr + +// Preloads the given address in L1/L2 cache. This is a non-blocking +// function that doesn't stall the CPU waiting for data to be loaded from memory, +// which can be quite slow. +void prefetch(void* addr); + +void start_logger(const std::string& fname); +void* std_aligned_alloc(size_t alignment, size_t size); +void std_aligned_free(void* ptr); +// memory aligned by page size, min alignment: 4096 bytes +void* aligned_large_pages_alloc(size_t size); +// nop if mem == nullptr +void aligned_large_pages_free(void* mem); void dbg_hit_on(bool cond, int slot = 0); void dbg_mean_of(int64_t value, int slot = 0); @@ -66,7 +72,7 @@ std::ostream& operator<<(std::ostream&, SyncCout); #define sync_endl std::endl << IO_UNLOCK -// align_ptr_up() : get the first aligned element of an array. +// Get the first aligned element of an array. // ptr must point to an array of size at least `sizeof(T) * N + alignment` bytes, // where N is the number of elements in the array. template @@ -79,7 +85,7 @@ T* align_ptr_up(T* ptr) { } -// IsLittleEndian : true if and only if the binary is compiled on a little-endian machine +// True if and only if the binary is compiled on a little-endian machine static inline const union { uint32_t i; char c[4]; @@ -166,7 +172,6 @@ inline uint64_t mul_hi64(uint64_t a, uint64_t b) { // cores. To overcome this, some special platform-specific API should be // called to set group affinity for each thread. Original code from Texel by // Peter Österlund. - namespace WinProcGroup { void bindThisThread(size_t idx); } diff --git a/src/movegen.cpp b/src/movegen.cpp index cf457d1176c..16da659d5e3 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -241,7 +241,6 @@ ExtMove* generate_all(const Position& pos, ExtMove* moveList) { // except castling and promotions // // Returns a pointer to the end of the move list. - template ExtMove* generate(const Position& pos, ExtMove* moveList) { diff --git a/src/movepick.cpp b/src/movepick.cpp index 41ad0dd6e8d..ff282262a5b 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -58,7 +58,7 @@ enum Stages { QCHECK }; -// partial_insertion_sort() sorts moves in descending order up to and including +// Sort moves in descending order up to and including // a given limit. The order of moves smaller than the limit is left unspecified. void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) { @@ -103,7 +103,7 @@ MovePicker::MovePicker(const Position& p, stage = (pos.checkers() ? EVASION_TT : MAIN_TT) + !(ttm && pos.pseudo_legal(ttm)); } -// MovePicker constructor for quiescence search +// Constructor for quiescence search MovePicker::MovePicker(const Position& p, Move ttm, Depth d, @@ -123,7 +123,7 @@ MovePicker::MovePicker(const Position& p, stage = (pos.checkers() ? EVASION_TT : QSEARCH_TT) + !(ttm && pos.pseudo_legal(ttm)); } -// MovePicker constructor for ProbCut: we generate captures with SEE greater +// Constructor for ProbCut: we generate captures with SEE greater // than or equal to the given threshold. MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph) : pos(p), @@ -136,7 +136,7 @@ MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePiece + !(ttm && pos.capture_stage(ttm) && pos.pseudo_legal(ttm) && pos.see_ge(ttm, threshold)); } -// MovePicker::score() assigns a numerical value to each move in a list, used +// Assigns a numerical value to each move in a list, used // for sorting. Captures are ordered by Most Valuable Victim (MVV), preferring // captures with a good history. Quiets moves are ordered using the history tables. template @@ -216,7 +216,7 @@ void MovePicker::score() { } } -// MovePicker::select() returns the next move satisfying a predicate function. +// Returns the next move satisfying a predicate function. // It never returns the TT move. template Move MovePicker::select(Pred filter) { @@ -234,7 +234,7 @@ Move MovePicker::select(Pred filter) { return MOVE_NONE; } -// MovePicker::next_move() is the most important method of the MovePicker class. It +// Most important method of the MovePicker class. It // returns a new pseudo-legal move every time it is called until there are no more // moves left, picking the move with the highest score from a list of generated moves. Move MovePicker::next_move(bool skipQuiets) { diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 679192d4710..ea53a5102fa 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -233,7 +233,7 @@ static NnueEvalTrace trace_evaluate(const Position& pos) { constexpr std::string_view PieceToChar(" PNBRQK pnbrqk"); -// format_cp_compact() converts a Value into (centi)pawns and writes it in a buffer. +// Converts a Value into (centi)pawns and writes it in a buffer. // The buffer must have capacity for at least 5 chars. static void format_cp_compact(Value v, char* buffer) { @@ -270,7 +270,7 @@ static void format_cp_compact(Value v, char* buffer) { } -// format_cp_aligned_dot() converts a Value into pawns, always keeping two decimals +// Converts a Value into pawns, always keeping two decimals static void format_cp_aligned_dot(Value v, std::stringstream& stream) { const double pawns = std::abs(0.01 * UCI::to_cp(v)); @@ -282,7 +282,7 @@ static void format_cp_aligned_dot(Value v, std::stringstream& stream) { } -// trace() returns a string with the value of each piece on a board, +// Returns a string with the value of each piece on a board, // and a table for (PSQT, Layers) values bucket by bucket. std::string trace(Position& pos) { diff --git a/src/nnue/features/half_ka_v2_hm.cpp b/src/nnue/features/half_ka_v2_hm.cpp index 6c3fdfdb60b..6d1b60ce43d 100644 --- a/src/nnue/features/half_ka_v2_hm.cpp +++ b/src/nnue/features/half_ka_v2_hm.cpp @@ -50,7 +50,7 @@ void HalfKAv2_hm::append_active_indices(const Position& pos, IndexList& active) template void HalfKAv2_hm::append_active_indices(const Position& pos, IndexList& active); template void HalfKAv2_hm::append_active_indices(const Position& pos, IndexList& active); -// append_changed_indices() : get a list of indices for recently changed features +// Get a list of indices for recently changed features template void HalfKAv2_hm::append_changed_indices(Square ksq, const DirtyPiece& dp, diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index f4c55e001e2..cf90850126b 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -85,7 +85,7 @@ constexpr IntType ceil_to_multiple(IntType n, IntType base) { } -// read_little_endian() is our utility to read an integer (signed or unsigned, any size) +// Utility to read an integer (signed or unsigned, any size) // from a stream in little-endian order. We swap the byte order after the read if // necessary to return a result with the byte ordering of the compiling machine. template @@ -110,7 +110,7 @@ inline IntType read_little_endian(std::istream& stream) { } -// write_little_endian() is our utility to write an integer (signed or unsigned, any size) +// Utility to write an integer (signed or unsigned, any size) // to a stream in little-endian order. We swap the byte order before the write if // necessary to always write in little endian order, independently of the byte // ordering of the compiling machine. @@ -141,7 +141,7 @@ inline void write_little_endian(std::ostream& stream, IntType value) { } -// read_little_endian(s, out, N) : read integers in bulk from a little indian stream. +// Read integers in bulk from a little indian stream. // This reads N integers from stream s and put them in array out. template inline void read_little_endian(std::istream& stream, IntType* out, std::size_t count) { @@ -153,7 +153,7 @@ inline void read_little_endian(std::istream& stream, IntType* out, std::size_t c } -// write_little_endian(s, values, N) : write integers in bulk to a little indian stream. +// Write integers in bulk to a little indian stream. // This takes N integers from array values and writes them on stream s. template inline void write_little_endian(std::ostream& stream, const IntType* values, std::size_t count) { @@ -165,7 +165,7 @@ inline void write_little_endian(std::ostream& stream, const IntType* values, std } -// read_leb_128(s, out, N) : read N signed integers from the stream s, putting them in +// Read N signed integers from the stream s, putting them in // the array out. The stream is assumed to be compressed using the signed LEB128 format. // See https://en.wikipedia.org/wiki/LEB128 for a description of the compression scheme. template @@ -215,7 +215,7 @@ inline void read_leb_128(std::istream& stream, IntType* out, std::size_t count) } -// write_leb_128(s, values, N) : write signed integers to a stream with LEB128 compression. +// Write signed integers to a stream with LEB128 compression. // This takes N integers from array values, compress them with the LEB128 algorithm and // writes the result on the stream s. // See https://en.wikipedia.org/wiki/LEB128 for a description of the compression scheme. diff --git a/src/position.cpp b/src/position.cpp index f7354b3d77c..37c586abbaa 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -61,8 +61,7 @@ constexpr Piece Pieces[] = {W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING, } // namespace -// operator<<(Position) returns an ASCII representation of the position - +// Returns an ASCII representation of the position std::ostream& operator<<(std::ostream& os, const Position& pos) { os << "\n +---+---+---+---+---+---+---+---+\n"; @@ -114,8 +113,7 @@ Key cuckoo[8192]; Move cuckooMove[8192]; -// Position::init() initializes at startup the various arrays used to compute hash keys - +// Initializes at startup the various arrays used to compute hash keys void Position::init() { PRNG rng(1070372); @@ -158,10 +156,9 @@ void Position::init() { } -// Position::set() initializes the position object with the given FEN string. +// Initializes the position object with the given FEN string. // This function is not very robust - make sure that input FENs are correct, // this is assumed to be the responsibility of the GUI. - Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Thread* th) { /* A FEN string defines a particular position using only the ASCII character set. @@ -298,9 +295,8 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th } -// Position::set_castling_right() is a helper function used to set castling +// Helper function used to set castling // rights given the corresponding color and the rook starting square. - void Position::set_castling_right(Color c, Square rfrom) { Square kfrom = square(c); @@ -318,8 +314,7 @@ void Position::set_castling_right(Color c, Square rfrom) { } -// Position::set_check_info() sets king attacks to detect if a move gives check - +// Sets king attacks to detect if a move gives check void Position::set_check_info() const { update_slider_blockers(WHITE); @@ -336,10 +331,9 @@ void Position::set_check_info() const { } -// Position::set_state() computes the hash keys of the position, and other +// Computes the hash keys of the position, and other // data that once computed is updated incrementally as moves are made. // The function is only used when a new position is set up - void Position::set_state() const { st->key = st->materialKey = 0; @@ -372,10 +366,9 @@ void Position::set_state() const { } -// Position::set() is an overload to initialize the position object with +// Overload to initialize the position object with // the given endgame code string like "KBPKN". It is mainly a helper to // get the material key out of an endgame code. - Position& Position::set(const string& code, Color c, StateInfo* si) { assert(code[0] == 'K'); @@ -395,9 +388,8 @@ Position& Position::set(const string& code, Color c, StateInfo* si) { } -// Position::fen() returns a FEN representation of the position. In case of +// Returns a FEN representation of the position. In case of // Chess960 the Shredder-FEN notation is used. This is mainly a debugging function. - string Position::fen() const { int emptyCnt; @@ -444,7 +436,7 @@ string Position::fen() const { return ss.str(); } -// update_slider_blockers() calculates st->blockersForKing[c] and st->pinners[~c], +// Calculates st->blockersForKing[c] and st->pinners[~c], // which store respectively the pieces preventing king of color c from being in check // and the slider pieces of color ~c pinning pieces of color c to the king. void Position::update_slider_blockers(Color c) const { @@ -475,9 +467,8 @@ void Position::update_slider_blockers(Color c) const { } -// Position::attackers_to() computes a bitboard of all pieces which attack a +// Computes a bitboard of all pieces which attack a // given square. Slider attacks use the occupied bitboard to indicate occupancy. - Bitboard Position::attackers_to(Square s, Bitboard occupied) const { return (pawn_attacks_bb(BLACK, s) & pieces(WHITE, PAWN)) @@ -489,8 +480,7 @@ Bitboard Position::attackers_to(Square s, Bitboard occupied) const { } -// Position::legal() tests whether a pseudo-legal move is legal - +// Tests whether a pseudo-legal move is legal bool Position::legal(Move m) const { assert(is_ok(m)); @@ -549,10 +539,9 @@ bool Position::legal(Move m) const { } -// Position::pseudo_legal() takes a random move and tests whether the move is +// Takes a random move and tests whether the move is // pseudo-legal. It is used to validate moves from TT that can be corrupted // due to SMP concurrent access or hash position key aliasing. - bool Position::pseudo_legal(const Move m) const { Color us = sideToMove; @@ -620,8 +609,7 @@ bool Position::pseudo_legal(const Move m) const { } -// Position::gives_check() tests whether a pseudo-legal move gives a check - +// Tests whether a pseudo-legal move gives a check bool Position::gives_check(Move m) const { assert(is_ok(m)); @@ -669,10 +657,9 @@ bool Position::gives_check(Move m) const { } -// Position::do_move() makes a move, and saves all information necessary +// Makes a move, and saves all information necessary // to a StateInfo object. The move is assumed to be legal. Pseudo-legal // moves should be filtered out before this function is called. - void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { assert(is_ok(m)); @@ -867,9 +854,8 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { } -// Position::undo_move() unmakes a move. When it returns, the position should +// Unmakes a move. When it returns, the position should // be restored to exactly the same state as before the move was made. - void Position::undo_move(Move m) { assert(is_ok(m)); @@ -931,7 +917,7 @@ void Position::undo_move(Move m) { } -// Position::do_castling() is a helper used to do/undo a castling move. This +// Helper used to do/undo a castling move. This // is a bit tricky in Chess960 where from/to squares can overlap. template void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto) { @@ -963,9 +949,8 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ } -// Position::do_null_move() is used to do a "null move": it flips +// Used to do a "null move": it flips // the side to move without executing any move on the board. - void Position::do_null_move(StateInfo& newSt) { assert(!checkers()); @@ -1003,8 +988,7 @@ void Position::do_null_move(StateInfo& newSt) { } -// Position::undo_null_move() must be used to undo a "null move" - +// Must be used to undo a "null move" void Position::undo_null_move() { assert(!checkers()); @@ -1014,10 +998,9 @@ void Position::undo_null_move() { } -// Position::key_after() computes the new hash key after the given move. Needed +// Computes the new hash key after the given move. Needed // for speculative prefetch. It doesn't recognize special moves like castling, // en passant and promotions. - Key Position::key_after(Move m) const { Square from = from_sq(m); @@ -1035,10 +1018,9 @@ Key Position::key_after(Move m) const { } -// Position::see_ge (Static Exchange Evaluation Greater or Equal) tests if the -// SEE value of move is greater or equal to the given threshold. We'll use an +// Tests if the SEE (Static Exchange Evaluation) +// value of move is greater or equal to the given threshold. We'll use an // algorithm similar to alpha-beta pruning with a null window. - bool Position::see_ge(Move m, Value threshold) const { assert(is_ok(m)); @@ -1140,9 +1122,8 @@ bool Position::see_ge(Move m, Value threshold) const { return bool(res); } -// Position::is_draw() tests whether the position is drawn by 50-move rule +// Tests whether the position is drawn by 50-move rule // or by repetition. It does not detect stalemates. - bool Position::is_draw(int ply) const { if (st->rule50 > 99 && (!checkers() || MoveList(*this).size())) @@ -1154,9 +1135,8 @@ bool Position::is_draw(int ply) const { } -// Position::has_repeated() tests whether there has been at least one repetition +// Tests whether there has been at least one repetition // of positions since the last capture or pawn move. - bool Position::has_repeated() const { StateInfo* stc = st; @@ -1172,9 +1152,8 @@ bool Position::has_repeated() const { } -// Position::has_game_cycle() tests if the position has a move which draws by repetition, +// Tests if the position has a move which draws by repetition, // or an earlier position has a move that directly reaches the current position. - bool Position::has_game_cycle(int ply) const { int j; @@ -1220,9 +1199,8 @@ bool Position::has_game_cycle(int ply) const { } -// Position::flip() flips position with the white and black sides reversed. This +// Flips position with the white and black sides reversed. This // is only useful for debugging e.g. for finding evaluation symmetry bugs. - void Position::flip() { string f, token; @@ -1255,10 +1233,9 @@ void Position::flip() { } -// Position::pos_is_ok() performs some consistency checks for the +// Performs some consistency checks for the // position object and raise an assert if something wrong is detected. // This is meant to be helpful when debugging. - bool Position::pos_is_ok() const { constexpr bool Fast = true; // Quick (default) or full check? diff --git a/src/search.cpp b/src/search.cpp index 43d78892f60..933cd154e0d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -148,7 +148,7 @@ void update_all_stats(const Position& pos, int captureCount, Depth depth); -// perft() is our utility to verify move generation. All the leaf nodes up +// Utility to verify move generation. All the leaf nodes up // to the given depth are generated and counted, and the sum is returned. template uint64_t perft(Position& pos, Depth depth) { @@ -179,8 +179,7 @@ uint64_t perft(Position& pos, Depth depth) { } // namespace -// Search::init() is called at startup to initialize various lookup tables - +// Called at startup to initialize various lookup tables void Search::init() { for (int i = 1; i < MAX_MOVES; ++i) @@ -188,8 +187,7 @@ void Search::init() { } -// Search::clear() resets search state to its initial value - +// Resets search state to its initial value void Search::clear() { Threads.main()->wait_for_search_finished(); @@ -201,9 +199,8 @@ void Search::clear() { } -// MainThread::search() is started when the program receives the UCI 'go' +// Called when the program receives the UCI 'go' // command. It searches from the root position and outputs the "bestmove". - void MainThread::search() { if (Limits.perft) @@ -277,10 +274,9 @@ void MainThread::search() { } -// Thread::search() is the main iterative deepening loop. It calls search() +// Main iterative deepening loop. It calls search() // repeatedly with increasing depth until the allocated thinking time has been // consumed, the user stops the search, or the maximum search depth is reached. - void Thread::search() { // Allocate stack with extra size to allow access from (ss-7) to (ss+2): @@ -521,8 +517,7 @@ void Thread::search() { namespace { -// search<>() is the main search function for both PV and non-PV nodes - +// Main search function for both PV and non-PV nodes template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) { @@ -1346,7 +1341,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo } -// qsearch() is the quiescence search function, which is called by the main search +// Quiescence search function, which is called by the main search // function with zero depth, or recursively with further decreasing depth per call. // (~155 Elo) template @@ -1593,10 +1588,9 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { } -// value_to_tt() adjusts a mate or TB score from "plies to mate from the root" +// Adjusts a mate or TB score from "plies to mate from the root" // to "plies to mate from the current position". Standard scores are unchanged. // The function is called before storing a value in the transposition table. - Value value_to_tt(Value v, int ply) { assert(v != VALUE_NONE); @@ -1605,12 +1599,11 @@ Value value_to_tt(Value v, int ply) { } -// value_from_tt() is the inverse of value_to_tt(): it adjusts a mate or TB score +// Inverse of value_to_tt(): it adjusts a mate or TB score // from the transposition table (which refers to the plies to mate/be mated from // current position) to "plies to mate/be mated (TB win/loss) from the root". // However, to avoid potentially false mate scores related to the 50 moves rule // and the graph history interaction problem, we return an optimal TB score instead. - Value value_from_tt(Value v, int ply, int r50c) { if (v == VALUE_NONE) @@ -1636,8 +1629,7 @@ Value value_from_tt(Value v, int ply, int r50c) { } -// update_pv() adds current move and appends child pv[] - +// Adds current move and appends child pv[] void update_pv(Move* pv, Move move, const Move* childPv) { for (*pv++ = move; childPv && *childPv != MOVE_NONE;) @@ -1646,8 +1638,7 @@ void update_pv(Move* pv, Move move, const Move* childPv) { } -// update_all_stats() updates stats at the end of search() when a bestMove is found - +// Updates stats at the end of search() when a bestMove is found void update_all_stats(const Position& pos, Stack* ss, Move bestMove, @@ -1709,9 +1700,8 @@ void update_all_stats(const Position& pos, } -// update_continuation_histories() updates histories of the move pairs formed +// Updates histories of the move pairs formed // by moves at ply -1, -2, -4, and -6 with current move. - void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { for (int i : {1, 2, 3, 4, 6}) @@ -1725,8 +1715,7 @@ void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { } -// update_quiet_stats() updates move sorting heuristics - +// Updates move sorting heuristics void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus) { // Update killers @@ -1751,7 +1740,6 @@ void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus) { // When playing with strength handicap, choose the best move among a set of RootMoves // using a statistical rule dependent on 'level'. Idea by Heinz van Saanen. - Move Skill::pick_best(size_t multiPV) { const RootMoves& rootMoves = Threads.main()->rootMoves; @@ -1786,9 +1774,8 @@ Move Skill::pick_best(size_t multiPV) { } // namespace -// MainThread::check_time() is used to print debug info and, more importantly, +// Used to print debug info and, more importantly, // to detect when we are out of available time and thus stop the search. - void MainThread::check_time() { if (--callsCnt > 0) @@ -1819,9 +1806,8 @@ void MainThread::check_time() { } -// UCI::pv() formats PV information according to the UCI protocol. UCI requires +// Formats PV information according to the UCI protocol. UCI requires // that all (if any) unsearched PV lines are sent using a previous search score. - string UCI::pv(const Position& pos, Depth depth) { std::stringstream ss; @@ -1874,11 +1860,10 @@ string UCI::pv(const Position& pos, Depth depth) { } -// RootMove::extract_ponder_from_tt() is called in case we have no ponder move +// Called in case we have no ponder move // before exiting the search, for instance, in case we stop the search during a // fail high at root. We try hard to have a ponder move to return to the GUI, // otherwise in case of 'ponder on' we have nothing to think about. - bool RootMove::extract_ponder_from_tt(Position& pos) { StateInfo st; diff --git a/src/search.h b/src/search.h index 37cd5e5a686..b2d22e612c4 100644 --- a/src/search.h +++ b/src/search.h @@ -36,7 +36,6 @@ namespace Search { // Stack struct keeps track of the information we need to remember from nodes // shallower and deeper in the tree during the search. Each search thread has // its own array of Stack objects, indexed by the current ply. - struct Stack { Move* pv; PieceToHistory* continuationHistory; @@ -58,14 +57,14 @@ struct Stack { // RootMove struct is used for moves at the root of the tree. For each root move // we store a score and a PV (really a refutation in the case of moves which // fail low). Score is normally set at -VALUE_INFINITE for all non-pv moves. - struct RootMove { explicit RootMove(Move m) : pv(1, m) {} bool extract_ponder_from_tt(Position& pos); bool operator==(const Move& m) const { return pv[0] == m; } - bool operator<(const RootMove& m) const { // Sort in descending order + // Sort in descending order + bool operator<(const RootMove& m) const { return m.score != score ? m.score < score : m.previousScore < previousScore; } @@ -89,7 +88,8 @@ using RootMoves = std::vector; struct LimitsType { - LimitsType() { // Init explicitly due to broken value-initialization of non POD in MSVC + // Init explicitly due to broken value-initialization of non POD in MSVC + LimitsType() { time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = npmsec = movetime = TimePoint(0); movestogo = depth = mate = perft = infinite = 0; nodes = 0; diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 31597f835a8..e23631575e6 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1317,7 +1317,7 @@ WDLScore search(Position& pos, ProbeState* result) { } // namespace -// Tablebases::init() is called at startup and after every change to +// Called at startup and after every change to // "SyzygyPath" UCI option to (re)create the various tables. It is not thread // safe, nor it needs to be. void Tablebases::init(const std::string& paths) { diff --git a/src/thread.cpp b/src/thread.cpp index 9f8a63bdc01..fdf89095b5e 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -40,9 +40,8 @@ namespace Stockfish { ThreadPool Threads; // Global object -// Thread constructor launches the thread and waits until it goes to sleep +// Constructor launches the thread and waits until it goes to sleep // in idle_loop(). Note that 'searching' and 'exit' should be already set. - Thread::Thread(size_t n) : idx(n), stdThread(&Thread::idle_loop, this) { @@ -51,9 +50,8 @@ Thread::Thread(size_t n) : } -// Thread destructor wakes up the thread in idle_loop() and waits +// Destructor wakes up the thread in idle_loop() and waits // for its termination. Thread should be already waiting. - Thread::~Thread() { assert(!searching); @@ -64,8 +62,7 @@ Thread::~Thread() { } -// Thread::clear() reset histories, usually before a new game - +// Reset histories, usually before a new game void Thread::clear() { counterMoves.fill(MOVE_NONE); @@ -80,8 +77,7 @@ void Thread::clear() { } -// Thread::start_searching() wakes up the thread that will start the search - +// Wakes up the thread that will start the search void Thread::start_searching() { mutex.lock(); searching = true; @@ -90,9 +86,8 @@ void Thread::start_searching() { } -// Thread::wait_for_search_finished() blocks on the condition variable +// Blocks on the condition variable // until the thread has finished searching. - void Thread::wait_for_search_finished() { std::unique_lock lk(mutex); @@ -100,7 +95,7 @@ void Thread::wait_for_search_finished() { } -// Thread::idle_loop() is where the thread is parked, blocked on the +// Thread gets parked here, blocked on the // condition variable, when it has no work to do. void Thread::idle_loop() { @@ -129,10 +124,9 @@ void Thread::idle_loop() { } } -// ThreadPool::set() creates/destroys threads to match the requested number. +// Creates/destroys threads to match the requested number. // Created and launched threads will immediately go to sleep in idle_loop. // Upon resizing, threads are recreated to allow for binding if necessary. - void ThreadPool::set(size_t requested) { if (threads.size() > 0) // destroy any existing thread(s) @@ -160,8 +154,7 @@ void ThreadPool::set(size_t requested) { } -// ThreadPool::clear() sets threadPool data to initial values - +// Sets threadPool data to initial values void ThreadPool::clear() { for (Thread* th : threads) @@ -174,9 +167,8 @@ void ThreadPool::clear() { } -// ThreadPool::start_thinking() wakes up main thread waiting in idle_loop() and +// Wakes up main thread waiting in idle_loop() and // returns immediately. Main thread will wake up other threads and start the search. - void ThreadPool::start_thinking(Position& pos, StateListPtr& states, const Search::LimitsType& limits, diff --git a/src/thread.h b/src/thread.h index 4a077962661..5f33b7369d3 100644 --- a/src/thread.h +++ b/src/thread.h @@ -38,7 +38,6 @@ namespace Stockfish { // per-thread pawn and material hash tables so that once we get a // pointer to an entry its lifetime is unlimited and we don't have // to care about someone changing the entry under our feet. - class Thread { std::mutex mutex; @@ -76,7 +75,6 @@ class Thread { // MainThread is a derived class specific for main thread - struct MainThread: public Thread { using Thread::Thread; @@ -97,7 +95,6 @@ struct MainThread: public Thread { // ThreadPool struct handles all the threads-related stuff like init, starting, // parking and, most importantly, launching a thread. All the access to threads // is done through this class. - struct ThreadPool { void start_thinking(Position&, StateListPtr&, const Search::LimitsType&, bool = false); diff --git a/src/timeman.cpp b/src/timeman.cpp index cf0e08ed789..7e77a4add05 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -29,11 +29,10 @@ namespace Stockfish { TimeManagement Time; // Our global time management object -// TimeManagement::init() is called at the beginning of the search and calculates +// Called at the beginning of the search and calculates // the bounds of time allowed for the current game ply. We currently support: // 1) x basetime (+ z increment) // 2) x moves in y seconds (+ z increment) - void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { // If we have no time, no need to initialize TM, except for the start time, diff --git a/src/timeman.h b/src/timeman.h index 4b9b62bd2a9..6c56d506b3f 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -30,7 +30,6 @@ namespace Stockfish { // The TimeManagement class computes the optimal time to think depending on // the maximum available time, the game move number, and other parameters. - class TimeManagement { public: void init(Search::LimitsType& limits, Color us, int ply); diff --git a/src/tt.cpp b/src/tt.cpp index a3ad0a78d4f..816d43f8603 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -33,9 +33,8 @@ namespace Stockfish { TranspositionTable TT; // Our global transposition table -// TTEntry::save() populates the TTEntry with a new node's data, possibly +// Populates the TTEntry with a new node's data, possibly // overwriting an old position. The update is not atomic and can be racy. - void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) { // Preserve any existing move for the same position @@ -57,10 +56,9 @@ void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) } -// TranspositionTable::resize() sets the size of the transposition table, +// Sets the size of the transposition table, // measured in megabytes. Transposition table consists of a power of 2 number // of clusters and each cluster consists of ClusterSize number of TTEntry. - void TranspositionTable::resize(size_t mbSize) { Threads.main()->wait_for_search_finished(); @@ -80,9 +78,8 @@ void TranspositionTable::resize(size_t mbSize) { } -// TranspositionTable::clear() initializes the entire transposition table to zero, -// in a multi-threaded way. - +// Initializes the entire transposition table to zero, +// in a multi-threaded way. void TranspositionTable::clear() { std::vector threads; @@ -109,13 +106,12 @@ void TranspositionTable::clear() { } -// TranspositionTable::probe() looks up the current position in the transposition +// Looks up the current position in the transposition // table. It returns true and a pointer to the TTEntry if the position is found. // Otherwise, it returns false and a pointer to an empty or least valuable TTEntry // to be replaced later. The replace value of an entry is calculated as its depth // minus 8 times its relative age. TTEntry t1 is considered more valuable than // TTEntry t2 if its replace value is greater than that of t2. - TTEntry* TranspositionTable::probe(const Key key, bool& found) const { TTEntry* const tte = first_entry(key); @@ -148,7 +144,7 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const { } -// TranspositionTable::hashfull() returns an approximation of the hashtable +// Returns an approximation of the hashtable // occupation during a search. The hash is x permill full, as per UCI protocol. int TranspositionTable::hashfull() const { diff --git a/src/tt.h b/src/tt.h index 628dfba28f7..12fedd2d42f 100644 --- a/src/tt.h +++ b/src/tt.h @@ -37,7 +37,6 @@ namespace Stockfish { // move 16 bit // value 16 bit // eval value 16 bit - struct TTEntry { Move move() const { return Move(move16); } @@ -65,7 +64,6 @@ struct TTEntry { // contains information on exactly one position. The size of a Cluster should // divide the size of a cache line for best performance, as the cacheline is // prefetched when possible. - class TranspositionTable { static constexpr int ClusterSize = 3; diff --git a/src/types.h b/src/types.h index c76efd077d1..7ac2f84951a 100644 --- a/src/types.h +++ b/src/types.h @@ -321,21 +321,17 @@ constexpr Square operator-(Square s, Direction d) { return Square(int(s) - int(d inline Square& operator+=(Square& s, Direction d) { return s = s + d; } inline Square& operator-=(Square& s, Direction d) { return s = s - d; } -constexpr Color operator~(Color c) { - return Color(c ^ BLACK); // Toggle color -} +// Toggle color +constexpr Color operator~(Color c) { return Color(c ^ BLACK); } -constexpr Square flip_rank(Square s) { // Swap A1 <-> A8 - return Square(s ^ SQ_A8); -} +// Swap A1 <-> A8 +constexpr Square flip_rank(Square s) { return Square(s ^ SQ_A8); } -constexpr Square flip_file(Square s) { // Swap A1 <-> H1 - return Square(s ^ SQ_H1); -} +// Swap A1 <-> H1 +constexpr Square flip_file(Square s) { return Square(s ^ SQ_H1); } -constexpr Piece operator~(Piece pc) { - return Piece(pc ^ 8); // Swap color of piece B_KNIGHT <-> W_KNIGHT -} +// Swap color of piece B_KNIGHT <-> W_KNIGHT +constexpr Piece operator~(Piece pc) { return Piece(pc ^ 8); } constexpr CastlingRights operator&(Color c, CastlingRights cr) { return CastlingRights((c == WHITE ? WHITE_CASTLING : BLACK_CASTLING) & cr); diff --git a/src/uci.cpp b/src/uci.cpp index 0671cb5ff65..1d8f5bdc05c 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -49,11 +49,10 @@ namespace { const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; -// position() is called when the engine receives the "position" UCI command. +// Called when the engine receives the "position" UCI command. // It sets up the position that is described in the given FEN string ("fen") or // the initial position ("startpos") and then makes the moves given in the following // move list ("moves"). - void position(Position& pos, std::istringstream& is, StateListPtr& states) { Move m; @@ -83,9 +82,8 @@ void position(Position& pos, std::istringstream& is, StateListPtr& states) { } } -// trace_eval() prints the evaluation of the current position, consistent with +// Prints the evaluation of the current position, consistent with // the UCI options set so far. - void trace_eval(Position& pos) { StateListPtr states(new std::deque(1)); @@ -98,7 +96,7 @@ void trace_eval(Position& pos) { } -// setoption() is called when the engine receives the "setoption" UCI command. +// Called when the engine receives the "setoption" UCI command. // The function updates the UCI option ("name") to the given value ("value"). void setoption(std::istringstream& is) { @@ -124,7 +122,7 @@ void setoption(std::istringstream& is) { } -// go() is called when the engine receives the "go" UCI command. The function +// Called when the engine receives the "go" UCI command. The function // sets the thinking time and other parameters from the input string, then starts // with a search. @@ -170,7 +168,7 @@ void go(Position& pos, std::istringstream& is, StateListPtr& states) { } -// bench() is called when the engine receives the "bench" command. +// Called when the engine receives the "bench" command. // First, a list of UCI commands is set up according to the bench // parameters, then it is run one by one, printing a summary at the end. @@ -252,12 +250,11 @@ int win_rate_model(Value v, int ply) { } // namespace -// UCI::loop() waits for a command from the stdin, parses it, and then calls the appropriate +// Waits for a command from the stdin, parses it, and then calls the appropriate // function. It also intercepts an end-of-file (EOF) indication from the stdin to ensure a // graceful exit if the GUI dies unexpectedly. When called with some command-line arguments, // like running 'bench', the function returns immediately after the command is executed. // In addition to the UCI ones, some additional debug commands are also supported. - void UCI::loop(int argc, char* argv[]) { Position pos; @@ -346,12 +343,11 @@ void UCI::loop(int argc, char* argv[]) { // without treatment of mate and similar special scores. int UCI::to_cp(Value v) { return 100 * v / UCI::NormalizeToPawnValue; } -// UCI::value() converts a Value to a string by adhering to the UCI protocol specification: +// Converts a Value to a string by adhering to the UCI protocol specification: // // cp The score from the engine's point of view in centipawns. // mate Mate in 'y' moves (not plies). If the engine is getting mated, // uses negative values for 'y'. - std::string UCI::value(Value v) { assert(-VALUE_INFINITE < v && v < VALUE_INFINITE); @@ -372,9 +368,8 @@ std::string UCI::value(Value v) { } -// UCI::wdl() reports the win-draw-loss (WDL) statistics given an evaluation +// Reports the win-draw-loss (WDL) statistics given an evaluation // and a game ply based on the data gathered for fishtest LTC games. - std::string UCI::wdl(Value v, int ply) { std::stringstream ss; @@ -388,18 +383,16 @@ std::string UCI::wdl(Value v, int ply) { } -// UCI::square() converts a Square to a string in algebraic notation (g1, a7, etc.) - +// Converts a Square to a string in algebraic notation (g1, a7, etc.) std::string UCI::square(Square s) { return std::string{char('a' + file_of(s)), char('1' + rank_of(s))}; } -// UCI::move() converts a Move to a string in coordinate notation (g1f3, a7a8q). +// Converts a Move to a string in coordinate notation (g1f3, a7a8q). // The only special case is castling where the e1g1 notation is printed in // standard chess mode and in e1h1 notation it is printed in Chess960 mode. // Internally, all castling moves are always encoded as 'king captures rook'. - std::string UCI::move(Move m, bool chess960) { if (m == MOVE_NONE) @@ -423,9 +416,8 @@ std::string UCI::move(Move m, bool chess960) { } -// UCI::to_move() converts a string representing a move in coordinate notation +// Converts a string representing a move in coordinate notation // (g1f3, a7a8q) to the corresponding legal Move, if any. - Move UCI::to_move(const Position& pos, std::string& str) { if (str.length() == 5) diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 8db4233af5f..d0db1c76dd2 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -60,8 +60,7 @@ bool CaseInsensitiveLess::operator()(const string& s1, const string& s2) const { } -// UCI::init() initializes the UCI options to their hard-coded default values - +// Initializes the UCI options to their hard-coded default values void init(OptionsMap& o) { constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048; @@ -89,9 +88,8 @@ void init(OptionsMap& o) { } -// operator<<() is used to print all the options default values in chronological +// Used to print all the options default values in chronological // insertion order (the idx field) and in the format defined by the UCI protocol. - std::ostream& operator<<(std::ostream& os, const OptionsMap& om) { for (size_t idx = 0; idx < om.size(); ++idx) @@ -172,7 +170,7 @@ bool Option::operator==(const char* s) const { } -// operator<<() inits options and assigns idx in the correct printing order +// Inits options and assigns idx in the correct printing order void Option::operator<<(const Option& o) { @@ -183,10 +181,9 @@ void Option::operator<<(const Option& o) { } -// operator=() updates currentValue and triggers on_change() action. It's up to +// Updates currentValue and triggers on_change() action. It's up to // the GUI to check for option's limits, but we could receive the new value // from the user by console window, so let's check the bounds anyway. - Option& Option::operator=(const string& v) { assert(!type.empty()); From cf3dbcb5acd66efaaa84fa1e24ce7afb062fba08 Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Sat, 21 Oct 2023 17:01:45 +0800 Subject: [PATCH 1236/1766] Time management improvements 1. Tune time management parameters. 2. Scale the optimum time and maximum time parameters based on the amount of time left, using a logarithmic scale. Many acknowledgements to @FauziAkram for tuning the parameters and for the original idea (see https://tests.stockfishchess.org/tests/view/652f0356de6d262d08d333c5). STC: https://tests.stockfishchess.org/tests/view/6533938fde6d262d08d39e4d LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 44320 W: 11301 L: 10982 D: 22037 Ptnml(0-2): 146, 4810, 11920, 5147, 137 LTC: https://tests.stockfishchess.org/tests/view/653477e4de6d262d08d3ae06 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 146442 W: 37338 L: 36811 D: 72293 Ptnml(0-2): 60, 14975, 42645, 15460, 81 Verification runs: 3+0.03: https://tests.stockfishchess.org/tests/view/65364e7ef127f3553505178a 10+0: https://tests.stockfishchess.org/tests/view/65364e9ff127f3553505178f 180+1.8: https://tests.stockfishchess.org/tests/view/65364ec3f127f35535051794 closes https://github.com/official-stockfish/Stockfish/pull/4843 No functional change. Co-Authored-By: FauziAkram <11150271+FauziAkram@users.noreply.github.com> --- src/search.cpp | 10 +++++----- src/timeman.cpp | 14 +++++++++----- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 933cd154e0d..d5416e40759 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -467,15 +467,15 @@ void Thread::search() { // Do we have time for the next iteration? Can we stop searching now? if (Limits.use_time_management() && !Threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (69 + 13 * (mainThread->bestPreviousAverageScore - bestValue) + double fallingEval = (66 + 14 * (mainThread->bestPreviousAverageScore - bestValue) + 6 * (mainThread->iterValue[iterIdx] - bestValue)) - / 619.6; + / 583.0; fallingEval = std::clamp(fallingEval, 0.5, 1.5); // If the bestMove is stable over several iterations, reduce time accordingly - timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.57 : 0.65; - double reduction = (1.4 + mainThread->previousTimeReduction) / (2.08 * timeReduction); - double bestMoveInstability = 1 + 1.8 * totBestMoveChanges / Threads.size(); + timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.56 : 0.69; + double reduction = (1.4 + mainThread->previousTimeReduction) / (2.03 * timeReduction); + double bestMoveInstability = 1 + 1.79 * totBestMoveChanges / Threads.size(); double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability; diff --git a/src/timeman.cpp b/src/timeman.cpp index 7e77a4add05..1253d434672 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -72,7 +72,11 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { - moveOverhead * (2 + mtg)); // Use extra time with larger increments - double optExtra = std::clamp(1.0 + 12.0 * limits.inc[us] / limits.time[us], 1.0, 1.12); + double optExtra = std::clamp(1.0 + 12.5 * limits.inc[us] / limits.time[us], 1.0, 1.12); + + // Calculate time constants based on current time left. + double optConstant = std::min(0.00335 + 0.0003 * std::log10(limits.time[us] / 1000.0), 0.0048); + double maxConstant = std::max(3.6 + 3.0 * std::log10(limits.time[us] / 1000.0), 2.7); // A user may scale time usage by setting UCI option "Slow Mover" // Default is 100 and changing this value will probably lose elo. @@ -83,10 +87,10 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { // game time for the current move, so also cap to 20% of available game time. if (limits.movestogo == 0) { - optScale = std::min(0.0120 + std::pow(ply + 3.0, 0.45) * 0.0039, + optScale = std::min(0.0120 + std::pow(ply + 3.3, 0.44) * optConstant, 0.2 * limits.time[us] / double(timeLeft)) * optExtra; - maxScale = std::min(7.0, 4.0 + ply / 12.0); + maxScale = std::min(6.8, maxConstant + ply / 12.2); } // x moves in y seconds (+ z increment) @@ -96,10 +100,10 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { maxScale = std::min(6.3, 1.5 + 0.11 * mtg); } - // Never use more than 80% of the available time for this move + // Limit the maximum possible time for this move optimumTime = TimePoint(optScale * timeLeft); maximumTime = - TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, maxScale * optimumTime)) - 10; + TimePoint(std::min(0.84 * limits.time[us] - moveOverhead, maxScale * optimumTime)) - 10; if (Options["Ponder"]) optimumTime += optimumTime / 4; From 49ece9f791b84a261f2a8865d2de51c20a520bc6 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 23 Oct 2023 19:42:08 +0200 Subject: [PATCH 1237/1766] Follow up Makefile changes for clang-format as reported on various OS, these changes help portability closes https://github.com/official-stockfish/Stockfish/pull/4844 No functional change. --- src/Makefile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Makefile b/src/Makefile index 76ef6fdeb8d..59ea7bfe7b5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -153,7 +153,7 @@ dotprod = no arm_version = 0 STRIP = strip -ifneq ($(shell command -v clang-format-17),) +ifneq ($(shell which clang-format-17 2> /dev/null),) CLANG-FORMAT = clang-format-17 else CLANG-FORMAT = clang-format @@ -854,7 +854,8 @@ endif objclean profileclean config-sanity \ icx-profile-use icx-profile-make \ gcc-profile-use gcc-profile-make \ - clang-profile-use clang-profile-make FORCE + clang-profile-use clang-profile-make FORCE \ + format analyze analyze: net config-sanity objclean $(MAKE) -k ARCH=$(ARCH) COMP=$(COMP) $(OBJS) @@ -951,7 +952,7 @@ net: netvariables fi; \ format: - $(CLANG-FORMAT) -i $(SRCS) $(HEADERS) -style=file:../.clang-format + $(CLANG-FORMAT) -i $(SRCS) $(HEADERS) -style=file # default target default: From d6a5c2b0852e0fe7efff1ef7e8bd6d94f962bf8e Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Tue, 24 Oct 2023 07:43:49 +0800 Subject: [PATCH 1238/1766] Small formatting improvements Changes some C style casts to C++ style, and fixes some incorrect comments and variable names. closes #4845 No functional change --- src/bitboard.h | 4 ++-- src/nnue/evaluate_nnue.cpp | 2 +- src/nnue/layers/sqr_clipped_relu.h | 2 +- src/nnue/nnue_common.h | 4 ++-- src/search.cpp | 20 ++++++++++---------- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/bitboard.h b/src/bitboard.h index 24f6deca840..7dbd5329be8 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -298,7 +298,7 @@ inline Square lsb(Bitboard b) { unsigned long idx; _BitScanForward64(&idx, b); - return (Square) idx; + return Square(idx); #else // MSVC, WIN32 unsigned long idx; @@ -332,7 +332,7 @@ inline Square msb(Bitboard b) { unsigned long idx; _BitScanReverse64(&idx, b); - return (Square) idx; + return Square(idx); #else // MSVC, WIN32 diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index ea53a5102fa..e29d3c17b17 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -116,7 +116,7 @@ static bool read_header(std::istream& stream, std::uint32_t* hashValue, std::str static bool write_header(std::ostream& stream, std::uint32_t hashValue, const std::string& desc) { write_little_endian(stream, Version); write_little_endian(stream, hashValue); - write_little_endian(stream, (std::uint32_t) desc.size()); + write_little_endian(stream, std::uint32_t(desc.size())); stream.write(&desc[0], desc.size()); return !stream.fail(); } diff --git a/src/nnue/layers/sqr_clipped_relu.h b/src/nnue/layers/sqr_clipped_relu.h index 987de892f3d..f8e2d497ac0 100644 --- a/src/nnue/layers/sqr_clipped_relu.h +++ b/src/nnue/layers/sqr_clipped_relu.h @@ -93,7 +93,7 @@ class SqrClippedReLU { output[i] = static_cast( // Really should be /127 but we need to make it fast so we right shift // by an extra 7 bits instead. Needs to be accounted for in the trainer. - std::min(127ll, ((long long) input[i] * input[i]) >> (2 * WeightScaleBits + 7))); + std::min(127ll, ((long long) (input[i]) * input[i]) >> (2 * WeightScaleBits + 7))); } } }; diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index cf90850126b..f9cd7fbb597 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -130,11 +130,11 @@ inline void write_little_endian(std::ostream& stream, IntType value) { { for (; i + 1 < sizeof(IntType); ++i) { - u[i] = (std::uint8_t) v; + u[i] = std::uint8_t(v); v >>= 8; } } - u[i] = (std::uint8_t) v; + u[i] = std::uint8_t(v); stream.write(reinterpret_cast(u), sizeof(IntType)); } diff --git a/src/search.cpp b/src/search.cpp index d5416e40759..4b390713d8d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -279,9 +279,9 @@ void MainThread::search() { // consumed, the user stops the search, or the maximum search depth is reached. void Thread::search() { - // Allocate stack with extra size to allow access from (ss-7) to (ss+2): - // (ss-7) is needed for update_continuation_histories(ss-1) which accesses (ss-6), - // (ss+2) is needed for initialization of statScore and killers. + // Allocate stack with extra size to allow access from (ss - 7) to (ss + 2): + // (ss - 7) is needed for update_continuation_histories(ss - 1) which accesses (ss - 6), + // (ss + 2) is needed for initialization of cutOffCnt and killers. Stack stack[MAX_PLY + 10], *ss = stack + 7; Move pv[MAX_PLY + 1]; Value alpha, beta, delta; @@ -363,13 +363,13 @@ void Thread::search() { selDepth = 0; // Reset aspiration window starting size - Value prev = rootMoves[pvIdx].averageScore; - delta = Value(10) + int(prev) * prev / 17470; - alpha = std::max(prev - delta, -VALUE_INFINITE); - beta = std::min(prev + delta, VALUE_INFINITE); + Value avg = rootMoves[pvIdx].averageScore; + delta = Value(10) + int(avg) * avg / 17470; + alpha = std::max(avg - delta, -VALUE_INFINITE); + beta = std::min(avg + delta, VALUE_INFINITE); - // Adjust optimism based on root move's previousScore (~4 Elo) - int opt = 113 * prev / (std::abs(prev) + 109); + // Adjust optimism based on root move's averageScore (~4 Elo) + int opt = 113 * avg / (std::abs(avg) + 109); optimism[us] = Value(opt); optimism[~us] = -optimism[us]; @@ -582,7 +582,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo : 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 + // would be at best mate_in(ss->ply + 1), but if alpha is already bigger because // a shorter mate was found upward in the tree then there is no need to search // because we will never beat the current alpha. Same logic but with reversed // signs apply also in the opposite condition of being mated instead of giving From ec02714b6262e26d6f96c45c4e2527f3d382a9f8 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 23 Oct 2023 22:49:37 +0200 Subject: [PATCH 1239/1766] Cleanup comments and some code reorg. passed STC: https://tests.stockfishchess.org/tests/view/6536dc7dcc309ae83955b04d LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 58048 W: 14693 L: 14501 D: 28854 Ptnml(0-2): 200, 6399, 15595, 6669, 161 closes https://github.com/official-stockfish/Stockfish/pull/4846 No functional change --- src/evaluate.cpp | 10 ++++---- src/movepick.cpp | 8 +++--- src/nnue/evaluate_nnue.cpp | 4 +-- src/nnue/layers/affine_transform.h | 3 ++- src/nnue/nnue_architecture.h | 4 +-- src/nnue/nnue_feature_transformer.h | 6 +++-- src/search.cpp | 39 ++++++++++++++++------------- src/uci.cpp | 5 ++-- 8 files changed, 43 insertions(+), 36 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 4ee3e6fd8b4..c405cfb5538 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -123,11 +123,11 @@ void NNUE::verify() { std::string msg1 = "Network evaluation parameters compatible with the engine must be available."; std::string msg2 = "The network file " + eval_file + " was not loaded successfully."; - std::string msg3 = - "The UCI option EvalFile might need to specify the full path, including the directory name, to the network file."; - std::string msg4 = - "The default net can be downloaded from: https://tests.stockfishchess.org/api/nn/" - + std::string(EvalFileDefaultName); + std::string msg3 = "The UCI option EvalFile might need to specify the full path, " + "including the directory name, to the network file."; + std::string msg4 = "The default net can be downloaded from: " + "https://tests.stockfishchess.org/api/nn/" + + std::string(EvalFileDefaultName); std::string msg5 = "The engine will be terminated now."; sync_cout << "info string ERROR: " << msg1 << sync_endl; diff --git a/src/movepick.cpp b/src/movepick.cpp index ff282262a5b..d2a49706fc2 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -263,11 +263,9 @@ Move MovePicker::next_move(bool skipQuiets) { case GOOD_CAPTURE : if (select([&]() { - return pos.see_ge(*cur, Value(-cur->value)) - ? - // Move losing capture to endBadCaptures to be tried later - true - : (*endBadCaptures++ = *cur, false); + // Move losing capture to endBadCaptures to be tried later + return pos.see_ge(*cur, Value(-cur->value)) ? true + : (*endBadCaptures++ = *cur, false); })) return *(cur - 1); diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index e29d3c17b17..ef6b7e91a60 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -407,8 +407,8 @@ bool save_eval(const std::optional& filename) { { if (currentEvalFileName != EvalFileDefaultName) { - msg = - "Failed to export a net. A non-embedded net can only be saved if the filename is specified"; + msg = "Failed to export a net. " + "A non-embedded net can only be saved if the filename is specified"; sync_cout << msg << sync_endl; return false; diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 3fba45ed87d..44fa5d00a43 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -256,7 +256,8 @@ class AffineTransform { else if constexpr (OutputDimensions == 1) { - // We cannot use AVX512 for the last layer because there's only 32 inputs and the buffer is not padded to 64 elements. + // We cannot use AVX512 for the last layer because there are only 32 inputs + // and the buffer is not padded to 64 elements. #if defined(USE_AVX2) using vec_t = __m256i; #define vec_setzero _mm256_setzero_si256 diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index be0138f14bd..e4c308cb267 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -113,8 +113,8 @@ struct Network { ac_1.propagate(buffer.fc_1_out, buffer.ac_1_out); fc_2.propagate(buffer.ac_1_out, buffer.fc_2_out); - // buffer.fc_0_out[FC_0_OUTPUTS] is such that 1.0 is equal to 127*(1<previous from states_to_update[i+1] or states_to_update[i] == nullptr. - // computed_st must be reachable by repeatedly applying ->previous on states_to_update[0], if not nullptr. + // by repeatedly applying ->previous from states_to_update[i+1] or + // states_to_update[i] == nullptr. + // computed_st must be reachable by repeatedly applying ->previous on + // states_to_update[0], if not nullptr. template void update_accumulator_incremental(const Position& pos, StateInfo* computed_st, diff --git a/src/search.cpp b/src/search.cpp index 4b390713d8d..ad4b458e180 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -379,8 +379,8 @@ void Thread::search() { int failedHighCnt = 0; while (true) { - // Adjust the effective depth searched, but ensure at least one effective increment for every - // four searchAgain steps (see issue #2717). + // Adjust the effective depth searched, but ensure at least one effective increment + // for every four searchAgain steps (see issue #2717). Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt - 3 * (searchAgainCounter + 1) / 4); bestValue = Stockfish::search(rootPos, ss, alpha, beta, adjustedDepth, false); @@ -633,7 +633,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo if (!ttCapture) update_quiet_stats(pos, ss, ttMove, stat_bonus(depth)); - // Extra penalty for early quiet moves of the previous ply (~0 Elo on STC, ~2 Elo on LTC) + // Extra penalty for early quiet moves of + // the previous ply (~0 Elo on STC, ~2 Elo on LTC). if (prevSq != SQ_NONE && (ss - 1)->moveCount <= 2 && !priorCapture) update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + 1)); @@ -715,7 +716,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo } else if (excludedMove) { - // Providing the hint that this node's accumulator will be used often brings significant Elo gain (~13 Elo) + // Providing the hint that this node's accumulator will be used often + // brings significant Elo gain (~13 Elo). Eval::NNUE::hint_common_parent_position(pos); eval = ss->staticEval; } @@ -817,8 +819,9 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo } } - // Step 10. If the position doesn't have a ttMove, decrease depth by 2 - // (or by 4 if the TT entry for the current position was hit and the stored depth is greater than or equal to the current depth). + // Step 10. If the position doesn't have a ttMove, decrease depth by 2, + // or by 4 if the TT entry for the current position was hit and + // the stored depth is greater than or equal to the current depth. // Use qsearch if depth is equal or below zero (~9 Elo) if (PvNode && !ttMove) depth -= 2 + 2 * (ss->ttHit && tte->depth() >= depth); @@ -967,13 +970,15 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo if (capture || givesCheck) { // Futility pruning for captures (~2 Elo) - if (!givesCheck && lmrDepth < 7 && !ss->inCheck - && ss->staticEval + 188 + 206 * lmrDepth + PieceValue[pos.piece_on(to_sq(move))] - + captureHistory[movedPiece][to_sq(move)] - [type_of(pos.piece_on(to_sq(move)))] - / 7 - < alpha) - continue; + if (!givesCheck && lmrDepth < 7 && !ss->inCheck) + { + Piece capturedPiece = pos.piece_on(to_sq(move)); + int futilityEval = + ss->staticEval + 188 + 206 * lmrDepth + PieceValue[capturedPiece] + + captureHistory[movedPiece][to_sq(move)][type_of(capturedPiece)] / 7; + if (futilityEval < alpha) + continue; + } // SEE based pruning for captures and checks (~11 Elo) if (!pos.see_ge(move, Value(-185) * depth)) @@ -1018,9 +1023,9 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // that depth margin and singularBeta margin are known for having non-linear // scaling. Their values are optimized to time controls of 180+1.8 and longer // so changing them requires tests at this type of time controls. - if (!rootNode + // Recursive singular search is avoided. + if (!rootNode && move == ttMove && !excludedMove && depth >= 4 - (thisThread->completedDepth > 24) + 2 * (PvNode && tte->is_pv()) - && move == ttMove && !excludedMove // Avoid recursive singular search && abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { @@ -1053,7 +1058,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo else if (singularBeta >= beta) return singularBeta; - // If the eval of ttMove is greater than beta, we reduce it (negative extension) (~7 Elo) + // If the eval of ttMove is greater than beta, reduce it (negative extension) (~7 Elo) else if (ttValue >= beta) extension = -2 - !PvNode; @@ -1061,7 +1066,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo else if (cutNode) extension = depth < 19 ? -2 : -1; - // If the eval of ttMove is less than value, we reduce it (negative extension) (~1 Elo) + // If the eval of ttMove is less than value, reduce it (negative extension) (~1 Elo) else if (ttValue <= value) extension = -1; } diff --git a/src/uci.cpp b/src/uci.cpp index 1d8f5bdc05c..8139fae4fd8 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -178,8 +178,9 @@ void bench(Position& pos, std::istream& args, StateListPtr& states) { uint64_t num, nodes = 0, cnt = 1; std::vector list = setup_bench(pos, args); - num = count_if(list.begin(), list.end(), - [](const std::string& s) { return s.find("go ") == 0 || s.find("eval") == 0; }); + + num = count_if(list.begin(), list.end(), + [](const std::string& s) { return s.find("go ") == 0 || s.find("eval") == 0; }); TimePoint elapsed = now(); From 0024133b08777b578c1036eb1e4a36343b7fa1bb Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Sun, 22 Oct 2023 12:59:21 -0400 Subject: [PATCH 1240/1766] Update 5 search params for pruning at shallow depth Found by spsa tuning at 45+0.45 with: ``` int fpcEvalOffset = 188; int fpcLmrDepthMult = 206; int histDepthMult = -3232; int histDenom = 5793; int fpEvalOffset = 115; int negSeeDepthMultSq = -27; TUNE(SetRange(0, 394), fpcEvalOffset); TUNE(SetRange(0, 412), fpcLmrDepthMult); TUNE(SetRange(-6464, -1616), histDepthMult); TUNE(SetRange(2896, 11586), histDenom); TUNE(SetRange(0, 230), fpEvalOffset); TUNE(SetRange(-54, 0), negSeeDepthMultSq); ``` Passed STC: https://tests.stockfishchess.org/tests/view/6535551de746e058e6c0165d LLR: 2.98 (-2.94,2.94) <0.00,2.00> Total: 109056 W: 28025 L: 27599 D: 53432 Ptnml(0-2): 357, 12669, 28038, 13119, 345 Passed LTC: https://tests.stockfishchess.org/tests/view/65364c6ff127f3553505175d LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 61290 W: 15316 L: 14941 D: 31033 Ptnml(0-2): 34, 6849, 16498, 7236, 28 closes https://github.com/official-stockfish/Stockfish/pull/4847 bench 1167412 --- src/search.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ad4b458e180..9cef7e5f558 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -974,7 +974,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo { Piece capturedPiece = pos.piece_on(to_sq(move)); int futilityEval = - ss->staticEval + 188 + 206 * lmrDepth + PieceValue[capturedPiece] + ss->staticEval + 239 + 291 * lmrDepth + PieceValue[capturedPiece] + captureHistory[movedPiece][to_sq(move)][type_of(capturedPiece)] / 7; if (futilityEval < alpha) continue; @@ -991,16 +991,16 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo + (*contHist[3])[movedPiece][to_sq(move)]; // Continuation history based pruning (~2 Elo) - if (lmrDepth < 6 && history < -3232 * depth) + if (lmrDepth < 6 && history < -3498 * depth) continue; history += 2 * thisThread->mainHistory[us][from_to(move)]; - lmrDepth += history / 5793; + lmrDepth += history / 7815; lmrDepth = std::max(lmrDepth, -2); // Futility pruning: parent node (~13 Elo) - if (!ss->inCheck && lmrDepth < 13 && ss->staticEval + 115 + 122 * lmrDepth <= alpha) + if (!ss->inCheck && lmrDepth < 13 && ss->staticEval + 80 + 122 * lmrDepth <= alpha) continue; lmrDepth = std::max(lmrDepth, 0); From 871ab55f01f844bd24ed768c5e734ed2c956ef78 Mon Sep 17 00:00:00 2001 From: Taras Vuk <117687515+TarasVuk@users.noreply.github.com> Date: Wed, 25 Oct 2023 16:00:58 +0200 Subject: [PATCH 1241/1766] Simplify futility pruning formula closes https://github.com/official-stockfish/Stockfish/pull/4848 No functional change --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 9cef7e5f558..24f0d9946f7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -775,7 +775,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo - (ss - 1)->statScore / 321 >= beta && eval >= beta && eval < 29462 // smaller than TB wins - && !(!ttCapture && ttMove)) + && (!ttMove || ttCapture)) return eval; // Step 9. Null move search with verification search (~35 Elo) From b0658f09b93185e2b43d4b2d6f0daa30c36ebcc2 Mon Sep 17 00:00:00 2001 From: Michael Chaly <26898827+Vizvezdenec@users.noreply.github.com> Date: Fri, 27 Oct 2023 17:19:31 +0200 Subject: [PATCH 1242/1766] Introduce pawn structure based history Original idea by Seer chess engine https://github.com/connormcmonigle/seer-nnue, coding done by @Disservin, code refactoring done by @locutus2 to match the style of other histories. This patch introduces pawn structure based history, which assings moves values based on last digits of pawn structure hash and piece type of moved piece and landing square of the move. Idea is that good places for pieces are quite often determined by pawn structure of position. Used in 3 different places - sorting of quiet moves, sorting of quiet check evasions and in history based pruning in search. Passed STC: https://tests.stockfishchess.org/tests/view/65391d08cc309ae83955dbaf LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 155488 W: 39408 L: 38913 D: 77167 Ptnml(0-2): 500, 18427, 39408, 18896, 513 Passed LTC: https://tests.stockfishchess.org/tests/view/653a36a2cc309ae83955f181 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 70110 W: 17548 L: 17155 D: 35407 Ptnml(0-2): 33, 7859, 18889, 8230, 44 closes https://github.com/official-stockfish/Stockfish/pull/4849 Bench: 1257882 Co-Authored-By: Disservin Co-Authored-By: Stefan Geschwentner --- src/movepick.cpp | 13 +++++++++++-- src/movepick.h | 13 +++++++++++-- src/position.cpp | 17 ++++++++++++++--- src/position.h | 4 ++++ src/search.cpp | 15 +++++++++++---- src/thread.cpp | 1 + src/thread.h | 1 + 7 files changed, 53 insertions(+), 11 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index d2a49706fc2..444477cf78e 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -89,12 +89,14 @@ MovePicker::MovePicker(const Position& p, const ButterflyHistory* mh, const CapturePieceToHistory* cph, const PieceToHistory** ch, + const PawnHistory& ph, Move cm, const Move* killers) : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), + pawnHistory(ph), ttMove(ttm), refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, depth(d) { @@ -110,11 +112,13 @@ MovePicker::MovePicker(const Position& p, const ButterflyHistory* mh, const CapturePieceToHistory* cph, const PieceToHistory** ch, + const PawnHistory& ph, Square rs) : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), + pawnHistory(ph), ttMove(ttm), recaptureSquare(rs), depth(d) { @@ -125,9 +129,11 @@ MovePicker::MovePicker(const Position& p, // Constructor for ProbCut: we generate captures with SEE greater // than or equal to the given threshold. -MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph) : +MovePicker::MovePicker( + const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph, const PawnHistory& ph) : pos(p), captureHistory(cph), + pawnHistory(ph), ttMove(ttm), threshold(th) { assert(!pos.checkers()); @@ -203,6 +209,8 @@ void MovePicker::score() { : pt != PAWN ? bool(to & threatenedByPawn) * 15000 : 0) : 0; + + m.value += pawnHistory[pawn_structure(pos)][pc][to]; } else // Type == EVASIONS @@ -212,7 +220,8 @@ void MovePicker::score() { + (1 << 28); else m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] - + (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]; + + (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] + + pawnHistory[pawn_structure(pos)][pos.moved_piece(m)][to_sq(m)]; } } diff --git a/src/movepick.h b/src/movepick.h index 65e93dda6fe..f210f5387fc 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -28,9 +28,13 @@ #include "movegen.h" #include "types.h" +#include "position.h" namespace Stockfish { -class Position; + +constexpr int PAWN_HISTORY_SIZE = 512; + +inline int pawn_structure(const Position& pos) { return pos.pawn_key() & (PAWN_HISTORY_SIZE - 1); } // StatsEntry stores the stat table value. It is usually a number but could // be a move or even a nested history. We use a class instead of a naked value @@ -112,6 +116,8 @@ using PieceToHistory = Stats; // (~63 elo) using ContinuationHistory = Stats; +// PawnStructureHistory is addressed by the pawn structure and a move's [piece][to] +using PawnHistory = Stats; // MovePicker class is used to pick one pseudo-legal move at a time from the // current position. The most important method is next_move(), which returns a @@ -135,6 +141,7 @@ class MovePicker { const ButterflyHistory*, const CapturePieceToHistory*, const PieceToHistory**, + const PawnHistory&, Move, const Move*); MovePicker(const Position&, @@ -143,8 +150,9 @@ class MovePicker { const ButterflyHistory*, const CapturePieceToHistory*, const PieceToHistory**, + const PawnHistory&, Square); - MovePicker(const Position&, Move, Value, const CapturePieceToHistory*); + MovePicker(const Position&, Move, Value, const CapturePieceToHistory*, const PawnHistory&); Move next_move(bool skipQuiets = false); private: @@ -159,6 +167,7 @@ class MovePicker { const ButterflyHistory* mainHistory; const CapturePieceToHistory* captureHistory; const PieceToHistory** continuationHistory; + const PawnHistory& pawnHistory; Move ttMove; ExtMove refutations[3], *cur, *endMoves, *endBadCaptures; int stage; diff --git a/src/position.cpp b/src/position.cpp index 37c586abbaa..2bb47871555 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -49,7 +49,7 @@ namespace Zobrist { Key psq[PIECE_NB][SQUARE_NB]; Key enpassant[FILE_NB]; Key castling[CASTLING_RIGHT_NB]; -Key side; +Key side, noPawns; } namespace { @@ -128,7 +128,8 @@ void Position::init() { for (int cr = NO_CASTLING; cr <= ANY_CASTLING; ++cr) Zobrist::castling[cr] = rng.rand(); - Zobrist::side = rng.rand(); + Zobrist::side = rng.rand(); + Zobrist::noPawns = rng.rand(); // Prepare the cuckoo tables std::memset(cuckoo, 0, sizeof(cuckoo)); @@ -337,6 +338,7 @@ void Position::set_check_info() const { void Position::set_state() const { st->key = st->materialKey = 0; + st->pawnKey = Zobrist::noPawns; st->nonPawnMaterial[WHITE] = st->nonPawnMaterial[BLACK] = VALUE_ZERO; st->checkersBB = attackers_to(square(sideToMove)) & pieces(~sideToMove); @@ -348,7 +350,10 @@ void Position::set_state() const { Piece pc = piece_on(s); st->key ^= Zobrist::psq[pc][s]; - if (type_of(pc) != KING && type_of(pc) != PAWN) + if (type_of(pc) == PAWN) + st->pawnKey ^= Zobrist::psq[pc][s]; + + else if (type_of(pc) != KING) st->nonPawnMaterial[color_of(pc)] += PieceValue[pc]; } @@ -728,6 +733,8 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { assert(piece_on(to) == NO_PIECE); assert(piece_on(capsq) == make_piece(them, PAWN)); } + + st->pawnKey ^= Zobrist::psq[captured][capsq]; } else st->nonPawnMaterial[them] -= PieceValue[captured]; @@ -806,6 +813,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // Update hash keys k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[promotion][to]; + st->pawnKey ^= Zobrist::psq[pc][to]; st->materialKey ^= Zobrist::psq[promotion][pieceCount[promotion] - 1] ^ Zobrist::psq[pc][pieceCount[pc]]; @@ -813,6 +821,9 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { st->nonPawnMaterial[us] += PieceValue[promotion]; } + // Update pawn hash key + st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; + // Reset rule 50 draw counter st->rule50 = 0; } diff --git a/src/position.h b/src/position.h index 2aeb8fcd575..ce03c34f332 100644 --- a/src/position.h +++ b/src/position.h @@ -39,6 +39,7 @@ struct StateInfo { // Copied when making a move Key materialKey; + Key pawnKey; Value nonPawnMaterial[COLOR_NB]; int castlingRights; int rule50; @@ -146,6 +147,7 @@ class Position { Key key() const; Key key_after(Move m) const; Key material_key() const; + Key pawn_key() const; // Other properties of the position Color side_to_move() const; @@ -293,6 +295,8 @@ inline Key Position::adjust_key50(Key k) const { return st->rule50 < 14 - AfterMove ? k : k ^ make_key((st->rule50 - (14 - AfterMove)) / 8); } +inline Key Position::pawn_key() const { return st->pawnKey; } + inline Key Position::material_key() const { return st->materialKey; } inline Value Position::non_pawn_material(Color c) const { return st->nonPawnMaterial[c]; } diff --git a/src/search.cpp b/src/search.cpp index 24f0d9946f7..0ffca247853 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -848,7 +848,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo { assert(probCutBeta < VALUE_INFINITE); - MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, &captureHistory); + MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, &captureHistory, + thisThread->pawnHistory); while ((move = mp.next_move()) != MOVE_NONE) if (move != excludedMove && pos.legal(move)) @@ -904,7 +905,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo prevSq != SQ_NONE ? thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] : MOVE_NONE; MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &captureHistory, contHist, - countermove, ss->killers); + thisThread->pawnHistory, countermove, ss->killers); value = bestValue; moveCountPruning = singularQuietLMR = false; @@ -988,7 +989,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo { int history = (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] - + (*contHist[3])[movedPiece][to_sq(move)]; + + (*contHist[3])[movedPiece][to_sq(move)] + + thisThread->pawnHistory[pawn_structure(pos)][movedPiece][to_sq(move)]; // Continuation history based pruning (~2 Elo) if (lmrDepth < 6 && history < -3498 * depth) @@ -1463,7 +1465,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { // will be generated. Square prevSq = is_ok((ss - 1)->currentMove) ? to_sq((ss - 1)->currentMove) : SQ_NONE; MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory, - contHist, prevSq); + contHist, thisThread->pawnHistory, prevSq); int quietCheckEvasions = 0; @@ -1671,10 +1673,15 @@ void update_all_stats(const Position& pos, // Increase stats for the best move in case it was a quiet move update_quiet_stats(pos, ss, bestMove, bestMoveBonus); + thisThread->pawnHistory[pawn_structure(pos)][moved_piece][to_sq(bestMove)] + << quietMoveBonus; // Decrease stats for all non-best quiet moves for (int i = 0; i < quietCount; ++i) { + thisThread->pawnHistory[pawn_structure(pos)][pos.moved_piece(quietsSearched[i])] + [to_sq(quietsSearched[i])] + << -bestMoveBonus; thisThread->mainHistory[us][from_to(quietsSearched[i])] << -bestMoveBonus; update_continuation_histories(ss, pos.moved_piece(quietsSearched[i]), to_sq(quietsSearched[i]), -bestMoveBonus); diff --git a/src/thread.cpp b/src/thread.cpp index fdf89095b5e..bc884dedf01 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -68,6 +68,7 @@ void Thread::clear() { counterMoves.fill(MOVE_NONE); mainHistory.fill(0); captureHistory.fill(0); + pawnHistory.fill(0); for (bool inCheck : {false, true}) for (StatsType c : {NoCaptures, Captures}) diff --git a/src/thread.h b/src/thread.h index 5f33b7369d3..37a4a6ca2bc 100644 --- a/src/thread.h +++ b/src/thread.h @@ -71,6 +71,7 @@ class Thread { ButterflyHistory mainHistory; CapturePieceToHistory captureHistory; ContinuationHistory continuationHistory[2][2]; + PawnHistory pawnHistory; }; From d30af4f669fa9a47e26a54967c571ffa7987d660 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Fri, 27 Oct 2023 13:28:55 +0300 Subject: [PATCH 1243/1766] Rewarding Quiet Moves that Enable Razoring The main idea of the patch comes from @peregrineshahin : https://tests.stockfishchess.org/tests/view/65205363ac57711436728781 Another small idea (tuning) comes from @Vizvezdenec https://tests.stockfishchess.org/tests/view/652e071ade6d262d08d318f4 And a long phases of tuning and tests was done by me in order to make the patch be able to pass both tests. The idea, as mentioned by Peregrine is that in our standard code, if no best move found after searching all moves, we give a bonus to the previous move that caused the fail high. So in razoring we assume no bestmove will be found so we might as well do the same. Passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 82336 W: 20997 L: 20610 D: 40729 Ptnml(0-2): 288, 9710, 20753, 10161, 256 https://tests.stockfishchess.org/tests/view/6538fafbcc309ae83955d8f0 Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 46584 W: 11753 L: 11411 D: 23420 Ptnml(0-2): 21, 5133, 12642, 5475, 21 https://tests.stockfishchess.org/tests/view/653a3f2ccc309ae83955f223 closes https://github.com/official-stockfish/Stockfish/pull/4850 Bench: 1258079 Co-Authored-By: Shahin M. Shahin <41402573+peregrineshahin@users.noreply.github.com> --- src/evaluate.cpp | 4 ++-- src/search.cpp | 34 +++++++++++++++++++++------------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index c405cfb5538..9c39d4c07fb 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -144,8 +144,8 @@ void NNUE::verify() { } -// Returns a static, purely materialistic evaluation of the position -// from the point of view of the given color. It can be divided by PawnValue to get +// Returns a static, purely materialistic evaluation of the position from +// the point of view of the given color. It can be divided by PawnValue to get // an approximation of the material advantage on the board in terms of pawns. Value Eval::simple_eval(const Position& pos, Color c) { return PawnValue * (pos.count(c) - pos.count(~c)) diff --git a/src/search.cpp b/src/search.cpp index 0ffca247853..65b27d16d4f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -77,7 +77,7 @@ enum NodeType { // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving) { - return Value((126 - 42 * noTtCutNode) * (d - improving)); + return Value((125 - 43 * noTtCutNode) * (d - improving)); } // Reductions lookup table initialized at startup @@ -85,8 +85,8 @@ int Reductions[MAX_MOVES]; // [depth or moveNumber] Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) { int reductionScale = Reductions[d] * Reductions[mn]; - return (reductionScale + 1560 - int(delta) * 945 / int(rootDelta)) / 1024 - + (!i && reductionScale > 791); + return (reductionScale + 1487 - int(delta) * 976 / int(rootDelta)) / 1024 + + (!i && reductionScale > 808); } constexpr int futility_move_count(bool improving, Depth depth) { @@ -94,7 +94,7 @@ constexpr int futility_move_count(bool improving, Depth depth) { } // History and stats update bonus, based on depth -int stat_bonus(Depth d) { return std::min(334 * d - 531, 1538); } +int stat_bonus(Depth d) { return std::min(357 * d - 483, 1511); } // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(const Thread* thisThread) { @@ -761,11 +761,19 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. // Adjust razor margin according to cutoffCnt. (~1 Elo) - if (eval < alpha - 492 - (257 - 200 * ((ss + 1)->cutoffCnt > 3)) * depth * depth) + if (eval < alpha - 474 - (270 - 174 * ((ss + 1)->cutoffCnt > 3)) * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) + { + if (!priorCapture && prevSq != SQ_NONE) + { + int bonus = (depth > 6) + (PvNode || cutNode) + (value < alpha - 658) + ((ss-1)->moveCount > 11); + update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); + thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << stat_bonus(depth) * bonus * 57 / 100; + } return value; + } } // Step 8. Futility pruning: child node (~40 Elo) @@ -993,22 +1001,22 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo + thisThread->pawnHistory[pawn_structure(pos)][movedPiece][to_sq(move)]; // Continuation history based pruning (~2 Elo) - if (lmrDepth < 6 && history < -3498 * depth) + if (lmrDepth < 6 && history < -3645 * depth) continue; history += 2 * thisThread->mainHistory[us][from_to(move)]; - lmrDepth += history / 7815; - lmrDepth = std::max(lmrDepth, -2); + lmrDepth += history / 7836; + lmrDepth = std::max(lmrDepth, -1); // Futility pruning: parent node (~13 Elo) - if (!ss->inCheck && lmrDepth < 13 && ss->staticEval + 80 + 122 * lmrDepth <= alpha) + if (!ss->inCheck && lmrDepth < 13 && ss->staticEval + 77 + 124 * lmrDepth <= alpha) continue; lmrDepth = std::max(lmrDepth, 0); // Prune moves with negative SEE (~4 Elo) - if (!pos.see_ge(move, Value(-27 * lmrDepth * lmrDepth))) + if (!pos.see_ge(move, Value(-26 * lmrDepth * lmrDepth))) continue; } } @@ -1318,12 +1326,12 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (depth > 6) + (PvNode || cutNode) + (bestValue < alpha - 653) - + ((ss - 1)->moveCount > 11); + int bonus = (depth > 6) + (PvNode || cutNode) + (bestValue < alpha - 657) + + ((ss - 1)->moveCount > 10); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); thisThread->mainHistory[~us][from_to((ss - 1)->currentMove)] - << stat_bonus(depth) * bonus / 2; + << stat_bonus(depth) * bonus * 61 / 100; } if (PvNode) From 08ed4c90db31959521b7ef3186c026edd1e90307 Mon Sep 17 00:00:00 2001 From: Disservin Date: Fri, 27 Oct 2023 17:33:41 +0200 Subject: [PATCH 1244/1766] Format Code No functional change --- src/search.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 65b27d16d4f..b7b51e0b3de 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -768,9 +768,12 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo { if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (depth > 6) + (PvNode || cutNode) + (value < alpha - 658) + ((ss-1)->moveCount > 11); - update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); - thisThread->mainHistory[~us][from_to((ss-1)->currentMove)] << stat_bonus(depth) * bonus * 57 / 100; + int bonus = (depth > 6) + (PvNode || cutNode) + (value < alpha - 658) + + ((ss - 1)->moveCount > 11); + update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, + stat_bonus(depth) * bonus); + thisThread->mainHistory[~us][from_to((ss - 1)->currentMove)] + << stat_bonus(depth) * bonus * 57 / 100; } return value; } From 347d613b0e2c47f90cbf1c5a5affe97303f1ac3d Mon Sep 17 00:00:00 2001 From: Disservin Date: Fri, 27 Oct 2023 18:35:52 +0200 Subject: [PATCH 1245/1766] remove outdated comment Bench: 1258079 --- src/thread.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/thread.h b/src/thread.h index 37a4a6ca2bc..cb2f6db1d41 100644 --- a/src/thread.h +++ b/src/thread.h @@ -34,10 +34,7 @@ namespace Stockfish { -// Thread class keeps together all the thread-related stuff. We use -// per-thread pawn and material hash tables so that once we get a -// pointer to an entry its lifetime is unlimited and we don't have -// to care about someone changing the entry under our feet. +// Thread class keeps together all the thread-related stuff. class Thread { std::mutex mutex; From 38aa70adcfa767387da91c8df1180fb11ad89ac7 Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Sun, 29 Oct 2023 09:18:10 +0800 Subject: [PATCH 1246/1766] Small cleanups Corrects some incorrect or outdated comments. Credit is shared with yaneurao (see 38e830a#commitcomment-131131500) and locutus2 closes #4852 No functional change. --- src/movepick.h | 5 ++++- src/search.cpp | 15 +++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/movepick.h b/src/movepick.h index f210f5387fc..dc171c9ff5b 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -32,7 +32,10 @@ namespace Stockfish { -constexpr int PAWN_HISTORY_SIZE = 512; +constexpr int PAWN_HISTORY_SIZE = 512; // has to be a power of 2 + +static_assert((PAWN_HISTORY_SIZE & (PAWN_HISTORY_SIZE - 1)) == 0, + "PAWN_HISTORY_SIZE has to be a power of 2"); inline int pawn_structure(const Position& pos) { return pos.pawn_key() & (PAWN_HISTORY_SIZE - 1); } diff --git a/src/search.cpp b/src/search.cpp index b7b51e0b3de..ef98d862e6a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -830,16 +830,19 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo } } - // Step 10. If the position doesn't have a ttMove, decrease depth by 2, - // or by 4 if the TT entry for the current position was hit and + // Step 10. Internal iterative reductions (~9 Elo) + // For PV nodes without a ttMove, we decrease depth by 2, + // or by 4 if the current position is present in the TT and // the stored depth is greater than or equal to the current depth. - // Use qsearch if depth is equal or below zero (~9 Elo) + // Use qsearch if depth <= 0. if (PvNode && !ttMove) depth -= 2 + 2 * (ss->ttHit && tte->depth() >= depth); if (depth <= 0) return qsearch(pos, ss, alpha, beta); + // For cutNodes without a ttMove, we decrease depth by 2 + // if current depth >= 8. if (cutNode && depth >= 8 && !ttMove) depth -= 2; @@ -1129,7 +1132,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo if (PvNode) r--; - // Decrease reduction if ttMove has been singularly extended (~1 Elo) + // Decrease reduction if a quiet ttMove has been singularly extended (~1 Elo) if (singularQuietLMR) r--; @@ -1194,7 +1197,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Step 18. Full-depth search when LMR is skipped else if (!PvNode || moveCount > 1) { - // Increase reduction for cut nodes and not ttMove (~1 Elo) + // Increase reduction for cut nodes without ttMove (~1 Elo) if (!ttMove && cutNode) r += 2; @@ -1724,7 +1727,7 @@ void update_all_stats(const Position& pos, // Updates histories of the move pairs formed -// by moves at ply -1, -2, -4, and -6 with current move. +// by moves at ply -1, -2, -3, -4, and -6 with current move. void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { for (int i : {1, 2, 3, 4, 6}) From 908811c24ab53d2cb1bebc1138427e21fefa8054 Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Sat, 28 Oct 2023 16:04:15 +0800 Subject: [PATCH 1247/1766] Introduce asymmetric optimism Introduce asymmetric optimism for both side to move and opponent. Parameter tuning was done with 200k LTC games. STC: https://tests.stockfishchess.org/tests/view/653cc08fcc309ae8395622b3 LLR: 2.96 (-2.94,2.94) <0.00,2.00> Total: 98336 W: 25074 L: 24661 D: 48601 Ptnml(0-2): 339, 11612, 24890, 11951, 376 LTC: https://tests.stockfishchess.org/tests/view/653db595cc309ae839563140 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 83244 W: 20760 L: 20339 D: 42145 Ptnml(0-2): 51, 9306, 22498, 9705, 62 closes https://github.com/official-stockfish/Stockfish/pull/4853 Bench: 1371690 --- src/search.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ef98d862e6a..daab1eb1770 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -364,14 +364,13 @@ void Thread::search() { // Reset aspiration window starting size Value avg = rootMoves[pvIdx].averageScore; - delta = Value(10) + int(avg) * avg / 17470; + delta = Value(10) + int(avg) * avg / 15335; alpha = std::max(avg - delta, -VALUE_INFINITE); beta = std::min(avg + delta, VALUE_INFINITE); // Adjust optimism based on root move's averageScore (~4 Elo) - int opt = 113 * avg / (std::abs(avg) + 109); - optimism[us] = Value(opt); - optimism[~us] = -optimism[us]; + optimism[us] = 103 * (avg + 33) / (std::abs(avg + 34) + 119); + optimism[~us] = -116 * (avg + 40) / (std::abs(avg + 12) + 123); // Start with a small aspiration window and, in the case of a fail // high/low, re-search with a bigger window until we don't fail From e277dda7166992536124891e212d6d6a866f8a12 Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Tue, 31 Oct 2023 08:25:06 +0800 Subject: [PATCH 1248/1766] Prefetch TT entries in probcut Passed STC: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 101344 W: 25893 L: 25491 D: 49960 Ptnml(0-2): 303, 11350, 26991, 11698, 330 https://tests.stockfishchess.org/tests/view/6540daa6cc309ae83956669b slight speedup: ``` Result of 100 runs ================== base (./stockfish.master ) = 1170705 +/- 3133 test (./stockfish.patch ) = 1174545 +/- 2895 diff = +3841 +/- 3196 speedup = +0.0033 P(speedup > 0) = 0.9907 ``` closes https://github.com/official-stockfish/Stockfish/pull/4856 No functional change --- src/search.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index daab1eb1770..6e719be82f4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -869,6 +869,9 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo { assert(pos.capture_stage(move)); + // Prefetch the TT entry for the resulting position + prefetch(TT.first_entry(pos.key_after(move))); + ss->currentMove = move; ss->continuationHistory = &thisThread From 101d2bb8eae2c93c94013f56eae3af4faf8886f9 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Wed, 1 Nov 2023 13:42:06 +0300 Subject: [PATCH 1249/1766] Simplifying two formulas by eliminating two multiplication operations. Passed STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 60000 W: 15193 L: 14996 D: 29811 Ptnml(0-2): 199, 7100, 15215, 7277, 209 https://tests.stockfishchess.org/tests/view/653beb69cc309ae83956129d Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 122910 W: 30471 L: 30353 D: 62086 Ptnml(0-2): 68, 13961, 33271, 14095, 60 https://tests.stockfishchess.org/tests/view/653c5848cc309ae839561ae7 closes https://github.com/official-stockfish/Stockfish/pull/4857 bench: 1216779 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 6e719be82f4..3cd3b555c4a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -772,7 +772,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); thisThread->mainHistory[~us][from_to((ss - 1)->currentMove)] - << stat_bonus(depth) * bonus * 57 / 100; + << stat_bonus(depth) * bonus / 2; } return value; } @@ -1339,7 +1339,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); thisThread->mainHistory[~us][from_to((ss - 1)->currentMove)] - << stat_bonus(depth) * bonus * 61 / 100; + << stat_bonus(depth) * bonus / 2; } if (PvNode) From 1cb9afbdc04fbb864ee48babe56c1e88867d11e9 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Sat, 28 Oct 2023 07:43:12 +0200 Subject: [PATCH 1250/1766] Remove razoring history update. The recently commit 'Rewarding Quiet Moves that Enable Razoring' add a history update if razoring. But its contains also many tuned values all over the search. Following tests shows that the tuned values and not the added history update is responsible for the elo gain. So remove later. Passed STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 29376 W: 7641 L: 7410 D: 14325 Ptnml(0-2): 100, 3411, 7451, 3610, 116 https://tests.stockfishchess.org/tests/view/653c9fe1cc309ae839562070 Passed LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 242922 W: 59879 L: 59885 D: 123158 Ptnml(0-2): 129, 27764, 65688, 27744, 136 https://tests.stockfishchess.org/tests/view/653d06cbcc309ae839562735 closes https://github.com/official-stockfish/Stockfish/pull/4858 Bench: 1286104 --- src/search.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 3cd3b555c4a..aaf545d2658 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -764,18 +764,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) - { - if (!priorCapture && prevSq != SQ_NONE) - { - int bonus = (depth > 6) + (PvNode || cutNode) + (value < alpha - 658) - + ((ss - 1)->moveCount > 11); - update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, - stat_bonus(depth) * bonus); - thisThread->mainHistory[~us][from_to((ss - 1)->currentMove)] - << stat_bonus(depth) * bonus / 2; - } return value; - } } // Step 8. Futility pruning: child node (~40 Elo) From b4b704e6866bde21c69cd53457a6a91a15182b36 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Fri, 3 Nov 2023 14:03:12 +0300 Subject: [PATCH 1251/1766] Update pawn history based on static eval difference Use the same logic as in main history but with 1/4 multiplier. Passed STC: https://tests.stockfishchess.org/tests/view/653c1282cc309ae8395615bf LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 306624 W: 77811 L: 77094 D: 151719 Ptnml(0-2): 975, 36411, 77830, 37114, 982 Passed LTC: https://tests.stockfishchess.org/tests/view/654258e2cc309ae83956818d LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 99150 W: 24681 L: 24228 D: 50241 Ptnml(0-2): 56, 11107, 26792, 11568, 52 closes https://github.com/official-stockfish/Stockfish/pull/4859 bench 1330590 --- src/search.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index aaf545d2658..55d11003354 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -745,6 +745,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo { int bonus = std::clamp(-18 * int((ss - 1)->staticEval + ss->staticEval), -1812, 1812); thisThread->mainHistory[~us][from_to((ss - 1)->currentMove)] << bonus; + if (type_of(pos.piece_on(prevSq)) != PAWN && type_of((ss - 1)->currentMove) != PROMOTION) + thisThread->pawnHistory[pawn_structure(pos)][pos.piece_on(prevSq)][prevSq] << bonus / 4; } // Set up the improving flag, which is true if current static evaluation is From 7f97ba775ece910402108d7a7b11ce645185d300 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sat, 4 Nov 2023 17:19:08 +0300 Subject: [PATCH 1252/1766] Tweaking the futility pruning formula Huge credit goes also to candirufish, as the idea was first tried by him, and then tuned by me at multiple phases. Tweaking the futility pruning formula to be a bit more selective about when pruning is applied. Adjust the value added to the static eval based on the bestValue relative to ss->staticEval. If bestValue is significantly lower, we add a larger value. Passed STC: LLR: 2.98 (-2.94,2.94) <0.00,2.00> Total: 37120 W: 9590 L: 9266 D: 18264 Ptnml(0-2): 130, 4301, 9385, 4603, 141 https://tests.stockfishchess.org/tests/view/6544cf90136acbc573523247 Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 49632 W: 12381 L: 12033 D: 25218 Ptnml(0-2): 30, 5429, 13549, 5779, 29 https://tests.stockfishchess.org/tests/view/65453bc1136acbc573523a3c closes https://github.com/official-stockfish/Stockfish/pull/4861 bench: 1107118 --- src/search.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 55d11003354..27f0f987085 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1009,7 +1009,10 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo lmrDepth = std::max(lmrDepth, -1); // Futility pruning: parent node (~13 Elo) - if (!ss->inCheck && lmrDepth < 13 && ss->staticEval + 77 + 124 * lmrDepth <= alpha) + if (!ss->inCheck && lmrDepth < 13 + && ss->staticEval + (bestValue < ss->staticEval - 62 ? 123 : 77) + + 127 * lmrDepth + <= alpha) continue; lmrDepth = std::max(lmrDepth, 0); From 791419aab529e714271ebc03d1d84ad4e7e9879a Mon Sep 17 00:00:00 2001 From: "Shahin M. Shahin" <41402573+peregrineshahin@users.noreply.github.com> Date: Sun, 5 Nov 2023 12:01:06 +0300 Subject: [PATCH 1253/1766] Enhance some comments This documents some code and makes some hard code easier to understand for new developers. closes https://github.com/official-stockfish/Stockfish/pull/4862 No functional change --- src/movepick.h | 2 +- src/search.cpp | 31 ++++++++++++++++++------------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/movepick.h b/src/movepick.h index dc171c9ff5b..f058ff0ee88 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -119,7 +119,7 @@ using PieceToHistory = Stats; // (~63 elo) using ContinuationHistory = Stats; -// PawnStructureHistory is addressed by the pawn structure and a move's [piece][to] +// PawnHistory is addressed by the pawn structure and a move's [piece][to] using PawnHistory = Stats; // MovePicker class is used to pick one pseudo-legal move at a time from the diff --git a/src/search.cpp b/src/search.cpp index 27f0f987085..483412c598a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1030,9 +1030,10 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Singular extension search (~94 Elo). If all moves but one fail low on a // search of (alpha-s, beta-s), and just one fails high on (alpha, beta), // 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. Note - // that depth margin and singularBeta margin are known for having non-linear + // a reduced search on the position excluding the ttMove and if the result + // is lower than ttValue minus a margin, then we will extend the ttMove. + + // Note: the depth margin and singularBeta margin are known for having non-linear // scaling. Their values are optimized to time controls of 180+1.8 and longer // so changing them requires tests at this type of time controls. // Recursive singular search is avoided. @@ -1063,22 +1064,28 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo } // Multi-cut pruning - // Our ttMove is assumed to fail high, and now we failed high also on a - // reduced search without the ttMove. So we assume this expected cut-node - // is not singular, that multiple moves fail high, and we can prune the - // whole subtree by returning a softbound. + // Our ttMove is assumed to fail high based on the bound of the TT entry, + // and if after excluding the ttMove with a reduced search we fail high over the original beta, + // we assume this expected cut-node is not singular (multiple moves fail high), + // and we can prune the whole subtree by returning a softbound. else if (singularBeta >= beta) return singularBeta; - // If the eval of ttMove is greater than beta, reduce it (negative extension) (~7 Elo) + // Negative extensions + // If other moves failed high over (ttValue - margin) without the ttMove on a reduced search, + // but we cannot do multi-cut because (ttValue - margin) is lower than the original beta, + // we do not know if the ttMove is singular or can do a multi-cut, + // so we reduce the ttMove in favor of other moves based on some conditions: + + // If the ttMove is assumed to fail high over currnet beta (~7 Elo) else if (ttValue >= beta) extension = -2 - !PvNode; - // If we are on a cutNode, reduce it based on depth (negative extension) (~1 Elo) + // If we are on a cutNode but the ttMove is not assumed to fail high over current beta (~1 Elo) else if (cutNode) extension = depth < 19 ? -2 : -1; - // If the eval of ttMove is less than value, reduce it (negative extension) (~1 Elo) + // If the ttMove is assumed to fail low over the value of the reduced search (~1 Elo) else if (ttValue <= value) extension = -1; } @@ -1411,9 +1418,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { assert(0 <= ss->ply && ss->ply < MAX_PLY); - // Decide whether or not to include checks: this fixes also the type of - // TT entry depth that we are going to use. Note that in qsearch we use - // only two types of depth in TT: DEPTH_QS_CHECKS or DEPTH_QS_NO_CHECKS. + // Decide the replacement and cutoff priority of the qsearch TT entries ttDepth = ss->inCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS : DEPTH_QS_NO_CHECKS; // Step 3. Transposition table lookup From d4b46ea6db7caf71cad3c36d0ef8c0162a743aba Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sun, 5 Nov 2023 13:52:20 +0300 Subject: [PATCH 1254/1766] Set reduction to 0 if the move is a TT move The reduction formula currently decreases by 1 if the move is a TT move. This changes this by just setting the reduction to 0 instead. Passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 83136 W: 21145 L: 20758 D: 41233 Ptnml(0-2): 279, 9772, 21090, 10137, 290 https://tests.stockfishchess.org/tests/view/653c0fbacc309ae839561584 Passed LTC: LLR: 2.96 (-2.94,2.94) <0.50,2.50> Total: 273150 W: 67987 L: 67171 D: 137992 Ptnml(0-2): 155, 30730, 73966, 31592, 132 https://tests.stockfishchess.org/tests/view/653d9d02cc309ae839562fdf closes https://github.com/official-stockfish/Stockfish/pull/4863 bench: 1110556 --- src/search.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 483412c598a..8b2cc572fe7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1147,9 +1147,10 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo if ((ss + 1)->cutoffCnt > 3) r++; - // Decrease reduction for first generated move (ttMove) + // Set reduction to 0 for first generated move (ttMove) + // Nullifies all previous reduction adjustments to ttMove and leaves only history to do them else if (move == ttMove) - r--; + r = 0; ss->statScore = 2 * thisThread->mainHistory[us][from_to(move)] + (*contHist[0])[movedPiece][to_sq(move)] From 442c294a07836e9e32ad8b3bad49a853cc6f47de Mon Sep 17 00:00:00 2001 From: Taras Vuk <117687515+TarasVuk@users.noreply.github.com> Date: Sun, 5 Nov 2023 16:01:52 +0100 Subject: [PATCH 1255/1766] Use stat_malus when decreasing stats This patch applies stat_bonus when increasing and stat_malus when decreasing stats. Passed STC: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 133792 W: 34221 L: 33758 D: 65813 Ptnml(0-2): 477, 15764, 33959, 16211, 485 https://tests.stockfishchess.org/tests/view/654699f3136acbc5735256b2 Passed LTC: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 67374 W: 16912 L: 16523 D: 33939 Ptnml(0-2): 42, 7528, 18171, 7891, 55 https://tests.stockfishchess.org/tests/view/65474558136acbc5735263ab closes https://github.com/official-stockfish/Stockfish/pull/4864 bench: 1114417 --- src/search.cpp | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 8b2cc572fe7..a8f178a31ca 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -94,7 +94,10 @@ constexpr int futility_move_count(bool improving, Depth depth) { } // History and stats update bonus, based on depth -int stat_bonus(Depth d) { return std::min(357 * d - 483, 1511); } +int stat_bonus(Depth d) { return std::min(364 * d - 438, 1501); } + +// History and stats update malus, based on depth +int stat_malus(Depth d) { return std::min(452 * d - 452, 1478); } // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(const Thread* thisThread) { @@ -636,12 +639,12 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // the previous ply (~0 Elo on STC, ~2 Elo on LTC). if (prevSq != SQ_NONE && (ss - 1)->moveCount <= 2 && !priorCapture) update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, - -stat_bonus(depth + 1)); + -stat_malus(depth + 1)); } // Penalty for a quiet ttMove that fails low (~1 Elo) else if (!ttCapture) { - int penalty = -stat_bonus(depth); + int penalty = -stat_malus(depth); thisThread->mainHistory[us][from_to(ttMove)] << penalty; update_continuation_histories(ss, pos.moved_piece(ttMove), to_sq(ttMove), penalty); } @@ -1190,7 +1193,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo if (newDepth > d) value = -search(pos, ss + 1, -(alpha + 1), -alpha, newDepth, !cutNode); - int bonus = value <= alpha ? -stat_bonus(newDepth) + int bonus = value <= alpha ? -stat_malus(newDepth) : value >= beta ? stat_bonus(newDepth) : 0; @@ -1681,6 +1684,7 @@ void update_all_stats(const Position& pos, PieceType captured; int quietMoveBonus = stat_bonus(depth + 1); + int quietMoveMalus = stat_malus(depth + 1); if (!pos.capture_stage(bestMove)) { @@ -1692,15 +1696,18 @@ void update_all_stats(const Position& pos, thisThread->pawnHistory[pawn_structure(pos)][moved_piece][to_sq(bestMove)] << quietMoveBonus; + int moveMalus = bestValue > beta + 168 ? quietMoveMalus // larger malus + : stat_malus(depth); // smaller malus + // Decrease stats for all non-best quiet moves for (int i = 0; i < quietCount; ++i) { thisThread->pawnHistory[pawn_structure(pos)][pos.moved_piece(quietsSearched[i])] [to_sq(quietsSearched[i])] - << -bestMoveBonus; - thisThread->mainHistory[us][from_to(quietsSearched[i])] << -bestMoveBonus; + << -moveMalus; + thisThread->mainHistory[us][from_to(quietsSearched[i])] << -moveMalus; update_continuation_histories(ss, pos.moved_piece(quietsSearched[i]), - to_sq(quietsSearched[i]), -bestMoveBonus); + to_sq(quietsSearched[i]), -moveMalus); } } else @@ -1716,14 +1723,14 @@ void update_all_stats(const Position& pos, && ((ss - 1)->moveCount == 1 + (ss - 1)->ttHit || ((ss - 1)->currentMove == (ss - 1)->killers[0])) && !pos.captured_piece()) - update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, -quietMoveBonus); + update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, -quietMoveMalus); // Decrease stats for all non-best capture moves for (int i = 0; i < captureCount; ++i) { moved_piece = pos.moved_piece(capturesSearched[i]); captured = type_of(pos.piece_on(to_sq(capturesSearched[i]))); - captureHistory[moved_piece][to_sq(capturesSearched[i])][captured] << -quietMoveBonus; + captureHistory[moved_piece][to_sq(capturesSearched[i])][captured] << -quietMoveMalus; } } From d0e87104aa782176735442e1f6668f91014f07eb Mon Sep 17 00:00:00 2001 From: Taras Vuk Date: Mon, 6 Nov 2023 15:00:06 +0100 Subject: [PATCH 1256/1766] Remove pawn history from ProbCut constructor use same style as other history tables Passed STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 184672 W: 46953 L: 46896 D: 90823 Ptnml(0-2): 604, 21095, 48887, 21140, 610 https://tests.stockfishchess.org/tests/view/654766b4136acbc573526602 closes https://github.com/official-stockfish/Stockfish/pull/4865 No functional change --- src/movepick.cpp | 13 +++++-------- src/movepick.h | 8 ++++---- src/search.cpp | 7 +++---- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 444477cf78e..0aba9a55fe7 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -89,7 +89,7 @@ MovePicker::MovePicker(const Position& p, const ButterflyHistory* mh, const CapturePieceToHistory* cph, const PieceToHistory** ch, - const PawnHistory& ph, + const PawnHistory* ph, Move cm, const Move* killers) : pos(p), @@ -112,7 +112,7 @@ MovePicker::MovePicker(const Position& p, const ButterflyHistory* mh, const CapturePieceToHistory* cph, const PieceToHistory** ch, - const PawnHistory& ph, + const PawnHistory* ph, Square rs) : pos(p), mainHistory(mh), @@ -129,11 +129,9 @@ MovePicker::MovePicker(const Position& p, // Constructor for ProbCut: we generate captures with SEE greater // than or equal to the given threshold. -MovePicker::MovePicker( - const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph, const PawnHistory& ph) : +MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph) : pos(p), captureHistory(cph), - pawnHistory(ph), ttMove(ttm), threshold(th) { assert(!pos.checkers()); @@ -188,6 +186,7 @@ void MovePicker::score() { m.value += (*continuationHistory[2])[pc][to] / 4; m.value += (*continuationHistory[3])[pc][to]; m.value += (*continuationHistory[5])[pc][to]; + m.value += (*pawnHistory)[pawn_structure(pos)][pc][to]; // bonus for checks m.value += bool(pos.check_squares(pt) & to) * 16384; @@ -209,8 +208,6 @@ void MovePicker::score() { : pt != PAWN ? bool(to & threatenedByPawn) * 15000 : 0) : 0; - - m.value += pawnHistory[pawn_structure(pos)][pc][to]; } else // Type == EVASIONS @@ -221,7 +218,7 @@ void MovePicker::score() { else m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] + (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] - + pawnHistory[pawn_structure(pos)][pos.moved_piece(m)][to_sq(m)]; + + (*pawnHistory)[pawn_structure(pos)][pos.moved_piece(m)][to_sq(m)]; } } diff --git a/src/movepick.h b/src/movepick.h index f058ff0ee88..9f18997406f 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -144,7 +144,7 @@ class MovePicker { const ButterflyHistory*, const CapturePieceToHistory*, const PieceToHistory**, - const PawnHistory&, + const PawnHistory*, Move, const Move*); MovePicker(const Position&, @@ -153,9 +153,9 @@ class MovePicker { const ButterflyHistory*, const CapturePieceToHistory*, const PieceToHistory**, - const PawnHistory&, + const PawnHistory*, Square); - MovePicker(const Position&, Move, Value, const CapturePieceToHistory*, const PawnHistory&); + MovePicker(const Position&, Move, Value, const CapturePieceToHistory*); Move next_move(bool skipQuiets = false); private: @@ -170,7 +170,7 @@ class MovePicker { const ButterflyHistory* mainHistory; const CapturePieceToHistory* captureHistory; const PieceToHistory** continuationHistory; - const PawnHistory& pawnHistory; + const PawnHistory* pawnHistory; Move ttMove; ExtMove refutations[3], *cur, *endMoves, *endBadCaptures; int stage; diff --git a/src/search.cpp b/src/search.cpp index a8f178a31ca..b947fc5f3e8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -855,8 +855,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo { assert(probCutBeta < VALUE_INFINITE); - MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, &captureHistory, - thisThread->pawnHistory); + MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, &captureHistory); while ((move = mp.next_move()) != MOVE_NONE) if (move != excludedMove && pos.legal(move)) @@ -915,7 +914,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo prevSq != SQ_NONE ? thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] : MOVE_NONE; MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &captureHistory, contHist, - thisThread->pawnHistory, countermove, ss->killers); + &thisThread->pawnHistory, countermove, ss->killers); value = bestValue; moveCountPruning = singularQuietLMR = false; @@ -1484,7 +1483,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { // will be generated. Square prevSq = is_ok((ss - 1)->currentMove) ? to_sq((ss - 1)->currentMove) : SQ_NONE; MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory, - contHist, thisThread->pawnHistory, prevSq); + contHist, &thisThread->pawnHistory, prevSq); int quietCheckEvasions = 0; From 80b0e3754303c44bdcc53c01339a955d5677cd64 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Mon, 6 Nov 2023 16:12:30 +0100 Subject: [PATCH 1257/1766] Double weight of pawn history for quiet move ordering. I measured on my 1000 position bench the average additional added pawn history per depth. This shows on average negative value with even smaller values with increaing depth. A linear regression against depth get following formula: -1960 - 130 * depth For compensation add this to the used sort limit to maintain roughly the same proportion of sorted quiet moves. Remarks: 1. using no compensation failed here https://tests.stockfishchess.org/tests/view/6547664f136acbc5735265f0 2. using only the compensation failed at LTC: passed STC: https://tests.stockfishchess.org/tests/view/65477457136acbc5735266f8 failed LTC: https://tests.stockfishchess.org/tests/view/65487fc8136acbc573527d1c STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 98528 W: 25109 L: 24699 D: 48720 Ptnml(0-2): 334, 11586, 25009, 12006, 329 https://tests.stockfishchess.org/tests/view/65475873136acbc5735264f7 LTC: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 69726 W: 17467 L: 17073 D: 35186 Ptnml(0-2): 39, 7814, 18769, 8196, 45 https://tests.stockfishchess.org/tests/view/6547e759136acbc573527071 closes https://github.com/official-stockfish/Stockfish/pull/4866 Bench: 1379422 --- src/movepick.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 0aba9a55fe7..798f51ddb6e 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -181,12 +181,12 @@ void MovePicker::score() { // histories m.value = 2 * (*mainHistory)[pos.side_to_move()][from_to(m)]; + m.value += 2 * (*pawnHistory)[pawn_structure(pos)][pc][to]; m.value += 2 * (*continuationHistory[0])[pc][to]; m.value += (*continuationHistory[1])[pc][to]; m.value += (*continuationHistory[2])[pc][to] / 4; m.value += (*continuationHistory[3])[pc][to]; m.value += (*continuationHistory[5])[pc][to]; - m.value += (*pawnHistory)[pawn_structure(pos)][pc][to]; // bonus for checks m.value += bool(pos.check_squares(pt) & to) * 16384; @@ -302,7 +302,7 @@ Move MovePicker::next_move(bool skipQuiets) { endMoves = generate(pos, cur); score(); - partial_insertion_sort(cur, endMoves, -3000 * depth); + partial_insertion_sort(cur, endMoves, -1960 - 3130 * depth); } ++stage; From fbc6b275051773b491c1c180fd7ff331194ca0f1 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Tue, 7 Nov 2023 13:03:05 -0500 Subject: [PATCH 1258/1766] Simplify away optimism average score offset params Passed non-regression STC: https://tests.stockfishchess.org/tests/view/654abf6b136acbc57352ac4b LLR: 2.97 (-2.94,2.94) <-1.75,0.25> Total: 49664 W: 12687 L: 12477 D: 24500 Ptnml(0-2): 138, 5840, 12703, 5976, 175 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/654b638e136acbc57352b961 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 347166 W: 85561 L: 85676 D: 175929 Ptnml(0-2): 206, 39569, 94150, 39450, 208 closes https://github.com/official-stockfish/Stockfish/pull/4871 bench 1257641 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index b947fc5f3e8..3ce74126aa7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -372,8 +372,8 @@ void Thread::search() { beta = std::min(avg + delta, VALUE_INFINITE); // Adjust optimism based on root move's averageScore (~4 Elo) - optimism[us] = 103 * (avg + 33) / (std::abs(avg + 34) + 119); - optimism[~us] = -116 * (avg + 40) / (std::abs(avg + 12) + 123); + optimism[us] = 103 * avg / (std::abs(avg) + 119); + optimism[~us] = -116 * avg / (std::abs(avg) + 123); // Start with a small aspiration window and, in the case of a fail // high/low, re-search with a bigger window until we don't fail From 863a1f2b4cb233be3126b244cbd8f6c8b9b4d13c Mon Sep 17 00:00:00 2001 From: Taras Vuk <117687515+TarasVuk@users.noreply.github.com> Date: Mon, 13 Nov 2023 14:45:36 +0100 Subject: [PATCH 1259/1766] Introduce recapture extensions When in a PV-node this patch extends ttMove if it is a recapture and has a good history. Passed STC: LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 83840 W: 21560 L: 21166 D: 41114 Ptnml(0-2): 343, 9905, 21027, 10305, 340 https://tests.stockfishchess.org/tests/view/654f4b02136acbc5735308ab Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 165318 W: 41068 L: 40476 D: 83774 Ptnml(0-2): 98, 18670, 44517, 19290, 84 https://tests.stockfishchess.org/tests/view/654fde04136acbc5735314e0 closes https://github.com/official-stockfish/Stockfish/pull/4872 Bench: 1393911 --- src/search.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 3ce74126aa7..5c53c0dae8b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1100,6 +1100,12 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo else if (PvNode && move == ttMove && move == ss->killers[0] && (*contHist[0])[movedPiece][to_sq(move)] >= 4194) extension = 1; + + // Recapture extensions (~1 Elo) + else if (PvNode && move == ttMove && to_sq(move) == prevSq + && captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] + > 4000) + extension = 1; } // Add extension to new depth From f9d8717844643e4ea3723f5ea240bf5d22800df7 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 12 Nov 2023 15:51:38 +0100 Subject: [PATCH 1260/1766] Symmetrize optimism Removes some additional parameters, making the term more logical at the same time. Passed STC: https://tests.stockfishchess.org/tests/view/6550e896136acbc5735328ed LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 271104 W: 68441 L: 68480 D: 134183 Ptnml(0-2): 827, 32590, 68816, 32433, 886 Passed LTC: https://tests.stockfishchess.org/tests/view/65523858136acbc5735344f7 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 198954 W: 49250 L: 49211 D: 100493 Ptnml(0-2): 93, 22565, 54117, 22614, 88 closes https://github.com/official-stockfish/Stockfish/pull/4874 Bench: 1334248 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5c53c0dae8b..5bea5945c24 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -372,8 +372,8 @@ void Thread::search() { beta = std::min(avg + delta, VALUE_INFINITE); // Adjust optimism based on root move's averageScore (~4 Elo) - optimism[us] = 103 * avg / (std::abs(avg) + 119); - optimism[~us] = -116 * avg / (std::abs(avg) + 123); + optimism[us] = 110 * avg / (std::abs(avg) + 121); + optimism[~us] = -optimism[us]; // Start with a small aspiration window and, in the case of a fail // high/low, re-search with a bigger window until we don't fail From d89217766b748eccd08f58f35209d762d8bf0600 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 15 Nov 2023 21:00:37 +0100 Subject: [PATCH 1261/1766] CI updates - updates the SDE action to v2.2 - removes the linux x86-32 builds, which were almost unused, and the build process under SDE started failing recently, possibly related to glibc update (The futex facility returned an unexpected error code.) closes https://github.com/official-stockfish/Stockfish/pull/4875 No functional change --- .github/workflows/stockfish_binaries.yml | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/.github/workflows/stockfish_binaries.yml b/.github/workflows/stockfish_binaries.yml index fadfbcfcd04..6da576e40a0 100644 --- a/.github/workflows/stockfish_binaries.yml +++ b/.github/workflows/stockfish_binaries.yml @@ -13,6 +13,7 @@ jobs: NAME: ${{ matrix.config.simple_name }} BINARY: ${{ matrix.binaries }} strategy: + fail-fast: false matrix: config: - name: Ubuntu 20.04 GCC @@ -42,7 +43,6 @@ jobs: sde: /d/a/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.14.0-2022-10-25-win/sde.exe -future -- archive_ext: zip binaries: - - x86-32 - x86-64 - x86-64-sse41-popcnt - x86-64-avx2 @@ -54,10 +54,6 @@ jobs: exclude: - binaries: x86-64-avxvnni config: { ubuntu-20.04 } - - binaries: x86-32 - config: { os: windows-2022} - - binaries: x86-32 - config: { os: macos-13 } - binaries: x86-64-avxvnni config: { os: macos-13 } - binaries: x86-64-avx512 @@ -75,12 +71,6 @@ jobs: with: fetch-depth: 0 - - name: Download required Linux packages - if: runner.os == 'Linux' - run: | - sudo apt update - sudo apt install g++-multilib g++-11-multilib - - name: Download required macOS packages if: runner.os == 'macOS' run: brew install coreutils @@ -100,7 +90,7 @@ jobs: - name: Download SDE package if: runner.os == 'Linux' || runner.os == 'Windows' - uses: petarpetrovt/setup-sde@v2.1 + uses: petarpetrovt/setup-sde@6f4926100f31791716b11d25c0f3f35809d44f84 with: environmentVariableName: SDE_DIR sdeVersion: 9.14.0 From 7970236e9ea64796d5c7597cb1aedde737751f07 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Thu, 16 Nov 2023 08:40:25 +0100 Subject: [PATCH 1262/1766] Fix undefined behavior in search. We use following line to clamp the search depth in some range: Depth d = std::clamp(newDepth - r, 1, newDepth + 1); Through negative extension its possible that the maximum value becomes smaller than the minimum value but then the behavior is undefined (see https://en.cppreference.com/w/cpp/algorithm/clamp). So replace this line with a safe implementation. Remark: We have in recent master already one line where up to 3 negative extensions are possible which could trigger this undefined behavior but this can only be happen for completed depth > 24 so its not discovered by our default bench. Recent negative extension tests by @fauzi shows then this undefined behavior with wrong bench numbers. closes https://github.com/official-stockfish/Stockfish/pull/4877 No functional change --- src/search.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 5bea5945c24..fa479c4b827 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1178,7 +1178,9 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // In general we want to cap the LMR depth search at newDepth, but when // reduction is negative, we allow this move a limited search extension // beyond the first move depth. This may lead to hidden double extensions. - Depth d = std::clamp(newDepth - r, 1, newDepth + 1); + // To prevent problems when the max value is less than the min value, + // std::clamp has been replaced by a more robust implementation. + Depth d = std::max(1, std::min(newDepth - r, newDepth + 1)); value = -search(pos, ss + 1, -(alpha + 1), -alpha, d, true); From 504bf0e8b8cf2dd818deb623c5ad7e428e504cd8 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Mon, 20 Nov 2023 18:56:34 +0100 Subject: [PATCH 1263/1766] Change depth - 1 to newDepth Replacing 'depth - 1' with 'newDepth' in the singularbeta formula utilizes existing variables more succinctly. closes https://github.com/official-stockfish/Stockfish/pull/4876 No functional change --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index fa479c4b827..7d567b8ab71 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1045,7 +1045,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo && tte->depth() >= depth - 3) { Value singularBeta = ttValue - (64 + 57 * (ss->ttPv && !PvNode)) * depth / 64; - Depth singularDepth = (depth - 1) / 2; + Depth singularDepth = newDepth / 2; ss->excludedMove = move; value = From b59786e750a59d3d7cff2630cf284553f607ed29 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Thu, 16 Nov 2023 14:00:11 +0300 Subject: [PATCH 1264/1766] Remove doEvenDeeperSearch Passed STC: LLR: 2.98 (-2.94,2.94) <-1.75,0.25> Total: 51040 W: 13014 L: 12804 D: 25222 Ptnml(0-2): 166, 6032, 12917, 6236, 169 https://tests.stockfishchess.org/tests/view/65525aa1136acbc573534801 Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 165168 W: 40863 L: 40789 D: 83516 Ptnml(0-2): 73, 18783, 44792, 18869, 67 https://tests.stockfishchess.org/tests/view/65535af5136acbc573535c84 closes https://github.com/official-stockfish/Stockfish/pull/4880 Bench: 1477007 --- src/search.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 7d567b8ab71..ae83ab34758 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1190,12 +1190,9 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Adjust full-depth search based on LMR results - if the result // was good enough search deeper, if it was bad enough search shallower. const bool doDeeperSearch = value > (bestValue + 51 + 10 * (newDepth - d)); - const bool doEvenDeeperSearch = value > alpha + 700 && ss->doubleExtensions <= 6; const bool doShallowerSearch = value < bestValue + newDepth; - ss->doubleExtensions = ss->doubleExtensions + doEvenDeeperSearch; - - newDepth += doDeeperSearch - doShallowerSearch + doEvenDeeperSearch; + newDepth += doDeeperSearch - doShallowerSearch; if (newDepth > d) value = -search(pos, ss + 1, -(alpha + 1), -alpha, newDepth, !cutNode); From b4e9ee72e36aadd0e653ac4ab5c07a9e3d639aca Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Mon, 20 Nov 2023 19:09:48 +0100 Subject: [PATCH 1265/1766] Reformat some comments Tests used to derive some Elo worth comments: https://tests.stockfishchess.org/tests/view/653cf6b7cc309ae83956263a https://tests.stockfishchess.org/tests/view/655250b7136acbc573534711 https://tests.stockfishchess.org/tests/view/65525767136acbc5735347b9 https://tests.stockfishchess.org/tests/view/65525aa1136acbc573534801 closes https://github.com/official-stockfish/Stockfish/pull/4879 No functional change --- src/misc.cpp | 19 +++++++++---------- src/movepick.h | 9 ++++----- src/position.cpp | 27 ++++++++++++--------------- src/search.cpp | 16 ++++++++-------- src/uci.cpp | 9 ++++----- src/uci.h | 2 +- 6 files changed, 38 insertions(+), 44 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index 3e9006156c6..4193f8d2c7d 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -393,8 +393,8 @@ void dbg_print() { } -// Used to serialize access to std::cout to avoid multiple threads writing at -// the same time. +// Used to serialize access to std::cout +// to avoid multiple threads writing at the same time. std::ostream& operator<<(std::ostream& os, SyncCout sc) { static std::mutex m; @@ -558,7 +558,7 @@ void* aligned_large_pages_alloc(size_t allocSize) { constexpr size_t alignment = 4096; // assumed small page size #endif - // round up to multiples of alignment + // Round up to multiples of alignment size_t size = ((allocSize + alignment - 1) / alignment) * alignment; void* mem = std_aligned_alloc(alignment, size); #if defined(MADV_HUGEPAGE) @@ -600,7 +600,7 @@ void bindThisThread(size_t) {} #else -// Retrieves logical processor information using Windows specific +// Retrieves logical processor information using Windows-specific // API and returns the best node id for the thread with index idx. Original // code from Texel by Peter Österlund. static int best_node(size_t idx) { @@ -660,8 +660,7 @@ static int best_node(size_t idx) { groups.push_back(n); // In case a core has more than one logical processor (we assume 2) and we - // have still threads to allocate, then spread them evenly across available - // nodes. + // still have threads to allocate, spread them evenly across available nodes. for (int t = 0; t < threads - cores; t++) groups.push_back(t % nodes); @@ -731,7 +730,7 @@ std::string workingDirectory; // path of the working directory void init([[maybe_unused]] int argc, char* argv[]) { std::string pathSeparator; - // extract the path+name of the executable binary + // Extract the path+name of the executable binary argv0 = argv[0]; #ifdef _WIN32 @@ -747,14 +746,14 @@ void init([[maybe_unused]] int argc, char* argv[]) { pathSeparator = "/"; #endif - // extract the working directory + // Extract the working directory workingDirectory = ""; char buff[40000]; char* cwd = GETCWD(buff, 40000); if (cwd) workingDirectory = cwd; - // extract the binary directory path from argv0 + // Extract the binary directory path from argv0 binaryDirectory = argv0; size_t pos = binaryDirectory.find_last_of("\\/"); if (pos == std::string::npos) @@ -762,7 +761,7 @@ void init([[maybe_unused]] int argc, char* argv[]) { else binaryDirectory.resize(pos + 1); - // pattern replacement: "./" at the start of path is replaced by the working directory + // Pattern replacement: "./" at the start of path is replaced by the working directory if (binaryDirectory.find("." + pathSeparator) == 0) binaryDirectory.replace(0, 1, workingDirectory); } diff --git a/src/movepick.h b/src/movepick.h index 9f18997406f..299925a582e 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -96,11 +96,10 @@ enum StatsType { Captures }; -// ButterflyHistory records how often quiet moves have been successful or -// unsuccessful during the current search, and is used for reduction and move -// ordering decisions. It uses 2 tables (one for each color) indexed by -// the move's from and to squares, see www.chessprogramming.org/Butterfly_Boards -// (~11 elo) +// ButterflyHistory records how often quiet moves have been successful or unsuccessful +// during the current search, and is used for reduction and move ordering decisions. +// It uses 2 tables (one for each color) indexed by the move's from and to squares, +// see www.chessprogramming.org/Butterfly_Boards (~11 elo) using ButterflyHistory = Stats; // CounterMoveHistory stores counter moves indexed by [piece][to] of the previous diff --git a/src/position.cpp b/src/position.cpp index 2bb47871555..c45dd7b2e22 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -371,9 +371,8 @@ void Position::set_state() const { } -// Overload to initialize the position object with -// the given endgame code string like "KBPKN". It is mainly a helper to -// get the material key out of an endgame code. +// Overload to initialize the position object with the given endgame code string +// like "KBPKN". It's mainly a helper to get the material key out of an endgame code. Position& Position::set(const string& code, Color c, StateInfo* si) { assert(code[0] == 'K'); @@ -472,8 +471,8 @@ void Position::update_slider_blockers(Color c) const { } -// Computes a bitboard of all pieces which attack a -// given square. Slider attacks use the occupied bitboard to indicate occupancy. +// Computes a bitboard of all pieces which attack a given square. +// Slider attacks use the occupied bitboard to indicate occupancy. Bitboard Position::attackers_to(Square s, Bitboard occupied) const { return (pawn_attacks_bb(BLACK, s) & pieces(WHITE, PAWN)) @@ -575,8 +574,7 @@ bool Position::pseudo_legal(const Move m) const { // Handle the special case of a pawn move if (type_of(pc) == PAWN) { - // We have already handled promotion moves, so destination - // cannot be on the 8th/1st rank. + // We have already handled promotion moves, so destination cannot be on the 8th/1st rank if ((Rank8BB | Rank1BB) & to) return false; @@ -639,10 +637,9 @@ bool Position::gives_check(Move m) const { case PROMOTION : return attacks_bb(promotion_type(m), to, pieces() ^ from) & square(~sideToMove); - // En passant capture with check? We have already handled the case - // of direct checks and ordinary discovered check, so the only case we - // need to handle is the unusual case of a discovered check through - // the captured pawn. + // En passant capture with check? We have already handled the case of direct + // checks and ordinary discovered check, so the only case we need to handle + // is the unusual case of a discovered check through the captured pawn. case EN_PASSANT : { Square capsq = make_square(file_of(to), rank_of(from)); Bitboard b = (pieces() ^ from ^ capsq) | to; @@ -928,8 +925,8 @@ void Position::undo_move(Move m) { } -// Helper used to do/undo a castling move. This -// is a bit tricky in Chess960 where from/to squares can overlap. +// Helper used to do/undo a castling move. This is a bit +// tricky in Chess960 where from/to squares can overlap. template void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto) { @@ -1244,8 +1241,8 @@ void Position::flip() { } -// Performs some consistency checks for the -// position object and raise an assert if something wrong is detected. +// Performs some consistency checks for the position object +// and raise an assert if something wrong is detected. // This is meant to be helpful when debugging. bool Position::pos_is_ok() const { diff --git a/src/search.cpp b/src/search.cpp index ae83ab34758..c878b0aba1d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -834,8 +834,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo if (depth <= 0) return qsearch(pos, ss, alpha, beta); - // For cutNodes without a ttMove, we decrease depth by 2 - // if current depth >= 8. + // For cutNodes without a ttMove, we decrease depth by 2 if depth is high enough. if (cutNode && depth >= 8 && !ttMove) depth -= 2; @@ -1037,7 +1036,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Note: the depth margin and singularBeta margin are known for having non-linear // scaling. Their values are optimized to time controls of 180+1.8 and longer - // so changing them requires tests at this type of time controls. + // so changing them requires tests at these types of time controls. // Recursive singular search is avoided. if (!rootNode && move == ttMove && !excludedMove && depth >= 4 - (thisThread->completedDepth > 24) + 2 * (PvNode && tte->is_pv()) @@ -1079,7 +1078,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // we do not know if the ttMove is singular or can do a multi-cut, // so we reduce the ttMove in favor of other moves based on some conditions: - // If the ttMove is assumed to fail high over currnet beta (~7 Elo) + // If the ttMove is assumed to fail high over current beta (~7 Elo) else if (ttValue >= beta) extension = -2 - !PvNode; @@ -1155,7 +1154,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo if ((ss + 1)->cutoffCnt > 3) r++; - // Set reduction to 0 for first generated move (ttMove) + // Set reduction to 0 for first picked move (ttMove) (~2 Elo) // Nullifies all previous reduction adjustments to ttMove and leaves only history to do them else if (move == ttMove) r = 0; @@ -1189,8 +1188,9 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo { // Adjust full-depth search based on LMR results - if the result // was good enough search deeper, if it was bad enough search shallower. - const bool doDeeperSearch = value > (bestValue + 51 + 10 * (newDepth - d)); - const bool doShallowerSearch = value < bestValue + newDepth; + const bool doDeeperSearch = + value > (bestValue + 51 + 10 * (newDepth - d)); // (~1 Elo) + const bool doShallowerSearch = value < bestValue + newDepth; // (~2 Elo) newDepth += doDeeperSearch - doShallowerSearch; @@ -1459,7 +1459,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { bestValue = ttValue; } else - // In case of null move search use previous static eval with a different sign + // In case of null move search, use previous static eval with a different sign ss->staticEval = bestValue = (ss - 1)->currentMove != MOVE_NULL ? evaluate(pos) : -(ss - 1)->staticEval; diff --git a/src/uci.cpp b/src/uci.cpp index 8139fae4fd8..95f6f349dd3 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -82,8 +82,8 @@ void position(Position& pos, std::istringstream& is, StateListPtr& states) { } } -// Prints the evaluation of the current position, consistent with -// the UCI options set so far. +// Prints the evaluation of the current position, +// consistent with the UCI options set so far. void trace_eval(Position& pos) { StateListPtr states(new std::deque(1)); @@ -122,9 +122,8 @@ void setoption(std::istringstream& is) { } -// Called when the engine receives the "go" UCI command. The function -// sets the thinking time and other parameters from the input string, then starts -// with a search. +// Called when the engine receives the "go" UCI command. The function sets the +// thinking time and other parameters from the input string then stars with a search void go(Position& pos, std::istringstream& is, StateListPtr& states) { diff --git a/src/uci.h b/src/uci.h index be5c70c54ce..55fb47c29ef 100644 --- a/src/uci.h +++ b/src/uci.h @@ -36,7 +36,7 @@ namespace UCI { // to the UCI centipawn result used in output. This value is derived from // the win_rate_model() such that Stockfish outputs an advantage of // "100 centipawns" for a position if the engine has a 50% probability to win -// from this position in selfplay at fishtest LTC time control. +// from this position in self-play at fishtest LTC time control. const int NormalizeToPawnValue = 328; class Option; From 13426a93c187c4953388a4484b8da69ee6f26fa3 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Thu, 23 Nov 2023 22:13:11 +0100 Subject: [PATCH 1266/1766] Simplify history update. Removal of the slowdown factor from the history update formula with corresponding adjustment of the stat bonus used in the search. Passed STC: https://tests.stockfishchess.org/tests/view/655e1079136acbc573544744 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 128096 W: 32355 L: 32235 D: 63506 Ptnml(0-2): 466, 15187, 32573, 15405, 417 Passed LTC: https://tests.stockfishchess.org/tests/view/655f4e60136acbc573546266 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 50652 W: 12653 L: 12459 D: 25540 Ptnml(0-2): 28, 5666, 13751, 5846, 35 closes https://github.com/official-stockfish/Stockfish/pull/4883 Bench: 1303857 --- src/movepick.h | 2 +- src/search.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/movepick.h b/src/movepick.h index 299925a582e..e032b0c7c79 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -58,7 +58,7 @@ class StatsEntry { assert(abs(bonus) <= D); // Ensure range is [-D, D] static_assert(D <= std::numeric_limits::max(), "D overflows T"); - entry += (bonus * D - entry * abs(bonus)) / (D * 5 / 4); + entry += bonus - entry * abs(bonus) / D; assert(abs(entry) <= D); } diff --git a/src/search.cpp b/src/search.cpp index c878b0aba1d..55be43b30f5 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -94,10 +94,10 @@ constexpr int futility_move_count(bool improving, Depth depth) { } // History and stats update bonus, based on depth -int stat_bonus(Depth d) { return std::min(364 * d - 438, 1501); } +int stat_bonus(Depth d) { return std::min(291 * d - 350, 1200); } // History and stats update malus, based on depth -int stat_malus(Depth d) { return std::min(452 * d - 452, 1478); } +int stat_malus(Depth d) { return std::min(361 * d - 361, 1182); } // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(const Thread* thisThread) { @@ -746,7 +746,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Use static evaluation difference to improve quiet move ordering (~4 Elo) if (is_ok((ss - 1)->currentMove) && !(ss - 1)->inCheck && !priorCapture) { - int bonus = std::clamp(-18 * int((ss - 1)->staticEval + ss->staticEval), -1812, 1812); + int bonus = std::clamp(-14 * int((ss - 1)->staticEval + ss->staticEval), -1449, 1449); thisThread->mainHistory[~us][from_to((ss - 1)->currentMove)] << bonus; if (type_of(pos.piece_on(prevSq)) != PAWN && type_of((ss - 1)->currentMove) != PROMOTION) thisThread->pawnHistory[pawn_structure(pos)][pos.piece_on(prevSq)][prevSq] << bonus / 4; From 757ae2ff53714b975066cd9eb3b518611bc06b11 Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Sat, 25 Nov 2023 22:16:56 +0800 Subject: [PATCH 1267/1766] Simplify move history reduction Recent VLTC search tuning has suggested that the depth limit can be increased by a lot. This patch simplifies away the depth-based bonus from statScore reduction, making the divisor a constant. Passed STC: https://tests.stockfishchess.org/tests/view/656201f5136acbc573549791 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 91520 W: 23130 L: 22967 D: 45423 Ptnml(0-2): 282, 10947, 23141, 11106, 284 Passed LTC: https://tests.stockfishchess.org/tests/view/6562b43a136acbc57354a581 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 352902 W: 86796 L: 86917 D: 179189 Ptnml(0-2): 190, 40227, 95741, 40100, 193 closes https://github.com/official-stockfish/Stockfish/pull/4886 Bench: 1297179 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 55be43b30f5..6580f520130 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1165,7 +1165,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo + (*contHist[3])[movedPiece][to_sq(move)] - 3848; // Decrease/increase reduction for moves with a good/bad history (~25 Elo) - r -= ss->statScore / (10216 + 3855 * (depth > 5 && depth < 23)); + r -= ss->statScore / 14200; // Step 17. Late moves reduction / extension (LMR, ~117 Elo) // We use various heuristics for the sons of a node after the first son has From f17db4641e0ec3a3d633cff6abc83e980a04ac4c Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sat, 2 Dec 2023 11:33:11 +0100 Subject: [PATCH 1268/1766] Simplify doDeeperSearch Removing dependence on d simplifies the doDeeperSearch formula and eliminates a variable that is not necessary in this context. Passed STC: https://tests.stockfishchess.org/tests/view/65647980136acbc57354c9f6 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 37440 W: 9558 L: 9334 D: 18548 Ptnml(0-2): 127, 4439, 9375, 4641, 138 Passed LTC: https://tests.stockfishchess.org/tests/view/6564c3f0136acbc57354d126 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 113946 W: 27993 L: 27864 D: 58089 Ptnml(0-2): 67, 12975, 30783, 13058, 90 closes https://github.com/official-stockfish/Stockfish/pull/4888 Bench: 1427733 --- src/search.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 6580f520130..0a12c85b721 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1188,9 +1188,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo { // Adjust full-depth search based on LMR results - if the result // was good enough search deeper, if it was bad enough search shallower. - const bool doDeeperSearch = - value > (bestValue + 51 + 10 * (newDepth - d)); // (~1 Elo) - const bool doShallowerSearch = value < bestValue + newDepth; // (~2 Elo) + const bool doDeeperSearch = value > (bestValue + 50 + 2 * newDepth); // (~1 Elo) + const bool doShallowerSearch = value < bestValue + newDepth; // (~2 Elo) newDepth += doDeeperSearch - doShallowerSearch; From 883163395ed464d17c6732e227a2d2c3c0b26f1e Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Sat, 2 Dec 2023 11:37:52 +0100 Subject: [PATCH 1269/1766] Simplify promotion move generation closes https://github.com/official-stockfish/Stockfish/pull/4892 No functional change --- src/movegen.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index 16da659d5e3..7d6856bb036 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -31,18 +31,12 @@ namespace { template ExtMove* make_promotions(ExtMove* moveList, [[maybe_unused]] Square to) { - if constexpr (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) - { + constexpr bool all = Type == EVASIONS || Type == NON_EVASIONS; + + if constexpr (Type == CAPTURES || all) *moveList++ = make(to - D, to, QUEEN); - if constexpr (Enemy && Type == CAPTURES) - { - *moveList++ = make(to - D, to, ROOK); - *moveList++ = make(to - D, to, BISHOP); - *moveList++ = make(to - D, to, KNIGHT); - } - } - if constexpr ((Type == QUIETS && !Enemy) || Type == EVASIONS || Type == NON_EVASIONS) + if constexpr ((Type == CAPTURES && Enemy) || (Type == QUIETS && !Enemy) || all) { *moveList++ = make(to - D, to, ROOK); *moveList++ = make(to - D, to, BISHOP); From 7dc40ac6437ec96d312e387b76573e5c496bd0b6 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Wed, 29 Nov 2023 13:47:25 +0300 Subject: [PATCH 1270/1766] Simplify quietMoveMalus malus Use a simple depth instead of depth + 1 in the quietMoveMalus formula. Passed STC: https://tests.stockfishchess.org/tests/view/65636bf0136acbc57354b662 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 105248 W: 26680 L: 26532 D: 52036 Ptnml(0-2): 409, 12590, 26481, 12732, 412 Passed LTC: https://tests.stockfishchess.org/tests/view/6563b5db136acbc57354bcab LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 204204 W: 50200 L: 50166 D: 103838 Ptnml(0-2): 123, 23331, 55145, 23395, 108 closes https://github.com/official-stockfish/Stockfish/pull/4893 Bench: 1717495 --- src/search.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 0a12c85b721..786d25c615b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1687,7 +1687,7 @@ void update_all_stats(const Position& pos, PieceType captured; int quietMoveBonus = stat_bonus(depth + 1); - int quietMoveMalus = stat_malus(depth + 1); + int quietMoveMalus = stat_malus(depth); if (!pos.capture_stage(bestMove)) { @@ -1898,9 +1898,9 @@ string UCI::pv(const Position& pos, Depth depth) { } -// Called in case we have no ponder move -// before exiting the search, for instance, in case we stop the search during a -// fail high at root. We try hard to have a ponder move to return to the GUI, +// Called in case we have no ponder move before exiting the search, +// for instance, in case we stop the search during a fail high at root. +// We try hard to have a ponder move to return to the GUI, // otherwise in case of 'ponder on' we have nothing to think about. bool RootMove::extract_ponder_from_tt(Position& pos) { From 85403a89bac9fe3538ae410fe651364abf78c504 Mon Sep 17 00:00:00 2001 From: Taras Vuk <117687515+TarasVuk@users.noreply.github.com> Date: Wed, 29 Nov 2023 18:20:43 +0100 Subject: [PATCH 1271/1766] Skip LMR for 2nd move at the root only This patch reverts commit by Vizvezdenec: https://github.com/official-stockfish/Stockfish/commit/27139dedac14af400f5b18e2ab50aca3f8cf0e33 Passed STC: https://tests.stockfishchess.org/tests/view/65660b4a136acbc57354f13d LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 301952 W: 76271 L: 76344 D: 149337 Ptnml(0-2): 1053, 36293, 76348, 36238, 1044 Passed LTC: https://tests.stockfishchess.org/tests/view/656738ab136acbc573550e39 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 25050 W: 6283 L: 6063 D: 12704 Ptnml(0-2): 10, 2756, 6775, 2972, 12 closes https://github.com/official-stockfish/Stockfish/pull/4895 Bench: 1722961 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 786d25c615b..409a3c1d429 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1171,7 +1171,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // We use various heuristics for the sons of a node after the first son has // been searched. In general, we would like to reduce them, but there are many // cases where we extend a son if it has good chances to be "interesting". - if (depth >= 2 && moveCount > 1 + (PvNode && ss->ply <= 1) + if (depth >= 2 && moveCount > 1 + rootNode && (!ss->ttPv || !capture || (cutNode && (ss - 1)->moveCount > 1))) { // In general we want to cap the LMR depth search at newDepth, but when From 15d47a2b3821b92c4d048f39f7f43c301299d365 Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Wed, 29 Nov 2023 17:25:01 +0800 Subject: [PATCH 1272/1766] Remove recaptures stage in qsearch Simplify an old commit https://github.com/official-stockfish/Stockfish/commit/72760c05c64d1fb2bb71c2ac54acfbeecf513b87. Search is not stuck on the test position given r1n1n1b1/1P1P1P1P/1N1N1N2/2RnQrRq/2pKp3/3BNQbQ/k7/4Bq2 w - - 0 1 Passed STC: https://tests.stockfishchess.org/tests/view/6567050d136acbc573550919 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 236160 W: 59475 L: 59475 D: 117210 Ptnml(0-2): 841, 28266, 59816, 28366, 791 Passed LTC: https://tests.stockfishchess.org/tests/view/6567d133136acbc573551c78 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 201690 W: 49630 L: 49593 D: 102467 Ptnml(0-2): 128, 23214, 54122, 23255, 126 closes https://github.com/official-stockfish/Stockfish/pull/4896 Bench: 1604361 --- src/movepick.cpp | 7 ++----- src/movepick.h | 4 +--- src/search.cpp | 2 +- src/types.h | 5 ++--- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 798f51ddb6e..0267a8e2e6f 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -112,15 +112,13 @@ MovePicker::MovePicker(const Position& p, const ButterflyHistory* mh, const CapturePieceToHistory* cph, const PieceToHistory** ch, - const PawnHistory* ph, - Square rs) : + const PawnHistory* ph) : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), pawnHistory(ph), ttMove(ttm), - recaptureSquare(rs), depth(d) { assert(d <= 0); @@ -340,8 +338,7 @@ Move MovePicker::next_move(bool skipQuiets) { return select([&]() { return pos.see_ge(*cur, threshold); }); case QCAPTURE : - if (select( - [&]() { return depth > DEPTH_QS_RECAPTURES || to_sq(*cur) == recaptureSquare; })) + if (select([]() { return true; })) return *(cur - 1); // If we did not find any move and we do not try checks, we have finished diff --git a/src/movepick.h b/src/movepick.h index e032b0c7c79..7828fa19d97 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -152,8 +152,7 @@ class MovePicker { const ButterflyHistory*, const CapturePieceToHistory*, const PieceToHistory**, - const PawnHistory*, - Square); + const PawnHistory*); MovePicker(const Position&, Move, Value, const CapturePieceToHistory*); Move next_move(bool skipQuiets = false); @@ -173,7 +172,6 @@ class MovePicker { Move ttMove; ExtMove refutations[3], *cur, *endMoves, *endBadCaptures; int stage; - Square recaptureSquare; Value threshold; Depth depth; ExtMove moves[MAX_MOVES]; diff --git a/src/search.cpp b/src/search.cpp index 409a3c1d429..16003138483 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1487,7 +1487,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { // will be generated. Square prevSq = is_ok((ss - 1)->currentMove) ? to_sq((ss - 1)->currentMove) : SQ_NONE; MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory, - contHist, &thisThread->pawnHistory, prevSq); + contHist, &thisThread->pawnHistory); int quietCheckEvasions = 0; diff --git a/src/types.h b/src/types.h index 7ac2f84951a..0575f1d453c 100644 --- a/src/types.h +++ b/src/types.h @@ -205,9 +205,8 @@ constexpr Value PieceValue[PIECE_NB] = { using Depth = int; enum : int { - DEPTH_QS_CHECKS = 0, - DEPTH_QS_NO_CHECKS = -1, - DEPTH_QS_RECAPTURES = -5, + DEPTH_QS_CHECKS = 0, + DEPTH_QS_NO_CHECKS = -1, DEPTH_NONE = -6, From 08cdbca56fac98513481683a92eb1ecdc00d3f6e Mon Sep 17 00:00:00 2001 From: lonfom169 <50217346+lonfom169@users.noreply.github.com> Date: Thu, 30 Nov 2023 01:12:19 -0300 Subject: [PATCH 1273/1766] Tweak return value in futility pruning In futility pruning, return the average between eval and beta. Passed STC: https://tests.stockfishchess.org/tests/view/65680bb6136acbc5735521d7 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 15200 W: 3926 L: 3642 D: 7632 Ptnml(0-2): 36, 1699, 3867, 1941, 57 Passed LTC: https://tests.stockfishchess.org/tests/view/656817fc136acbc573552304 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 200376 W: 49700 L: 49036 D: 101640 Ptnml(0-2): 110, 22584, 54137, 23246, 111 closes https://github.com/official-stockfish/Stockfish/pull/4897 Bench: 1403703 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 16003138483..ba5ea2e54f9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -780,7 +780,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo >= beta && eval >= beta && eval < 29462 // smaller than TB wins && (!ttMove || ttCapture)) - return eval; + return (eval + beta) / 2; // Step 9. Null move search with verification search (~35 Elo) if (!PvNode && (ss - 1)->currentMove != MOVE_NULL && (ss - 1)->statScore < 17257 && eval >= beta From 8f65953583a2abc34041b087120a378e22d0509d Mon Sep 17 00:00:00 2001 From: Sebastian Buchwald Date: Sun, 3 Dec 2023 13:37:59 +0100 Subject: [PATCH 1274/1766] Temporarily disable CI include checks The include checks currently fail because of broken LLVM nightly packages: https://github.com/llvm/llvm-project/issues/73402. closes https://github.com/official-stockfish/Stockfish/pull/4899 No functional change --- .github/workflows/stockfish.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index 1ed4b92d4ca..83dd1b9c86c 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -33,8 +33,9 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - Analyzers: - uses: ./.github/workflows/stockfish_analyzers.yml + # The include checks currently fail because of broken LLVM nightly packages: https://github.com/llvm/llvm-project/issues/73402 + #Analyzers: + # uses: ./.github/workflows/stockfish_analyzers.yml Sanitizers: uses: ./.github/workflows/stockfish_sanitizers.yml Tests: From 0ff2ea654971445f4e8955840bcb974dc62e5106 Mon Sep 17 00:00:00 2001 From: ppigazzini Date: Sun, 3 Dec 2023 13:39:52 +0100 Subject: [PATCH 1275/1766] Update GitHub workflows - Use the latest version of the actions - Use commit hash for actions from little providers - Update Intel SDE to 9.27 closes https://github.com/official-stockfish/Stockfish/pull/4900 No functional change --- .github/workflows/codeql.yml | 2 +- .github/workflows/stockfish.yml | 2 +- .github/workflows/stockfish_analyzers.yml | 4 ++-- .github/workflows/stockfish_arm_binaries.yml | 6 +++--- .github/workflows/stockfish_binaries.yml | 16 ++++++++-------- .github/workflows/stockfish_compile_test.yml | 2 +- .github/workflows/stockfish_format_check.yml | 8 ++++---- .github/workflows/stockfish_sanitizers.yml | 2 +- .github/workflows/stockfish_test.yml | 6 +++--- 9 files changed, 24 insertions(+), 24 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 863f219ca7b..054be90040c 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -29,7 +29,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index 83dd1b9c86c..6d71fef5aef 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -26,7 +26,7 @@ jobs: echo "COMMIT_SHA=$(jq -r 'map(select(.prerelease)) | first | .tag_name' <<< $(curl -s https://api.github.com/repos/${{ github.repository_owner }}/Stockfish/releases))" >> $GITHUB_ENV # delete old previous pre-release and tag - - uses: dev-drprasad/delete-tag-and-release@v0.2.1 + - uses: dev-drprasad/delete-tag-and-release@8cd619d00037e4aeb781909c9a6b03940507d0da # @v1.0.1 if: env.COMMIT_SHA != 'null' with: tag_name: ${{ env.COMMIT_SHA }} diff --git a/.github/workflows/stockfish_analyzers.yml b/.github/workflows/stockfish_analyzers.yml index 5f985cc859f..f54cdd7ca6b 100644 --- a/.github/workflows/stockfish_analyzers.yml +++ b/.github/workflows/stockfish_analyzers.yml @@ -11,12 +11,12 @@ jobs: shell: bash steps: - name: Checkout Stockfish - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: Stockfish - name: Checkout include-what-you-use - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: include-what-you-use/include-what-you-use ref: f25caa280dc3277c4086ec345ad279a2463fea0f diff --git a/.github/workflows/stockfish_arm_binaries.yml b/.github/workflows/stockfish_arm_binaries.yml index 4d7f3d55c1a..203c00f2ce2 100644 --- a/.github/workflows/stockfish_arm_binaries.yml +++ b/.github/workflows/stockfish_arm_binaries.yml @@ -46,7 +46,7 @@ jobs: working-directory: src shell: ${{ matrix.config.shell }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 @@ -139,7 +139,7 @@ jobs: - name: Release if: startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # @v1 with: files: stockfish-android-${{ matrix.binaries }}.tar @@ -162,7 +162,7 @@ jobs: - name: Prerelease if: github.ref_name == 'master' && env.CHANGES == '0' continue-on-error: true - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # @v1 with: name: Stockfish dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} tag_name: stockfish-dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} diff --git a/.github/workflows/stockfish_binaries.yml b/.github/workflows/stockfish_binaries.yml index 6da576e40a0..5b3a522625c 100644 --- a/.github/workflows/stockfish_binaries.yml +++ b/.github/workflows/stockfish_binaries.yml @@ -23,7 +23,7 @@ jobs: comp: gcc shell: bash archive_ext: tar - sde: /home/runner/work/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.14.0-2022-10-25-lin/sde -future -- + sde: /home/runner/work/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.27.0-2023-09-13-lin/sde -future -- - name: MacOS 13 Apple Clang os: macos-13 simple_name: macos @@ -40,7 +40,7 @@ jobs: msys_env: x86_64-gcc shell: msys2 {0} ext: .exe - sde: /d/a/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.14.0-2022-10-25-win/sde.exe -future -- + sde: /d/a/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.27.0-2023-09-13-win/sde.exe -future -- archive_ext: zip binaries: - x86-64 @@ -67,7 +67,7 @@ jobs: working-directory: src shell: ${{ matrix.config.shell }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 @@ -77,7 +77,7 @@ jobs: - name: Install fixed GCC on Linux if: runner.os == 'Linux' - uses: egor-tensin/setup-gcc@v1 + uses: egor-tensin/setup-gcc@eaa888eb19115a521fa72b65cd94fe1f25bbcaac # @v1.3 with: version: 11 @@ -90,10 +90,10 @@ jobs: - name: Download SDE package if: runner.os == 'Linux' || runner.os == 'Windows' - uses: petarpetrovt/setup-sde@6f4926100f31791716b11d25c0f3f35809d44f84 + uses: petarpetrovt/setup-sde@91a1a03434384e064706634125a15f7446d2aafb # @v2.3 with: environmentVariableName: SDE_DIR - sdeVersion: 9.14.0 + sdeVersion: 9.27.0 - name: Download the used network from the fishtest framework run: make net @@ -183,7 +183,7 @@ jobs: - name: Release if: startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # @v1 with: files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.${{ matrix.config.archive_ext }} @@ -206,7 +206,7 @@ jobs: - name: Prerelease if: github.ref_name == 'master' && env.CHANGES == '0' continue-on-error: true - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # @v1 with: name: Stockfish dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} tag_name: stockfish-dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} diff --git a/.github/workflows/stockfish_compile_test.yml b/.github/workflows/stockfish_compile_test.yml index 808fcb55f7b..1adc3e34b7c 100644 --- a/.github/workflows/stockfish_compile_test.yml +++ b/.github/workflows/stockfish_compile_test.yml @@ -51,7 +51,7 @@ jobs: working-directory: src shell: ${{ matrix.config.shell }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup msys and install required packages if: runner.os == 'Windows' diff --git a/.github/workflows/stockfish_format_check.yml b/.github/workflows/stockfish_format_check.yml index cb16b327871..7a47ab6f406 100644 --- a/.github/workflows/stockfish_format_check.yml +++ b/.github/workflows/stockfish_format_check.yml @@ -16,12 +16,12 @@ jobs: name: clang-format check runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} - name: Run clang-format style check - uses: jidicula/clang-format-action@f62da5e3d3a2d88ff364771d9d938773a618ab5e + uses: jidicula/clang-format-action@f62da5e3d3a2d88ff364771d9d938773a618ab5e # @v4.11.0 id: clang-format continue-on-error: true with: @@ -30,7 +30,7 @@ jobs: - name: Comment on PR if: steps.clang-format.outcome == 'failure' - uses: thollander/actions-comment-pull-request@1d3973dc4b8e1399c0620d3f2b1aa5e795465308 + uses: thollander/actions-comment-pull-request@1d3973dc4b8e1399c0620d3f2b1aa5e795465308 # @v2.4.3 with: message: | clang-format 17 needs to be run on this PR. @@ -42,7 +42,7 @@ jobs: - name: Comment on PR if: steps.clang-format.outcome != 'failure' - uses: thollander/actions-comment-pull-request@1d3973dc4b8e1399c0620d3f2b1aa5e795465308 + uses: thollander/actions-comment-pull-request@1d3973dc4b8e1399c0620d3f2b1aa5e795465308 # @v2.4.3 with: message: | _(execution **${{ github.run_id }}** / attempt **${{ github.run_attempt }}**)_ diff --git a/.github/workflows/stockfish_sanitizers.yml b/.github/workflows/stockfish_sanitizers.yml index b137f50eb06..e3f046178f5 100644 --- a/.github/workflows/stockfish_sanitizers.yml +++ b/.github/workflows/stockfish_sanitizers.yml @@ -35,7 +35,7 @@ jobs: working-directory: src shell: ${{ matrix.config.shell }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Download required linux packages run: | diff --git a/.github/workflows/stockfish_test.yml b/.github/workflows/stockfish_test.yml index 72f0c22e136..cff3ef1b184 100644 --- a/.github/workflows/stockfish_test.yml +++ b/.github/workflows/stockfish_test.yml @@ -95,7 +95,7 @@ jobs: working-directory: src shell: ${{ matrix.config.shell }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 @@ -121,11 +121,11 @@ jobs: - name: Set up QEMU if: matrix.config.base_image - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx if: matrix.config.base_image - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Build Docker container if: matrix.config.base_image From 7a8bcfc229ca6e4e44c0b284b7609a3aa26fa1ee Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Mon, 4 Dec 2023 11:31:16 +0100 Subject: [PATCH 1276/1766] Remove cutNode condition cutNode condition seems to be irrelevant. Passed STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 24224 W: 6206 L: 5970 D: 12048 Ptnml(0-2): 69, 2818, 6122, 3014, 89 https://tests.stockfishchess.org/tests/view/65686910136acbc5735529ec Passed LTC: LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 236538 W: 58624 L: 58622 D: 119292 Ptnml(0-2): 136, 26955, 64091, 26945, 142 https://tests.stockfishchess.org/tests/view/6568925a136acbc573552d8f closes https://github.com/official-stockfish/Stockfish/pull/4901 Bench: 1244386 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ba5ea2e54f9..b3ca8c9afe5 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1207,8 +1207,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Step 18. Full-depth search when LMR is skipped else if (!PvNode || moveCount > 1) { - // Increase reduction for cut nodes without ttMove (~1 Elo) - if (!ttMove && cutNode) + // Increase reduction if ttMove is not present (~1 Elo) + if (!ttMove) r += 2; // Note that if expected reduction is high, we reduce search depth by 1 here From dadff4698651336ebd07c414f74c1e707cd9bd15 Mon Sep 17 00:00:00 2001 From: Disservin Date: Tue, 5 Dec 2023 11:49:12 +0100 Subject: [PATCH 1277/1766] Revert "Temporarily disable CI include checks" This reverts commit 8f65953583a2abc34041b087120a378e22d0509d. closes https://github.com/official-stockfish/Stockfish/pull/4904 No functional change --- .github/workflows/stockfish.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index 6d71fef5aef..7bbb53d5c62 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -33,9 +33,8 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # The include checks currently fail because of broken LLVM nightly packages: https://github.com/llvm/llvm-project/issues/73402 - #Analyzers: - # uses: ./.github/workflows/stockfish_analyzers.yml + Analyzers: + uses: ./.github/workflows/stockfish_analyzers.yml Sanitizers: uses: ./.github/workflows/stockfish_sanitizers.yml Tests: From 53ad6d23b0e7ec2814579d4acba7c02c2b12008f Mon Sep 17 00:00:00 2001 From: Taras Vuk <117687515+TarasVuk@users.noreply.github.com> Date: Tue, 5 Dec 2023 15:41:10 +0100 Subject: [PATCH 1278/1766] Remove moveMalus Passed STC: https://tests.stockfishchess.org/tests/view/656e0bb86980e15f69c763fa LLR: 3.15 (-2.94,2.94) <-1.75,0.25> Total: 123008 W: 30973 L: 30831 D: 61204 Ptnml(0-2): 368, 14032, 32568, 14162, 374 closes https://github.com/official-stockfish/Stockfish/pull/4905 No functional change --- src/search.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index b3ca8c9afe5..39711f3c611 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1699,18 +1699,15 @@ void update_all_stats(const Position& pos, thisThread->pawnHistory[pawn_structure(pos)][moved_piece][to_sq(bestMove)] << quietMoveBonus; - int moveMalus = bestValue > beta + 168 ? quietMoveMalus // larger malus - : stat_malus(depth); // smaller malus - // Decrease stats for all non-best quiet moves for (int i = 0; i < quietCount; ++i) { thisThread->pawnHistory[pawn_structure(pos)][pos.moved_piece(quietsSearched[i])] [to_sq(quietsSearched[i])] - << -moveMalus; - thisThread->mainHistory[us][from_to(quietsSearched[i])] << -moveMalus; + << -quietMoveMalus; + thisThread->mainHistory[us][from_to(quietsSearched[i])] << -quietMoveMalus; update_continuation_histories(ss, pos.moved_piece(quietsSearched[i]), - to_sq(quietsSearched[i]), -moveMalus); + to_sq(quietsSearched[i]), -quietMoveMalus); } } else From 8724503d9c8f15f9185bd4394e425e809f3992c1 Mon Sep 17 00:00:00 2001 From: ppigazzini Date: Wed, 6 Dec 2023 18:01:58 +0100 Subject: [PATCH 1279/1766] Simplify the code to get the native flags closes https://github.com/official-stockfish/Stockfish/pull/4908 No functional change --- scripts/get_native_properties.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts/get_native_properties.sh b/scripts/get_native_properties.sh index cffb0ce2731..ae23c3bb4ac 100755 --- a/scripts/get_native_properties.sh +++ b/scripts/get_native_properties.sh @@ -14,10 +14,9 @@ check_flags() { } # Set the CPU flags list +# remove underscores and points from flags, e.g. gcc uses avx512vnni, while some cpuinfo can have avx512_vnni, some systems use sse4_1 others sse4.1 get_flags() { - flags="$(awk '/^flags[ \t]*:/{gsub(/^flags[ \t]*:[ \t]*/, ""); line=$0} END{print line}' /proc/cpuinfo) $(awk '/^Features[ \t]*:/{gsub(/^Features[ \t]*:[ \t]*/, ""); line=$0} END{print line}' /proc/cpuinfo)" - # remove underscores and points from flags, e.g. gcc uses avx512vnni, while some cpuinfo can have avx512_vnni, some systems use sse4_1 others sse4.1 - flags=$(printf '%s' "$flags" | sed "s/[_.]//g") + flags=$(awk '/^flags[ \t]*:|^Features[ \t]*:/{gsub(/^flags[ \t]*:[ \t]*|^Features[ \t]*:[ \t]*|[_.]/, ""); line=$0} END{print line}' /proc/cpuinfo) } # Check for gcc march "znver1" or "znver2" https://en.wikichip.org/wiki/amd/cpuid @@ -55,7 +54,7 @@ case $uname_s in file_arch='x86-64-sse41-popcnt' # Supported by Rosetta 2 ;; 'x86_64') - flags=$(sysctl -n machdep.cpu.features machdep.cpu.leaf7_features | tr '\n' ' ' | tr '[:upper:]' '[:lower:]' | sed "s/[_.]//g") + flags=$(sysctl -n machdep.cpu.features machdep.cpu.leaf7_features | tr '\n' ' ' | tr '[:upper:]' '[:lower:]' | tr -d '_.') set_arch_x86_64 if [ "$true_arch" = 'x86-64-vnni256' ] || [ "$true_arch" = 'x86-64-avx512' ]; then file_arch='x86-64-bmi2' From 36db936e769a2e7a95fc4032eec3b79251bbaef5 Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Fri, 8 Dec 2023 21:39:57 +0800 Subject: [PATCH 1280/1766] VLTC Search parameters tune MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SPSA tuning was done for 44k games at 120+1.2. https://tests.stockfishchess.org/tests/view/656ee2a76980e15f69c7767f. Note that the tune was originally done in combination with the recent dual NNUE idea (see #4910). VLTC: https://tests.stockfishchess.org/tests/view/65731ccbf09ce1261f12246e LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 52806 W: 13069 L: 12760 D: 26977 Ptnml(0-2): 19, 5498, 15056, 5815, 15 VLTC SMP: https://tests.stockfishchess.org/tests/view/65740ffaf09ce1261f1239ba LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 27630 W: 6934 L: 6651 D: 14045 Ptnml(0-2): 1, 2643, 8243, 2928, 0 Estimated close to neutral at LTC: https://tests.stockfishchess.org/tests/view/6575485a8ec68176cf7d9423 Elo: -0.59 ± 1.8 (95%) LOS: 26.6% Total: 32060 W: 7859 L: 7913 D: 16288 Ptnml(0-2): 20, 3679, 8676, 3645, 10 nElo: -1.21 ± 3.8 (95%) PairsRatio: 0.99 closes https://github.com/official-stockfish/Stockfish/pull/4912 Bench: 1283323 --- src/search.cpp | 76 +++++++++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 39711f3c611..f398074065d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -77,7 +77,7 @@ enum NodeType { // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving) { - return Value((125 - 43 * noTtCutNode) * (d - improving)); + return Value((116 - 44 * noTtCutNode) * (d - improving)); } // Reductions lookup table initialized at startup @@ -85,8 +85,8 @@ int Reductions[MAX_MOVES]; // [depth or moveNumber] Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) { int reductionScale = Reductions[d] * Reductions[mn]; - return (reductionScale + 1487 - int(delta) * 976 / int(rootDelta)) / 1024 - + (!i && reductionScale > 808); + return (reductionScale + 1346 - int(delta) * 896 / int(rootDelta)) / 1024 + + (!i && reductionScale > 880); } constexpr int futility_move_count(bool improving, Depth depth) { @@ -94,10 +94,10 @@ constexpr int futility_move_count(bool improving, Depth depth) { } // History and stats update bonus, based on depth -int stat_bonus(Depth d) { return std::min(291 * d - 350, 1200); } +int stat_bonus(Depth d) { return std::min(268 * d - 352, 1153); } // History and stats update malus, based on depth -int stat_malus(Depth d) { return std::min(361 * d - 361, 1182); } +int stat_malus(Depth d) { return std::min(400 * d - 354, 1201); } // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(const Thread* thisThread) { @@ -367,12 +367,12 @@ void Thread::search() { // Reset aspiration window starting size Value avg = rootMoves[pvIdx].averageScore; - delta = Value(10) + int(avg) * avg / 15335; + delta = Value(9) + int(avg) * avg / 14847; alpha = std::max(avg - delta, -VALUE_INFINITE); beta = std::min(avg + delta, VALUE_INFINITE); // Adjust optimism based on root move's averageScore (~4 Elo) - optimism[us] = 110 * avg / (std::abs(avg) + 121); + optimism[us] = 121 * avg / (std::abs(avg) + 109); optimism[~us] = -optimism[us]; // Start with a small aspiration window and, in the case of a fail @@ -746,7 +746,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Use static evaluation difference to improve quiet move ordering (~4 Elo) if (is_ok((ss - 1)->currentMove) && !(ss - 1)->inCheck && !priorCapture) { - int bonus = std::clamp(-14 * int((ss - 1)->staticEval + ss->staticEval), -1449, 1449); + int bonus = std::clamp(-13 * int((ss - 1)->staticEval + ss->staticEval), -1555, 1452); thisThread->mainHistory[~us][from_to((ss - 1)->currentMove)] << bonus; if (type_of(pos.piece_on(prevSq)) != PAWN && type_of((ss - 1)->currentMove) != PROMOTION) thisThread->pawnHistory[pawn_structure(pos)][pos.piece_on(prevSq)][prevSq] << bonus / 4; @@ -765,7 +765,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. // Adjust razor margin according to cutoffCnt. (~1 Elo) - if (eval < alpha - 474 - (270 - 174 * ((ss + 1)->cutoffCnt > 3)) * depth * depth) + if (eval < alpha - 472 - (284 - 165 * ((ss + 1)->cutoffCnt > 3)) * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -776,22 +776,22 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // The depth condition is important for mate finding. if (!ss->ttPv && depth < 9 && eval - futility_margin(depth, cutNode && !ss->ttHit, improving) - - (ss - 1)->statScore / 321 + - (ss - 1)->statScore / 337 >= beta - && eval >= beta && eval < 29462 // smaller than TB wins + && eval >= beta && eval < 29008 // smaller than TB wins && (!ttMove || ttCapture)) return (eval + beta) / 2; // Step 9. Null move search with verification search (~35 Elo) - if (!PvNode && (ss - 1)->currentMove != MOVE_NULL && (ss - 1)->statScore < 17257 && eval >= beta - && eval >= ss->staticEval && ss->staticEval >= beta - 24 * depth + 281 && !excludedMove + if (!PvNode && (ss - 1)->currentMove != MOVE_NULL && (ss - 1)->statScore < 17496 && eval >= beta + && eval >= ss->staticEval && ss->staticEval >= beta - 23 * depth + 304 && !excludedMove && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly && beta > VALUE_TB_LOSS_IN_MAX_PLY) { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and eval - Depth R = std::min(int(eval - beta) / 152, 6) + depth / 3 + 4; + Depth R = std::min(int(eval - beta) / 144, 6) + depth / 3 + 4; ss->currentMove = MOVE_NULL; ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -805,7 +805,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Do not return unproven mate or TB scores if (nullValue >= beta && nullValue < VALUE_TB_WIN_IN_MAX_PLY) { - if (thisThread->nmpMinPly || depth < 14) + if (thisThread->nmpMinPly || depth < 15) return nullValue; assert(!thisThread->nmpMinPly); // Recursive verification is not allowed @@ -838,7 +838,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo if (cutNode && depth >= 8 && !ttMove) depth -= 2; - probCutBeta = beta + 168 - 70 * improving; + probCutBeta = beta + 163 - 67 * improving; // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search returns a value @@ -896,7 +896,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo moves_loop: // When in check, search starts here // Step 12. A small Probcut idea, when we are in check (~4 Elo) - probCutBeta = beta + 416; + probCutBeta = beta + 425; if (ss->inCheck && !PvNode && ttCapture && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 4 && ttValue >= probCutBeta && abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) @@ -983,14 +983,14 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo { Piece capturedPiece = pos.piece_on(to_sq(move)); int futilityEval = - ss->staticEval + 239 + 291 * lmrDepth + PieceValue[capturedPiece] + ss->staticEval + 238 + 305 * lmrDepth + PieceValue[capturedPiece] + captureHistory[movedPiece][to_sq(move)][type_of(capturedPiece)] / 7; if (futilityEval < alpha) continue; } // SEE based pruning for captures and checks (~11 Elo) - if (!pos.see_ge(move, Value(-185) * depth)) + if (!pos.see_ge(move, Value(-187) * depth)) continue; } else @@ -1001,18 +1001,18 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo + thisThread->pawnHistory[pawn_structure(pos)][movedPiece][to_sq(move)]; // Continuation history based pruning (~2 Elo) - if (lmrDepth < 6 && history < -3645 * depth) + if (lmrDepth < 6 && history < -3752 * depth) continue; history += 2 * thisThread->mainHistory[us][from_to(move)]; - lmrDepth += history / 7836; + lmrDepth += history / 7838; lmrDepth = std::max(lmrDepth, -1); // Futility pruning: parent node (~13 Elo) - if (!ss->inCheck && lmrDepth < 13 - && ss->staticEval + (bestValue < ss->staticEval - 62 ? 123 : 77) - + 127 * lmrDepth + if (!ss->inCheck && lmrDepth < 14 + && ss->staticEval + (bestValue < ss->staticEval - 57 ? 124 : 71) + + 118 * lmrDepth <= alpha) continue; @@ -1039,11 +1039,11 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // so changing them requires tests at these types of time controls. // Recursive singular search is avoided. if (!rootNode && move == ttMove && !excludedMove - && depth >= 4 - (thisThread->completedDepth > 24) + 2 * (PvNode && tte->is_pv()) + && depth >= 4 - (thisThread->completedDepth > 27) + 2 * (PvNode && tte->is_pv()) && abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - (64 + 57 * (ss->ttPv && !PvNode)) * depth / 64; + Value singularBeta = ttValue - (66 + 58 * (ss->ttPv && !PvNode)) * depth / 64; Depth singularDepth = newDepth / 2; ss->excludedMove = move; @@ -1057,7 +1057,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo singularQuietLMR = !ttCapture; // Avoid search explosion by limiting the number of double extensions - if (!PvNode && value < singularBeta - 18 && ss->doubleExtensions <= 11) + if (!PvNode && value < singularBeta - 17 && ss->doubleExtensions <= 11) { extension = 2; depth += depth < 15; @@ -1092,18 +1092,18 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo } // Check extensions (~1 Elo) - else if (givesCheck && depth > 9) + else if (givesCheck && depth > 10) extension = 1; // Quiet ttMove extensions (~1 Elo) else if (PvNode && move == ttMove && move == ss->killers[0] - && (*contHist[0])[movedPiece][to_sq(move)] >= 4194) + && (*contHist[0])[movedPiece][to_sq(move)] >= 4325) extension = 1; // Recapture extensions (~1 Elo) else if (PvNode && move == ttMove && to_sq(move) == prevSq && captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] - > 4000) + > 4146) extension = 1; } @@ -1162,10 +1162,10 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo ss->statScore = 2 * thisThread->mainHistory[us][from_to(move)] + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] - + (*contHist[3])[movedPiece][to_sq(move)] - 3848; + + (*contHist[3])[movedPiece][to_sq(move)] - 3817; // Decrease/increase reduction for moves with a good/bad history (~25 Elo) - r -= ss->statScore / 14200; + r -= ss->statScore / 14767; // Step 17. Late moves reduction / extension (LMR, ~117 Elo) // We use various heuristics for the sons of a node after the first son has @@ -1188,7 +1188,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo { // Adjust full-depth search based on LMR results - if the result // was good enough search deeper, if it was bad enough search shallower. - const bool doDeeperSearch = value > (bestValue + 50 + 2 * newDepth); // (~1 Elo) + const bool doDeeperSearch = value > (bestValue + 53 + 2 * newDepth); // (~1 Elo) const bool doShallowerSearch = value < bestValue + newDepth; // (~2 Elo) newDepth += doDeeperSearch - doShallowerSearch; @@ -1303,7 +1303,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo else { // Reduce other moves if we have found at least one score improvement (~2 Elo) - if (depth > 2 && depth < 12 && beta < 13828 && value > -11369) + if (depth > 2 && depth < 12 && beta < 13782 && value > -11541) depth -= 2; assert(depth > 0); @@ -1342,7 +1342,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (depth > 6) + (PvNode || cutNode) + (bestValue < alpha - 657) + int bonus = (depth > 6) + (PvNode || cutNode) + (bestValue < alpha - 656) + ((ss - 1)->moveCount > 10); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); @@ -1475,7 +1475,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { if (bestValue > alpha) alpha = bestValue; - futilityBase = ss->staticEval + 200; + futilityBase = ss->staticEval + 182; } const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, @@ -1555,7 +1555,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { continue; // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, Value(-90))) + if (!pos.see_ge(move, Value(-77))) continue; } @@ -1691,7 +1691,7 @@ void update_all_stats(const Position& pos, if (!pos.capture_stage(bestMove)) { - int bestMoveBonus = bestValue > beta + 168 ? quietMoveBonus // larger bonus + int bestMoveBonus = bestValue > beta + 173 ? quietMoveBonus // larger bonus : stat_bonus(depth); // smaller bonus // Increase stats for the best move in case it was a quiet move From 282e15bf75bd1142de96306b22424f0cd2bb8dfa Mon Sep 17 00:00:00 2001 From: "Shahin M. Shahin" <41402573+peregrineshahin@users.noreply.github.com> Date: Fri, 17 Nov 2023 18:25:50 +0300 Subject: [PATCH 1281/1766] Fix TB score output in UCI without using TB This is a rewrite of the fix introduced for https://github.com/official-stockfish/Stockfish/issues/4413 in https://github.com/official-stockfish/Stockfish/pull/4591 by @windfishballad it targets only the relevant part of this issue that returns TB scores (CP 20000) without using TB due to the downgrading of potentially false mates from the TT to an optimal TB score. the difference is that it is a much clearer code that introduces a separate TB_VALUE constant to account for a correct distance from the TB_VALUE with MAX_PLY. the originally posted position in the issue does not trigger the problem anymore, so here is a new position to test: ``` position fen 3k4/8/8/8/8/8/3BN3/3K4 w - - 0 1 go infinite ``` Passed non-regression STC: https://tests.stockfishchess.org/tests/view/65578994136acbc57353b258 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 119264 W: 29993 L: 29863 D: 59408 Ptnml(0-2): 372, 13692, 31379, 13812, 377 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/6558323f136acbc57353c1ca LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 237834 W: 58791 L: 58792 D: 120251 Ptnml(0-2): 193, 26200, 66111, 26241, 172 fixes https://github.com/official-stockfish/Stockfish/issues/4413 closes https://github.com/official-stockfish/Stockfish/pull/4591 closes https://github.com/official-stockfish/Stockfish/pull/4882 Bench: 1305821 --- src/search.cpp | 39 +++++++++++++++++++++++++++------------ src/types.h | 12 +++++++----- src/uci.cpp | 4 ++-- 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index f398074065d..89879374c1a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -678,9 +678,11 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo int drawScore = TB::UseRule50 ? 1 : 0; - // use the range VALUE_MATE_IN_MAX_PLY to VALUE_TB_WIN_IN_MAX_PLY to score - value = wdl < -drawScore ? VALUE_MATED_IN_MAX_PLY + ss->ply + 1 - : wdl > drawScore ? VALUE_MATE_IN_MAX_PLY - ss->ply - 1 + Value tbValue = VALUE_TB - ss->ply; + + // use the range VALUE_TB to VALUE_TB_WIN_IN_MAX_PLY to score + value = wdl < -drawScore ? -tbValue + : wdl > drawScore ? tbValue : VALUE_DRAW + 2 * wdl * drawScore; Bound b = wdl < -drawScore ? BOUND_UPPER @@ -1631,25 +1633,38 @@ Value value_to_tt(Value v, int ply) { // Inverse of value_to_tt(): it adjusts a mate or TB score // from the transposition table (which refers to the plies to mate/be mated from // current position) to "plies to mate/be mated (TB win/loss) from the root". -// However, to avoid potentially false mate scores related to the 50 moves rule -// and the graph history interaction problem, we return an optimal TB score instead. +// However, to avoid potentially false mate or TB scores related to the 50 moves rule +// and the graph history interaction, we return highest non-TB score instead. + Value value_from_tt(Value v, int ply, int r50c) { if (v == VALUE_NONE) return VALUE_NONE; - if (v >= VALUE_TB_WIN_IN_MAX_PLY) // TB win or better + // handle TB win or better + if (v >= VALUE_TB_WIN_IN_MAX_PLY) { - if (v >= VALUE_MATE_IN_MAX_PLY && VALUE_MATE - v > 99 - r50c) - return VALUE_MATE_IN_MAX_PLY - 1; // do not return a potentially false mate score + // Downgrade a potentially false mate score + if (v >= VALUE_MATE_IN_MAX_PLY && VALUE_MATE - v > 100 - r50c) + return VALUE_TB_WIN_IN_MAX_PLY - 1; + + // Downgrade a potentially false TB score. + if (VALUE_TB - v > 100 - r50c) + return VALUE_TB_WIN_IN_MAX_PLY - 1; return v - ply; } - if (v <= VALUE_TB_LOSS_IN_MAX_PLY) // TB loss or worse + // handle TB loss or worse + if (v <= VALUE_TB_LOSS_IN_MAX_PLY) { - if (v <= VALUE_MATED_IN_MAX_PLY && VALUE_MATE + v > 99 - r50c) - return VALUE_MATED_IN_MAX_PLY + 1; // do not return a potentially false mate score + // Downgrade a potentially false mate score. + if (v <= VALUE_MATED_IN_MAX_PLY && VALUE_MATE + v > 100 - r50c) + return VALUE_TB_LOSS_IN_MAX_PLY + 1; + + // Downgrade a potentially false TB score. + if (VALUE_TB + v > 100 - r50c) + return VALUE_TB_LOSS_IN_MAX_PLY + 1; return v + ply; } @@ -1866,7 +1881,7 @@ string UCI::pv(const Position& pos, Depth depth) { if (v == -VALUE_INFINITE) v = VALUE_ZERO; - bool tb = TB::RootInTB && abs(v) < VALUE_MATE_IN_MAX_PLY; + bool tb = TB::RootInTB && abs(v) <= VALUE_TB; v = tb ? rootMoves[i].tbScore : v; if (ss.rdbuf()->in_avail()) // Not at first line diff --git a/src/types.h b/src/types.h index 0575f1d453c..3e00d68d19d 100644 --- a/src/types.h +++ b/src/types.h @@ -164,14 +164,16 @@ enum Bound { enum Value : int { VALUE_ZERO = 0, VALUE_DRAW = 0, - VALUE_MATE = 32000, - VALUE_INFINITE = 32001, VALUE_NONE = 32002, + VALUE_INFINITE = 32001, + + VALUE_MATE = 32000, + VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY, + VALUE_MATED_IN_MAX_PLY = -VALUE_MATE_IN_MAX_PLY, - VALUE_TB_WIN_IN_MAX_PLY = VALUE_MATE - 2 * MAX_PLY, + VALUE_TB = VALUE_MATE_IN_MAX_PLY - 1, + VALUE_TB_WIN_IN_MAX_PLY = VALUE_TB - MAX_PLY, VALUE_TB_LOSS_IN_MAX_PLY = -VALUE_TB_WIN_IN_MAX_PLY, - VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY, - VALUE_MATED_IN_MAX_PLY = -VALUE_MATE_IN_MAX_PLY, // In the code, we make the assumption that these values // are such that non_pawn_material() can be used to uniquely diff --git a/src/uci.cpp b/src/uci.cpp index 95f6f349dd3..d0341bd7168 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -356,9 +356,9 @@ std::string UCI::value(Value v) { if (abs(v) < VALUE_TB_WIN_IN_MAX_PLY) ss << "cp " << UCI::to_cp(v); - else if (abs(v) < VALUE_MATE_IN_MAX_PLY) + else if (abs(v) <= VALUE_TB) { - const int ply = VALUE_MATE_IN_MAX_PLY - 1 - std::abs(v); // recompute ss->ply + const int ply = VALUE_TB - std::abs(v); // recompute ss->ply ss << "cp " << (v > 0 ? 20000 - ply : -20000 + ply); } else From 7885fa5bd3c8aae1e992ec80cbaaab1177502426 Mon Sep 17 00:00:00 2001 From: peregrineshahin Date: Mon, 4 Dec 2023 00:39:41 +0300 Subject: [PATCH 1282/1766] Track seldepth in qsearch too Sometimes if we count the reported PV length, it turns out to be longer than the selective depth reported. This fixes this behavior by applying the selective depth to qsearch since we do report PVs from it as well. Passed non-regression STC: https://tests.stockfishchess.org/tests/view/656cf5b66980e15f69c7499d LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 223648 W: 56372 L: 56356 D: 110920 Ptnml(0-2): 710, 25580, 59231, 25590, 713 closes https://github.com/official-stockfish/Stockfish/pull/4903 No functional change --- src/search.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 89879374c1a..10a36cbf261 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1421,6 +1421,10 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { ss->inCheck = pos.checkers(); moveCount = 0; + // Used to send selDepth info to GUI (selDepth counts from 1, ply from 0) + if (PvNode && thisThread->selDepth < ss->ply + 1) + thisThread->selDepth = ss->ply + 1; + // Step 2. Check for an immediate draw or maximum ply reached if (pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos) : VALUE_DRAW; From cdfafb3426cdf3a6c60fe2e20eb52c72d2777e51 Mon Sep 17 00:00:00 2001 From: WangXiang Date: Sun, 10 Dec 2023 23:04:32 +0800 Subject: [PATCH 1283/1766] Add loongarch64 support Adding support for LoongArch64 architecture. Tested on Loongson 3A6000 EVB Board. Since Loongson's SIMD extended instruction set ([LSX](https://gcc.gnu.org/onlinedocs/gcc/LoongArch-SX-Vector-Intrinsics.html), [LASX](https://gcc.gnu.org/onlinedocs/gcc/LoongArch-ASX-Vector-Intrinsics.html)) is already supported by GCC, more optimizations are being developed. Here's the benchmark result for Loongson 3A6000 (4c8t, 2.5Ghz) without SIMD optimizations. ``` Total time (ms) : 17903 Nodes searched : 1244386 Nodes/second : 69507 ``` closes https://github.com/official-stockfish/Stockfish/pull/4913 No functional change --- AUTHORS | 1 + src/Makefile | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index d7b64b62e8c..f6a10288476 100644 --- a/AUTHORS +++ b/AUTHORS @@ -227,6 +227,7 @@ Vince Negri (cuddlestmonkey) Viren windfishballad xefoci7612 +Xiang Wang (KatyushaScarlet) zz4032 # Additionally, we acknowledge the authors and maintainers of fishtest, diff --git a/src/Makefile b/src/Makefile index 59ea7bfe7b5..761b40869ee 100644 --- a/src/Makefile +++ b/src/Makefile @@ -125,7 +125,7 @@ ifeq ($(ARCH), $(filter $(ARCH), \ x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-avxvnni x86-64-bmi2 \ x86-64-avx2 x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \ x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-32 e2k \ - armv7 armv7-neon armv8 armv8-dotprod apple-silicon general-64 general-32 riscv64)) + armv7 armv7-neon armv8 armv8-dotprod apple-silicon general-64 general-32 riscv64 loongarch64)) SUPPORTED_ARCH=true else SUPPORTED_ARCH=false @@ -369,6 +369,10 @@ endif ifeq ($(ARCH),riscv64) arch = riscv64 endif + +ifeq ($(ARCH),loongarch64) + arch = loongarch64 +endif endif @@ -404,6 +408,8 @@ ifeq ($(COMP),gcc) ifeq ($(ARCH),riscv64) CXXFLAGS += -latomic endif + else ifeq ($(ARCH),loongarch64) + CXXFLAGS += -latomic else CXXFLAGS += -m$(bits) LDFLAGS += -m$(bits) @@ -474,6 +480,8 @@ ifeq ($(COMP),clang) ifeq ($(ARCH),riscv64) CXXFLAGS += -latomic endif + else ifeq ($(ARCH),loongarch64) + CXXFLAGS += -latomic else CXXFLAGS += -m$(bits) LDFLAGS += -m$(bits) @@ -823,6 +831,7 @@ help: @echo "general-64 > unspecified 64-bit" @echo "general-32 > unspecified 32-bit" @echo "riscv64 > RISC-V 64-bit" + @echo "loongarch64 > LoongArch 64-bit" @echo "" @echo "Supported compilers:" @echo "" @@ -1004,7 +1013,7 @@ config-sanity: net @test "$(SUPPORTED_ARCH)" = "true" @test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \ test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || test "$(arch)" = "e2k" || \ - test "$(arch)" = "armv7" || test "$(arch)" = "armv8" || test "$(arch)" = "arm64" || test "$(arch)" = "riscv64" + test "$(arch)" = "armv7" || test "$(arch)" = "armv8" || test "$(arch)" = "arm64" || test "$(arch)" = "riscv64" || test "$(arch)" = "loongarch64" @test "$(bits)" = "32" || test "$(bits)" = "64" @test "$(prefetch)" = "yes" || test "$(prefetch)" = "no" @test "$(popcnt)" = "yes" || test "$(popcnt)" = "no" From 9fc064e872e772f941e6fb5d303d827174003ce7 Mon Sep 17 00:00:00 2001 From: Disservin Date: Sun, 10 Dec 2023 23:40:45 +0100 Subject: [PATCH 1284/1766] Fix action deprecation warning for dev-drprasad closes https://github.com/official-stockfish/Stockfish/pull/4914 No functional change --- .github/workflows/stockfish.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index 7bbb53d5c62..e8db52351dd 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -30,8 +30,7 @@ jobs: if: env.COMMIT_SHA != 'null' with: tag_name: ${{ env.COMMIT_SHA }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + github_token: ${{ secrets.GITHUB_TOKEN }} Analyzers: uses: ./.github/workflows/stockfish_analyzers.yml From 536d692a3082cc86afcf1a48b0cf25ac73fa7074 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Tue, 12 Dec 2023 16:38:24 +0300 Subject: [PATCH 1285/1766] Remove SlowMover Option The SlowMover option allows users to modify the timeLeft variant, impacting the engine's time management. However, this feature, while theoretically flexible, doesn't offer substantial benefits. Instead, it introduces the risk of non-experienced users altering values without a clear understanding of the effects, potentially leading to a weaker engine. The vast majority of SF users don't use it anyway, and based on tests conducted by fauzi several months ago suggest that changing it would only lose Elo. Examples: https://tests.stockfishchess.org/tests/view/651f309bac57711436726bba https://tests.stockfishchess.org/tests/view/651fea29ac57711436727d85 https://tests.stockfishchess.org/tests/view/65257c343125598fc7eb68a1 https://tests.stockfishchess.org/tests/view/652296c83125598fc7eb2ad7 Tune: https://tests.stockfishchess.org/tests/view/652a70313125598fc7ebd706 (keeping the value at 100, zz2) closes https://github.com/official-stockfish/Stockfish/pull/4917 No functional change --- src/timeman.cpp | 5 ----- src/ucioption.cpp | 1 - 2 files changed, 6 deletions(-) diff --git a/src/timeman.cpp b/src/timeman.cpp index 1253d434672..9ff422fe966 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -42,7 +42,6 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { return; TimePoint moveOverhead = TimePoint(Options["Move Overhead"]); - TimePoint slowMover = TimePoint(Options["Slow Mover"]); TimePoint npmsec = TimePoint(Options["nodestime"]); // optScale is a percentage of available time to use for the current move. @@ -78,10 +77,6 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { double optConstant = std::min(0.00335 + 0.0003 * std::log10(limits.time[us] / 1000.0), 0.0048); double maxConstant = std::max(3.6 + 3.0 * std::log10(limits.time[us] / 1000.0), 2.7); - // A user may scale time usage by setting UCI option "Slow Mover" - // Default is 100 and changing this value will probably lose elo. - timeLeft = slowMover * timeLeft / 100; - // x basetime (+ z increment) // If there is a healthy increment, timeLeft can exceed actual available // game time for the current move, so also cap to 20% of available game time. diff --git a/src/ucioption.cpp b/src/ucioption.cpp index d0db1c76dd2..233602ca153 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -73,7 +73,6 @@ void init(OptionsMap& o) { o["MultiPV"] << Option(1, 1, 500); o["Skill Level"] << Option(20, 0, 20); o["Move Overhead"] << Option(10, 0, 5000); - o["Slow Mover"] << Option(100, 10, 1000); o["nodestime"] << Option(0, 0, 10000); o["UCI_Chess960"] << Option(false); o["UCI_AnalyseMode"] << Option(false); From c53d2ec253557d8a679197becb7ba9a6aa393ecc Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Wed, 13 Dec 2023 19:05:37 +0800 Subject: [PATCH 1286/1766] Remove UCI_AnalyseMode Option Simplify away the useless option, as documented: "An option handled by your GUI. This currently doesn't do anything." The option was originally added with the introduction of contempt (https://github.com/official-stockfish/Stockfish/commit/e9aeaad05266ca557a9496b5a17b4c5f82f0e946), but it is now no longer used. closes https://github.com/official-stockfish/Stockfish/pull/4918 No functional change --- src/ucioption.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 233602ca153..1dc9b89baed 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -75,7 +75,6 @@ void init(OptionsMap& o) { o["Move Overhead"] << Option(10, 0, 5000); 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(1320, 1320, 3190); o["UCI_ShowWDL"] << Option(false); From d9ec82e7438716671168d78eee26fae327249e8c Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Fri, 15 Dec 2023 01:51:52 +0300 Subject: [PATCH 1287/1766] Adjust stand pat in qsearch on pv nodes Instead of immediately returning a fail high do this only at non-pv nodes, for pv nodes adjust bestValue to value between alpha and beta and continue searching. Idea is to do it the same way as it's done in search where we don't return positive beta cutoffs after ttHits / zero window search at PvNodes and instead fully search lines. Passed STC: https://tests.stockfishchess.org/tests/view/65739b0af09ce1261f122f33 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 189216 W: 48142 L: 47598 D: 93476 Ptnml(0-2): 584, 22463, 48051, 22845, 665 Passed LTC: https://tests.stockfishchess.org/tests/view/657701214d789acf40aac194 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 82506 W: 20689 L: 20269 D: 41548 Ptnml(0-2): 56, 9236, 22268, 9618, 75 Two issues had to be resolved: - in rare cases it set alpha to the same value as beta and thus broke some asserts; - messed up with returning tb win values. Fix passed non-regression LTC vs this patch: https://tests.stockfishchess.org/tests/view/6578113b4d789acf40aad544 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 277308 W: 68839 L: 68880 D: 139589 Ptnml(0-2): 167, 31580, 75212, 31517, 178 closes https://github.com/official-stockfish/Stockfish/pull/4922 Bench: 1069503 Co-Authored-By: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Co-Authored-By: Shahin M. Shahin <41402573+peregrineshahin@users.noreply.github.com> Co-Authored-By: fffelix-huang <72808219+fffelix-huang@users.noreply.github.com> --- src/search.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 10a36cbf261..27c2c84e9dd 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1468,14 +1468,19 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { ss->staticEval = bestValue = (ss - 1)->currentMove != MOVE_NULL ? evaluate(pos) : -(ss - 1)->staticEval; - // Stand pat. Return immediately if static value is at least beta + // Stand pat. Return immediately if bestValue is at least beta at non-Pv nodes. + // At PvNodes set bestValue between alpha and beta instead if (bestValue >= beta) { - if (!ss->ttHit) - tte->save(posKey, value_to_tt(bestValue, ss->ply), false, BOUND_LOWER, DEPTH_NONE, - MOVE_NONE, ss->staticEval); + if (!PvNode || abs(bestValue) >= VALUE_TB_WIN_IN_MAX_PLY) + { + if (!ss->ttHit) + tte->save(posKey, value_to_tt(bestValue, ss->ply), false, BOUND_LOWER, + DEPTH_NONE, MOVE_NONE, ss->staticEval); - return bestValue; + return bestValue; + } + bestValue = std::min((alpha + beta) / 2, beta - 1); } if (bestValue > alpha) From 07a2619b62a25910a32ad8a4e9912f748338580f Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Fri, 15 Dec 2023 14:29:44 +0300 Subject: [PATCH 1288/1766] Improvement of Time Management Parameters Passed STC: https://tests.stockfishchess.org/tests/view/6579c5574d789acf40aaf914 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 44672 W: 11354 L: 11030 D: 22288 Ptnml(0-2): 140, 5033, 11685, 5319, 159 Passed LTC: https://tests.stockfishchess.org/tests/view/657ad7f44d789acf40ab105e LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 40932 W: 10275 L: 9950 D: 20707 Ptnml(0-2): 21, 4316, 11473, 4629, 27 Passed non-regression Sudden death 10+0: https://tests.stockfishchess.org/tests/view/657b9b9e393ac02e7911f1a8 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 21384 W: 5171 L: 4925 D: 11288 Ptnml(0-2): 112, 2420, 5409, 2612, 139 closes https://github.com/official-stockfish/Stockfish/pull/4923 No functional change --- src/search.cpp | 6 +++--- src/timeman.cpp | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 27c2c84e9dd..ce71c7883ca 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -471,12 +471,12 @@ void Thread::search() { { double fallingEval = (66 + 14 * (mainThread->bestPreviousAverageScore - bestValue) + 6 * (mainThread->iterValue[iterIdx] - bestValue)) - / 583.0; - fallingEval = std::clamp(fallingEval, 0.5, 1.5); + / 616.6; + fallingEval = std::clamp(fallingEval, 0.51, 1.51); // If the bestMove is stable over several iterations, reduce time accordingly timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.56 : 0.69; - double reduction = (1.4 + mainThread->previousTimeReduction) / (2.03 * timeReduction); + double reduction = (1.4 + mainThread->previousTimeReduction) / (2.17 * timeReduction); double bestMoveInstability = 1 + 1.79 * totBestMoveChanges / Threads.size(); double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability; diff --git a/src/timeman.cpp b/src/timeman.cpp index 9ff422fe966..f404ee0c353 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -71,21 +71,21 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { - moveOverhead * (2 + mtg)); // Use extra time with larger increments - double optExtra = std::clamp(1.0 + 12.5 * limits.inc[us] / limits.time[us], 1.0, 1.12); + double optExtra = std::clamp(1.0 + 12.5 * limits.inc[us] / limits.time[us], 1.0, 1.11); // Calculate time constants based on current time left. - double optConstant = std::min(0.00335 + 0.0003 * std::log10(limits.time[us] / 1000.0), 0.0048); - double maxConstant = std::max(3.6 + 3.0 * std::log10(limits.time[us] / 1000.0), 2.7); + double optConstant = std::min(0.00334 + 0.0003 * std::log10(limits.time[us] / 1000.0), 0.0049); + double maxConstant = std::max(3.4 + 3.0 * std::log10(limits.time[us] / 1000.0), 2.76); // x basetime (+ z increment) // If there is a healthy increment, timeLeft can exceed actual available // game time for the current move, so also cap to 20% of available game time. if (limits.movestogo == 0) { - optScale = std::min(0.0120 + std::pow(ply + 3.3, 0.44) * optConstant, - 0.2 * limits.time[us] / double(timeLeft)) + optScale = std::min(0.0120 + std::pow(ply + 3.1, 0.44) * optConstant, + 0.21 * limits.time[us] / double(timeLeft)) * optExtra; - maxScale = std::min(6.8, maxConstant + ply / 12.2); + maxScale = std::min(6.9, maxConstant + ply / 12.2); } // x moves in y seconds (+ z increment) From a069a1bbbfb60abddbe3fe5276b06f35f783f41c Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Mon, 18 Dec 2023 16:20:41 +0300 Subject: [PATCH 1289/1766] Use std::abs over abs closes https://github.com/official-stockfish/Stockfish/pull/4926 closes https://github.com/official-stockfish/Stockfish/pull/4909 No functional change Co-Authored-By: fffelix-huang <72808219+fffelix-huang@users.noreply.github.com> --- AUTHORS | 1 + src/evaluate.cpp | 11 ++++++----- src/movepick.h | 7 ++++--- src/nnue/evaluate_nnue.cpp | 2 +- src/search.cpp | 8 ++++---- src/thread.cpp | 3 ++- src/uci.cpp | 4 ++-- 7 files changed, 20 insertions(+), 16 deletions(-) diff --git a/AUTHORS b/AUTHORS index f6a10288476..cedee2f3536 100644 --- a/AUTHORS +++ b/AUTHORS @@ -214,6 +214,7 @@ Taras Vuk (TarasVuk) Thanar2 thaspel theo77186 +Ting-Hsuan Huang (fffelix-huang) Tomasz Sobczyk (Sopel97) Tom Truscott Tom Vijlbrief (tomtor) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 9c39d4c07fb..586cadc0ec5 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -164,9 +165,9 @@ Value Eval::evaluate(const Position& pos) { int shuffling = pos.rule50_count(); int simpleEval = simple_eval(pos, stm) + (int(pos.key() & 7) - 3); - bool lazy = abs(simpleEval) >= RookValue + KnightValue + 16 * shuffling * shuffling - + abs(pos.this_thread()->bestValue) - + abs(pos.this_thread()->rootSimpleEval); + bool lazy = std::abs(simpleEval) >= RookValue + KnightValue + 16 * shuffling * shuffling + + std::abs(pos.this_thread()->bestValue) + + std::abs(pos.this_thread()->rootSimpleEval); if (lazy) v = Value(simpleEval); @@ -178,8 +179,8 @@ Value Eval::evaluate(const Position& pos) { Value optimism = pos.this_thread()->optimism[stm]; // Blend optimism and eval with nnue complexity and material imbalance - optimism += optimism * (nnueComplexity + abs(simpleEval - nnue)) / 512; - nnue -= nnue * (nnueComplexity + abs(simpleEval - nnue)) / 32768; + optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / 512; + nnue -= nnue * (nnueComplexity + std::abs(simpleEval - nnue)) / 32768; int npm = pos.non_pawn_material() / 64; v = (nnue * (915 + npm + 9 * pos.count()) + optimism * (154 + npm)) / 1024; diff --git a/src/movepick.h b/src/movepick.h index 7828fa19d97..5077f4e3b72 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -55,12 +56,12 @@ class StatsEntry { operator const T&() const { return entry; } void operator<<(int bonus) { - assert(abs(bonus) <= D); // Ensure range is [-D, D] + assert(std::abs(bonus) <= D); // Ensure range is [-D, D] static_assert(D <= std::numeric_limits::max(), "D overflows T"); - entry += bonus - entry * abs(bonus) / D; + entry += bonus - entry * std::abs(bonus) / D; - assert(abs(entry) <= D); + assert(std::abs(entry) <= D); } }; diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index ef6b7e91a60..e7339c10ae9 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -180,7 +180,7 @@ Value evaluate(const Position& pos, bool adjusted, int* complexity) { const auto positional = network[bucket]->propagate(transformedFeatures); if (complexity) - *complexity = abs(psqt - positional) / OutputScale; + *complexity = std::abs(psqt - positional) / OutputScale; // Give more value to positional evaluation when adjusted flag is set if (adjusted) diff --git a/src/search.cpp b/src/search.cpp index ce71c7883ca..bd3da5a23b1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -847,7 +847,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // much above beta, we can (almost) safely prune the previous move. if ( !PvNode && depth > 3 - && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY + && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY // If value from transposition table is lower than probCutBeta, don't attempt probCut // there and in further interactions with transposition table cutoff depth is set to depth - 3 // because probCut search has depth set to depth - 4 but we also do a move before it @@ -901,7 +901,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo probCutBeta = beta + 425; if (ss->inCheck && !PvNode && ttCapture && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 4 && ttValue >= probCutBeta - && abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) + && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) return probCutBeta; const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, @@ -1042,7 +1042,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Recursive singular search is avoided. if (!rootNode && move == ttMove && !excludedMove && depth >= 4 - (thisThread->completedDepth > 27) + 2 * (PvNode && tte->is_pv()) - && abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && (tte->bound() & BOUND_LOWER) + && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { Value singularBeta = ttValue - (66 + 58 * (ss->ttPv && !PvNode)) * depth / 64; @@ -1890,7 +1890,7 @@ string UCI::pv(const Position& pos, Depth depth) { if (v == -VALUE_INFINITE) v = VALUE_ZERO; - bool tb = TB::RootInTB && abs(v) <= VALUE_TB; + bool tb = TB::RootInTB && std::abs(v) <= VALUE_TB; v = tb ? rootMoves[i].tbScore : v; if (ss.rdbuf()->in_avail()) // Not at first line diff --git a/src/thread.cpp b/src/thread.cpp index bc884dedf01..de8de87d8a2 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -235,7 +236,7 @@ Thread* ThreadPool::get_best_thread() const { votes[th->rootMoves[0].pv[0]] += thread_value(th); for (Thread* th : threads) - if (abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY) + if (std::abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY) { // Make sure we pick the shortest mate / TB conversion or stave off mate the longest if (th->rootMoves[0].score > bestThread->rootMoves[0].score) diff --git a/src/uci.cpp b/src/uci.cpp index d0341bd7168..5f250a3617a 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -354,9 +354,9 @@ std::string UCI::value(Value v) { std::stringstream ss; - if (abs(v) < VALUE_TB_WIN_IN_MAX_PLY) + if (std::abs(v) < VALUE_TB_WIN_IN_MAX_PLY) ss << "cp " << UCI::to_cp(v); - else if (abs(v) <= VALUE_TB) + else if (std::abs(v) <= VALUE_TB) { const int ply = VALUE_TB - std::abs(v); // recompute ss->ply ss << "cp " << (v > 0 ? 20000 - ply : -20000 + ply); From 9be0360aa414556f231873ce2348f9c1f00d1713 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Mon, 18 Dec 2023 20:34:35 +0300 Subject: [PATCH 1290/1766] Adjust return value in qsearch after fail high Instead of returning strict fail soft fail high return value between value from search and beta (somewhat by analogy to futility pruning and probcut). This seems to be somewhat depth sensitive heuristic which performed much worse at LTC while performing much better at STC if it is more aggressive, passed version is the least aggressive one. Passed STC: https://tests.stockfishchess.org/tests/view/657b06414d789acf40ab1475 LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 212352 W: 53900 L: 53315 D: 105137 Ptnml(0-2): 809, 25236, 53520, 25783, 828 Passed LTC: https://tests.stockfishchess.org/tests/view/657ce36f393ac02e79120a7c LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 319362 W: 79541 L: 78630 D: 161191 Ptnml(0-2): 202, 35839, 86709, 36708, 223 closes https://github.com/official-stockfish/Stockfish/pull/4928 Bench: 974739 --- src/search.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index bd3da5a23b1..fad43b624dc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1618,6 +1618,9 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { return mated_in(ss->ply); // Plies to mate from the root } + if (abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY) + bestValue = bestValue >= beta ? (3 * bestValue + beta) / 4 : bestValue; + // Save gathered info in transposition table tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit, bestValue >= beta ? BOUND_LOWER : BOUND_UPPER, ttDepth, bestMove, ss->staticEval); From 358a85379094cbffa9d80b443ba63f3066c4cd33 Mon Sep 17 00:00:00 2001 From: Disservin Date: Fri, 22 Dec 2023 11:46:28 +0100 Subject: [PATCH 1291/1766] Revert "Adjust stand pat in qsearch on pv nodes" This reverts commit d9ec82e7438716671168d78eee26fae327249e8c. Bench: 1249544 --- src/search.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index fad43b624dc..235b35c19d8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1468,19 +1468,14 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { ss->staticEval = bestValue = (ss - 1)->currentMove != MOVE_NULL ? evaluate(pos) : -(ss - 1)->staticEval; - // Stand pat. Return immediately if bestValue is at least beta at non-Pv nodes. - // At PvNodes set bestValue between alpha and beta instead + // Stand pat. Return immediately if static value is at least beta if (bestValue >= beta) { - if (!PvNode || abs(bestValue) >= VALUE_TB_WIN_IN_MAX_PLY) - { - if (!ss->ttHit) - tte->save(posKey, value_to_tt(bestValue, ss->ply), false, BOUND_LOWER, - DEPTH_NONE, MOVE_NONE, ss->staticEval); + if (!ss->ttHit) + tte->save(posKey, value_to_tt(bestValue, ss->ply), false, BOUND_LOWER, DEPTH_NONE, + MOVE_NONE, ss->staticEval); - return bestValue; - } - bestValue = std::min((alpha + beta) / 2, beta - 1); + return bestValue; } if (bestValue > alpha) From fbdf5d94a9a42acb92720a5896b16c92931ec3de Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Wed, 20 Dec 2023 14:26:11 +0300 Subject: [PATCH 1292/1766] Tweak quiet move bonus Improving quiet move bonus by replacing bestvalue and alpha comparison, with checking the statScore of the previous search step instead. Inspired by @locutus2 Passed STC: https://tests.stockfishchess.org/tests/view/657f22fb893104ee25b614e8 LLR: 2.96 (-2.94,2.94) <0.00,2.00> Total: 51296 W: 13121 L: 12774 D: 25401 Ptnml(0-2): 225, 5986, 12868, 6355, 214 Passed LTC: https://tests.stockfishchess.org/tests/view/658024a2893104ee25b62587 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 82758 W: 20606 L: 20189 D: 41963 Ptnml(0-2): 51, 9149, 22555, 9580, 44 closes https://github.com/official-stockfish/Stockfish/pull/4930 Bench: 1312822 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 235b35c19d8..3c61ea2f395 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -748,7 +748,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Use static evaluation difference to improve quiet move ordering (~4 Elo) if (is_ok((ss - 1)->currentMove) && !(ss - 1)->inCheck && !priorCapture) { - int bonus = std::clamp(-13 * int((ss - 1)->staticEval + ss->staticEval), -1555, 1452); + int bonus = std::clamp(-13 * int((ss - 1)->staticEval + ss->staticEval), -1652, 1546); thisThread->mainHistory[~us][from_to((ss - 1)->currentMove)] << bonus; if (type_of(pos.piece_on(prevSq)) != PAWN && type_of((ss - 1)->currentMove) != PROMOTION) thisThread->pawnHistory[pawn_structure(pos)][pos.piece_on(prevSq)][prevSq] << bonus / 4; @@ -1344,7 +1344,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (depth > 6) + (PvNode || cutNode) + (bestValue < alpha - 656) + int bonus = (depth > 6) + (PvNode || cutNode) + ((ss - 1)->statScore < -18782) + ((ss - 1)->moveCount > 10); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); From 3f5adc037e14e40d1e0e1380e3e7c5884ca528ed Mon Sep 17 00:00:00 2001 From: peregrineshahin Date: Thu, 21 Dec 2023 07:44:32 +0300 Subject: [PATCH 1293/1766] Fix wrong mate/tb scores from probCut This fixes returning wrong mated-in scores, or losing a proven mate-in score from probCut after recent tweaks. The issue reported by @cj5716 on discord. Passed non-reg STC: https://tests.stockfishchess.org/tests/view/6583c36b5457644dc9843afe LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 295936 W: 75011 L: 75075 D: 145850 Ptnml(0-2): 978, 33947, 78146, 33955, 942 Passed non-reg LTC: https://tests.stockfishchess.org/tests/view/658513075457644dc98451cd LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 55932 W: 13970 L: 13786 D: 28176 Ptnml(0-2): 33, 5933, 15837, 6143, 20 closes https://github.com/official-stockfish/Stockfish/pull/4933 Bench: 1308739 --- src/search.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 3c61ea2f395..25fc30ba191 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -854,7 +854,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // So effective depth is equal to depth - 3 && !(tte->depth() >= depth - 3 && ttValue != VALUE_NONE && ttValue < probCutBeta)) { - assert(probCutBeta < VALUE_INFINITE); + assert(probCutBeta < VALUE_INFINITE && probCutBeta > beta); MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, &captureHistory); @@ -888,7 +888,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Save ProbCut data into transposition table tte->save(posKey, value_to_tt(value, ss->ply), ss->ttPv, BOUND_LOWER, depth - 3, move, ss->staticEval); - return value - (probCutBeta - beta); + return std::abs(value) < VALUE_TB_WIN_IN_MAX_PLY ? value - (probCutBeta - beta) + : value; } } @@ -1613,7 +1614,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { return mated_in(ss->ply); // Plies to mate from the root } - if (abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY) + if (std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY) bestValue = bestValue >= beta ? (3 * bestValue + beta) / 4 : bestValue; // Save gathered info in transposition table From f388e4180950833a1f79e023c88ff2521c9583b2 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 24 Dec 2023 20:36:52 +0300 Subject: [PATCH 1294/1766] Adjust value returned after TT cutoff Instead of returning value from TT in case of a fail high return mix between it and beta. Passed STC: https://tests.stockfishchess.org/tests/view/658465395457644dc98446c7 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 220704 W: 56404 L: 55811 D: 108489 Ptnml(0-2): 750, 26214, 55921, 26627, 840 Passed LTC: https://tests.stockfishchess.org/tests/view/6585c3f55457644dc9845db9 LLR: 2.97 (-2.94,2.94) <0.50,2.50> Total: 124980 W: 31169 L: 30658 D: 63153 Ptnml(0-2): 57, 14147, 33603, 14594, 89 closes https://github.com/official-stockfish/Stockfish/pull/4934 Bench: 1191093 --- src/search.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 25fc30ba191..bc196ec4f50 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -653,7 +653,9 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Partial workaround for the graph history interaction problem // For high rule50 counts don't produce transposition table cutoffs. if (pos.rule50_count() < 90) - return ttValue; + return ttValue >= beta && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY + ? (ttValue * 3 + beta) / 4 + : ttValue; } // Step 5. Tablebases probe From bab1cc300cbf1929fe42bdbce786a22dd97c8e1b Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Tue, 26 Dec 2023 20:27:45 +0300 Subject: [PATCH 1295/1766] Refactor bestvalue adjustment in qsearch closes https://github.com/official-stockfish/Stockfish/pull/4935 No functional change --- AUTHORS | 2 +- src/search.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index cedee2f3536..28586eec974 100644 --- a/AUTHORS +++ b/AUTHORS @@ -72,7 +72,7 @@ Fabian Beuke (madnight) Fabian Fichter (ianfab) Fanael Linithien (Fanael) fanon -Fauzi Akram Dabat (FauziAkram) +Fauzi Akram Dabat (fauzi2) Felix Wittmann gamander Gabriele Lombardo (gabe) diff --git a/src/search.cpp b/src/search.cpp index bc196ec4f50..7709af35a02 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1616,8 +1616,8 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { return mated_in(ss->ply); // Plies to mate from the root } - if (std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY) - bestValue = bestValue >= beta ? (3 * bestValue + beta) / 4 : bestValue; + if (std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY && bestValue >= beta) + bestValue = (3 * bestValue + beta) / 4; // Save gathered info in transposition table tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit, From f12035c88c58a5fd568d26cde9868f73a8d7b839 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Mon, 25 Dec 2023 10:14:15 -0500 Subject: [PATCH 1296/1766] Update default net to nn-b1e55edbea57.nnue Created by retraining the master big net `nn-0000000000a0.nnue` on the same dataset with the ranger21 optimizer and more WDL skipping at training time. More WDL skipping is meant to increase lambda accuracy and train on fewer misevaluated positions where position scores are unlikely to correlate with game outcomes. Inspired by: - repeated reports in discord #events-discuss about SF misplaying due to wrong endgame evals, possibly due to Leela's endgame weaknesses reflected in training data - an attempt to reduce the skewed dataset piece count distribution where there are much more positions with less than 16 pieces, since the target piece count distribution in the trainer is symmetric around 16 The faster convergence seen with ranger21 is meant to: - prune experiment ideas more quickly since fewer epochs are needed to reach elo maxima - research faster potential trainings by shortening each run ```yaml experiment-name: 2560-S7-Re-514G-ranger21-more-wdl-skip training-dataset: /data/S6-514G.binpack early-fen-skipping: 28 start-from-engine-test-net: True nnue-pytorch-branch: linrock/nnue-pytorch/r21-more-wdl-skip num-epochs: 1200 lr: 4.375e-4 gamma: 0.995 start-lambda: 1.0 end-lambda: 0.7 ``` Experiment yaml configs converted to easy_train.sh commands with: https://github.com/linrock/nnue-tools/blob/4339954/yaml_easy_train.py Implementations based off of Sopel's NNUE training & experimentation log: https://docs.google.com/document/d/1gTlrr02qSNKiXNZ_SuO4-RjK4MXBiFlLE6jvNqqMkAY - Experiment 336 - ranger21 https://github.com/Sopel97/nnue-pytorch/tree/experiment_336 - Experiment 351 - more WDL skipping The version of the ranger21 optimizer used is: https://github.com/lessw2020/Ranger21/blob/b507df6/ranger21/ranger21.py The dataset is the exact same as in: https://github.com/official-stockfish/Stockfish/pull/4782 Local elo at 25k nodes per move: nn-epoch619.nnue : 6.2 +/- 4.2 Passed STC: https://tests.stockfishchess.org/tests/view/658a029779aa8af82b94fbe6 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 46528 W: 11985 L: 11650 D: 22893 Ptnml(0-2): 154, 5489, 11688, 5734, 199 Passed LTC: https://tests.stockfishchess.org/tests/view/658a448979aa8af82b95010f LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 265326 W: 66378 L: 65574 D: 133374 Ptnml(0-2): 153, 30175, 71254, 30877, 204 This was additionally tested with the latest DualNNUE and passed SPRTs: Passed STC vs. https://github.com/official-stockfish/Stockfish/pull/4919 https://tests.stockfishchess.org/tests/view/658bcd5c79aa8af82b951846 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 296128 W: 76273 L: 75554 D: 144301 Ptnml(0-2): 1223, 35768, 73617, 35979, 1477 Passed LTC vs. https://github.com/official-stockfish/Stockfish/pull/4919 https://tests.stockfishchess.org/tests/view/658c988d79aa8af82b95240f LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 75618 W: 19085 L: 18680 D: 37853 Ptnml(0-2): 45, 8420, 20497, 8779, 68 closes https://github.com/official-stockfish/Stockfish/pull/4942 Bench: 1304666 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 2ab477eced2..33df13089fb 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ extern std::string currentEvalFileName; // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. -#define EvalFileDefaultName "nn-0000000000a0.nnue" +#define EvalFileDefaultName "nn-b1e55edbea57.nnue" namespace NNUE { From 1a69efbb404fd4389651ab9f45127fb012c0cf94 Mon Sep 17 00:00:00 2001 From: "Shahin M. Shahin" <41402573+peregrineshahin@users.noreply.github.com> Date: Sat, 30 Dec 2023 00:28:13 +0300 Subject: [PATCH 1297/1766] Fix scores from reverse futility pruning This fixes futility pruning return values after recent tweaks, `eval` is guaranteed to be less than the mate-in range but it can be as low value such that the average between eval and beta can still fall in the mated-in range when beta is as low in mated range. i.e. (eval + beta) / 2 being at mated-range which can break mates. Passed non-regression STC: https://tests.stockfishchess.org/tests/view/658f3eed79aa8af82b955139 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 117408 W: 29891 L: 29761 D: 57756 Ptnml(0-2): 386, 13355, 31120, 13429, 414 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/658f8b7a79aa8af82b9557bd LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 60240 W: 14962 L: 14786 D: 30492 Ptnml(0-2): 22, 6257, 17390, 6425, 26 changes signature at higher depth e.g. `128 1 15` closes https://github.com/official-stockfish/Stockfish/pull/4944 Bench: 1304666 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 7709af35a02..4e12a6c925a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -784,7 +784,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo >= beta && eval >= beta && eval < 29008 // smaller than TB wins && (!ttMove || ttCapture)) - return (eval + beta) / 2; + return beta > VALUE_TB_LOSS_IN_MAX_PLY ? (eval + beta) / 2 : eval; // Step 9. Null move search with verification search (~35 Elo) if (!PvNode && (ss - 1)->currentMove != MOVE_NULL && (ss - 1)->statScore < 17496 && eval >= beta From 4f99dfcae2dd8e9a4b163ade623084888655ed46 Mon Sep 17 00:00:00 2001 From: Tobias Steinmann Date: Mon, 18 Dec 2023 15:07:14 +0100 Subject: [PATCH 1298/1766] Update Makefile for android x86-64 builds For developing an Android GUI it can be helpful to use the Emulator on Windows. Therefor an android_x86-64 library of Stockfish is needed. It would be nice to compile it "out-of-the-box". This change is originally suggested by Craftyawesome closes https://github.com/official-stockfish/Stockfish/pull/4927 No functional change --- AUTHORS | 1 + src/Makefile | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/AUTHORS b/AUTHORS index 28586eec974..6f518ec24a8 100644 --- a/AUTHORS +++ b/AUTHORS @@ -215,6 +215,7 @@ Thanar2 thaspel theo77186 Ting-Hsuan Huang (fffelix-huang) +Tobias Steinmann Tomasz Sobczyk (Sopel97) Tom Truscott Tom Vijlbrief (tomtor) diff --git a/src/Makefile b/src/Makefile index 761b40869ee..ac354c7b99c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -521,6 +521,14 @@ ifeq ($(COMP),ndk) STRIP=llvm-strip endif endif + ifeq ($(arch),x86_64) + CXX=x86_64-linux-android21-clang++ + ifneq ($(shell which x86_64-linux-android-strip 2>/dev/null),) + STRIP=x86_64-linux-android-strip + else + STRIP=llvm-strip + endif + endif LDFLAGS += -static-libstdc++ -pie -lm -latomic endif From 833a2e2bc09e3640440766683043134d72bffd51 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sat, 30 Dec 2023 15:22:17 +0300 Subject: [PATCH 1299/1766] Cleanup comments Tests used to derive some Elo worth comments: https://tests.stockfishchess.org/tests/view/656a7f4e136acbc573555a31 https://tests.stockfishchess.org/tests/view/6585fb455457644dc984620f closes https://github.com/official-stockfish/Stockfish/pull/4945 No functional change --- .github/ISSUE_TEMPLATE/config.yml | 2 +- .github/workflows/codeql.yml | 2 +- .github/workflows/stockfish_binaries.yml | 6 +++--- src/incbin/incbin.h | 10 +++++----- src/nnue/features/half_ka_v2_hm.h | 14 +++++++------- src/nnue/layers/affine_transform_sparse_input.h | 2 +- src/nnue/layers/sqr_clipped_relu.h | 2 +- src/nnue/nnue_common.h | 16 ++++++++-------- src/nnue/nnue_feature_transformer.h | 6 +++--- src/search.cpp | 8 ++++---- tests/instrumented.sh | 6 +++--- 11 files changed, 37 insertions(+), 37 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 1f8694d2e6f..0666eb32fb0 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -2,7 +2,7 @@ blank_issues_enabled: false contact_links: - name: Discord server url: https://discord.gg/GWDRS3kU6R - about: Feel free to ask for support or have a chat with us in our Discord server! + about: Feel free to ask for support or have a chat with us on our Discord server! - name: Discussions, Q&A, ideas, show us something... url: https://github.com/official-stockfish/Stockfish/discussions/new about: Do you have an idea for Stockfish? Do you want to show something that you made? Please open a discussion about it! diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 054be90040c..d6da8a1c288 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -23,7 +23,7 @@ jobs: matrix: language: [ 'cpp' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] - # Use only 'java' to analyze code written in Java, Kotlin or both + # Use only 'java' to analyze code written in Java, Kotlin, or both # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support diff --git a/.github/workflows/stockfish_binaries.yml b/.github/workflows/stockfish_binaries.yml index 5b3a522625c..eff2c2c9471 100644 --- a/.github/workflows/stockfish_binaries.yml +++ b/.github/workflows/stockfish_binaries.yml @@ -172,8 +172,8 @@ jobs: name: stockfish-${{ matrix.config.os }}-${{ matrix.binaries }} path: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.tar - # Artifacts automatically get zipped - # to avoid double zipping, we use the unzipped directory + # Artifacts automatically get zipped. + # To avoid double-zipping, we use the unzipped directory - name: Upload binaries if: runner.os == 'Windows' uses: actions/upload-artifact@v3 @@ -195,7 +195,7 @@ jobs: id: commit_date run: echo "COMMIT_DATE=$(git show -s --date=format:'%Y%m%d' --format=%cd HEAD)" >> $GITHUB_ENV - # Make sure that an old ci which still runs on master doesn't recreate a prerelease + # Make sure that an old ci that still runs on master doesn't recreate a prerelease - name: Check Pullable Commits id: check_commits run: | diff --git a/src/incbin/incbin.h b/src/incbin/incbin.h index c19684d7242..18718b95fae 100644 --- a/src/incbin/incbin.h +++ b/src/incbin/incbin.h @@ -3,8 +3,8 @@ * @author Dale Weiler * @brief Utility for including binary files * - * Facilities for including binary files into the current translation unit and - * making use from them externally in other translation units. + * Facilities for including binary files into the current translation unit + * and making use of them externally in other translation units. */ #ifndef INCBIN_HDR #define INCBIN_HDR @@ -139,7 +139,7 @@ #endif #if defined(__APPLE__) -/* The directives are different for Apple branded compilers */ +/* The directives are different for Apple-branded compilers */ # define INCBIN_SECTION INCBIN_OUTPUT_SECTION "\n" # define INCBIN_GLOBAL(NAME) ".globl " INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n" # define INCBIN_INT ".long " @@ -261,8 +261,8 @@ INCBIN_STRINGIZE( \ INCBIN_STYLE_IDENT(TYPE)) \ -/* Generate the global labels by indirectly invoking the macro with our style - * type and concatenating the name against them. */ +/* Generate the global labels by indirectly invoking the macro + * with our style type and concatenate the name against them. */ #define INCBIN_GLOBAL_LABELS(NAME, TYPE) \ INCBIN_INVOKE( \ INCBIN_GLOBAL, \ diff --git a/src/nnue/features/half_ka_v2_hm.h b/src/nnue/features/half_ka_v2_hm.h index 540ff895a5a..c208e38dbad 100644 --- a/src/nnue/features/half_ka_v2_hm.h +++ b/src/nnue/features/half_ka_v2_hm.h @@ -34,11 +34,11 @@ class Position; namespace Stockfish::Eval::NNUE::Features { -// Feature HalfKAv2_hm: Combination of the position of own king -// and the position of pieces. Position mirrored such that king always on e..h files. +// Feature HalfKAv2_hm: Combination of the position of own king and the +// position of pieces. Position mirrored such that king is always on e..h files. class HalfKAv2_hm { - // unique number for each piece type on each square + // Unique number for each piece type on each square enum { PS_NONE = 0, PS_W_PAWN = 0, @@ -56,8 +56,8 @@ class HalfKAv2_hm { }; static constexpr IndexType PieceSquareIndex[COLOR_NB][PIECE_NB] = { - // convention: W - us, B - them - // viewed from other side, W and B are reversed + // Convention: W - us, B - them + // Viewed from other side, W and B are reversed {PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE, PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE}, {PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE, @@ -140,8 +140,8 @@ class HalfKAv2_hm { static int update_cost(const StateInfo* st); static int refresh_cost(const Position& pos); - // Returns whether the change stored in this StateInfo means that - // a full accumulator refresh is required. + // Returns whether the change stored in this StateInfo means + // that a full accumulator refresh is required. static bool requires_refresh(const StateInfo* st, Color perspective); }; diff --git a/src/nnue/layers/affine_transform_sparse_input.h b/src/nnue/layers/affine_transform_sparse_input.h index 6cb4d1a9347..70dbd790469 100644 --- a/src/nnue/layers/affine_transform_sparse_input.h +++ b/src/nnue/layers/affine_transform_sparse_input.h @@ -235,7 +235,7 @@ class AffineTransformSparseInput { const auto input32 = reinterpret_cast(input); - // Find indices of nonzero 32bit blocks + // Find indices of nonzero 32-bit blocks find_nnz(input32, nnz, count); const outvec_t* biasvec = reinterpret_cast(biases); diff --git a/src/nnue/layers/sqr_clipped_relu.h b/src/nnue/layers/sqr_clipped_relu.h index f8e2d497ac0..b9d8f030a24 100644 --- a/src/nnue/layers/sqr_clipped_relu.h +++ b/src/nnue/layers/sqr_clipped_relu.h @@ -91,7 +91,7 @@ class SqrClippedReLU { for (IndexType i = Start; i < InputDimensions; ++i) { output[i] = static_cast( - // Really should be /127 but we need to make it fast so we right shift + // Really should be /127 but we need to make it fast so we right-shift // by an extra 7 bits instead. Needs to be accounted for in the trainer. std::min(127ll, ((long long) (input[i]) * input[i]) >> (2 * WeightScaleBits + 7))); } diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index f9cd7fbb597..d4bd0028969 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -112,7 +112,7 @@ inline IntType read_little_endian(std::istream& stream) { // Utility to write an integer (signed or unsigned, any size) // to a stream in little-endian order. We swap the byte order before the write if -// necessary to always write in little endian order, independently of the byte +// necessary to always write in little-endian order, independently of the byte // ordering of the compiling machine. template inline void write_little_endian(std::ostream& stream, IntType value) { @@ -141,8 +141,8 @@ inline void write_little_endian(std::ostream& stream, IntType value) { } -// Read integers in bulk from a little indian stream. -// This reads N integers from stream s and put them in array out. +// Read integers in bulk from a little-endian stream. +// This reads N integers from stream s and puts them in array out. template inline void read_little_endian(std::istream& stream, IntType* out, std::size_t count) { if (IsLittleEndian) @@ -153,7 +153,7 @@ inline void read_little_endian(std::istream& stream, IntType* out, std::size_t c } -// Write integers in bulk to a little indian stream. +// Write integers in bulk to a little-endian stream. // This takes N integers from array values and writes them on stream s. template inline void write_little_endian(std::ostream& stream, const IntType* values, std::size_t count) { @@ -165,8 +165,8 @@ inline void write_little_endian(std::ostream& stream, const IntType* values, std } -// Read N signed integers from the stream s, putting them in -// the array out. The stream is assumed to be compressed using the signed LEB128 format. +// Read N signed integers from the stream s, putting them in the array out. +// The stream is assumed to be compressed using the signed LEB128 format. // See https://en.wikipedia.org/wiki/LEB128 for a description of the compression scheme. template inline void read_leb_128(std::istream& stream, IntType* out, std::size_t count) { @@ -216,8 +216,8 @@ inline void read_leb_128(std::istream& stream, IntType* out, std::size_t count) // Write signed integers to a stream with LEB128 compression. -// This takes N integers from array values, compress them with the LEB128 algorithm and -// writes the result on the stream s. +// This takes N integers from array values, compresses them with +// the LEB128 algorithm and writes the result on the stream s. // See https://en.wikipedia.org/wiki/LEB128 for a description of the compression scheme. template inline void write_leb_128(std::ostream& stream, const IntType* values, std::size_t count) { diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 2af80f07792..a83a77c9d71 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -366,14 +366,14 @@ class FeatureTransformer { // The size must be enough to contain the largest possible update. // That might depend on the feature set and generally relies on the - // feature set's update cost calculation to be correct and never - // allow updates with more added/removed features than MaxActiveDimensions. + // feature set's update cost calculation to be correct and never allow + // updates with more added/removed features than MaxActiveDimensions. FeatureSet::IndexList removed[N - 1], added[N - 1]; { int i = N - - 2; // last potential state to update. Skip last element because it must be nullptr. + - 2; // Last potential state to update. Skip last element because it must be nullptr. while (states_to_update[i] == nullptr) --i; diff --git a/src/search.cpp b/src/search.cpp index 4e12a6c925a..eb63ec90762 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -747,7 +747,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); } - // Use static evaluation difference to improve quiet move ordering (~4 Elo) + // Use static evaluation difference to improve quiet move ordering (~9 Elo) if (is_ok((ss - 1)->currentMove) && !(ss - 1)->inCheck && !priorCapture) { int bonus = std::clamp(-13 * int((ss - 1)->staticEval + ss->staticEval), -1652, 1546); @@ -1201,6 +1201,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo if (newDepth > d) value = -search(pos, ss + 1, -(alpha + 1), -alpha, newDepth, !cutNode); + // Post LMR continuation history updates (~1 Elo) int bonus = value <= alpha ? -stat_malus(newDepth) : value >= beta ? stat_bonus(newDepth) : 0; @@ -1216,7 +1217,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo if (!ttMove) r += 2; - // Note that if expected reduction is high, we reduce search depth by 1 here + // Note that if expected reduction is high, we reduce search depth by 1 here (~9 Elo) value = -search(pos, ss + 1, -(alpha + 1), -alpha, newDepth - (r > 3), !cutNode); } @@ -1644,8 +1645,7 @@ Value value_to_tt(Value v, int ply) { // from the transposition table (which refers to the plies to mate/be mated from // current position) to "plies to mate/be mated (TB win/loss) from the root". // However, to avoid potentially false mate or TB scores related to the 50 moves rule -// and the graph history interaction, we return highest non-TB score instead. - +// and the graph history interaction, we return the highest non-TB score instead. Value value_from_tt(Value v, int ply, int r50c) { if (v == VALUE_NONE) diff --git a/tests/instrumented.sh b/tests/instrumented.sh index 637d19f9d63..2a3eadc074e 100755 --- a/tests/instrumented.sh +++ b/tests/instrumented.sh @@ -1,5 +1,5 @@ #!/bin/bash -# check for errors under valgrind or sanitizers. +# check for errors under Valgrind or sanitizers. error() { @@ -151,7 +151,7 @@ cat << EOF > game.exp send "quit\n" expect eof - # return error code of the spawned program, useful for valgrind + # return error code of the spawned program, useful for Valgrind lassign [wait] pid spawnid os_error_flag value exit \$value EOF @@ -179,7 +179,7 @@ cat << EOF > syzygy.exp send "quit\n" expect eof - # return error code of the spawned program, useful for valgrind + # return error code of the spawned program, useful for Valgrind lassign [wait] pid spawnid os_error_flag value exit \$value EOF From 1fe562fdf32c153f82929660197f8b97469f76b4 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sat, 30 Dec 2023 19:26:41 +0300 Subject: [PATCH 1300/1766] Simplify the improving flag calculation Passed STC: https://tests.stockfishchess.org/tests/view/658ec29979aa8af82b9547f6 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 93408 W: 23747 L: 23587 D: 46074 Ptnml(0-2): 340, 11178, 23527, 11300, 359 Passed LTC: https://tests.stockfishchess.org/tests/view/658f73e479aa8af82b9555b6 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 64026 W: 15984 L: 15806 D: 32236 Ptnml(0-2): 31, 7113, 17552, 7281, 36 closes https://github.com/official-stockfish/Stockfish/pull/4948 Bench: 1143749 --- src/search.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index eb63ec90762..cb6b450de1c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -761,9 +761,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // check at our previous move we look at static evaluation at move prior to it // and if we were in check at move prior to it flag is set to true) and is // false otherwise. The improving flag is used in various pruning heuristics. - improving = (ss - 2)->staticEval != VALUE_NONE ? ss->staticEval > (ss - 2)->staticEval - : (ss - 4)->staticEval != VALUE_NONE ? ss->staticEval > (ss - 4)->staticEval - : true; + improving = (ss - 2)->staticEval != VALUE_NONE ? ss->staticEval > (ss - 2)->staticEval + : (ss - 4)->staticEval != VALUE_NONE && ss->staticEval > (ss - 4)->staticEval; // Step 7. Razoring (~1 Elo) // If eval is really low check with qsearch if it can exceed alpha, if it can't, From 4ff297a6dfae199571a4f24631a8e970924c8d63 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sun, 31 Dec 2023 03:43:19 +0300 Subject: [PATCH 1301/1766] Mark square_bb() as constexpr closes https://github.com/official-stockfish/Stockfish/pull/4949 No functional change --- src/bitboard.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitboard.h b/src/bitboard.h index 7dbd5329be8..8b9c291807a 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -89,7 +89,7 @@ struct Magic { extern Magic RookMagics[SQUARE_NB]; extern Magic BishopMagics[SQUARE_NB]; -inline Bitboard square_bb(Square s) { +constexpr Bitboard square_bb(Square s) { assert(is_ok(s)); return (1ULL << s); } From b4d995d0d910044cf4ea2ad3ee30fd1d21070cd8 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 31 Dec 2023 10:13:03 +0300 Subject: [PATCH 1302/1766] Introduce static evaluation correction history Idea from Caissa (https://github.com/Witek902/Caissa) chess engine. With given pawn structure collect data with how often search result and by how much it was better / worse than static evalution of position and use it to adjust static evaluation of positions with given pawn structure. Details: 1. excludes positions with fail highs and moves producing it being a capture; 2. update value is function of not only difference between best value and static evaluation but also is multiplied by linear function of depth; 3. maximum update value is maximum value of correction history divided by 2; 4. correction history itself is divided by 32 when applied so maximum value of static evaluation adjustment is 32 internal units. Passed STC: https://tests.stockfishchess.org/tests/view/658fc7b679aa8af82b955cac LLR: 2.96 (-2.94,2.94) <0.00,2.00> Total: 128672 W: 32757 L: 32299 D: 63616 Ptnml(0-2): 441, 15241, 32543, 15641, 470 Passed LTC: https://tests.stockfishchess.org/tests/view/65903f6979aa8af82b9566f1 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 97422 W: 24626 L: 24178 D: 48618 Ptnml(0-2): 41, 10837, 26527, 11245, 61 closes https://github.com/official-stockfish/Stockfish/pull/4950 Bench: 1157852 --- src/movepick.cpp | 4 +- src/movepick.h | 21 ++++++++++- src/search.cpp | 97 +++++++++++++++++++++++++++++++++++++----------- src/thread.cpp | 1 + src/thread.h | 1 + 5 files changed, 98 insertions(+), 26 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 0267a8e2e6f..ab37ff68e12 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -179,7 +179,7 @@ void MovePicker::score() { // histories m.value = 2 * (*mainHistory)[pos.side_to_move()][from_to(m)]; - m.value += 2 * (*pawnHistory)[pawn_structure(pos)][pc][to]; + m.value += 2 * (*pawnHistory)[pawn_structure_index(pos)][pc][to]; m.value += 2 * (*continuationHistory[0])[pc][to]; m.value += (*continuationHistory[1])[pc][to]; m.value += (*continuationHistory[2])[pc][to] / 4; @@ -216,7 +216,7 @@ void MovePicker::score() { else m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] + (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] - + (*pawnHistory)[pawn_structure(pos)][pos.moved_piece(m)][to_sq(m)]; + + (*pawnHistory)[pawn_structure_index(pos)][pos.moved_piece(m)][to_sq(m)]; } } diff --git a/src/movepick.h b/src/movepick.h index 5077f4e3b72..eefc0d5037b 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -33,12 +33,25 @@ namespace Stockfish { -constexpr int PAWN_HISTORY_SIZE = 512; // has to be a power of 2 +constexpr int PAWN_HISTORY_SIZE = 512; // has to be a power of 2 +constexpr int CORRECTION_HISTORY_SIZE = 16384; // has to be a power of 2 +constexpr int CORRECTION_HISTORY_LIMIT = 1024; static_assert((PAWN_HISTORY_SIZE & (PAWN_HISTORY_SIZE - 1)) == 0, "PAWN_HISTORY_SIZE has to be a power of 2"); -inline int pawn_structure(const Position& pos) { return pos.pawn_key() & (PAWN_HISTORY_SIZE - 1); } +static_assert((CORRECTION_HISTORY_SIZE & (CORRECTION_HISTORY_SIZE - 1)) == 0, + "CORRECTION_HISTORY_SIZE has to be a power of 2"); + +enum PawnHistoryType { + Normal, + Correction +}; + +template +inline int pawn_structure_index(const Position& pos) { + return pos.pawn_key() & ((T == Normal ? PAWN_HISTORY_SIZE : CORRECTION_HISTORY_SIZE) - 1); +} // StatsEntry stores the stat table value. It is usually a number but could // be a move or even a nested history. We use a class instead of a naked value @@ -122,6 +135,10 @@ using ContinuationHistory = Stats // PawnHistory is addressed by the pawn structure and a move's [piece][to] using PawnHistory = Stats; +// CorrectionHistory is addressed by color and pawn structure +using CorrectionHistory = + Stats; + // MovePicker class is used to pick one pseudo-legal move at a time from the // current position. The most important method is next_move(), which returns a // new pseudo-legal move each time it is called, until there are no moves left, diff --git a/src/search.cpp b/src/search.cpp index cb6b450de1c..1ec77bcd93b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -93,6 +93,11 @@ constexpr int futility_move_count(bool improving, Depth depth) { return improving ? (3 + depth * depth) : (3 + depth * depth) / 2; } +// Guarantee evaluation does not hit the tablebase range +constexpr Value to_static_eval(const Value v) { + return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); +} + // History and stats update bonus, based on depth int stat_bonus(Depth d) { return std::min(268 * d - 352, 1153); } @@ -712,6 +717,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo CapturePieceToHistory& captureHistory = thisThread->captureHistory; + Value unadjustedStaticEval = VALUE_NONE; + // Step 6. Static evaluation of the position if (ss->inCheck) { @@ -725,26 +732,40 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Providing the hint that this node's accumulator will be used often // brings significant Elo gain (~13 Elo). Eval::NNUE::hint_common_parent_position(pos); - eval = ss->staticEval; + unadjustedStaticEval = eval = ss->staticEval; } else if (ss->ttHit) { // Never assume anything about values stored in TT - ss->staticEval = eval = tte->eval(); + unadjustedStaticEval = ss->staticEval = eval = tte->eval(); if (eval == VALUE_NONE) - ss->staticEval = eval = evaluate(pos); + unadjustedStaticEval = ss->staticEval = eval = evaluate(pos); else if (PvNode) Eval::NNUE::hint_common_parent_position(pos); + Value newEval = + ss->staticEval + + thisThread->correctionHistory[us][pawn_structure_index(pos)] / 32; + + ss->staticEval = eval = to_static_eval(newEval); + // ttValue can be used as a better position evaluation (~7 Elo) if (ttValue != VALUE_NONE && (tte->bound() & (ttValue > eval ? BOUND_LOWER : BOUND_UPPER))) eval = ttValue; } else { - ss->staticEval = eval = evaluate(pos); - // Save static evaluation into the transposition table - tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, eval); + unadjustedStaticEval = ss->staticEval = eval = evaluate(pos); + + Value newEval = + ss->staticEval + + thisThread->correctionHistory[us][pawn_structure_index(pos)] / 32; + + ss->staticEval = eval = to_static_eval(newEval); + + // Static evaluation is saved as it was before adjustment by correction history + tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, + unadjustedStaticEval); } // Use static evaluation difference to improve quiet move ordering (~9 Elo) @@ -753,7 +774,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo int bonus = std::clamp(-13 * int((ss - 1)->staticEval + ss->staticEval), -1652, 1546); thisThread->mainHistory[~us][from_to((ss - 1)->currentMove)] << bonus; if (type_of(pos.piece_on(prevSq)) != PAWN && type_of((ss - 1)->currentMove) != PROMOTION) - thisThread->pawnHistory[pawn_structure(pos)][pos.piece_on(prevSq)][prevSq] << bonus / 4; + thisThread->pawnHistory[pawn_structure_index(pos)][pos.piece_on(prevSq)][prevSq] + << bonus / 4; } // Set up the improving flag, which is true if current static evaluation is @@ -888,7 +910,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo { // Save ProbCut data into transposition table tte->save(posKey, value_to_tt(value, ss->ply), ss->ttPv, BOUND_LOWER, depth - 3, - move, ss->staticEval); + move, unadjustedStaticEval); return std::abs(value) < VALUE_TB_WIN_IN_MAX_PLY ? value - (probCutBeta - beta) : value; } @@ -999,10 +1021,10 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo } else { - int history = (*contHist[0])[movedPiece][to_sq(move)] - + (*contHist[1])[movedPiece][to_sq(move)] - + (*contHist[3])[movedPiece][to_sq(move)] - + thisThread->pawnHistory[pawn_structure(pos)][movedPiece][to_sq(move)]; + int history = + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + + (*contHist[3])[movedPiece][to_sq(move)] + + thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][to_sq(move)]; // Continuation history based pruning (~2 Elo) if (lmrDepth < 6 && history < -3752 * depth) @@ -1364,12 +1386,23 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo ss->ttPv = ss->ttPv || ((ss - 1)->ttPv && depth > 3); // Write gathered information in transposition table + // Static evaluation is saved as it was before correction history if (!excludedMove && !(rootNode && thisThread->pvIdx)) tte->save(posKey, value_to_tt(bestValue, ss->ply), ss->ttPv, bestValue >= beta ? BOUND_LOWER : PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER, - depth, bestMove, ss->staticEval); + depth, bestMove, unadjustedStaticEval); + + // Adjust correction history + if (!ss->inCheck && (!bestMove || !pos.capture(bestMove)) + && !(bestValue >= beta && bestValue <= ss->staticEval) + && !(!bestMove && bestValue >= ss->staticEval)) + { + auto bonus = std::clamp(int(bestValue - ss->staticEval) * depth / 8, + -CORRECTION_HISTORY_LIMIT / 4, CORRECTION_HISTORY_LIMIT / 4); + thisThread->correctionHistory[us][pawn_structure_index(pos)] << bonus; + } assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); @@ -1450,6 +1483,8 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { && (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER))) return ttValue; + Value unadjustedStaticEval = VALUE_NONE; + // Step 4. Static evaluation of the position if (ss->inCheck) bestValue = futilityBase = -VALUE_INFINITE; @@ -1458,8 +1493,14 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { if (ss->ttHit) { // Never assume anything about values stored in TT - if ((ss->staticEval = bestValue = tte->eval()) == VALUE_NONE) - ss->staticEval = bestValue = evaluate(pos); + if ((unadjustedStaticEval = ss->staticEval = bestValue = tte->eval()) == VALUE_NONE) + unadjustedStaticEval = ss->staticEval = bestValue = evaluate(pos); + + Value newEval = + ss->staticEval + + thisThread->correctionHistory[us][pawn_structure_index(pos)] / 32; + + ss->staticEval = bestValue = to_static_eval(newEval); // ttValue can be used as a better position evaluation (~13 Elo) if (ttValue != VALUE_NONE @@ -1467,16 +1508,24 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { bestValue = ttValue; } else + { // In case of null move search, use previous static eval with a different sign - ss->staticEval = bestValue = + unadjustedStaticEval = ss->staticEval = bestValue = (ss - 1)->currentMove != MOVE_NULL ? evaluate(pos) : -(ss - 1)->staticEval; + Value newEval = + ss->staticEval + + thisThread->correctionHistory[us][pawn_structure_index(pos)] / 32; + + ss->staticEval = bestValue = to_static_eval(newEval); + } + // Stand pat. Return immediately if static value is at least beta if (bestValue >= beta) { if (!ss->ttHit) tte->save(posKey, value_to_tt(bestValue, ss->ply), false, BOUND_LOWER, DEPTH_NONE, - MOVE_NONE, ss->staticEval); + MOVE_NONE, unadjustedStaticEval); return bestValue; } @@ -1620,8 +1669,10 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { bestValue = (3 * bestValue + beta) / 4; // Save gathered info in transposition table + // Static evaluation is saved as it was before adjustment by correction history tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit, - bestValue >= beta ? BOUND_LOWER : BOUND_UPPER, ttDepth, bestMove, ss->staticEval); + bestValue >= beta ? BOUND_LOWER : BOUND_UPPER, ttDepth, bestMove, + unadjustedStaticEval); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); @@ -1720,15 +1771,17 @@ void update_all_stats(const Position& pos, // Increase stats for the best move in case it was a quiet move update_quiet_stats(pos, ss, bestMove, bestMoveBonus); - thisThread->pawnHistory[pawn_structure(pos)][moved_piece][to_sq(bestMove)] - << quietMoveBonus; + + int pIndex = pawn_structure_index(pos); + thisThread->pawnHistory[pIndex][moved_piece][to_sq(bestMove)] << quietMoveBonus; // Decrease stats for all non-best quiet moves for (int i = 0; i < quietCount; ++i) { - thisThread->pawnHistory[pawn_structure(pos)][pos.moved_piece(quietsSearched[i])] - [to_sq(quietsSearched[i])] + thisThread + ->pawnHistory[pIndex][pos.moved_piece(quietsSearched[i])][to_sq(quietsSearched[i])] << -quietMoveMalus; + thisThread->mainHistory[us][from_to(quietsSearched[i])] << -quietMoveMalus; update_continuation_histories(ss, pos.moved_piece(quietsSearched[i]), to_sq(quietsSearched[i]), -quietMoveMalus); diff --git a/src/thread.cpp b/src/thread.cpp index de8de87d8a2..eeab18821ba 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -70,6 +70,7 @@ void Thread::clear() { mainHistory.fill(0); captureHistory.fill(0); pawnHistory.fill(0); + correctionHistory.fill(0); for (bool inCheck : {false, true}) for (StatsType c : {NoCaptures, Captures}) diff --git a/src/thread.h b/src/thread.h index cb2f6db1d41..1edc9cc9e31 100644 --- a/src/thread.h +++ b/src/thread.h @@ -69,6 +69,7 @@ class Thread { CapturePieceToHistory captureHistory; ContinuationHistory continuationHistory[2][2]; PawnHistory pawnHistory; + CorrectionHistory correctionHistory; }; From 3cfaef74311e943298a9a82bce5717d272338e66 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Sun, 31 Dec 2023 18:45:48 +0100 Subject: [PATCH 1303/1766] Tweak static eval history update Modify the applied static eval bonus for main and pawn history with different factors for positive and negative values. Passed STC: https://tests.stockfishchess.org/tests/view/659132e179aa8af82b957bb0 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 12512 W: 3308 L: 3027 D: 6177 Ptnml(0-2): 32, 1372, 3189, 1609, 54 Passed LTC: https://tests.stockfishchess.org/tests/view/65913e3d79aa8af82b957cd2 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 35946 W: 9128 L: 8809 D: 18009 Ptnml(0-2): 19, 3879, 9862, 4190, 23 closes https://github.com/official-stockfish/Stockfish/pull/4952 Bench: 1392883 --- src/search.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/search.cpp b/src/search.cpp index 1ec77bcd93b..8e48b1647ba 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -772,6 +772,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo if (is_ok((ss - 1)->currentMove) && !(ss - 1)->inCheck && !priorCapture) { int bonus = std::clamp(-13 * int((ss - 1)->staticEval + ss->staticEval), -1652, 1546); + bonus = bonus > 0 ? 2 * bonus : bonus / 2; thisThread->mainHistory[~us][from_to((ss - 1)->currentMove)] << bonus; if (type_of(pos.piece_on(prevSq)) != PAWN && type_of((ss - 1)->currentMove) != PROMOTION) thisThread->pawnHistory[pawn_structure_index(pos)][pos.piece_on(prevSq)][prevSq] From 0fca5605fa2e5e7240fde5e1aae50952b2612231 Mon Sep 17 00:00:00 2001 From: Disservin Date: Mon, 1 Jan 2024 02:29:28 +0100 Subject: [PATCH 1304/1766] Fix formatting in search.cpp fixes the formatting for 1fe562fdf32c153f82929660197f8b97469f76b4 --- src/search.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 8e48b1647ba..c45e9f20c01 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -784,8 +784,9 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // check at our previous move we look at static evaluation at move prior to it // and if we were in check at move prior to it flag is set to true) and is // false otherwise. The improving flag is used in various pruning heuristics. - improving = (ss - 2)->staticEval != VALUE_NONE ? ss->staticEval > (ss - 2)->staticEval - : (ss - 4)->staticEval != VALUE_NONE && ss->staticEval > (ss - 4)->staticEval; + improving = (ss - 2)->staticEval != VALUE_NONE + ? ss->staticEval > (ss - 2)->staticEval + : (ss - 4)->staticEval != VALUE_NONE && ss->staticEval > (ss - 4)->staticEval; // Step 7. Razoring (~1 Elo) // If eval is really low check with qsearch if it can exceed alpha, if it can't, From 154abb337e8737aedd6def4e7c0ca18bd4737252 Mon Sep 17 00:00:00 2001 From: Joseph Huang Date: Sun, 31 Dec 2023 03:11:04 -0500 Subject: [PATCH 1305/1766] Lower MultiPV max to MAX_MOVES Link max value of MultiPV to that of MAX_MOVES which is 256 closes https://github.com/official-stockfish/Stockfish/pull/4951 No functional change --- src/ucioption.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 1dc9b89baed..43392e9a19d 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -70,7 +70,7 @@ void init(OptionsMap& o) { o["Hash"] << Option(16, 1, MaxHashMB, on_hash_size); o["Clear Hash"] << Option(on_clear_hash); o["Ponder"] << Option(false); - o["MultiPV"] << Option(1, 1, 500); + o["MultiPV"] << Option(1, 1, MAX_MOVES); o["Skill Level"] << Option(20, 0, 20); o["Move Overhead"] << Option(10, 0, 5000); o["nodestime"] << Option(0, 0, 10000); From a25f48a23671b0e18d1eae58e95d1a28fc389221 Mon Sep 17 00:00:00 2001 From: Disservin Date: Mon, 1 Jan 2024 02:19:23 +0100 Subject: [PATCH 1306/1766] Silence security alert warning about possible infinite loop As some have noticed, a security alert has been complaining about a for loop in our TB code for quite some now. Though it was never a real issue, so not of high importance. A few lines earlier the symlen vector is resized `d->symlen.resize(number(data));` while this code seems odd at first, it resizes the array to at most (2 << 16) - 1 elements, basically making the infinite loop issue impossible to occur. closes https://github.com/official-stockfish/Stockfish/pull/4953 No functional change --- src/syzygy/tbprobe.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index e23631575e6..5fe28fd2966 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1074,7 +1074,7 @@ uint8_t* set_sizes(PairsData* d, uint8_t* data) { // See https://web.archive.org/web/20201106232444/http://www.larsson.dogma.net/dcc99.pdf std::vector visited(d->symlen.size()); - for (Sym sym = 0; sym < d->symlen.size(); ++sym) + for (std::size_t sym = 0; sym < d->symlen.size(); ++sym) if (!visited[sym]) d->symlen[sym] = set_symlen(d, sym, visited); From 444f03ee95fcde4cf9014d82cae72c644357a31d Mon Sep 17 00:00:00 2001 From: Disservin Date: Mon, 1 Jan 2024 12:41:20 +0100 Subject: [PATCH 1307/1766] Update copyright year closes https://github.com/official-stockfish/Stockfish/pull/4954 No functional change --- src/Makefile | 2 +- src/benchmark.cpp | 2 +- src/benchmark.h | 2 +- src/bitboard.cpp | 2 +- src/bitboard.h | 2 +- src/evaluate.cpp | 2 +- src/evaluate.h | 2 +- src/main.cpp | 2 +- src/misc.cpp | 2 +- src/misc.h | 2 +- src/movegen.cpp | 2 +- src/movegen.h | 2 +- src/movepick.cpp | 2 +- src/movepick.h | 2 +- src/nnue/evaluate_nnue.cpp | 2 +- src/nnue/evaluate_nnue.h | 2 +- src/nnue/features/half_ka_v2_hm.cpp | 2 +- src/nnue/features/half_ka_v2_hm.h | 2 +- src/nnue/layers/affine_transform.h | 2 +- src/nnue/layers/affine_transform_sparse_input.h | 2 +- src/nnue/layers/clipped_relu.h | 2 +- src/nnue/layers/simd.h | 2 +- src/nnue/layers/sqr_clipped_relu.h | 2 +- src/nnue/nnue_accumulator.h | 2 +- src/nnue/nnue_architecture.h | 2 +- src/nnue/nnue_common.h | 2 +- src/nnue/nnue_feature_transformer.h | 2 +- src/position.cpp | 2 +- src/position.h | 2 +- src/search.cpp | 2 +- src/search.h | 2 +- src/syzygy/tbprobe.cpp | 2 +- src/syzygy/tbprobe.h | 2 +- src/thread.cpp | 2 +- src/thread.h | 2 +- src/thread_win32_osx.h | 2 +- src/timeman.cpp | 2 +- src/timeman.h | 2 +- src/tt.cpp | 2 +- src/tt.h | 2 +- src/tune.cpp | 2 +- src/tune.h | 2 +- src/types.h | 2 +- src/uci.cpp | 2 +- src/uci.h | 2 +- src/ucioption.cpp | 2 +- 46 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/Makefile b/src/Makefile index ac354c7b99c..660b41e7edb 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,5 +1,5 @@ # Stockfish, a UCI chess playing engine derived from Glaurung 2.1 -# Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) +# Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) # # Stockfish is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 2270dcc3c83..50f8612d912 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/benchmark.h b/src/benchmark.h index e6206d19349..86f8a0ad50b 100644 --- a/src/benchmark.h +++ b/src/benchmark.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/bitboard.cpp b/src/bitboard.cpp index a8a10cbb874..72afabb6554 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/bitboard.h b/src/bitboard.h index 8b9c291807a..d028be02906 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 586cadc0ec5..b6342f189b8 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/evaluate.h b/src/evaluate.h index 33df13089fb..c2b08aaf1b0 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/main.cpp b/src/main.cpp index 04879cc4673..78b3f54d919 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/misc.cpp b/src/misc.cpp index 4193f8d2c7d..9350a4830df 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/misc.h b/src/misc.h index 91fdb72f1b1..ca6cc16639f 100644 --- a/src/misc.h +++ b/src/misc.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/movegen.cpp b/src/movegen.cpp index 7d6856bb036..750a07e85b7 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/movegen.h b/src/movegen.h index 9a39d1c50ea..3ae84c4c0cf 100644 --- a/src/movegen.h +++ b/src/movegen.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/movepick.cpp b/src/movepick.cpp index ab37ff68e12..aa577541d66 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/movepick.h b/src/movepick.h index eefc0d5037b..242524338f3 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index e7339c10ae9..14e2fec15a4 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/evaluate_nnue.h b/src/nnue/evaluate_nnue.h index 6edc212f4d7..05c98bc5380 100644 --- a/src/nnue/evaluate_nnue.h +++ b/src/nnue/evaluate_nnue.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/features/half_ka_v2_hm.cpp b/src/nnue/features/half_ka_v2_hm.cpp index 6d1b60ce43d..5789db4844a 100644 --- a/src/nnue/features/half_ka_v2_hm.cpp +++ b/src/nnue/features/half_ka_v2_hm.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/features/half_ka_v2_hm.h b/src/nnue/features/half_ka_v2_hm.h index c208e38dbad..8363184f430 100644 --- a/src/nnue/features/half_ka_v2_hm.h +++ b/src/nnue/features/half_ka_v2_hm.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 44fa5d00a43..e6852236108 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/layers/affine_transform_sparse_input.h b/src/nnue/layers/affine_transform_sparse_input.h index 70dbd790469..0ac557abac2 100644 --- a/src/nnue/layers/affine_transform_sparse_input.h +++ b/src/nnue/layers/affine_transform_sparse_input.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index a3a0c1ede9e..813234c59cd 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/layers/simd.h b/src/nnue/layers/simd.h index 5425ca192bc..6f4c9d206ed 100644 --- a/src/nnue/layers/simd.h +++ b/src/nnue/layers/simd.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/layers/sqr_clipped_relu.h b/src/nnue/layers/sqr_clipped_relu.h index b9d8f030a24..9c20df9d6f5 100644 --- a/src/nnue/layers/sqr_clipped_relu.h +++ b/src/nnue/layers/sqr_clipped_relu.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index 2f1b1d35e52..f6d705243ef 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index e4c308cb267..6c0e52b738e 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/nnue_common.h b/src/nnue/nnue_common.h index d4bd0028969..4bc3408f18a 100644 --- a/src/nnue/nnue_common.h +++ b/src/nnue/nnue_common.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index a83a77c9d71..2008cf25f1d 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/position.cpp b/src/position.cpp index c45dd7b2e22..32823bd05f2 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/position.h b/src/position.h index ce03c34f332..46956afc6e9 100644 --- a/src/position.h +++ b/src/position.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/search.cpp b/src/search.cpp index c45e9f20c01..3553065fd99 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/search.h b/src/search.h index b2d22e612c4..72e275d36f7 100644 --- a/src/search.h +++ b/src/search.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 5fe28fd2966..c1275cf5755 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/syzygy/tbprobe.h b/src/syzygy/tbprobe.h index 3b7c8aa70fd..cc8eb0d4d70 100644 --- a/src/syzygy/tbprobe.h +++ b/src/syzygy/tbprobe.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/thread.cpp b/src/thread.cpp index eeab18821ba..e900a9ac88f 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/thread.h b/src/thread.h index 1edc9cc9e31..22fe32c3a12 100644 --- a/src/thread.h +++ b/src/thread.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/thread_win32_osx.h b/src/thread_win32_osx.h index 248e4a67450..4bc62d678ad 100644 --- a/src/thread_win32_osx.h +++ b/src/thread_win32_osx.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/timeman.cpp b/src/timeman.cpp index f404ee0c353..77db2f621df 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/timeman.h b/src/timeman.h index 6c56d506b3f..0509158c3f4 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/tt.cpp b/src/tt.cpp index 816d43f8603..5c4e6d537fc 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/tt.h b/src/tt.h index 12fedd2d42f..82a66863b9d 100644 --- a/src/tt.h +++ b/src/tt.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/tune.cpp b/src/tune.cpp index cf80b9d7b70..44bfa682a7d 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/tune.h b/src/tune.h index 480aea165b5..3d45e51c19e 100644 --- a/src/tune.h +++ b/src/tune.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/types.h b/src/types.h index 3e00d68d19d..dde1a52c2a0 100644 --- a/src/types.h +++ b/src/types.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/uci.cpp b/src/uci.cpp index 5f250a3617a..5dc9b2b0690 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/uci.h b/src/uci.h index 55fb47c29ef..d249da7442b 100644 --- a/src/uci.h +++ b/src/uci.h @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 43392e9a19d..087882f11b8 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -1,6 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by From 5546bc0a260d9bd01b1ef9d5b6b10fbbcb3a24b0 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Mon, 1 Jan 2024 14:52:05 +0300 Subject: [PATCH 1308/1766] Simplification of partial_insertion_sort formula. Passed STC: https://tests.stockfishchess.org/tests/view/6590110879aa8af82b9562e9 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 134880 W: 34468 L: 34355 D: 66057 Ptnml(0-2): 476, 16060, 34220, 16243, 441 Passed LTC: https://tests.stockfishchess.org/tests/view/659156ca79aa8af82b957f07 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 60780 W: 15179 L: 14996 D: 30605 Ptnml(0-2): 27, 6847, 16464, 7020, 32 closes https://github.com/official-stockfish/Stockfish/pull/4955 Bench: 1338331 --- src/movepick.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index aa577541d66..f33839cd04b 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -300,7 +300,7 @@ Move MovePicker::next_move(bool skipQuiets) { endMoves = generate(pos, cur); score(); - partial_insertion_sort(cur, endMoves, -1960 - 3130 * depth); + partial_insertion_sort(cur, endMoves, -3330 * depth); } ++stage; From 28f8663f3947e716fefe392a463060dc12e39849 Mon Sep 17 00:00:00 2001 From: Viren6 <94880762+Viren6@users.noreply.github.com> Date: Tue, 2 Jan 2024 02:54:45 +0000 Subject: [PATCH 1309/1766] Modify ttPV reduction This patch modifies ttPV reduction by reducing 1 more unless ttValue is above alpha. Inspired from @pb00068 https://tests.stockfishchess.org/tests/view/658060796a3b4f6202215f1f Passed STC: https://tests.stockfishchess.org/tests/view/6591867679aa8af82b958328 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 37856 W: 9727 L: 9407 D: 18722 Ptnml(0-2): 99, 4444, 9568, 4672, 145 Passed LTC: https://tests.stockfishchess.org/tests/view/6591d9b679aa8af82b958a6c LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 128256 W: 32152 L: 31639 D: 64465 Ptnml(0-2): 64, 14364, 34772, 14851, 77 closes https://github.com/official-stockfish/Stockfish/pull/4957 Bench: 1176235 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 3553065fd99..aae3625a6da 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1152,7 +1152,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Decrease reduction if position is or has been on the PV (~4 Elo) if (ss->ttPv && !likelyFailLow) - r -= cutNode && tte->depth() >= depth ? 3 : 2; + r -= 1 + (cutNode && tte->depth() >= depth) + (ttValue > alpha); // Decrease reduction if opponent's move count is high (~1 Elo) if ((ss - 1)->moveCount > 7) From cafbe8e8e8c26594dd7040788e6f72bc4bc8cfd9 Mon Sep 17 00:00:00 2001 From: Disservin Date: Mon, 1 Jan 2024 23:13:18 +0100 Subject: [PATCH 1310/1766] Change the Move enum to a class This changes the Move enum to a class, this way all move related functions can be moved into the class and be more self contained. closes https://github.com/official-stockfish/Stockfish/pull/4958 No functional change --- src/movegen.cpp | 28 ++++----- src/movegen.h | 8 +-- src/movepick.cpp | 32 +++++----- src/movepick.h | 2 +- src/position.cpp | 86 +++++++++++++-------------- src/position.h | 10 ++-- src/search.cpp | 148 ++++++++++++++++++++++++----------------------- src/thread.cpp | 10 ++-- src/tt.cpp | 2 +- src/tt.h | 2 +- src/types.h | 111 +++++++++++++++++++++-------------- src/uci.cpp | 18 +++--- 12 files changed, 238 insertions(+), 219 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index 750a07e85b7..e6923067f88 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -34,13 +34,13 @@ ExtMove* make_promotions(ExtMove* moveList, [[maybe_unused]] Square to) { constexpr bool all = Type == EVASIONS || Type == NON_EVASIONS; if constexpr (Type == CAPTURES || all) - *moveList++ = make(to - D, to, QUEEN); + *moveList++ = Move::make(to - D, to, QUEEN); if constexpr ((Type == CAPTURES && Enemy) || (Type == QUIETS && !Enemy) || all) { - *moveList++ = make(to - D, to, ROOK); - *moveList++ = make(to - D, to, BISHOP); - *moveList++ = make(to - D, to, KNIGHT); + *moveList++ = Move::make(to - D, to, ROOK); + *moveList++ = Move::make(to - D, to, BISHOP); + *moveList++ = Move::make(to - D, to, KNIGHT); } return moveList; @@ -89,13 +89,13 @@ ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard ta while (b1) { Square to = pop_lsb(b1); - *moveList++ = make_move(to - Up, to); + *moveList++ = Move(to - Up, to); } while (b2) { Square to = pop_lsb(b2); - *moveList++ = make_move(to - Up - Up, to); + *moveList++ = Move(to - Up - Up, to); } } @@ -128,13 +128,13 @@ ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard ta while (b1) { Square to = pop_lsb(b1); - *moveList++ = make_move(to - UpRight, to); + *moveList++ = Move(to - UpRight, to); } while (b2) { Square to = pop_lsb(b2); - *moveList++ = make_move(to - UpLeft, to); + *moveList++ = Move(to - UpLeft, to); } if (pos.ep_square() != SQ_NONE) @@ -150,7 +150,7 @@ ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard ta assert(b1); while (b1) - *moveList++ = make(pop_lsb(b1), pos.ep_square()); + *moveList++ = Move::make(pop_lsb(b1), pos.ep_square()); } } @@ -175,7 +175,7 @@ ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) b &= pos.check_squares(Pt); while (b) - *moveList++ = make_move(from, pop_lsb(b)); + *moveList++ = Move(from, pop_lsb(b)); } return moveList; @@ -213,12 +213,12 @@ ExtMove* generate_all(const Position& pos, ExtMove* moveList) { b &= ~attacks_bb(pos.square(~Us)); while (b) - *moveList++ = make_move(ksq, pop_lsb(b)); + *moveList++ = Move(ksq, pop_lsb(b)); if ((Type == QUIETS || Type == NON_EVASIONS) && pos.can_castle(Us & ANY_CASTLING)) for (CastlingRights cr : {Us & KING_SIDE, Us & QUEEN_SIDE}) if (!pos.castling_impeded(cr) && pos.can_castle(cr)) - *moveList++ = make(ksq, pos.castling_rook_square(cr)); + *moveList++ = Move::make(ksq, pos.castling_rook_square(cr)); } return moveList; @@ -268,9 +268,9 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { moveList = pos.checkers() ? generate(pos, moveList) : generate(pos, moveList); while (cur != moveList) - if (((pinned & from_sq(*cur)) || from_sq(*cur) == ksq || type_of(*cur) == EN_PASSANT) + if (((pinned & cur->from_sq()) || cur->from_sq() == ksq || cur->type_of() == EN_PASSANT) && !pos.legal(*cur)) - *cur = (--moveList)->move; + *cur = *(--moveList); else ++cur; diff --git a/src/movegen.h b/src/movegen.h index 3ae84c4c0cf..5f650d2e36d 100644 --- a/src/movegen.h +++ b/src/movegen.h @@ -37,12 +37,10 @@ enum GenType { LEGAL }; -struct ExtMove { - Move move; - int value; +struct ExtMove: public Move { + int value; - operator Move() const { return move; } - void operator=(Move m) { move = m; } + void operator=(Move m) { data = m.raw(); } // Inhibit unwanted implicit conversions to Move // with an ambiguity that yields to a compile error. diff --git a/src/movepick.cpp b/src/movepick.cpp index f33839cd04b..cae018915f0 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -166,19 +166,19 @@ void MovePicker::score() { for (auto& m : *this) if constexpr (Type == CAPTURES) m.value = - (7 * int(PieceValue[pos.piece_on(to_sq(m))]) - + (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))]) + (7 * int(PieceValue[pos.piece_on(m.to_sq())]) + + (*captureHistory)[pos.moved_piece(m)][m.to_sq()][type_of(pos.piece_on(m.to_sq()))]) / 16; else if constexpr (Type == QUIETS) { Piece pc = pos.moved_piece(m); PieceType pt = type_of(pos.moved_piece(m)); - Square from = from_sq(m); - Square to = to_sq(m); + Square from = m.from_sq(); + Square to = m.to_sq(); // histories - m.value = 2 * (*mainHistory)[pos.side_to_move()][from_to(m)]; + m.value = 2 * (*mainHistory)[pos.side_to_move()][m.from_to()]; m.value += 2 * (*pawnHistory)[pawn_structure_index(pos)][pc][to]; m.value += 2 * (*continuationHistory[0])[pc][to]; m.value += (*continuationHistory[1])[pc][to]; @@ -211,12 +211,12 @@ void MovePicker::score() { else // Type == EVASIONS { if (pos.capture_stage(m)) - m.value = PieceValue[pos.piece_on(to_sq(m))] - Value(type_of(pos.moved_piece(m))) + m.value = PieceValue[pos.piece_on(m.to_sq())] - Value(type_of(pos.moved_piece(m))) + (1 << 28); else - m.value = (*mainHistory)[pos.side_to_move()][from_to(m)] - + (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)] - + (*pawnHistory)[pawn_structure_index(pos)][pos.moved_piece(m)][to_sq(m)]; + m.value = (*mainHistory)[pos.side_to_move()][m.from_to()] + + (*continuationHistory[0])[pos.moved_piece(m)][m.to_sq()] + + (*pawnHistory)[pawn_structure_index(pos)][pos.moved_piece(m)][m.to_sq()]; } } @@ -235,7 +235,7 @@ Move MovePicker::select(Pred filter) { cur++; } - return MOVE_NONE; + return Move::none(); } // Most important method of the MovePicker class. It @@ -278,8 +278,7 @@ Move MovePicker::next_move(bool skipQuiets) { endMoves = std::end(refutations); // If the countermove is the same as a killer, skip it - if (refutations[0].move == refutations[2].move - || refutations[1].move == refutations[2].move) + if (refutations[0] == refutations[2] || refutations[1] == refutations[2]) --endMoves; ++stage; @@ -287,7 +286,7 @@ Move MovePicker::next_move(bool skipQuiets) { case REFUTATION : if (select([&]() { - return *cur != MOVE_NONE && !pos.capture_stage(*cur) && pos.pseudo_legal(*cur); + return *cur != Move::none() && !pos.capture_stage(*cur) && pos.pseudo_legal(*cur); })) return *(cur - 1); ++stage; @@ -308,8 +307,7 @@ Move MovePicker::next_move(bool skipQuiets) { case QUIET : if (!skipQuiets && select([&]() { - return *cur != refutations[0].move && *cur != refutations[1].move - && *cur != refutations[2].move; + return *cur != refutations[0] && *cur != refutations[1] && *cur != refutations[2]; })) return *(cur - 1); @@ -343,7 +341,7 @@ Move MovePicker::next_move(bool skipQuiets) { // If we did not find any move and we do not try checks, we have finished if (depth != DEPTH_QS_CHECKS) - return MOVE_NONE; + return Move::none(); ++stage; [[fallthrough]]; @@ -360,7 +358,7 @@ Move MovePicker::next_move(bool skipQuiets) { } assert(false); - return MOVE_NONE; // Silence warning + return Move::none(); // Silence warning } } // namespace Stockfish diff --git a/src/movepick.h b/src/movepick.h index 242524338f3..ad4be8e9a9c 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -142,7 +142,7 @@ using CorrectionHistory = // MovePicker class is used to pick one pseudo-legal move at a time from the // current position. The most important method is next_move(), which returns a // new pseudo-legal move each time it is called, until there are no moves left, -// when MOVE_NONE is returned. In order to improve the efficiency of the +// when Move::none() is returned. In order to improve the efficiency of the // alpha-beta algorithm, MovePicker attempts to return the moves which are most // likely to get a cut-off first. class MovePicker { diff --git a/src/position.cpp b/src/position.cpp index 32823bd05f2..810bba57d6f 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -140,14 +140,14 @@ void Position::init() { for (Square s2 = Square(s1 + 1); s2 <= SQ_H8; ++s2) if ((type_of(pc) != PAWN) && (attacks_bb(type_of(pc), s1, 0) & s2)) { - Move move = make_move(s1, s2); + Move move = Move(s1, s2); Key key = Zobrist::psq[pc][s1] ^ Zobrist::psq[pc][s2] ^ Zobrist::side; int i = H1(key); while (true) { std::swap(cuckoo[i], key); std::swap(cuckooMove[i], move); - if (move == MOVE_NONE) // Arrived at empty slot? + if (move == Move::none()) // Arrived at empty slot? break; i = (i == H1(key)) ? H2(key) : H1(key); // Push victim to alternative slot } @@ -487,11 +487,11 @@ Bitboard Position::attackers_to(Square s, Bitboard occupied) const { // Tests whether a pseudo-legal move is legal bool Position::legal(Move m) const { - assert(is_ok(m)); + assert(m.is_ok()); Color us = sideToMove; - Square from = from_sq(m); - Square to = to_sq(m); + Square from = m.from_sq(); + Square to = m.to_sq(); assert(color_of(moved_piece(m)) == us); assert(piece_on(square(us)) == make_piece(us, KING)); @@ -499,7 +499,7 @@ bool Position::legal(Move m) const { // En passant captures are a tricky special case. Because they are rather // uncommon, we do it simply by testing whether the king is attacked after // the move is made. - if (type_of(m) == EN_PASSANT) + if (m.type_of() == EN_PASSANT) { Square ksq = square(us); Square capsq = to - pawn_push(us); @@ -516,7 +516,7 @@ bool Position::legal(Move m) const { // Castling moves generation does not check if the castling path is clear of // enemy attacks, it is delayed at a later time: now! - if (type_of(m) == CASTLING) + if (m.type_of() == CASTLING) { // After castling, the rook and king final positions are the same in // Chess960 as they would be in standard chess. @@ -529,7 +529,7 @@ bool Position::legal(Move m) const { // In case of Chess960, verify if the Rook blocks some checks. // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1. - return !chess960 || !(blockers_for_king(us) & to_sq(m)); + return !chess960 || !(blockers_for_king(us) & m.to_sq()); } // If the moving piece is a king, check whether the destination square is @@ -549,18 +549,18 @@ bool Position::legal(Move m) const { bool Position::pseudo_legal(const Move m) const { Color us = sideToMove; - Square from = from_sq(m); - Square to = to_sq(m); + Square from = m.from_sq(); + Square to = m.to_sq(); Piece pc = moved_piece(m); // Use a slower but simpler function for uncommon cases // yet we skip the legality check of MoveList(). - if (type_of(m) != NORMAL) + if (m.type_of() != NORMAL) return checkers() ? MoveList(*this).contains(m) : MoveList(*this).contains(m); // Is not a promotion, so the promotion piece must be empty - assert(promotion_type(m) - KNIGHT == NO_PIECE_TYPE); + assert(m.promotion_type() - KNIGHT == NO_PIECE_TYPE); // If the 'from' square is not occupied by a piece belonging to the side to // move, the move is obviously not legal. @@ -615,11 +615,11 @@ bool Position::pseudo_legal(const Move m) const { // Tests whether a pseudo-legal move gives a check bool Position::gives_check(Move m) const { - assert(is_ok(m)); + assert(m.is_ok()); assert(color_of(moved_piece(m)) == sideToMove); - Square from = from_sq(m); - Square to = to_sq(m); + Square from = m.from_sq(); + Square to = m.to_sq(); // Is there a direct check? if (check_squares(type_of(piece_on(from))) & to) @@ -627,15 +627,15 @@ bool Position::gives_check(Move m) const { // Is there a discovered check? if (blockers_for_king(~sideToMove) & from) - return !aligned(from, to, square(~sideToMove)) || type_of(m) == CASTLING; + return !aligned(from, to, square(~sideToMove)) || m.type_of() == CASTLING; - switch (type_of(m)) + switch (m.type_of()) { case NORMAL : return false; case PROMOTION : - return attacks_bb(promotion_type(m), to, pieces() ^ from) & square(~sideToMove); + return attacks_bb(m.promotion_type(), to, pieces() ^ from) & square(~sideToMove); // En passant capture with check? We have already handled the case of direct // checks and ordinary discovered check, so the only case we need to handle @@ -664,7 +664,7 @@ bool Position::gives_check(Move m) const { // moves should be filtered out before this function is called. void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { - assert(is_ok(m)); + assert(m.is_ok()); assert(&newSt != st); thisThread->nodes.fetch_add(1, std::memory_order_relaxed); @@ -691,16 +691,16 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { Color us = sideToMove; Color them = ~us; - Square from = from_sq(m); - Square to = to_sq(m); + Square from = m.from_sq(); + Square to = m.to_sq(); Piece pc = piece_on(from); - Piece captured = type_of(m) == EN_PASSANT ? make_piece(them, PAWN) : piece_on(to); + Piece captured = m.type_of() == EN_PASSANT ? make_piece(them, PAWN) : piece_on(to); assert(color_of(pc) == us); - assert(captured == NO_PIECE || color_of(captured) == (type_of(m) != CASTLING ? them : us)); + assert(captured == NO_PIECE || color_of(captured) == (m.type_of() != CASTLING ? them : us)); assert(type_of(captured) != KING); - if (type_of(m) == CASTLING) + if (m.type_of() == CASTLING) { assert(pc == make_piece(us, KING)); assert(captured == make_piece(us, ROOK)); @@ -720,7 +720,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // update non-pawn material. if (type_of(captured) == PAWN) { - if (type_of(m) == EN_PASSANT) + if (m.type_of() == EN_PASSANT) { capsq -= pawn_push(us); @@ -771,7 +771,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { } // Move the piece. The tricky Chess960 castling is handled earlier - if (type_of(m) != CASTLING) + if (m.type_of() != CASTLING) { dp.piece[0] = pc; dp.from[0] = from; @@ -791,9 +791,9 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { k ^= Zobrist::enpassant[file_of(st->epSquare)]; } - else if (type_of(m) == PROMOTION) + else if (m.type_of() == PROMOTION) { - Piece promotion = make_piece(us, promotion_type(m)); + Piece promotion = make_piece(us, m.promotion_type()); assert(relative_rank(us, to) == RANK_8); assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN); @@ -866,22 +866,22 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // be restored to exactly the same state as before the move was made. void Position::undo_move(Move m) { - assert(is_ok(m)); + assert(m.is_ok()); sideToMove = ~sideToMove; Color us = sideToMove; - Square from = from_sq(m); - Square to = to_sq(m); + Square from = m.from_sq(); + Square to = m.to_sq(); Piece pc = piece_on(to); - assert(empty(from) || type_of(m) == CASTLING); + assert(empty(from) || m.type_of() == CASTLING); assert(type_of(st->capturedPiece) != KING); - if (type_of(m) == PROMOTION) + if (m.type_of() == PROMOTION) { assert(relative_rank(us, to) == RANK_8); - assert(type_of(pc) == promotion_type(m)); + assert(type_of(pc) == m.promotion_type()); assert(type_of(pc) >= KNIGHT && type_of(pc) <= QUEEN); remove_piece(to); @@ -889,7 +889,7 @@ void Position::undo_move(Move m) { put_piece(pc, to); } - if (type_of(m) == CASTLING) + if (m.type_of() == CASTLING) { Square rfrom, rto; do_castling(us, from, to, rfrom, rto); @@ -902,7 +902,7 @@ void Position::undo_move(Move m) { { Square capsq = to; - if (type_of(m) == EN_PASSANT) + if (m.type_of() == EN_PASSANT) { capsq -= pawn_push(us); @@ -1011,8 +1011,8 @@ void Position::undo_null_move() { // en passant and promotions. Key Position::key_after(Move m) const { - Square from = from_sq(m); - Square to = to_sq(m); + Square from = m.from_sq(); + Square to = m.to_sq(); Piece pc = piece_on(from); Piece captured = piece_on(to); Key k = st->key ^ Zobrist::side; @@ -1031,13 +1031,13 @@ Key Position::key_after(Move m) const { // algorithm similar to alpha-beta pruning with a null window. bool Position::see_ge(Move m, Value threshold) const { - assert(is_ok(m)); + assert(m.is_ok()); // Only deal with normal moves, assume others pass a simple SEE - if (type_of(m) != NORMAL) + if (m.type_of() != NORMAL) return VALUE_ZERO >= threshold; - Square from = from_sq(m), to = to_sq(m); + Square from = m.from_sq(), to = m.to_sq(); int swap = PieceValue[piece_on(to)] - threshold; if (swap < 0) @@ -1182,8 +1182,8 @@ bool Position::has_game_cycle(int ply) const { if ((j = H1(moveKey), cuckoo[j] == moveKey) || (j = H2(moveKey), cuckoo[j] == moveKey)) { Move move = cuckooMove[j]; - Square s1 = from_sq(move); - Square s2 = to_sq(move); + Square s1 = move.from_sq(); + Square s2 = move.to_sq(); if (!((between_bb(s1, s2) ^ s2) & pieces())) { diff --git a/src/position.h b/src/position.h index 46956afc6e9..3e9327592fa 100644 --- a/src/position.h +++ b/src/position.h @@ -210,7 +210,7 @@ inline Piece Position::piece_on(Square s) const { inline bool Position::empty(Square s) const { return piece_on(s) == NO_PIECE; } -inline Piece Position::moved_piece(Move m) const { return piece_on(from_sq(m)); } +inline Piece Position::moved_piece(Move m) const { return piece_on(m.from_sq()); } inline Bitboard Position::pieces(PieceType pt) const { return byTypeBB[pt]; } @@ -312,16 +312,16 @@ inline int Position::rule50_count() const { return st->rule50; } inline bool Position::is_chess960() const { return chess960; } inline bool Position::capture(Move m) const { - assert(is_ok(m)); - return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == EN_PASSANT; + assert(m.is_ok()); + return (!empty(m.to_sq()) && m.type_of() != CASTLING) || m.type_of() == EN_PASSANT; } // Returns true if a move is generated from the capture stage, having also // queen promotions covered, i.e. consistency with the capture stage move generation // is needed to avoid the generation of duplicate moves. inline bool Position::capture_stage(Move m) const { - assert(is_ok(m)); - return capture(m) || promotion_type(m) == QUEEN; + assert(m.is_ok()); + return capture(m) || m.promotion_type() == QUEEN; } inline Piece Position::captured_piece() const { return st->capturedPiece; } diff --git a/src/search.cpp b/src/search.cpp index aae3625a6da..0d41f48d981 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -130,7 +130,7 @@ struct Skill { Move pick_best(size_t multiPV); double level; - Move best = MOVE_NONE; + Move best = Move::none(); }; template @@ -226,7 +226,7 @@ void MainThread::search() { if (rootMoves.empty()) { - rootMoves.emplace_back(MOVE_NONE); + rootMoves.emplace_back(Move::none()); sync_cout << "info depth 0 score " << UCI::value(rootPos.checkers() ? -VALUE_MATE : VALUE_DRAW) << sync_endl; } @@ -262,7 +262,7 @@ void MainThread::search() { Skill(Options["Skill Level"], Options["UCI_LimitStrength"] ? int(Options["UCI_Elo"]) : 0); if (int(Options["MultiPV"]) == 1 && !Limits.depth && !skill.enabled() - && rootMoves[0].pv[0] != MOVE_NONE) + && rootMoves[0].pv[0] != Move::none()) bestThread = Threads.get_best_thread(); bestPreviousScore = bestThread->rootMoves[0].score; @@ -293,7 +293,7 @@ void Thread::search() { Stack stack[MAX_PLY + 10], *ss = stack + 7; Move pv[MAX_PLY + 1]; Value alpha, beta, delta; - Move lastBestMove = MOVE_NONE; + Move lastBestMove = Move::none(); Depth lastBestMoveDepth = 0; MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr); double timeReduction = 1, totBestMoveChanges = 0; @@ -604,11 +604,11 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo assert(0 <= ss->ply && ss->ply < MAX_PLY); - (ss + 1)->excludedMove = bestMove = MOVE_NONE; - (ss + 2)->killers[0] = (ss + 2)->killers[1] = MOVE_NONE; + (ss + 1)->excludedMove = bestMove = Move::none(); + (ss + 2)->killers[0] = (ss + 2)->killers[1] = Move::none(); (ss + 2)->cutoffCnt = 0; ss->doubleExtensions = (ss - 1)->doubleExtensions; - Square prevSq = is_ok((ss - 1)->currentMove) ? to_sq((ss - 1)->currentMove) : SQ_NONE; + Square prevSq = ((ss - 1)->currentMove).is_ok() ? ((ss - 1)->currentMove).to_sq() : SQ_NONE; ss->statScore = 0; // Step 4. Transposition table lookup. @@ -618,7 +618,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] : ss->ttHit ? tte->move() - : MOVE_NONE; + : Move::none(); ttCapture = ttMove && pos.capture_stage(ttMove); // At this point, if excluded, skip straight to step 6, static eval. However, @@ -650,8 +650,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo else if (!ttCapture) { int penalty = -stat_malus(depth); - thisThread->mainHistory[us][from_to(ttMove)] << penalty; - update_continuation_histories(ss, pos.moved_piece(ttMove), to_sq(ttMove), penalty); + thisThread->mainHistory[us][ttMove.from_to()] << penalty; + update_continuation_histories(ss, pos.moved_piece(ttMove), ttMove.to_sq(), penalty); } } @@ -699,7 +699,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo if (b == BOUND_EXACT || (b == BOUND_LOWER ? value >= beta : value <= alpha)) { tte->save(posKey, value_to_tt(value, ss->ply), ss->ttPv, b, - std::min(MAX_PLY - 1, depth + 6), MOVE_NONE, VALUE_NONE); + std::min(MAX_PLY - 1, depth + 6), Move::none(), VALUE_NONE); return value; } @@ -764,17 +764,17 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo ss->staticEval = eval = to_static_eval(newEval); // Static evaluation is saved as it was before adjustment by correction history - tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE, + tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, Move::none(), unadjustedStaticEval); } // Use static evaluation difference to improve quiet move ordering (~9 Elo) - if (is_ok((ss - 1)->currentMove) && !(ss - 1)->inCheck && !priorCapture) + if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture) { int bonus = std::clamp(-13 * int((ss - 1)->staticEval + ss->staticEval), -1652, 1546); bonus = bonus > 0 ? 2 * bonus : bonus / 2; - thisThread->mainHistory[~us][from_to((ss - 1)->currentMove)] << bonus; - if (type_of(pos.piece_on(prevSq)) != PAWN && type_of((ss - 1)->currentMove) != PROMOTION) + thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus; + if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) thisThread->pawnHistory[pawn_structure_index(pos)][pos.piece_on(prevSq)][prevSq] << bonus / 4; } @@ -810,9 +810,9 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo return beta > VALUE_TB_LOSS_IN_MAX_PLY ? (eval + beta) / 2 : eval; // Step 9. Null move search with verification search (~35 Elo) - if (!PvNode && (ss - 1)->currentMove != MOVE_NULL && (ss - 1)->statScore < 17496 && eval >= beta - && eval >= ss->staticEval && ss->staticEval >= beta - 23 * depth + 304 && !excludedMove - && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly + if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 17496 + && eval >= beta && eval >= ss->staticEval && ss->staticEval >= beta - 23 * depth + 304 + && !excludedMove && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly && beta > VALUE_TB_LOSS_IN_MAX_PLY) { assert(eval - beta >= 0); @@ -820,7 +820,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Null move dynamic reduction based on depth and eval Depth R = std::min(int(eval - beta) / 144, 6) + depth / 3 + 4; - ss->currentMove = MOVE_NULL; + ss->currentMove = Move::null(); ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; pos.do_null_move(st); @@ -883,7 +883,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, &captureHistory); - while ((move = mp.next_move()) != MOVE_NONE) + while ((move = mp.next_move()) != Move::none()) if (move != excludedMove && pos.legal(move)) { assert(pos.capture_stage(move)); @@ -894,7 +894,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo ss->currentMove = move; ss->continuationHistory = &thisThread - ->continuationHistory[ss->inCheck][true][pos.moved_piece(move)][to_sq(move)]; + ->continuationHistory[ss->inCheck][true][pos.moved_piece(move)][move.to_sq()]; pos.do_move(move, st); @@ -938,7 +938,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo (ss - 6)->continuationHistory}; Move countermove = - prevSq != SQ_NONE ? thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] : MOVE_NONE; + prevSq != SQ_NONE ? thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] : Move::none(); MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &captureHistory, contHist, &thisThread->pawnHistory, countermove, ss->killers); @@ -953,9 +953,9 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Step 13. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. - while ((move = mp.next_move(moveCountPruning)) != MOVE_NONE) + while ((move = mp.next_move(moveCountPruning)) != Move::none()) { - assert(is_ok(move)); + assert(move.is_ok()); if (move == excludedMove) continue; @@ -1009,10 +1009,10 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Futility pruning for captures (~2 Elo) if (!givesCheck && lmrDepth < 7 && !ss->inCheck) { - Piece capturedPiece = pos.piece_on(to_sq(move)); + Piece capturedPiece = pos.piece_on(move.to_sq()); int futilityEval = ss->staticEval + 238 + 305 * lmrDepth + PieceValue[capturedPiece] - + captureHistory[movedPiece][to_sq(move)][type_of(capturedPiece)] / 7; + + captureHistory[movedPiece][move.to_sq()][type_of(capturedPiece)] / 7; if (futilityEval < alpha) continue; } @@ -1024,15 +1024,16 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo else { int history = - (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] - + (*contHist[3])[movedPiece][to_sq(move)] - + thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][to_sq(move)]; + (*contHist[0])[movedPiece][move.to_sq()] + + (*contHist[1])[movedPiece][move.to_sq()] + + (*contHist[3])[movedPiece][move.to_sq()] + + thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()]; // Continuation history based pruning (~2 Elo) if (lmrDepth < 6 && history < -3752 * depth) continue; - history += 2 * thisThread->mainHistory[us][from_to(move)]; + history += 2 * thisThread->mainHistory[us][move.from_to()]; lmrDepth += history / 7838; lmrDepth = std::max(lmrDepth, -1); @@ -1077,7 +1078,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo ss->excludedMove = move; value = search(pos, ss, singularBeta - 1, singularBeta, singularDepth, cutNode); - ss->excludedMove = MOVE_NONE; + ss->excludedMove = Move::none(); if (value < singularBeta) { @@ -1125,12 +1126,13 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Quiet ttMove extensions (~1 Elo) else if (PvNode && move == ttMove && move == ss->killers[0] - && (*contHist[0])[movedPiece][to_sq(move)] >= 4325) + && (*contHist[0])[movedPiece][move.to_sq()] >= 4325) extension = 1; // Recapture extensions (~1 Elo) - else if (PvNode && move == ttMove && to_sq(move) == prevSq - && captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))] + else if (PvNode && move == ttMove && move.to_sq() == prevSq + && captureHistory[movedPiece][move.to_sq()] + [type_of(pos.piece_on(move.to_sq()))] > 4146) extension = 1; } @@ -1145,7 +1147,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Update the current move (this must be done after singular extension search) ss->currentMove = move; ss->continuationHistory = - &thisThread->continuationHistory[ss->inCheck][capture][movedPiece][to_sq(move)]; + &thisThread->continuationHistory[ss->inCheck][capture][movedPiece][move.to_sq()]; // Step 16. Make the move pos.do_move(move, st, givesCheck); @@ -1187,10 +1189,10 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo else if (move == ttMove) r = 0; - ss->statScore = 2 * thisThread->mainHistory[us][from_to(move)] - + (*contHist[0])[movedPiece][to_sq(move)] - + (*contHist[1])[movedPiece][to_sq(move)] - + (*contHist[3])[movedPiece][to_sq(move)] - 3817; + ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + + (*contHist[0])[movedPiece][move.to_sq()] + + (*contHist[1])[movedPiece][move.to_sq()] + + (*contHist[3])[movedPiece][move.to_sq()] - 3817; // Decrease/increase reduction for moves with a good/bad history (~25 Elo) r -= ss->statScore / 14767; @@ -1229,7 +1231,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo : value >= beta ? stat_bonus(newDepth) : 0; - update_continuation_histories(ss, movedPiece, to_sq(move), bonus); + update_continuation_histories(ss, movedPiece, move.to_sq(), bonus); } } @@ -1249,7 +1251,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo if (PvNode && (moveCount == 1 || value > alpha)) { (ss + 1)->pv = pv; - (ss + 1)->pv[0] = MOVE_NONE; + (ss + 1)->pv[0] = Move::none(); value = -search(pos, ss + 1, -beta, -alpha, newDepth, false); } @@ -1296,7 +1298,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo assert((ss + 1)->pv); - for (Move* m = (ss + 1)->pv; *m != MOVE_NONE; ++m) + for (Move* m = (ss + 1)->pv; *m != Move::none(); ++m) rm.pv.push_back(*m); // We record how often the best move has been changed in each iteration. @@ -1375,7 +1377,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo + ((ss - 1)->moveCount > 10); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); - thisThread->mainHistory[~us][from_to((ss - 1)->currentMove)] + thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << stat_bonus(depth) * bonus / 2; } @@ -1451,11 +1453,11 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { if (PvNode) { (ss + 1)->pv = pv; - ss->pv[0] = MOVE_NONE; + ss->pv[0] = Move::none(); } Thread* thisThread = pos.this_thread(); - bestMove = MOVE_NONE; + bestMove = Move::none(); ss->inCheck = pos.checkers(); moveCount = 0; @@ -1476,7 +1478,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { posKey = pos.key(); tte = TT.probe(posKey, ss->ttHit); ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; - ttMove = ss->ttHit ? tte->move() : MOVE_NONE; + ttMove = ss->ttHit ? tte->move() : Move::none(); pvHit = ss->ttHit && tte->is_pv(); // At non-PV nodes we check for an early TT cutoff @@ -1513,7 +1515,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { { // In case of null move search, use previous static eval with a different sign unadjustedStaticEval = ss->staticEval = bestValue = - (ss - 1)->currentMove != MOVE_NULL ? evaluate(pos) : -(ss - 1)->staticEval; + (ss - 1)->currentMove != Move::null() ? evaluate(pos) : -(ss - 1)->staticEval; Value newEval = ss->staticEval @@ -1527,7 +1529,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { { if (!ss->ttHit) tte->save(posKey, value_to_tt(bestValue, ss->ply), false, BOUND_LOWER, DEPTH_NONE, - MOVE_NONE, unadjustedStaticEval); + Move::none(), unadjustedStaticEval); return bestValue; } @@ -1545,7 +1547,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { // to search the moves. Because the depth is <= 0 here, only captures, // queen promotions, and other checks (only if depth >= DEPTH_QS_CHECKS) // will be generated. - Square prevSq = is_ok((ss - 1)->currentMove) ? to_sq((ss - 1)->currentMove) : SQ_NONE; + Square prevSq = ((ss - 1)->currentMove).is_ok() ? ((ss - 1)->currentMove).to_sq() : SQ_NONE; MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory, contHist, &thisThread->pawnHistory); @@ -1553,9 +1555,9 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { // Step 5. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. - while ((move = mp.next_move()) != MOVE_NONE) + while ((move = mp.next_move()) != Move::none()) { - assert(is_ok(move)); + assert(move.is_ok()); // Check for legality if (!pos.legal(move)) @@ -1570,13 +1572,13 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { if (bestValue > VALUE_TB_LOSS_IN_MAX_PLY && pos.non_pawn_material(us)) { // Futility pruning and moveCount pruning (~10 Elo) - if (!givesCheck && to_sq(move) != prevSq && futilityBase > VALUE_TB_LOSS_IN_MAX_PLY - && type_of(move) != PROMOTION) + if (!givesCheck && move.to_sq() != prevSq && futilityBase > VALUE_TB_LOSS_IN_MAX_PLY + && move.type_of() != PROMOTION) { if (moveCount > 2) continue; - futilityValue = futilityBase + PieceValue[pos.piece_on(to_sq(move))]; + futilityValue = futilityBase + PieceValue[pos.piece_on(move.to_sq())]; // If static eval + value of piece we are going to capture is much lower // than alpha we can prune this move. @@ -1610,8 +1612,8 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { break; // Continuation history based pruning (~3 Elo) - if (!capture && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < 0 - && (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < 0) + if (!capture && (*contHist[0])[pos.moved_piece(move)][move.to_sq()] < 0 + && (*contHist[1])[pos.moved_piece(move)][move.to_sq()] < 0) continue; // Do not search moves with bad enough SEE values (~5 Elo) @@ -1626,7 +1628,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { ss->currentMove = move; ss->continuationHistory = &thisThread - ->continuationHistory[ss->inCheck][capture][pos.moved_piece(move)][to_sq(move)]; + ->continuationHistory[ss->inCheck][capture][pos.moved_piece(move)][move.to_sq()]; quietCheckEvasions += !capture && ss->inCheck; @@ -1738,9 +1740,9 @@ Value value_from_tt(Value v, int ply, int r50c) { // Adds current move and appends child pv[] void update_pv(Move* pv, Move move, const Move* childPv) { - for (*pv++ = move; childPv && *childPv != MOVE_NONE;) + for (*pv++ = move; childPv && *childPv != Move::none();) *pv++ = *childPv++; - *pv = MOVE_NONE; + *pv = Move::none(); } @@ -1775,25 +1777,25 @@ void update_all_stats(const Position& pos, update_quiet_stats(pos, ss, bestMove, bestMoveBonus); int pIndex = pawn_structure_index(pos); - thisThread->pawnHistory[pIndex][moved_piece][to_sq(bestMove)] << quietMoveBonus; + thisThread->pawnHistory[pIndex][moved_piece][bestMove.to_sq()] << quietMoveBonus; // Decrease stats for all non-best quiet moves for (int i = 0; i < quietCount; ++i) { thisThread - ->pawnHistory[pIndex][pos.moved_piece(quietsSearched[i])][to_sq(quietsSearched[i])] + ->pawnHistory[pIndex][pos.moved_piece(quietsSearched[i])][quietsSearched[i].to_sq()] << -quietMoveMalus; - thisThread->mainHistory[us][from_to(quietsSearched[i])] << -quietMoveMalus; + thisThread->mainHistory[us][quietsSearched[i].from_to()] << -quietMoveMalus; update_continuation_histories(ss, pos.moved_piece(quietsSearched[i]), - to_sq(quietsSearched[i]), -quietMoveMalus); + quietsSearched[i].to_sq(), -quietMoveMalus); } } else { // Increase stats for the best move in case it was a capture move - captured = type_of(pos.piece_on(to_sq(bestMove))); - captureHistory[moved_piece][to_sq(bestMove)][captured] << quietMoveBonus; + captured = type_of(pos.piece_on(bestMove.to_sq())); + captureHistory[moved_piece][bestMove.to_sq()][captured] << quietMoveBonus; } // Extra penalty for a quiet early move that was not a TT move or @@ -1808,8 +1810,8 @@ void update_all_stats(const Position& pos, for (int i = 0; i < captureCount; ++i) { moved_piece = pos.moved_piece(capturesSearched[i]); - captured = type_of(pos.piece_on(to_sq(capturesSearched[i]))); - captureHistory[moved_piece][to_sq(capturesSearched[i])][captured] << -quietMoveMalus; + captured = type_of(pos.piece_on(capturesSearched[i].to_sq())); + captureHistory[moved_piece][capturesSearched[i].to_sq()][captured] << -quietMoveMalus; } } @@ -1823,7 +1825,7 @@ void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { // Only update the first 2 continuation histories if we are in check if (ss->inCheck && i > 2) break; - if (is_ok((ss - i)->currentMove)) + if (((ss - i)->currentMove).is_ok()) (*(ss - i)->continuationHistory)[pc][to] << bonus / (1 + 3 * (i == 3)); } } @@ -1841,13 +1843,13 @@ void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus) { Color us = pos.side_to_move(); Thread* thisThread = pos.this_thread(); - thisThread->mainHistory[us][from_to(move)] << bonus; - update_continuation_histories(ss, pos.moved_piece(move), to_sq(move), bonus); + thisThread->mainHistory[us][move.from_to()] << bonus; + update_continuation_histories(ss, pos.moved_piece(move), move.to_sq(), bonus); // Update countermove history - if (is_ok((ss - 1)->currentMove)) + if (((ss - 1)->currentMove).is_ok()) { - Square prevSq = to_sq((ss - 1)->currentMove); + Square prevSq = ((ss - 1)->currentMove).to_sq(); thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] = move; } } @@ -1987,7 +1989,7 @@ bool RootMove::extract_ponder_from_tt(Position& pos) { assert(pv.size() == 1); - if (pv[0] == MOVE_NONE) + if (pv[0] == Move::none()) return false; pos.do_move(pv[0], st); diff --git a/src/thread.cpp b/src/thread.cpp index e900a9ac88f..01ccd4fc565 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include @@ -66,7 +66,7 @@ Thread::~Thread() { // Reset histories, usually before a new game void Thread::clear() { - counterMoves.fill(MOVE_NONE); + counterMoves.fill(Move::none()); mainHistory.fill(0); captureHistory.fill(0); pawnHistory.fill(0); @@ -220,9 +220,9 @@ void ThreadPool::start_thinking(Position& pos, Thread* ThreadPool::get_best_thread() const { - Thread* bestThread = threads.front(); - std::map votes; - Value minScore = VALUE_NONE; + Thread* bestThread = threads.front(); + std::unordered_map votes; + Value minScore = VALUE_NONE; // Find the minimum score of all threads for (Thread* th : threads) diff --git a/src/tt.cpp b/src/tt.cpp index 5c4e6d537fc..2e3f7d32835 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -39,7 +39,7 @@ void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) // Preserve any existing move for the same position if (m || uint16_t(k) != key16) - move16 = uint16_t(m); + move16 = m; // Overwrite less valuable entries (cheapest checks first) if (b == BOUND_EXACT || uint16_t(k) != key16 || d - DEPTH_OFFSET + 2 * pv > depth8 - 4) diff --git a/src/tt.h b/src/tt.h index 82a66863b9d..61e854c1af2 100644 --- a/src/tt.h +++ b/src/tt.h @@ -53,7 +53,7 @@ struct TTEntry { uint16_t key16; uint8_t depth8; uint8_t genBound8; - uint16_t move16; + Move move16; int16_t value16; int16_t eval16; }; diff --git a/src/types.h b/src/types.h index dde1a52c2a0..2970d1e00ce 100644 --- a/src/types.h +++ b/src/types.h @@ -108,30 +108,6 @@ using Bitboard = uint64_t; constexpr int MAX_MOVES = 256; constexpr int MAX_PLY = 246; -// A move needs 16 bits to be stored -// -// bit 0- 5: destination square (from 0 to 63) -// bit 6-11: origin square (from 0 to 63) -// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2) -// bit 14-15: special move flag: promotion (1), en passant (2), castling (3) -// NOTE: en passant bit is set only when a pawn can be captured -// -// Special cases are MOVE_NONE and MOVE_NULL. We can sneak these in because in -// any normal move destination square is always different from origin square -// while MOVE_NONE and MOVE_NULL have the same origin and destination square. - -enum Move : int { - MOVE_NONE, - MOVE_NULL = 65 -}; - -enum MoveType { - NORMAL, - PROMOTION = 1 << 14, - EN_PASSANT = 2 << 14, - CASTLING = 3 << 14 -}; - enum Color { WHITE, BLACK, @@ -353,8 +329,6 @@ inline Color color_of(Piece pc) { return Color(pc >> 3); } -constexpr bool is_ok(Move m) { return m != MOVE_NONE && m != MOVE_NULL; } - constexpr bool is_ok(Square s) { return s >= SQ_A1 && s <= SQ_H8; } constexpr File file_of(Square s) { return File(s & 7); } @@ -369,33 +343,80 @@ constexpr Rank relative_rank(Color c, Square s) { return relative_rank(c, rank_o constexpr Direction pawn_push(Color c) { return c == WHITE ? NORTH : SOUTH; } -constexpr Square from_sq(Move m) { - assert(is_ok(m)); - return Square((m >> 6) & 0x3F); -} -constexpr Square to_sq(Move m) { - assert(is_ok(m)); - return Square(m & 0x3F); +// Based on a congruential pseudo-random number generator +constexpr Key make_key(uint64_t seed) { + return seed * 6364136223846793005ULL + 1442695040888963407ULL; } -constexpr int from_to(Move m) { return m & 0xFFF; } -constexpr MoveType type_of(Move m) { return MoveType(m & (3 << 14)); } +enum MoveType { + NORMAL, + PROMOTION = 1 << 14, + EN_PASSANT = 2 << 14, + CASTLING = 3 << 14 +}; + +// A move needs 16 bits to be stored +// +// bit 0- 5: destination square (from 0 to 63) +// bit 6-11: origin square (from 0 to 63) +// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2) +// bit 14-15: special move flag: promotion (1), en passant (2), castling (3) +// NOTE: en passant bit is set only when a pawn can be captured +// +// Special cases are Move::none() and Move::null(). We can sneak these in because in +// any normal move destination square is always different from origin square +// while Move::none() and Move::null() have the same origin and destination square. +class Move { + public: + Move() = default; + constexpr explicit Move(std::uint16_t d) : + data(d) {} + + constexpr Move(Square from, Square to) : + data((from << 6) + to) {} -constexpr PieceType promotion_type(Move m) { return PieceType(((m >> 12) & 3) + KNIGHT); } + template + static constexpr Move make(Square from, Square to, PieceType pt = KNIGHT) { + return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to); + } -constexpr Move make_move(Square from, Square to) { return Move((from << 6) + to); } + constexpr Square from_sq() const { + assert(is_ok()); + return Square((data >> 6) & 0x3F); + } -template -constexpr Move make(Square from, Square to, PieceType pt = KNIGHT) { - return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to); -} + constexpr Square to_sq() const { + assert(is_ok()); + return Square(data & 0x3F); + } -// Based on a congruential pseudo-random number generator -constexpr Key make_key(uint64_t seed) { - return seed * 6364136223846793005ULL + 1442695040888963407ULL; -} + constexpr int from_to() const { return data & 0xFFF; } + + constexpr MoveType type_of() const { return MoveType(data & (3 << 14)); } + + constexpr PieceType promotion_type() const { return PieceType(((data >> 12) & 3) + KNIGHT); } + + constexpr bool is_ok() const { return none().data != data && null().data != data; } + + static constexpr Move null() { return Move(65); } + static constexpr Move none() { return Move(0); } + + constexpr bool operator==(const Move& m) const { return data == m.data; } + constexpr bool operator!=(const Move& m) const { return data != m.data; } + + constexpr explicit operator bool() const { return data != 0; } + + constexpr std::uint16_t raw() const { return data; } + + struct MoveHash { + std::size_t operator()(const Move& m) const { return m.data; } + }; + + protected: + std::uint16_t data; +}; } // namespace Stockfish diff --git a/src/uci.cpp b/src/uci.cpp index 5dc9b2b0690..8e93eee6dc5 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -75,7 +75,7 @@ void position(Position& pos, std::istringstream& is, StateListPtr& states) { pos.set(fen, Options["UCI_Chess960"], &states->back(), Threads.main()); // Parse the move list, if any - while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE) + while (is >> token && (m = UCI::to_move(pos, token)) != Move::none()) { states->emplace_back(); pos.do_move(m, states->back()); @@ -395,22 +395,22 @@ std::string UCI::square(Square s) { // Internally, all castling moves are always encoded as 'king captures rook'. std::string UCI::move(Move m, bool chess960) { - if (m == MOVE_NONE) + if (m == Move::none()) return "(none)"; - if (m == MOVE_NULL) + if (m == Move::null()) return "0000"; - Square from = from_sq(m); - Square to = to_sq(m); + Square from = m.from_sq(); + Square to = m.to_sq(); - if (type_of(m) == CASTLING && !chess960) + if (m.type_of() == CASTLING && !chess960) to = make_square(to > from ? FILE_G : FILE_C, rank_of(from)); std::string move = UCI::square(from) + UCI::square(to); - if (type_of(m) == PROMOTION) - move += " pnbrqk"[promotion_type(m)]; + if (m.type_of() == PROMOTION) + move += " pnbrqk"[m.promotion_type()]; return move; } @@ -427,7 +427,7 @@ Move UCI::to_move(const Position& pos, std::string& str) { if (str == UCI::move(m, pos.is_chess960())) return m; - return MOVE_NONE; + return Move::none(); } } // namespace Stockfish From 49308929852731e118b2c6d5d4232e930182cc11 Mon Sep 17 00:00:00 2001 From: RainRat Date: Tue, 2 Jan 2024 23:01:24 -0800 Subject: [PATCH 1311/1766] Fix typo in tbprobe.cpp closes https://github.com/official-stockfish/Stockfish/pull/4959 No functional change --- src/syzygy/tbprobe.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index c1275cf5755..91013dcac28 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -863,7 +863,7 @@ do_probe_table(const Position& pos, T* entry, WDLScore wdl, ProbeState* result) int adjust2 = (squares[2] > squares[0]) + (squares[2] > squares[1]); // First piece is below a1-h8 diagonal. MapA1D1D4[] maps the b1-d1-d3 - // triangle to 0...5. There are 63 squares for second piece and and 62 + // triangle to 0...5. There are 63 squares for second piece and 62 // (mapped to 0...61) for the third. if (off_A1H8(squares[0])) idx = (MapA1D1D4[squares[0]] * 63 + (squares[1] - adjust1)) * 62 + squares[2] - adjust2; From b987d4f0332f57a58157641bf3a6e437133e7879 Mon Sep 17 00:00:00 2001 From: Disservin Date: Mon, 1 Jan 2024 20:45:14 +0100 Subject: [PATCH 1312/1766] Use type aliases instead of enums for Value types The primary rationale behind this lies in the fact that enums were not originally designed to be employed in the manner we currently utilize them. The Value enum was used like a type alias throughout the code and was often misused. Furthermore, changing the underlying size of the enum to int16_t broke everything, mostly because of the operator overloads for the Value enum, were causing data to be truncated. Since Value is now a type alias, the operator overloads are no longer required. Passed Non-Regression STC: https://tests.stockfishchess.org/tests/view/6593b8bb79aa8af82b95b401 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 235296 W: 59919 L: 59917 D: 115460 Ptnml(0-2): 743, 27085, 62054, 26959, 807 closes https://github.com/official-stockfish/Stockfish/pull/4960 No functional change --- src/evaluate.cpp | 10 ++++---- src/evaluate.h | 2 +- src/movepick.cpp | 9 ++++--- src/movepick.h | 4 ++-- src/nnue/evaluate_nnue.h | 2 +- src/position.cpp | 2 +- src/position.h | 2 +- src/search.cpp | 26 ++++++++++---------- src/thread.h | 6 +++-- src/tune.cpp | 15 ------------ src/tune.h | 4 +--- src/types.h | 51 +++++++++++++++++++++------------------- 12 files changed, 60 insertions(+), 73 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index b6342f189b8..bda7132a1ff 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -148,7 +148,7 @@ void NNUE::verify() { // Returns a static, purely materialistic evaluation of the position from // the point of view of the given color. It can be divided by PawnValue to get // an approximation of the material advantage on the board in terms of pawns. -Value Eval::simple_eval(const Position& pos, Color c) { +int Eval::simple_eval(const Position& pos, Color c) { return PawnValue * (pos.count(c) - pos.count(~c)) + (pos.non_pawn_material(c) - pos.non_pawn_material(~c)); } @@ -160,7 +160,7 @@ Value Eval::evaluate(const Position& pos) { assert(!pos.checkers()); - Value v; + int v; Color stm = pos.side_to_move(); int shuffling = pos.rule50_count(); int simpleEval = simple_eval(pos, stm) + (int(pos.key() & 7) - 3); @@ -170,13 +170,13 @@ Value Eval::evaluate(const Position& pos) { + std::abs(pos.this_thread()->rootSimpleEval); if (lazy) - v = Value(simpleEval); + v = simpleEval; else { int nnueComplexity; Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); - Value optimism = pos.this_thread()->optimism[stm]; + int optimism = pos.this_thread()->optimism[stm]; // Blend optimism and eval with nnue complexity and material imbalance optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / 512; @@ -190,7 +190,7 @@ Value Eval::evaluate(const Position& pos) { v = v * (200 - shuffling) / 214; // Guarantee evaluation does not hit the tablebase range - v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); + v = std::clamp(int(v), VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); return v; } diff --git a/src/evaluate.h b/src/evaluate.h index c2b08aaf1b0..0a7ec61a3cf 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -31,7 +31,7 @@ namespace Eval { std::string trace(Position& pos); -Value simple_eval(const Position& pos, Color c); +int simple_eval(const Position& pos, Color c); Value evaluate(const Position& pos); extern std::string currentEvalFileName; diff --git a/src/movepick.cpp b/src/movepick.cpp index cae018915f0..14b6c87a744 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -127,7 +127,7 @@ MovePicker::MovePicker(const Position& p, // Constructor for ProbCut: we generate captures with SEE greater // than or equal to the given threshold. -MovePicker::MovePicker(const Position& p, Move ttm, Value th, const CapturePieceToHistory* cph) : +MovePicker::MovePicker(const Position& p, Move ttm, int th, const CapturePieceToHistory* cph) : pos(p), captureHistory(cph), ttMove(ttm), @@ -211,8 +211,8 @@ void MovePicker::score() { else // Type == EVASIONS { if (pos.capture_stage(m)) - m.value = PieceValue[pos.piece_on(m.to_sq())] - Value(type_of(pos.moved_piece(m))) - + (1 << 28); + m.value = + PieceValue[pos.piece_on(m.to_sq())] - type_of(pos.moved_piece(m)) + (1 << 28); else m.value = (*mainHistory)[pos.side_to_move()][m.from_to()] + (*continuationHistory[0])[pos.moved_piece(m)][m.to_sq()] @@ -268,8 +268,7 @@ Move MovePicker::next_move(bool skipQuiets) { case GOOD_CAPTURE : if (select([&]() { // Move losing capture to endBadCaptures to be tried later - return pos.see_ge(*cur, Value(-cur->value)) ? true - : (*endBadCaptures++ = *cur, false); + return pos.see_ge(*cur, -cur->value) ? true : (*endBadCaptures++ = *cur, false); })) return *(cur - 1); diff --git a/src/movepick.h b/src/movepick.h index ad4be8e9a9c..c429f8aec9a 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -171,7 +171,7 @@ class MovePicker { const CapturePieceToHistory*, const PieceToHistory**, const PawnHistory*); - MovePicker(const Position&, Move, Value, const CapturePieceToHistory*); + MovePicker(const Position&, Move, int, const CapturePieceToHistory*); Move next_move(bool skipQuiets = false); private: @@ -190,7 +190,7 @@ class MovePicker { Move ttMove; ExtMove refutations[3], *cur, *endMoves, *endBadCaptures; int stage; - Value threshold; + int threshold; Depth depth; ExtMove moves[MAX_MOVES]; }; diff --git a/src/nnue/evaluate_nnue.h b/src/nnue/evaluate_nnue.h index 05c98bc5380..f80aa398bba 100644 --- a/src/nnue/evaluate_nnue.h +++ b/src/nnue/evaluate_nnue.h @@ -30,10 +30,10 @@ #include "../misc.h" #include "nnue_architecture.h" #include "nnue_feature_transformer.h" +#include "../types.h" namespace Stockfish { class Position; -enum Value : int; } namespace Stockfish::Eval::NNUE { diff --git a/src/position.cpp b/src/position.cpp index 810bba57d6f..4fba3c234b7 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1029,7 +1029,7 @@ Key Position::key_after(Move m) const { // Tests if the SEE (Static Exchange Evaluation) // value of move is greater or equal to the given threshold. We'll use an // algorithm similar to alpha-beta pruning with a null window. -bool Position::see_ge(Move m, Value threshold) const { +bool Position::see_ge(Move m, int threshold) const { assert(m.is_ok()); diff --git a/src/position.h b/src/position.h index 3e9327592fa..7e0c3eefd77 100644 --- a/src/position.h +++ b/src/position.h @@ -141,7 +141,7 @@ class Position { void undo_null_move(); // Static Exchange Evaluation - bool see_ge(Move m, Value threshold = VALUE_ZERO) const; + bool see_ge(Move m, int threshold = 0) const; // Accessing hash keys Key key() const; diff --git a/src/search.cpp b/src/search.cpp index 0d41f48d981..9dc4ee988dd 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -77,13 +77,13 @@ enum NodeType { // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving) { - return Value((116 - 44 * noTtCutNode) * (d - improving)); + return ((116 - 44 * noTtCutNode) * (d - improving)); } // Reductions lookup table initialized at startup int Reductions[MAX_MOVES]; // [depth or moveNumber] -Depth reduction(bool i, Depth d, int mn, Value delta, Value rootDelta) { +Depth reduction(bool i, Depth d, int mn, int delta, int rootDelta) { int reductionScale = Reductions[d] * Reductions[mn]; return (reductionScale + 1346 - int(delta) * 896 / int(rootDelta)) / 1024 + (!i && reductionScale > 880); @@ -95,7 +95,7 @@ constexpr int futility_move_count(bool improving, Depth depth) { // Guarantee evaluation does not hit the tablebase range constexpr Value to_static_eval(const Value v) { - return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); + return std::clamp(int(v), VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } // History and stats update bonus, based on depth @@ -292,13 +292,13 @@ void Thread::search() { // (ss + 2) is needed for initialization of cutOffCnt and killers. Stack stack[MAX_PLY + 10], *ss = stack + 7; Move pv[MAX_PLY + 1]; - Value alpha, beta, delta; + Value alpha, beta; Move lastBestMove = Move::none(); Depth lastBestMoveDepth = 0; MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr); double timeReduction = 1, totBestMoveChanges = 0; - Color us = rootPos.side_to_move(); - int iterIdx = 0; + Color us = rootPos.side_to_move(); + int delta, iterIdx = 0; std::memset(ss - 7, 0, 10 * sizeof(Stack)); for (int i = 7; i > 0; --i) @@ -374,7 +374,7 @@ void Thread::search() { Value avg = rootMoves[pvIdx].averageScore; delta = Value(9) + int(avg) * avg / 14847; alpha = std::max(avg - delta, -VALUE_INFINITE); - beta = std::min(avg + delta, VALUE_INFINITE); + beta = std::min(avg + delta, int(VALUE_INFINITE)); // Adjust optimism based on root move's averageScore (~4 Elo) optimism[us] = 121 * avg / (std::abs(avg) + 109); @@ -425,7 +425,7 @@ void Thread::search() { } else if (bestValue >= beta) { - beta = std::min(bestValue + delta, VALUE_INFINITE); + beta = std::min(bestValue + delta, int(VALUE_INFINITE)); ++failedHighCnt; } else @@ -989,7 +989,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Calculate new depth for this move newDepth = depth - 1; - Value delta = beta - alpha; + int delta = beta - alpha; Depth r = reduction(improving, depth, moveCount, delta, thisThread->rootDelta); @@ -1018,7 +1018,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo } // SEE based pruning for captures and checks (~11 Elo) - if (!pos.see_ge(move, Value(-187) * depth)) + if (!pos.see_ge(move, -187 * depth)) continue; } else @@ -1048,7 +1048,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo lmrDepth = std::max(lmrDepth, 0); // Prune moves with negative SEE (~4 Elo) - if (!pos.see_ge(move, Value(-26 * lmrDepth * lmrDepth))) + if (!pos.see_ge(move, -26 * lmrDepth * lmrDepth)) continue; } } @@ -1617,7 +1617,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { continue; // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, Value(-77))) + if (!pos.see_ge(move, -77)) continue; } @@ -1863,7 +1863,7 @@ Move Skill::pick_best(size_t multiPV) { // RootMoves are already sorted by score in descending order Value topScore = rootMoves[0].score; - int delta = std::min(topScore - rootMoves[multiPV - 1].score, PawnValue); + int delta = std::min(topScore - rootMoves[multiPV - 1].score, int(PawnValue)); int maxScore = -VALUE_INFINITE; double weakness = 120 - 2 * level; diff --git a/src/thread.h b/src/thread.h index 22fe32c3a12..7db7c15905b 100644 --- a/src/thread.h +++ b/src/thread.h @@ -56,13 +56,15 @@ class Thread { size_t pvIdx, pvLast; std::atomic nodes, tbHits, bestMoveChanges; int selDepth, nmpMinPly; - Value bestValue, optimism[COLOR_NB]; + Value bestValue; + + int optimism[COLOR_NB]; Position rootPos; StateInfo rootState; Search::RootMoves rootMoves; Depth rootDepth, completedDepth; - Value rootDelta; + int rootDelta; Value rootSimpleEval; CounterMoveHistory counterMoves; ButterflyHistory mainHistory; diff --git a/src/tune.cpp b/src/tune.cpp index 44bfa682a7d..1dddca0c37d 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -26,10 +26,6 @@ #include "uci.h" -namespace Stockfish { -enum Value : int; -} - using std::string; namespace Stockfish { @@ -92,17 +88,6 @@ void Tune::Entry::read_option() { value = int(Options[name]); } -template<> -void Tune::Entry::init_option() { - make_option(name, value, range); -} - -template<> -void Tune::Entry::read_option() { - if (Options.count(name)) - value = Value(int(Options[name])); -} - // Instead of a variable here we have a PostUpdate function: just call it template<> void Tune::Entry::init_option() {} diff --git a/src/tune.h b/src/tune.h index 3d45e51c19e..17057001225 100644 --- a/src/tune.h +++ b/src/tune.h @@ -27,7 +27,6 @@ #include namespace Stockfish { -enum Value : int; using Range = std::pair; // Option's min-max values using RangeFun = Range(int); @@ -101,8 +100,7 @@ class Tune { static_assert(!std::is_const_v, "Parameter cannot be const!"); - static_assert(std::is_same_v || std::is_same_v - || std::is_same_v, + static_assert(std::is_same_v || std::is_same_v, "Parameter type not supported!"); Entry(const std::string& n, T& v, const SetRange& r) : diff --git a/src/types.h b/src/types.h index 2970d1e00ce..ca9ef6152ba 100644 --- a/src/types.h +++ b/src/types.h @@ -137,29 +137,33 @@ enum Bound { BOUND_EXACT = BOUND_UPPER | BOUND_LOWER }; -enum Value : int { - VALUE_ZERO = 0, - VALUE_DRAW = 0, - VALUE_NONE = 32002, - VALUE_INFINITE = 32001, - - VALUE_MATE = 32000, - VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY, - VALUE_MATED_IN_MAX_PLY = -VALUE_MATE_IN_MAX_PLY, - - VALUE_TB = VALUE_MATE_IN_MAX_PLY - 1, - VALUE_TB_WIN_IN_MAX_PLY = VALUE_TB - MAX_PLY, - VALUE_TB_LOSS_IN_MAX_PLY = -VALUE_TB_WIN_IN_MAX_PLY, - - // In the code, we make the assumption that these values - // are such that non_pawn_material() can be used to uniquely - // identify the material on the board. - PawnValue = 208, - KnightValue = 781, - BishopValue = 825, - RookValue = 1276, - QueenValue = 2538, -}; +// Value is used as an alias for int16_t, this is done to differentiate between +// a search value and any other integer value. The values used in search are always +// supposed to be in the range (-VALUE_NONE, VALUE_NONE] and should not exceed this range. +using Value = int; + +constexpr Value VALUE_ZERO = 0; +constexpr Value VALUE_DRAW = 0; +constexpr Value VALUE_NONE = 32002; +constexpr Value VALUE_INFINITE = 32001; + +constexpr Value VALUE_MATE = 32000; +constexpr Value VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY; +constexpr Value VALUE_MATED_IN_MAX_PLY = -VALUE_MATE_IN_MAX_PLY; + +constexpr Value VALUE_TB = VALUE_MATE_IN_MAX_PLY - 1; +constexpr Value VALUE_TB_WIN_IN_MAX_PLY = VALUE_TB - MAX_PLY; +constexpr Value VALUE_TB_LOSS_IN_MAX_PLY = -VALUE_TB_WIN_IN_MAX_PLY; + +// In the code, we make the assumption that these values +// are such that non_pawn_material() can be used to uniquely +// identify the material on the board. +constexpr Value PawnValue = 208; +constexpr Value KnightValue = 781; +constexpr Value BishopValue = 825; +constexpr Value RookValue = 1276; +constexpr Value QueenValue = 2538; + // clang-format off enum PieceType { @@ -280,7 +284,6 @@ struct DirtyPiece { 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(Direction) ENABLE_INCR_OPERATORS_ON(PieceType) From 8b4583bce76da7d27aaa565e6302d2e540cd496a Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Wed, 3 Jan 2024 17:32:57 +0300 Subject: [PATCH 1313/1766] Remove redundant int cast Remove a redundant int cast in the calculation of fwdOut. The variable OutputType is already defined as std::int32_t, which is an integer type, making the cast unnecessary. closes https://github.com/official-stockfish/Stockfish/pull/4961 No functional change --- src/nnue/nnue_architecture.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index 6c0e52b738e..92445704296 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -116,7 +116,7 @@ struct Network { // buffer.fc_0_out[FC_0_OUTPUTS] is such that 1.0 is equal to 127*(1< Date: Fri, 5 Jan 2024 21:03:19 +0800 Subject: [PATCH 1314/1766] Remove unneeded operator overload macros Only Direction type is using two of the enable overload macros. Aside from this, only two of the overloads are even being used. Therefore, we can just define the needed overloads and remove the macros. closes https://github.com/official-stockfish/Stockfish/pull/4966 No functional change. --- src/types.h | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/src/types.h b/src/types.h index ca9ef6152ba..e83b306dc73 100644 --- a/src/types.h +++ b/src/types.h @@ -264,36 +264,19 @@ struct DirtyPiece { Square to[3]; }; - #define ENABLE_BASE_OPERATORS_ON(T) \ - constexpr T operator+(T d1, int d2) { return T(int(d1) + d2); } \ - constexpr T operator-(T d1, int d2) { return T(int(d1) - d2); } \ - constexpr T operator-(T d) { return T(-int(d)); } \ - inline T& operator+=(T& d1, int d2) { return d1 = d1 + d2; } \ - inline T& operator-=(T& d1, int d2) { return d1 = d1 - d2; } - #define ENABLE_INCR_OPERATORS_ON(T) \ inline T& operator++(T& d) { return d = T(int(d) + 1); } \ inline T& operator--(T& d) { return d = T(int(d) - 1); } - #define ENABLE_FULL_OPERATORS_ON(T) \ - ENABLE_BASE_OPERATORS_ON(T) \ - constexpr T operator*(int i, T d) { return T(i * int(d)); } \ - constexpr T operator*(T d, int i) { return T(int(d) * i); } \ - constexpr T operator/(T d, int i) { return T(int(d) / i); } \ - constexpr int operator/(T d1, T d2) { return int(d1) / int(d2); } \ - 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(Direction) - ENABLE_INCR_OPERATORS_ON(PieceType) ENABLE_INCR_OPERATORS_ON(Square) ENABLE_INCR_OPERATORS_ON(File) ENABLE_INCR_OPERATORS_ON(Rank) - #undef ENABLE_FULL_OPERATORS_ON #undef ENABLE_INCR_OPERATORS_ON - #undef ENABLE_BASE_OPERATORS_ON + +constexpr Direction operator+(Direction d1, Direction d2) { return Direction(int(d1) + int(d2)); } +constexpr Direction operator*(int i, Direction d) { return Direction(i * int(d)); } // Additional operators to add a Direction to a Square constexpr Square operator+(Square s, Direction d) { return Square(int(s) + int(d)); } From 6f9071c64354a34970e7b5669701d0ad15b7a694 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 6 Jan 2024 07:42:53 +0300 Subject: [PATCH 1315/1766] Tweak usage of correction history Instead of using linear formula use quadratic one. Maximum impact of correction history is doubled this way, it breaks even with previous formula on half of maximum value. Passed STC: https://tests.stockfishchess.org/tests/view/659591e579aa8af82b95d7e8 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 225216 W: 57616 L: 57019 D: 110581 Ptnml(0-2): 747, 26677, 57201, 27198, 785 Passed LTC: https://tests.stockfishchess.org/tests/view/6596ee0b79aa8af82b95f08a LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 73314 W: 18524 L: 18125 D: 36665 Ptnml(0-2): 41, 8159, 19875, 8524, 58 closes https://github.com/official-stockfish/Stockfish/pull/4967 Bench: 1464785 --- src/search.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 9dc4ee988dd..e93b12d1598 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -745,7 +745,9 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo Value newEval = ss->staticEval - + thisThread->correctionHistory[us][pawn_structure_index(pos)] / 32; + + thisThread->correctionHistory[us][pawn_structure_index(pos)] + * std::abs(thisThread->correctionHistory[us][pawn_structure_index(pos)]) + / 16384; ss->staticEval = eval = to_static_eval(newEval); @@ -759,7 +761,9 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo Value newEval = ss->staticEval - + thisThread->correctionHistory[us][pawn_structure_index(pos)] / 32; + + thisThread->correctionHistory[us][pawn_structure_index(pos)] + * std::abs(thisThread->correctionHistory[us][pawn_structure_index(pos)]) + / 16384; ss->staticEval = eval = to_static_eval(newEval); @@ -1502,7 +1506,10 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { Value newEval = ss->staticEval - + thisThread->correctionHistory[us][pawn_structure_index(pos)] / 32; + + thisThread->correctionHistory[us][pawn_structure_index(pos)] + * std::abs( + thisThread->correctionHistory[us][pawn_structure_index(pos)]) + / 16384; ss->staticEval = bestValue = to_static_eval(newEval); @@ -1519,7 +1526,10 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { Value newEval = ss->staticEval - + thisThread->correctionHistory[us][pawn_structure_index(pos)] / 32; + + thisThread->correctionHistory[us][pawn_structure_index(pos)] + * std::abs( + thisThread->correctionHistory[us][pawn_structure_index(pos)]) + / 16384; ss->staticEval = bestValue = to_static_eval(newEval); } From 19f9a197be95395f761304310f9792d40b05307c Mon Sep 17 00:00:00 2001 From: Disservin Date: Sat, 6 Jan 2024 19:36:17 +0100 Subject: [PATCH 1316/1766] Add .git-blame-ignore-revs Add a `.git-blame-ignore-revs` file which can be used to skip specified commits when blaming, this is useful to ignore formatting commits, like clang-format #4790. Github blame automatically supports this file format, as well as other third party tools. Git itself needs to be told about the file name to work, the following command will add it to the current git repo. `git config blame.ignoreRevsFile .git-blame-ignore-revs`, alternatively one has to specify it with every blame. `git blame --ignore-revs-file .git-blame-ignore-revs search.cpp` Supported since git 2.23. closes https://github.com/official-stockfish/Stockfish/pull/4969 No functional change --- .git-blame-ignore-revs | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .git-blame-ignore-revs diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 00000000000..d2d6cfe67ba --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,7 @@ +# .git-blame-ignore-revs +# Ignore commit which added clang-format +2d0237db3f0e596fb06e3ffbadba84dcc4e018f6 + +# Post commit formatting fixes +0fca5605fa2e5e7240fde5e1aae50952b2612231 +08ed4c90db31959521b7ef3186c026edd1e90307 \ No newline at end of file From a5a76a63704009d35997725558dfafd90f5d616f Mon Sep 17 00:00:00 2001 From: mstembera Date: Sat, 6 Jan 2024 19:29:27 -0800 Subject: [PATCH 1317/1766] Introduce BAD_QUIET movepicker stage Split quiets into good and bad as we do with captures. When we find the first quiet move below a certain threshold that has been sorted we consider all subsequent quiets bad. Inspired by @locutus2 idea to skip bad captures. Passed STC: https://tests.stockfishchess.org/tests/view/6597759f79aa8af82b95fa17 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 138688 W: 35566 L: 35096 D: 68026 Ptnml(0-2): 476, 16367, 35183, 16847, 471 Passed LTC: https://tests.stockfishchess.org/tests/view/6598583c79aa8af82b960ad0 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 84108 W: 21468 L: 21048 D: 41592 Ptnml(0-2): 38, 9355, 22858, 9755, 48 closes https://github.com/official-stockfish/Stockfish/pull/4970 Bench: 1336907 --- src/movepick.cpp | 45 +++++++++++++++++++++++++++++++++++++++------ src/movepick.h | 10 +++++----- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 14b6c87a744..6a5629961e3 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -37,8 +37,9 @@ enum Stages { GOOD_CAPTURE, REFUTATION, QUIET_INIT, - QUIET, + GOOD_QUIET, BAD_CAPTURE, + BAD_QUIET, // generate evasion moves EVASION_TT, @@ -243,6 +244,8 @@ Move MovePicker::select(Pred filter) { // moves left, picking the move with the highest score from a list of generated moves. Move MovePicker::next_move(bool skipQuiets) { + auto quiet_threshold = [](Depth d) { return -3330 * d; }; + top: switch (stage) { @@ -295,20 +298,34 @@ Move MovePicker::next_move(bool skipQuiets) { if (!skipQuiets) { cur = endBadCaptures; - endMoves = generate(pos, cur); + endMoves = beginBadQuiets = endBadQuiets = generate(pos, cur); score(); - partial_insertion_sort(cur, endMoves, -3330 * depth); + partial_insertion_sort(cur, endMoves, quiet_threshold(depth)); } ++stage; [[fallthrough]]; - case QUIET : + case GOOD_QUIET : if (!skipQuiets && select([&]() { return *cur != refutations[0] && *cur != refutations[1] && *cur != refutations[2]; })) - return *(cur - 1); + { + Move tmp = *(cur - 1); + if ((cur - 1)->value < -7500 && (cur - 1)->value > quiet_threshold(depth)) + { + // Remaining quiets are bad + beginBadQuiets = cur; + + // Prepare the pointers to loop over the bad captures + cur = moves; + endMoves = endBadCaptures; + + ++stage; + } + return tmp; + } // Prepare the pointers to loop over the bad captures cur = moves; @@ -318,7 +335,23 @@ Move MovePicker::next_move(bool skipQuiets) { [[fallthrough]]; case BAD_CAPTURE : - return select([]() { return true; }); + if (select([]() { return true; })) + return *(cur - 1); + + // Prepare the pointers to loop over the bad quiets + cur = beginBadQuiets; + endMoves = endBadQuiets; + + ++stage; + [[fallthrough]]; + + case BAD_QUIET : + if (!skipQuiets) + return select([&]() { + return *cur != refutations[0] && *cur != refutations[1] && *cur != refutations[2]; + }); + + return Move::none(); case EVASION_INIT : cur = moves; diff --git a/src/movepick.h b/src/movepick.h index c429f8aec9a..357918a90f2 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -188,11 +188,11 @@ class MovePicker { const PieceToHistory** continuationHistory; const PawnHistory* pawnHistory; Move ttMove; - ExtMove refutations[3], *cur, *endMoves, *endBadCaptures; - int stage; - int threshold; - Depth depth; - ExtMove moves[MAX_MOVES]; + ExtMove refutations[3], *cur, *endMoves, *endBadCaptures, *beginBadQuiets, *endBadQuiets; + int stage; + int threshold; + Depth depth; + ExtMove moves[MAX_MOVES]; }; } // namespace Stockfish From 584d9efedcde330eeb96a99215552ddfb06f52ba Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Sat, 2 Dec 2023 17:50:32 -0800 Subject: [PATCH 1318/1766] Dual NNUE with L1-128 smallnet Credit goes to @mstembera for: - writing the code enabling dual NNUE: https://github.com/official-stockfish/Stockfish/pull/4898 - the idea of trying L1-128 trained exclusively on high simple eval positions The L1-128 smallnet is: - epoch 399 of a single-stage training from scratch - trained only on positions from filtered data with high material difference - defined by abs(simple_eval) > 1000 ```yaml experiment-name: 128--S1-only-hse-v2 training-dataset: - /data/hse/S3/dfrc99-16tb7p-eval-filt-v2.min.high-simple-eval-1k.binpack - /data/hse/S3/leela96-filt-v2.min.high-simple-eval-1k.binpack - /data/hse/S3/test80-apr2022-16tb7p.min.high-simple-eval-1k.binpack - /data/hse/S7/test60-2020-2tb7p.v6-3072.high-simple-eval-1k.binpack - /data/hse/S7/test60-novdec2021-12tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.binpack - /data/hse/S7/test77-nov2021-2tb7p.v6-3072.min.high-simple-eval-1k.binpack - /data/hse/S7/test77-dec2021-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.binpack - /data/hse/S7/test77-jan2022-2tb7p.high-simple-eval-1k.binpack - /data/hse/S7/test78-jantomay2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.binpack - /data/hse/S7/test78-juntosep2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.binpack - /data/hse/S7/test79-apr2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.binpack - /data/hse/S7/test79-may2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.binpack # T80 2022 - /data/hse/S7/test80-may2022-16tb7p.high-simple-eval-1k.binpack - /data/hse/S7/test80-jun2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.binpack - /data/hse/S7/test80-jul2022-16tb7p.v6-dd.min.high-simple-eval-1k.binpack - /data/hse/S7/test80-aug2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.binpack - /data/hse/S7/test80-sep2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.binpack - /data/hse/S7/test80-oct2022-16tb7p.v6-dd.high-simple-eval-1k.binpack - /data/hse/S7/test80-nov2022-16tb7p-v6-dd.min.high-simple-eval-1k.binpack # T80 2023 - /data/hse/S7/test80-jan2023-3of3-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.binpack - /data/hse/S7/test80-feb2023-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.binpack - /data/hse/S7/test80-mar2023-2tb7p.v6-sk16.min.high-simple-eval-1k.binpack - /data/hse/S7/test80-apr2023-2tb7p-filter-v6-sk16.min.high-simple-eval-1k.binpack - /data/hse/S7/test80-may2023-2tb7p.v6.min.high-simple-eval-1k.binpack - /data/hse/S7/test80-jun2023-2tb7p.v6-3072.min.high-simple-eval-1k.binpack - /data/hse/S7/test80-jul2023-2tb7p.v6-3072.min.high-simple-eval-1k.binpack - /data/hse/S7/test80-aug2023-2tb7p.v6.min.high-simple-eval-1k.binpack - /data/hse/S7/test80-sep2023-2tb7p.high-simple-eval-1k.binpack - /data/hse/S7/test80-oct2023-2tb7p.high-simple-eval-1k.binpack start-from-engine-test-net: False nnue-pytorch-branch: linrock/nnue-pytorch/L1-128 engine-test-branch: linrock/Stockfish/L1-128-nolazy engine-base-branch: linrock/Stockfish/L1-128 num-epochs: 500 lambda: 1.0 ``` Experiment yaml configs converted to easy_train.sh commands with: https://github.com/linrock/nnue-tools/blob/4339954/yaml_easy_train.py Binpacks interleaved at training time with: https://github.com/official-stockfish/nnue-pytorch/pull/259 Data filtered for high simple eval positions with: https://github.com/linrock/nnue-data/blob/32d6a68/filter_high_simple_eval_plain.py https://github.com/linrock/Stockfish/blob/61dbfe/src/tools/transform.cpp#L626-L655 Training data can be found at: https://robotmoon.com/nnue-training-data/ Local elo at 25k nodes per move of L1-128 smallnet (nnue-only eval) vs. L1-128 trained on standard S1 data: nn-epoch399.nnue : -318.1 +/- 2.1 Passed STC: https://tests.stockfishchess.org/tests/view/6574cb9d95ea6ba1fcd49e3b LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 62432 W: 15875 L: 15521 D: 31036 Ptnml(0-2): 177, 7331, 15872, 7633, 203 Passed LTC: https://tests.stockfishchess.org/tests/view/6575da2d4d789acf40aaac6e LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 64830 W: 16118 L: 15738 D: 32974 Ptnml(0-2): 43, 7129, 17697, 7497, 49 closes https://github.com/official-stockfish/Stockfish/pulls Bench: 1330050 Co-Authored-By: mstembera <5421953+mstembera@users.noreply.github.com> --- src/Makefile | 32 ++++-- src/evaluate.cpp | 163 ++++++++++++++++------------ src/evaluate.h | 5 +- src/nnue/evaluate_nnue.cpp | 139 ++++++++++++++++-------- src/nnue/evaluate_nnue.h | 19 ++-- src/nnue/nnue_accumulator.h | 3 +- src/nnue/nnue_architecture.h | 38 +++++-- src/nnue/nnue_feature_transformer.h | 65 +++++------ src/position.cpp | 18 +-- src/position.h | 6 +- src/uci.cpp | 3 +- src/ucioption.cpp | 4 +- 12 files changed, 297 insertions(+), 198 deletions(-) diff --git a/src/Makefile b/src/Makefile index 660b41e7edb..e6de514e568 100644 --- a/src/Makefile +++ b/src/Makefile @@ -806,7 +806,7 @@ help: @echo "help > Display architecture details" @echo "profile-build > standard build with profile-guided optimization" @echo "build > skip profile-guided optimization" - @echo "net > Download the default nnue net" + @echo "net > Download the default nnue nets" @echo "strip > Strip executable" @echo "install > Install executable" @echo "clean > Clean up" @@ -922,16 +922,7 @@ profileclean: @rm -f stockfish.res @rm -f ./-lstdc++.res -# set up shell variables for the net stuff -netvariables: - $(eval nnuenet := $(shell grep EvalFileDefaultName evaluate.h | grep define | sed 's/.*\(nn-[a-z0-9]\{12\}.nnue\).*/\1/')) - $(eval nnuedownloadurl1 := https://tests.stockfishchess.org/api/nn/$(nnuenet)) - $(eval nnuedownloadurl2 := https://github.com/official-stockfish/networks/raw/master/$(nnuenet)) - $(eval curl_or_wget := $(shell if hash curl 2>/dev/null; then echo "curl -skL"; elif hash wget 2>/dev/null; then echo "wget -qO-"; fi)) - $(eval shasum_command := $(shell if hash shasum 2>/dev/null; then echo "shasum -a 256 "; elif hash sha256sum 2>/dev/null; then echo "sha256sum "; fi)) - -# evaluation network (nnue) -net: netvariables +define fetch_network @echo "Default net: $(nnuenet)" @if [ "x$(curl_or_wget)" = "x" ]; then \ echo "Neither curl nor wget is installed. Install one of these tools unless the net has been downloaded manually"; \ @@ -966,7 +957,24 @@ net: netvariables if [ "$(nnuenet)" = "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then \ echo "Network validated"; break; \ fi; \ - fi; \ + fi; +endef + +# set up shell variables for the net stuff +define netvariables +$(eval nnuenet := $(shell grep $(1) evaluate.h | grep define | sed 's/.*\(nn-[a-z0-9]\{12\}.nnue\).*/\1/')) +$(eval nnuedownloadurl1 := https://tests.stockfishchess.org/api/nn/$(nnuenet)) +$(eval nnuedownloadurl2 := https://github.com/official-stockfish/networks/raw/master/$(nnuenet)) +$(eval curl_or_wget := $(shell if hash curl 2>/dev/null; then echo "curl -skL"; elif hash wget 2>/dev/null; then echo "wget -qO-"; fi)) +$(eval shasum_command := $(shell if hash shasum 2>/dev/null; then echo "shasum -a 256 "; elif hash sha256sum 2>/dev/null; then echo "sha256sum "; fi)) +endef + +# evaluation network (nnue) +net: + $(call netvariables, EvalFileDefaultNameBig) + $(call fetch_network) + $(call netvariables, EvalFileDefaultNameSmall) + $(call fetch_network) format: $(CLANG-FORMAT) -i $(SRCS) $(HEADERS) -style=file diff --git a/src/evaluate.cpp b/src/evaluate.cpp index bda7132a1ff..deeb9e673d0 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,7 @@ #include "incbin/incbin.h" #include "misc.h" #include "nnue/evaluate_nnue.h" +#include "nnue/nnue_architecture.h" #include "position.h" #include "thread.h" #include "types.h" @@ -44,11 +46,15 @@ // const unsigned int gEmbeddedNNUESize; // the size of the embedded file // Note that this does not work in Microsoft Visual Studio. #if !defined(_MSC_VER) && !defined(NNUE_EMBEDDING_OFF) -INCBIN(EmbeddedNNUE, EvalFileDefaultName); +INCBIN(EmbeddedNNUEBig, EvalFileDefaultNameBig); +INCBIN(EmbeddedNNUESmall, EvalFileDefaultNameSmall); #else -const unsigned char gEmbeddedNNUEData[1] = {0x0}; -const unsigned char* const gEmbeddedNNUEEnd = &gEmbeddedNNUEData[1]; -const unsigned int gEmbeddedNNUESize = 1; +const unsigned char gEmbeddedNNUEBigData[1] = {0x0}; +const unsigned char* const gEmbeddedNNUEBigEnd = &gEmbeddedNNUEBigData[1]; +const unsigned int gEmbeddedNNUEBigSize = 1; +const unsigned char gEmbeddedNNUESmallData[1] = {0x0}; +const unsigned char* const gEmbeddedNNUESmallEnd = &gEmbeddedNNUESmallData[1]; +const unsigned int gEmbeddedNNUESmallSize = 1; #endif @@ -56,7 +62,9 @@ namespace Stockfish { namespace Eval { -std::string currentEvalFileName = "None"; +std::string currentEvalFileName[2] = {"None", "None"}; +const std::string EvFiles[2] = {"EvalFile", "EvalFileSmall"}; +const std::string EvFileNames[2] = {EvalFileDefaultNameBig, EvalFileDefaultNameSmall}; // Tries to load a NNUE network at startup time, or when the engine // receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue" @@ -67,84 +75,96 @@ std::string currentEvalFileName = "None"; // variable to have the engine search in a special directory in their distro. void NNUE::init() { - std::string eval_file = std::string(Options["EvalFile"]); - if (eval_file.empty()) - eval_file = EvalFileDefaultName; + for (NetSize netSize : {Big, Small}) + { + // change after fishtest supports EvalFileSmall + std::string eval_file = + std::string(netSize == Small ? EvalFileDefaultNameSmall : Options[EvFiles[netSize]]); + if (eval_file.empty()) + eval_file = EvFileNames[netSize]; #if defined(DEFAULT_NNUE_DIRECTORY) - std::vector dirs = {"", "", CommandLine::binaryDirectory, - stringify(DEFAULT_NNUE_DIRECTORY)}; + std::vector dirs = {"", "", CommandLine::binaryDirectory, + stringify(DEFAULT_NNUE_DIRECTORY)}; #else - std::vector dirs = {"", "", CommandLine::binaryDirectory}; + std::vector dirs = {"", "", CommandLine::binaryDirectory}; #endif - for (const std::string& directory : dirs) - if (currentEvalFileName != eval_file) + for (const std::string& directory : dirs) { - if (directory != "") - { - std::ifstream stream(directory + eval_file, std::ios::binary); - if (NNUE::load_eval(eval_file, stream)) - currentEvalFileName = eval_file; - } - - if (directory == "" && eval_file == EvalFileDefaultName) + if (currentEvalFileName[netSize] != eval_file) { - // C++ way to prepare a buffer for a memory stream - class MemoryBuffer: public std::basic_streambuf { - public: - MemoryBuffer(char* p, size_t n) { - setg(p, p, p + n); - setp(p, p + n); - } - }; - - MemoryBuffer buffer( - const_cast(reinterpret_cast(gEmbeddedNNUEData)), - size_t(gEmbeddedNNUESize)); - (void) gEmbeddedNNUEEnd; // Silence warning on unused variable - - std::istream stream(&buffer); - if (NNUE::load_eval(eval_file, stream)) - currentEvalFileName = eval_file; + if (directory != "") + { + std::ifstream stream(directory + eval_file, std::ios::binary); + if (NNUE::load_eval(eval_file, stream, netSize)) + currentEvalFileName[netSize] = eval_file; + } + + if (directory == "" && eval_file == EvFileNames[netSize]) + { + // C++ way to prepare a buffer for a memory stream + class MemoryBuffer: public std::basic_streambuf { + public: + MemoryBuffer(char* p, size_t n) { + setg(p, p, p + n); + setp(p, p + n); + } + }; + + MemoryBuffer buffer( + const_cast(reinterpret_cast( + netSize == Small ? gEmbeddedNNUESmallData : gEmbeddedNNUEBigData)), + size_t(netSize == Small ? gEmbeddedNNUESmallSize : gEmbeddedNNUEBigSize)); + (void) gEmbeddedNNUEBigEnd; // Silence warning on unused variable + (void) gEmbeddedNNUESmallEnd; + + std::istream stream(&buffer); + if (NNUE::load_eval(eval_file, stream, netSize)) + currentEvalFileName[netSize] = eval_file; + } } } + } } // Verifies that the last net used was loaded successfully void NNUE::verify() { - std::string eval_file = std::string(Options["EvalFile"]); - if (eval_file.empty()) - eval_file = EvalFileDefaultName; - - if (currentEvalFileName != eval_file) + for (NetSize netSize : {Big, Small}) { + // change after fishtest supports EvalFileSmall + std::string eval_file = + std::string(netSize == Small ? EvalFileDefaultNameSmall : Options[EvFiles[netSize]]); + if (eval_file.empty()) + eval_file = EvFileNames[netSize]; - std::string msg1 = - "Network evaluation parameters compatible with the engine must be available."; - std::string msg2 = "The network file " + eval_file + " was not loaded successfully."; - std::string msg3 = "The UCI option EvalFile might need to specify the full path, " - "including the directory name, to the network file."; - std::string msg4 = "The default net can be downloaded from: " - "https://tests.stockfishchess.org/api/nn/" - + std::string(EvalFileDefaultName); - std::string msg5 = "The engine will be terminated now."; - - sync_cout << "info string ERROR: " << msg1 << sync_endl; - sync_cout << "info string ERROR: " << msg2 << sync_endl; - sync_cout << "info string ERROR: " << msg3 << sync_endl; - sync_cout << "info string ERROR: " << msg4 << sync_endl; - sync_cout << "info string ERROR: " << msg5 << sync_endl; - - exit(EXIT_FAILURE); - } + if (currentEvalFileName[netSize] != eval_file) + { + std::string msg1 = + "Network evaluation parameters compatible with the engine must be available."; + std::string msg2 = "The network file " + eval_file + " was not loaded successfully."; + std::string msg3 = "The UCI option EvalFile might need to specify the full path, " + "including the directory name, to the network file."; + std::string msg4 = "The default net can be downloaded from: " + "https://tests.stockfishchess.org/api/nn/" + + std::string(EvFileNames[netSize]); + std::string msg5 = "The engine will be terminated now."; + + sync_cout << "info string ERROR: " << msg1 << sync_endl; + sync_cout << "info string ERROR: " << msg2 << sync_endl; + sync_cout << "info string ERROR: " << msg3 << sync_endl; + sync_cout << "info string ERROR: " << msg4 << sync_endl; + sync_cout << "info string ERROR: " << msg5 << sync_endl; + + exit(EXIT_FAILURE); + } - sync_cout << "info string NNUE evaluation using " << eval_file << sync_endl; + sync_cout << "info string NNUE evaluation using " << eval_file << sync_endl; + } } } - // Returns a static, purely materialistic evaluation of the position from // the point of view of the given color. It can be divided by PawnValue to get // an approximation of the material advantage on the board in terms of pawns. @@ -163,18 +183,19 @@ Value Eval::evaluate(const Position& pos) { int v; Color stm = pos.side_to_move(); int shuffling = pos.rule50_count(); - int simpleEval = simple_eval(pos, stm) + (int(pos.key() & 7) - 3); - - bool lazy = std::abs(simpleEval) >= RookValue + KnightValue + 16 * shuffling * shuffling - + std::abs(pos.this_thread()->bestValue) - + std::abs(pos.this_thread()->rootSimpleEval); + int simpleEval = simple_eval(pos, stm); + bool lazy = std::abs(simpleEval) > 2300; if (lazy) v = simpleEval; else { - int nnueComplexity; - Value nnue = NNUE::evaluate(pos, true, &nnueComplexity); + bool smallNet = std::abs(simpleEval) > 1100; + + int nnueComplexity; + + Value nnue = smallNet ? NNUE::evaluate(pos, true, &nnueComplexity) + : NNUE::evaluate(pos, true, &nnueComplexity); int optimism = pos.this_thread()->optimism[stm]; @@ -217,7 +238,7 @@ std::string Eval::trace(Position& pos) { ss << std::showpoint << std::showpos << std::fixed << std::setprecision(2) << std::setw(15); Value v; - v = NNUE::evaluate(pos, false); + v = NNUE::evaluate(pos, false); v = pos.side_to_move() == WHITE ? v : -v; ss << "NNUE evaluation " << 0.01 * UCI::to_cp(v) << " (white side)\n"; diff --git a/src/evaluate.h b/src/evaluate.h index 0a7ec61a3cf..3ead6b763dc 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -34,12 +34,13 @@ std::string trace(Position& pos); int simple_eval(const Position& pos, Color c); Value evaluate(const Position& pos); -extern std::string currentEvalFileName; +extern std::string currentEvalFileName[2]; // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. -#define EvalFileDefaultName "nn-b1e55edbea57.nnue" +#define EvalFileDefaultNameBig "nn-b1e55edbea57.nnue" +#define EvalFileDefaultNameSmall "nn-c01dc0ffeede.nnue" namespace NNUE { diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 14e2fec15a4..004e28dfb41 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -40,14 +40,18 @@ namespace Stockfish::Eval::NNUE { // Input feature converter -LargePagePtr featureTransformer; +LargePagePtr> + featureTransformerBig; +LargePagePtr> + featureTransformerSmall; // Evaluation function -AlignedPtr network[LayerStacks]; +AlignedPtr> networkBig[LayerStacks]; +AlignedPtr> networkSmall[LayerStacks]; -// Evaluation function file name -std::string fileName; -std::string netDescription; +// Evaluation function file names +std::string fileName[2]; +std::string netDescription[2]; namespace Detail { @@ -91,11 +95,20 @@ bool write_parameters(std::ostream& stream, const T& reference) { // Initialize the evaluation function parameters -static void initialize() { +static void initialize(NetSize netSize) { - Detail::initialize(featureTransformer); - for (std::size_t i = 0; i < LayerStacks; ++i) - Detail::initialize(network[i]); + if (netSize == Small) + { + Detail::initialize(featureTransformerSmall); + for (std::size_t i = 0; i < LayerStacks; ++i) + Detail::initialize(networkSmall[i]); + } + else + { + Detail::initialize(featureTransformerBig); + for (std::size_t i = 0; i < LayerStacks; ++i) + Detail::initialize(networkBig[i]); + } } // Read network header @@ -122,39 +135,57 @@ static bool write_header(std::ostream& stream, std::uint32_t hashValue, const st } // Read network parameters -static bool read_parameters(std::istream& stream) { +static bool read_parameters(std::istream& stream, NetSize netSize) { std::uint32_t hashValue; - if (!read_header(stream, &hashValue, &netDescription)) + if (!read_header(stream, &hashValue, &netDescription[netSize])) + return false; + if (hashValue != HashValue[netSize]) return false; - if (hashValue != HashValue) + if (netSize == Big && !Detail::read_parameters(stream, *featureTransformerBig)) return false; - if (!Detail::read_parameters(stream, *featureTransformer)) + if (netSize == Small && !Detail::read_parameters(stream, *featureTransformerSmall)) return false; for (std::size_t i = 0; i < LayerStacks; ++i) - if (!Detail::read_parameters(stream, *(network[i]))) + { + if (netSize == Big && !Detail::read_parameters(stream, *(networkBig[i]))) return false; + if (netSize == Small && !Detail::read_parameters(stream, *(networkSmall[i]))) + return false; + } return stream && stream.peek() == std::ios::traits_type::eof(); } // Write network parameters -static bool write_parameters(std::ostream& stream) { +static bool write_parameters(std::ostream& stream, NetSize netSize) { - if (!write_header(stream, HashValue, netDescription)) + if (!write_header(stream, HashValue[netSize], netDescription[netSize])) return false; - if (!Detail::write_parameters(stream, *featureTransformer)) + if (netSize == Big && !Detail::write_parameters(stream, *featureTransformerBig)) + return false; + if (netSize == Small && !Detail::write_parameters(stream, *featureTransformerSmall)) return false; for (std::size_t i = 0; i < LayerStacks; ++i) - if (!Detail::write_parameters(stream, *(network[i]))) + { + if (netSize == Big && !Detail::write_parameters(stream, *(networkBig[i]))) + return false; + if (netSize == Small && !Detail::write_parameters(stream, *(networkSmall[i]))) return false; + } return bool(stream); } void hint_common_parent_position(const Position& pos) { - featureTransformer->hint_common_access(pos); + + int simpleEval = simple_eval(pos, pos.side_to_move()); + if (abs(simpleEval) > 1100) + featureTransformerSmall->hint_common_access(pos); + else + featureTransformerBig->hint_common_access(pos); } // Evaluation function. Perform differential calculation. +template Value evaluate(const Position& pos, bool adjusted, int* complexity) { // We manually align the arrays on the stack because with gcc < 9.3 @@ -165,19 +196,28 @@ Value evaluate(const Position& pos, bool adjusted, int* complexity) { #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) TransformedFeatureType - transformedFeaturesUnaligned[FeatureTransformer::BufferSize - + alignment / sizeof(TransformedFeatureType)]; + transformedFeaturesUnaligned[FeatureTransformer < Small ? TransformedFeatureDimensionsSmall + : TransformedFeatureDimensionsBig, + nullptr + > ::BufferSize + alignment / sizeof(TransformedFeatureType)]; auto* transformedFeatures = align_ptr_up(&transformedFeaturesUnaligned[0]); #else - alignas(alignment) TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize]; + + alignas(alignment) TransformedFeatureType + transformedFeatures[FeatureTransformer < Net_Size == Small ? TransformedFeatureDimensionsSmall + : TransformedFeatureDimensionsBig, + nullptr > ::BufferSize]; #endif ASSERT_ALIGNED(transformedFeatures, alignment); const int bucket = (pos.count() - 1) / 4; - const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket); - const auto positional = network[bucket]->propagate(transformedFeatures); + const auto psqt = Net_Size == Small + ? featureTransformerSmall->transform(pos, transformedFeatures, bucket) + : featureTransformerBig->transform(pos, transformedFeatures, bucket); + const auto positional = Net_Size == Small ? networkSmall[bucket]->propagate(transformedFeatures) + : networkBig[bucket]->propagate(transformedFeatures); if (complexity) *complexity = std::abs(psqt - positional) / OutputScale; @@ -190,6 +230,9 @@ Value evaluate(const Position& pos, bool adjusted, int* complexity) { return static_cast((psqt + positional) / OutputScale); } +template Value evaluate(const Position& pos, bool adjusted, int* complexity); +template Value evaluate(const Position& pos, bool adjusted, int* complexity); + struct NnueEvalTrace { static_assert(LayerStacks == PSQTBuckets); @@ -205,13 +248,14 @@ static NnueEvalTrace trace_evaluate(const Position& pos) { constexpr uint64_t alignment = CacheLineSize; #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) - TransformedFeatureType - transformedFeaturesUnaligned[FeatureTransformer::BufferSize - + alignment / sizeof(TransformedFeatureType)]; + TransformedFeatureType transformedFeaturesUnaligned + [FeatureTransformer::BufferSize + + alignment / sizeof(TransformedFeatureType)]; auto* transformedFeatures = align_ptr_up(&transformedFeaturesUnaligned[0]); #else - alignas(alignment) TransformedFeatureType transformedFeatures[FeatureTransformer::BufferSize]; + alignas(alignment) TransformedFeatureType + transformedFeatures[FeatureTransformer::BufferSize]; #endif ASSERT_ALIGNED(transformedFeatures, alignment); @@ -220,8 +264,8 @@ static NnueEvalTrace trace_evaluate(const Position& pos) { t.correctBucket = (pos.count() - 1) / 4; for (IndexType bucket = 0; bucket < LayerStacks; ++bucket) { - const auto materialist = featureTransformer->transform(pos, transformedFeatures, bucket); - const auto positional = network[bucket]->propagate(transformedFeatures); + const auto materialist = featureTransformerBig->transform(pos, transformedFeatures, bucket); + const auto positional = networkBig[bucket]->propagate(transformedFeatures); t.psqt[bucket] = static_cast(materialist / OutputScale); t.positional[bucket] = static_cast(positional / OutputScale); @@ -310,7 +354,7 @@ std::string trace(Position& pos) { // We estimate the value of each piece by doing a differential evaluation from // the current base eval, simulating the removal of the piece from its square. - Value base = evaluate(pos); + Value base = evaluate(pos); base = pos.side_to_move() == WHITE ? base : -base; for (File f = FILE_A; f <= FILE_H; ++f) @@ -325,16 +369,16 @@ std::string trace(Position& pos) { auto st = pos.state(); pos.remove_piece(sq); - st->accumulator.computed[WHITE] = false; - st->accumulator.computed[BLACK] = false; + st->accumulatorBig.computed[WHITE] = false; + st->accumulatorBig.computed[BLACK] = false; - Value eval = evaluate(pos); + Value eval = evaluate(pos); eval = pos.side_to_move() == WHITE ? eval : -eval; v = base - eval; pos.put_piece(pc, sq); - st->accumulator.computed[WHITE] = false; - st->accumulator.computed[BLACK] = false; + st->accumulatorBig.computed[WHITE] = false; + st->accumulatorBig.computed[BLACK] = false; } writeSquare(f, r, pc, v); @@ -379,24 +423,24 @@ std::string trace(Position& pos) { // Load eval, from a file stream or a memory stream -bool load_eval(std::string name, std::istream& stream) { +bool load_eval(const std::string name, std::istream& stream, NetSize netSize) { - initialize(); - fileName = name; - return read_parameters(stream); + initialize(netSize); + fileName[netSize] = name; + return read_parameters(stream, netSize); } // Save eval, to a file stream or a memory stream -bool save_eval(std::ostream& stream) { +bool save_eval(std::ostream& stream, NetSize netSize) { - if (fileName.empty()) + if (fileName[netSize].empty()) return false; - return write_parameters(stream); + return write_parameters(stream, netSize); } // Save eval, to a file given by its name -bool save_eval(const std::optional& filename) { +bool save_eval(const std::optional& filename, NetSize netSize) { std::string actualFilename; std::string msg; @@ -405,7 +449,8 @@ bool save_eval(const std::optional& filename) { actualFilename = filename.value(); else { - if (currentEvalFileName != EvalFileDefaultName) + if (currentEvalFileName[netSize] + != (netSize == Small ? EvalFileDefaultNameSmall : EvalFileDefaultNameBig)) { msg = "Failed to export a net. " "A non-embedded net can only be saved if the filename is specified"; @@ -413,11 +458,11 @@ bool save_eval(const std::optional& filename) { sync_cout << msg << sync_endl; return false; } - actualFilename = EvalFileDefaultName; + actualFilename = (netSize == Small ? EvalFileDefaultNameSmall : EvalFileDefaultNameBig); } std::ofstream stream(actualFilename, std::ios_base::binary); - bool saved = save_eval(stream); + bool saved = save_eval(stream, netSize); msg = saved ? "Network saved successfully to " + actualFilename : "Failed to export a net"; diff --git a/src/nnue/evaluate_nnue.h b/src/nnue/evaluate_nnue.h index f80aa398bba..fabfb5693f9 100644 --- a/src/nnue/evaluate_nnue.h +++ b/src/nnue/evaluate_nnue.h @@ -39,9 +39,11 @@ class Position; namespace Stockfish::Eval::NNUE { // Hash value of evaluation function structure -constexpr std::uint32_t HashValue = - FeatureTransformer::get_hash_value() ^ Network::get_hash_value(); - +constexpr std::uint32_t HashValue[2] = { + FeatureTransformer::get_hash_value() + ^ Network::get_hash_value(), + FeatureTransformer::get_hash_value() + ^ Network::get_hash_value()}; // Deleter for automating release of memory area template @@ -67,12 +69,13 @@ template using LargePagePtr = std::unique_ptr>; std::string trace(Position& pos); -Value evaluate(const Position& pos, bool adjusted = false, int* complexity = nullptr); -void hint_common_parent_position(const Position& pos); +template +Value evaluate(const Position& pos, bool adjusted = false, int* complexity = nullptr); +void hint_common_parent_position(const Position& pos); -bool load_eval(std::string name, std::istream& stream); -bool save_eval(std::ostream& stream); -bool save_eval(const std::optional& filename); +bool load_eval(const std::string name, std::istream& stream, NetSize netSize); +bool save_eval(std::ostream& stream, NetSize netSize); +bool save_eval(const std::optional& filename, NetSize netSize); } // namespace Stockfish::Eval::NNUE diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index f6d705243ef..0b05d00da28 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -29,8 +29,9 @@ namespace Stockfish::Eval::NNUE { // Class that holds the result of affine transformation of input features +template struct alignas(CacheLineSize) Accumulator { - std::int16_t accumulation[2][TransformedFeatureDimensions]; + std::int16_t accumulation[2][Size]; std::int32_t psqtAccumulation[2][PSQTBuckets]; bool computed[2]; }; diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index 92445704296..949f2d8687f 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -37,14 +37,28 @@ namespace Stockfish::Eval::NNUE { // Input features used in evaluation function using FeatureSet = Features::HalfKAv2_hm; +enum NetSize { + Big, + Small +}; + // Number of input feature dimensions after conversion -constexpr IndexType TransformedFeatureDimensions = 2560; -constexpr IndexType PSQTBuckets = 8; -constexpr IndexType LayerStacks = 8; +constexpr IndexType TransformedFeatureDimensionsBig = 2560; +constexpr int L2Big = 15; +constexpr int L3Big = 32; + +constexpr IndexType TransformedFeatureDimensionsSmall = 128; +constexpr int L2Small = 15; +constexpr int L3Small = 32; + +constexpr IndexType PSQTBuckets = 8; +constexpr IndexType LayerStacks = 8; +template struct Network { - static constexpr int FC_0_OUTPUTS = 15; - static constexpr int FC_1_OUTPUTS = 32; + static constexpr IndexType TransformedFeatureDimensions = L1; + static constexpr int FC_0_OUTPUTS = L2; + static constexpr int FC_1_OUTPUTS = L3; Layers::AffineTransformSparseInput fc_0; Layers::SqrClippedReLU ac_sqr_0; @@ -84,13 +98,13 @@ struct Network { std::int32_t propagate(const TransformedFeatureType* transformedFeatures) { struct alignas(CacheLineSize) Buffer { - alignas(CacheLineSize) decltype(fc_0)::OutputBuffer fc_0_out; - alignas(CacheLineSize) decltype(ac_sqr_0)::OutputType + alignas(CacheLineSize) typename decltype(fc_0)::OutputBuffer fc_0_out; + alignas(CacheLineSize) typename decltype(ac_sqr_0)::OutputType ac_sqr_0_out[ceil_to_multiple(FC_0_OUTPUTS * 2, 32)]; - alignas(CacheLineSize) decltype(ac_0)::OutputBuffer ac_0_out; - alignas(CacheLineSize) decltype(fc_1)::OutputBuffer fc_1_out; - alignas(CacheLineSize) decltype(ac_1)::OutputBuffer ac_1_out; - alignas(CacheLineSize) decltype(fc_2)::OutputBuffer fc_2_out; + alignas(CacheLineSize) typename decltype(ac_0)::OutputBuffer ac_0_out; + alignas(CacheLineSize) typename decltype(fc_1)::OutputBuffer fc_1_out; + alignas(CacheLineSize) typename decltype(ac_1)::OutputBuffer ac_1_out; + alignas(CacheLineSize) typename decltype(fc_2)::OutputBuffer fc_2_out; Buffer() { std::memset(this, 0, sizeof(*this)); } }; @@ -108,7 +122,7 @@ struct Network { ac_sqr_0.propagate(buffer.fc_0_out, buffer.ac_sqr_0_out); ac_0.propagate(buffer.fc_0_out, buffer.ac_0_out); std::memcpy(buffer.ac_sqr_0_out + FC_0_OUTPUTS, buffer.ac_0_out, - FC_0_OUTPUTS * sizeof(decltype(ac_0)::OutputType)); + FC_0_OUTPUTS * sizeof(typename decltype(ac_0)::OutputType)); fc_1.propagate(buffer.ac_sqr_0_out, buffer.fc_1_out); ac_1.propagate(buffer.fc_1_out, buffer.ac_1_out); fc_2.propagate(buffer.ac_1_out, buffer.fc_2_out); diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 2008cf25f1d..9a162ac9853 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -186,11 +186,6 @@ static constexpr int BestRegisterCount() { return 1; } - -static constexpr int NumRegs = - BestRegisterCount(); -static constexpr int NumPsqtRegs = - BestRegisterCount(); #if defined(__GNUC__) #pragma GCC diagnostic pop #endif @@ -198,6 +193,8 @@ static constexpr int NumPsqtRegs = // Input feature converter +template StateInfo::*accPtr> class FeatureTransformer { private: @@ -205,6 +202,11 @@ class FeatureTransformer { static constexpr IndexType HalfDimensions = TransformedFeatureDimensions; #ifdef VECTOR + static constexpr int NumRegs = + BestRegisterCount(); + static constexpr int NumPsqtRegs = + BestRegisterCount(); + static constexpr IndexType TileHeight = NumRegs * sizeof(vec_t) / 2; static constexpr IndexType PsqtTileHeight = NumPsqtRegs * sizeof(psqt_vec_t) / 4; static_assert(HalfDimensions % TileHeight == 0, "TileHeight must divide HalfDimensions"); @@ -253,8 +255,8 @@ class FeatureTransformer { update_accumulator(pos); const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()}; - const auto& accumulation = pos.state()->accumulator.accumulation; - const auto& psqtAccumulation = pos.state()->accumulator.psqtAccumulation; + const auto& accumulation = (pos.state()->*accPtr).accumulation; + const auto& psqtAccumulation = (pos.state()->*accPtr).psqtAccumulation; const auto psqt = (psqtAccumulation[perspectives[0]][bucket] - psqtAccumulation[perspectives[1]][bucket]) @@ -323,7 +325,7 @@ class FeatureTransformer { // of the estimated gain in terms of features to be added/subtracted. StateInfo *st = pos.state(), *next = nullptr; int gain = FeatureSet::refresh_cost(pos); - while (st->previous && !st->accumulator.computed[Perspective]) + while (st->previous && !(st->*accPtr).computed[Perspective]) { // This governs when a full feature refresh is needed and how many // updates are better than just one full refresh. @@ -381,7 +383,7 @@ class FeatureTransformer { for (; i >= 0; --i) { - states_to_update[i]->accumulator.computed[Perspective] = true; + (states_to_update[i]->*accPtr).computed[Perspective] = true; const StateInfo* end_state = i == 0 ? computed_st : states_to_update[i - 1]; @@ -402,9 +404,9 @@ class FeatureTransformer { assert(states_to_update[0]); auto accIn = - reinterpret_cast(&st->accumulator.accumulation[Perspective][0]); + reinterpret_cast(&(st->*accPtr).accumulation[Perspective][0]); auto accOut = reinterpret_cast( - &states_to_update[0]->accumulator.accumulation[Perspective][0]); + &(states_to_update[0]->*accPtr).accumulation[Perspective][0]); const IndexType offsetR0 = HalfDimensions * removed[0][0]; auto columnR0 = reinterpret_cast(&weights[offsetR0]); @@ -428,10 +430,10 @@ class FeatureTransformer { vec_add_16(columnR0[k], columnR1[k])); } - auto accPsqtIn = reinterpret_cast( - &st->accumulator.psqtAccumulation[Perspective][0]); + auto accPsqtIn = + reinterpret_cast(&(st->*accPtr).psqtAccumulation[Perspective][0]); auto accPsqtOut = reinterpret_cast( - &states_to_update[0]->accumulator.psqtAccumulation[Perspective][0]); + &(states_to_update[0]->*accPtr).psqtAccumulation[Perspective][0]); const IndexType offsetPsqtR0 = PSQTBuckets * removed[0][0]; auto columnPsqtR0 = reinterpret_cast(&psqtWeights[offsetPsqtR0]); @@ -463,7 +465,7 @@ class FeatureTransformer { { // Load accumulator auto accTileIn = reinterpret_cast( - &st->accumulator.accumulation[Perspective][j * TileHeight]); + &(st->*accPtr).accumulation[Perspective][j * TileHeight]); for (IndexType k = 0; k < NumRegs; ++k) acc[k] = vec_load(&accTileIn[k]); @@ -489,7 +491,7 @@ class FeatureTransformer { // Store accumulator auto accTileOut = reinterpret_cast( - &states_to_update[i]->accumulator.accumulation[Perspective][j * TileHeight]); + &(states_to_update[i]->*accPtr).accumulation[Perspective][j * TileHeight]); for (IndexType k = 0; k < NumRegs; ++k) vec_store(&accTileOut[k], acc[k]); } @@ -499,7 +501,7 @@ class FeatureTransformer { { // Load accumulator auto accTilePsqtIn = reinterpret_cast( - &st->accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); + &(st->*accPtr).psqtAccumulation[Perspective][j * PsqtTileHeight]); for (std::size_t k = 0; k < NumPsqtRegs; ++k) psqt[k] = vec_load_psqt(&accTilePsqtIn[k]); @@ -525,8 +527,8 @@ class FeatureTransformer { // Store accumulator auto accTilePsqtOut = reinterpret_cast( - &states_to_update[i] - ->accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); + &(states_to_update[i]->*accPtr) + .psqtAccumulation[Perspective][j * PsqtTileHeight]); for (std::size_t k = 0; k < NumPsqtRegs; ++k) vec_store_psqt(&accTilePsqtOut[k], psqt[k]); } @@ -535,13 +537,12 @@ class FeatureTransformer { #else for (IndexType i = 0; states_to_update[i]; ++i) { - std::memcpy(states_to_update[i]->accumulator.accumulation[Perspective], - st->accumulator.accumulation[Perspective], - HalfDimensions * sizeof(BiasType)); + std::memcpy((states_to_update[i]->*accPtr).accumulation[Perspective], + (st->*accPtr).accumulation[Perspective], HalfDimensions * sizeof(BiasType)); for (std::size_t k = 0; k < PSQTBuckets; ++k) - states_to_update[i]->accumulator.psqtAccumulation[Perspective][k] = - st->accumulator.psqtAccumulation[Perspective][k]; + (states_to_update[i]->*accPtr).psqtAccumulation[Perspective][k] = + (st->*accPtr).psqtAccumulation[Perspective][k]; st = states_to_update[i]; @@ -551,10 +552,10 @@ class FeatureTransformer { const IndexType offset = HalfDimensions * index; for (IndexType j = 0; j < HalfDimensions; ++j) - st->accumulator.accumulation[Perspective][j] -= weights[offset + j]; + (st->*accPtr).accumulation[Perspective][j] -= weights[offset + j]; for (std::size_t k = 0; k < PSQTBuckets; ++k) - st->accumulator.psqtAccumulation[Perspective][k] -= + (st->*accPtr).psqtAccumulation[Perspective][k] -= psqtWeights[index * PSQTBuckets + k]; } @@ -564,10 +565,10 @@ class FeatureTransformer { const IndexType offset = HalfDimensions * index; for (IndexType j = 0; j < HalfDimensions; ++j) - st->accumulator.accumulation[Perspective][j] += weights[offset + j]; + (st->*accPtr).accumulation[Perspective][j] += weights[offset + j]; for (std::size_t k = 0; k < PSQTBuckets; ++k) - st->accumulator.psqtAccumulation[Perspective][k] += + (st->*accPtr).psqtAccumulation[Perspective][k] += psqtWeights[index * PSQTBuckets + k]; } } @@ -586,7 +587,7 @@ class FeatureTransformer { // Refresh the accumulator // Could be extracted to a separate function because it's done in 2 places, // but it's unclear if compilers would correctly handle register allocation. - auto& accumulator = pos.state()->accumulator; + auto& accumulator = pos.state()->*accPtr; accumulator.computed[Perspective] = true; FeatureSet::IndexList active; FeatureSet::append_active_indices(pos, active); @@ -663,12 +664,12 @@ class FeatureTransformer { // Look for a usable accumulator of an earlier position. We keep track // of the estimated gain in terms of features to be added/subtracted. // Fast early exit. - if (pos.state()->accumulator.computed[Perspective]) + if ((pos.state()->*accPtr).computed[Perspective]) return; auto [oldest_st, _] = try_find_computed_accumulator(pos); - if (oldest_st->accumulator.computed[Perspective]) + if ((oldest_st->*accPtr).computed[Perspective]) { // Only update current position accumulator to minimize work. StateInfo* states_to_update[2] = {pos.state(), nullptr}; @@ -685,7 +686,7 @@ class FeatureTransformer { auto [oldest_st, next] = try_find_computed_accumulator(pos); - if (oldest_st->accumulator.computed[Perspective]) + if ((oldest_st->*accPtr).computed[Perspective]) { if (next == nullptr) return; diff --git a/src/position.cpp b/src/position.cpp index 4fba3c234b7..ddc31888422 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -684,10 +684,10 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { ++st->pliesFromNull; // Used by NNUE - st->accumulator.computed[WHITE] = false; - st->accumulator.computed[BLACK] = false; - auto& dp = st->dirtyPiece; - dp.dirty_num = 1; + st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] = + st->accumulatorSmall.computed[WHITE] = st->accumulatorSmall.computed[BLACK] = false; + auto& dp = st->dirtyPiece; + dp.dirty_num = 1; Color us = sideToMove; Color them = ~us; @@ -964,15 +964,15 @@ void Position::do_null_move(StateInfo& newSt) { assert(!checkers()); assert(&newSt != st); - std::memcpy(&newSt, st, offsetof(StateInfo, accumulator)); + std::memcpy(&newSt, st, offsetof(StateInfo, accumulatorBig)); newSt.previous = st; st = &newSt; - st->dirtyPiece.dirty_num = 0; - st->dirtyPiece.piece[0] = NO_PIECE; // Avoid checks in UpdateAccumulator() - st->accumulator.computed[WHITE] = false; - st->accumulator.computed[BLACK] = false; + st->dirtyPiece.dirty_num = 0; + st->dirtyPiece.piece[0] = NO_PIECE; // Avoid checks in UpdateAccumulator() + st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] = + st->accumulatorSmall.computed[WHITE] = st->accumulatorSmall.computed[BLACK] = false; if (st->epSquare != SQ_NONE) { diff --git a/src/position.h b/src/position.h index 7e0c3eefd77..34b53f4a558 100644 --- a/src/position.h +++ b/src/position.h @@ -27,6 +27,7 @@ #include "bitboard.h" #include "nnue/nnue_accumulator.h" +#include "nnue/nnue_architecture.h" #include "types.h" namespace Stockfish { @@ -57,8 +58,9 @@ struct StateInfo { int repetition; // Used by NNUE - Eval::NNUE::Accumulator accumulator; - DirtyPiece dirtyPiece; + Eval::NNUE::Accumulator accumulatorBig; + Eval::NNUE::Accumulator accumulatorSmall; + DirtyPiece dirtyPiece; }; diff --git a/src/uci.cpp b/src/uci.cpp index 8e93eee6dc5..be902277984 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -37,6 +37,7 @@ #include "misc.h" #include "movegen.h" #include "nnue/evaluate_nnue.h" +#include "nnue/nnue_architecture.h" #include "position.h" #include "search.h" #include "thread.h" @@ -320,7 +321,7 @@ void UCI::loop(int argc, char* argv[]) { std::string f; if (is >> std::skipws >> f) filename = f; - Eval::NNUE::save_eval(filename); + Eval::NNUE::save_eval(filename, Eval::NNUE::Big); } else if (token == "--help" || token == "help" || token == "--license" || token == "license") sync_cout diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 087882f11b8..f8cbcc53077 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -82,7 +82,9 @@ void init(OptionsMap& o) { o["SyzygyProbeDepth"] << Option(1, 1, 100); o["Syzygy50MoveRule"] << Option(true); o["SyzygyProbeLimit"] << Option(7, 0, 7); - o["EvalFile"] << Option(EvalFileDefaultName, on_eval_file); + o["EvalFile"] << Option(EvalFileDefaultNameBig, on_eval_file); + // Enable this after fishtest workers support EvalFileSmall + // o["EvalFileSmall"] << Option(EvalFileDefaultNameSmall, on_eval_file); } From f09adaa4a4c3cbb44e1ca8cc687a08dc3d58076e Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Wed, 13 Dec 2023 13:07:36 -0500 Subject: [PATCH 1319/1766] Update smallnet to nn-baff1ede1f90.nnue with wider eval range Created by training an L1-128 net from scratch with a wider range of evals in the training data and wld-fen-skipping disabled during training. The differences in this training data compared to the first dual nnue PR are: - removal of all positions with 3 pieces - when piece count >= 16, keep positions with simple eval above 750 - when piece count < 16, remove positions with simple eval above 3000 The asymmetric data filtering was meant to flatten the training data piece count distribution, which was previously heavily skewed towards positions with low piece counts. Additionally, the simple eval range where the smallnet is used was widened to cover more positions previously evaluated by the big net and simple eval. ```yaml experiment-name: 128--S1-hse-S7-v4-S3-v1-no-wld-skip training-dataset: - /data/hse/S3/leela96-filt-v2.min.high-simple-eval-1k.binpack - /data/hse/S3/dfrc99-16tb7p-eval-filt-v2.min.high-simple-eval-1k.binpack - /data/hse/S3/test80-apr2022-16tb7p.min.high-simple-eval-1k.binpack - /data/hse/S7/test60-2020-2tb7p.v6-3072.high-simple-eval-v4.binpack - /data/hse/S7/test60-novdec2021-12tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-v4.binpack - /data/hse/S7/test77-nov2021-2tb7p.v6-3072.min.high-simple-eval-v4.binpack - /data/hse/S7/test77-dec2021-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-v4.binpack - /data/hse/S7/test77-jan2022-2tb7p.high-simple-eval-v4.binpack - /data/hse/S7/test78-jantomay2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-v4.binpack - /data/hse/S7/test78-juntosep2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-v4.binpack - /data/hse/S7/test79-apr2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-v4.binpack - /data/hse/S7/test79-may2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-v4.binpack - /data/hse/S7/test80-may2022-16tb7p.high-simple-eval-v4.binpack - /data/hse/S7/test80-jun2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-v4.binpack - /data/hse/S7/test80-jul2022-16tb7p.v6-dd.min.high-simple-eval-v4.binpack - /data/hse/S7/test80-aug2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-v4.binpack - /data/hse/S7/test80-sep2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-v4.binpack - /data/hse/S7/test80-oct2022-16tb7p.v6-dd.high-simple-eval-v4.binpack - /data/hse/S7/test80-nov2022-16tb7p-v6-dd.min.high-simple-eval-v4.binpack - /data/hse/S7/test80-jan2023-3of3-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-v4.binpack - /data/hse/S7/test80-feb2023-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-v4.binpack - /data/hse/S7/test80-mar2023-2tb7p.v6-sk16.min.high-simple-eval-v4.binpack - /data/hse/S7/test80-apr2023-2tb7p-filter-v6-sk16.min.high-simple-eval-v4.binpack - /data/hse/S7/test80-may2023-2tb7p.v6.min.high-simple-eval-v4.binpack - /data/hse/S7/test80-jun2023-2tb7p.v6-3072.min.high-simple-eval-v4.binpack - /data/hse/S7/test80-jul2023-2tb7p.v6-3072.min.high-simple-eval-v4.binpack - /data/hse/S7/test80-aug2023-2tb7p.v6.min.high-simple-eval-v4.binpack - /data/hse/S7/test80-sep2023-2tb7p.high-simple-eval-v4.binpack - /data/hse/S7/test80-oct2023-2tb7p.high-simple-eval-v4.binpack wld-fen-skipping: False start-from-engine-test-net: False nnue-pytorch-branch: linrock/nnue-pytorch/L1-128 engine-test-branch: linrock/Stockfish/L1-128-nolazy engine-base-branch: linrock/Stockfish/L1-128 num-epochs: 500 start-lambda: 1.0 end-lambda: 1.0 ``` Experiment yaml configs converted to easy_train.sh commands with: https://github.com/linrock/nnue-tools/blob/4339954/yaml_easy_train.py Binpacks interleaved at training time with: https://github.com/official-stockfish/nnue-pytorch/pull/259 FT weights permuted with 10k positions from fishpack32.binpack with: https://github.com/official-stockfish/nnue-pytorch/pull/254 Data filtered for high simple eval positions (v4) with: https://github.com/linrock/Stockfish/blob/b9c8440/src/tools/transform.cpp#L640-L675 Training data can be found at: https://robotmoon.com/nnue-training-data/ Local elo at 25k nodes per move of L1-128 smallnet (nnue-only eval) vs. L1-128 trained on standard S1 data: nn-epoch319.nnue : -241.7 +/- 3.2 Passed STC vs. 36db936: https://tests.stockfishchess.org/tests/view/6576b3484d789acf40aabbfe LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 21920 W: 5680 L: 5381 D: 10859 Ptnml(0-2): 82, 2488, 5520, 2789, 81 Passed LTC vs. DualNNUE #4915: https://tests.stockfishchess.org/tests/view/65775c034d789acf40aac7e3 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 147606 W: 36619 L: 36063 D: 74924 Ptnml(0-2): 98, 16591, 39891, 17103, 120 closes https://github.com/official-stockfish/Stockfish/pull/4919 Bench: 1438336 --- src/evaluate.cpp | 4 ++-- src/evaluate.h | 2 +- src/nnue/evaluate_nnue.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index deeb9e673d0..e3f60f9cd72 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -185,12 +185,12 @@ Value Eval::evaluate(const Position& pos) { int shuffling = pos.rule50_count(); int simpleEval = simple_eval(pos, stm); - bool lazy = std::abs(simpleEval) > 2300; + bool lazy = std::abs(simpleEval) > 2550; if (lazy) v = simpleEval; else { - bool smallNet = std::abs(simpleEval) > 1100; + bool smallNet = std::abs(simpleEval) > 1050; int nnueComplexity; diff --git a/src/evaluate.h b/src/evaluate.h index 3ead6b763dc..ce608735b51 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -40,7 +40,7 @@ extern std::string currentEvalFileName[2]; // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. #define EvalFileDefaultNameBig "nn-b1e55edbea57.nnue" -#define EvalFileDefaultNameSmall "nn-c01dc0ffeede.nnue" +#define EvalFileDefaultNameSmall "nn-baff1ede1f90.nnue" namespace NNUE { diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 004e28dfb41..7566d84981d 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -178,7 +178,7 @@ static bool write_parameters(std::ostream& stream, NetSize netSize) { void hint_common_parent_position(const Position& pos) { int simpleEval = simple_eval(pos, pos.side_to_move()); - if (abs(simpleEval) > 1100) + if (abs(simpleEval) > 1050) featureTransformerSmall->hint_common_access(pos); else featureTransformerBig->hint_common_access(pos); From 7c5e3f28655607288a980645e6b2ce600a627b11 Mon Sep 17 00:00:00 2001 From: Disservin Date: Sun, 7 Jan 2024 21:41:52 +0100 Subject: [PATCH 1320/1766] Prefix abs with std:: --- src/nnue/evaluate_nnue.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 7566d84981d..7a3f68774c4 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -178,7 +178,7 @@ static bool write_parameters(std::ostream& stream, NetSize netSize) { void hint_common_parent_position(const Position& pos) { int simpleEval = simple_eval(pos, pos.side_to_move()); - if (abs(simpleEval) > 1050) + if (std::abs(simpleEval) > 1050) featureTransformerSmall->hint_common_access(pos); else featureTransformerBig->hint_common_access(pos); From 99cdb920fcee4cb09cbe273eef9deb85b5a1af2c Mon Sep 17 00:00:00 2001 From: Disservin Date: Sun, 7 Jan 2024 23:08:33 +0100 Subject: [PATCH 1321/1766] Cleanup Evalfile handling This cleans up the EvalFile handling after the merge of #4915, which has become a bit confusing on what it is actually doing. closes https://github.com/official-stockfish/Stockfish/pull/4971 No functional change --- src/evaluate.cpp | 63 ++++++++++++++++++++---------------- src/evaluate.h | 13 ++++++-- src/nnue/evaluate_nnue.cpp | 3 +- src/nnue/nnue_architecture.h | 2 +- 4 files changed, 49 insertions(+), 32 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index e3f60f9cd72..e220b92a7fc 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -23,10 +23,10 @@ #include #include #include -#include #include #include #include +#include #include #include "incbin/incbin.h" @@ -62,9 +62,10 @@ namespace Stockfish { namespace Eval { -std::string currentEvalFileName[2] = {"None", "None"}; -const std::string EvFiles[2] = {"EvalFile", "EvalFileSmall"}; -const std::string EvFileNames[2] = {EvalFileDefaultNameBig, EvalFileDefaultNameSmall}; +std::unordered_map EvalFiles = { + {NNUE::Big, {"EvalFile", EvalFileDefaultNameBig, "None"}}, + {NNUE::Small, {"EvalFileSmall", EvalFileDefaultNameSmall, "None"}}}; + // Tries to load a NNUE network at startup time, or when the engine // receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue" @@ -75,13 +76,16 @@ const std::string EvFileNames[2] = {EvalFileDefaultNameBig, EvalFileDefa // variable to have the engine search in a special directory in their distro. void NNUE::init() { - for (NetSize netSize : {Big, Small}) + for (auto& [netSize, evalFile] : EvalFiles) { - // change after fishtest supports EvalFileSmall - std::string eval_file = - std::string(netSize == Small ? EvalFileDefaultNameSmall : Options[EvFiles[netSize]]); - if (eval_file.empty()) - eval_file = EvFileNames[netSize]; + // Replace with + // Options[evalFile.option_name] + // once fishtest supports the uci option EvalFileSmall + std::string user_eval_file = + netSize == Small ? evalFile.default_name : Options[evalFile.option_name]; + + if (user_eval_file.empty()) + user_eval_file = evalFile.default_name; #if defined(DEFAULT_NNUE_DIRECTORY) std::vector dirs = {"", "", CommandLine::binaryDirectory, @@ -92,16 +96,16 @@ void NNUE::init() { for (const std::string& directory : dirs) { - if (currentEvalFileName[netSize] != eval_file) + if (evalFile.selected_name != user_eval_file) { if (directory != "") { - std::ifstream stream(directory + eval_file, std::ios::binary); - if (NNUE::load_eval(eval_file, stream, netSize)) - currentEvalFileName[netSize] = eval_file; + std::ifstream stream(directory + user_eval_file, std::ios::binary); + if (NNUE::load_eval(user_eval_file, stream, netSize)) + evalFile.selected_name = user_eval_file; } - if (directory == "" && eval_file == EvFileNames[netSize]) + if (directory == "" && user_eval_file == evalFile.default_name) { // C++ way to prepare a buffer for a memory stream class MemoryBuffer: public std::basic_streambuf { @@ -120,8 +124,8 @@ void NNUE::init() { (void) gEmbeddedNNUESmallEnd; std::istream stream(&buffer); - if (NNUE::load_eval(eval_file, stream, netSize)) - currentEvalFileName[netSize] = eval_file; + if (NNUE::load_eval(user_eval_file, stream, netSize)) + evalFile.selected_name = user_eval_file; } } } @@ -131,24 +135,27 @@ void NNUE::init() { // Verifies that the last net used was loaded successfully void NNUE::verify() { - for (NetSize netSize : {Big, Small}) + for (const auto& [netSize, evalFile] : EvalFiles) { - // change after fishtest supports EvalFileSmall - std::string eval_file = - std::string(netSize == Small ? EvalFileDefaultNameSmall : Options[EvFiles[netSize]]); - if (eval_file.empty()) - eval_file = EvFileNames[netSize]; - - if (currentEvalFileName[netSize] != eval_file) + // Replace with + // Options[evalFile.option_name] + // once fishtest supports the uci option EvalFileSmall + std::string user_eval_file = + netSize == Small ? evalFile.default_name : Options[evalFile.option_name]; + if (user_eval_file.empty()) + user_eval_file = evalFile.default_name; + + if (evalFile.selected_name != user_eval_file) { std::string msg1 = "Network evaluation parameters compatible with the engine must be available."; - std::string msg2 = "The network file " + eval_file + " was not loaded successfully."; + std::string msg2 = + "The network file " + user_eval_file + " was not loaded successfully."; std::string msg3 = "The UCI option EvalFile might need to specify the full path, " "including the directory name, to the network file."; std::string msg4 = "The default net can be downloaded from: " "https://tests.stockfishchess.org/api/nn/" - + std::string(EvFileNames[netSize]); + + evalFile.default_name; std::string msg5 = "The engine will be terminated now."; sync_cout << "info string ERROR: " << msg1 << sync_endl; @@ -160,7 +167,7 @@ void NNUE::verify() { exit(EXIT_FAILURE); } - sync_cout << "info string NNUE evaluation using " << eval_file << sync_endl; + sync_cout << "info string NNUE evaluation using " << user_eval_file << sync_endl; } } } diff --git a/src/evaluate.h b/src/evaluate.h index ce608735b51..f712d8e6a0f 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -20,6 +20,7 @@ #define EVALUATE_H_INCLUDED #include +#include #include "types.h" @@ -34,8 +35,6 @@ std::string trace(Position& pos); int simple_eval(const Position& pos, Color c); Value evaluate(const Position& pos); -extern std::string currentEvalFileName[2]; - // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. @@ -44,11 +43,21 @@ extern std::string currentEvalFileName[2]; namespace NNUE { +enum NetSize : int; + void init(); void verify(); } // namespace NNUE +struct EvalFile { + std::string option_name; + std::string default_name; + std::string selected_name; +}; + +extern std::unordered_map EvalFiles; + } // namespace Eval } // namespace Stockfish diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 7a3f68774c4..86fe523050a 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include "../evaluate.h" #include "../misc.h" @@ -449,7 +450,7 @@ bool save_eval(const std::optional& filename, NetSize netSize) { actualFilename = filename.value(); else { - if (currentEvalFileName[netSize] + if (EvalFiles.at(netSize).selected_name != (netSize == Small ? EvalFileDefaultNameSmall : EvalFileDefaultNameBig)) { msg = "Failed to export a net. " diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index 949f2d8687f..b222ab997db 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -37,7 +37,7 @@ namespace Stockfish::Eval::NNUE { // Input features used in evaluation function using FeatureSet = Features::HalfKAv2_hm; -enum NetSize { +enum NetSize : int { Big, Small }; From 6deb88728fb141e853243c2873ad0cda4dd19320 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Wed, 3 Jan 2024 00:58:16 -0500 Subject: [PATCH 1322/1766] Update default main net to nn-baff1edbea57.nnue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Created by retraining the previous main net nn-b1e55edbea57.nnue with: - some of the same options as before: ranger21 optimizer, more WDL skipping - adding T80 aug filter-v6, sep, and oct 2023 data to the previous best dataset - increasing training loss for positions where predicted win rates were higher than estimated match results from training data position scores ```yaml experiment-name: 2560--S8-r21-more-wdl-skip-10p-more-loss-high-q-sk28 training-dataset: # https://github.com/official-stockfish/Stockfish/pull/4782 - /data/S6-1ee1aba5ed.binpack - /data/test80-aug2023-2tb7p.v6.min.binpack - /data/test80-sep2023-2tb7p.binpack - /data/test80-oct2023-2tb7p.binpack early-fen-skipping: 28 start-from-engine-test-net: True nnue-pytorch-branch: linrock/nnue-pytorch/r21-more-wdl-skip-10p-more-loss-high-q num-epochs: 1000 lr: 4.375e-4 gamma: 0.995 start-lambda: 1.0 end-lambda: 0.7 ``` Training data can be found at: https://robotmoon.com/nnue-training-data/ Training loss was increased by 10% for positions where predicted win rates were higher than suggested by the win rate model based on the training data, by multiplying with: ((qf > pt) * 0.1 + 1). This was a variant of experiments from Sopel's NNUE training & experimentation log: https://docs.google.com/document/d/1gTlrr02qSNKiXNZ_SuO4-RjK4MXBiFlLE6jvNqqMkAY Experiment 302 - increase loss when prediction too high, vondele’s idea Experiment 309 - increase loss when prediction too high, normalize in a batch Passed STC: https://tests.stockfishchess.org/tests/view/6597a21c79aa8af82b95fd5c LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 148320 W: 37960 L: 37475 D: 72885 Ptnml(0-2): 542, 17565, 37383, 18206, 464 Passed LTC: https://tests.stockfishchess.org/tests/view/659834a679aa8af82b960845 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 55188 W: 13955 L: 13592 D: 27641 Ptnml(0-2): 34, 6162, 14834, 6535, 29 closes https://github.com/official-stockfish/Stockfish/pull/4972 Bench: 1219824 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index f712d8e6a0f..79b77192a24 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -38,7 +38,7 @@ Value evaluate(const Position& pos); // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. -#define EvalFileDefaultNameBig "nn-b1e55edbea57.nnue" +#define EvalFileDefaultNameBig "nn-baff1edbea57.nnue" #define EvalFileDefaultNameSmall "nn-baff1ede1f90.nnue" namespace NNUE { From a10791095150bf7c020b92be0f55566fe34e9bf2 Mon Sep 17 00:00:00 2001 From: Disservin Date: Mon, 8 Jan 2024 19:48:46 +0100 Subject: [PATCH 1323/1766] Refactor global variables This aims to remove some of the annoying global structure which Stockfish has. Overall there is no major elo regression to be expected. Non regression SMP STC (paused, early version): https://tests.stockfishchess.org/tests/view/65983d7979aa8af82b9608f1 LLR: 0.23 (-2.94,2.94) <-1.75,0.25> Total: 76232 W: 19035 L: 19096 D: 38101 Ptnml(0-2): 92, 8735, 20515, 8690, 84 Non regression STC (early version): https://tests.stockfishchess.org/tests/view/6595b3a479aa8af82b95da7f LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 185344 W: 47027 L: 46972 D: 91345 Ptnml(0-2): 571, 21285, 48943, 21264, 609 Non regression SMP STC: https://tests.stockfishchess.org/tests/view/65a0715c79aa8af82b96b7e4 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 142936 W: 35761 L: 35662 D: 71513 Ptnml(0-2): 209, 16400, 38135, 16531, 193 These global structures/variables add hidden dependencies and allow data to be mutable from where it shouldn't it be (i.e. options). They also prevent Stockfish from internal selfplay, which would be a nice thing to be able to do, i.e. instantiate two Stockfish instances and let them play against each other. It will also allow us to make Stockfish a library, which can be easier used on other platforms. For consistency with the old search code, `thisThread` has been kept, even though it is not strictly necessary anymore. This the first major refactor of this kind (in recent time), and future changes are required, to achieve the previously described goals. This includes cleaning up the dependencies, transforming the network to be self contained and coming up with a plan to deal with proper tablebase memory management (see comments for more information on this). The removal of these global structures has been discussed in parts with Vondele and Sopel. closes https://github.com/official-stockfish/Stockfish/pull/4968 No functional change --- src/Makefile | 2 +- src/evaluate.cpp | 79 +++--- src/evaluate.h | 34 ++- src/main.cpp | 19 +- src/misc.cpp | 15 +- src/misc.h | 15 +- src/nnue/evaluate_nnue.cpp | 38 +-- src/nnue/evaluate_nnue.h | 19 +- src/position.cpp | 18 +- src/position.h | 30 +-- src/search.cpp | 502 +++++++++++++++++-------------------- src/search.h | 155 +++++++++++- src/syzygy/tbprobe.cpp | 8 +- src/syzygy/tbprobe.h | 8 +- src/thread.cpp | 134 +++++----- src/thread.h | 115 ++++----- src/thread_win32_osx.h | 26 +- src/timeman.cpp | 33 ++- src/timeman.h | 28 ++- src/tt.cpp | 31 +-- src/tt.h | 14 +- src/tune.cpp | 19 +- src/tune.h | 9 +- src/uci.cpp | 485 ++++++++++++++++++----------------- src/uci.h | 101 ++++---- src/ucioption.cpp | 133 ++++------ src/ucioption.h | 81 ++++++ 27 files changed, 1175 insertions(+), 976 deletions(-) create mode 100644 src/ucioption.h diff --git a/src/Makefile b/src/Makefile index e6de514e568..9680ca7feff 100644 --- a/src/Makefile +++ b/src/Makefile @@ -63,7 +63,7 @@ HEADERS = benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h \ nnue/layers/sqr_clipped_relu.h nnue/nnue_accumulator.h nnue/nnue_architecture.h \ nnue/nnue_common.h nnue/nnue_feature_transformer.h position.h \ search.h syzygy/tbprobe.h thread.h thread_win32_osx.h timeman.h \ - tt.h tune.h types.h uci.h + tt.h tune.h types.h uci.h ucioption.h OBJS = $(notdir $(SRCS:.cpp=.o)) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index e220b92a7fc..3e067e4c447 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -34,9 +35,10 @@ #include "nnue/evaluate_nnue.h" #include "nnue/nnue_architecture.h" #include "position.h" -#include "thread.h" +#include "search.h" #include "types.h" #include "uci.h" +#include "ucioption.h" // Macro to embed the default efficiently updatable neural network (NNUE) file // data in the engine binary (using incbin.h, by Dale Weiler). @@ -62,10 +64,6 @@ namespace Stockfish { namespace Eval { -std::unordered_map EvalFiles = { - {NNUE::Big, {"EvalFile", EvalFileDefaultNameBig, "None"}}, - {NNUE::Small, {"EvalFileSmall", EvalFileDefaultNameSmall, "None"}}}; - // Tries to load a NNUE network at startup time, or when the engine // receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue" @@ -74,38 +72,45 @@ std::unordered_map EvalFiles = { // network may be embedded in the binary), in the active working directory and // in the engine directory. Distro packagers may define the DEFAULT_NNUE_DIRECTORY // variable to have the engine search in a special directory in their distro. -void NNUE::init() { +NNUE::EvalFiles NNUE::load_networks(const std::string& rootDirectory, + const OptionsMap& options, + NNUE::EvalFiles evalFiles) { - for (auto& [netSize, evalFile] : EvalFiles) + for (auto& [netSize, evalFile] : evalFiles) { // Replace with - // Options[evalFile.option_name] + // options[evalFile.optionName] // once fishtest supports the uci option EvalFileSmall std::string user_eval_file = - netSize == Small ? evalFile.default_name : Options[evalFile.option_name]; + netSize == Small ? evalFile.defaultName : options[evalFile.optionName]; if (user_eval_file.empty()) - user_eval_file = evalFile.default_name; + user_eval_file = evalFile.defaultName; #if defined(DEFAULT_NNUE_DIRECTORY) - std::vector dirs = {"", "", CommandLine::binaryDirectory, + std::vector dirs = {"", "", rootDirectory, stringify(DEFAULT_NNUE_DIRECTORY)}; #else - std::vector dirs = {"", "", CommandLine::binaryDirectory}; + std::vector dirs = {"", "", rootDirectory}; #endif for (const std::string& directory : dirs) { - if (evalFile.selected_name != user_eval_file) + if (evalFile.current != user_eval_file) { if (directory != "") { std::ifstream stream(directory + user_eval_file, std::ios::binary); - if (NNUE::load_eval(user_eval_file, stream, netSize)) - evalFile.selected_name = user_eval_file; + auto description = NNUE::load_eval(stream, netSize); + + if (description.has_value()) + { + evalFile.current = user_eval_file; + evalFile.netDescription = description.value(); + } } - if (directory == "" && user_eval_file == evalFile.default_name) + if (directory == "" && user_eval_file == evalFile.defaultName) { // C++ way to prepare a buffer for a memory stream class MemoryBuffer: public std::basic_streambuf { @@ -124,28 +129,36 @@ void NNUE::init() { (void) gEmbeddedNNUESmallEnd; std::istream stream(&buffer); - if (NNUE::load_eval(user_eval_file, stream, netSize)) - evalFile.selected_name = user_eval_file; + auto description = NNUE::load_eval(stream, netSize); + + if (description.has_value()) + { + evalFile.current = user_eval_file; + evalFile.netDescription = description.value(); + } } } } } + + return evalFiles; } // Verifies that the last net used was loaded successfully -void NNUE::verify() { +void NNUE::verify(const OptionsMap& options, + const std::unordered_map& evalFiles) { - for (const auto& [netSize, evalFile] : EvalFiles) + for (const auto& [netSize, evalFile] : evalFiles) { // Replace with - // Options[evalFile.option_name] + // options[evalFile.optionName] // once fishtest supports the uci option EvalFileSmall std::string user_eval_file = - netSize == Small ? evalFile.default_name : Options[evalFile.option_name]; + netSize == Small ? evalFile.defaultName : options[evalFile.optionName]; if (user_eval_file.empty()) - user_eval_file = evalFile.default_name; + user_eval_file = evalFile.defaultName; - if (evalFile.selected_name != user_eval_file) + if (evalFile.current != user_eval_file) { std::string msg1 = "Network evaluation parameters compatible with the engine must be available."; @@ -155,7 +168,7 @@ void NNUE::verify() { "including the directory name, to the network file."; std::string msg4 = "The default net can be downloaded from: " "https://tests.stockfishchess.org/api/nn/" - + evalFile.default_name; + + evalFile.defaultName; std::string msg5 = "The engine will be terminated now."; sync_cout << "info string ERROR: " << msg1 << sync_endl; @@ -183,7 +196,7 @@ int Eval::simple_eval(const Position& pos, Color c) { // Evaluate is the evaluator for the outer world. It returns a static evaluation // of the position from the point of view of the side to move. -Value Eval::evaluate(const Position& pos) { +Value Eval::evaluate(const Position& pos, const Search::Worker& workerThread) { assert(!pos.checkers()); @@ -204,7 +217,7 @@ Value Eval::evaluate(const Position& pos) { Value nnue = smallNet ? NNUE::evaluate(pos, true, &nnueComplexity) : NNUE::evaluate(pos, true, &nnueComplexity); - int optimism = pos.this_thread()->optimism[stm]; + int optimism = workerThread.optimism[stm]; // Blend optimism and eval with nnue complexity and material imbalance optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / 512; @@ -227,16 +240,16 @@ Value Eval::evaluate(const Position& pos) { // a string (suitable for outputting to stdout) that contains the detailed // descriptions and values of each evaluation term. Useful for debugging. // Trace scores are from white's point of view -std::string Eval::trace(Position& pos) { +std::string Eval::trace(Position& pos, Search::Worker& workerThread) { if (pos.checkers()) return "Final evaluation: none (in check)"; // Reset any global variable used in eval - pos.this_thread()->bestValue = VALUE_ZERO; - pos.this_thread()->rootSimpleEval = VALUE_ZERO; - pos.this_thread()->optimism[WHITE] = VALUE_ZERO; - pos.this_thread()->optimism[BLACK] = VALUE_ZERO; + workerThread.iterBestValue = VALUE_ZERO; + workerThread.rootSimpleEval = VALUE_ZERO; + workerThread.optimism[WHITE] = VALUE_ZERO; + workerThread.optimism[BLACK] = VALUE_ZERO; std::stringstream ss; ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2); @@ -249,7 +262,7 @@ std::string Eval::trace(Position& pos) { v = pos.side_to_move() == WHITE ? v : -v; ss << "NNUE evaluation " << 0.01 * UCI::to_cp(v) << " (white side)\n"; - v = evaluate(pos); + v = evaluate(pos, workerThread); v = pos.side_to_move() == WHITE ? v : -v; ss << "Final evaluation " << 0.01 * UCI::to_cp(v) << " (white side)"; ss << " [with scaled NNUE, ...]"; diff --git a/src/evaluate.h b/src/evaluate.h index 79b77192a24..8a9d6fc7c87 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -27,13 +27,18 @@ namespace Stockfish { class Position; +class OptionsMap; + +namespace Search { +class Worker; +} namespace Eval { -std::string trace(Position& pos); +std::string trace(Position& pos, Search::Worker& workerThread); int simple_eval(const Position& pos, Color c); -Value evaluate(const Position& pos); +Value evaluate(const Position& pos, const Search::Worker& workerThread); // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the @@ -41,22 +46,27 @@ Value evaluate(const Position& pos); #define EvalFileDefaultNameBig "nn-baff1edbea57.nnue" #define EvalFileDefaultNameSmall "nn-baff1ede1f90.nnue" +struct EvalFile { + // UCI option name + std::string optionName; + // Default net name, will use one of the macros above + std::string defaultName; + // Selected net name, either via uci option or default + std::string current; + // Net description extracted from the net file + std::string netDescription; +}; + namespace NNUE { enum NetSize : int; -void init(); -void verify(); +using EvalFiles = std::unordered_map; -} // namespace NNUE +EvalFiles load_networks(const std::string&, const OptionsMap&, EvalFiles); +void verify(const OptionsMap&, const EvalFiles&); -struct EvalFile { - std::string option_name; - std::string default_name; - std::string selected_name; -}; - -extern std::unordered_map EvalFiles; +} // namespace NNUE } // namespace Eval diff --git a/src/main.cpp b/src/main.cpp index 78b3f54d919..de07d6a8738 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,15 +16,13 @@ along with this program. If not, see . */ -#include #include +#include #include "bitboard.h" #include "evaluate.h" #include "misc.h" #include "position.h" -#include "search.h" -#include "thread.h" #include "tune.h" #include "types.h" #include "uci.h" @@ -35,17 +33,16 @@ int main(int argc, char* argv[]) { std::cout << engine_info() << std::endl; - CommandLine::init(argc, argv); - UCI::init(Options); - Tune::init(); Bitboards::init(); Position::init(); - Threads.set(size_t(Options["Threads"])); - Search::clear(); // After threads are up - Eval::NNUE::init(); - UCI::loop(argc, argv); + UCI uci(argc, argv); + + Tune::init(uci.options); + + uci.evalFiles = Eval::NNUE::load_networks(uci.workingDirectory(), uci.options, uci.evalFiles); + + uci.loop(); - Threads.set(0); return 0; } diff --git a/src/misc.cpp b/src/misc.cpp index 9350a4830df..4885a5cd35c 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -721,17 +721,13 @@ void bindThisThread(size_t idx) { #define GETCWD getcwd #endif -namespace CommandLine { - -std::string argv0; // path+name of the executable binary, as given by argv[0] -std::string binaryDirectory; // path of the executable directory -std::string workingDirectory; // path of the working directory - -void init([[maybe_unused]] int argc, char* argv[]) { +CommandLine::CommandLine(int _argc, char** _argv) : + argc(_argc), + argv(_argv) { std::string pathSeparator; // Extract the path+name of the executable binary - argv0 = argv[0]; + std::string argv0 = argv[0]; #ifdef _WIN32 pathSeparator = "\\"; @@ -766,7 +762,4 @@ void init([[maybe_unused]] int argc, char* argv[]) { binaryDirectory.replace(0, 1, workingDirectory); } - -} // namespace CommandLine - } // namespace Stockfish diff --git a/src/misc.h b/src/misc.h index ca6cc16639f..994f551d5ff 100644 --- a/src/misc.h +++ b/src/misc.h @@ -176,12 +176,17 @@ namespace WinProcGroup { void bindThisThread(size_t idx); } -namespace CommandLine { -void init(int argc, char* argv[]); -extern std::string binaryDirectory; // path of the executable directory -extern std::string workingDirectory; // path of the working directory -} +struct CommandLine { + public: + CommandLine(int, char**); + + int argc; + char** argv; + + std::string binaryDirectory; // path of the executable directory + std::string workingDirectory; // path of the working directory +}; } // namespace Stockfish diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 86fe523050a..d4a4dbe4c45 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -26,8 +26,10 @@ #include #include #include +#include #include #include +#include #include #include "../evaluate.h" @@ -51,8 +53,6 @@ AlignedPtr> network AlignedPtr> networkSmall[LayerStacks]; // Evaluation function file names -std::string fileName[2]; -std::string netDescription[2]; namespace Detail { @@ -136,10 +136,10 @@ static bool write_header(std::ostream& stream, std::uint32_t hashValue, const st } // Read network parameters -static bool read_parameters(std::istream& stream, NetSize netSize) { +static bool read_parameters(std::istream& stream, NetSize netSize, std::string& netDescription) { std::uint32_t hashValue; - if (!read_header(stream, &hashValue, &netDescription[netSize])) + if (!read_header(stream, &hashValue, &netDescription)) return false; if (hashValue != HashValue[netSize]) return false; @@ -158,9 +158,10 @@ static bool read_parameters(std::istream& stream, NetSize netSize) { } // Write network parameters -static bool write_parameters(std::ostream& stream, NetSize netSize) { +static bool +write_parameters(std::ostream& stream, NetSize netSize, const std::string& netDescription) { - if (!write_header(stream, HashValue[netSize], netDescription[netSize])) + if (!write_header(stream, HashValue[netSize], netDescription)) return false; if (netSize == Big && !Detail::write_parameters(stream, *featureTransformerBig)) return false; @@ -424,24 +425,30 @@ std::string trace(Position& pos) { // Load eval, from a file stream or a memory stream -bool load_eval(const std::string name, std::istream& stream, NetSize netSize) { +std::optional load_eval(std::istream& stream, NetSize netSize) { initialize(netSize); - fileName[netSize] = name; - return read_parameters(stream, netSize); + std::string netDescription; + return read_parameters(stream, netSize, netDescription) ? std::make_optional(netDescription) + : std::nullopt; } // Save eval, to a file stream or a memory stream -bool save_eval(std::ostream& stream, NetSize netSize) { +bool save_eval(std::ostream& stream, + NetSize netSize, + const std::string& name, + const std::string& netDescription) { - if (fileName[netSize].empty()) + if (name.empty() || name == "None") return false; - return write_parameters(stream, netSize); + return write_parameters(stream, netSize, netDescription); } // Save eval, to a file given by its name -bool save_eval(const std::optional& filename, NetSize netSize) { +bool save_eval(const std::optional& filename, + NetSize netSize, + const std::unordered_map& evalFiles) { std::string actualFilename; std::string msg; @@ -450,7 +457,7 @@ bool save_eval(const std::optional& filename, NetSize netSize) { actualFilename = filename.value(); else { - if (EvalFiles.at(netSize).selected_name + if (evalFiles.at(netSize).current != (netSize == Small ? EvalFileDefaultNameSmall : EvalFileDefaultNameBig)) { msg = "Failed to export a net. " @@ -463,7 +470,8 @@ bool save_eval(const std::optional& filename, NetSize netSize) { } std::ofstream stream(actualFilename, std::ios_base::binary); - bool saved = save_eval(stream, netSize); + bool saved = save_eval(stream, netSize, evalFiles.at(netSize).current, + evalFiles.at(netSize).netDescription); msg = saved ? "Network saved successfully to " + actualFilename : "Failed to export a net"; diff --git a/src/nnue/evaluate_nnue.h b/src/nnue/evaluate_nnue.h index fabfb5693f9..ea88f890227 100644 --- a/src/nnue/evaluate_nnue.h +++ b/src/nnue/evaluate_nnue.h @@ -26,14 +26,20 @@ #include #include #include +#include #include "../misc.h" +#include "../types.h" #include "nnue_architecture.h" #include "nnue_feature_transformer.h" -#include "../types.h" namespace Stockfish { class Position; + +namespace Eval { +struct EvalFile; +} + } namespace Stockfish::Eval::NNUE { @@ -73,9 +79,14 @@ template Value evaluate(const Position& pos, bool adjusted = false, int* complexity = nullptr); void hint_common_parent_position(const Position& pos); -bool load_eval(const std::string name, std::istream& stream, NetSize netSize); -bool save_eval(std::ostream& stream, NetSize netSize); -bool save_eval(const std::optional& filename, NetSize netSize); +std::optional load_eval(std::istream& stream, NetSize netSize); +bool save_eval(std::ostream& stream, + NetSize netSize, + const std::string& name, + const std::string& netDescription); +bool save_eval(const std::optional& filename, + NetSize netSize, + const std::unordered_map&); } // namespace Stockfish::Eval::NNUE diff --git a/src/position.cpp b/src/position.cpp index ddc31888422..6202381d072 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -19,7 +19,6 @@ #include "position.h" #include -#include #include #include #include @@ -36,7 +35,6 @@ #include "movegen.h" #include "nnue/nnue_common.h" #include "syzygy/tbprobe.h" -#include "thread.h" #include "tt.h" #include "uci.h" @@ -87,7 +85,7 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) { ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize); Position p; - p.set(pos.fen(), pos.is_chess960(), &st, pos.this_thread()); + p.set(pos.fen(), pos.is_chess960(), &st); Tablebases::ProbeState s1, s2; Tablebases::WDLScore wdl = Tablebases::probe_wdl(p, &s1); int dtz = Tablebases::probe_dtz(p, &s2); @@ -160,7 +158,7 @@ void Position::init() { // Initializes the position object with the given FEN string. // This function is not very robust - make sure that input FENs are correct, // this is assumed to be the responsibility of the GUI. -Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Thread* th) { +Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si) { /* A FEN string defines a particular position using only the ASCII character set. @@ -286,8 +284,7 @@ Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Th // handle also common incorrect FEN with fullmove = 0. gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK); - chess960 = isChess960; - thisThread = th; + chess960 = isChess960; set_state(); assert(pos_is_ok()); @@ -388,7 +385,7 @@ Position& Position::set(const string& code, Color c, StateInfo* si) { string fenStr = "8/" + sides[0] + char(8 - sides[0].length() + '0') + "/8/8/8/8/" + sides[1] + char(8 - sides[1].length() + '0') + "/8 w - - 0 10"; - return set(fenStr, false, si, nullptr); + return set(fenStr, false, si); } @@ -667,7 +664,6 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { assert(m.is_ok()); assert(&newSt != st); - thisThread->nodes.fetch_add(1, std::memory_order_relaxed); Key k = st->key ^ Zobrist::side; // Copy some fields of the old state to our new StateInfo object except the @@ -959,7 +955,7 @@ void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Squ // Used to do a "null move": it flips // the side to move without executing any move on the board. -void Position::do_null_move(StateInfo& newSt) { +void Position::do_null_move(StateInfo& newSt, TranspositionTable& tt) { assert(!checkers()); assert(&newSt != st); @@ -982,7 +978,7 @@ void Position::do_null_move(StateInfo& newSt) { st->key ^= Zobrist::side; ++st->rule50; - prefetch(TT.first_entry(key())); + prefetch(tt.first_entry(key())); st->pliesFromNull = 0; @@ -1235,7 +1231,7 @@ void Position::flip() { std::getline(ss, token); // Half and full moves f += token; - set(f, is_chess960(), st, this_thread()); + set(f, is_chess960(), st); assert(pos_is_ok()); } diff --git a/src/position.h b/src/position.h index 34b53f4a558..7ce3556f0e9 100644 --- a/src/position.h +++ b/src/position.h @@ -32,6 +32,8 @@ namespace Stockfish { +class TranspositionTable; + // StateInfo struct stores information needed to restore a Position object to // its previous state when we retract a move. Whenever a move is made on the // board (by calling Position::do_move), a StateInfo object must be passed. @@ -75,8 +77,6 @@ using StateListPtr = std::unique_ptr>; // pieces, side to move, hash keys, castling info, etc. Important methods are // do_move() and undo_move(), used by the search to update node info when // traversing the search tree. -class Thread; - class Position { public: static void init(); @@ -86,7 +86,7 @@ class Position { Position& operator=(const Position&) = delete; // FEN string input/output - Position& set(const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th); + Position& set(const std::string& fenStr, bool isChess960, StateInfo* si); Position& set(const std::string& code, Color c, StateInfo* si); std::string fen() const; @@ -139,7 +139,7 @@ class Position { void do_move(Move m, StateInfo& newSt); void do_move(Move m, StateInfo& newSt, bool givesCheck); void undo_move(Move m); - void do_null_move(StateInfo& newSt); + void do_null_move(StateInfo& newSt, TranspositionTable& tt); void undo_null_move(); // Static Exchange Evaluation @@ -152,16 +152,15 @@ class Position { Key pawn_key() const; // Other properties of the position - Color side_to_move() const; - int game_ply() const; - bool is_chess960() const; - Thread* this_thread() const; - bool is_draw(int ply) const; - bool has_game_cycle(int ply) const; - bool has_repeated() const; - int rule50_count() const; - Value non_pawn_material(Color c) const; - Value non_pawn_material() const; + Color side_to_move() const; + int game_ply() const; + bool is_chess960() const; + bool is_draw(int ply) const; + bool has_game_cycle(int ply) const; + bool has_repeated() const; + int rule50_count() const; + Value non_pawn_material(Color c) const; + Value non_pawn_material() const; // Position consistency check, for debugging bool pos_is_ok() const; @@ -194,7 +193,6 @@ class Position { int castlingRightsMask[SQUARE_NB]; Square castlingRookSquare[CASTLING_RIGHT_NB]; Bitboard castlingPath[CASTLING_RIGHT_NB]; - Thread* thisThread; StateInfo* st; int gamePly; Color sideToMove; @@ -328,8 +326,6 @@ inline bool Position::capture_stage(Move m) const { inline Piece Position::captured_piece() const { return st->capturedPiece; } -inline Thread* Position::this_thread() const { return thisThread; } - inline void Position::put_piece(Piece pc, Square s) { board[s] = pc; diff --git a/src/search.cpp b/src/search.cpp index e93b12d1598..5530d125f15 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -27,8 +27,6 @@ #include #include #include -#include -#include #include #include "bitboard.h" @@ -44,14 +42,10 @@ #include "timeman.h" #include "tt.h" #include "uci.h" +#include "ucioption.h" namespace Stockfish { -namespace Search { - -LimitsType Limits; -} - namespace Tablebases { int Cardinality; @@ -62,33 +56,17 @@ Depth ProbeDepth; namespace TB = Tablebases; -using std::string; using Eval::evaluate; using namespace Search; namespace { -// Different node types, used as a template parameter -enum NodeType { - NonPV, - PV, - Root -}; // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving) { return ((116 - 44 * noTtCutNode) * (d - improving)); } -// Reductions lookup table initialized at startup -int Reductions[MAX_MOVES]; // [depth or moveNumber] - -Depth reduction(bool i, Depth d, int mn, int delta, int rootDelta) { - int reductionScale = Reductions[d] * Reductions[mn]; - return (reductionScale + 1346 - int(delta) * 896 / int(rootDelta)) / 1024 - + (!i && reductionScale > 880); -} - constexpr int futility_move_count(bool improving, Depth depth) { return improving ? (3 + depth * depth) : (3 + depth * depth) / 2; } @@ -105,9 +83,7 @@ int stat_bonus(Depth d) { return std::min(268 * d - 352, 1153); } int stat_malus(Depth d) { return std::min(400 * d - 354, 1201); } // Add a small random component to draw evaluations to avoid 3-fold blindness -Value value_draw(const Thread* thisThread) { - return VALUE_DRAW - 1 + Value(thisThread->nodes & 0x2); -} +Value value_draw(size_t nodes) { return VALUE_DRAW - 1 + Value(nodes & 0x2); } // Skill structure is used to implement strength limit. If we have a UCI_Elo, // we convert it to an appropriate skill level, anchored to the Stash engine. @@ -127,34 +103,30 @@ struct Skill { } bool enabled() const { return level < 20.0; } bool time_to_pick(Depth depth) const { return depth == 1 + int(level); } - Move pick_best(size_t multiPV); + Move pick_best(const RootMoves&, size_t multiPV); double level; Move best = Move::none(); }; -template -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 = 0); - Value value_to_tt(Value v, int ply); Value value_from_tt(Value v, int ply, int r50c); void update_pv(Move* pv, Move move, const Move* childPv); void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus); -void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus); -void update_all_stats(const Position& pos, - Stack* ss, - Move bestMove, - Value bestValue, - Value beta, - Square prevSq, - Move* quietsSearched, - int quietCount, - Move* capturesSearched, - int captureCount, - Depth depth); +void update_quiet_stats( + const Position& pos, Stack* ss, Search::Worker& workerThread, Move move, int bonus); +void update_all_stats(const Position& pos, + Stack* ss, + Search::Worker& workerThread, + Move bestMove, + Value bestValue, + Value beta, + Square prevSq, + Move* quietsSearched, + int quietCount, + Move* capturesSearched, + int captureCount, + Depth depth); // Utility to verify move generation. All the leaf nodes up // to the given depth are generated and counted, and the sum is returned. @@ -187,42 +159,35 @@ uint64_t perft(Position& pos, Depth depth) { } // namespace -// Called at startup to initialize various lookup tables -void Search::init() { - - for (int i = 1; i < MAX_MOVES; ++i) - Reductions[i] = int((20.37 + std::log(Threads.size()) / 2) * std::log(i)); +Search::Worker::Worker(SharedState& sharedState, + std::unique_ptr sm, + size_t thread_id) : + // Unpack the SharedState struct into member variables + thread_idx(thread_id), + manager(std::move(sm)), + options(sharedState.options), + threads(sharedState.threads), + tt(sharedState.tt) { + clear(); } +void Search::Worker::start_searching() { + // Non-main threads go directly to iterative_deepening() + if (!is_mainthread()) + { + iterative_deepening(); + return; + } -// Resets search state to its initial value -void Search::clear() { - - Threads.main()->wait_for_search_finished(); - - Time.availableNodes = 0; - TT.clear(); - Threads.clear(); - Tablebases::init(Options["SyzygyPath"]); // Free mapped files -} - - -// Called when the program receives the UCI 'go' -// command. It searches from the root position and outputs the "bestmove". -void MainThread::search() { - - if (Limits.perft) + if (limits.perft) { - nodes = perft(rootPos, Limits.perft); + nodes = perft(rootPos, limits.perft); sync_cout << "\nNodes searched: " << nodes << "\n" << sync_endl; return; } - Color us = rootPos.side_to_move(); - Time.init(Limits, us, rootPos.game_ply()); - TT.new_search(); - - Eval::NNUE::verify(); + main_manager()->tm.init(limits, rootPos.side_to_move(), rootPos.game_ply(), options); + tt.new_search(); if (rootMoves.empty()) { @@ -232,73 +197,75 @@ void MainThread::search() { } else { - Threads.start_searching(); // start non-main threads - Thread::search(); // main thread start searching + threads.start_searching(); // start non-main threads + iterative_deepening(); // main thread start searching } // When we reach the maximum depth, we can arrive here without a raise of - // Threads.stop. However, if we are pondering or in an infinite search, + // threads.stop. However, if we are pondering or in an infinite search, // the UCI protocol states that we shouldn't print the best move before the // GUI sends a "stop" or "ponderhit" command. We therefore simply wait here // until the GUI sends one of those commands. - - while (!Threads.stop && (ponder || Limits.infinite)) + while (!threads.stop && (main_manager()->ponder || limits.infinite)) {} // Busy wait for a stop or a ponder reset // Stop the threads if not already stopped (also raise the stop if - // "ponderhit" just reset Threads.ponder). - Threads.stop = true; + // "ponderhit" just reset threads.ponder). + threads.stop = true; // Wait until all threads have finished - Threads.wait_for_search_finished(); + threads.wait_for_search_finished(); // When playing in 'nodes as time' mode, subtract the searched nodes from // the available ones before exiting. - if (Limits.npmsec) - Time.availableNodes += Limits.inc[us] - Threads.nodes_searched(); + if (limits.npmsec) + main_manager()->tm.advance_nodes_time(limits.inc[rootPos.side_to_move()] + - threads.nodes_searched()); - Thread* bestThread = this; + Worker* bestThread = this; Skill skill = - Skill(Options["Skill Level"], Options["UCI_LimitStrength"] ? int(Options["UCI_Elo"]) : 0); + Skill(options["Skill Level"], options["UCI_LimitStrength"] ? int(options["UCI_Elo"]) : 0); - if (int(Options["MultiPV"]) == 1 && !Limits.depth && !skill.enabled() + if (int(options["MultiPV"]) == 1 && !limits.depth && !skill.enabled() && rootMoves[0].pv[0] != Move::none()) - bestThread = Threads.get_best_thread(); + bestThread = threads.get_best_thread()->worker.get(); - bestPreviousScore = bestThread->rootMoves[0].score; - bestPreviousAverageScore = bestThread->rootMoves[0].averageScore; + main_manager()->bestPreviousScore = bestThread->rootMoves[0].score; + main_manager()->bestPreviousAverageScore = bestThread->rootMoves[0].averageScore; // Send again PV info if we have a new best thread if (bestThread != this) - sync_cout << UCI::pv(bestThread->rootPos, bestThread->completedDepth) << sync_endl; + sync_cout << UCI::pv(*bestThread, main_manager()->tm.elapsed(threads.nodes_searched()), + threads.nodes_searched(), threads.tb_hits(), tt.hashfull(), + TB::RootInTB) + << sync_endl; sync_cout << "bestmove " << UCI::move(bestThread->rootMoves[0].pv[0], rootPos.is_chess960()); if (bestThread->rootMoves[0].pv.size() > 1 - || bestThread->rootMoves[0].extract_ponder_from_tt(rootPos)) + || bestThread->rootMoves[0].extract_ponder_from_tt(tt, rootPos)) std::cout << " ponder " << UCI::move(bestThread->rootMoves[0].pv[1], rootPos.is_chess960()); std::cout << sync_endl; } - // Main iterative deepening loop. It calls search() // repeatedly with increasing depth until the allocated thinking time has been // consumed, the user stops the search, or the maximum search depth is reached. -void Thread::search() { +void Search::Worker::iterative_deepening() { // Allocate stack with extra size to allow access from (ss - 7) to (ss + 2): // (ss - 7) is needed for update_continuation_histories(ss - 1) which accesses (ss - 6), // (ss + 2) is needed for initialization of cutOffCnt and killers. - Stack stack[MAX_PLY + 10], *ss = stack + 7; - Move pv[MAX_PLY + 1]; - Value alpha, beta; - Move lastBestMove = Move::none(); - Depth lastBestMoveDepth = 0; - MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr); - double timeReduction = 1, totBestMoveChanges = 0; - Color us = rootPos.side_to_move(); - int delta, iterIdx = 0; + Stack stack[MAX_PLY + 10], *ss = stack + 7; + Move pv[MAX_PLY + 1]; + Value alpha, beta; + Move lastBestMove = Move::none(); + Depth lastBestMoveDepth = 0; + SearchManager* mainThread = (thread_idx == 0 ? main_manager() : nullptr); + double timeReduction = 1, totBestMoveChanges = 0; + Color us = rootPos.side_to_move(); + int delta, iterIdx = 0; std::memset(ss - 7, 0, 10 * sizeof(Stack)); for (int i = 7; i > 0; --i) @@ -313,7 +280,7 @@ void Thread::search() { ss->pv = pv; - bestValue = -VALUE_INFINITE; + iterBestValue = -VALUE_INFINITE; if (mainThread) { @@ -325,8 +292,8 @@ void Thread::search() { mainThread->iterValue[i] = mainThread->bestPreviousScore; } - size_t multiPV = size_t(Options["MultiPV"]); - Skill skill(Options["Skill Level"], Options["UCI_LimitStrength"] ? int(Options["UCI_Elo"]) : 0); + size_t multiPV = size_t(options["MultiPV"]); + Skill skill(options["Skill Level"], options["UCI_LimitStrength"] ? int(options["UCI_Elo"]) : 0); // When playing with strength handicap enable MultiPV search that we will // use behind-the-scenes to retrieve a set of possible moves. @@ -338,8 +305,8 @@ void Thread::search() { int searchAgainCounter = 0; // Iterative deepening loop until requested to stop or the target depth is reached - while (++rootDepth < MAX_PLY && !Threads.stop - && !(Limits.depth && mainThread && rootDepth > Limits.depth)) + while (++rootDepth < MAX_PLY && !threads.stop + && !(limits.depth && mainThread && rootDepth > limits.depth)) { // Age out PV variability metric if (mainThread) @@ -353,11 +320,11 @@ void Thread::search() { size_t pvFirst = 0; pvLast = 0; - if (!Threads.increaseDepth) + if (!threads.increaseDepth) searchAgainCounter++; // MultiPV loop. We perform a full root search for each PV line - for (pvIdx = 0; pvIdx < multiPV && !Threads.stop; ++pvIdx) + for (pvIdx = 0; pvIdx < multiPV && !threads.stop; ++pvIdx) { if (pvIdx == pvLast) { @@ -390,7 +357,7 @@ void Thread::search() { // for every four searchAgain steps (see issue #2717). Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt - 3 * (searchAgainCounter + 1) / 4); - bestValue = Stockfish::search(rootPos, ss, alpha, beta, adjustedDepth, false); + iterBestValue = search(rootPos, ss, alpha, beta, adjustedDepth, false); // Bring the best move to the front. It is critical that sorting // is done with a stable algorithm because all the values but the @@ -403,29 +370,32 @@ void Thread::search() { // If search has been stopped, we break immediately. Sorting is // safe because RootMoves is still valid, although it refers to // the previous iteration. - if (Threads.stop) + if (threads.stop) break; // When failing high/low give some update (without cluttering // the UI) before a re-search. - if (mainThread && multiPV == 1 && (bestValue <= alpha || bestValue >= beta) - && Time.elapsed() > 3000) - sync_cout << UCI::pv(rootPos, rootDepth) << sync_endl; + if (mainThread && multiPV == 1 && (iterBestValue <= alpha || iterBestValue >= beta) + && mainThread->tm.elapsed(threads.nodes_searched()) > 3000) + sync_cout << UCI::pv(*this, mainThread->tm.elapsed(threads.nodes_searched()), + threads.nodes_searched(), threads.tb_hits(), tt.hashfull(), + TB::RootInTB) + << sync_endl; // In case of failing low/high increase aspiration window and // re-search, otherwise exit the loop. - if (bestValue <= alpha) + if (iterBestValue <= alpha) { beta = (alpha + beta) / 2; - alpha = std::max(bestValue - delta, -VALUE_INFINITE); + alpha = std::max(iterBestValue - delta, -VALUE_INFINITE); failedHighCnt = 0; if (mainThread) mainThread->stopOnPonderhit = false; } - else if (bestValue >= beta) + else if (iterBestValue >= beta) { - beta = std::min(bestValue + delta, int(VALUE_INFINITE)); + beta = std::min(iterBestValue + delta, int(VALUE_INFINITE)); ++failedHighCnt; } else @@ -439,11 +409,16 @@ void Thread::search() { // Sort the PV lines searched so far and update the GUI std::stable_sort(rootMoves.begin() + pvFirst, rootMoves.begin() + pvIdx + 1); - if (mainThread && (Threads.stop || pvIdx + 1 == multiPV || Time.elapsed() > 3000)) - sync_cout << UCI::pv(rootPos, rootDepth) << sync_endl; + if (mainThread + && (threads.stop || pvIdx + 1 == multiPV + || mainThread->tm.elapsed(threads.nodes_searched()) > 3000)) + sync_cout << UCI::pv(*this, mainThread->tm.elapsed(threads.nodes_searched()), + threads.nodes_searched(), threads.tb_hits(), tt.hashfull(), + TB::RootInTB) + << sync_endl; } - if (!Threads.stop) + if (!threads.stop) completedDepth = rootDepth; if (rootMoves[0].pv[0] != lastBestMove) @@ -453,60 +428,62 @@ void Thread::search() { } // Have we found a "mate in x"? - if (Limits.mate && bestValue >= VALUE_MATE_IN_MAX_PLY - && VALUE_MATE - bestValue <= 2 * Limits.mate) - Threads.stop = true; + if (limits.mate && iterBestValue >= VALUE_MATE_IN_MAX_PLY + && VALUE_MATE - iterBestValue <= 2 * limits.mate) + threads.stop = true; if (!mainThread) continue; // If the skill level is enabled and time is up, pick a sub-optimal best move if (skill.enabled() && skill.time_to_pick(rootDepth)) - skill.pick_best(multiPV); + skill.pick_best(rootMoves, multiPV); // Use part of the gained time from a previous stable move for the current move - for (Thread* th : Threads) + for (Thread* th : threads) { - totBestMoveChanges += th->bestMoveChanges; - th->bestMoveChanges = 0; + totBestMoveChanges += th->worker->bestMoveChanges; + th->worker->bestMoveChanges = 0; } // Do we have time for the next iteration? Can we stop searching now? - if (Limits.use_time_management() && !Threads.stop && !mainThread->stopOnPonderhit) + if (limits.use_time_management() && !threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (66 + 14 * (mainThread->bestPreviousAverageScore - bestValue) - + 6 * (mainThread->iterValue[iterIdx] - bestValue)) + double fallingEval = (66 + 14 * (mainThread->bestPreviousAverageScore - iterBestValue) + + 6 * (mainThread->iterValue[iterIdx] - iterBestValue)) / 616.6; fallingEval = std::clamp(fallingEval, 0.51, 1.51); // If the bestMove is stable over several iterations, reduce time accordingly timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.56 : 0.69; double reduction = (1.4 + mainThread->previousTimeReduction) / (2.17 * timeReduction); - double bestMoveInstability = 1 + 1.79 * totBestMoveChanges / Threads.size(); + double bestMoveInstability = 1 + 1.79 * totBestMoveChanges / threads.size(); - double totalTime = Time.optimum() * fallingEval * reduction * bestMoveInstability; + double totalTime = + mainThread->tm.optimum() * fallingEval * reduction * bestMoveInstability; // Cap used time in case of a single legal move for a better viewer experience if (rootMoves.size() == 1) totalTime = std::min(500.0, totalTime); // Stop the search if we have exceeded the totalTime - if (Time.elapsed() > totalTime) + if (mainThread->tm.elapsed(threads.nodes_searched()) > totalTime) { // If we are allowed to ponder do not stop the search now but // keep pondering until the GUI sends "ponderhit" or "stop". if (mainThread->ponder) mainThread->stopOnPonderhit = true; else - Threads.stop = true; + threads.stop = true; } - else if (!mainThread->ponder && Time.elapsed() > totalTime * 0.50) - Threads.increaseDepth = false; + else if (!mainThread->ponder + && mainThread->tm.elapsed(threads.nodes_searched()) > totalTime * 0.50) + threads.increaseDepth = false; else - Threads.increaseDepth = true; + threads.increaseDepth = true; } - mainThread->iterValue[iterIdx] = bestValue; + mainThread->iterValue[iterIdx] = iterBestValue; iterIdx = (iterIdx + 1) & 3; } @@ -517,16 +494,34 @@ void Thread::search() { // If the skill level is enabled, swap the best PV line with the sub-optimal one if (skill.enabled()) - std::swap(rootMoves[0], *std::find(rootMoves.begin(), rootMoves.end(), - skill.best ? skill.best : skill.pick_best(multiPV))); + std::swap(rootMoves[0], + *std::find(rootMoves.begin(), rootMoves.end(), + skill.best ? skill.best : skill.pick_best(rootMoves, multiPV))); } +void Search::Worker::clear() { + counterMoves.fill(Move::none()); + mainHistory.fill(0); + captureHistory.fill(0); + pawnHistory.fill(0); + correctionHistory.fill(0); + + for (bool inCheck : {false, true}) + for (StatsType c : {NoCaptures, Captures}) + for (auto& to : continuationHistory[inCheck][c]) + for (auto& h : to) + h->fill(-71); -namespace { -// Main search function for both PV and non-PV nodes + for (int i = 1; i < MAX_MOVES; ++i) + reductions[i] = int((20.37 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); +} + + +// Main search function for both PV and non-PV nodes. template -Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) { +Value Search::Worker::search( + Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) { constexpr bool PvNode = nodeType != NonPV; constexpr bool rootNode = nodeType == Root; @@ -539,7 +534,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // if the opponent had an alternative move earlier to this position. if (!rootNode && alpha < VALUE_DRAW && pos.has_game_cycle(ss->ply)) { - alpha = value_draw(pos.this_thread()); + alpha = value_draw(this->nodes); if (alpha >= beta) return alpha; } @@ -564,7 +559,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo int moveCount, captureCount, quietCount; // Step 1. Initialize node - Thread* thisThread = pos.this_thread(); + Worker* thisThread = this; ss->inCheck = pos.checkers(); priorCapture = pos.captured_piece(); Color us = pos.side_to_move(); @@ -573,8 +568,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo maxValue = VALUE_INFINITE; // Check for the available remaining time - if (thisThread == Threads.main()) - static_cast(thisThread)->check_time(); + if (is_mainthread()) + main_manager()->check_time(*this); // Used to send selDepth info to GUI (selDepth counts from 1, ply from 0) if (PvNode && thisThread->selDepth < ss->ply + 1) @@ -583,10 +578,10 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo if (!rootNode) { // Step 2. Check for aborted search and immediate draw - if (Threads.stop.load(std::memory_order_relaxed) || pos.is_draw(ss->ply) + if (threads.stop.load(std::memory_order_relaxed) || pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) - return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos) - : value_draw(pos.this_thread()); + return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos, *thisThread) + : value_draw(thisThread->nodes); // 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 @@ -614,7 +609,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Step 4. Transposition table lookup. excludedMove = ss->excludedMove; posKey = pos.key(); - tte = TT.probe(posKey, ss->ttHit); + tte = tt.probe(posKey, ss->ttHit); ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] : ss->ttHit ? tte->move() @@ -638,7 +633,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo { // Bonus for a quiet ttMove that fails high (~2 Elo) if (!ttCapture) - update_quiet_stats(pos, ss, ttMove, stat_bonus(depth)); + update_quiet_stats(pos, ss, *this, ttMove, stat_bonus(depth)); // Extra penalty for early quiet moves of // the previous ply (~0 Elo on STC, ~2 Elo on LTC). @@ -676,8 +671,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo TB::WDLScore wdl = Tablebases::probe_wdl(pos, &err); // Force check of time on the next occasion - if (thisThread == Threads.main()) - static_cast(thisThread)->callsCnt = 0; + if (is_mainthread()) + main_manager()->callsCnt = 0; if (err != TB::ProbeState::FAIL) { @@ -699,7 +694,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo if (b == BOUND_EXACT || (b == BOUND_LOWER ? value >= beta : value <= alpha)) { tte->save(posKey, value_to_tt(value, ss->ply), ss->ttPv, b, - std::min(MAX_PLY - 1, depth + 6), Move::none(), VALUE_NONE); + std::min(MAX_PLY - 1, depth + 6), Move::none(), VALUE_NONE, + tt.generation()); return value; } @@ -715,7 +711,6 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo } } - CapturePieceToHistory& captureHistory = thisThread->captureHistory; Value unadjustedStaticEval = VALUE_NONE; @@ -739,7 +734,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Never assume anything about values stored in TT unadjustedStaticEval = ss->staticEval = eval = tte->eval(); if (eval == VALUE_NONE) - unadjustedStaticEval = ss->staticEval = eval = evaluate(pos); + unadjustedStaticEval = ss->staticEval = eval = evaluate(pos, *thisThread); else if (PvNode) Eval::NNUE::hint_common_parent_position(pos); @@ -757,7 +752,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo } else { - unadjustedStaticEval = ss->staticEval = eval = evaluate(pos); + unadjustedStaticEval = ss->staticEval = eval = evaluate(pos, *thisThread); Value newEval = ss->staticEval @@ -769,7 +764,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Static evaluation is saved as it was before adjustment by correction history tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, Move::none(), - unadjustedStaticEval); + unadjustedStaticEval, tt.generation()); } // Use static evaluation difference to improve quiet move ordering (~9 Elo) @@ -827,7 +822,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo ss->currentMove = Move::null(); ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; - pos.do_null_move(st); + pos.do_null_move(st, tt); Value nullValue = -search(pos, ss + 1, -beta, -beta + 1, depth - R, !cutNode); @@ -885,7 +880,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo { assert(probCutBeta < VALUE_INFINITE && probCutBeta > beta); - MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, &captureHistory); + MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, &thisThread->captureHistory); while ((move = mp.next_move()) != Move::none()) if (move != excludedMove && pos.legal(move)) @@ -893,13 +888,14 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo assert(pos.capture_stage(move)); // Prefetch the TT entry for the resulting position - prefetch(TT.first_entry(pos.key_after(move))); + prefetch(tt.first_entry(pos.key_after(move))); ss->currentMove = move; ss->continuationHistory = - &thisThread + &this ->continuationHistory[ss->inCheck][true][pos.moved_piece(move)][move.to_sq()]; + thisThread->nodes.fetch_add(1, std::memory_order_relaxed); pos.do_move(move, st); // Perform a preliminary qsearch to verify that the move holds @@ -916,7 +912,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo { // Save ProbCut data into transposition table tte->save(posKey, value_to_tt(value, ss->ply), ss->ttPv, BOUND_LOWER, depth - 3, - move, unadjustedStaticEval); + move, unadjustedStaticEval, tt.generation()); return std::abs(value) < VALUE_TB_WIN_IN_MAX_PLY ? value - (probCutBeta - beta) : value; } @@ -944,8 +940,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo Move countermove = prevSq != SQ_NONE ? thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] : Move::none(); - MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &captureHistory, contHist, - &thisThread->pawnHistory, countermove, ss->killers); + MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory, + contHist, &thisThread->pawnHistory, countermove, ss->killers); value = bestValue; moveCountPruning = singularQuietLMR = false; @@ -978,7 +974,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo ss->moveCount = ++moveCount; - if (rootNode && thisThread == Threads.main() && Time.elapsed() > 3000) + if (rootNode && is_mainthread() + && main_manager()->tm.elapsed(threads.nodes_searched()) > 3000) sync_cout << "info depth " << depth << " currmove " << UCI::move(move, pos.is_chess960()) << " currmovenumber " << moveCount + thisThread->pvIdx << sync_endl; @@ -995,7 +992,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo int delta = beta - alpha; - Depth r = reduction(improving, depth, moveCount, delta, thisThread->rootDelta); + Depth r = reduction(improving, depth, moveCount, delta); // Step 14. Pruning at shallow depth (~120 Elo). // Depth conditions are important for mate finding. @@ -1016,7 +1013,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo Piece capturedPiece = pos.piece_on(move.to_sq()); int futilityEval = ss->staticEval + 238 + 305 * lmrDepth + PieceValue[capturedPiece] - + captureHistory[movedPiece][move.to_sq()][type_of(capturedPiece)] / 7; + + thisThread->captureHistory[movedPiece][move.to_sq()][type_of(capturedPiece)] + / 7; if (futilityEval < alpha) continue; } @@ -1135,8 +1133,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Recapture extensions (~1 Elo) else if (PvNode && move == ttMove && move.to_sq() == prevSq - && captureHistory[movedPiece][move.to_sq()] - [type_of(pos.piece_on(move.to_sq()))] + && thisThread->captureHistory[movedPiece][move.to_sq()] + [type_of(pos.piece_on(move.to_sq()))] > 4146) extension = 1; } @@ -1146,7 +1144,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo ss->doubleExtensions = (ss - 1)->doubleExtensions + (extension == 2); // Speculative prefetch as early as possible - prefetch(TT.first_entry(pos.key_after(move))); + prefetch(tt.first_entry(pos.key_after(move))); // Update the current move (this must be done after singular extension search) ss->currentMove = move; @@ -1154,6 +1152,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo &thisThread->continuationHistory[ss->inCheck][capture][movedPiece][move.to_sq()]; // Step 16. Make the move + thisThread->nodes.fetch_add(1, std::memory_order_relaxed); pos.do_move(move, st, givesCheck); // Decrease reduction if position is or has been on the PV (~4 Elo) @@ -1269,7 +1268,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // Finished searching the move. If a stop occurred, the return value of // the search cannot be trusted, and we return immediately without // updating best move, PV and TT. - if (Threads.stop.load(std::memory_order_relaxed)) + if (threads.stop.load(std::memory_order_relaxed)) return VALUE_ZERO; if (rootNode) @@ -1371,8 +1370,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // If there is a move that produces search value greater than alpha we update the stats of searched moves else if (bestMove) - update_all_stats(pos, ss, bestMove, bestValue, beta, prevSq, quietsSearched, quietCount, - capturesSearched, captureCount, depth); + update_all_stats(pos, ss, *this, bestMove, bestValue, beta, prevSq, quietsSearched, + quietCount, capturesSearched, captureCount, depth); // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) @@ -1400,7 +1399,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo bestValue >= beta ? BOUND_LOWER : PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER, - depth, bestMove, unadjustedStaticEval); + depth, bestMove, unadjustedStaticEval, tt.generation()); // Adjust correction history if (!ss->inCheck && (!bestMove || !pos.capture(bestMove)) @@ -1422,7 +1421,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo // function with zero depth, or recursively with further decreasing depth per call. // (~155 Elo) template -Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { +Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { static_assert(nodeType != Root); constexpr bool PvNode = nodeType == PV; @@ -1435,7 +1434,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { // if the opponent had an alternative move earlier to this position. if (alpha < VALUE_DRAW && pos.has_game_cycle(ss->ply)) { - alpha = value_draw(pos.this_thread()); + alpha = value_draw(this->nodes); if (alpha >= beta) return alpha; } @@ -1460,7 +1459,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { ss->pv[0] = Move::none(); } - Thread* thisThread = pos.this_thread(); + Worker* thisThread = this; bestMove = Move::none(); ss->inCheck = pos.checkers(); moveCount = 0; @@ -1471,7 +1470,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { // Step 2. Check for an immediate draw or maximum ply reached if (pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) - return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos) : VALUE_DRAW; + return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos, *thisThread) : VALUE_DRAW; assert(0 <= ss->ply && ss->ply < MAX_PLY); @@ -1480,7 +1479,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { // Step 3. Transposition table lookup posKey = pos.key(); - tte = TT.probe(posKey, ss->ttHit); + tte = tt.probe(posKey, ss->ttHit); ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; ttMove = ss->ttHit ? tte->move() : Move::none(); pvHit = ss->ttHit && tte->is_pv(); @@ -1502,7 +1501,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { { // Never assume anything about values stored in TT if ((unadjustedStaticEval = ss->staticEval = bestValue = tte->eval()) == VALUE_NONE) - unadjustedStaticEval = ss->staticEval = bestValue = evaluate(pos); + unadjustedStaticEval = ss->staticEval = bestValue = evaluate(pos, *thisThread); Value newEval = ss->staticEval @@ -1522,7 +1521,8 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { { // In case of null move search, use previous static eval with a different sign unadjustedStaticEval = ss->staticEval = bestValue = - (ss - 1)->currentMove != Move::null() ? evaluate(pos) : -(ss - 1)->staticEval; + (ss - 1)->currentMove != Move::null() ? evaluate(pos, *thisThread) + : -(ss - 1)->staticEval; Value newEval = ss->staticEval @@ -1539,7 +1539,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { { if (!ss->ttHit) tte->save(posKey, value_to_tt(bestValue, ss->ply), false, BOUND_LOWER, DEPTH_NONE, - Move::none(), unadjustedStaticEval); + Move::none(), unadjustedStaticEval, tt.generation()); return bestValue; } @@ -1632,7 +1632,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { } // Speculative prefetch as early as possible - prefetch(TT.first_entry(pos.key_after(move))); + prefetch(tt.first_entry(pos.key_after(move))); // Update the current move ss->currentMove = move; @@ -1643,6 +1643,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { quietCheckEvasions += !capture && ss->inCheck; // Step 7. Make and search the move + thisThread->nodes.fetch_add(1, std::memory_order_relaxed); pos.do_move(move, st, givesCheck); value = -qsearch(pos, ss + 1, -beta, -alpha, depth - 1); pos.undo_move(move); @@ -1686,7 +1687,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { // Static evaluation is saved as it was before adjustment by correction history tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit, bestValue >= beta ? BOUND_LOWER : BOUND_UPPER, ttDepth, bestMove, - unadjustedStaticEval); + unadjustedStaticEval, tt.generation()); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); @@ -1694,6 +1695,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { } +namespace { // Adjusts a mate or TB score from "plies to mate from the root" // to "plies to mate from the current position". Standard scores are unchanged. // The function is called before storing a value in the transposition table. @@ -1759,6 +1761,7 @@ void update_pv(Move* pv, Move move, const Move* childPv) { // Updates stats at the end of search() when a bestMove is found void update_all_stats(const Position& pos, Stack* ss, + Search::Worker& workerThread, Move bestMove, Value bestValue, Value beta, @@ -1770,8 +1773,7 @@ void update_all_stats(const Position& pos, Depth depth) { Color us = pos.side_to_move(); - Thread* thisThread = pos.this_thread(); - CapturePieceToHistory& captureHistory = thisThread->captureHistory; + CapturePieceToHistory& captureHistory = workerThread.captureHistory; Piece moved_piece = pos.moved_piece(bestMove); PieceType captured; @@ -1784,19 +1786,19 @@ void update_all_stats(const Position& pos, : stat_bonus(depth); // smaller bonus // Increase stats for the best move in case it was a quiet move - update_quiet_stats(pos, ss, bestMove, bestMoveBonus); + update_quiet_stats(pos, ss, workerThread, bestMove, bestMoveBonus); int pIndex = pawn_structure_index(pos); - thisThread->pawnHistory[pIndex][moved_piece][bestMove.to_sq()] << quietMoveBonus; + workerThread.pawnHistory[pIndex][moved_piece][bestMove.to_sq()] << quietMoveBonus; // Decrease stats for all non-best quiet moves for (int i = 0; i < quietCount; ++i) { - thisThread - ->pawnHistory[pIndex][pos.moved_piece(quietsSearched[i])][quietsSearched[i].to_sq()] + workerThread + .pawnHistory[pIndex][pos.moved_piece(quietsSearched[i])][quietsSearched[i].to_sq()] << -quietMoveMalus; - thisThread->mainHistory[us][quietsSearched[i].from_to()] << -quietMoveMalus; + workerThread.mainHistory[us][quietsSearched[i].from_to()] << -quietMoveMalus; update_continuation_histories(ss, pos.moved_piece(quietsSearched[i]), quietsSearched[i].to_sq(), -quietMoveMalus); } @@ -1842,7 +1844,8 @@ void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { // Updates move sorting heuristics -void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus) { +void update_quiet_stats( + const Position& pos, Stack* ss, Search::Worker& workerThread, Move move, int bonus) { // Update killers if (ss->killers[0] != move) @@ -1851,25 +1854,23 @@ void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus) { ss->killers[0] = move; } - Color us = pos.side_to_move(); - Thread* thisThread = pos.this_thread(); - thisThread->mainHistory[us][move.from_to()] << bonus; + Color us = pos.side_to_move(); + workerThread.mainHistory[us][move.from_to()] << bonus; update_continuation_histories(ss, pos.moved_piece(move), move.to_sq(), bonus); // Update countermove history if (((ss - 1)->currentMove).is_ok()) { - Square prevSq = ((ss - 1)->currentMove).to_sq(); - thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] = move; + Square prevSq = ((ss - 1)->currentMove).to_sq(); + workerThread.counterMoves[pos.piece_on(prevSq)][prevSq] = move; } } +} // When playing with strength handicap, choose the best move among a set of RootMoves // using a statistical rule dependent on 'level'. Idea by Heinz van Saanen. -Move Skill::pick_best(size_t multiPV) { - - const RootMoves& rootMoves = Threads.main()->rootMoves; - static PRNG rng(now()); // PRNG sequence should be non-deterministic +Move Skill::pick_best(const RootMoves& rootMoves, size_t multiPV) { + static PRNG rng(now()); // PRNG sequence should be non-deterministic // RootMoves are already sorted by score in descending order Value topScore = rootMoves[0].score; @@ -1897,23 +1898,20 @@ Move Skill::pick_best(size_t multiPV) { return best; } -} // namespace - // Used to print debug info and, more importantly, // to detect when we are out of available time and thus stop the search. -void MainThread::check_time() { - +void SearchManager::check_time(Search::Worker& worker) { if (--callsCnt > 0) return; // When using nodes, ensure checking rate is not lower than 0.1% of nodes - callsCnt = Limits.nodes ? std::min(512, int(Limits.nodes / 1024)) : 512; + callsCnt = worker.limits.nodes ? std::min(512, int(worker.limits.nodes / 1024)) : 512; static TimePoint lastInfoTime = now(); - TimePoint elapsed = Time.elapsed(); - TimePoint tick = Limits.startTime + elapsed; + TimePoint elapsed = tm.elapsed(worker.threads.nodes_searched()); + TimePoint tick = worker.limits.startTime + elapsed; if (tick - lastInfoTime >= 1000) { @@ -1925,72 +1923,18 @@ void MainThread::check_time() { if (ponder) return; - if ((Limits.use_time_management() && (elapsed > Time.maximum() || stopOnPonderhit)) - || (Limits.movetime && elapsed >= Limits.movetime) - || (Limits.nodes && Threads.nodes_searched() >= uint64_t(Limits.nodes))) - Threads.stop = true; + if ((worker.limits.use_time_management() && (elapsed > tm.maximum() || stopOnPonderhit)) + || (worker.limits.movetime && elapsed >= worker.limits.movetime) + || (worker.limits.nodes + && worker.threads.nodes_searched() >= uint64_t(worker.limits.nodes))) + worker.threads.stop = true; } - -// Formats PV information according to the UCI protocol. UCI requires -// that all (if any) unsearched PV lines are sent using a previous search score. -string UCI::pv(const Position& pos, Depth depth) { - - std::stringstream ss; - TimePoint elapsed = Time.elapsed() + 1; - const RootMoves& rootMoves = pos.this_thread()->rootMoves; - size_t pvIdx = pos.this_thread()->pvIdx; - size_t multiPV = std::min(size_t(Options["MultiPV"]), rootMoves.size()); - uint64_t nodesSearched = Threads.nodes_searched(); - uint64_t tbHits = Threads.tb_hits() + (TB::RootInTB ? rootMoves.size() : 0); - - for (size_t i = 0; i < multiPV; ++i) - { - bool updated = rootMoves[i].score != -VALUE_INFINITE; - - if (depth == 1 && !updated && i > 0) - continue; - - Depth d = updated ? depth : std::max(1, depth - 1); - Value v = updated ? rootMoves[i].uciScore : rootMoves[i].previousScore; - - if (v == -VALUE_INFINITE) - v = VALUE_ZERO; - - bool tb = TB::RootInTB && std::abs(v) <= VALUE_TB; - v = tb ? rootMoves[i].tbScore : v; - - if (ss.rdbuf()->in_avail()) // Not at first line - ss << "\n"; - - ss << "info" - << " depth " << d << " seldepth " << rootMoves[i].selDepth << " multipv " << i + 1 - << " score " << UCI::value(v); - - if (Options["UCI_ShowWDL"]) - ss << UCI::wdl(v, pos.game_ply()); - - if (i == pvIdx && !tb && updated) // tablebase- and previous-scores are exact - ss << (rootMoves[i].scoreLowerbound - ? " lowerbound" - : (rootMoves[i].scoreUpperbound ? " upperbound" : "")); - - ss << " nodes " << nodesSearched << " nps " << nodesSearched * 1000 / elapsed - << " hashfull " << TT.hashfull() << " tbhits " << tbHits << " time " << elapsed << " pv"; - - for (Move m : rootMoves[i].pv) - ss << " " << UCI::move(m, pos.is_chess960()); - } - - return ss.str(); -} - - // Called in case we have no ponder move before exiting the search, // for instance, in case we stop the search during a fail high at root. // We try hard to have a ponder move to return to the GUI, // otherwise in case of 'ponder on' we have nothing to think about. -bool RootMove::extract_ponder_from_tt(Position& pos) { +bool RootMove::extract_ponder_from_tt(const TranspositionTable& tt, Position& pos) { StateInfo st; ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize); @@ -2003,7 +1947,7 @@ bool RootMove::extract_ponder_from_tt(Position& pos) { return false; pos.do_move(pv[0], st); - TTEntry* tte = TT.probe(pos.key(), ttHit); + TTEntry* tte = tt.probe(pos.key(), ttHit); if (ttHit) { @@ -2016,12 +1960,14 @@ bool RootMove::extract_ponder_from_tt(Position& pos) { return pv.size() > 1; } -void Tablebases::rank_root_moves(Position& pos, Search::RootMoves& rootMoves) { +void Tablebases::rank_root_moves(const OptionsMap& options, + Position& pos, + Search::RootMoves& rootMoves) { RootInTB = false; - UseRule50 = bool(Options["Syzygy50MoveRule"]); - ProbeDepth = int(Options["SyzygyProbeDepth"]); - Cardinality = int(Options["SyzygyProbeLimit"]); + UseRule50 = bool(options["Syzygy50MoveRule"]); + ProbeDepth = int(options["SyzygyProbeDepth"]); + Cardinality = int(options["SyzygyProbeLimit"]); bool dtz_available = true; // Tables with fewer pieces than SyzygyProbeLimit are searched with @@ -2035,13 +1981,13 @@ void Tablebases::rank_root_moves(Position& pos, Search::RootMoves& rootMoves) { if (Cardinality >= popcount(pos.pieces()) && !pos.can_castle(ANY_CASTLING)) { // Rank moves using DTZ tables - RootInTB = root_probe(pos, rootMoves); + RootInTB = root_probe(pos, rootMoves, options["Syzygy50MoveRule"]); if (!RootInTB) { // DTZ tables are missing; try to rank moves using WDL tables dtz_available = false; - RootInTB = root_probe_wdl(pos, rootMoves); + RootInTB = root_probe_wdl(pos, rootMoves, options["Syzygy50MoveRule"]); } } diff --git a/src/search.h b/src/search.h index 72e275d36f7..48a5630cddb 100644 --- a/src/search.h +++ b/src/search.h @@ -19,19 +19,37 @@ #ifndef SEARCH_H_INCLUDED #define SEARCH_H_INCLUDED +#include +#include +#include #include +#include #include #include "misc.h" #include "movepick.h" +#include "position.h" +#include "timeman.h" #include "types.h" namespace Stockfish { -class Position; +// Different node types, used as a template parameter +enum NodeType { + NonPV, + PV, + Root +}; + +class TranspositionTable; +class ThreadPool; +class OptionsMap; +class UCI; namespace Search { +// Called at startup to initialize various lookup tables, after program startup +void init(int); // Stack struct keeps track of the information we need to remember from nodes // shallower and deeper in the tree during the search. Each search thread has @@ -61,7 +79,7 @@ struct RootMove { explicit RootMove(Move m) : pv(1, m) {} - bool extract_ponder_from_tt(Position& pos); + bool extract_ponder_from_tt(const TranspositionTable& tt, Position& pos); bool operator==(const Move& m) const { return pv[0] == m; } // Sort in descending order bool operator<(const RootMove& m) const { @@ -85,7 +103,6 @@ using RootMoves = std::vector; // LimitsType struct stores information sent by GUI about available time to // search the current move, maximum depth/time, or if we are in analysis mode. - struct LimitsType { // Init explicitly due to broken value-initialization of non POD in MSVC @@ -103,10 +120,136 @@ struct LimitsType { int64_t nodes; }; -extern LimitsType Limits; -void init(); -void clear(); +// The UCI stores the uci options, thread pool, and transposition table. +// This struct is used to easily forward data to the Search::Worker class. +struct SharedState { + SharedState(const OptionsMap& o, ThreadPool& tp, TranspositionTable& t) : + options(o), + threads(tp), + tt(t) {} + + const OptionsMap& options; + ThreadPool& threads; + TranspositionTable& tt; +}; + +class Worker; + +// Null Object Pattern, implement a common interface +// for the SearchManagers. A Null Object will be given to +// non-mainthread workers. +class ISearchManager { + public: + virtual ~ISearchManager() {} + virtual void check_time(Search::Worker&) = 0; +}; + +// SearchManager manages the search from the main thread. It is responsible for +// keeping track of the time, and storing data strictly related to the main thread. +class SearchManager: public ISearchManager { + public: + void check_time(Search::Worker& worker) override; + + Stockfish::TimeManagement tm; + int callsCnt; + std::atomic_bool ponder; + + double previousTimeReduction; + Value bestPreviousScore; + Value bestPreviousAverageScore; + Value iterValue[4]; + bool stopOnPonderhit; + + size_t id; +}; + +class NullSearchManager: public ISearchManager { + public: + void check_time(Search::Worker&) override {} +}; + +// Search::Worker is the class that does the actual search. +// It is instantiated once per thread, and it is responsible for keeping track +// of the search history, and storing data required for the search. +class Worker { + public: + Worker(SharedState&, std::unique_ptr, size_t); + + // Reset histories, usually before a new game + void clear(); + + // Called when the program receives the UCI 'go' + // command. It searches from the root position and outputs the "bestmove". + void start_searching(); + + bool is_mainthread() const { return thread_idx == 0; } + + // Public because evaluate uses this + Value iterBestValue, optimism[COLOR_NB]; + Value rootSimpleEval; + + // Public because they need to be updatable by the stats + CounterMoveHistory counterMoves; + ButterflyHistory mainHistory; + CapturePieceToHistory captureHistory; + ContinuationHistory continuationHistory[2][2]; + PawnHistory pawnHistory; + CorrectionHistory correctionHistory; + + private: + void iterative_deepening(); + + // Main search function for both PV and non-PV nodes + template + Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode); + + // Quiescence search function, which is called by the main search + template + Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth = 0); + + Depth reduction(bool i, Depth d, int mn, int delta) { + int reductionScale = reductions[d] * reductions[mn]; + return (reductionScale + 1346 - int(delta) * 896 / int(rootDelta)) / 1024 + + (!i && reductionScale > 880); + } + + // Get a pointer to the search manager, only allowed to be called by the + // main thread. + SearchManager* main_manager() const { + assert(thread_idx == 0); + return static_cast(manager.get()); + } + + LimitsType limits; + + size_t pvIdx, pvLast; + std::atomic nodes, tbHits, bestMoveChanges; + int selDepth, nmpMinPly; + + Position rootPos; + StateInfo rootState; + RootMoves rootMoves; + Depth rootDepth, completedDepth; + Value rootDelta; + + size_t thread_idx; + + // Reductions lookup table initialized at startup + int reductions[MAX_MOVES]; // [depth or moveNumber] + + // The main thread has a SearchManager, the others have a NullSearchManager + std::unique_ptr manager; + + const OptionsMap& options; + ThreadPool& threads; + TranspositionTable& tt; + + friend class Stockfish::ThreadPool; + friend class Stockfish::UCI; + friend class SearchManager; +}; + } // namespace Search diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 91013dcac28..6f30bf6b1f7 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -42,7 +42,6 @@ #include "../position.h" #include "../search.h" #include "../types.h" -#include "../uci.h" #ifndef _WIN32 #include @@ -1574,7 +1573,7 @@ int Tablebases::probe_dtz(Position& pos, ProbeState* result) { // Use the DTZ tables to rank root moves. // // A return value false indicates that not all probes were successful. -bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves) { +bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves, bool rule50) { ProbeState result = OK; StateInfo st; @@ -1585,7 +1584,7 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves) { // Check whether a position was repeated since the last zeroing move. bool rep = pos.has_repeated(); - int dtz, bound = Options["Syzygy50MoveRule"] ? (MAX_DTZ - 100) : 1; + int dtz, bound = rule50 ? (MAX_DTZ - 100) : 1; // Probe and rank each move for (auto& m : rootMoves) @@ -1647,7 +1646,7 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves) { // This is a fallback for the case that some or all DTZ tables are missing. // // A return value false indicates that not all probes were successful. -bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves) { +bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, bool rule50) { static const int WDL_to_rank[] = {-MAX_DTZ, -MAX_DTZ + 101, 0, MAX_DTZ - 101, MAX_DTZ}; @@ -1655,7 +1654,6 @@ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves) { StateInfo st; WDLScore wdl; - bool rule50 = Options["Syzygy50MoveRule"]; // Probe and rank each move for (auto& m : rootMoves) diff --git a/src/syzygy/tbprobe.h b/src/syzygy/tbprobe.h index cc8eb0d4d70..d7b412a10e5 100644 --- a/src/syzygy/tbprobe.h +++ b/src/syzygy/tbprobe.h @@ -25,6 +25,7 @@ namespace Stockfish { class Position; +class OptionsMap; } namespace Stockfish::Tablebases { @@ -47,12 +48,13 @@ enum ProbeState { extern int MaxCardinality; + void init(const std::string& paths); WDLScore probe_wdl(Position& pos, ProbeState* result); int probe_dtz(Position& pos, ProbeState* result); -bool root_probe(Position& pos, Search::RootMoves& rootMoves); -bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves); -void rank_root_moves(Position& pos, Search::RootMoves& rootMoves); +bool root_probe(Position& pos, Search::RootMoves& rootMoves, bool rule50); +bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, bool rule50); +void rank_root_moves(const OptionsMap& options, Position& pos, Search::RootMoves& rootMoves); } // namespace Stockfish::Tablebases diff --git a/src/thread.cpp b/src/thread.cpp index 01ccd4fc565..a512c0a52b8 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -23,9 +23,8 @@ #include #include #include -#include -#include #include +#include #include #include "evaluate.h" @@ -33,18 +32,21 @@ #include "movegen.h" #include "search.h" #include "syzygy/tbprobe.h" +#include "timeman.h" #include "tt.h" -#include "uci.h" +#include "types.h" +#include "ucioption.h" namespace Stockfish { -ThreadPool Threads; // Global object - - // Constructor launches the thread and waits until it goes to sleep // in idle_loop(). Note that 'searching' and 'exit' should be already set. -Thread::Thread(size_t n) : +Thread::Thread(Search::SharedState& sharedState, + std::unique_ptr sm, + size_t n) : + worker(std::make_unique(sharedState, std::move(sm), n)), idx(n), + nthreads(sharedState.options["Threads"]), stdThread(&Thread::idle_loop, this) { wait_for_search_finished(); @@ -62,24 +64,6 @@ Thread::~Thread() { stdThread.join(); } - -// Reset histories, usually before a new game -void Thread::clear() { - - counterMoves.fill(Move::none()); - mainHistory.fill(0); - captureHistory.fill(0); - pawnHistory.fill(0); - correctionHistory.fill(0); - - for (bool inCheck : {false, true}) - for (StatsType c : {NoCaptures, Captures}) - for (auto& to : continuationHistory[inCheck][c]) - for (auto& h : to) - h->fill(-71); -} - - // Wakes up the thread that will start the search void Thread::start_searching() { mutex.lock(); @@ -108,7 +92,7 @@ void Thread::idle_loop() { // some Windows NUMA hardware, for instance in fishtest. To make it simple, // just check if running threads are below a threshold, in this case, all this // NUMA machinery is not needed. - if (Options["Threads"] > 8) + if (nthreads > 8) WinProcGroup::bindThisThread(idx); while (true) @@ -123,36 +107,41 @@ void Thread::idle_loop() { lk.unlock(); - search(); + worker->start_searching(); } } // Creates/destroys threads to match the requested number. // Created and launched threads will immediately go to sleep in idle_loop. // Upon resizing, threads are recreated to allow for binding if necessary. -void ThreadPool::set(size_t requested) { +void ThreadPool::set(Search::SharedState sharedState) { if (threads.size() > 0) // destroy any existing thread(s) { - main()->wait_for_search_finished(); + main_thread()->wait_for_search_finished(); while (threads.size() > 0) delete threads.back(), threads.pop_back(); } + const size_t requested = sharedState.options["Threads"]; + if (requested > 0) // create new thread(s) { - threads.push_back(new MainThread(0)); + threads.push_back(new Thread( + sharedState, std::unique_ptr(new Search::SearchManager()), 0)); + while (threads.size() < requested) - threads.push_back(new Thread(threads.size())); + threads.push_back(new Thread( + sharedState, std::unique_ptr(new Search::NullSearchManager()), + threads.size())); clear(); - // Reallocate the hash with the new threadpool size - TT.resize(size_t(Options["Hash"])); + main_thread()->wait_for_search_finished(); - // Init thread number dependent search params. - Search::init(); + // Reallocate the hash with the new threadpool size + sharedState.tt.resize(sharedState.options["Hash"], requested); } } @@ -161,28 +150,31 @@ void ThreadPool::set(size_t requested) { void ThreadPool::clear() { for (Thread* th : threads) - th->clear(); + th->worker->clear(); - main()->callsCnt = 0; - main()->bestPreviousScore = VALUE_INFINITE; - main()->bestPreviousAverageScore = VALUE_INFINITE; - main()->previousTimeReduction = 1.0; + main_manager()->callsCnt = 0; + main_manager()->bestPreviousScore = VALUE_INFINITE; + main_manager()->bestPreviousAverageScore = VALUE_INFINITE; + main_manager()->previousTimeReduction = 1.0; + main_manager()->tm.clear(); } // Wakes up main thread waiting in idle_loop() and // returns immediately. Main thread will wake up other threads and start the search. -void ThreadPool::start_thinking(Position& pos, - StateListPtr& states, - const Search::LimitsType& limits, - bool ponderMode) { +void ThreadPool::start_thinking(const OptionsMap& options, + Position& pos, + StateListPtr& states, + Search::LimitsType limits, + bool ponderMode) { + + main_thread()->wait_for_search_finished(); - main()->wait_for_search_finished(); + main_manager()->stopOnPonderhit = stop = false; + main_manager()->ponder = ponderMode; + + increaseDepth = true; - main()->stopOnPonderhit = stop = false; - increaseDepth = true; - main()->ponder = ponderMode; - Search::Limits = limits; Search::RootMoves rootMoves; for (const auto& m : MoveList(pos)) @@ -191,7 +183,7 @@ void ThreadPool::start_thinking(Position& pos, rootMoves.emplace_back(m); if (!rootMoves.empty()) - Tablebases::rank_root_moves(pos, rootMoves); + Tablebases::rank_root_moves(options, pos, rootMoves); // After ownership transfer 'states' becomes empty, so if we stop the search // and call 'go' again without setting a new position states.get() == nullptr. @@ -207,15 +199,17 @@ void ThreadPool::start_thinking(Position& pos, // since they are read-only. for (Thread* th : threads) { - th->nodes = th->tbHits = th->nmpMinPly = th->bestMoveChanges = 0; - th->rootDepth = th->completedDepth = 0; - th->rootMoves = rootMoves; - th->rootPos.set(pos.fen(), pos.is_chess960(), &th->rootState, th); - th->rootState = setupStates->back(); - th->rootSimpleEval = Eval::simple_eval(pos, pos.side_to_move()); + th->worker->limits = limits; + th->worker->nodes = th->worker->tbHits = th->worker->nmpMinPly = + th->worker->bestMoveChanges = 0; + th->worker->rootDepth = th->worker->completedDepth = 0; + th->worker->rootMoves = rootMoves; + th->worker->rootPos.set(pos.fen(), pos.is_chess960(), &th->worker->rootState); + th->worker->rootState = setupStates->back(); + th->worker->rootSimpleEval = Eval::simple_eval(pos, pos.side_to_move()); } - main()->start_searching(); + main_thread()->start_searching(); } Thread* ThreadPool::get_best_thread() const { @@ -226,30 +220,32 @@ Thread* ThreadPool::get_best_thread() const { // Find the minimum score of all threads for (Thread* th : threads) - minScore = std::min(minScore, th->rootMoves[0].score); + minScore = std::min(minScore, th->worker->rootMoves[0].score); // Vote according to score and depth, and select the best thread auto thread_value = [minScore](Thread* th) { - return (th->rootMoves[0].score - minScore + 14) * int(th->completedDepth); + return (th->worker->rootMoves[0].score - minScore + 14) * int(th->worker->completedDepth); }; for (Thread* th : threads) - votes[th->rootMoves[0].pv[0]] += thread_value(th); + votes[th->worker->rootMoves[0].pv[0]] += thread_value(th); for (Thread* th : threads) - if (std::abs(bestThread->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY) + if (std::abs(bestThread->worker->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY) { // Make sure we pick the shortest mate / TB conversion or stave off mate the longest - if (th->rootMoves[0].score > bestThread->rootMoves[0].score) + if (th->worker->rootMoves[0].score > bestThread->worker->rootMoves[0].score) bestThread = th; } - else if (th->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY - || (th->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY - && (votes[th->rootMoves[0].pv[0]] > votes[bestThread->rootMoves[0].pv[0]] - || (votes[th->rootMoves[0].pv[0]] == votes[bestThread->rootMoves[0].pv[0]] - && thread_value(th) * int(th->rootMoves[0].pv.size() > 2) + else if (th->worker->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY + || (th->worker->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY + && (votes[th->worker->rootMoves[0].pv[0]] + > votes[bestThread->worker->rootMoves[0].pv[0]] + || (votes[th->worker->rootMoves[0].pv[0]] + == votes[bestThread->worker->rootMoves[0].pv[0]] + && thread_value(th) * int(th->worker->rootMoves[0].pv.size() > 2) > thread_value(bestThread) - * int(bestThread->rootMoves[0].pv.size() > 2))))) + * int(bestThread->worker->rootMoves[0].pv.size() > 2))))) bestThread = th; return bestThread; @@ -257,7 +253,7 @@ Thread* ThreadPool::get_best_thread() const { // Start non-main threads - +// Will be invoked by main thread after it has started searching void ThreadPool::start_searching() { for (Thread* th : threads) diff --git a/src/thread.h b/src/thread.h index 7db7c15905b..6575b14e638 100644 --- a/src/thread.h +++ b/src/thread.h @@ -23,91 +23,76 @@ #include #include #include +#include #include #include -#include "movepick.h" #include "position.h" #include "search.h" #include "thread_win32_osx.h" -#include "types.h" namespace Stockfish { -// Thread class keeps together all the thread-related stuff. -class Thread { - - std::mutex mutex; - std::condition_variable cv; - size_t idx; - bool exit = false, searching = true; // Set before starting std::thread - NativeThread stdThread; +class OptionsMap; +using Value = int; +// Abstraction of a thread. It contains a pointer to the worker and a native thread. +// After construction, the native thread is started with idle_loop() +// waiting for a signal to start searching. +// When the signal is received, the thread starts searching and when +// the search is finished, it goes back to idle_loop() waiting for a new signal. +class Thread { public: - explicit Thread(size_t); + Thread(Search::SharedState&, std::unique_ptr, size_t); virtual ~Thread(); - virtual void search(); - void clear(); - void idle_loop(); - void start_searching(); - void wait_for_search_finished(); - size_t id() const { return idx; } - - size_t pvIdx, pvLast; - std::atomic nodes, tbHits, bestMoveChanges; - int selDepth, nmpMinPly; - Value bestValue; - - int optimism[COLOR_NB]; - - Position rootPos; - StateInfo rootState; - Search::RootMoves rootMoves; - Depth rootDepth, completedDepth; - int rootDelta; - Value rootSimpleEval; - CounterMoveHistory counterMoves; - ButterflyHistory mainHistory; - CapturePieceToHistory captureHistory; - ContinuationHistory continuationHistory[2][2]; - PawnHistory pawnHistory; - CorrectionHistory correctionHistory; -}; - - -// MainThread is a derived class specific for main thread -struct MainThread: public Thread { - using Thread::Thread; + void idle_loop(); + void start_searching(); + void wait_for_search_finished(); + size_t id() const { return idx; } - void search() override; - void check_time(); + std::unique_ptr worker; - double previousTimeReduction; - Value bestPreviousScore; - Value bestPreviousAverageScore; - Value iterValue[4]; - int callsCnt; - bool stopOnPonderhit; - std::atomic_bool ponder; + private: + std::mutex mutex; + std::condition_variable cv; + size_t idx, nthreads; + bool exit = false, searching = true; // Set before starting std::thread + NativeThread stdThread; }; // ThreadPool struct handles all the threads-related stuff like init, starting, // parking and, most importantly, launching a thread. All the access to threads // is done through this class. -struct ThreadPool { +class ThreadPool { - void start_thinking(Position&, StateListPtr&, const Search::LimitsType&, bool = false); - void clear(); - void set(size_t); + public: + ~ThreadPool() { + // destroy any existing thread(s) + if (threads.size() > 0) + { + main_thread()->wait_for_search_finished(); + + while (threads.size() > 0) + delete threads.back(), threads.pop_back(); + } + } - MainThread* main() const { return static_cast(threads.front()); } - uint64_t nodes_searched() const { return accumulate(&Thread::nodes); } - uint64_t tb_hits() const { return accumulate(&Thread::tbHits); } - Thread* get_best_thread() const; - void start_searching(); - void wait_for_search_finished() const; + void + start_thinking(const OptionsMap&, Position&, StateListPtr&, Search::LimitsType, bool = false); + void clear(); + void set(Search::SharedState); + + Search::SearchManager* main_manager() const { + return static_cast(main_thread()->worker.get()->manager.get()); + }; + Thread* main_thread() const { return threads.front(); } + uint64_t nodes_searched() const { return accumulate(&Search::Worker::nodes); } + uint64_t tb_hits() const { return accumulate(&Search::Worker::tbHits); } + Thread* get_best_thread() const; + void start_searching(); + void wait_for_search_finished() const; std::atomic_bool stop, increaseDepth; @@ -122,17 +107,15 @@ struct ThreadPool { StateListPtr setupStates; std::vector threads; - uint64_t accumulate(std::atomic Thread::*member) const { + uint64_t accumulate(std::atomic Search::Worker::*member) const { uint64_t sum = 0; for (Thread* th : threads) - sum += (th->*member).load(std::memory_order_relaxed); + sum += (th->worker.get()->*member).load(std::memory_order_relaxed); return sum; } }; -extern ThreadPool Threads; - } // namespace Stockfish #endif // #ifndef THREAD_H_INCLUDED diff --git a/src/thread_win32_osx.h b/src/thread_win32_osx.h index 4bc62d678ad..8d424b72a50 100644 --- a/src/thread_win32_osx.h +++ b/src/thread_win32_osx.h @@ -30,31 +30,35 @@ #if defined(__APPLE__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(USE_PTHREADS) #include + #include namespace Stockfish { -static const size_t TH_STACK_SIZE = 8 * 1024 * 1024; - -template> -void* start_routine(void* ptr) { - P* p = reinterpret_cast(ptr); - (p->first->*(p->second))(); // Call member function pointer - delete p; +// free function to be passed to pthread_create() +inline void* start_routine(void* ptr) { + auto func = reinterpret_cast*>(ptr); + (*func)(); // Call the function + delete func; return nullptr; } class NativeThread { - pthread_t thread; + static constexpr size_t TH_STACK_SIZE = 8 * 1024 * 1024; + public: - template> - explicit NativeThread(void (T::*fun)(), T* obj) { + template + explicit NativeThread(Function&& fun, Args&&... args) { + auto func = new std::function( + std::bind(std::forward(fun), std::forward(args)...)); + pthread_attr_t attr_storage, *attr = &attr_storage; pthread_attr_init(attr); pthread_attr_setstacksize(attr, TH_STACK_SIZE); - pthread_create(&thread, attr, start_routine, new P(obj, fun)); + pthread_create(&thread, attr, start_routine, func); } + void join() { pthread_join(thread, nullptr); } }; diff --git a/src/timeman.cpp b/src/timeman.cpp index 77db2f621df..121f8edb985 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -19,30 +19,47 @@ #include "timeman.h" #include +#include #include +#include #include "search.h" -#include "uci.h" +#include "ucioption.h" namespace Stockfish { -TimeManagement Time; // Our global time management object +TimePoint TimeManagement::optimum() const { return optimumTime; } +TimePoint TimeManagement::maximum() const { return maximumTime; } +TimePoint TimeManagement::elapsed(size_t nodes) const { + return useNodesTime ? TimePoint(nodes) : now() - startTime; +} + +void TimeManagement::clear() { + availableNodes = 0; // When in 'nodes as time' mode +} + +void TimeManagement::advance_nodes_time(std::int64_t nodes) { + assert(useNodesTime); + availableNodes += nodes; +} // Called at the beginning of the search and calculates // the bounds of time allowed for the current game ply. We currently support: // 1) x basetime (+ z increment) // 2) x moves in y seconds (+ z increment) -void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { - +void TimeManagement::init(Search::LimitsType& limits, + Color us, + int ply, + const OptionsMap& options) { // If we have no time, no need to initialize TM, except for the start time, // which is used by movetime. startTime = limits.startTime; if (limits.time[us] == 0) return; - TimePoint moveOverhead = TimePoint(Options["Move Overhead"]); - TimePoint npmsec = TimePoint(Options["nodestime"]); + TimePoint moveOverhead = TimePoint(options["Move Overhead"]); + TimePoint npmsec = TimePoint(options["nodestime"]); // optScale is a percentage of available time to use for the current move. // maxScale is a multiplier applied to optimumTime. @@ -54,6 +71,8 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { // must be much lower than the real engine speed. if (npmsec) { + useNodesTime = true; + if (!availableNodes) // Only once at game start availableNodes = npmsec * limits.time[us]; // Time is in msec @@ -100,7 +119,7 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) { maximumTime = TimePoint(std::min(0.84 * limits.time[us] - moveOverhead, maxScale * optimumTime)) - 10; - if (Options["Ponder"]) + if (options["Ponder"]) optimumTime += optimumTime / 4; } diff --git a/src/timeman.h b/src/timeman.h index 0509158c3f4..b07712a25c2 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -19,35 +19,41 @@ #ifndef TIMEMAN_H_INCLUDED #define TIMEMAN_H_INCLUDED +#include #include #include "misc.h" -#include "search.h" -#include "thread.h" #include "types.h" namespace Stockfish { +class OptionsMap; + +namespace Search { +struct LimitsType; +} + // The TimeManagement class computes the optimal time to think depending on // the maximum available time, the game move number, and other parameters. class TimeManagement { public: - void init(Search::LimitsType& limits, Color us, int ply); - TimePoint optimum() const { return optimumTime; } - TimePoint maximum() const { return maximumTime; } - TimePoint elapsed() const { - return Search::Limits.npmsec ? TimePoint(Threads.nodes_searched()) : now() - startTime; - } + void init(Search::LimitsType& limits, Color us, int ply, const OptionsMap& options); + + TimePoint optimum() const; + TimePoint maximum() const; + TimePoint elapsed(std::size_t nodes) const; - int64_t availableNodes; // When in 'nodes as time' mode + void clear(); + void advance_nodes_time(std::int64_t nodes); private: TimePoint startTime; TimePoint optimumTime; TimePoint maximumTime; -}; -extern TimeManagement Time; + std::int64_t availableNodes = 0; // When in 'nodes as time' mode + bool useNodesTime = false; // True if we are in 'nodes as time' mode +}; } // namespace Stockfish diff --git a/src/tt.cpp b/src/tt.cpp index 2e3f7d32835..f3f58979d1b 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -26,16 +26,13 @@ #include #include "misc.h" -#include "thread.h" -#include "uci.h" namespace Stockfish { -TranspositionTable TT; // Our global transposition table - // Populates the TTEntry with a new node's data, possibly // overwriting an old position. The update is not atomic and can be racy. -void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) { +void TTEntry::save( + Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev, uint8_t generation8) { // Preserve any existing move for the same position if (m || uint16_t(k) != key16) @@ -49,7 +46,7 @@ void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) key16 = uint16_t(k); depth8 = uint8_t(d - DEPTH_OFFSET); - genBound8 = uint8_t(TT.generation8 | uint8_t(pv) << 2 | b); + genBound8 = uint8_t(generation8 | uint8_t(pv) << 2 | b); value16 = int16_t(v); eval16 = int16_t(ev); } @@ -59,10 +56,7 @@ void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) // Sets the size of the transposition table, // measured in megabytes. Transposition table consists of a power of 2 number // of clusters and each cluster consists of ClusterSize number of TTEntry. -void TranspositionTable::resize(size_t mbSize) { - - Threads.main()->wait_for_search_finished(); - +void TranspositionTable::resize(size_t mbSize, int threadCount) { aligned_large_pages_free(table); clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster); @@ -74,28 +68,25 @@ void TranspositionTable::resize(size_t mbSize) { exit(EXIT_FAILURE); } - clear(); + clear(threadCount); } // Initializes the entire transposition table to zero, // in a multi-threaded way. -void TranspositionTable::clear() { - +void TranspositionTable::clear(size_t threadCount) { std::vector threads; - for (size_t idx = 0; idx < size_t(Options["Threads"]); ++idx) + for (size_t idx = 0; idx < size_t(threadCount); ++idx) { - threads.emplace_back([this, idx]() { + threads.emplace_back([this, idx, threadCount]() { // Thread binding gives faster search on systems with a first-touch policy - if (Options["Threads"] > 8) + if (threadCount > 8) WinProcGroup::bindThisThread(idx); // Each thread will zero its part of the hash table - const size_t stride = size_t(clusterCount / Options["Threads"]), - start = size_t(stride * idx), - len = - idx != size_t(Options["Threads"]) - 1 ? stride : clusterCount - start; + const size_t stride = size_t(clusterCount / threadCount), start = size_t(stride * idx), + len = idx != size_t(threadCount) - 1 ? stride : clusterCount - start; std::memset(&table[start], 0, len * sizeof(Cluster)); }); diff --git a/src/tt.h b/src/tt.h index 61e854c1af2..4115ee7ae51 100644 --- a/src/tt.h +++ b/src/tt.h @@ -45,7 +45,7 @@ struct TTEntry { 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); + void save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev, uint8_t generation8); private: friend class TranspositionTable; @@ -88,23 +88,23 @@ class TranspositionTable { void new_search() { generation8 += GENERATION_DELTA; } // Lower bits are used for other things TTEntry* probe(const Key key, bool& found) const; int hashfull() const; - void resize(size_t mbSize); - void clear(); + void resize(size_t mbSize, int threadCount); + void clear(size_t threadCount); TTEntry* first_entry(const Key key) const { return &table[mul_hi64(key, clusterCount)].entry[0]; } + uint8_t generation() const { return generation8; } + private: friend struct TTEntry; size_t clusterCount; - Cluster* table; - uint8_t generation8; // Size must be not bigger than TTEntry::genBound8 + Cluster* table = nullptr; + uint8_t generation8 = 0; // Size must be not bigger than TTEntry::genBound8 }; -extern TranspositionTable TT; - } // namespace Stockfish #endif // #ifndef TT_H_INCLUDED diff --git a/src/tune.cpp b/src/tune.cpp index 1dddca0c37d..88b3b7912d5 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -24,14 +24,15 @@ #include #include -#include "uci.h" +#include "ucioption.h" using std::string; namespace Stockfish { bool Tune::update_on_last; -const UCI::Option* LastOption = nullptr; +const Option* LastOption = nullptr; +OptionsMap* Tune::options; static std::map TuneResults; string Tune::next(string& names, bool pop) { @@ -53,13 +54,13 @@ string Tune::next(string& names, bool pop) { return name; } -static void on_tune(const UCI::Option& o) { +static void on_tune(const Option& o) { if (!Tune::update_on_last || LastOption == &o) Tune::read_options(); } -static void make_option(const string& n, int v, const SetRange& r) { +static void make_option(OptionsMap* options, const string& n, int v, const SetRange& r) { // Do not generate option when there is nothing to tune (ie. min = max) if (r(v).first == r(v).second) @@ -68,8 +69,8 @@ static void make_option(const string& n, int v, const SetRange& r) { if (TuneResults.count(n)) v = TuneResults[n]; - Options[n] << UCI::Option(v, r(v).first, r(v).second, on_tune); - LastOption = &Options[n]; + (*options)[n] << Option(v, r(v).first, r(v).second, on_tune); + LastOption = &((*options)[n]); // Print formatted parameters, ready to be copy-pasted in Fishtest std::cout << n << "," << v << "," << r(v).first << "," << r(v).second << "," @@ -79,13 +80,13 @@ static void make_option(const string& n, int v, const SetRange& r) { template<> void Tune::Entry::init_option() { - make_option(name, value, range); + make_option(options, name, value, range); } template<> void Tune::Entry::read_option() { - if (Options.count(name)) - value = int(Options[name]); + if (options->count(name)) + value = int((*options)[name]); } // Instead of a variable here we have a PostUpdate function: just call it diff --git a/src/tune.h b/src/tune.h index 17057001225..b88c085fd4b 100644 --- a/src/tune.h +++ b/src/tune.h @@ -28,6 +28,8 @@ namespace Stockfish { +class OptionsMap; + using Range = std::pair; // Option's min-max values using RangeFun = Range(int); @@ -151,7 +153,8 @@ class Tune { return instance().add(SetDefaultRange, names.substr(1, names.size() - 2), args...); // Remove trailing parenthesis } - static void init() { + static void init(OptionsMap& o) { + options = &o; for (auto& e : instance().list) e->init_option(); read_options(); @@ -160,7 +163,9 @@ class Tune { for (auto& e : instance().list) e->read_option(); } - static bool update_on_last; + + static bool update_on_last; + static OptionsMap* options; }; // Some macro magic :-) we define a dummy int variable that the compiler initializes calling Tune::add() diff --git a/src/uci.cpp b/src/uci.cpp index be902277984..82fb25c1d27 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -22,111 +22,156 @@ #include #include #include -#include #include #include -#include #include #include #include -#include #include #include "benchmark.h" #include "evaluate.h" -#include "misc.h" #include "movegen.h" #include "nnue/evaluate_nnue.h" #include "nnue/nnue_architecture.h" #include "position.h" #include "search.h" -#include "thread.h" +#include "syzygy/tbprobe.h" +#include "types.h" +#include "ucioption.h" namespace Stockfish { -namespace { - -// FEN string for the initial position in standard chess -const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; - - -// Called when the engine receives the "position" UCI command. -// It sets up the position that is described in the given FEN string ("fen") or -// the initial position ("startpos") and then makes the moves given in the following -// move list ("moves"). -void position(Position& pos, std::istringstream& is, StateListPtr& states) { - - Move m; - std::string token, fen; - - is >> token; - - if (token == "startpos") - { - fen = StartFEN; - is >> token; // Consume the "moves" token, if any - } - else if (token == "fen") - while (is >> token && token != "moves") - fen += token + " "; - else - return; - - states = StateListPtr(new std::deque(1)); // Drop the old state and create a new one - pos.set(fen, Options["UCI_Chess960"], &states->back(), Threads.main()); - - // Parse the move list, if any - while (is >> token && (m = UCI::to_move(pos, token)) != Move::none()) - { - states->emplace_back(); - pos.do_move(m, states->back()); - } +constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; +constexpr int NormalizeToPawnValue = 328; +constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048; + +UCI::UCI(int argc, char** argv) : + cli(argc, argv) { + + evalFiles = {{Eval::NNUE::Big, {"EvalFile", EvalFileDefaultNameBig, "None", ""}}, + {Eval::NNUE::Small, {"EvalFileSmall", EvalFileDefaultNameSmall, "None", ""}}}; + + + options["Debug Log File"] << Option("", [](const Option& o) { start_logger(o); }); + + options["Threads"] << Option(1, 1, 1024, [this](const Option&) { + threads.set({options, threads, tt}); + }); + + options["Hash"] << Option(16, 1, MaxHashMB, [this](const Option& o) { + threads.main_thread()->wait_for_search_finished(); + tt.resize(o, options["Threads"]); + }); + + options["Clear Hash"] << Option(true, [this](const Option&) { search_clear(); }); + options["Ponder"] << Option(false); + options["MultiPV"] << Option(1, 1, 500); + options["Skill Level"] << Option(20, 0, 20); + options["Move Overhead"] << Option(10, 0, 5000); + options["nodestime"] << Option(0, 0, 10000); + options["UCI_Chess960"] << Option(false); + options["UCI_LimitStrength"] << Option(false); + options["UCI_Elo"] << Option(1320, 1320, 3190); + options["UCI_ShowWDL"] << Option(false); + options["SyzygyPath"] << Option("", [](const Option& o) { Tablebases::init(o); }); + options["SyzygyProbeDepth"] << Option(1, 1, 100); + options["Syzygy50MoveRule"] << Option(true); + options["SyzygyProbeLimit"] << Option(7, 0, 7); + options["EvalFile"] << Option(EvalFileDefaultNameBig, [this](const Option&) { + evalFiles = Eval::NNUE::load_networks(cli.binaryDirectory, options, evalFiles); + }); + + threads.set({options, threads, tt}); + + search_clear(); // After threads are up } -// Prints the evaluation of the current position, -// consistent with the UCI options set so far. -void trace_eval(Position& pos) { +void UCI::loop() { + Position pos; + std::string token, cmd; StateListPtr states(new std::deque(1)); - Position p; - p.set(pos.fen(), Options["UCI_Chess960"], &states->back(), Threads.main()); - Eval::NNUE::verify(); + pos.set(StartFEN, false, &states->back()); - sync_cout << "\n" << Eval::trace(p) << sync_endl; -} + for (int i = 1; i < cli.argc; ++i) + cmd += std::string(cli.argv[i]) + " "; + do + { + if (cli.argc == 1 + && !getline(std::cin, cmd)) // Wait for an input or an end-of-file (EOF) indication + cmd = "quit"; -// Called when the engine receives the "setoption" UCI command. -// The function updates the UCI option ("name") to the given value ("value"). + std::istringstream is(cmd); -void setoption(std::istringstream& is) { + token.clear(); // Avoid a stale if getline() returns nothing or a blank line + is >> std::skipws >> token; - Threads.main()->wait_for_search_finished(); + if (token == "quit" || token == "stop") + threads.stop = true; - std::string token, name, value; + // The GUI sends 'ponderhit' to tell that the user has played the expected move. + // So, 'ponderhit' is sent if pondering was done on the same move that the user + // has played. The search should continue, but should also switch from pondering + // to the normal search. + else if (token == "ponderhit") + threads.main_manager()->ponder = false; // Switch to the normal search - is >> token; // Consume the "name" token + else if (token == "uci") + sync_cout << "id name " << engine_info(true) << "\n" + << options << "\nuciok" << sync_endl; - // Read the option name (can contain spaces) - while (is >> token && token != "value") - name += (name.empty() ? "" : " ") + token; + else if (token == "setoption") + setoption(is); + else if (token == "go") + go(pos, is, states); + else if (token == "position") + position(pos, is, states); + else if (token == "ucinewgame") + search_clear(); + else if (token == "isready") + sync_cout << "readyok" << sync_endl; - // Read the option value (can contain spaces) - while (is >> token) - value += (value.empty() ? "" : " ") + token; + // Add custom non-UCI commands, mainly for debugging purposes. + // These commands must not be used 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; + else if (token == "eval") + trace_eval(pos); + else if (token == "compiler") + sync_cout << compiler_info() << sync_endl; + else if (token == "export_net") + { + std::optional filename; + std::string f; + if (is >> std::skipws >> f) + filename = f; + Eval::NNUE::save_eval(filename, Eval::NNUE::Big, evalFiles); + } + else if (token == "--help" || token == "help" || token == "--license" || token == "license") + sync_cout + << "\nStockfish is a powerful chess engine for playing and analyzing." + "\nIt is released as free software licensed under the GNU GPLv3 License." + "\nStockfish is normally used with a graphical user interface (GUI) and implements" + "\nthe Universal Chess Interface (UCI) protocol to communicate with a GUI, an API, etc." + "\nFor any further information, visit https://github.com/official-stockfish/Stockfish#readme" + "\nor read the corresponding README.md and Copying.txt files distributed along with this program.\n" + << sync_endl; + else if (!token.empty() && token[0] != '#') + sync_cout << "Unknown command: '" << cmd << "'. Type help for more information." + << sync_endl; - if (Options.count(name)) - Options[name] = value; - else - sync_cout << "No such option: " << name << sync_endl; + } while (token != "quit" && cli.argc == 1); // The command-line arguments are one-shot } +void UCI::go(Position& pos, std::istringstream& is, StateListPtr& states) { -// Called when the engine receives the "go" UCI command. The function sets the -// thinking time and other parameters from the input string then stars with a search - -void go(Position& pos, std::istringstream& is, StateListPtr& states) { Search::LimitsType limits; std::string token; @@ -137,7 +182,7 @@ void go(Position& pos, std::istringstream& is, StateListPtr& states) { while (is >> token) if (token == "searchmoves") // Needs to be the last command on the line while (is >> token) - limits.searchmoves.push_back(UCI::to_move(pos, token)); + limits.searchmoves.push_back(to_move(pos, token)); else if (token == "wtime") is >> limits.time[WHITE]; @@ -164,16 +209,12 @@ void go(Position& pos, std::istringstream& is, StateListPtr& states) { else if (token == "ponder") ponderMode = true; - Threads.start_thinking(pos, states, limits, ponderMode); -} - - -// Called when the engine receives the "bench" command. -// First, a list of UCI commands is set up according to the bench -// parameters, then it is run one by one, printing a summary at the end. + Eval::NNUE::verify(options, evalFiles); -void bench(Position& pos, std::istream& args, StateListPtr& states) { + threads.start_thinking(options, pos, states, limits, ponderMode); +} +void UCI::bench(Position& pos, std::istream& args, StateListPtr& states) { std::string token; uint64_t num, nodes = 0, cnt = 1; @@ -196,8 +237,8 @@ void bench(Position& pos, std::istream& args, StateListPtr& states) { if (token == "go") { go(pos, is, states); - Threads.main()->wait_for_search_finished(); - nodes += Threads.nodes_searched(); + threads.main_thread()->wait_for_search_finished(); + nodes += threads.nodes_searched(); } else trace_eval(pos); @@ -208,9 +249,9 @@ void bench(Position& pos, std::istream& args, StateListPtr& states) { position(pos, is, states); else if (token == "ucinewgame") { - Search::clear(); + search_clear(); // Search::clear() may take a while elapsed = now(); - } // Search::clear() may take a while + } } elapsed = now() - elapsed + 1; // Ensure positivity to avoid a 'divide by zero' @@ -222,141 +263,66 @@ void bench(Position& pos, std::istream& args, StateListPtr& states) { << "\nNodes/second : " << 1000 * nodes / elapsed << std::endl; } -// The win rate model returns the probability of winning (in per mille units) given an -// eval and a game ply. It fits the LTC fishtest statistics rather accurately. -int win_rate_model(Value v, int ply) { - - // The model only captures up to 240 plies, so limit the input and then rescale - double m = std::min(240, ply) / 64.0; - - // The coefficients of a third-order polynomial fit is based on the fishtest data - // for two parameters that need to transform eval to the argument of a logistic - // function. - constexpr double as[] = {0.38036525, -2.82015070, 23.17882135, 307.36768407}; - constexpr double bs[] = {-2.29434733, 13.27689788, -14.26828904, 63.45318330}; - - // Enforce that NormalizeToPawnValue corresponds to a 50% win rate at ply 64 - static_assert(UCI::NormalizeToPawnValue == int(as[0] + as[1] + as[2] + as[3])); +void UCI::trace_eval(Position& pos) { + StateListPtr states(new std::deque(1)); + Position p; + p.set(pos.fen(), options["UCI_Chess960"], &states->back()); - double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3]; - double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3]; + Eval::NNUE::verify(options, evalFiles); - // Transform the eval to centipawns with limited range - double x = std::clamp(double(v), -4000.0, 4000.0); - - // Return the win rate in per mille units, rounded to the nearest integer - return int(0.5 + 1000 / (1 + std::exp((a - x) / b))); + sync_cout << "\n" << Eval::trace(p, *threads.main_thread()->worker.get()) << sync_endl; } -} // namespace +void UCI::search_clear() { + threads.main_thread()->wait_for_search_finished(); + tt.clear(options["Threads"]); + threads.clear(); + Tablebases::init(options["SyzygyPath"]); // Free mapped files +} -// Waits for a command from the stdin, parses it, and then calls the appropriate -// function. It also intercepts an end-of-file (EOF) indication from the stdin to ensure a -// graceful exit if the GUI dies unexpectedly. When called with some command-line arguments, -// like running 'bench', the function returns immediately after the command is executed. -// In addition to the UCI ones, some additional debug commands are also supported. -void UCI::loop(int argc, char* argv[]) { - - Position pos; - std::string token, cmd; - StateListPtr states(new std::deque(1)); +void UCI::setoption(std::istringstream& is) { + threads.main_thread()->wait_for_search_finished(); + options.setoption(is); +} - pos.set(StartFEN, false, &states->back(), Threads.main()); +void UCI::position(Position& pos, std::istringstream& is, StateListPtr& states) { + Move m; + std::string token, fen; - for (int i = 1; i < argc; ++i) - cmd += std::string(argv[i]) + " "; + is >> token; - do + if (token == "startpos") { - if (argc == 1 - && !getline(std::cin, cmd)) // Wait for an input or an end-of-file (EOF) indication - cmd = "quit"; - - std::istringstream is(cmd); - - token.clear(); // Avoid a stale if getline() returns nothing or a blank line - is >> std::skipws >> token; - - if (token == "quit" || token == "stop") - Threads.stop = true; - - // The GUI sends 'ponderhit' to tell that the user has played the expected move. - // So, 'ponderhit' is sent if pondering was done on the same move that the user - // has played. The search should continue, but should also switch from pondering - // to the normal search. - else if (token == "ponderhit") - Threads.main()->ponder = false; // Switch to the normal search - - else if (token == "uci") - sync_cout << "id name " << engine_info(true) << "\n" - << Options << "\nuciok" << sync_endl; - - else if (token == "setoption") - setoption(is); - else if (token == "go") - go(pos, is, states); - else if (token == "position") - position(pos, is, states); - else if (token == "ucinewgame") - Search::clear(); - else if (token == "isready") - sync_cout << "readyok" << sync_endl; + fen = StartFEN; + is >> token; // Consume the "moves" token, if any + } + else if (token == "fen") + while (is >> token && token != "moves") + fen += token + " "; + else + return; - // Add custom non-UCI commands, mainly for debugging purposes. - // These commands must not be used 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; - else if (token == "eval") - trace_eval(pos); - else if (token == "compiler") - sync_cout << compiler_info() << sync_endl; - else if (token == "export_net") - { - std::optional filename; - std::string f; - if (is >> std::skipws >> f) - filename = f; - Eval::NNUE::save_eval(filename, Eval::NNUE::Big); - } - else if (token == "--help" || token == "help" || token == "--license" || token == "license") - sync_cout - << "\nStockfish is a powerful chess engine for playing and analyzing." - "\nIt is released as free software licensed under the GNU GPLv3 License." - "\nStockfish is normally used with a graphical user interface (GUI) and implements" - "\nthe Universal Chess Interface (UCI) protocol to communicate with a GUI, an API, etc." - "\nFor any further information, visit https://github.com/official-stockfish/Stockfish#readme" - "\nor read the corresponding README.md and Copying.txt files distributed along with this program.\n" - << sync_endl; - else if (!token.empty() && token[0] != '#') - sync_cout << "Unknown command: '" << cmd << "'. Type help for more information." - << sync_endl; + states = StateListPtr(new std::deque(1)); // Drop the old state and create a new one + pos.set(fen, options["UCI_Chess960"], &states->back()); - } while (token != "quit" && argc == 1); // The command-line arguments are one-shot + // Parse the move list, if any + while (is >> token && (m = to_move(pos, token)) != Move::none()) + { + states->emplace_back(); + pos.do_move(m, states->back()); + } } +int UCI::to_cp(Value v) { return 100 * v / NormalizeToPawnValue; } -// Turns a Value to an integer centipawn number, -// without treatment of mate and similar special scores. -int UCI::to_cp(Value v) { return 100 * v / UCI::NormalizeToPawnValue; } - -// Converts a Value to a string by adhering to the UCI protocol specification: -// -// cp The score from the engine's point of view in centipawns. -// mate Mate in 'y' moves (not plies). If the engine is getting mated, -// uses negative values for 'y'. std::string UCI::value(Value v) { - assert(-VALUE_INFINITE < v && v < VALUE_INFINITE); std::stringstream ss; if (std::abs(v) < VALUE_TB_WIN_IN_MAX_PLY) - ss << "cp " << UCI::to_cp(v); + ss << "cp " << to_cp(v); else if (std::abs(v) <= VALUE_TB) { const int ply = VALUE_TB - std::abs(v); // recompute ss->ply @@ -368,34 +334,11 @@ std::string UCI::value(Value v) { return ss.str(); } - -// Reports the win-draw-loss (WDL) statistics given an evaluation -// and a game ply based on the data gathered for fishtest LTC games. -std::string UCI::wdl(Value v, int ply) { - - std::stringstream ss; - - int wdl_w = win_rate_model(v, ply); - int wdl_l = win_rate_model(-v, ply); - int wdl_d = 1000 - wdl_w - wdl_l; - ss << " wdl " << wdl_w << " " << wdl_d << " " << wdl_l; - - return ss.str(); -} - - -// Converts a Square to a string in algebraic notation (g1, a7, etc.) std::string UCI::square(Square s) { return std::string{char('a' + file_of(s)), char('1' + rank_of(s))}; } - -// Converts a Move to a string in coordinate notation (g1f3, a7a8q). -// The only special case is castling where the e1g1 notation is printed in -// standard chess mode and in e1h1 notation it is printed in Chess960 mode. -// Internally, all castling moves are always encoded as 'king captures rook'. std::string UCI::move(Move m, bool chess960) { - if (m == Move::none()) return "(none)"; @@ -408,7 +351,7 @@ std::string UCI::move(Move m, bool chess960) { if (m.type_of() == CASTLING && !chess960) to = make_square(to > from ? FILE_G : FILE_C, rank_of(from)); - std::string move = UCI::square(from) + UCI::square(to); + std::string move = square(from) + square(to); if (m.type_of() == PROMOTION) move += " pnbrqk"[m.promotion_type()]; @@ -416,16 +359,108 @@ std::string UCI::move(Move m, bool chess960) { return move; } +std::string UCI::pv(const Search::Worker& workerThread, + TimePoint elapsed, + uint64_t nodesSearched, + uint64_t tb_hits, + int hashfull, + bool rootInTB) { + std::stringstream ss; + TimePoint time = elapsed + 1; + const auto& rootMoves = workerThread.rootMoves; + const auto& depth = workerThread.completedDepth; + const auto& pos = workerThread.rootPos; + size_t pvIdx = workerThread.pvIdx; + size_t multiPV = std::min(size_t(workerThread.options["MultiPV"]), rootMoves.size()); + uint64_t tbHits = tb_hits + (rootInTB ? rootMoves.size() : 0); -// Converts a string representing a move in coordinate notation -// (g1f3, a7a8q) to the corresponding legal Move, if any. -Move UCI::to_move(const Position& pos, std::string& str) { + for (size_t i = 0; i < multiPV; ++i) + { + bool updated = rootMoves[i].score != -VALUE_INFINITE; + + if (depth == 1 && !updated && i > 0) + continue; + + Depth d = updated ? depth : std::max(1, depth - 1); + Value v = updated ? rootMoves[i].uciScore : rootMoves[i].previousScore; + + if (v == -VALUE_INFINITE) + v = VALUE_ZERO; + + bool tb = rootInTB && std::abs(v) <= VALUE_TB; + v = tb ? rootMoves[i].tbScore : v; + + if (ss.rdbuf()->in_avail()) // Not at first line + ss << "\n"; + + ss << "info" + << " depth " << d << " seldepth " << rootMoves[i].selDepth << " multipv " << i + 1 + << " score " << value(v); + + if (workerThread.options["UCI_ShowWDL"]) + ss << wdl(v, pos.game_ply()); + + if (i == pvIdx && !tb && updated) // tablebase- and previous-scores are exact + ss << (rootMoves[i].scoreLowerbound + ? " lowerbound" + : (rootMoves[i].scoreUpperbound ? " upperbound" : "")); + + ss << " nodes " << nodesSearched << " nps " << nodesSearched * 1000 / time << " hashfull " + << hashfull << " tbhits " << tbHits << " time " << time << " pv"; + + for (Move m : rootMoves[i].pv) + ss << " " << move(m, pos.is_chess960()); + } + + return ss.str(); +} + +namespace { +// The win rate model returns the probability of winning (in per mille units) given an +// eval and a game ply. It fits the LTC fishtest statistics rather accurately. +int win_rate_model(Value v, int ply) { + + // The model only captures up to 240 plies, so limit the input and then rescale + double m = std::min(240, ply) / 64.0; + + // The coefficients of a third-order polynomial fit is based on the fishtest data + // for two parameters that need to transform eval to the argument of a logistic + // function. + constexpr double as[] = {0.38036525, -2.82015070, 23.17882135, 307.36768407}; + constexpr double bs[] = {-2.29434733, 13.27689788, -14.26828904, 63.45318330}; + + // Enforce that NormalizeToPawnValue corresponds to a 50% win rate at ply 64 + static_assert(NormalizeToPawnValue == int(as[0] + as[1] + as[2] + as[3])); + + double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3]; + double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3]; + + // Transform the eval to centipawns with limited range + double x = std::clamp(double(v), -4000.0, 4000.0); + + // Return the win rate in per mille units, rounded to the nearest integer + return int(0.5 + 1000 / (1 + std::exp((a - x) / b))); +} +} + +std::string UCI::wdl(Value v, int ply) { + std::stringstream ss; + + int wdl_w = win_rate_model(v, ply); + int wdl_l = win_rate_model(-v, ply); + int wdl_d = 1000 - wdl_w - wdl_l; + ss << " wdl " << wdl_w << " " << wdl_d << " " << wdl_l; + + return ss.str(); +} + +Move UCI::to_move(const Position& pos, std::string& str) { if (str.length() == 5) str[4] = char(tolower(str[4])); // The promotion piece character must be lowercased for (const auto& m : MoveList(pos)) - if (str == UCI::move(m, pos.is_chess960())) + if (str == move(m, pos.is_chess960())) return m; return Move::none(); diff --git a/src/uci.h b/src/uci.h index d249da7442b..cd113b1ad29 100644 --- a/src/uci.h +++ b/src/uci.h @@ -19,77 +19,70 @@ #ifndef UCI_H_INCLUDED #define UCI_H_INCLUDED -#include -#include -#include +#include +#include #include +#include -#include "types.h" +#include "evaluate.h" +#include "misc.h" +#include "position.h" +#include "thread.h" +#include "tt.h" +#include "ucioption.h" namespace Stockfish { -class Position; +namespace Eval::NNUE { +enum NetSize : int; +} -namespace UCI { +namespace Search { +class Worker; +} -// Normalizes the internal value as reported by evaluate or search -// to the UCI centipawn result used in output. This value is derived from -// the win_rate_model() such that Stockfish outputs an advantage of -// "100 centipawns" for a position if the engine has a 50% probability to win -// from this position in self-play at fishtest LTC time control. -const int NormalizeToPawnValue = 328; +class Move; +enum Square : int; +using Value = int; -class Option; +class UCI { + public: + UCI(int argc, char** argv); -// Define a custom comparator, because the UCI options should be case-insensitive -struct CaseInsensitiveLess { - bool operator()(const std::string&, const std::string&) const; -}; + void loop(); -// The options container is defined as a std::map -using OptionsMap = std::map; + static int to_cp(Value v); + static std::string value(Value v); + static std::string square(Square s); + static std::string move(Move m, bool chess960); + static std::string pv(const Search::Worker& workerThread, + TimePoint elapsed, + uint64_t nodesSearched, + uint64_t tb_hits, + int hashfull, + bool rootInTB); + static std::string wdl(Value v, int ply); + static Move to_move(const Position& pos, std::string& str); -// The Option class implements each option as specified by the UCI protocol -class Option { + const std::string& workingDirectory() const { return cli.workingDirectory; } - using OnChange = void (*)(const Option&); + OptionsMap options; - public: - Option(OnChange = nullptr); - Option(bool v, OnChange = nullptr); - Option(const char* v, OnChange = nullptr); - Option(double v, int minv, int maxv, OnChange = nullptr); - Option(const char* v, const char* cur, OnChange = nullptr); - - Option& operator=(const std::string&); - void operator<<(const Option&); - operator int() const; - operator std::string() const; - bool operator==(const char*) const; + std::unordered_map evalFiles; private: - friend std::ostream& operator<<(std::ostream&, const OptionsMap&); - - std::string defaultValue, currentValue, type; - int min, max; - size_t idx; - OnChange on_change; + TranspositionTable tt; + ThreadPool threads; + CommandLine cli; + + void go(Position& pos, std::istringstream& is, StateListPtr& states); + void bench(Position& pos, std::istream& args, StateListPtr& states); + void position(Position& pos, std::istringstream& is, StateListPtr& states); + void trace_eval(Position& pos); + void search_clear(); + void setoption(std::istringstream& is); }; -void init(OptionsMap&); -void loop(int argc, char* argv[]); -int to_cp(Value v); -std::string value(Value v); -std::string square(Square s); -std::string move(Move m, bool chess960); -std::string pv(const Position& pos, Depth depth); -std::string wdl(Value v, int ply); -Move to_move(const Position& pos, std::string& str); - -} // namespace UCI - -extern UCI::OptionsMap Options; - } // namespace Stockfish #endif // #ifndef UCI_H_INCLUDED diff --git a/src/ucioption.cpp b/src/ucioption.cpp index f8cbcc53077..c7de7e3f7f2 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -16,104 +16,53 @@ along with this program. If not, see . */ +#include "ucioption.h" + #include #include #include -#include -#include -#include -#include -#include +#include #include -#include +#include -#include "evaluate.h" #include "misc.h" -#include "search.h" -#include "syzygy/tbprobe.h" -#include "thread.h" -#include "tt.h" -#include "types.h" -#include "uci.h" - -using std::string; namespace Stockfish { -UCI::OptionsMap Options; // Global object - -namespace UCI { +bool CaseInsensitiveLess::operator()(const std::string& s1, const std::string& s2) const { -// 'On change' actions, triggered by an option's value change -static void on_clear_hash(const Option&) { Search::clear(); } -static void on_hash_size(const Option& o) { TT.resize(size_t(o)); } -static void on_logger(const Option& o) { start_logger(o); } -static void on_threads(const Option& o) { Threads.set(size_t(o)); } -static void on_tb_path(const Option& o) { Tablebases::init(o); } -static void on_eval_file(const Option&) { Eval::NNUE::init(); } - -// Our case insensitive less() function as required by UCI protocol -bool CaseInsensitiveLess::operator()(const string& s1, const string& s2) const { - - return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), - [](char c1, char c2) { return tolower(c1) < tolower(c2); }); + return std::lexicographical_compare( + s1.begin(), s1.end(), s2.begin(), s2.end(), + [](char c1, char c2) { return std::tolower(c1) < std::tolower(c2); }); } +void OptionsMap::setoption(std::istringstream& is) { + std::string token, name, value; -// Initializes the UCI options to their hard-coded default values -void init(OptionsMap& o) { - - constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048; - - o["Debug Log File"] << Option("", on_logger); - o["Threads"] << Option(1, 1, 1024, on_threads); - o["Hash"] << Option(16, 1, MaxHashMB, on_hash_size); - o["Clear Hash"] << Option(on_clear_hash); - o["Ponder"] << Option(false); - o["MultiPV"] << Option(1, 1, MAX_MOVES); - o["Skill Level"] << Option(20, 0, 20); - o["Move Overhead"] << Option(10, 0, 5000); - o["nodestime"] << Option(0, 0, 10000); - o["UCI_Chess960"] << Option(false); - o["UCI_LimitStrength"] << Option(false); - o["UCI_Elo"] << Option(1320, 1320, 3190); - o["UCI_ShowWDL"] << Option(false); - o["SyzygyPath"] << Option("", on_tb_path); - o["SyzygyProbeDepth"] << Option(1, 1, 100); - o["Syzygy50MoveRule"] << Option(true); - o["SyzygyProbeLimit"] << Option(7, 0, 7); - o["EvalFile"] << Option(EvalFileDefaultNameBig, on_eval_file); - // Enable this after fishtest workers support EvalFileSmall - // o["EvalFileSmall"] << Option(EvalFileDefaultNameSmall, on_eval_file); -} + is >> token; // Consume the "name" token + // Read the option name (can contain spaces) + while (is >> token && token != "value") + name += (name.empty() ? "" : " ") + token; -// Used to print all the options default values in chronological -// insertion order (the idx field) and in the format defined by the UCI protocol. -std::ostream& operator<<(std::ostream& os, const OptionsMap& om) { + // Read the option value (can contain spaces) + while (is >> token) + value += (value.empty() ? "" : " ") + token; - for (size_t idx = 0; idx < om.size(); ++idx) - for (const auto& it : om) - if (it.second.idx == idx) - { - const Option& o = it.second; - os << "\noption name " << it.first << " type " << o.type; - - if (o.type == "string" || o.type == "check" || o.type == "combo") - os << " default " << o.defaultValue; - - if (o.type == "spin") - os << " default " << int(stof(o.defaultValue)) << " min " << o.min << " max " - << o.max; - - break; - } + if (options_map.count(name)) + options_map[name] = value; + else + sync_cout << "No such option: " << name << sync_endl; +} - return os; +Option OptionsMap::operator[](const std::string& name) const { + auto it = options_map.find(name); + return it != options_map.end() ? it->second : Option(); } +Option& OptionsMap::operator[](const std::string& name) { return options_map[name]; } -// Option class constructors and conversion operators +std::size_t OptionsMap::count(const std::string& name) const { return options_map.count(name); } Option::Option(const char* v, OnChange f) : type("string"), @@ -184,19 +133,19 @@ void Option::operator<<(const Option& o) { // Updates currentValue and triggers on_change() action. It's up to // the GUI to check for option's limits, but we could receive the new value // from the user by console window, so let's check the bounds anyway. -Option& Option::operator=(const string& v) { +Option& Option::operator=(const std::string& v) { assert(!type.empty()); if ((type != "button" && type != "string" && v.empty()) || (type == "check" && v != "true" && v != "false") - || (type == "spin" && (stof(v) < min || stof(v) > max))) + || (type == "spin" && (std::stof(v) < min || std::stof(v) > max))) return *this; if (type == "combo") { OptionsMap comboMap; // To have case insensitive compare - string token; + std::string token; std::istringstream ss(defaultValue); while (ss >> token) comboMap[token] << Option(); @@ -213,6 +162,24 @@ Option& Option::operator=(const string& v) { return *this; } -} // namespace UCI +std::ostream& operator<<(std::ostream& os, const OptionsMap& om) { + for (size_t idx = 0; idx < om.options_map.size(); ++idx) + for (const auto& it : om.options_map) + if (it.second.idx == idx) + { + const Option& o = it.second; + os << "\noption name " << it.first << " type " << o.type; + + if (o.type == "string" || o.type == "check" || o.type == "combo") + os << " default " << o.defaultValue; + + if (o.type == "spin") + os << " default " << int(stof(o.defaultValue)) << " min " << o.min << " max " + << o.max; + + break; + } -} // namespace Stockfish + return os; +} +} diff --git a/src/ucioption.h b/src/ucioption.h new file mode 100644 index 00000000000..b575d1646e6 --- /dev/null +++ b/src/ucioption.h @@ -0,0 +1,81 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef UCIOPTION_H_INCLUDED +#define UCIOPTION_H_INCLUDED + +#include +#include +#include +#include +#include + +namespace Stockfish { +// Define a custom comparator, because the UCI options should be case-insensitive +struct CaseInsensitiveLess { + bool operator()(const std::string&, const std::string&) const; +}; + +class Option; + +class OptionsMap { + public: + void setoption(std::istringstream&); + + friend std::ostream& operator<<(std::ostream&, const OptionsMap&); + + Option operator[](const std::string&) const; + Option& operator[](const std::string&); + + std::size_t count(const std::string&) const; + + private: + // The options container is defined as a std::map + using OptionsStore = std::map; + + OptionsStore options_map; +}; + +// The Option class implements each option as specified by the UCI protocol +class Option { + public: + using OnChange = std::function; + + Option(OnChange = nullptr); + Option(bool v, OnChange = nullptr); + Option(const char* v, OnChange = nullptr); + Option(double v, int minv, int maxv, OnChange = nullptr); + Option(const char* v, const char* cur, OnChange = nullptr); + + Option& operator=(const std::string&); + void operator<<(const Option&); + operator int() const; + operator std::string() const; + bool operator==(const char*) const; + + friend std::ostream& operator<<(std::ostream&, const OptionsMap&); + + private: + std::string defaultValue, currentValue, type; + int min, max; + size_t idx; + OnChange on_change; +}; + +} +#endif // #ifndef UCIOPTION_H_INCLUDED From 3372ee9c2671dedcbf70ad6a744ef0825eaaba94 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Thu, 11 Jan 2024 13:25:01 +0300 Subject: [PATCH 1324/1766] Remove threatenedByPawn term for queen threats Passed STC: https://tests.stockfishchess.org/tests/view/659d614c79aa8af82b9677d0 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 151776 W: 38690 L: 38597 D: 74489 Ptnml(0-2): 522, 17841, 39015, 18042, 468 Passed LTC: https://tests.stockfishchess.org/tests/view/659d94d379aa8af82b967cb2 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 91908 W: 23075 L: 22924 D: 45909 Ptnml(0-2): 70, 10311, 25037, 10470, 66 closes https://github.com/official-stockfish/Stockfish/pull/4977 Bench: 1266493 --- src/movepick.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 6a5629961e3..c6532520d63 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -198,15 +198,15 @@ void MovePicker::score() { : 0; // malus for putting piece en prise - m.value -= !(threatenedPieces & from) - ? (pt == QUEEN ? bool(to & threatenedByRook) * 50000 - + bool(to & threatenedByMinor) * 10000 - + bool(to & threatenedByPawn) * 20000 - : pt == ROOK ? bool(to & threatenedByMinor) * 25000 - + bool(to & threatenedByPawn) * 10000 - : pt != PAWN ? bool(to & threatenedByPawn) * 15000 - : 0) - : 0; + m.value -= + !(threatenedPieces & from) + ? (pt == QUEEN + ? bool(to & threatenedByRook) * 50000 + bool(to & threatenedByMinor) * 10000 + : pt == ROOK + ? bool(to & threatenedByMinor) * 25000 + bool(to & threatenedByPawn) * 10000 + : pt != PAWN ? bool(to & threatenedByPawn) * 15000 + : 0) + : 0; } else // Type == EVASIONS From eec361f64c7d28ff3a5add64bddc5a96800f7fba Mon Sep 17 00:00:00 2001 From: mstembera Date: Mon, 8 Jan 2024 01:03:38 -0800 Subject: [PATCH 1325/1766] Simplify bad quiets The main difference is that instead of returning the first bad quiet as a good one we fall through. This is actually more correct and simpler to implement. Non regression STC: https://tests.stockfishchess.org/tests/view/659bbb3479aa8af82b964ec7 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 150944 W: 38399 L: 38305 D: 74240 Ptnml(0-2): 485, 18042, 38298, 18188, 459 Non regression LTC: https://tests.stockfishchess.org/tests/view/659c6e6279aa8af82b9660eb LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 192060 W: 47871 L: 47823 D: 96366 Ptnml(0-2): 144, 21912, 51845, 22010, 119 The cutoff is now -8K instead of -7.5K. -7.5K failed. https://tests.stockfishchess.org/tests/view/659a1f4b79aa8af82b962a0e This was likely a false negative. closes https://github.com/official-stockfish/Stockfish/pull/4975 Bench: 1308279 --- src/movepick.cpp | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index c6532520d63..e521689ef73 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -312,19 +312,11 @@ Move MovePicker::next_move(bool skipQuiets) { return *cur != refutations[0] && *cur != refutations[1] && *cur != refutations[2]; })) { - Move tmp = *(cur - 1); - if ((cur - 1)->value < -7500 && (cur - 1)->value > quiet_threshold(depth)) - { - // Remaining quiets are bad - beginBadQuiets = cur; - - // Prepare the pointers to loop over the bad captures - cur = moves; - endMoves = endBadCaptures; - - ++stage; - } - return tmp; + if ((cur - 1)->value > -8000 || (cur - 1)->value <= quiet_threshold(depth)) + return *(cur - 1); + + // Remaining quiets are bad + beginBadQuiets = cur - 1; } // Prepare the pointers to loop over the bad captures From cf5b070913755e2aac2a6e827e7abaa91c0b1d35 Mon Sep 17 00:00:00 2001 From: Disservin Date: Sun, 14 Jan 2024 00:30:03 +0100 Subject: [PATCH 1326/1766] Remove unused method init() is no longer used, and was previously replaced by the clear function. fixes https://github.com/official-stockfish/Stockfish/issues/4981 No functional change --- src/search.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.h b/src/search.h index 48a5630cddb..90ed82b923c 100644 --- a/src/search.h +++ b/src/search.h @@ -48,8 +48,6 @@ class UCI; namespace Search { -// Called at startup to initialize various lookup tables, after program startup -void init(int); // Stack struct keeps track of the information we need to remember from nodes // shallower and deeper in the tree during the search. Each search thread has @@ -176,6 +174,7 @@ class Worker { public: Worker(SharedState&, std::unique_ptr, size_t); + // Called at instantiation to initialize Reductions tables // Reset histories, usually before a new game void clear(); From 12e97701b299a2abfaa86d67b670411a39e5ccce Mon Sep 17 00:00:00 2001 From: Disservin Date: Sun, 14 Jan 2024 10:39:57 +0100 Subject: [PATCH 1327/1766] Fix UCI options Fixes the type for 'Clear Hash' and uses MAX_MOVES for 'MultiPV' as we had before. No functional change --- src/uci.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uci.cpp b/src/uci.cpp index 82fb25c1d27..aa493c63487 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -64,9 +64,9 @@ UCI::UCI(int argc, char** argv) : tt.resize(o, options["Threads"]); }); - options["Clear Hash"] << Option(true, [this](const Option&) { search_clear(); }); + options["Clear Hash"] << Option([this](const Option&) { search_clear(); }); options["Ponder"] << Option(false); - options["MultiPV"] << Option(1, 1, 500); + options["MultiPV"] << Option(1, 1, MAX_MOVES); options["Skill Level"] << Option(20, 0, 20); options["Move Overhead"] << Option(10, 0, 5000); options["nodestime"] << Option(0, 0, 10000); From 88331add0d1220068f1bc1c0e1db88598425dafc Mon Sep 17 00:00:00 2001 From: Disservin Date: Sat, 13 Jan 2024 20:19:33 +0100 Subject: [PATCH 1328/1766] Remove the dependency on a Worker from evaluate Also remove dead code, `rootSimpleEval` is no longer used since the introduction of dual net. `iterBestValue` is also no longer used in evaluate and can be reduced to a local variable. closes https://github.com/official-stockfish/Stockfish/pull/4979 No functional change --- src/evaluate.cpp | 15 +++------------ src/evaluate.h | 8 ++------ src/search.cpp | 38 ++++++++++++++++++++------------------ src/search.h | 6 ++---- src/thread.cpp | 4 +--- src/uci.cpp | 2 +- 6 files changed, 29 insertions(+), 44 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 3e067e4c447..45658798c52 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -35,7 +35,6 @@ #include "nnue/evaluate_nnue.h" #include "nnue/nnue_architecture.h" #include "position.h" -#include "search.h" #include "types.h" #include "uci.h" #include "ucioption.h" @@ -196,7 +195,7 @@ int Eval::simple_eval(const Position& pos, Color c) { // Evaluate is the evaluator for the outer world. It returns a static evaluation // of the position from the point of view of the side to move. -Value Eval::evaluate(const Position& pos, const Search::Worker& workerThread) { +Value Eval::evaluate(const Position& pos, int optimism) { assert(!pos.checkers()); @@ -217,8 +216,6 @@ Value Eval::evaluate(const Position& pos, const Search::Worker& workerThread) { Value nnue = smallNet ? NNUE::evaluate(pos, true, &nnueComplexity) : NNUE::evaluate(pos, true, &nnueComplexity); - int optimism = workerThread.optimism[stm]; - // Blend optimism and eval with nnue complexity and material imbalance optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / 512; nnue -= nnue * (nnueComplexity + std::abs(simpleEval - nnue)) / 32768; @@ -240,17 +237,11 @@ Value Eval::evaluate(const Position& pos, const Search::Worker& workerThread) { // a string (suitable for outputting to stdout) that contains the detailed // descriptions and values of each evaluation term. Useful for debugging. // Trace scores are from white's point of view -std::string Eval::trace(Position& pos, Search::Worker& workerThread) { +std::string Eval::trace(Position& pos) { if (pos.checkers()) return "Final evaluation: none (in check)"; - // Reset any global variable used in eval - workerThread.iterBestValue = VALUE_ZERO; - workerThread.rootSimpleEval = VALUE_ZERO; - workerThread.optimism[WHITE] = VALUE_ZERO; - workerThread.optimism[BLACK] = VALUE_ZERO; - std::stringstream ss; ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2); ss << '\n' << NNUE::trace(pos) << '\n'; @@ -262,7 +253,7 @@ std::string Eval::trace(Position& pos, Search::Worker& workerThread) { v = pos.side_to_move() == WHITE ? v : -v; ss << "NNUE evaluation " << 0.01 * UCI::to_cp(v) << " (white side)\n"; - v = evaluate(pos, workerThread); + v = evaluate(pos, VALUE_ZERO); v = pos.side_to_move() == WHITE ? v : -v; ss << "Final evaluation " << 0.01 * UCI::to_cp(v) << " (white side)"; ss << " [with scaled NNUE, ...]"; diff --git a/src/evaluate.h b/src/evaluate.h index 8a9d6fc7c87..729baa6bcb8 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -29,16 +29,12 @@ namespace Stockfish { class Position; class OptionsMap; -namespace Search { -class Worker; -} - namespace Eval { -std::string trace(Position& pos, Search::Worker& workerThread); +std::string trace(Position& pos); int simple_eval(const Position& pos, Color c); -Value evaluate(const Position& pos, const Search::Worker& workerThread); +Value evaluate(const Position& pos, int optimism); // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the diff --git a/src/search.cpp b/src/search.cpp index 5530d125f15..b23740d831b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -280,7 +280,7 @@ void Search::Worker::iterative_deepening() { ss->pv = pv; - iterBestValue = -VALUE_INFINITE; + Value bestValue = -VALUE_INFINITE; if (mainThread) { @@ -357,7 +357,7 @@ void Search::Worker::iterative_deepening() { // for every four searchAgain steps (see issue #2717). Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt - 3 * (searchAgainCounter + 1) / 4); - iterBestValue = search(rootPos, ss, alpha, beta, adjustedDepth, false); + bestValue = search(rootPos, ss, alpha, beta, adjustedDepth, false); // Bring the best move to the front. It is critical that sorting // is done with a stable algorithm because all the values but the @@ -375,7 +375,7 @@ void Search::Worker::iterative_deepening() { // When failing high/low give some update (without cluttering // the UI) before a re-search. - if (mainThread && multiPV == 1 && (iterBestValue <= alpha || iterBestValue >= beta) + if (mainThread && multiPV == 1 && (bestValue <= alpha || bestValue >= beta) && mainThread->tm.elapsed(threads.nodes_searched()) > 3000) sync_cout << UCI::pv(*this, mainThread->tm.elapsed(threads.nodes_searched()), threads.nodes_searched(), threads.tb_hits(), tt.hashfull(), @@ -384,18 +384,18 @@ void Search::Worker::iterative_deepening() { // In case of failing low/high increase aspiration window and // re-search, otherwise exit the loop. - if (iterBestValue <= alpha) + if (bestValue <= alpha) { beta = (alpha + beta) / 2; - alpha = std::max(iterBestValue - delta, -VALUE_INFINITE); + alpha = std::max(bestValue - delta, -VALUE_INFINITE); failedHighCnt = 0; if (mainThread) mainThread->stopOnPonderhit = false; } - else if (iterBestValue >= beta) + else if (bestValue >= beta) { - beta = std::min(iterBestValue + delta, int(VALUE_INFINITE)); + beta = std::min(bestValue + delta, int(VALUE_INFINITE)); ++failedHighCnt; } else @@ -428,8 +428,8 @@ void Search::Worker::iterative_deepening() { } // Have we found a "mate in x"? - if (limits.mate && iterBestValue >= VALUE_MATE_IN_MAX_PLY - && VALUE_MATE - iterBestValue <= 2 * limits.mate) + if (limits.mate && bestValue >= VALUE_MATE_IN_MAX_PLY + && VALUE_MATE - bestValue <= 2 * limits.mate) threads.stop = true; if (!mainThread) @@ -449,8 +449,8 @@ void Search::Worker::iterative_deepening() { // Do we have time for the next iteration? Can we stop searching now? if (limits.use_time_management() && !threads.stop && !mainThread->stopOnPonderhit) { - double fallingEval = (66 + 14 * (mainThread->bestPreviousAverageScore - iterBestValue) - + 6 * (mainThread->iterValue[iterIdx] - iterBestValue)) + double fallingEval = (66 + 14 * (mainThread->bestPreviousAverageScore - bestValue) + + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 616.6; fallingEval = std::clamp(fallingEval, 0.51, 1.51); @@ -483,7 +483,7 @@ void Search::Worker::iterative_deepening() { threads.increaseDepth = true; } - mainThread->iterValue[iterIdx] = iterBestValue; + mainThread->iterValue[iterIdx] = bestValue; iterIdx = (iterIdx + 1) & 3; } @@ -580,7 +580,7 @@ Value Search::Worker::search( // Step 2. Check for aborted search and immediate draw if (threads.stop.load(std::memory_order_relaxed) || pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) - return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos, *thisThread) + return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos, thisThread->optimism[us]) : value_draw(thisThread->nodes); // Step 3. Mate distance pruning. Even if we mate at the next move our score @@ -734,7 +734,7 @@ Value Search::Worker::search( // Never assume anything about values stored in TT unadjustedStaticEval = ss->staticEval = eval = tte->eval(); if (eval == VALUE_NONE) - unadjustedStaticEval = ss->staticEval = eval = evaluate(pos, *thisThread); + unadjustedStaticEval = ss->staticEval = eval = evaluate(pos, thisThread->optimism[us]); else if (PvNode) Eval::NNUE::hint_common_parent_position(pos); @@ -752,7 +752,7 @@ Value Search::Worker::search( } else { - unadjustedStaticEval = ss->staticEval = eval = evaluate(pos, *thisThread); + unadjustedStaticEval = ss->staticEval = eval = evaluate(pos, thisThread->optimism[us]); Value newEval = ss->staticEval @@ -1470,7 +1470,8 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, // Step 2. Check for an immediate draw or maximum ply reached if (pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) - return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos, *thisThread) : VALUE_DRAW; + return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos, thisThread->optimism[us]) + : VALUE_DRAW; assert(0 <= ss->ply && ss->ply < MAX_PLY); @@ -1501,7 +1502,8 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, { // Never assume anything about values stored in TT if ((unadjustedStaticEval = ss->staticEval = bestValue = tte->eval()) == VALUE_NONE) - unadjustedStaticEval = ss->staticEval = bestValue = evaluate(pos, *thisThread); + unadjustedStaticEval = ss->staticEval = bestValue = + evaluate(pos, thisThread->optimism[us]); Value newEval = ss->staticEval @@ -1521,7 +1523,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, { // In case of null move search, use previous static eval with a different sign unadjustedStaticEval = ss->staticEval = bestValue = - (ss - 1)->currentMove != Move::null() ? evaluate(pos, *thisThread) + (ss - 1)->currentMove != Move::null() ? evaluate(pos, thisThread->optimism[us]) : -(ss - 1)->staticEval; Value newEval = diff --git a/src/search.h b/src/search.h index 90ed82b923c..68c9f2aa7e0 100644 --- a/src/search.h +++ b/src/search.h @@ -184,10 +184,6 @@ class Worker { bool is_mainthread() const { return thread_idx == 0; } - // Public because evaluate uses this - Value iterBestValue, optimism[COLOR_NB]; - Value rootSimpleEval; - // Public because they need to be updatable by the stats CounterMoveHistory counterMoves; ButterflyHistory mainHistory; @@ -226,6 +222,8 @@ class Worker { std::atomic nodes, tbHits, bestMoveChanges; int selDepth, nmpMinPly; + Value optimism[COLOR_NB]; + Position rootPos; StateInfo rootState; RootMoves rootMoves; diff --git a/src/thread.cpp b/src/thread.cpp index a512c0a52b8..9dc4446f1cc 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -27,7 +27,6 @@ #include #include -#include "evaluate.h" #include "misc.h" #include "movegen.h" #include "search.h" @@ -205,8 +204,7 @@ void ThreadPool::start_thinking(const OptionsMap& options, th->worker->rootDepth = th->worker->completedDepth = 0; th->worker->rootMoves = rootMoves; th->worker->rootPos.set(pos.fen(), pos.is_chess960(), &th->worker->rootState); - th->worker->rootState = setupStates->back(); - th->worker->rootSimpleEval = Eval::simple_eval(pos, pos.side_to_move()); + th->worker->rootState = setupStates->back(); } main_thread()->start_searching(); diff --git a/src/uci.cpp b/src/uci.cpp index aa493c63487..789f345481d 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -270,7 +270,7 @@ void UCI::trace_eval(Position& pos) { Eval::NNUE::verify(options, evalFiles); - sync_cout << "\n" << Eval::trace(p, *threads.main_thread()->worker.get()) << sync_endl; + sync_cout << "\n" << Eval::trace(p) << sync_endl; } void UCI::search_clear() { From b5e8169a85f6937d7d9d90612863fe5eec72d6ca Mon Sep 17 00:00:00 2001 From: Disservin Date: Sat, 13 Jan 2024 21:35:02 +0100 Subject: [PATCH 1329/1766] Add ignoreRevsFile to CONTRIBUTING.md closes https://github.com/official-stockfish/Stockfish/pull/4980 No functional change --- CONTRIBUTING.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9e72e1db6bc..e589b4fcc82 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -61,6 +61,16 @@ Changes to Stockfish C++ code should respect our coding style defined by [.clang-format](.clang-format). You can format your changes by running `make format`. This requires clang-format version 17 to be installed on your system. +## Navigate + +For experienced Git users who frequently use git blame, it is recommended to +configure the blame.ignoreRevsFile setting. +This setting is useful for excluding noisy formatting commits. + +```bash +git config blame.ignoreRevsFile .git-blame-ignore-revs +``` + ## Community and Communication - Join the [Stockfish discord][discord-link] to discuss ideas, issues, and From 32e46fc47f521d2b93f96c63267fc0bda26332dc Mon Sep 17 00:00:00 2001 From: mstembera Date: Mon, 8 Jan 2024 23:20:23 -0800 Subject: [PATCH 1330/1766] Remove some outdated SIMD functions Since https://github.com/official-stockfish/Stockfish/pull/4391 the x2 SIMD functions no longer serve any useful purpose. Passed non-regression STC: https://tests.stockfishchess.org/tests/view/659cf42579aa8af82b966d55 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 67392 W: 17222 L: 17037 D: 33133 Ptnml(0-2): 207, 7668, 17762, 7851, 208 closes https://github.com/official-stockfish/Stockfish/pull/4974 No functional change --- src/nnue/layers/affine_transform.h | 19 +++-------- src/nnue/layers/simd.h | 54 ------------------------------ 2 files changed, 5 insertions(+), 68 deletions(-) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index e6852236108..ad9167c05c4 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -200,21 +200,18 @@ class AffineTransform { #define vec_setzero _mm512_setzero_si512 #define vec_set_32 _mm512_set1_epi32 #define vec_add_dpbusd_32 Simd::m512_add_dpbusd_epi32 - #define vec_add_dpbusd_32x2 Simd::m512_add_dpbusd_epi32x2 #define vec_hadd Simd::m512_hadd #elif defined(USE_AVX2) using vec_t = __m256i; #define vec_setzero _mm256_setzero_si256 #define vec_set_32 _mm256_set1_epi32 #define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32 - #define vec_add_dpbusd_32x2 Simd::m256_add_dpbusd_epi32x2 #define vec_hadd Simd::m256_hadd #elif defined(USE_SSSE3) using vec_t = __m128i; #define vec_setzero _mm_setzero_si128 #define vec_set_32 _mm_set1_epi32 #define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32 - #define vec_add_dpbusd_32x2 Simd::m128_add_dpbusd_epi32x2 #define vec_hadd Simd::m128_hadd #endif @@ -231,16 +228,14 @@ class AffineTransform { for (IndexType k = 0; k < NumRegs; ++k) acc[k] = biasvec[k]; - for (IndexType i = 0; i < NumChunks; i += 2) + for (IndexType i = 0; i < NumChunks; ++i) { - const vec_t in0 = vec_set_32(input32[i + 0]); - const vec_t in1 = vec_set_32(input32[i + 1]); + const vec_t in0 = vec_set_32(input32[i]); const auto col0 = - reinterpret_cast(&weights[(i + 0) * OutputDimensions * 4]); - const auto col1 = - reinterpret_cast(&weights[(i + 1) * OutputDimensions * 4]); + reinterpret_cast(&weights[i * OutputDimensions * 4]); + for (IndexType k = 0; k < NumRegs; ++k) - vec_add_dpbusd_32x2(acc[k], in0, col0[k], in1, col1[k]); + vec_add_dpbusd_32(acc[k], in0, col0[k]); } vec_t* outptr = reinterpret_cast(output); @@ -250,7 +245,6 @@ class AffineTransform { #undef vec_setzero #undef vec_set_32 #undef vec_add_dpbusd_32 - #undef vec_add_dpbusd_32x2 #undef vec_hadd } else if constexpr (OutputDimensions == 1) @@ -263,14 +257,12 @@ class AffineTransform { #define vec_setzero _mm256_setzero_si256 #define vec_set_32 _mm256_set1_epi32 #define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32 - #define vec_add_dpbusd_32x2 Simd::m256_add_dpbusd_epi32x2 #define vec_hadd Simd::m256_hadd #elif defined(USE_SSSE3) using vec_t = __m128i; #define vec_setzero _mm_setzero_si128 #define vec_set_32 _mm_set1_epi32 #define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32 - #define vec_add_dpbusd_32x2 Simd::m128_add_dpbusd_epi32x2 #define vec_hadd Simd::m128_hadd #endif @@ -294,7 +286,6 @@ class AffineTransform { #undef vec_setzero #undef vec_set_32 #undef vec_add_dpbusd_32 - #undef vec_add_dpbusd_32x2 #undef vec_hadd } #else diff --git a/src/nnue/layers/simd.h b/src/nnue/layers/simd.h index 6f4c9d206ed..cec41474bb0 100644 --- a/src/nnue/layers/simd.h +++ b/src/nnue/layers/simd.h @@ -87,21 +87,6 @@ m512_hadd128x16_interleave(__m512i sum0, __m512i sum1, __m512i sum2, __m512i sum #endif } -[[maybe_unused]] static void -m512_add_dpbusd_epi32x2(__m512i& acc, __m512i a0, __m512i b0, __m512i a1, __m512i b1) { - - #if defined(USE_VNNI) - acc = _mm512_dpbusd_epi32(acc, a0, b0); - acc = _mm512_dpbusd_epi32(acc, a1, b1); - #else - __m512i product0 = _mm512_maddubs_epi16(a0, b0); - __m512i product1 = _mm512_maddubs_epi16(a1, b1); - product0 = _mm512_madd_epi16(product0, _mm512_set1_epi16(1)); - product1 = _mm512_madd_epi16(product1, _mm512_set1_epi16(1)); - acc = _mm512_add_epi32(acc, _mm512_add_epi32(product0, product1)); - #endif -} - #endif #if defined(USE_AVX2) @@ -124,21 +109,6 @@ m512_add_dpbusd_epi32x2(__m512i& acc, __m512i a0, __m512i b0, __m512i a1, __m512 #endif } -[[maybe_unused]] static void -m256_add_dpbusd_epi32x2(__m256i& acc, __m256i a0, __m256i b0, __m256i a1, __m256i b1) { - - #if defined(USE_VNNI) - acc = _mm256_dpbusd_epi32(acc, a0, b0); - acc = _mm256_dpbusd_epi32(acc, a1, b1); - #else - __m256i product0 = _mm256_maddubs_epi16(a0, b0); - __m256i product1 = _mm256_maddubs_epi16(a1, b1); - product0 = _mm256_madd_epi16(product0, _mm256_set1_epi16(1)); - product1 = _mm256_madd_epi16(product1, _mm256_set1_epi16(1)); - acc = _mm256_add_epi32(acc, _mm256_add_epi32(product0, product1)); - #endif -} - #endif #if defined(USE_SSSE3) @@ -156,27 +126,10 @@ m256_add_dpbusd_epi32x2(__m256i& acc, __m256i a0, __m256i b0, __m256i a1, __m256 acc = _mm_add_epi32(acc, product0); } -[[maybe_unused]] static void -m128_add_dpbusd_epi32x2(__m128i& acc, __m128i a0, __m128i b0, __m128i a1, __m128i b1) { - - __m128i product0 = _mm_maddubs_epi16(a0, b0); - __m128i product1 = _mm_maddubs_epi16(a1, b1); - product0 = _mm_madd_epi16(product0, _mm_set1_epi16(1)); - product1 = _mm_madd_epi16(product1, _mm_set1_epi16(1)); - acc = _mm_add_epi32(acc, _mm_add_epi32(product0, product1)); -} - #endif #if defined(USE_NEON_DOTPROD) -[[maybe_unused]] static void dotprod_m128_add_dpbusd_epi32x2( - int32x4_t& acc, int8x16_t a0, int8x16_t b0, int8x16_t a1, int8x16_t b1) { - - acc = vdotq_s32(acc, a0, b0); - acc = vdotq_s32(acc, a1, b1); -} - [[maybe_unused]] static void dotprod_m128_add_dpbusd_epi32(int32x4_t& acc, int8x16_t a, int8x16_t b) { @@ -198,13 +151,6 @@ dotprod_m128_add_dpbusd_epi32(int32x4_t& acc, int8x16_t a, int8x16_t b) { return neon_m128_reduce_add_epi32(sum) + bias; } -[[maybe_unused]] static void -neon_m128_add_dpbusd_epi32x2(int32x4_t& acc, int8x8_t a0, int8x8_t b0, int8x8_t a1, int8x8_t b1) { - - int16x8_t product = vmull_s8(a0, b0); - product = vmlal_s8(product, a1, b1); - acc = vpadalq_s16(acc, product); -} #endif #if USE_NEON >= 8 From a5675f19d87a15a5e599a108dfaa2d08ac50d6a5 Mon Sep 17 00:00:00 2001 From: Disservin Date: Sun, 14 Jan 2024 14:29:12 +0100 Subject: [PATCH 1331/1766] Remove global TB variables from search.cpp Follow up cleanup of #4968, removes the global variables from search and instead uses a dedicated tb config struct. closes https://github.com/official-stockfish/Stockfish/pull/4982 No functional change --- src/bitboard.cpp | 6 ++-- src/search.cpp | 73 +++++------------------------------------- src/search.h | 4 ++- src/syzygy/tbprobe.cpp | 59 +++++++++++++++++++++++++++++++++- src/syzygy/tbprobe.h | 18 +++++++++-- src/thread.cpp | 4 +-- 6 files changed, 89 insertions(+), 75 deletions(-) diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 72afabb6554..32c626d4773 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -44,15 +44,13 @@ Bitboard BishopTable[0x1480]; // To store bishop attacks void init_magics(PieceType pt, Bitboard table[], Magic magics[]); -} - // Returns the bitboard of target square for the given step // from the given square. If the step is off the board, returns empty bitboard. -inline Bitboard safe_destination(Square s, int step) { +Bitboard safe_destination(Square s, int step) { Square to = Square(s + step); return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0); } - +} // Returns an ASCII representation of a bitboard suitable // to be printed to standard output. Useful for debugging. diff --git a/src/search.cpp b/src/search.cpp index b23740d831b..8901dfd1c1e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -29,7 +29,6 @@ #include #include -#include "bitboard.h" #include "evaluate.h" #include "misc.h" #include "movegen.h" @@ -46,14 +45,6 @@ namespace Stockfish { -namespace Tablebases { - -int Cardinality; -bool RootInTB; -bool UseRule50; -Depth ProbeDepth; -} - namespace TB = Tablebases; using Eval::evaluate; @@ -237,7 +228,7 @@ void Search::Worker::start_searching() { if (bestThread != this) sync_cout << UCI::pv(*bestThread, main_manager()->tm.elapsed(threads.nodes_searched()), threads.nodes_searched(), threads.tb_hits(), tt.hashfull(), - TB::RootInTB) + tbConfig.rootInTB) << sync_endl; sync_cout << "bestmove " << UCI::move(bestThread->rootMoves[0].pv[0], rootPos.is_chess960()); @@ -379,7 +370,7 @@ void Search::Worker::iterative_deepening() { && mainThread->tm.elapsed(threads.nodes_searched()) > 3000) sync_cout << UCI::pv(*this, mainThread->tm.elapsed(threads.nodes_searched()), threads.nodes_searched(), threads.tb_hits(), tt.hashfull(), - TB::RootInTB) + tbConfig.rootInTB) << sync_endl; // In case of failing low/high increase aspiration window and @@ -414,7 +405,7 @@ void Search::Worker::iterative_deepening() { || mainThread->tm.elapsed(threads.nodes_searched()) > 3000)) sync_cout << UCI::pv(*this, mainThread->tm.elapsed(threads.nodes_searched()), threads.nodes_searched(), threads.tb_hits(), tt.hashfull(), - TB::RootInTB) + tbConfig.rootInTB) << sync_endl; } @@ -659,13 +650,13 @@ Value Search::Worker::search( } // Step 5. Tablebases probe - if (!rootNode && !excludedMove && TB::Cardinality) + if (!rootNode && !excludedMove && tbConfig.cardinality) { int piecesCount = pos.count(); - if (piecesCount <= TB::Cardinality - && (piecesCount < TB::Cardinality || depth >= TB::ProbeDepth) && pos.rule50_count() == 0 - && !pos.can_castle(ANY_CASTLING)) + if (piecesCount <= tbConfig.cardinality + && (piecesCount < tbConfig.cardinality || depth >= tbConfig.probeDepth) + && pos.rule50_count() == 0 && !pos.can_castle(ANY_CASTLING)) { TB::ProbeState err; TB::WDLScore wdl = Tablebases::probe_wdl(pos, &err); @@ -678,7 +669,7 @@ Value Search::Worker::search( { thisThread->tbHits.fetch_add(1, std::memory_order_relaxed); - int drawScore = TB::UseRule50 ? 1 : 0; + int drawScore = tbConfig.useRule50 ? 1 : 0; Value tbValue = VALUE_TB - ss->ply; @@ -1962,53 +1953,5 @@ bool RootMove::extract_ponder_from_tt(const TranspositionTable& tt, Position& po return pv.size() > 1; } -void Tablebases::rank_root_moves(const OptionsMap& options, - Position& pos, - Search::RootMoves& rootMoves) { - - RootInTB = false; - UseRule50 = bool(options["Syzygy50MoveRule"]); - ProbeDepth = int(options["SyzygyProbeDepth"]); - Cardinality = int(options["SyzygyProbeLimit"]); - bool dtz_available = true; - - // Tables with fewer pieces than SyzygyProbeLimit are searched with - // ProbeDepth == DEPTH_ZERO - if (Cardinality > MaxCardinality) - { - Cardinality = MaxCardinality; - ProbeDepth = 0; - } - - if (Cardinality >= popcount(pos.pieces()) && !pos.can_castle(ANY_CASTLING)) - { - // Rank moves using DTZ tables - RootInTB = root_probe(pos, rootMoves, options["Syzygy50MoveRule"]); - - if (!RootInTB) - { - // DTZ tables are missing; try to rank moves using WDL tables - dtz_available = false; - RootInTB = root_probe_wdl(pos, rootMoves, options["Syzygy50MoveRule"]); - } - } - - if (RootInTB) - { - // Sort moves according to TB rank - std::stable_sort(rootMoves.begin(), rootMoves.end(), - [](const RootMove& a, const RootMove& b) { return a.tbRank > b.tbRank; }); - - // Probe during search only if DTZ is not available and we are winning - if (dtz_available || rootMoves[0].tbScore <= VALUE_DRAW) - Cardinality = 0; - } - else - { - // Clean up if root_probe() and root_probe_wdl() have failed - for (auto& m : rootMoves) - m.tbRank = 0; - } -} } // namespace Stockfish diff --git a/src/search.h b/src/search.h index 68c9f2aa7e0..daf0ff85489 100644 --- a/src/search.h +++ b/src/search.h @@ -29,6 +29,7 @@ #include "misc.h" #include "movepick.h" #include "position.h" +#include "syzygy/tbprobe.h" #include "timeman.h" #include "types.h" @@ -48,7 +49,6 @@ class UCI; namespace Search { - // Stack struct keeps track of the information we need to remember from nodes // shallower and deeper in the tree during the search. Each search thread has // its own array of Stack objects, indexed by the current ply. @@ -238,6 +238,8 @@ class Worker { // The main thread has a SearchManager, the others have a NullSearchManager std::unique_ptr manager; + Tablebases::Config tbConfig; + const OptionsMap& options; ThreadPool& threads; TranspositionTable& tt; diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 6f30bf6b1f7..722dc9d3e8e 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -18,7 +18,6 @@ #include "tbprobe.h" -#include #include #include #include @@ -32,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -42,6 +42,7 @@ #include "../position.h" #include "../search.h" #include "../types.h" +#include "../ucioption.h" #ifndef _WIN32 #include @@ -1680,4 +1681,60 @@ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, boo return true; } +Config Tablebases::rank_root_moves(const OptionsMap& options, + Position& pos, + Search::RootMoves& rootMoves) { + Config config; + + if (rootMoves.empty()) + return config; + + config.rootInTB = false; + config.useRule50 = bool(options["Syzygy50MoveRule"]); + config.probeDepth = int(options["SyzygyProbeDepth"]); + config.cardinality = int(options["SyzygyProbeLimit"]); + + bool dtz_available = true; + + // Tables with fewer pieces than SyzygyProbeLimit are searched with + // probeDepth == DEPTH_ZERO + if (config.cardinality > MaxCardinality) + { + config.cardinality = MaxCardinality; + config.probeDepth = 0; + } + + if (config.cardinality >= popcount(pos.pieces()) && !pos.can_castle(ANY_CASTLING)) + { + // Rank moves using DTZ tables + config.rootInTB = root_probe(pos, rootMoves, options["Syzygy50MoveRule"]); + + if (!config.rootInTB) + { + // DTZ tables are missing; try to rank moves using WDL tables + dtz_available = false; + config.rootInTB = root_probe_wdl(pos, rootMoves, options["Syzygy50MoveRule"]); + } + } + + if (config.rootInTB) + { + // Sort moves according to TB rank + std::stable_sort( + rootMoves.begin(), rootMoves.end(), + [](const Search::RootMove& a, const Search::RootMove& b) { return a.tbRank > b.tbRank; }); + + // Probe during search only if DTZ is not available and we are winning + if (dtz_available || rootMoves[0].tbScore <= VALUE_DRAW) + config.cardinality = 0; + } + else + { + // Clean up if root_probe() and root_probe_wdl() have failed + for (auto& m : rootMoves) + m.tbRank = 0; + } + + return config; +} } // namespace Stockfish diff --git a/src/syzygy/tbprobe.h b/src/syzygy/tbprobe.h index d7b412a10e5..e10950f4e6b 100644 --- a/src/syzygy/tbprobe.h +++ b/src/syzygy/tbprobe.h @@ -20,16 +20,30 @@ #define TBPROBE_H #include +#include -#include "../search.h" namespace Stockfish { class Position; class OptionsMap; + +using Depth = int; + +namespace Search { +struct RootMove; +using RootMoves = std::vector; +} } namespace Stockfish::Tablebases { +struct Config { + int cardinality = 0; + bool rootInTB = false; + bool useRule50 = false; + Depth probeDepth = 0; +}; + enum WDLScore { WDLLoss = -2, // Loss WDLBlessedLoss = -1, // Loss, but draw under 50-move rule @@ -54,7 +68,7 @@ WDLScore probe_wdl(Position& pos, ProbeState* result); int probe_dtz(Position& pos, ProbeState* result); bool root_probe(Position& pos, Search::RootMoves& rootMoves, bool rule50); bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, bool rule50); -void rank_root_moves(const OptionsMap& options, Position& pos, Search::RootMoves& rootMoves); +Config rank_root_moves(const OptionsMap& options, Position& pos, Search::RootMoves& rootMoves); } // namespace Stockfish::Tablebases diff --git a/src/thread.cpp b/src/thread.cpp index 9dc4446f1cc..a4bc3d6795b 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -181,8 +181,7 @@ void ThreadPool::start_thinking(const OptionsMap& options, || std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m)) rootMoves.emplace_back(m); - if (!rootMoves.empty()) - Tablebases::rank_root_moves(options, pos, rootMoves); + Tablebases::Config tbConfig = Tablebases::rank_root_moves(options, pos, rootMoves); // After ownership transfer 'states' becomes empty, so if we stop the search // and call 'go' again without setting a new position states.get() == nullptr. @@ -205,6 +204,7 @@ void ThreadPool::start_thinking(const OptionsMap& options, th->worker->rootMoves = rootMoves; th->worker->rootPos.set(pos.fen(), pos.is_chess960(), &th->worker->rootState); th->worker->rootState = setupStates->back(); + th->worker->tbConfig = tbConfig; } main_thread()->start_searching(); From f15e4f50aae3bea3991da4f72a9ff124e4db644b Mon Sep 17 00:00:00 2001 From: Disservin Date: Sun, 14 Jan 2024 20:44:38 +0100 Subject: [PATCH 1332/1766] Update installation guide links in CONTRIBUTING.md Link to more user friendly installation guides, these are shorter and easier to follow. closes https://github.com/official-stockfish/Stockfish/pull/4985 No functional change --- CONTRIBUTING.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e589b4fcc82..cf9cecda2b5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,7 +19,7 @@ making contributions to Stockfish. In case you do not have a C++ compiler installed, you can follow the instructions from our wiki. -- [Linux][linux-compiling-link] +- [Ubuntu][ubuntu-compiling-link] - [Windows][windows-compiling-link] - [macOS][macos-compiling-link] @@ -92,6 +92,6 @@ Thank you for contributing to Stockfish and helping us make it even better! [discussions-link]: https://github.com/official-stockfish/Stockfish/discussions/new [creating-my-first-test]: https://github.com/official-stockfish/fishtest/wiki/Creating-my-first-test#create-your-test [issue-tracker-link]: https://github.com/official-stockfish/Stockfish/issues -[linux-compiling-link]: https://github.com/official-stockfish/Stockfish/wiki/Compiling-from-source#linux -[windows-compiling-link]: https://github.com/official-stockfish/Stockfish/wiki/Compiling-from-source#windows -[macos-compiling-link]: https://github.com/official-stockfish/Stockfish/wiki/Compiling-from-source#macos +[ubuntu-compiling-link]: https://github.com/official-stockfish/Stockfish/wiki/Developers#user-content-installing-a-compiler-1 +[windows-compiling-link]: https://github.com/official-stockfish/Stockfish/wiki/Developers#user-content-installing-a-compiler +[macos-compiling-link]: https://github.com/official-stockfish/Stockfish/wiki/Developers#user-content-installing-a-compiler-2 From 0c7f56dea6ee9a46a4be8481b2316711cb356f02 Mon Sep 17 00:00:00 2001 From: "Shahin M. Shahin" <41402573+peregrineshahin@users.noreply.github.com> Date: Mon, 8 Jan 2024 10:31:23 +0300 Subject: [PATCH 1333/1766] Fix mated-in behaviour MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This addresses the issue where Stockfish may output non-proven checkmate scores if the search is prematurely halted, either due to a time control or node limit, before it explores other possibilities where the checkmate score could have been delayed or refuted. The fix also replaces staving off from proven mated scores in a multithread environment making use of the threads instead of a negative effect with multithreads (1t was better in proving mated in scores than more threads). Issue reported on mate tracker repo by and this PR is co-authored with @robertnurnberg Special thanks to @AndyGrant for outlining that a fix is eventually possible. Passed Adj off SMP STC: https://tests.stockfishchess.org/tests/view/65a125d779aa8af82b96c3eb LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 303256 W: 75823 L: 75892 D: 151541 Ptnml(0-2): 406, 35269, 80395, 35104, 454 Passed Adj off SMP LTC: https://tests.stockfishchess.org/tests/view/65a37add79aa8af82b96f0f7 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 56056 W: 13951 L: 13770 D: 28335 Ptnml(0-2): 11, 5910, 16002, 6097, 8 Passed all tests in matetrack without any better mate for opponent found in 1t and multithreads. Fixed bugs in https://github.com/official-stockfish/Stockfish/pull/4976 closes https://github.com/official-stockfish/Stockfish/pull/4990 Bench: 1308279 Co-Authored-By: Robert Nürnberg <28635489+robertnurnberg@users.noreply.github.com> --- src/misc.h | 15 ++++++++++++++ src/search.cpp | 55 ++++++++++++++++++++++++++++++++++---------------- src/search.h | 2 +- src/thread.cpp | 20 ++++++++++++------ src/thread.h | 2 +- 5 files changed, 69 insertions(+), 25 deletions(-) diff --git a/src/misc.h b/src/misc.h index 994f551d5ff..f73e7889a8d 100644 --- a/src/misc.h +++ b/src/misc.h @@ -19,12 +19,14 @@ #ifndef MISC_H_INCLUDED #define MISC_H_INCLUDED +#include #include #include #include #include #include #include +#include #define stringify2(x) #x #define stringify(x) stringify2(x) @@ -188,6 +190,19 @@ struct CommandLine { std::string workingDirectory; // path of the working directory }; +namespace Utility { + +template +void move_to_front(std::vector& vec, Predicate pred) { + auto it = std::find_if(vec.begin(), vec.end(), pred); + + if (it != vec.end()) + { + std::rotate(vec.begin(), it, it + 1); + } +} +} + } // namespace Stockfish #endif // #ifndef MISC_H_INCLUDED diff --git a/src/search.cpp b/src/search.cpp index 8901dfd1c1e..8363f2215b1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -248,15 +248,16 @@ void Search::Worker::iterative_deepening() { // Allocate stack with extra size to allow access from (ss - 7) to (ss + 2): // (ss - 7) is needed for update_continuation_histories(ss - 1) which accesses (ss - 6), // (ss + 2) is needed for initialization of cutOffCnt and killers. - Stack stack[MAX_PLY + 10], *ss = stack + 7; - Move pv[MAX_PLY + 1]; - Value alpha, beta; - Move lastBestMove = Move::none(); - Depth lastBestMoveDepth = 0; - SearchManager* mainThread = (thread_idx == 0 ? main_manager() : nullptr); - double timeReduction = 1, totBestMoveChanges = 0; - Color us = rootPos.side_to_move(); - int delta, iterIdx = 0; + Stack stack[MAX_PLY + 10], *ss = stack + 7; + Move pv[MAX_PLY + 1]; + Value alpha, beta; + Value lastBestScore = -VALUE_INFINITE; + std::vector lastBestPV = {Move::none()}; + Depth lastBestMoveDepth = 0; + SearchManager* mainThread = (thread_idx == 0 ? main_manager() : nullptr); + double timeReduction = 1, totBestMoveChanges = 0; + Color us = rootPos.side_to_move(); + int delta, iterIdx = 0; std::memset(ss - 7, 0, 10 * sizeof(Stack)); for (int i = 7; i > 0; --i) @@ -402,7 +403,12 @@ void Search::Worker::iterative_deepening() { if (mainThread && (threads.stop || pvIdx + 1 == multiPV - || mainThread->tm.elapsed(threads.nodes_searched()) > 3000)) + || mainThread->tm.elapsed(threads.nodes_searched()) > 3000) + // A thread that aborted search can have mated-in/TB-loss PV and score + // that cannot be trusted, i.e. it can be delayed or refuted if we would have + // had time to fully search other root-moves. Thus we suppress this output and + // below pick a proven score/PV for this thread (from the previous iteration). + && !(threads.abortedSearch && rootMoves[0].uciScore <= VALUE_TB_LOSS_IN_MAX_PLY)) sync_cout << UCI::pv(*this, mainThread->tm.elapsed(threads.nodes_searched()), threads.nodes_searched(), threads.tb_hits(), tt.hashfull(), tbConfig.rootInTB) @@ -412,9 +418,21 @@ void Search::Worker::iterative_deepening() { if (!threads.stop) completedDepth = rootDepth; - if (rootMoves[0].pv[0] != lastBestMove) + // We make sure not to pick an unproven mated-in score, + // in case this thread prematurely stopped search (aborted-search). + if (threads.abortedSearch && rootMoves[0].score != -VALUE_INFINITE + && rootMoves[0].score <= VALUE_TB_LOSS_IN_MAX_PLY) { - lastBestMove = rootMoves[0].pv[0]; + // Bring the last best move to the front for best thread selection. + Utility::move_to_front(rootMoves, [&lastBestPV = std::as_const(lastBestPV)]( + const auto& rm) { return rm == lastBestPV[0]; }); + rootMoves[0].pv = lastBestPV; + rootMoves[0].score = rootMoves[0].uciScore = lastBestScore; + } + else if (rootMoves[0].pv[0] != lastBestPV[0]) + { + lastBestPV = rootMoves[0].pv; + lastBestScore = rootMoves[0].score; lastBestMoveDepth = rootDepth; } @@ -1916,11 +1934,14 @@ void SearchManager::check_time(Search::Worker& worker) { if (ponder) return; - if ((worker.limits.use_time_management() && (elapsed > tm.maximum() || stopOnPonderhit)) - || (worker.limits.movetime && elapsed >= worker.limits.movetime) - || (worker.limits.nodes - && worker.threads.nodes_searched() >= uint64_t(worker.limits.nodes))) - worker.threads.stop = true; + if ( + // Later we rely on the fact that we can at least use the mainthread previous + // root-search score and PV in a multithreaded environment to prove mated-in scores. + worker.completedDepth >= 1 + && ((worker.limits.use_time_management() && (elapsed > tm.maximum() || stopOnPonderhit)) + || (worker.limits.movetime && elapsed >= worker.limits.movetime) + || (worker.limits.nodes && worker.threads.nodes_searched() >= worker.limits.nodes))) + worker.threads.stop = worker.threads.abortedSearch = true; } // Called in case we have no ponder move before exiting the search, diff --git a/src/search.h b/src/search.h index daf0ff85489..4bd013ad670 100644 --- a/src/search.h +++ b/src/search.h @@ -115,7 +115,7 @@ struct LimitsType { std::vector searchmoves; TimePoint time[COLOR_NB], inc[COLOR_NB], npmsec, movetime, startTime; int movestogo, depth, mate, perft, infinite; - int64_t nodes; + uint64_t nodes; }; diff --git a/src/thread.cpp b/src/thread.cpp index a4bc3d6795b..4c1d01f46da 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -20,8 +20,6 @@ #include #include -#include -#include #include #include #include @@ -169,8 +167,8 @@ void ThreadPool::start_thinking(const OptionsMap& options, main_thread()->wait_for_search_finished(); - main_manager()->stopOnPonderhit = stop = false; - main_manager()->ponder = ponderMode; + main_manager()->stopOnPonderhit = stop = abortedSearch = false; + main_manager()->ponder = ponderMode; increaseDepth = true; @@ -229,13 +227,23 @@ Thread* ThreadPool::get_best_thread() const { votes[th->worker->rootMoves[0].pv[0]] += thread_value(th); for (Thread* th : threads) - if (std::abs(bestThread->worker->rootMoves[0].score) >= VALUE_TB_WIN_IN_MAX_PLY) + if (bestThread->worker->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY) { - // Make sure we pick the shortest mate / TB conversion or stave off mate the longest + // Make sure we pick the shortest mate / TB conversion if (th->worker->rootMoves[0].score > bestThread->worker->rootMoves[0].score) bestThread = th; } + else if (bestThread->worker->rootMoves[0].score != -VALUE_INFINITE + && bestThread->worker->rootMoves[0].score <= VALUE_TB_LOSS_IN_MAX_PLY) + { + // Make sure we pick the shortest mated / TB conversion + if (th->worker->rootMoves[0].score != -VALUE_INFINITE + && th->worker->rootMoves[0].score < bestThread->worker->rootMoves[0].score) + bestThread = th; + } else if (th->worker->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY + || (th->worker->rootMoves[0].score != -VALUE_INFINITE + && th->worker->rootMoves[0].score <= VALUE_TB_LOSS_IN_MAX_PLY) || (th->worker->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY && (votes[th->worker->rootMoves[0].pv[0]] > votes[bestThread->worker->rootMoves[0].pv[0]] diff --git a/src/thread.h b/src/thread.h index 6575b14e638..a2a1d18c454 100644 --- a/src/thread.h +++ b/src/thread.h @@ -94,7 +94,7 @@ class ThreadPool { void start_searching(); void wait_for_search_finished() const; - std::atomic_bool stop, increaseDepth; + std::atomic_bool stop, abortedSearch, increaseDepth; auto cbegin() const noexcept { return threads.cbegin(); } auto begin() noexcept { return threads.begin(); } From 6c02329860d49198ff6a20b8469fd50dd1aa2eab Mon Sep 17 00:00:00 2001 From: Torsten Hellwig Date: Mon, 15 Jan 2024 13:13:53 +0100 Subject: [PATCH 1334/1766] Fix dotprod detection This fixes the detection of dotprod capable CPUs. Previously it looked for the `dotprod` flag, but this does not exist (https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/arm64/kernel/cpuinfo.c#n50). The correct flag that specifies the dotprod capability is the `asimddp` flag. fixes #4931 closes https://github.com/official-stockfish/Stockfish/pull/4991 No functional change --- scripts/get_native_properties.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/get_native_properties.sh b/scripts/get_native_properties.sh index ae23c3bb4ac..fb124021a31 100755 --- a/scripts/get_native_properties.sh +++ b/scripts/get_native_properties.sh @@ -79,7 +79,7 @@ case $uname_s in 'aarch64') file_os='android' true_arch='armv8' - if check_flags 'dotprod'; then + if check_flags 'asimddp'; then true_arch="$true_arch-dotprod" fi ;; From 0fbad56c50edab533e5a2f0319016d14e6a9f99c Mon Sep 17 00:00:00 2001 From: pb00067 Date: Mon, 15 Jan 2024 15:32:06 +0100 Subject: [PATCH 1335/1766] Refactor code for correcting unadjustedStaticEval Passed non-regression STC: https://tests.stockfishchess.org/tests/live_elo/65a4df6a79aa8af82b970ca0 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 43328 W: 11103 L: 10892 D: 21333 Ptnml(0-2): 120, 4920, 11407, 5063, 154 https://github.com/official-stockfish/Stockfish/pull/4992 No functional change --- src/search.cpp | 45 ++++++++++----------------------------------- 1 file changed, 10 insertions(+), 35 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 8363f2215b1..ffa37ab623a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -62,8 +62,10 @@ constexpr int futility_move_count(bool improving, Depth depth) { return improving ? (3 + depth * depth) : (3 + depth * depth) / 2; } -// Guarantee evaluation does not hit the tablebase range -constexpr Value to_static_eval(const Value v) { +// Add correctionHistory value to raw staticEval and guarantee evaluation does not hit the tablebase range +Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { + auto cv = w.correctionHistory[pos.side_to_move()][pawn_structure_index(pos)]; + v += cv * std::abs(cv) / 16384; return std::clamp(int(v), VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } @@ -747,13 +749,7 @@ Value Search::Worker::search( else if (PvNode) Eval::NNUE::hint_common_parent_position(pos); - Value newEval = - ss->staticEval - + thisThread->correctionHistory[us][pawn_structure_index(pos)] - * std::abs(thisThread->correctionHistory[us][pawn_structure_index(pos)]) - / 16384; - - ss->staticEval = eval = to_static_eval(newEval); + ss->staticEval = eval = to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); // ttValue can be used as a better position evaluation (~7 Elo) if (ttValue != VALUE_NONE && (tte->bound() & (ttValue > eval ? BOUND_LOWER : BOUND_UPPER))) @@ -762,14 +758,7 @@ Value Search::Worker::search( else { unadjustedStaticEval = ss->staticEval = eval = evaluate(pos, thisThread->optimism[us]); - - Value newEval = - ss->staticEval - + thisThread->correctionHistory[us][pawn_structure_index(pos)] - * std::abs(thisThread->correctionHistory[us][pawn_structure_index(pos)]) - / 16384; - - ss->staticEval = eval = to_static_eval(newEval); + ss->staticEval = eval = to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); // Static evaluation is saved as it was before adjustment by correction history tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, Move::none(), @@ -1513,15 +1502,8 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, if ((unadjustedStaticEval = ss->staticEval = bestValue = tte->eval()) == VALUE_NONE) unadjustedStaticEval = ss->staticEval = bestValue = evaluate(pos, thisThread->optimism[us]); - - Value newEval = - ss->staticEval - + thisThread->correctionHistory[us][pawn_structure_index(pos)] - * std::abs( - thisThread->correctionHistory[us][pawn_structure_index(pos)]) - / 16384; - - ss->staticEval = bestValue = to_static_eval(newEval); + ss->staticEval = bestValue = + to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); // ttValue can be used as a better position evaluation (~13 Elo) if (ttValue != VALUE_NONE @@ -1534,15 +1516,8 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, unadjustedStaticEval = ss->staticEval = bestValue = (ss - 1)->currentMove != Move::null() ? evaluate(pos, thisThread->optimism[us]) : -(ss - 1)->staticEval; - - Value newEval = - ss->staticEval - + thisThread->correctionHistory[us][pawn_structure_index(pos)] - * std::abs( - thisThread->correctionHistory[us][pawn_structure_index(pos)]) - / 16384; - - ss->staticEval = bestValue = to_static_eval(newEval); + ss->staticEval = bestValue = + to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); } // Stand pat. Return immediately if static value is at least beta From 9a9702d668af807b9044fef5a83f6ed0854ce44f Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Tue, 16 Jan 2024 13:23:49 +0300 Subject: [PATCH 1336/1766] Remove threatenedByPawn from rook threat Can be simplified away. Passed STC: https://tests.stockfishchess.org/tests/view/65a3fa4179aa8af82b96face LLR: 2.92 (-2.94,2.94) <-1.75,0.25> Total: 30592 W: 7903 L: 7674 D: 15015 Ptnml(0-2): 96, 3590, 7711, 3787, 112 Passed LTC: https://tests.stockfishchess.org/tests/view/65a42b9a79aa8af82b96fe88 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 73656 W: 18382 L: 18212 D: 37062 Ptnml(0-2): 47, 8287, 19981, 8475, 38 closes https://github.com/official-stockfish/Stockfish/pull/4993 Bench: 1430061 --- src/movepick.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index e521689ef73..b2638350de1 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -198,15 +198,13 @@ void MovePicker::score() { : 0; // malus for putting piece en prise - m.value -= - !(threatenedPieces & from) - ? (pt == QUEEN - ? bool(to & threatenedByRook) * 50000 + bool(to & threatenedByMinor) * 10000 - : pt == ROOK - ? bool(to & threatenedByMinor) * 25000 + bool(to & threatenedByPawn) * 10000 - : pt != PAWN ? bool(to & threatenedByPawn) * 15000 - : 0) - : 0; + m.value -= !(threatenedPieces & from) + ? (pt == QUEEN ? bool(to & threatenedByRook) * 50000 + + bool(to & threatenedByMinor) * 10000 + : pt == ROOK ? bool(to & threatenedByMinor) * 25000 + : pt != PAWN ? bool(to & threatenedByPawn) * 15000 + : 0) + : 0; } else // Type == EVASIONS From c8bc2ce4fae2bc9a5dd9b5121274c47d5c9262c0 Mon Sep 17 00:00:00 2001 From: Viren6 <94880762+Viren6@users.noreply.github.com> Date: Wed, 17 Jan 2024 14:45:59 +0000 Subject: [PATCH 1337/1766] Improve ttPv reduction This patch allows a partial reduction decrease when a node is likely to fail low, and increases the reduction decrease when a node has failed high. Passed STC: https://tests.stockfishchess.org/tests/view/65a626e779aa8af82b9722bc LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 157824 W: 40332 L: 39835 D: 77657 Ptnml(0-2): 543, 18617, 40098, 19108, 546 Passed LTC: https://tests.stockfishchess.org/tests/view/65a7290279aa8af82b97328a LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 57228 W: 14475 L: 14111 D: 28642 Ptnml(0-2): 34, 6278, 15633, 6628, 41 closes https://github.com/official-stockfish/Stockfish/pull/4994 Bench: 1364759 --- src/search.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ffa37ab623a..3c630db0b8a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -944,11 +944,6 @@ Value Search::Worker::search( value = bestValue; moveCountPruning = singularQuietLMR = false; - // Indicate PvNodes that will probably fail low if the node was searched - // at a depth equal to or greater than the current depth, and the result - // of this search was a fail low. - bool likelyFailLow = PvNode && ttMove && (tte->bound() & BOUND_UPPER) && tte->depth() >= depth; - // Step 13. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. while ((move = mp.next_move(moveCountPruning)) != Move::none()) @@ -1153,9 +1148,10 @@ Value Search::Worker::search( thisThread->nodes.fetch_add(1, std::memory_order_relaxed); pos.do_move(move, st, givesCheck); - // Decrease reduction if position is or has been on the PV (~4 Elo) - if (ss->ttPv && !likelyFailLow) - r -= 1 + (cutNode && tte->depth() >= depth) + (ttValue > alpha); + // Decrease reduction if position is or has been on the PV (~7 Elo) + if (ss->ttPv) + r -= !(tte->bound() == BOUND_UPPER && PvNode) + (cutNode && tte->depth() >= depth) + + (ttValue > alpha) + (ttValue > beta && tte->depth() >= depth); // Decrease reduction if opponent's move count is high (~1 Elo) if ((ss - 1)->moveCount > 7) From 856e60d12f11cd250cf00cdd336ed87c25bd0677 Mon Sep 17 00:00:00 2001 From: Disservin Date: Wed, 17 Jan 2024 22:58:46 +0100 Subject: [PATCH 1338/1766] Refactor NativeThread start_routine Removes the free function and fixes the formatting for the function call. closes https://github.com/official-stockfish/Stockfish/pull/4995 No functional change --- src/thread_win32_osx.h | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/thread_win32_osx.h b/src/thread_win32_osx.h index 8d424b72a50..1d9a834f600 100644 --- a/src/thread_win32_osx.h +++ b/src/thread_win32_osx.h @@ -34,14 +34,6 @@ namespace Stockfish { -// free function to be passed to pthread_create() -inline void* start_routine(void* ptr) { - auto func = reinterpret_cast*>(ptr); - (*func)(); // Call the function - delete func; - return nullptr; -} - class NativeThread { pthread_t thread; @@ -56,6 +48,15 @@ class NativeThread { pthread_attr_t attr_storage, *attr = &attr_storage; pthread_attr_init(attr); pthread_attr_setstacksize(attr, TH_STACK_SIZE); + + auto start_routine = [](void* ptr) -> void* { + auto f = reinterpret_cast*>(ptr); + // Call the function + (*f)(); + delete f; + return nullptr; + }; + pthread_create(&thread, attr, start_routine, func); } From aa15a9179b8cc5598a6bd04b7c2cfeb81282a0c7 Mon Sep 17 00:00:00 2001 From: Viren6 <94880762+Viren6@users.noreply.github.com> Date: Fri, 19 Jan 2024 04:51:02 +0000 Subject: [PATCH 1339/1766] Refactor ttPv reduction conditions closes https://github.com/official-stockfish/Stockfish/pull/4999 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 3c630db0b8a..57e87fb2484 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1148,25 +1148,24 @@ Value Search::Worker::search( thisThread->nodes.fetch_add(1, std::memory_order_relaxed); pos.do_move(move, st, givesCheck); - // Decrease reduction if position is or has been on the PV (~7 Elo) + // Decrease reduction if position is or has been on the PV (~5 Elo) if (ss->ttPv) - r -= !(tte->bound() == BOUND_UPPER && PvNode) + (cutNode && tte->depth() >= depth) - + (ttValue > alpha) + (ttValue > beta && tte->depth() >= depth); + r -= 1 + (ttValue > alpha) + (ttValue > beta && tte->depth() >= depth); // Decrease reduction if opponent's move count is high (~1 Elo) if ((ss - 1)->moveCount > 7) r--; - // Increase reduction for cut nodes (~3 Elo) + // Increase reduction for cut nodes (~4 Elo) if (cutNode) - r += 2; + r += 2 - (tte->depth() >= depth && ss->ttPv); // Increase reduction if ttMove is a capture (~3 Elo) if (ttCapture) r++; - // Decrease reduction for PvNodes (~2 Elo) - if (PvNode) + // Decrease reduction for PvNodes (~3 Elo) + if (PvNode && tte->bound() != BOUND_UPPER) r--; // Decrease reduction if a quiet ttMove has been singularly extended (~1 Elo) From e860f620aa3eb42f8a6be78c030c75855d0c9ff0 Mon Sep 17 00:00:00 2001 From: rn5f107s2 Date: Fri, 19 Jan 2024 07:32:56 +0100 Subject: [PATCH 1340/1766] Reduce futility_margin further when improving The idea of this is to unroll the futility_margin calculation to allow for the improving flag to have a greater effect on the futility margin. The current factor is 1.5 instead of the previous 1 resulting in a deduction of an extra margin/2 from futilit_margin if improving. The chosen value was not tuned, meaning that there is room for tweaking it. This patch is partially inspired by @Vizvezdenec, who, although quite different in execution, tested another idea where the futility_margin is lowered further when improving [1]. [1]: (first take) https://tests.stockfishchess.org/tests/view/65a56b1879aa8af82b97164b Passed STC: https://tests.stockfishchess.org/tests/live_elo/65a8bfc179aa8af82b974e3c LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 161152 W: 41321 L: 40816 D: 79015 Ptnml(0-2): 559, 19030, 40921, 19479, 587 Passed rebased LTC: https://tests.stockfishchess.org/tests/live_elo/65a8b9ef79aa8af82b974dc0 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 96024 W: 24172 L: 23728 D: 48124 Ptnml(0-2): 56, 10598, 26275, 11012, 71 closes https://github.com/official-stockfish/Stockfish/pull/5000 Bench: 1281703 --- AUTHORS | 2 +- src/search.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index 6f518ec24a8..a179d273d04 100644 --- a/AUTHORS +++ b/AUTHORS @@ -46,6 +46,7 @@ candirufish Chess13234 Chris Cain (ceebo) clefrks +Clemens L. (rn5f107s2) Cody Ho (aesrentai) Dale Weiler (graphitemaster) Daniel Axtens (daxtens) @@ -183,7 +184,6 @@ Raminder Singh renouve Reuven Peleg (R-Peleg) Richard Lloyd (Richard-Lloyd) -rn5f107s2 Robert Nürnberg (robertnurnberg) Rodrigo Exterckötter Tjäder Rodrigo Roim (roim) diff --git a/src/search.cpp b/src/search.cpp index 57e87fb2484..c244207d004 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -55,7 +55,8 @@ namespace { // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving) { - return ((116 - 44 * noTtCutNode) * (d - improving)); + Value futilityMult = 116 - 44 * noTtCutNode; + return (futilityMult * d - 3 * futilityMult / 2 * improving); } constexpr int futility_move_count(bool improving, Depth depth) { From ad9fcbc4961229286e5043d984dc172d1b0de052 Mon Sep 17 00:00:00 2001 From: "Shahin M. Shahin" <41402573+peregrineshahin@users.noreply.github.com> Date: Thu, 18 Jan 2024 07:31:59 +0300 Subject: [PATCH 1341/1766] Refactor get_best_thread Make get_best_thread function easier to understand. Passed non-reg SMP STC: https://tests.stockfishchess.org/tests/view/65a91c6679aa8af82b975500 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 186000 W: 46379 L: 46325 D: 93296 Ptnml(0-2): 269, 21374, 49634, 21480, 243 closes https://github.com/official-stockfish/Stockfish/pull/5001 No functional change --- src/thread.cpp | 59 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/src/thread.cpp b/src/thread.cpp index 4c1d01f46da..3cce7c56295 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -210,49 +210,66 @@ void ThreadPool::start_thinking(const OptionsMap& options, Thread* ThreadPool::get_best_thread() const { - Thread* bestThread = threads.front(); std::unordered_map votes; - Value minScore = VALUE_NONE; + + Thread* bestThread = threads.front(); + Value minScore = VALUE_NONE; // Find the minimum score of all threads for (Thread* th : threads) minScore = std::min(minScore, th->worker->rootMoves[0].score); // Vote according to score and depth, and select the best thread - auto thread_value = [minScore](Thread* th) { + auto thread_voting_value = [minScore](Thread* th) { return (th->worker->rootMoves[0].score - minScore + 14) * int(th->worker->completedDepth); }; for (Thread* th : threads) - votes[th->worker->rootMoves[0].pv[0]] += thread_value(th); + votes[th->worker->rootMoves[0].pv[0]] += thread_voting_value(th); for (Thread* th : threads) - if (bestThread->worker->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY) + { + const auto bestThreadScore = bestThread->worker->rootMoves[0].score; + const auto newThreadScore = th->worker->rootMoves[0].score; + + const auto bestThreadPV = bestThread->worker->rootMoves[0].pv; + const auto newThreadPV = th->worker->rootMoves[0].pv; + + const auto bestThreadMoveVote = votes[bestThreadPV[0]]; + const auto newThreadMoveVote = votes[newThreadPV[0]]; + + + const bool bestThreadInProvenWin = bestThreadScore >= VALUE_TB_WIN_IN_MAX_PLY; + const bool newThreadInProvenWin = newThreadScore >= VALUE_TB_WIN_IN_MAX_PLY; + + const bool bestThreadInProvenLoss = + bestThreadScore != -VALUE_INFINITE && bestThreadScore <= VALUE_TB_LOSS_IN_MAX_PLY; + const bool newThreadInProvenLoss = + newThreadScore != -VALUE_INFINITE && newThreadScore <= VALUE_TB_LOSS_IN_MAX_PLY; + + // Note that we make sure not to pick a thread with truncated-PV for better viewer experience. + const bool betterVotingValue = + thread_voting_value(th) * int(newThreadPV.size() > 2) + > thread_voting_value(bestThread) * int(bestThreadPV.size() > 2); + + if (bestThreadInProvenWin) { // Make sure we pick the shortest mate / TB conversion - if (th->worker->rootMoves[0].score > bestThread->worker->rootMoves[0].score) + if (newThreadScore > bestThreadScore) bestThread = th; } - else if (bestThread->worker->rootMoves[0].score != -VALUE_INFINITE - && bestThread->worker->rootMoves[0].score <= VALUE_TB_LOSS_IN_MAX_PLY) + else if (bestThreadInProvenLoss) { // Make sure we pick the shortest mated / TB conversion - if (th->worker->rootMoves[0].score != -VALUE_INFINITE - && th->worker->rootMoves[0].score < bestThread->worker->rootMoves[0].score) + if (newThreadInProvenLoss && newThreadScore < bestThreadScore) bestThread = th; } - else if (th->worker->rootMoves[0].score >= VALUE_TB_WIN_IN_MAX_PLY - || (th->worker->rootMoves[0].score != -VALUE_INFINITE - && th->worker->rootMoves[0].score <= VALUE_TB_LOSS_IN_MAX_PLY) - || (th->worker->rootMoves[0].score > VALUE_TB_LOSS_IN_MAX_PLY - && (votes[th->worker->rootMoves[0].pv[0]] - > votes[bestThread->worker->rootMoves[0].pv[0]] - || (votes[th->worker->rootMoves[0].pv[0]] - == votes[bestThread->worker->rootMoves[0].pv[0]] - && thread_value(th) * int(th->worker->rootMoves[0].pv.size() > 2) - > thread_value(bestThread) - * int(bestThread->worker->rootMoves[0].pv.size() > 2))))) + else if (newThreadInProvenWin || newThreadInProvenLoss + || (newThreadScore > VALUE_TB_LOSS_IN_MAX_PLY + && (newThreadMoveVote > bestThreadMoveVote + || (newThreadMoveVote == bestThreadMoveVote && betterVotingValue)))) bestThread = th; + } return bestThread; } From a901474bf9579ba259179eb09618d8401a156f64 Mon Sep 17 00:00:00 2001 From: "Robert Nurnberg @ elitebook" Date: Sat, 20 Jan 2024 19:15:20 +0100 Subject: [PATCH 1342/1766] Update the WDL model Update the internal WDL model. After the dual net merge, the internal evaluations have drifted upwards a bit. With this PR `NormalizeToPawnValue` changes from `328` to `345`. The new model was fitted based on about 200M positions extracted from 3.4M fishtest LTC games from the last two weeks, involving SF versions from 6deb88728fb141e853243c2873ad0cda4dd19320 to current master. Apart from the WDL model parameter update, this PR implements the following changes: WDL Model: - an incorrect 8-move shift in master's WDL model has been fixed - the polynomials `p_a` and `p_b` are fitted over the move range [8, 120] - the coefficients for `p_a` and `p_b` are optimized by maximizing the probability of predicting the observed outcome (credits to @vondele) SF code: - for wdl values, move will be clamped to `max(8, min(120, move))` - no longer clamp the internal eval to [-4000,4000] - compute `NormalizeToPawnValue` with `round`, not `trunc` The PR only affects displayed `cp` and `wdl` values. closes https://github.com/official-stockfish/Stockfish/pull/5002 No functional change --- src/uci.cpp | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/uci.cpp b/src/uci.cpp index 789f345481d..2a55fbfab0a 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -43,7 +43,7 @@ namespace Stockfish { constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; -constexpr int NormalizeToPawnValue = 328; +constexpr int NormalizeToPawnValue = 345; constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048; UCI::UCI(int argc, char** argv) : @@ -421,26 +421,23 @@ namespace { // eval and a game ply. It fits the LTC fishtest statistics rather accurately. int win_rate_model(Value v, int ply) { - // The model only captures up to 240 plies, so limit the input and then rescale - double m = std::min(240, ply) / 64.0; + // The fitted model only uses data for moves in [8, 120], and is anchored at move 32. + double m = std::clamp(ply / 2 + 1, 8, 120) / 32.0; // The coefficients of a third-order polynomial fit is based on the fishtest data // for two parameters that need to transform eval to the argument of a logistic // function. - constexpr double as[] = {0.38036525, -2.82015070, 23.17882135, 307.36768407}; - constexpr double bs[] = {-2.29434733, 13.27689788, -14.26828904, 63.45318330}; + constexpr double as[] = {-2.00568292, 10.45906746, 1.67438883, 334.45864705}; + constexpr double bs[] = {-4.97134419, 36.15096345, -82.25513499, 117.35186805}; - // Enforce that NormalizeToPawnValue corresponds to a 50% win rate at ply 64 - static_assert(NormalizeToPawnValue == int(as[0] + as[1] + as[2] + as[3])); + // Enforce that NormalizeToPawnValue corresponds to a 50% win rate at move 32. + static_assert(NormalizeToPawnValue == int(0.5 + as[0] + as[1] + as[2] + as[3])); double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3]; double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3]; - // Transform the eval to centipawns with limited range - double x = std::clamp(double(v), -4000.0, 4000.0); - - // Return the win rate in per mille units, rounded to the nearest integer - return int(0.5 + 1000 / (1 + std::exp((a - x) / b))); + // Return the win rate in per mille units, rounded to the nearest integer. + return int(0.5 + 1000 / (1 + std::exp((a - double(v)) / b))); } } From a6fd17f27d7675332166e9e6ea8210237281fc77 Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Wed, 17 Jan 2024 18:10:38 +0800 Subject: [PATCH 1343/1766] VLTC search tune MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Search parameters were tuned using 152k games at 180+1.8. Passed VLTC: https://tests.stockfishchess.org/tests/view/65a7a81979aa8af82b973a20 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 117338 W: 29244 L: 28848 D: 59246 Ptnml(0-2): 24, 12474, 33267, 12890, 14 Passed VVLTC: https://tests.stockfishchess.org/tests/view/65ab246679aa8af82b977982 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 28164 W: 7239 L: 6957 D: 13968 Ptnml(0-2): 3, 2651, 8490, 2937, 1 STC Elo estimate: https://tests.stockfishchess.org/tests/view/65ac7c0979aa8af82b9792a6 Elo: -0.53 ± 2.0 (95%) LOS: 30.4% Total: 30000 W: 7688 L: 7734 D: 14578 Ptnml(0-2): 102, 3617, 7614, 3559, 108 nElo: -1.03 ± 3.9 (95%) PairsRatio: 0.99 closes https://github.com/official-stockfish/Stockfish/pull/5003 Bench: 1235377 --- src/search.cpp | 74 +++++++++++++++++++++++++------------------------- src/search.h | 4 +-- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c244207d004..a22df12c2c6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -55,7 +55,7 @@ namespace { // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving) { - Value futilityMult = 116 - 44 * noTtCutNode; + Value futilityMult = 114 - 47 * noTtCutNode; return (futilityMult * d - 3 * futilityMult / 2 * improving); } @@ -66,15 +66,15 @@ constexpr int futility_move_count(bool improving, Depth depth) { // Add correctionHistory value to raw staticEval and guarantee evaluation does not hit the tablebase range Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { auto cv = w.correctionHistory[pos.side_to_move()][pawn_structure_index(pos)]; - v += cv * std::abs(cv) / 16384; + v += cv * std::abs(cv) / 14095; return std::clamp(int(v), VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } // History and stats update bonus, based on depth -int stat_bonus(Depth d) { return std::min(268 * d - 352, 1153); } +int stat_bonus(Depth d) { return std::min(265 * d - 349, 1112); } // History and stats update malus, based on depth -int stat_malus(Depth d) { return std::min(400 * d - 354, 1201); } +int stat_malus(Depth d) { return std::min(482 * d - 326, 1172); } // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(size_t nodes) { return VALUE_DRAW - 1 + Value(nodes & 0x2); } @@ -334,12 +334,12 @@ void Search::Worker::iterative_deepening() { // Reset aspiration window starting size Value avg = rootMoves[pvIdx].averageScore; - delta = Value(9) + int(avg) * avg / 14847; + delta = Value(9) + int(avg) * avg / 13181; alpha = std::max(avg - delta, -VALUE_INFINITE); beta = std::min(avg + delta, int(VALUE_INFINITE)); // Adjust optimism based on root move's averageScore (~4 Elo) - optimism[us] = 121 * avg / (std::abs(avg) + 109); + optimism[us] = 132 * avg / (std::abs(avg) + 98); optimism[~us] = -optimism[us]; // Start with a small aspiration window and, in the case of a fail @@ -769,7 +769,7 @@ Value Search::Worker::search( // Use static evaluation difference to improve quiet move ordering (~9 Elo) if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture) { - int bonus = std::clamp(-13 * int((ss - 1)->staticEval + ss->staticEval), -1652, 1546); + int bonus = std::clamp(-13 * int((ss - 1)->staticEval + ss->staticEval), -1680, 1406); bonus = bonus > 0 ? 2 * bonus : bonus / 2; thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) @@ -790,7 +790,7 @@ Value Search::Worker::search( // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. // Adjust razor margin according to cutoffCnt. (~1 Elo) - if (eval < alpha - 472 - (284 - 165 * ((ss + 1)->cutoffCnt > 3)) * depth * depth) + if (eval < alpha - 435 - (327 - 167 * ((ss + 1)->cutoffCnt > 3)) * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -799,17 +799,17 @@ Value Search::Worker::search( // Step 8. Futility pruning: child node (~40 Elo) // The depth condition is important for mate finding. - if (!ss->ttPv && depth < 9 + if (!ss->ttPv && depth < 11 && eval - futility_margin(depth, cutNode && !ss->ttHit, improving) - - (ss - 1)->statScore / 337 + - (ss - 1)->statScore / 327 >= beta - && eval >= beta && eval < 29008 // smaller than TB wins + && eval >= beta && eval < 27734 // smaller than TB wins && (!ttMove || ttCapture)) return beta > VALUE_TB_LOSS_IN_MAX_PLY ? (eval + beta) / 2 : eval; // Step 9. Null move search with verification search (~35 Elo) - if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 17496 - && eval >= beta && eval >= ss->staticEval && ss->staticEval >= beta - 23 * depth + 304 + if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 17787 + && eval >= beta && eval >= ss->staticEval && ss->staticEval >= beta - 22 * depth + 313 && !excludedMove && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly && beta > VALUE_TB_LOSS_IN_MAX_PLY) { @@ -863,7 +863,7 @@ Value Search::Worker::search( if (cutNode && depth >= 8 && !ttMove) depth -= 2; - probCutBeta = beta + 163 - 67 * improving; + probCutBeta = beta + 173 - 73 * improving; // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search returns a value @@ -923,7 +923,7 @@ Value Search::Worker::search( moves_loop: // When in check, search starts here // Step 12. A small Probcut idea, when we are in check (~4 Elo) - probCutBeta = beta + 425; + probCutBeta = beta + 427; if (ss->inCheck && !PvNode && ttCapture && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 4 && ttValue >= probCutBeta && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) @@ -1006,7 +1006,7 @@ Value Search::Worker::search( { Piece capturedPiece = pos.piece_on(move.to_sq()); int futilityEval = - ss->staticEval + 238 + 305 * lmrDepth + PieceValue[capturedPiece] + ss->staticEval + 277 + 298 * lmrDepth + PieceValue[capturedPiece] + thisThread->captureHistory[movedPiece][move.to_sq()][type_of(capturedPiece)] / 7; if (futilityEval < alpha) @@ -1014,7 +1014,7 @@ Value Search::Worker::search( } // SEE based pruning for captures and checks (~11 Elo) - if (!pos.see_ge(move, -187 * depth)) + if (!pos.see_ge(move, -203 * depth)) continue; } else @@ -1026,18 +1026,18 @@ Value Search::Worker::search( + thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()]; // Continuation history based pruning (~2 Elo) - if (lmrDepth < 6 && history < -3752 * depth) + if (lmrDepth < 6 && history < -4195 * depth) continue; - history += 2 * thisThread->mainHistory[us][move.from_to()]; + history += 69 * thisThread->mainHistory[us][move.from_to()] / 32; - lmrDepth += history / 7838; + lmrDepth += history / 6992; lmrDepth = std::max(lmrDepth, -1); // Futility pruning: parent node (~13 Elo) - if (!ss->inCheck && lmrDepth < 14 - && ss->staticEval + (bestValue < ss->staticEval - 57 ? 124 : 71) - + 118 * lmrDepth + if (!ss->inCheck && lmrDepth < 15 + && ss->staticEval + (bestValue < ss->staticEval - 63 ? 137 : 64) + + 111 * lmrDepth <= alpha) continue; @@ -1064,11 +1064,11 @@ Value Search::Worker::search( // so changing them requires tests at these types of time controls. // Recursive singular search is avoided. if (!rootNode && move == ttMove && !excludedMove - && depth >= 4 - (thisThread->completedDepth > 27) + 2 * (PvNode && tte->is_pv()) + && depth >= 4 - (thisThread->completedDepth > 31) + 2 * (PvNode && tte->is_pv()) && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - (66 + 58 * (ss->ttPv && !PvNode)) * depth / 64; + Value singularBeta = ttValue - (58 + 52 * (ss->ttPv && !PvNode)) * depth / 64; Depth singularDepth = newDepth / 2; ss->excludedMove = move; @@ -1082,7 +1082,7 @@ Value Search::Worker::search( singularQuietLMR = !ttCapture; // Avoid search explosion by limiting the number of double extensions - if (!PvNode && value < singularBeta - 17 && ss->doubleExtensions <= 11) + if (!PvNode && value < singularBeta - 16 && ss->doubleExtensions <= 12) { extension = 2; depth += depth < 15; @@ -1109,7 +1109,7 @@ Value Search::Worker::search( // If we are on a cutNode but the ttMove is not assumed to fail high over current beta (~1 Elo) else if (cutNode) - extension = depth < 19 ? -2 : -1; + extension = depth < 20 ? -2 : -1; // If the ttMove is assumed to fail low over the value of the reduced search (~1 Elo) else if (ttValue <= value) @@ -1122,14 +1122,14 @@ Value Search::Worker::search( // Quiet ttMove extensions (~1 Elo) else if (PvNode && move == ttMove && move == ss->killers[0] - && (*contHist[0])[movedPiece][move.to_sq()] >= 4325) + && (*contHist[0])[movedPiece][move.to_sq()] >= 4111) extension = 1; // Recapture extensions (~1 Elo) else if (PvNode && move == ttMove && move.to_sq() == prevSq && thisThread->captureHistory[movedPiece][move.to_sq()] [type_of(pos.piece_on(move.to_sq()))] - > 4146) + > 4484) extension = 1; } @@ -1189,10 +1189,10 @@ Value Search::Worker::search( ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + (*contHist[0])[movedPiece][move.to_sq()] + (*contHist[1])[movedPiece][move.to_sq()] - + (*contHist[3])[movedPiece][move.to_sq()] - 3817; + + (*contHist[3])[movedPiece][move.to_sq()] - 4119; // Decrease/increase reduction for moves with a good/bad history (~25 Elo) - r -= ss->statScore / 14767; + r -= ss->statScore / 15373; // Step 17. Late moves reduction / extension (LMR, ~117 Elo) // We use various heuristics for the sons of a node after the first son has @@ -1215,7 +1215,7 @@ Value Search::Worker::search( { // Adjust full-depth search based on LMR results - if the result // was good enough search deeper, if it was bad enough search shallower. - const bool doDeeperSearch = value > (bestValue + 53 + 2 * newDepth); // (~1 Elo) + const bool doDeeperSearch = value > (bestValue + 51 + 2 * newDepth); // (~1 Elo) const bool doShallowerSearch = value < bestValue + newDepth; // (~2 Elo) newDepth += doDeeperSearch - doShallowerSearch; @@ -1331,7 +1331,7 @@ Value Search::Worker::search( else { // Reduce other moves if we have found at least one score improvement (~2 Elo) - if (depth > 2 && depth < 12 && beta < 13782 && value > -11541) + if (depth > 2 && depth < 12 && beta < 13195 && value > -12346) depth -= 2; assert(depth > 0); @@ -1370,7 +1370,7 @@ Value Search::Worker::search( // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (depth > 6) + (PvNode || cutNode) + ((ss - 1)->statScore < -18782) + int bonus = (depth > 5) + (PvNode || cutNode) + ((ss - 1)->statScore < -16797) + ((ss - 1)->moveCount > 10); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); @@ -1529,7 +1529,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, if (bestValue > alpha) alpha = bestValue; - futilityBase = ss->staticEval + 182; + futilityBase = ss->staticEval + 186; } const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, @@ -1609,7 +1609,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, continue; // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, -77)) + if (!pos.see_ge(move, -76)) continue; } @@ -1764,7 +1764,7 @@ void update_all_stats(const Position& pos, if (!pos.capture_stage(bestMove)) { - int bestMoveBonus = bestValue > beta + 173 ? quietMoveBonus // larger bonus + int bestMoveBonus = bestValue > beta + 177 ? quietMoveBonus // larger bonus : stat_bonus(depth); // smaller bonus // Increase stats for the best move in case it was a quiet move diff --git a/src/search.h b/src/search.h index 4bd013ad670..3a099c5dddf 100644 --- a/src/search.h +++ b/src/search.h @@ -205,8 +205,8 @@ class Worker { Depth reduction(bool i, Depth d, int mn, int delta) { int reductionScale = reductions[d] * reductions[mn]; - return (reductionScale + 1346 - int(delta) * 896 / int(rootDelta)) / 1024 - + (!i && reductionScale > 880); + return (reductionScale + 1177 - int(delta) * 776 / int(rootDelta)) / 1024 + + (!i && reductionScale > 842); } // Get a pointer to the search manager, only allowed to be called by the From 2b62c4452db5a02c5ed7d4a2b4c4cb1529fb82b7 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Tue, 23 Jan 2024 13:39:06 +0300 Subject: [PATCH 1344/1766] Remove redundant max operation on lmrDepth Removed a restriction that prohibited history heuristics sum in futility pruning to exceed some negative value. Passed STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 279040 W: 71095 L: 71143 D: 136802 Ptnml(0-2): 949, 33574, 70474, 33622, 901 https://tests.stockfishchess.org/tests/view/65aaef4c79aa8af82b977631 Passed LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 75156 W: 18884 L: 18715 D: 37557 Ptnml(0-2): 52, 8445, 20408, 8628, 45 https://tests.stockfishchess.org/tests/view/65ae7ef3c865510db026abf5 closes https://github.com/official-stockfish/Stockfish/pull/5004 Bench: 1566543 --- src/search.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index a22df12c2c6..f5395f98376 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1032,7 +1032,6 @@ Value Search::Worker::search( history += 69 * thisThread->mainHistory[us][move.from_to()] / 32; lmrDepth += history / 6992; - lmrDepth = std::max(lmrDepth, -1); // Futility pruning: parent node (~13 Elo) if (!ss->inCheck && lmrDepth < 15 From 3d49a99aaf75f6f44ef6ec5a22b0acd191b8d01e Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Wed, 24 Jan 2024 14:38:59 +0300 Subject: [PATCH 1345/1766] Refactor history score calculation Passed STC: https://tests.stockfishchess.org/tests/view/65ad08b179aa8af82b979dd1 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 161376 W: 41582 L: 41498 D: 78296 Ptnml(0-2): 633, 19354, 40611, 19476, 614 Passed LTC: https://tests.stockfishchess.org/tests/view/65af966fc865510db026c0f0 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 116526 W: 29269 L: 29143 D: 58114 Ptnml(0-2): 71, 13252, 31509, 13342, 89 closes https://github.com/official-stockfish/Stockfish/pull/5006 Bench: 1317504 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index f5395f98376..f4b372536c9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1029,7 +1029,7 @@ Value Search::Worker::search( if (lmrDepth < 6 && history < -4195 * depth) continue; - history += 69 * thisThread->mainHistory[us][move.from_to()] / 32; + history += 2 * thisThread->mainHistory[us][move.from_to()]; lmrDepth += history / 6992; From 1dfbde2d1056b49442aab9bf5145ad30d0e87dd1 Mon Sep 17 00:00:00 2001 From: Disservin Date: Sun, 14 Jan 2024 00:21:46 +0100 Subject: [PATCH 1346/1766] Move perft out of search This splits the logic of search and perft. Before, threads were started, which then constructed a search object, which then started perft and returned immediately. All of this is unnecessary, instead uci should start perft right away. closes https://github.com/official-stockfish/Stockfish/pull/5008 No functional change --- src/Makefile | 2 +- src/perft.h | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/search.cpp | 36 -------------------------- src/uci.cpp | 8 +++++- 4 files changed, 77 insertions(+), 38 deletions(-) create mode 100644 src/perft.h diff --git a/src/Makefile b/src/Makefile index 9680ca7feff..907b6155028 100644 --- a/src/Makefile +++ b/src/Makefile @@ -63,7 +63,7 @@ HEADERS = benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h \ nnue/layers/sqr_clipped_relu.h nnue/nnue_accumulator.h nnue/nnue_architecture.h \ nnue/nnue_common.h nnue/nnue_feature_transformer.h position.h \ search.h syzygy/tbprobe.h thread.h thread_win32_osx.h timeman.h \ - tt.h tune.h types.h uci.h ucioption.h + tt.h tune.h types.h uci.h ucioption.h perft.h OBJS = $(notdir $(SRCS:.cpp=.o)) diff --git a/src/perft.h b/src/perft.h new file mode 100644 index 00000000000..2edc3ad0a6e --- /dev/null +++ b/src/perft.h @@ -0,0 +1,69 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef PERFT_H_INCLUDED +#define PERFT_H_INCLUDED + +#include + +#include "movegen.h" +#include "position.h" +#include "types.h" +#include "uci.h" + +namespace Stockfish { + +// Utility to verify move generation. All the leaf nodes up +// to the given depth are generated and counted, and the sum is returned. +template +uint64_t perft(Position& pos, Depth depth) { + + StateInfo st; + ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize); + + uint64_t cnt, nodes = 0; + const bool leaf = (depth == 2); + + for (const auto& m : MoveList(pos)) + { + if (Root && depth <= 1) + cnt = 1, nodes++; + else + { + pos.do_move(m, st); + cnt = leaf ? MoveList(pos).size() : perft(pos, depth - 1); + nodes += cnt; + pos.undo_move(m); + } + if (Root) + sync_cout << UCI::move(m, pos.is_chess960()) << ": " << cnt << sync_endl; + } + return nodes; +} + +inline void perft(const std::string& fen, Depth depth, bool isChess960) { + StateListPtr states(new std::deque(1)); + Position p; + p.set(fen, isChess960, &states->back()); + + uint64_t nodes = perft(p, depth); + sync_cout << "\nNodes searched: " << nodes << "\n" << sync_endl; +} +} + +#endif // PERFT_H_INCLUDED diff --git a/src/search.cpp b/src/search.cpp index f4b372536c9..086bff34029 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -122,37 +122,8 @@ void update_all_stats(const Position& pos, int captureCount, Depth depth); -// Utility to verify move generation. All the leaf nodes up -// to the given depth are generated and counted, and the sum is returned. -template -uint64_t perft(Position& pos, Depth depth) { - - StateInfo st; - ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize); - - uint64_t cnt, nodes = 0; - const bool leaf = (depth == 2); - - for (const auto& m : MoveList(pos)) - { - if (Root && depth <= 1) - cnt = 1, nodes++; - else - { - pos.do_move(m, st); - cnt = leaf ? MoveList(pos).size() : perft(pos, depth - 1); - nodes += cnt; - pos.undo_move(m); - } - if (Root) - sync_cout << UCI::move(m, pos.is_chess960()) << ": " << cnt << sync_endl; - } - return nodes; -} - } // namespace - Search::Worker::Worker(SharedState& sharedState, std::unique_ptr sm, size_t thread_id) : @@ -173,13 +144,6 @@ void Search::Worker::start_searching() { return; } - if (limits.perft) - { - nodes = perft(rootPos, limits.perft); - sync_cout << "\nNodes searched: " << nodes << "\n" << sync_endl; - return; - } - main_manager()->tm.init(limits, rootPos.side_to_move(), rootPos.game_ply(), options); tt.new_search(); diff --git a/src/uci.cpp b/src/uci.cpp index 2a55fbfab0a..e6107d4713a 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -39,6 +39,7 @@ #include "syzygy/tbprobe.h" #include "types.h" #include "ucioption.h" +#include "perft.h" namespace Stockfish { @@ -172,7 +173,6 @@ void UCI::loop() { void UCI::go(Position& pos, std::istringstream& is, StateListPtr& states) { - Search::LimitsType limits; std::string token; bool ponderMode = false; @@ -211,6 +211,12 @@ void UCI::go(Position& pos, std::istringstream& is, StateListPtr& states) { Eval::NNUE::verify(options, evalFiles); + if (limits.perft) + { + perft(pos.fen(), limits.perft, options["UCI_Chess960"]); + return; + } + threads.start_thinking(options, pos, states, limits, ponderMode); } From 37bd1e774ee4eb03e558062284da1e72cbce5a95 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Thu, 25 Jan 2024 23:22:07 +0300 Subject: [PATCH 1347/1766] Do more double extensions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parameter tweak from Black Marlin chess engine. Choose a significantly lower value that triggers in 95% of cases, compared to the usual 84% in standard benchmark runs. Since the introduction by https://github.com/official-stockfish/Stockfish/commit/33a858eaa1f792b3413384a3d0993dba36aca92e this constant has only decreased in value over time. 2-16-17-18-21-22-25-26-52-71-75-93-140 Failed STC really fast: https://tests.stockfishchess.org/tests/view/65b11d05c865510db026df7b LLR: -2.94 (-2.94,2.94) <0.00,2.00> Total: 13216 W: 3242 L: 3485 D: 6489 Ptnml(0-2): 50, 1682, 3371, 1471, 34 Was reasonable at LTC: https://tests.stockfishchess.org/tests/view/65b13e20c865510db026e210 Elo: 1.18 ± 1.5 (95%) LOS: 94.3% Total: 50000 W: 12517 L: 12347 D: 25136 Ptnml(0-2): 31, 5598, 13579, 5754, 38 nElo: 2.45 ± 3.0 (95%) PairsRatio: 1.03 Passed VLTC with STC bounds: https://tests.stockfishchess.org/tests/view/65b18870c865510db026e769 LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 30456 W: 7726 L: 7448 D: 15282 Ptnml(0-2): 6, 3111, 8717, 3387, 7 Passed VVLTC with LTC bounds: https://tests.stockfishchess.org/tests/view/65b20b95c865510db026eef0 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 36134 W: 9158 L: 8859 D: 18117 Ptnml(0-2): 3, 3455, 10850, 3758, 1 closes https://github.com/official-stockfish/Stockfish/pull/5013 Bench: 1503692 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 086bff34029..9b74141bcfa 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1045,7 +1045,7 @@ Value Search::Worker::search( singularQuietLMR = !ttCapture; // Avoid search explosion by limiting the number of double extensions - if (!PvNode && value < singularBeta - 16 && ss->doubleExtensions <= 12) + if (!PvNode && value < singularBeta - 2 && ss->doubleExtensions <= 12) { extension = 2; depth += depth < 15; From c17ec9524d57c2fdaba1fd7f16ce30744780b6aa Mon Sep 17 00:00:00 2001 From: Ahmed Kerimov Date: Fri, 26 Jan 2024 13:52:56 +0300 Subject: [PATCH 1348/1766] Move OnChange callback in Option ctors Parameter 'f' is passed by value and only copied once. Moving it to avoid unnecessary copies. closes https://github.com/official-stockfish/Stockfish/pull/5014 No functional change --- AUTHORS | 1 + src/ucioption.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/AUTHORS b/AUTHORS index a179d273d04..cc8edafa4aa 100644 --- a/AUTHORS +++ b/AUTHORS @@ -12,6 +12,7 @@ Hisayori Noda (nodchip) # All other authors of Stockfish code (in alphabetical order) Aditya (absimaldata) Adrian Petrescu (apetresc) +Ahmed Kerimov (wcdbmv) Ajith Chandy Jose (ajithcj) Alain Savard (Rocky640) Alayan Feh (Alayan-stk-2) diff --git a/src/ucioption.cpp b/src/ucioption.cpp index c7de7e3f7f2..e1ffe546525 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -68,7 +68,7 @@ Option::Option(const char* v, OnChange f) : type("string"), min(0), max(0), - on_change(f) { + on_change(std::move(f)) { defaultValue = currentValue = v; } @@ -76,7 +76,7 @@ Option::Option(bool v, OnChange f) : type("check"), min(0), max(0), - on_change(f) { + on_change(std::move(f)) { defaultValue = currentValue = (v ? "true" : "false"); } @@ -84,13 +84,13 @@ Option::Option(OnChange f) : type("button"), min(0), max(0), - on_change(f) {} + on_change(std::move(f)) {} Option::Option(double v, int minv, int maxv, OnChange f) : type("spin"), min(minv), max(maxv), - on_change(f) { + on_change(std::move(f)) { defaultValue = currentValue = std::to_string(v); } @@ -98,7 +98,7 @@ Option::Option(const char* v, const char* cur, OnChange f) : type("combo"), min(0), max(0), - on_change(f) { + on_change(std::move(f)) { defaultValue = v; currentValue = cur; } From fcbb02ffdeebb65c970ecf5aebaa3078cdf8f374 Mon Sep 17 00:00:00 2001 From: Viren6 <94880762+Viren6@users.noreply.github.com> Date: Fri, 26 Jan 2024 11:27:49 +0000 Subject: [PATCH 1349/1766] Use ttPv in depth condition of singular extensions This replaces the PvNode condition and tte Pv call previously with using the precomputed ttPv, and also removes the multiplier of 2. This new depth condition occurs with approximately equal frequency (47%) to the old depth condition (measured when the other conditions in the if are true), so non-linear scaling behaviour isn't expected. Passed Non-Reg STC: https://tests.stockfishchess.org/tests/view/65b0e132c865510db026da27 LLR: 2.97 (-2.94,2.94) <-1.75,0.25> Total: 243232 W: 62432 L: 62437 D: 118363 Ptnml(0-2): 910, 28937, 61900, 28986, 883 Passed Non-Reg LTC: https://tests.stockfishchess.org/tests/view/65b2053bc865510db026eea1 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 190596 W: 47666 L: 47618 D: 95312 Ptnml(0-2): 115, 21710, 51596, 21766, 111 closes https://github.com/official-stockfish/Stockfish/pull/5015 Bench: 1492957 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 9b74141bcfa..6a464961a61 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1027,7 +1027,7 @@ Value Search::Worker::search( // so changing them requires tests at these types of time controls. // Recursive singular search is avoided. if (!rootNode && move == ttMove && !excludedMove - && depth >= 4 - (thisThread->completedDepth > 31) + 2 * (PvNode && tte->is_pv()) + && depth >= 4 - (thisThread->completedDepth > 31) + ss->ttPv && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { From 13eb023fc09343c80c45f51df83a1b9f6401bd35 Mon Sep 17 00:00:00 2001 From: Disservin Date: Sat, 27 Jan 2024 10:54:44 +0100 Subject: [PATCH 1350/1766] Simplify array initializations also retire a few std::memset calls. Passed non-regresion STC: https://tests.stockfishchess.org/tests/view/65b8e162c865510db0276901 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 97504 W: 25294 L: 25140 D: 47070 Ptnml(1-2): 378, 11102, 25667, 11198, 407 closes https://github.com/official-stockfish/Stockfish/pull/5018 No functional change --- src/position.cpp | 10 +++++----- src/search.cpp | 40 ++++++++++++++++++++-------------------- src/search.h | 13 +++++++------ 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index 6202381d072..c89b1eb0889 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -19,6 +19,7 @@ #include "position.h" #include +#include #include #include #include @@ -107,9 +108,8 @@ inline int H1(Key h) { return h & 0x1fff; } inline int H2(Key h) { return (h >> 16) & 0x1fff; } // Cuckoo tables with Zobrist hashes of valid reversible moves, and the moves themselves -Key cuckoo[8192]; -Move cuckooMove[8192]; - +std::array cuckoo; +std::array cuckooMove; // Initializes at startup the various arrays used to compute hash keys void Position::init() { @@ -130,8 +130,8 @@ void Position::init() { Zobrist::noPawns = rng.rand(); // Prepare the cuckoo tables - std::memset(cuckoo, 0, sizeof(cuckoo)); - std::memset(cuckooMove, 0, sizeof(cuckooMove)); + cuckoo.fill(0); + cuckooMove.fill(Move::none()); [[maybe_unused]] int count = 0; for (Piece pc : Pieces) for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) diff --git a/src/search.cpp b/src/search.cpp index 6a464961a61..29b5c5243b3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -212,21 +211,26 @@ void Search::Worker::start_searching() { // consumed, the user stops the search, or the maximum search depth is reached. void Search::Worker::iterative_deepening() { + SearchManager* mainThread = (thread_idx == 0 ? main_manager() : nullptr); + + Move pv[MAX_PLY + 1]; + + Depth lastBestMoveDepth = 0; + Value lastBestScore = -VALUE_INFINITE; + auto lastBestPV = std::vector{Move::none()}; + + Value alpha, beta; + Value bestValue = -VALUE_INFINITE; + Color us = rootPos.side_to_move(); + double timeReduction = 1, totBestMoveChanges = 0; + int delta, iterIdx = 0; + // Allocate stack with extra size to allow access from (ss - 7) to (ss + 2): // (ss - 7) is needed for update_continuation_histories(ss - 1) which accesses (ss - 6), // (ss + 2) is needed for initialization of cutOffCnt and killers. - Stack stack[MAX_PLY + 10], *ss = stack + 7; - Move pv[MAX_PLY + 1]; - Value alpha, beta; - Value lastBestScore = -VALUE_INFINITE; - std::vector lastBestPV = {Move::none()}; - Depth lastBestMoveDepth = 0; - SearchManager* mainThread = (thread_idx == 0 ? main_manager() : nullptr); - double timeReduction = 1, totBestMoveChanges = 0; - Color us = rootPos.side_to_move(); - int delta, iterIdx = 0; - - std::memset(ss - 7, 0, 10 * sizeof(Stack)); + Stack stack[MAX_PLY + 10] = {}; + Stack* ss = stack + 7; + for (int i = 7; i > 0; --i) { (ss - i)->continuationHistory = @@ -239,16 +243,12 @@ void Search::Worker::iterative_deepening() { ss->pv = pv; - Value bestValue = -VALUE_INFINITE; - if (mainThread) { if (mainThread->bestPreviousScore == VALUE_INFINITE) - for (int i = 0; i < 4; ++i) - mainThread->iterValue[i] = VALUE_ZERO; + mainThread->iterValue.fill(VALUE_ZERO); else - for (int i = 0; i < 4; ++i) - mainThread->iterValue[i] = mainThread->bestPreviousScore; + mainThread->iterValue.fill(mainThread->bestPreviousScore); } size_t multiPV = size_t(options["MultiPV"]); @@ -489,7 +489,7 @@ void Search::Worker::clear() { h->fill(-71); - for (int i = 1; i < MAX_MOVES; ++i) + for (size_t i = 1; i < reductions.size(); ++i) reductions[i] = int((20.37 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); } diff --git a/src/search.h b/src/search.h index 3a099c5dddf..b4a65d8e6f6 100644 --- a/src/search.h +++ b/src/search.h @@ -19,6 +19,7 @@ #ifndef SEARCH_H_INCLUDED #define SEARCH_H_INCLUDED +#include #include #include #include @@ -153,11 +154,11 @@ class SearchManager: public ISearchManager { int callsCnt; std::atomic_bool ponder; - double previousTimeReduction; - Value bestPreviousScore; - Value bestPreviousAverageScore; - Value iterValue[4]; - bool stopOnPonderhit; + std::array iterValue; + double previousTimeReduction; + Value bestPreviousScore; + Value bestPreviousAverageScore; + bool stopOnPonderhit; size_t id; }; @@ -233,7 +234,7 @@ class Worker { size_t thread_idx; // Reductions lookup table initialized at startup - int reductions[MAX_MOVES]; // [depth or moveNumber] + std::array reductions; // [depth or moveNumber] // The main thread has a SearchManager, the others have a NullSearchManager std::unique_ptr manager; From 16afec058229067e2f3965672aff450d6a9babc7 Mon Sep 17 00:00:00 2001 From: Disservin Date: Sat, 27 Jan 2024 22:27:22 +0100 Subject: [PATCH 1351/1766] Refactor pv printing Also fix the case which is currently printing depth 0. fixes #5019 closes https://github.com/official-stockfish/Stockfish/pull/5020 No functional change --- src/search.cpp | 70 ++++++++++++++++++++++++++++++++++++++++++-------- src/search.h | 6 +++++ src/uci.cpp | 58 +---------------------------------------- src/uci.h | 11 -------- 4 files changed, 66 insertions(+), 79 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 29b5c5243b3..0a07c8bb972 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include "evaluate.h" #include "misc.h" @@ -192,9 +193,7 @@ void Search::Worker::start_searching() { // Send again PV info if we have a new best thread if (bestThread != this) - sync_cout << UCI::pv(*bestThread, main_manager()->tm.elapsed(threads.nodes_searched()), - threads.nodes_searched(), threads.tb_hits(), tt.hashfull(), - tbConfig.rootInTB) + sync_cout << main_manager()->pv(*bestThread, threads, tt, bestThread->completedDepth) << sync_endl; sync_cout << "bestmove " << UCI::move(bestThread->rootMoves[0].pv[0], rootPos.is_chess960()); @@ -336,10 +335,7 @@ void Search::Worker::iterative_deepening() { // the UI) before a re-search. if (mainThread && multiPV == 1 && (bestValue <= alpha || bestValue >= beta) && mainThread->tm.elapsed(threads.nodes_searched()) > 3000) - sync_cout << UCI::pv(*this, mainThread->tm.elapsed(threads.nodes_searched()), - threads.nodes_searched(), threads.tb_hits(), tt.hashfull(), - tbConfig.rootInTB) - << sync_endl; + sync_cout << main_manager()->pv(*this, threads, tt, rootDepth) << sync_endl; // In case of failing low/high increase aspiration window and // re-search, otherwise exit the loop. @@ -376,10 +372,7 @@ void Search::Worker::iterative_deepening() { // had time to fully search other root-moves. Thus we suppress this output and // below pick a proven score/PV for this thread (from the previous iteration). && !(threads.abortedSearch && rootMoves[0].uciScore <= VALUE_TB_LOSS_IN_MAX_PLY)) - sync_cout << UCI::pv(*this, mainThread->tm.elapsed(threads.nodes_searched()), - threads.nodes_searched(), threads.tb_hits(), tt.hashfull(), - tbConfig.rootInTB) - << sync_endl; + sync_cout << main_manager()->pv(*this, threads, tt, rootDepth) << sync_endl; } if (!threads.stop) @@ -1878,6 +1871,61 @@ void SearchManager::check_time(Search::Worker& worker) { worker.threads.stop = worker.threads.abortedSearch = true; } +std::string SearchManager::pv(const Search::Worker& worker, + const ThreadPool& threads, + const TranspositionTable& tt, + Depth depth) const { + std::stringstream ss; + + const auto nodes = threads.nodes_searched(); + const auto& rootMoves = worker.rootMoves; + const auto& pos = worker.rootPos; + size_t pvIdx = worker.pvIdx; + TimePoint time = tm.elapsed(nodes) + 1; + size_t multiPV = std::min(size_t(worker.options["MultiPV"]), rootMoves.size()); + uint64_t tbHits = threads.tb_hits() + (worker.tbConfig.rootInTB ? rootMoves.size() : 0); + + for (size_t i = 0; i < multiPV; ++i) + { + bool updated = rootMoves[i].score != -VALUE_INFINITE; + + if (depth == 1 && !updated && i > 0) + continue; + + Depth d = updated ? depth : std::max(1, depth - 1); + Value v = updated ? rootMoves[i].uciScore : rootMoves[i].previousScore; + + if (v == -VALUE_INFINITE) + v = VALUE_ZERO; + + bool tb = worker.tbConfig.rootInTB && std::abs(v) <= VALUE_TB; + v = tb ? rootMoves[i].tbScore : v; + + if (ss.rdbuf()->in_avail()) // Not at first line + ss << "\n"; + + ss << "info" + << " depth " << d << " seldepth " << rootMoves[i].selDepth << " multipv " << i + 1 + << " score " << UCI::value(v); + + if (worker.options["UCI_ShowWDL"]) + ss << UCI::wdl(v, pos.game_ply()); + + if (i == pvIdx && !tb && updated) // tablebase- and previous-scores are exact + ss << (rootMoves[i].scoreLowerbound + ? " lowerbound" + : (rootMoves[i].scoreUpperbound ? " upperbound" : "")); + + ss << " nodes " << nodes << " nps " << nodes * 1000 / time << " hashfull " << tt.hashfull() + << " tbhits " << tbHits << " time " << time << " pv"; + + for (Move m : rootMoves[i].pv) + ss << " " << UCI::move(m, pos.is_chess960()); + } + + return ss.str(); +} + // Called in case we have no ponder move before exiting the search, // for instance, in case we stop the search during a fail high at root. // We try hard to have a ponder move to return to the GUI, diff --git a/src/search.h b/src/search.h index b4a65d8e6f6..c8534b40177 100644 --- a/src/search.h +++ b/src/search.h @@ -26,6 +26,7 @@ #include #include #include +#include #include "misc.h" #include "movepick.h" @@ -150,6 +151,11 @@ class SearchManager: public ISearchManager { public: void check_time(Search::Worker& worker) override; + std::string pv(const Search::Worker& worker, + const ThreadPool& threads, + const TranspositionTable& tt, + Depth depth) const; + Stockfish::TimeManagement tm; int callsCnt; std::atomic_bool ponder; diff --git a/src/uci.cpp b/src/uci.cpp index e6107d4713a..d1d69d69752 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include "benchmark.h" #include "evaluate.h" @@ -365,63 +366,6 @@ std::string UCI::move(Move m, bool chess960) { return move; } -std::string UCI::pv(const Search::Worker& workerThread, - TimePoint elapsed, - uint64_t nodesSearched, - uint64_t tb_hits, - int hashfull, - bool rootInTB) { - std::stringstream ss; - TimePoint time = elapsed + 1; - const auto& rootMoves = workerThread.rootMoves; - const auto& depth = workerThread.completedDepth; - const auto& pos = workerThread.rootPos; - size_t pvIdx = workerThread.pvIdx; - size_t multiPV = std::min(size_t(workerThread.options["MultiPV"]), rootMoves.size()); - uint64_t tbHits = tb_hits + (rootInTB ? rootMoves.size() : 0); - - - for (size_t i = 0; i < multiPV; ++i) - { - bool updated = rootMoves[i].score != -VALUE_INFINITE; - - if (depth == 1 && !updated && i > 0) - continue; - - Depth d = updated ? depth : std::max(1, depth - 1); - Value v = updated ? rootMoves[i].uciScore : rootMoves[i].previousScore; - - if (v == -VALUE_INFINITE) - v = VALUE_ZERO; - - bool tb = rootInTB && std::abs(v) <= VALUE_TB; - v = tb ? rootMoves[i].tbScore : v; - - if (ss.rdbuf()->in_avail()) // Not at first line - ss << "\n"; - - ss << "info" - << " depth " << d << " seldepth " << rootMoves[i].selDepth << " multipv " << i + 1 - << " score " << value(v); - - if (workerThread.options["UCI_ShowWDL"]) - ss << wdl(v, pos.game_ply()); - - if (i == pvIdx && !tb && updated) // tablebase- and previous-scores are exact - ss << (rootMoves[i].scoreLowerbound - ? " lowerbound" - : (rootMoves[i].scoreUpperbound ? " upperbound" : "")); - - ss << " nodes " << nodesSearched << " nps " << nodesSearched * 1000 / time << " hashfull " - << hashfull << " tbhits " << tbHits << " time " << time << " pv"; - - for (Move m : rootMoves[i].pv) - ss << " " << move(m, pos.is_chess960()); - } - - return ss.str(); -} - namespace { // The win rate model returns the probability of winning (in per mille units) given an // eval and a game ply. It fits the LTC fishtest statistics rather accurately. diff --git a/src/uci.h b/src/uci.h index cd113b1ad29..9d5f524ad65 100644 --- a/src/uci.h +++ b/src/uci.h @@ -19,7 +19,6 @@ #ifndef UCI_H_INCLUDED #define UCI_H_INCLUDED -#include #include #include #include @@ -37,10 +36,6 @@ namespace Eval::NNUE { enum NetSize : int; } -namespace Search { -class Worker; -} - class Move; enum Square : int; using Value = int; @@ -55,12 +50,6 @@ class UCI { static std::string value(Value v); static std::string square(Square s); static std::string move(Move m, bool chess960); - static std::string pv(const Search::Worker& workerThread, - TimePoint elapsed, - uint64_t nodesSearched, - uint64_t tb_hits, - int hashfull, - bool rootInTB); static std::string wdl(Value v, int ply); static Move to_move(const Position& pos, std::string& str); From 3cce4c4cf4e39f05cea01e1020080284bf5a0fae Mon Sep 17 00:00:00 2001 From: Disservin Date: Wed, 31 Jan 2024 22:47:02 +0100 Subject: [PATCH 1352/1766] Add Apple Silicon Runners to CI GitHub CI runners are available for macOS 14, these runners are using apple silicon chips (M1). https://github.blog/changelog/2024-01-30-github-actions-introducing-the-new-m1-macos-runner-available-to-open-source/ closes https://github.com/official-stockfish/Stockfish/pull/5025 No functional change --- .github/workflows/stockfish_binaries.yml | 46 ++++++++++++++++++-- .github/workflows/stockfish_compile_test.yml | 17 ++++++++ .github/workflows/stockfish_test.yml | 22 ++++++++-- 3 files changed, 77 insertions(+), 8 deletions(-) diff --git a/.github/workflows/stockfish_binaries.yml b/.github/workflows/stockfish_binaries.yml index eff2c2c9471..2911badacf7 100644 --- a/.github/workflows/stockfish_binaries.yml +++ b/.github/workflows/stockfish_binaries.yml @@ -31,6 +31,13 @@ jobs: comp: clang shell: bash archive_ext: tar + - name: MacOS 14 Apple Clang M1 + os: macos-14 + simple_name: macos-m1 + compiler: clang++ + comp: clang + shell: bash + archive_ext: tar - name: Windows 2022 Mingw-w64 GCC x86_64 os: windows-2022 simple_name: windows @@ -51,9 +58,32 @@ jobs: - x86-64-avx512 - x86-64-vnni256 - x86-64-vnni512 + - apple-silicon exclude: + # Apple M1 + - binaries: x86-64 + config: { os: macos-14 } + - binaries: x86-64-sse41-popcnt + config: { os: macos-14 } + - binaries: x86-64-avx2 + config: { os: macos-14 } + - binaries: x86-64-bmi2 + config: { os: macos-14 } + - binaries: x86-64-avxvnni + config: { os: macos-14 } + - binaries: x86-64-avxvnni + config: { os: macos-14 } + - binaries: x86-64-avx512 + config: { os: macos-14 } + - binaries: x86-64-vnni256 + config: { os: macos-14 } + - binaries: x86-64-vnni512 + config: { os: macos-14 } + - binaries: x86-64-avxvnni config: { ubuntu-20.04 } + + # Apple x86_64 (no sde) - binaries: x86-64-avxvnni config: { os: macos-13 } - binaries: x86-64-avx512 @@ -62,6 +92,14 @@ jobs: config: { os: macos-13 } - binaries: x86-64-vnni512 config: { os: macos-13 } + + # Apple silicon from windows, macos-13 and ubuntu + - binaries: apple-silicon + config: { os: windows-2022 } + - binaries: apple-silicon + config: { os: macos-13 } + - binaries: apple-silicon + config: { os: ubuntu-20.04 } defaults: run: working-directory: src @@ -77,7 +115,7 @@ jobs: - name: Install fixed GCC on Linux if: runner.os == 'Linux' - uses: egor-tensin/setup-gcc@eaa888eb19115a521fa72b65cd94fe1f25bbcaac # @v1.3 + uses: egor-tensin/setup-gcc@eaa888eb19115a521fa72b65cd94fe1f25bbcaac # @v1.3 with: version: 11 @@ -90,7 +128,7 @@ jobs: - name: Download SDE package if: runner.os == 'Linux' || runner.os == 'Windows' - uses: petarpetrovt/setup-sde@91a1a03434384e064706634125a15f7446d2aafb # @v2.3 + uses: petarpetrovt/setup-sde@91a1a03434384e064706634125a15f7446d2aafb # @v2.3 with: environmentVariableName: SDE_DIR sdeVersion: 9.27.0 @@ -183,7 +221,7 @@ jobs: - name: Release if: startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag' - uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # @v1 + uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # @v1 with: files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.${{ matrix.config.archive_ext }} @@ -206,7 +244,7 @@ jobs: - name: Prerelease if: github.ref_name == 'master' && env.CHANGES == '0' continue-on-error: true - uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # @v1 + uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # @v1 with: name: Stockfish dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} tag_name: stockfish-dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} diff --git a/.github/workflows/stockfish_compile_test.yml b/.github/workflows/stockfish_compile_test.yml index 1adc3e34b7c..a47fcb0fc31 100644 --- a/.github/workflows/stockfish_compile_test.yml +++ b/.github/workflows/stockfish_compile_test.yml @@ -26,6 +26,12 @@ jobs: compiler: clang++ comp: clang shell: bash + - name: MacOS 14 Apple Clang M1 + os: macos-14 + compiler: clang++ + comp: clang + shell: bash + m1: true - name: MacOS 13 GCC 11 os: macos-13 compiler: g++-11 @@ -75,26 +81,37 @@ jobs: # x86-64 with newer extensions tests - name: Compile x86-64-avx2 build + if: ${{ ! matrix.config.m1 }} run: | make clean make -j2 ARCH=x86-64-avx2 build - name: Compile x86-64-bmi2 build + if: ${{ ! matrix.config.m1 }} run: | make clean make -j2 ARCH=x86-64-bmi2 build - name: Compile x86-64-avx512 build + if: ${{ ! matrix.config.m1 }} run: | make clean make -j2 ARCH=x86-64-avx512 build - name: Compile x86-64-vnni512 build + if: ${{ ! matrix.config.m1 }} run: | make clean make -j2 ARCH=x86-64-vnni512 build - name: Compile x86-64-vnni256 build + if: ${{ ! matrix.config.m1 }} run: | make clean make -j2 ARCH=x86-64-vnni256 build + + - name: Compile apple-silicon build + if: matrix.config.m1 + run: | + make clean + make -j2 ARCH=apple-silicon build diff --git a/.github/workflows/stockfish_test.yml b/.github/workflows/stockfish_test.yml index cff3ef1b184..867099ee58c 100644 --- a/.github/workflows/stockfish_test.yml +++ b/.github/workflows/stockfish_test.yml @@ -43,16 +43,16 @@ jobs: compiler: g++ comp: gcc run_riscv64_tests: true - base_image: 'riscv64/alpine:edge' - platform: linux/riscv64 + base_image: "riscv64/alpine:edge" + platform: linux/riscv64 shell: bash - name: Linux GCC ppc64 os: ubuntu-22.04 compiler: g++ comp: gcc run_ppc64_tests: true - base_image: 'ppc64le/alpine:latest' - platform: linux/ppc64le + base_image: "ppc64le/alpine:latest" + platform: linux/ppc64le shell: bash - name: MacOS 13 Apple Clang os: macos-13 @@ -60,6 +60,13 @@ jobs: comp: clang run_64bit_tests: true shell: bash + - name: MacOS 14 Apple Clang M1 + os: macos-14 + compiler: clang++ + comp: clang + run_64bit_tests: false + run_m1_tests: true + shell: bash - name: MacOS 13 GCC 11 os: macos-13 compiler: g++-11 @@ -281,6 +288,13 @@ jobs: make -j2 ARCH=general-64 build ../tests/signature.sh $benchref + - name: Test apple-silicon build + if: matrix.config.run_m1_tests + run: | + make clean + make -j2 ARCH=apple-silicon build + ../tests/signature.sh $benchref + # armv8 tests - name: Test armv8 build From 56b342f9b27827e77cb9e898aa2ed69b628d672f Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sat, 27 Jan 2024 21:55:00 +0300 Subject: [PATCH 1353/1766] Simplify the extension formula Simplify the extension formula in the case of cutNode by removing the depth condition and always setting extension to -2. Passed STC: LLR: 2.97 (-2.94,2.94) <-1.75,0.25> Total: 277280 W: 70760 L: 70802 D: 135718 Ptnml(0-2): 971, 31775, 73153, 31807, 934 https://tests.stockfishchess.org/tests/view/65ad08f779aa8af82b979dd6 Passed LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 452976 W: 112992 L: 113215 D: 226769 Ptnml(0-2): 266, 51041, 124112, 50788, 281 https://tests.stockfishchess.org/tests/view/65ae466fc865510db026a760 closes https://github.com/official-stockfish/Stockfish/pull/5021 Bench: 1492957 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 0a07c8bb972..6eb05081092 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1065,7 +1065,7 @@ Value Search::Worker::search( // If we are on a cutNode but the ttMove is not assumed to fail high over current beta (~1 Elo) else if (cutNode) - extension = depth < 20 ? -2 : -1; + extension = -2; // If the ttMove is assumed to fail low over the value of the reduced search (~1 Elo) else if (ttValue <= value) From f2b6b5cfc9bd27c0595520c96f6e1f8416296f75 Mon Sep 17 00:00:00 2001 From: Viren6 <94880762+Viren6@users.noreply.github.com> Date: Thu, 1 Feb 2024 18:48:45 +0000 Subject: [PATCH 1354/1766] Introduce Triple Extensions This replaces singularquietLMR with triple instead of double extending non-capture ttmoves that have value far below singularBeta. This threshold value is initially set to 200, there is scope for more scaling by reducing it as occured with double extensions. Passed STC: https://tests.stockfishchess.org/tests/view/65b683b8c865510db0274074 LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 222912 W: 58141 L: 57535 D: 107236 Ptnml(0-2): 1063, 26244, 56154, 27014, 981 Passed LTC: https://tests.stockfishchess.org/tests/view/65bae6d4c865510db0278eb5 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 66306 W: 16825 L: 16440 D: 33041 Ptnml(0-2): 40, 7374, 17952, 7735, 52 closes https://github.com/official-stockfish/Stockfish/pull/5027 bench 1394701 --- src/search.cpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 6eb05081092..d47582a57f1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -522,7 +522,7 @@ Value Search::Worker::search( Move ttMove, move, excludedMove, bestMove; Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue, probCutBeta; - bool givesCheck, improving, priorCapture, singularQuietLMR; + bool givesCheck, improving, priorCapture; bool capture, moveCountPruning, ttCapture; Piece movedPiece; int moveCount, captureCount, quietCount; @@ -900,7 +900,7 @@ Value Search::Worker::search( contHist, &thisThread->pawnHistory, countermove, ss->killers); value = bestValue; - moveCountPruning = singularQuietLMR = false; + moveCountPruning = false; // Step 13. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. @@ -1034,13 +1034,12 @@ Value Search::Worker::search( if (value < singularBeta) { - extension = 1; - singularQuietLMR = !ttCapture; + extension = 1; // Avoid search explosion by limiting the number of double extensions - if (!PvNode && value < singularBeta - 2 && ss->doubleExtensions <= 12) + if (!PvNode && value < singularBeta - 2 && ss->doubleExtensions <= 15) { - extension = 2; + extension = 2 + (value < singularBeta - 200 && !ttCapture); depth += depth < 15; } } @@ -1091,7 +1090,7 @@ Value Search::Worker::search( // Add extension to new depth newDepth += extension; - ss->doubleExtensions = (ss - 1)->doubleExtensions + (extension == 2); + ss->doubleExtensions = (ss - 1)->doubleExtensions + (extension >= 2); // Speculative prefetch as early as possible prefetch(tt.first_entry(pos.key_after(move))); @@ -1125,10 +1124,6 @@ Value Search::Worker::search( if (PvNode && tte->bound() != BOUND_UPPER) r--; - // Decrease reduction if a quiet ttMove has been singularly extended (~1 Elo) - if (singularQuietLMR) - r--; - // Increase reduction on repetition (~1 Elo) if (move == (ss - 4)->currentMove && pos.has_repeated()) r += 2; From e815227c3081269f7b37538cf5f32c838991db29 Mon Sep 17 00:00:00 2001 From: gab8192 Date: Thu, 1 Feb 2024 20:38:48 +0100 Subject: [PATCH 1355/1766] Simplify LMR condition Apply LMR on captures the same way it is applied on quiets Passed Non-Reg STC: https://tests.stockfishchess.org/tests/view/65bbf39bc865510db027a14a LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 77152 W: 19970 L: 19791 D: 37391 Ptnml(0-2): 304, 9159, 19496, 9288, 329 Passed Non-Reg LTC: https://tests.stockfishchess.org/tests/view/65bc8889c865510db027ac9e LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 103230 W: 25997 L: 25858 D: 51375 Ptnml(0-2): 71, 11687, 27958, 11830, 69 Hit rate of removed condition (!ss->ttPv || !capture || (cutNode && (ss - 1)->moveCount > 1)) Total 1253801 Hits 1228904 Hit Rate (%) 98.0143 Hit rate of previous LMR (depth >= 2 && moveCount > 1 + rootNode && ...) Total 1253801 Hits 727234 Hit Rate (%) 58.0023 Hit rate of simplified LMR (depth >= 2 && moveCount > 1 + rootNode) Total 1201839 Hits 713540 Hit Rate (%) 59.3707 closes https://github.com/official-stockfish/Stockfish/pull/5028 Bench: 1438224 --- src/search.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index d47582a57f1..538f0f30ae2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1146,11 +1146,7 @@ Value Search::Worker::search( r -= ss->statScore / 15373; // Step 17. Late moves reduction / extension (LMR, ~117 Elo) - // We use various heuristics for the sons of a node after the first son has - // been searched. In general, we would like to reduce them, but there are many - // cases where we extend a son if it has good chances to be "interesting". - if (depth >= 2 && moveCount > 1 + rootNode - && (!ss->ttPv || !capture || (cutNode && (ss - 1)->moveCount > 1))) + if (depth >= 2 && moveCount > 1 + rootNode) { // In general we want to cap the LMR depth search at newDepth, but when // reduction is negative, we allow this move a limited search extension From ededadcd6f7fbb9eb122f5fe336025cc4b11753b Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Thu, 1 Feb 2024 08:13:06 +0800 Subject: [PATCH 1356/1766] VVLTC search tune Search parameters were tuned at 60+0.6 8-thread. Link to the tuning attempt: https://tests.stockfishchess.org/tests/view/65b84e8dc865510db0276030 The most significant change is the triple extension parameter, from 200 to 78. This presumably improves scaling. Additionally, the value < singularBeta - 2 condition for double extensions was removed. This can simply be considered a parameter tweak from 2 to 0. Passed VVLTC: https://tests.stockfishchess.org/tests/view/65baec69c865510db0278f19 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 26136 W: 6564 L: 6305 D: 13267 Ptnml(0-2): 2, 2413, 7977, 2676, 0 Passed VVLTC vs passed PR #5027: https://tests.stockfishchess.org/tests/view/65bc2adfc865510db027a561 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 52968 W: 13372 L: 13046 D: 26550 Ptnml(0-2): 4, 4944, 16265, 5264, 7 STC Elo estimate: https://tests.stockfishchess.org/tests/view/65be5514c865510db027cbc5 closes https://github.com/official-stockfish/Stockfish/pull/5029 Bench: 1478189 --- src/search.cpp | 74 +++++++++++++++++++++++++------------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 538f0f30ae2..e57f2557cf8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -55,7 +55,7 @@ namespace { // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving) { - Value futilityMult = 114 - 47 * noTtCutNode; + Value futilityMult = 116 - 47 * noTtCutNode; return (futilityMult * d - 3 * futilityMult / 2 * improving); } @@ -66,15 +66,15 @@ constexpr int futility_move_count(bool improving, Depth depth) { // Add correctionHistory value to raw staticEval and guarantee evaluation does not hit the tablebase range Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { auto cv = w.correctionHistory[pos.side_to_move()][pawn_structure_index(pos)]; - v += cv * std::abs(cv) / 14095; + v += cv * std::abs(cv) / 12890; return std::clamp(int(v), VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } // History and stats update bonus, based on depth -int stat_bonus(Depth d) { return std::min(265 * d - 349, 1112); } +int stat_bonus(Depth d) { return std::min(253 * d - 356, 1117); } // History and stats update malus, based on depth -int stat_malus(Depth d) { return std::min(482 * d - 326, 1172); } +int stat_malus(Depth d) { return std::min(517 * d - 308, 1206); } // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(size_t nodes) { return VALUE_DRAW - 1 + Value(nodes & 0x2); } @@ -297,12 +297,12 @@ void Search::Worker::iterative_deepening() { // Reset aspiration window starting size Value avg = rootMoves[pvIdx].averageScore; - delta = Value(9) + int(avg) * avg / 13181; + delta = Value(9) + int(avg) * avg / 12480; alpha = std::max(avg - delta, -VALUE_INFINITE); beta = std::min(avg + delta, int(VALUE_INFINITE)); // Adjust optimism based on root move's averageScore (~4 Elo) - optimism[us] = 132 * avg / (std::abs(avg) + 98); + optimism[us] = 131 * avg / (std::abs(avg) + 95); optimism[~us] = -optimism[us]; // Start with a small aspiration window and, in the case of a fail @@ -726,7 +726,7 @@ Value Search::Worker::search( // Use static evaluation difference to improve quiet move ordering (~9 Elo) if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture) { - int bonus = std::clamp(-13 * int((ss - 1)->staticEval + ss->staticEval), -1680, 1406); + int bonus = std::clamp(-14 * int((ss - 1)->staticEval + ss->staticEval), -1661, 1495); bonus = bonus > 0 ? 2 * bonus : bonus / 2; thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) @@ -747,7 +747,7 @@ Value Search::Worker::search( // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. // Adjust razor margin according to cutoffCnt. (~1 Elo) - if (eval < alpha - 435 - (327 - 167 * ((ss + 1)->cutoffCnt > 3)) * depth * depth) + if (eval < alpha - 450 - (332 - 160 * ((ss + 1)->cutoffCnt > 3)) * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -760,20 +760,20 @@ Value Search::Worker::search( && eval - futility_margin(depth, cutNode && !ss->ttHit, improving) - (ss - 1)->statScore / 327 >= beta - && eval >= beta && eval < 27734 // smaller than TB wins + && eval >= beta && eval < 28702 // smaller than TB wins && (!ttMove || ttCapture)) return beta > VALUE_TB_LOSS_IN_MAX_PLY ? (eval + beta) / 2 : eval; // Step 9. Null move search with verification search (~35 Elo) - if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 17787 - && eval >= beta && eval >= ss->staticEval && ss->staticEval >= beta - 22 * depth + 313 + if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 17379 + && eval >= beta && eval >= ss->staticEval && ss->staticEval >= beta - 21 * depth + 329 && !excludedMove && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly && beta > VALUE_TB_LOSS_IN_MAX_PLY) { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and eval - Depth R = std::min(int(eval - beta) / 144, 6) + depth / 3 + 4; + Depth R = std::min(int(eval - beta) / 148, 6) + depth / 3 + 4; ss->currentMove = Move::null(); ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -787,7 +787,7 @@ Value Search::Worker::search( // Do not return unproven mate or TB scores if (nullValue >= beta && nullValue < VALUE_TB_WIN_IN_MAX_PLY) { - if (thisThread->nmpMinPly || depth < 15) + if (thisThread->nmpMinPly || depth < 16) return nullValue; assert(!thisThread->nmpMinPly); // Recursive verification is not allowed @@ -820,7 +820,7 @@ Value Search::Worker::search( if (cutNode && depth >= 8 && !ttMove) depth -= 2; - probCutBeta = beta + 173 - 73 * improving; + probCutBeta = beta + 182 - 68 * improving; // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search returns a value @@ -880,7 +880,7 @@ Value Search::Worker::search( moves_loop: // When in check, search starts here // Step 12. A small Probcut idea, when we are in check (~4 Elo) - probCutBeta = beta + 427; + probCutBeta = beta + 446; if (ss->inCheck && !PvNode && ttCapture && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 4 && ttValue >= probCutBeta && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) @@ -963,7 +963,7 @@ Value Search::Worker::search( { Piece capturedPiece = pos.piece_on(move.to_sq()); int futilityEval = - ss->staticEval + 277 + 298 * lmrDepth + PieceValue[capturedPiece] + ss->staticEval + 279 + 295 * lmrDepth + PieceValue[capturedPiece] + thisThread->captureHistory[movedPiece][move.to_sq()][type_of(capturedPiece)] / 7; if (futilityEval < alpha) @@ -971,7 +971,7 @@ Value Search::Worker::search( } // SEE based pruning for captures and checks (~11 Elo) - if (!pos.see_ge(move, -203 * depth)) + if (!pos.see_ge(move, -204 * depth)) continue; } else @@ -983,17 +983,17 @@ Value Search::Worker::search( + thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()]; // Continuation history based pruning (~2 Elo) - if (lmrDepth < 6 && history < -4195 * depth) + if (lmrDepth < 6 && history < -4215 * depth) continue; history += 2 * thisThread->mainHistory[us][move.from_to()]; - lmrDepth += history / 6992; + lmrDepth += history / 6658; // Futility pruning: parent node (~13 Elo) if (!ss->inCheck && lmrDepth < 15 - && ss->staticEval + (bestValue < ss->staticEval - 63 ? 137 : 64) - + 111 * lmrDepth + && ss->staticEval + (bestValue < ss->staticEval - 58 ? 139 : 55) + + 121 * lmrDepth <= alpha) continue; @@ -1020,11 +1020,11 @@ Value Search::Worker::search( // so changing them requires tests at these types of time controls. // Recursive singular search is avoided. if (!rootNode && move == ttMove && !excludedMove - && depth >= 4 - (thisThread->completedDepth > 31) + ss->ttPv + && depth >= 4 - (thisThread->completedDepth > 29) + ss->ttPv && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - (58 + 52 * (ss->ttPv && !PvNode)) * depth / 64; + Value singularBeta = ttValue - (62 + 52 * (ss->ttPv && !PvNode)) * depth / 64; Depth singularDepth = newDepth / 2; ss->excludedMove = move; @@ -1037,10 +1037,10 @@ Value Search::Worker::search( extension = 1; // Avoid search explosion by limiting the number of double extensions - if (!PvNode && value < singularBeta - 2 && ss->doubleExtensions <= 15) + if (!PvNode && ss->doubleExtensions <= 16) { - extension = 2 + (value < singularBeta - 200 && !ttCapture); - depth += depth < 15; + extension = 2 + (value < singularBeta - 78 && !ttCapture); + depth += depth < 16; } } @@ -1077,14 +1077,14 @@ Value Search::Worker::search( // Quiet ttMove extensions (~1 Elo) else if (PvNode && move == ttMove && move == ss->killers[0] - && (*contHist[0])[movedPiece][move.to_sq()] >= 4111) + && (*contHist[0])[movedPiece][move.to_sq()] >= 4339) extension = 1; // Recapture extensions (~1 Elo) else if (PvNode && move == ttMove && move.to_sq() == prevSq && thisThread->captureHistory[movedPiece][move.to_sq()] [type_of(pos.piece_on(move.to_sq()))] - > 4484) + > 4356) extension = 1; } @@ -1140,10 +1140,10 @@ Value Search::Worker::search( ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + (*contHist[0])[movedPiece][move.to_sq()] + (*contHist[1])[movedPiece][move.to_sq()] - + (*contHist[3])[movedPiece][move.to_sq()] - 4119; + + (*contHist[3])[movedPiece][move.to_sq()] - 4409; // Decrease/increase reduction for moves with a good/bad history (~25 Elo) - r -= ss->statScore / 15373; + r -= ss->statScore / 14894; // Step 17. Late moves reduction / extension (LMR, ~117 Elo) if (depth >= 2 && moveCount > 1 + rootNode) @@ -1162,7 +1162,7 @@ Value Search::Worker::search( { // Adjust full-depth search based on LMR results - if the result // was good enough search deeper, if it was bad enough search shallower. - const bool doDeeperSearch = value > (bestValue + 51 + 2 * newDepth); // (~1 Elo) + const bool doDeeperSearch = value > (bestValue + 49 + 2 * newDepth); // (~1 Elo) const bool doShallowerSearch = value < bestValue + newDepth; // (~2 Elo) newDepth += doDeeperSearch - doShallowerSearch; @@ -1278,7 +1278,7 @@ Value Search::Worker::search( else { // Reduce other moves if we have found at least one score improvement (~2 Elo) - if (depth > 2 && depth < 12 && beta < 13195 && value > -12346) + if (depth > 2 && depth < 13 && beta < 13710 && value > -12589) depth -= 2; assert(depth > 0); @@ -1317,8 +1317,8 @@ Value Search::Worker::search( // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (depth > 5) + (PvNode || cutNode) + ((ss - 1)->statScore < -16797) - + ((ss - 1)->moveCount > 10); + int bonus = (depth > 5) + (PvNode || cutNode) + ((ss - 1)->statScore < -15401) + + ((ss - 1)->moveCount > 11); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] @@ -1476,7 +1476,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, if (bestValue > alpha) alpha = bestValue; - futilityBase = ss->staticEval + 186; + futilityBase = ss->staticEval + 204; } const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, @@ -1556,7 +1556,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, continue; // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, -76)) + if (!pos.see_ge(move, -75)) continue; } @@ -1711,7 +1711,7 @@ void update_all_stats(const Position& pos, if (!pos.capture_stage(bestMove)) { - int bestMoveBonus = bestValue > beta + 177 ? quietMoveBonus // larger bonus + int bestMoveBonus = bestValue > beta + 167 ? quietMoveBonus // larger bonus : stat_bonus(depth); // smaller bonus // Increase stats for the best move in case it was a quiet move From 59691d46a13880534802fe7e610f56813f0e47fc Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sun, 4 Feb 2024 12:59:26 +0300 Subject: [PATCH 1357/1766] Assorted trivial cleanups Renaming doubleExtensions variable to multiExtensions, since now we have also triple extensions. Some extra cleanups. Recent tests used to measure the elo worth: https://tests.stockfishchess.org/tests/view/659fd0c379aa8af82b96abc3 https://tests.stockfishchess.org/tests/view/65a8f3da79aa8af82b9751e3 https://tests.stockfishchess.org/tests/view/65b51824c865510db0272740 https://tests.stockfishchess.org/tests/view/65b58fbfc865510db0272f5b closes https://github.com/official-stockfish/Stockfish/pull/5032 No functional change --- src/nnue/nnue_feature_transformer.h | 4 +--- src/search.cpp | 20 ++++++++++---------- src/search.h | 11 +++++------ 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 9a162ac9853..3399b82df6a 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -281,7 +281,7 @@ class FeatureTransformer { reinterpret_cast(&(accumulation[perspectives[p]][HalfDimensions / 2])); vec_t* out = reinterpret_cast(output + offset); - for (IndexType j = 0; j < NumOutputChunks; j += 1) + for (IndexType j = 0; j < NumOutputChunks; ++j) { const vec_t sum0a = vec_max_16(vec_min_16(in0[j * 2 + 0], One), Zero); const vec_t sum0b = vec_max_16(vec_min_16(in0[j * 2 + 1], One), Zero); @@ -676,9 +676,7 @@ class FeatureTransformer { update_accumulator_incremental(pos, oldest_st, states_to_update); } else - { update_accumulator_refresh(pos); - } } template diff --git a/src/search.cpp b/src/search.cpp index e57f2557cf8..336678c0617 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -571,7 +571,7 @@ Value Search::Worker::search( (ss + 1)->excludedMove = bestMove = Move::none(); (ss + 2)->killers[0] = (ss + 2)->killers[1] = Move::none(); (ss + 2)->cutoffCnt = 0; - ss->doubleExtensions = (ss - 1)->doubleExtensions; + ss->multipleExtensions = (ss - 1)->multipleExtensions; Square prevSq = ((ss - 1)->currentMove).is_ok() ? ((ss - 1)->currentMove).to_sq() : SQ_NONE; ss->statScore = 0; @@ -1036,8 +1036,8 @@ Value Search::Worker::search( { extension = 1; - // Avoid search explosion by limiting the number of double extensions - if (!PvNode && ss->doubleExtensions <= 16) + // We make sure to limit the extensions in some way to avoid a search explosion + if (!PvNode && ss->multipleExtensions <= 16) { extension = 2 + (value < singularBeta - 78 && !ttCapture); depth += depth < 16; @@ -1090,7 +1090,7 @@ Value Search::Worker::search( // Add extension to new depth newDepth += extension; - ss->doubleExtensions = (ss - 1)->doubleExtensions + (extension >= 2); + ss->multipleExtensions = (ss - 1)->multipleExtensions + (extension >= 2); // Speculative prefetch as early as possible prefetch(tt.first_entry(pos.key_after(move))); @@ -1142,7 +1142,7 @@ Value Search::Worker::search( + (*contHist[1])[movedPiece][move.to_sq()] + (*contHist[3])[movedPiece][move.to_sq()] - 4409; - // Decrease/increase reduction for moves with a good/bad history (~25 Elo) + // Decrease/increase reduction for moves with a good/bad history (~8 Elo) r -= ss->statScore / 14894; // Step 17. Late moves reduction / extension (LMR, ~117 Elo) @@ -1150,7 +1150,7 @@ Value Search::Worker::search( { // In general we want to cap the LMR depth search at newDepth, but when // reduction is negative, we allow this move a limited search extension - // beyond the first move depth. This may lead to hidden double extensions. + // beyond the first move depth. This may lead to hidden multiple extensions. // To prevent problems when the max value is less than the min value, // std::clamp has been replaced by a more robust implementation. Depth d = std::max(1, std::min(newDepth - r, newDepth + 1)); @@ -1371,8 +1371,8 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, assert(PvNode || (alpha == beta - 1)); assert(depth <= 0); - // Check if we have an upcoming move that draws by repetition, or - // if the opponent had an alternative move earlier to this position. + // Check if we have an upcoming move that draws by repetition, or if + // the opponent had an alternative move earlier to this position. (~1 Elo) if (alpha < VALUE_DRAW && pos.has_game_cycle(ss->ply)) { alpha = value_draw(this->nodes); @@ -1520,7 +1520,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, futilityValue = futilityBase + PieceValue[pos.piece_on(move.to_sq())]; // If static eval + value of piece we are going to capture is much lower - // than alpha we can prune this move. + // than alpha we can prune this move. (~2 Elo) if (futilityValue <= alpha) { bestValue = std::max(bestValue, futilityValue); @@ -1528,7 +1528,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, } // If static eval is much lower than alpha and move is not winning material - // we can prune this move. + // we can prune this move. (~2 Elo) if (futilityBase <= alpha && !pos.see_ge(move, VALUE_ZERO + 1)) { bestValue = std::max(bestValue, futilityBase); diff --git a/src/search.h b/src/search.h index c8534b40177..97cb2ca40b2 100644 --- a/src/search.h +++ b/src/search.h @@ -67,7 +67,7 @@ struct Stack { bool inCheck; bool ttPv; bool ttHit; - int doubleExtensions; + int multipleExtensions; int cutoffCnt; }; @@ -136,9 +136,8 @@ struct SharedState { class Worker; -// Null Object Pattern, implement a common interface -// for the SearchManagers. A Null Object will be given to -// non-mainthread workers. +// Null Object Pattern, implement a common interface for the SearchManagers. +// A Null Object will be given to non-mainthread workers. class ISearchManager { public: virtual ~ISearchManager() {} @@ -185,8 +184,8 @@ class Worker { // Reset histories, usually before a new game void clear(); - // Called when the program receives the UCI 'go' - // command. It searches from the root position and outputs the "bestmove". + // Called when the program receives the UCI 'go' command. + // It searches from the root position and outputs the "bestmove". void start_searching(); bool is_mainthread() const { return thread_idx == 0; } From a20726eb0b6c049e191ce0f04dac4f4f923efcee Mon Sep 17 00:00:00 2001 From: Disservin Date: Fri, 9 Feb 2024 17:56:58 +0100 Subject: [PATCH 1358/1766] Refactor the CI workflows This refactors the CI workflows to group some logic and makes sure that all (pre)release binaries are actually tested. The screenshot below shows the execution logic of the reworked ci, https://github.com/Disservin/Stockfish/actions/runs/7773581379. You can also hover over the cards to see the execution flow. The `matrix.json` and `arm_matrix.json` define the binaries which will be uploaded to GitHub. Afterwards a matrix is created and each job compiles a profile guided build for that arch and uploads that as an artifact to GitHub. The Binaries/ARM_Binaries workflow's are called when the previous step has been completed, and uploads all artifacts to the (pre)release. This also fixes some indentations and renames the workflows, see https://github.com/official-stockfish/Stockfish/actions, where every workflow is called `Stockfish` vs https://github.com/Disservin/Stockfish/actions. It also increases the parallel compilation used for make from `-j2 to -j4`. It now also prevents the prerelease action from running on forks. A test release can be viewed here https://github.com/Disservin/Stockfish/releases. closes https://github.com/official-stockfish/Stockfish/pull/5035 No functional change --- .github/ci/arm_matrix.json | 51 ++++ .github/{workflows => ci}/libcxx17.imp | 0 .github/ci/matrix.json | 160 +++++++++++ .github/workflows/arm_compilation.yml | 94 +++++++ ...fish_format_check.yml => clang-format.yml} | 22 +- .github/workflows/codeql.yml | 54 ++-- .github/workflows/compilation.yml | 89 +++++++ .../{stockfish_analyzers.yml => iwyu.yml} | 4 +- ...tockfish_sanitizers.yml => sanitizers.yml} | 7 +- .github/workflows/stockfish.yml | 60 +++-- .github/workflows/stockfish_arm_binaries.yml | 170 ------------ .github/workflows/stockfish_binaries.yml | 252 ------------------ .github/workflows/stockfish_compile_test.yml | 117 -------- .../{stockfish_test.yml => tests.yml} | 49 ++-- .github/workflows/upload_binaries.yml | 105 ++++++++ 15 files changed, 612 insertions(+), 622 deletions(-) create mode 100644 .github/ci/arm_matrix.json rename .github/{workflows => ci}/libcxx17.imp (100%) create mode 100644 .github/ci/matrix.json create mode 100644 .github/workflows/arm_compilation.yml rename .github/workflows/{stockfish_format_check.yml => clang-format.yml} (84%) create mode 100644 .github/workflows/compilation.yml rename .github/workflows/{stockfish_analyzers.yml => iwyu.yml} (92%) rename .github/workflows/{stockfish_sanitizers.yml => sanitizers.yml} (93%) delete mode 100644 .github/workflows/stockfish_arm_binaries.yml delete mode 100644 .github/workflows/stockfish_binaries.yml delete mode 100644 .github/workflows/stockfish_compile_test.yml rename .github/workflows/{stockfish_test.yml => tests.yml} (91%) create mode 100644 .github/workflows/upload_binaries.yml diff --git a/.github/ci/arm_matrix.json b/.github/ci/arm_matrix.json new file mode 100644 index 00000000000..70f2efaa21e --- /dev/null +++ b/.github/ci/arm_matrix.json @@ -0,0 +1,51 @@ +{ + "config": [ + { + "name": "Android NDK aarch64", + "os": "ubuntu-22.04", + "simple_name": "android", + "compiler": "aarch64-linux-android21-clang++", + "emu": "qemu-aarch64", + "comp": "ndk", + "shell": "bash", + "archive_ext": "tar" + }, + { + "name": "Android NDK arm", + "os": "ubuntu-22.04", + "simple_name": "android", + "compiler": "armv7a-linux-androideabi21-clang++", + "emu": "qemu-arm", + "comp": "ndk", + "shell": "bash", + "archive_ext": "tar" + } + ], + "binaries": ["armv8-dotprod", "armv8", "armv7", "armv7-neon"], + "exclude": [ + { + "binaries": "armv8-dotprod", + "config": { + "compiler": "armv7a-linux-androideabi21-clang++" + } + }, + { + "binaries": "armv8", + "config": { + "compiler": "armv7a-linux-androideabi21-clang++" + } + }, + { + "binaries": "armv7", + "config": { + "compiler": "aarch64-linux-android21-clang++" + } + }, + { + "binaries": "armv7-neon", + "config": { + "compiler": "aarch64-linux-android21-clang++" + } + } + ] +} diff --git a/.github/workflows/libcxx17.imp b/.github/ci/libcxx17.imp similarity index 100% rename from .github/workflows/libcxx17.imp rename to .github/ci/libcxx17.imp diff --git a/.github/ci/matrix.json b/.github/ci/matrix.json new file mode 100644 index 00000000000..c6563eadf2f --- /dev/null +++ b/.github/ci/matrix.json @@ -0,0 +1,160 @@ +{ + "config": [ + { + "name": "Ubuntu 20.04 GCC", + "os": "ubuntu-20.04", + "simple_name": "ubuntu", + "compiler": "g++", + "comp": "gcc", + "shell": "bash", + "archive_ext": "tar", + "sde": "/home/runner/work/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.27.0-2023-09-13-lin/sde -future --" + }, + { + "name": "MacOS 13 Apple Clang", + "os": "macos-13", + "simple_name": "macos", + "compiler": "clang++", + "comp": "clang", + "shell": "bash", + "archive_ext": "tar" + }, + { + "name": "MacOS 14 Apple Clang M1", + "os": "macos-14", + "simple_name": "macos-m1", + "compiler": "clang++", + "comp": "clang", + "shell": "bash", + "archive_ext": "tar" + }, + { + "name": "Windows 2022 Mingw-w64 GCC x86_64", + "os": "windows-2022", + "simple_name": "windows", + "compiler": "g++", + "comp": "mingw", + "msys_sys": "mingw64", + "msys_env": "x86_64-gcc", + "shell": "msys2 {0}", + "ext": ".exe", + "sde": "/d/a/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.27.0-2023-09-13-win/sde.exe -future --", + "archive_ext": "zip" + } + ], + "binaries": [ + "x86-64", + "x86-64-sse41-popcnt", + "x86-64-avx2", + "x86-64-bmi2", + "x86-64-avxvnni", + "x86-64-avx512", + "x86-64-vnni256", + "x86-64-vnni512", + "apple-silicon" + ], + "exclude": [ + { + "binaries": "x86-64", + "config": { + "os": "macos-14" + } + }, + { + "binaries": "x86-64-sse41-popcnt", + "config": { + "os": "macos-14" + } + }, + { + "binaries": "x86-64-avx2", + "config": { + "os": "macos-14" + } + }, + { + "binaries": "x86-64-bmi2", + "config": { + "os": "macos-14" + } + }, + { + "binaries": "x86-64-avxvnni", + "config": { + "os": "macos-14" + } + }, + { + "binaries": "x86-64-avxvnni", + "config": { + "os": "macos-14" + } + }, + { + "binaries": "x86-64-avx512", + "config": { + "os": "macos-14" + } + }, + { + "binaries": "x86-64-vnni256", + "config": { + "os": "macos-14" + } + }, + { + "binaries": "x86-64-vnni512", + "config": { + "os": "macos-14" + } + }, + { + "binaries": "x86-64-avxvnni", + "config": { + "ubuntu-20.04": null + } + }, + { + "binaries": "x86-64-avxvnni", + "config": { + "os": "macos-13" + } + }, + { + "binaries": "x86-64-avx512", + "config": { + "os": "macos-13" + } + }, + { + "binaries": "x86-64-vnni256", + "config": { + "os": "macos-13" + } + }, + { + "binaries": "x86-64-vnni512", + "config": { + "os": "macos-13" + } + }, + { + "binaries": "apple-silicon", + "config": { + "os": "windows-2022" + } + }, + { + "binaries": "apple-silicon", + "config": { + "os": "macos-13" + } + }, + { + "binaries": "apple-silicon", + "config": { + "os": "ubuntu-20.04" + } + } + ] +} diff --git a/.github/workflows/arm_compilation.yml b/.github/workflows/arm_compilation.yml new file mode 100644 index 00000000000..ef141971d2b --- /dev/null +++ b/.github/workflows/arm_compilation.yml @@ -0,0 +1,94 @@ +name: Compilation +on: + workflow_call: + inputs: + matrix: + type: string + required: true +jobs: + Compilation: + name: ${{ matrix.config.name }} ${{ matrix.binaries }} + runs-on: ${{ matrix.config.os }} + env: + COMPILER: ${{ matrix.config.compiler }} + COMP: ${{ matrix.config.comp }} + EMU: ${{ matrix.config.emu }} + EXT: ${{ matrix.config.ext }} + BINARY: ${{ matrix.binaries }} + strategy: + fail-fast: false + matrix: ${{ fromJson(inputs.matrix) }} + defaults: + run: + working-directory: src + shell: ${{ matrix.config.shell }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Download required linux packages + if: runner.os == 'Linux' + run: | + sudo apt update + sudo apt install qemu-user + + - name: Install NDK + if: runner.os == 'Linux' + run: | + if [ $COMP == ndk ]; then + NDKV="21.4.7075529" + ANDROID_ROOT=/usr/local/lib/android + ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk + SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager + echo "y" | $SDKMANAGER "ndk;$NDKV" + ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/$NDKV + ANDROID_NDK_BIN=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin + echo "ANDROID_NDK_BIN=$ANDROID_NDK_BIN" >> $GITHUB_ENV + fi + + - name: Extract the bench number from the commit history + run: | + for hash in $(git rev-list -100 HEAD); do + benchref=$(git show -s $hash | tac | grep -m 1 -o -x '[[:space:]]*\b[Bb]ench[ :]\+[1-9][0-9]\{5,7\}\b[[:space:]]*' | sed 's/[^0-9]//g') && break || true + done + [[ -n "$benchref" ]] && echo "benchref=$benchref" >> $GITHUB_ENV && echo "From commit: $hash" && echo "Reference bench: $benchref" || echo "No bench found" + + - name: Download the used network from the fishtest framework + run: make net + + - name: Check compiler + run: | + if [ $COMP == ndk ]; then + export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH + fi + $COMPILER -v + + - name: Test help target + run: make help + + - name: Check git + run: git --version + + # Compile profile guided builds + + - name: Compile ${{ matrix.binaries }} build + run: | + if [ $COMP == ndk ]; then + export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH + export LDFLAGS="-static -Wno-unused-command-line-argument" + fi + make clean + make -j4 profile-build ARCH=$BINARY COMP=$COMP WINE_PATH=$EMU + make strip ARCH=$BINARY COMP=$COMP + WINE_PATH=$EMU ../tests/signature.sh $benchref + mv ./stockfish$EXT ../stockfish-android-$BINARY$EXT + + - name: Remove non src files + run: git clean -fx + + - name: Upload artifact for (pre)-release + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.config.simple_name }} ${{ matrix.binaries }} + path: . diff --git a/.github/workflows/stockfish_format_check.yml b/.github/workflows/clang-format.yml similarity index 84% rename from .github/workflows/stockfish_format_check.yml rename to .github/workflows/clang-format.yml index 7a47ab6f406..0eb3fc70dab 100644 --- a/.github/workflows/stockfish_format_check.yml +++ b/.github/workflows/clang-format.yml @@ -3,17 +3,17 @@ # executes no shell script nor runs make. # Read this before editing: https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ -name: Stockfish +name: Clang-Format on: pull_request_target: branches: - - 'master' + - "master" paths: - - '**.cpp' - - '**.h' + - "**.cpp" + - "**.h" jobs: - Stockfish: - name: clang-format check + Clang-Format: + name: Clang-Format runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v4 @@ -21,16 +21,16 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} - name: Run clang-format style check - uses: jidicula/clang-format-action@f62da5e3d3a2d88ff364771d9d938773a618ab5e # @v4.11.0 + uses: jidicula/clang-format-action@f62da5e3d3a2d88ff364771d9d938773a618ab5e # @v4.11.0 id: clang-format continue-on-error: true with: - clang-format-version: '17' - exclude-regex: 'incbin' + clang-format-version: "17" + exclude-regex: "incbin" - name: Comment on PR if: steps.clang-format.outcome == 'failure' - uses: thollander/actions-comment-pull-request@1d3973dc4b8e1399c0620d3f2b1aa5e795465308 # @v2.4.3 + uses: thollander/actions-comment-pull-request@1d3973dc4b8e1399c0620d3f2b1aa5e795465308 # @v2.4.3 with: message: | clang-format 17 needs to be run on this PR. @@ -42,7 +42,7 @@ jobs: - name: Comment on PR if: steps.clang-format.outcome != 'failure' - uses: thollander/actions-comment-pull-request@1d3973dc4b8e1399c0620d3f2b1aa5e795465308 # @v2.4.3 + uses: thollander/actions-comment-pull-request@1d3973dc4b8e1399c0620d3f2b1aa5e795465308 # @v2.4.3 with: message: | _(execution **${{ github.run_id }}** / attempt **${{ github.run_attempt }}**)_ diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index d6da8a1c288..1c3a3a6b468 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -2,12 +2,12 @@ name: "CodeQL" on: push: - branches: [ 'master' ] + branches: ["master"] pull_request: # The branches below must be a subset of the branches above - branches: [ 'master' ] + branches: ["master"] schedule: - - cron: '17 18 * * 1' + - cron: "17 18 * * 1" jobs: analyze: @@ -21,33 +21,33 @@ jobs: strategy: fail-fast: false matrix: - language: [ 'cpp' ] + language: ["cpp"] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] # Use only 'java' to analyze code written in Java, Kotlin, or both # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support steps: - - name: Checkout repository - uses: actions/checkout@v4 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - - # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - - - name: Build - working-directory: src - run: make -j build ARCH=x86-64-modern - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 - with: - category: "/language:${{matrix.language}}" + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + - name: Build + working-directory: src + run: make -j build ARCH=x86-64-modern + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/compilation.yml b/.github/workflows/compilation.yml new file mode 100644 index 00000000000..964b5f05edc --- /dev/null +++ b/.github/workflows/compilation.yml @@ -0,0 +1,89 @@ +name: Compilation +on: + workflow_call: + inputs: + matrix: + type: string + required: true +jobs: + Compilation: + name: ${{ matrix.config.name }} ${{ matrix.binaries }} + runs-on: ${{ matrix.config.os }} + env: + COMPILER: ${{ matrix.config.compiler }} + COMP: ${{ matrix.config.comp }} + EXT: ${{ matrix.config.ext }} + NAME: ${{ matrix.config.simple_name }} + BINARY: ${{ matrix.binaries }} + SDE: ${{ matrix.config.sde }} + strategy: + fail-fast: false + matrix: ${{ fromJson(inputs.matrix) }} + defaults: + run: + working-directory: src + shell: ${{ matrix.config.shell }} + steps: + - uses: actions/checkout@v4 + + - name: Install fixed GCC on Linux + if: runner.os == 'Linux' + uses: egor-tensin/setup-gcc@eaa888eb19115a521fa72b65cd94fe1f25bbcaac # @v1.3 + with: + version: 11 + + - name: Setup msys and install required packages + if: runner.os == 'Windows' + uses: msys2/setup-msys2@v2 + with: + msystem: ${{ matrix.config.msys_sys }} + install: mingw-w64-${{ matrix.config.msys_env }} make git zip + + - name: Download SDE package + if: runner.os == 'Linux' || runner.os == 'Windows' + uses: petarpetrovt/setup-sde@91a1a03434384e064706634125a15f7446d2aafb # @v2.3 + with: + environmentVariableName: SDE_DIR + sdeVersion: 9.27.0 + + - name: Download the used network from the fishtest framework + run: make net + + - name: Check compiler + run: $COMPILER -v + + - name: Test help target + run: make help + + - name: Check git + run: git --version + + - name: Check compiler + run: $COMPILER -v + + - name: Show g++ cpu info + if: runner.os != 'macOS' + run: g++ -Q -march=native --help=target + + - name: Show clang++ cpu info + if: runner.os == 'macOS' + run: clang++ -E - -march=native -### + + # x86-64 with newer extensions tests + + - name: Compile ${{ matrix.config.binaries }} build + run: | + make clean + make -j4 profile-build ARCH=$BINARY COMP=$COMP WINE_PATH="$SDE" + make strip ARCH=$BINARY COMP=$COMP + WINE_PATH="$SDE" ../tests/signature.sh $benchref + mv ./stockfish$EXT ../stockfish-$NAME-$BINARY$EXT + + - name: Remove non src files + run: git clean -fx + + - name: Upload artifact for (pre)-release + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.config.simple_name }} ${{ matrix.binaries }} + path: . diff --git a/.github/workflows/stockfish_analyzers.yml b/.github/workflows/iwyu.yml similarity index 92% rename from .github/workflows/stockfish_analyzers.yml rename to .github/workflows/iwyu.yml index f54cdd7ca6b..0552a598c8f 100644 --- a/.github/workflows/stockfish_analyzers.yml +++ b/.github/workflows/iwyu.yml @@ -1,4 +1,4 @@ -name: Stockfish +name: IWYU on: workflow_call: jobs: @@ -44,4 +44,4 @@ jobs: make analyze COMP=clang CXX=include-what-you-use - CXXFLAGS="-stdlib=libc++ -Xiwyu --comment_style=long -Xiwyu --mapping='${{ github.workspace }}/Stockfish/.github/workflows/libcxx17.imp' -Xiwyu --error" + CXXFLAGS="-stdlib=libc++ -Xiwyu --comment_style=long -Xiwyu --mapping='${{ github.workspace }}/Stockfish/.github/ci/libcxx17.imp' -Xiwyu --error" diff --git a/.github/workflows/stockfish_sanitizers.yml b/.github/workflows/sanitizers.yml similarity index 93% rename from .github/workflows/stockfish_sanitizers.yml rename to .github/workflows/sanitizers.yml index e3f046178f5..7ab1f997ad7 100644 --- a/.github/workflows/stockfish_sanitizers.yml +++ b/.github/workflows/sanitizers.yml @@ -1,8 +1,8 @@ -name: Stockfish +name: Sanitizers on: workflow_call: jobs: - Stockfish: + Test-under-sanitizers: name: ${{ matrix.sanitizers.name }} runs-on: ${{ matrix.config.os }} env: @@ -10,6 +10,7 @@ jobs: COMP: ${{ matrix.config.comp }} CXXFLAGS: "-Werror" strategy: + fail-fast: false matrix: config: - name: Ubuntu 22.04 GCC @@ -60,5 +61,5 @@ jobs: run: | export CXXFLAGS="-O1 -fno-inline" make clean - make -j2 ARCH=x86-64-sse41-popcnt ${{ matrix.sanitizers.make_option }} debug=yes optimize=no build > /dev/null + make -j4 ARCH=x86-64-sse41-popcnt ${{ matrix.sanitizers.make_option }} debug=yes optimize=no build > /dev/null ../tests/instrumented.sh --${{ matrix.sanitizers.instrumented_option }} diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index e8db52351dd..1c0dd0e1336 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -1,8 +1,8 @@ name: Stockfish on: push: - tags: - - '*' + tags: + - "*" branches: - master - tools @@ -13,7 +13,7 @@ on: - tools jobs: Prerelease: - if: github.ref == 'refs/heads/master' + if: github.repository == 'official-stockfish/Stockfish' && (github.ref == 'refs/heads/master' || (startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag')) runs-on: ubuntu-latest steps: # returns null if no pre-release exists @@ -25,24 +25,52 @@ jobs: echo "COMMIT_SHA=$(jq -r 'map(select(.prerelease)) | first | .tag_name' <<< $(curl -s https://api.github.com/repos/${{ github.repository_owner }}/Stockfish/releases))" >> $GITHUB_ENV - # delete old previous pre-release and tag - - uses: dev-drprasad/delete-tag-and-release@8cd619d00037e4aeb781909c9a6b03940507d0da # @v1.0.1 + # delete old previous pre-release and tag + - uses: dev-drprasad/delete-tag-and-release@8cd619d00037e4aeb781909c9a6b03940507d0da # @v1.0.1 if: env.COMMIT_SHA != 'null' with: tag_name: ${{ env.COMMIT_SHA }} github_token: ${{ secrets.GITHUB_TOKEN }} - - Analyzers: - uses: ./.github/workflows/stockfish_analyzers.yml + Matrix: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + arm_matrix: ${{ steps.set-arm-matrix.outputs.arm_matrix }} + steps: + - uses: actions/checkout@v4 + - id: set-matrix + run: | + TASKS=$(echo $(cat .github/ci/matrix.json) ) + echo "MATRIX=$TASKS" >> $GITHUB_OUTPUT + - id: set-arm-matrix + run: | + TASKS_ARM=$(echo $(cat .github/ci/arm_matrix.json) ) + echo "ARM_MATRIX=$TASKS_ARM" >> $GITHUB_OUTPUT + Compilation: + needs: [Matrix] + uses: ./.github/workflows/compilation.yml + with: + matrix: ${{ needs.Matrix.outputs.matrix }} + ARMCompilation: + needs: [Matrix] + uses: ./.github/workflows/arm_compilation.yml + with: + matrix: ${{ needs.Matrix.outputs.arm_matrix }} + IWYU: + uses: ./.github/workflows/iwyu.yml Sanitizers: - uses: ./.github/workflows/stockfish_sanitizers.yml + uses: ./.github/workflows/sanitizers.yml Tests: - uses: ./.github/workflows/stockfish_test.yml - Compiles: - uses: ./.github/workflows/stockfish_compile_test.yml + uses: ./.github/workflows/tests.yml Binaries: - if: github.ref == 'refs/heads/master' || (startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag') - uses: ./.github/workflows/stockfish_binaries.yml + if: github.repository == 'official-stockfish/Stockfish' + needs: [Matrix, Prerelease, Compilation] + uses: ./.github/workflows/upload_binaries.yml + with: + matrix: ${{ needs.Matrix.outputs.matrix }} ARM_Binaries: - if: github.ref == 'refs/heads/master' || (startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag') - uses: ./.github/workflows/stockfish_arm_binaries.yml + if: github.repository == 'official-stockfish/Stockfish' + needs: [Matrix, Prerelease, ARMCompilation] + uses: ./.github/workflows/upload_binaries.yml + with: + matrix: ${{ needs.Matrix.outputs.arm_matrix }} diff --git a/.github/workflows/stockfish_arm_binaries.yml b/.github/workflows/stockfish_arm_binaries.yml deleted file mode 100644 index 203c00f2ce2..00000000000 --- a/.github/workflows/stockfish_arm_binaries.yml +++ /dev/null @@ -1,170 +0,0 @@ -name: Stockfish -on: - workflow_call: -jobs: - Stockfish: - name: ${{ matrix.config.name }} ${{ matrix.binaries }} - runs-on: ${{ matrix.config.os }} - env: - COMPILER: ${{ matrix.config.compiler }} - COMP: ${{ matrix.config.comp }} - EMU: ${{ matrix.config.emu }} - EXT: ${{ matrix.config.ext }} - OS: ${{ matrix.config.os }} - BINARY: ${{ matrix.binaries }} - strategy: - matrix: - config: - - name: Android NDK aarch64 - os: ubuntu-22.04 - compiler: aarch64-linux-android21-clang++ - emu: qemu-aarch64 - comp: ndk - shell: bash - - name: Android NDK arm - os: ubuntu-22.04 - compiler: armv7a-linux-androideabi21-clang++ - emu: qemu-arm - comp: ndk - shell: bash - binaries: - - armv8-dotprod - - armv8 - - armv7 - - armv7-neon - exclude: - - binaries: armv8-dotprod - config: {compiler: armv7a-linux-androideabi21-clang++} - - binaries: armv8 - config: {compiler: armv7a-linux-androideabi21-clang++} - - binaries: armv7 - config: {compiler: aarch64-linux-android21-clang++} - - binaries: armv7-neon - config: {compiler: aarch64-linux-android21-clang++} - defaults: - run: - working-directory: src - shell: ${{ matrix.config.shell }} - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Download required linux packages - if: runner.os == 'Linux' - run: | - sudo apt update - sudo apt install qemu-user - - - name: Install NDK - if: runner.os == 'Linux' - run: | - if [ $COMP == ndk ]; then - NDKV="21.4.7075529" - ANDROID_ROOT=/usr/local/lib/android - ANDROID_SDK_ROOT=$ANDROID_ROOT/sdk - SDKMANAGER=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager - echo "y" | $SDKMANAGER "ndk;$NDKV" - ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/$NDKV - ANDROID_NDK_BIN=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin - echo "ANDROID_NDK_BIN=$ANDROID_NDK_BIN" >> $GITHUB_ENV - fi - - - name: Extract the bench number from the commit history - run: | - for hash in $(git rev-list -100 HEAD); do - benchref=$(git show -s $hash | tac | grep -m 1 -o -x '[[:space:]]*\b[Bb]ench[ :]\+[1-9][0-9]\{5,7\}\b[[:space:]]*' | sed 's/[^0-9]//g') && break || true - done - [[ -n "$benchref" ]] && echo "benchref=$benchref" >> $GITHUB_ENV && echo "From commit: $hash" && echo "Reference bench: $benchref" || echo "No bench found" - - - name: Download the used network from the fishtest framework - run: make net - - - name: Check compiler - run: | - if [ $COMP == ndk ]; then - export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH - fi - $COMPILER -v - - - name: Test help target - run: make help - - - name: Check git - run: git --version - - # Compile profile guided builds - - - name: Compile ${{ matrix.binaries }} build - run: | - if [ $COMP == ndk ]; then - export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH - export LDFLAGS="-static -Wno-unused-command-line-argument" - fi - make clean - make -j2 profile-build ARCH=$BINARY COMP=$COMP WINE_PATH=$EMU - make strip ARCH=$BINARY COMP=$COMP - WINE_PATH=$EMU ../tests/signature.sh $benchref - mv ./stockfish$EXT ../stockfish-android-$BINARY$EXT - - - name: Remove non src files - run: rm -f *.o .depend *.nnue - - - name: Download wiki - run: | - git clone https://github.com/official-stockfish/Stockfish.wiki.git ../wiki - cd ../wiki - rm -rf .git - - - name: Create tar archive. - run: | - cd .. - mkdir stockfish - cp -r wiki stockfish/ - cp -r src stockfish/ - cp stockfish-android-$BINARY$EXT stockfish/ - cp "Top CPU Contributors.txt" stockfish/ - cp Copying.txt stockfish/ - cp AUTHORS stockfish/ - cp CITATION.cff stockfish/ - cp README.md stockfish/ - cp CONTRIBUTING.md stockfish/ - tar -cvf stockfish-android-$BINARY.tar stockfish - - - name: Upload binaries - uses: actions/upload-artifact@v3 - with: - name: stockfish-android-${{ matrix.binaries }} - path: stockfish-android-${{ matrix.binaries }}.tar - - - name: Release - if: startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag' - uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # @v1 - with: - files: stockfish-android-${{ matrix.binaries }}.tar - - - name: Get last commit sha - id: last_commit - run: echo "COMMIT_SHA=$(git rev-parse HEAD | cut -c 1-8)" >> $GITHUB_ENV - - - name: Get commit date - id: commit_date - run: echo "COMMIT_DATE=$(git show -s --date=format:'%Y%m%d' --format=%cd HEAD)" >> $GITHUB_ENV - - # Make sure that an old ci which still runs on master doesn't recreate a prerelease - - name: Check Pullable Commits - id: check_commits - run: | - git fetch - CHANGES=$(git rev-list HEAD..origin/master --count) - echo "CHANGES=$CHANGES" >> $GITHUB_ENV - - - name: Prerelease - if: github.ref_name == 'master' && env.CHANGES == '0' - continue-on-error: true - uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # @v1 - with: - name: Stockfish dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} - tag_name: stockfish-dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} - prerelease: true - files: stockfish-android-${{ matrix.binaries }}.tar diff --git a/.github/workflows/stockfish_binaries.yml b/.github/workflows/stockfish_binaries.yml deleted file mode 100644 index 2911badacf7..00000000000 --- a/.github/workflows/stockfish_binaries.yml +++ /dev/null @@ -1,252 +0,0 @@ -name: Stockfish -on: - workflow_call: -jobs: - Stockfish: - name: ${{ matrix.config.name }} ${{ matrix.binaries }} - runs-on: ${{ matrix.config.os }} - env: - COMPILER: ${{ matrix.config.compiler }} - COMP: ${{ matrix.config.comp }} - EXT: ${{ matrix.config.ext }} - SDE: ${{ matrix.config.sde }} - NAME: ${{ matrix.config.simple_name }} - BINARY: ${{ matrix.binaries }} - strategy: - fail-fast: false - matrix: - config: - - name: Ubuntu 20.04 GCC - os: ubuntu-20.04 - simple_name: ubuntu - compiler: g++ - comp: gcc - shell: bash - archive_ext: tar - sde: /home/runner/work/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.27.0-2023-09-13-lin/sde -future -- - - name: MacOS 13 Apple Clang - os: macos-13 - simple_name: macos - compiler: clang++ - comp: clang - shell: bash - archive_ext: tar - - name: MacOS 14 Apple Clang M1 - os: macos-14 - simple_name: macos-m1 - compiler: clang++ - comp: clang - shell: bash - archive_ext: tar - - name: Windows 2022 Mingw-w64 GCC x86_64 - os: windows-2022 - simple_name: windows - compiler: g++ - comp: mingw - msys_sys: mingw64 - msys_env: x86_64-gcc - shell: msys2 {0} - ext: .exe - sde: /d/a/Stockfish/Stockfish/.output/sde-temp-files/sde-external-9.27.0-2023-09-13-win/sde.exe -future -- - archive_ext: zip - binaries: - - x86-64 - - x86-64-sse41-popcnt - - x86-64-avx2 - - x86-64-bmi2 - - x86-64-avxvnni - - x86-64-avx512 - - x86-64-vnni256 - - x86-64-vnni512 - - apple-silicon - exclude: - # Apple M1 - - binaries: x86-64 - config: { os: macos-14 } - - binaries: x86-64-sse41-popcnt - config: { os: macos-14 } - - binaries: x86-64-avx2 - config: { os: macos-14 } - - binaries: x86-64-bmi2 - config: { os: macos-14 } - - binaries: x86-64-avxvnni - config: { os: macos-14 } - - binaries: x86-64-avxvnni - config: { os: macos-14 } - - binaries: x86-64-avx512 - config: { os: macos-14 } - - binaries: x86-64-vnni256 - config: { os: macos-14 } - - binaries: x86-64-vnni512 - config: { os: macos-14 } - - - binaries: x86-64-avxvnni - config: { ubuntu-20.04 } - - # Apple x86_64 (no sde) - - binaries: x86-64-avxvnni - config: { os: macos-13 } - - binaries: x86-64-avx512 - config: { os: macos-13 } - - binaries: x86-64-vnni256 - config: { os: macos-13 } - - binaries: x86-64-vnni512 - config: { os: macos-13 } - - # Apple silicon from windows, macos-13 and ubuntu - - binaries: apple-silicon - config: { os: windows-2022 } - - binaries: apple-silicon - config: { os: macos-13 } - - binaries: apple-silicon - config: { os: ubuntu-20.04 } - defaults: - run: - working-directory: src - shell: ${{ matrix.config.shell }} - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Download required macOS packages - if: runner.os == 'macOS' - run: brew install coreutils - - - name: Install fixed GCC on Linux - if: runner.os == 'Linux' - uses: egor-tensin/setup-gcc@eaa888eb19115a521fa72b65cd94fe1f25bbcaac # @v1.3 - with: - version: 11 - - - name: Setup msys and install required packages - if: runner.os == 'Windows' - uses: msys2/setup-msys2@v2 - with: - msystem: ${{ matrix.config.msys_sys }} - install: mingw-w64-${{ matrix.config.msys_env }} make git zip - - - name: Download SDE package - if: runner.os == 'Linux' || runner.os == 'Windows' - uses: petarpetrovt/setup-sde@91a1a03434384e064706634125a15f7446d2aafb # @v2.3 - with: - environmentVariableName: SDE_DIR - sdeVersion: 9.27.0 - - - name: Download the used network from the fishtest framework - run: make net - - - name: Extract the bench number from the commit history - run: | - for hash in $(git rev-list -100 HEAD); do - benchref=$(git show -s $hash | tac | grep -m 1 -o -x '[[:space:]]*\b[Bb]ench[ :]\+[1-9][0-9]\{5,7\}\b[[:space:]]*' | sed 's/[^0-9]//g') && break || true - done - [[ -n "$benchref" ]] && echo "benchref=$benchref" >> $GITHUB_ENV && echo "From commit: $hash" && echo "Reference bench: $benchref" || echo "No bench found" - - - name: Check compiler - run: $COMPILER -v - - - name: Show g++ cpu info - if: runner.os != 'macOS' - run: g++ -Q -march=native --help=target - - - name: Show clang++ cpu info - if: runner.os == 'macOS' - run: clang++ -E - -march=native -### - - - name: Test help target - run: make help - - - name: Check git - run: git --version - - # Compile profile guided builds - - - name: Compile ${{ matrix.binaries }} build - run: | - make -j2 profile-build ARCH=$BINARY COMP=$COMP WINE_PATH="$SDE" - make strip ARCH=$BINARY COMP=$COMP - WINE_PATH="$SDE" ../tests/signature.sh $benchref - mv ./stockfish$EXT ../stockfish-$NAME-$BINARY$EXT - - - name: Remove non src files - run: git clean -fx - - - name: Download wiki - run: | - git clone https://github.com/official-stockfish/Stockfish.wiki.git ../wiki - rm -rf ../wiki/.git - - - name: Create directory. - run: | - cd .. - mkdir stockfish - cp -r wiki stockfish/ - cp -r src stockfish/ - cp stockfish-$NAME-$BINARY$EXT stockfish/ - cp "Top CPU Contributors.txt" stockfish/ - cp Copying.txt stockfish/ - cp AUTHORS stockfish/ - cp CITATION.cff stockfish/ - cp README.md stockfish/ - cp CONTRIBUTING.md stockfish/ - - - name: Create tar - if: runner.os != 'Windows' - run: | - cd .. - tar -cvf stockfish-$NAME-$BINARY.tar stockfish - - - name: Create zip - if: runner.os == 'Windows' - run: | - cd .. - zip -r stockfish-$NAME-$BINARY.zip stockfish - - - name: Upload binaries - if: runner.os != 'Windows' - uses: actions/upload-artifact@v3 - with: - name: stockfish-${{ matrix.config.os }}-${{ matrix.binaries }} - path: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.tar - - # Artifacts automatically get zipped. - # To avoid double-zipping, we use the unzipped directory - - name: Upload binaries - if: runner.os == 'Windows' - uses: actions/upload-artifact@v3 - with: - name: stockfish-${{ matrix.config.os }}-${{ matrix.binaries }} - path: stockfish - - - name: Release - if: startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag' - uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # @v1 - with: - files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.${{ matrix.config.archive_ext }} - - - name: Get last commit sha - id: last_commit - run: echo "COMMIT_SHA=$(git rev-parse HEAD | cut -c 1-8)" >> $GITHUB_ENV - - - name: Get commit date - id: commit_date - run: echo "COMMIT_DATE=$(git show -s --date=format:'%Y%m%d' --format=%cd HEAD)" >> $GITHUB_ENV - - # Make sure that an old ci that still runs on master doesn't recreate a prerelease - - name: Check Pullable Commits - id: check_commits - run: | - git fetch - CHANGES=$(git rev-list HEAD..origin/master --count) - echo "CHANGES=$CHANGES" >> $GITHUB_ENV - - - name: Prerelease - if: github.ref_name == 'master' && env.CHANGES == '0' - continue-on-error: true - uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # @v1 - with: - name: Stockfish dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} - tag_name: stockfish-dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} - prerelease: true - files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.${{ matrix.config.archive_ext }} diff --git a/.github/workflows/stockfish_compile_test.yml b/.github/workflows/stockfish_compile_test.yml deleted file mode 100644 index a47fcb0fc31..00000000000 --- a/.github/workflows/stockfish_compile_test.yml +++ /dev/null @@ -1,117 +0,0 @@ -name: Stockfish -on: - workflow_call: -jobs: - Stockfish: - name: ${{ matrix.config.name }} - runs-on: ${{ matrix.config.os }} - env: - COMPILER: ${{ matrix.config.compiler }} - COMP: ${{ matrix.config.comp }} - strategy: - matrix: - config: - - name: Ubuntu 20.04 GCC - os: ubuntu-20.04 - compiler: g++ - comp: gcc - shell: bash - - name: Ubuntu 20.04 Clang - os: ubuntu-20.04 - compiler: clang++ - comp: clang - shell: bash - - name: MacOS 13 Apple Clang - os: macos-13 - compiler: clang++ - comp: clang - shell: bash - - name: MacOS 14 Apple Clang M1 - os: macos-14 - compiler: clang++ - comp: clang - shell: bash - m1: true - - name: MacOS 13 GCC 11 - os: macos-13 - compiler: g++-11 - comp: gcc - shell: bash - - name: Windows 2022 Mingw-w64 GCC x86_64 - os: windows-2022 - compiler: g++ - comp: mingw - msys_sys: mingw64 - msys_env: x86_64-gcc - shell: msys2 {0} - - name: Windows 2022 Mingw-w64 Clang x86_64 - os: windows-2022 - compiler: clang++ - comp: clang - msys_sys: clang64 - msys_env: clang-x86_64-clang - shell: msys2 {0} - - defaults: - run: - working-directory: src - shell: ${{ matrix.config.shell }} - steps: - - uses: actions/checkout@v4 - - - name: Setup msys and install required packages - if: runner.os == 'Windows' - uses: msys2/setup-msys2@v2 - with: - msystem: ${{matrix.config.msys_sys}} - install: mingw-w64-${{matrix.config.msys_env}} make git - - - name: Download the used network from the fishtest framework - run: make net - - - name: Check compiler - run: $COMPILER -v - - - name: Test help target - run: make help - - - name: Check git - run: git --version - - # x86-64 with newer extensions tests - - - name: Compile x86-64-avx2 build - if: ${{ ! matrix.config.m1 }} - run: | - make clean - make -j2 ARCH=x86-64-avx2 build - - - name: Compile x86-64-bmi2 build - if: ${{ ! matrix.config.m1 }} - run: | - make clean - make -j2 ARCH=x86-64-bmi2 build - - - name: Compile x86-64-avx512 build - if: ${{ ! matrix.config.m1 }} - run: | - make clean - make -j2 ARCH=x86-64-avx512 build - - - name: Compile x86-64-vnni512 build - if: ${{ ! matrix.config.m1 }} - run: | - make clean - make -j2 ARCH=x86-64-vnni512 build - - - name: Compile x86-64-vnni256 build - if: ${{ ! matrix.config.m1 }} - run: | - make clean - make -j2 ARCH=x86-64-vnni256 build - - - name: Compile apple-silicon build - if: matrix.config.m1 - run: | - make clean - make -j2 ARCH=apple-silicon build diff --git a/.github/workflows/stockfish_test.yml b/.github/workflows/tests.yml similarity index 91% rename from .github/workflows/stockfish_test.yml rename to .github/workflows/tests.yml index 867099ee58c..702e86e5f74 100644 --- a/.github/workflows/stockfish_test.yml +++ b/.github/workflows/tests.yml @@ -1,8 +1,8 @@ -name: Stockfish +name: Tests on: workflow_call: jobs: - Stockfish: + Test-Targets: name: ${{ matrix.config.name }} runs-on: ${{ matrix.config.os }} env: @@ -10,6 +10,7 @@ jobs: COMP: ${{ matrix.config.comp }} CXXFLAGS: "-Werror" strategy: + fail-fast: false matrix: config: - name: Ubuntu 20.04 GCC @@ -190,35 +191,35 @@ jobs: run: | export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" make clean - make -j2 ARCH=x86-32 optimize=no debug=yes build + make -j4 ARCH=x86-32 optimize=no debug=yes build ../tests/signature.sh $benchref - name: Test x86-32 build if: matrix.config.run_32bit_tests run: | make clean - make -j2 ARCH=x86-32 build + make -j4 ARCH=x86-32 build ../tests/signature.sh $benchref - name: Test x86-32-sse41-popcnt build if: matrix.config.run_32bit_tests run: | make clean - make -j2 ARCH=x86-32-sse41-popcnt build + make -j4 ARCH=x86-32-sse41-popcnt build ../tests/signature.sh $benchref - name: Test x86-32-sse2 build if: matrix.config.run_32bit_tests run: | make clean - make -j2 ARCH=x86-32-sse2 build + make -j4 ARCH=x86-32-sse2 build ../tests/signature.sh $benchref - name: Test general-32 build if: matrix.config.run_32bit_tests run: | make clean - make -j2 ARCH=general-32 build + make -j4 ARCH=general-32 build ../tests/signature.sh $benchref # x86-64 tests @@ -228,21 +229,21 @@ jobs: run: | export CXXFLAGS="-Werror -D_GLIBCXX_DEBUG" make clean - make -j2 ARCH=x86-64-avx2 optimize=no debug=yes build + make -j4 ARCH=x86-64-avx2 optimize=no debug=yes build ../tests/signature.sh $benchref - name: Test x86-64-bmi2 build if: matrix.config.run_64bit_tests run: | make clean - make -j2 ARCH=x86-64-bmi2 build + make -j4 ARCH=x86-64-bmi2 build ../tests/signature.sh $benchref - name: Test x86-64-avx2 build if: matrix.config.run_64bit_tests run: | make clean - make -j2 ARCH=x86-64-avx2 build + make -j4 ARCH=x86-64-avx2 build ../tests/signature.sh $benchref # Test a deprecated arch @@ -250,49 +251,49 @@ jobs: if: matrix.config.run_64bit_tests run: | make clean - make -j2 ARCH=x86-64-modern build + make -j4 ARCH=x86-64-modern build ../tests/signature.sh $benchref - name: Test x86-64-sse41-popcnt build if: matrix.config.run_64bit_tests run: | make clean - make -j2 ARCH=x86-64-sse41-popcnt build + make -j4 ARCH=x86-64-sse41-popcnt build ../tests/signature.sh $benchref - name: Test x86-64-ssse3 build if: matrix.config.run_64bit_tests run: | make clean - make -j2 ARCH=x86-64-ssse3 build + make -j4 ARCH=x86-64-ssse3 build ../tests/signature.sh $benchref - name: Test x86-64-sse3-popcnt build if: matrix.config.run_64bit_tests run: | make clean - make -j2 ARCH=x86-64-sse3-popcnt build + make -j4 ARCH=x86-64-sse3-popcnt build ../tests/signature.sh $benchref - name: Test x86-64 build if: matrix.config.run_64bit_tests run: | make clean - make -j2 ARCH=x86-64 build + make -j4 ARCH=x86-64 build ../tests/signature.sh $benchref - name: Test general-64 build if: matrix.config.run_64bit_tests run: | make clean - make -j2 ARCH=general-64 build + make -j4 ARCH=general-64 build ../tests/signature.sh $benchref - name: Test apple-silicon build if: matrix.config.run_m1_tests run: | make clean - make -j2 ARCH=apple-silicon build + make -j4 ARCH=apple-silicon build ../tests/signature.sh $benchref # armv8 tests @@ -303,7 +304,7 @@ jobs: export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH export LDFLAGS="-static -Wno-unused-command-line-argument" make clean - make -j2 ARCH=armv8 build + make -j4 ARCH=armv8 build ../tests/signature.sh $benchref - name: Test armv8-dotprod build @@ -312,7 +313,7 @@ jobs: export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH export LDFLAGS="-static -Wno-unused-command-line-argument" make clean - make -j2 ARCH=armv8-dotprod build + make -j4 ARCH=armv8-dotprod build ../tests/signature.sh $benchref # armv7 tests @@ -323,7 +324,7 @@ jobs: export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH export LDFLAGS="-static -Wno-unused-command-line-argument" make clean - make -j2 ARCH=armv7 build + make -j4 ARCH=armv7 build ../tests/signature.sh $benchref - name: Test armv7-neon build @@ -332,7 +333,7 @@ jobs: export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH export LDFLAGS="-static -Wno-unused-command-line-argument" make clean - make -j2 ARCH=armv7-neon build + make -j4 ARCH=armv7-neon build ../tests/signature.sh $benchref # riscv64 tests @@ -340,7 +341,7 @@ jobs: - name: Test riscv64 build if: matrix.config.run_riscv64_tests run: | - echo "export LDFLAGS='-static' && make clean && make -j2 ARCH=riscv64 build" > script.sh + echo "export LDFLAGS='-static' && make clean && make -j4 ARCH=riscv64 build" > script.sh docker run --rm --platform ${{ matrix.config.platform }} -v ${{ github.workspace }}/src:/app sf_builder ../tests/signature.sh $benchref @@ -349,7 +350,7 @@ jobs: - name: Test ppc64 build if: matrix.config.run_ppc64_tests run: | - echo "export LDFLAGS='-static' && make clean && make -j2 ARCH=ppc-64 build" > script.sh + echo "export LDFLAGS='-static' && make clean && make -j4 ARCH=ppc-64 build" > script.sh docker run --rm --platform ${{ matrix.config.platform }} -v ${{ github.workspace }}/src:/app sf_builder ../tests/signature.sh $benchref @@ -359,6 +360,6 @@ jobs: if: matrix.config.run_64bit_tests run: | make clean - make -j2 ARCH=x86-64-avx2 build + make -j4 ARCH=x86-64-avx2 build ../tests/perft.sh ../tests/reprosearch.sh diff --git a/.github/workflows/upload_binaries.yml b/.github/workflows/upload_binaries.yml new file mode 100644 index 00000000000..7c8f470b125 --- /dev/null +++ b/.github/workflows/upload_binaries.yml @@ -0,0 +1,105 @@ +name: Upload Binaries +on: + workflow_call: + inputs: + matrix: + type: string + required: true + +jobs: + Artifacts: + name: ${{ matrix.config.name }} ${{ matrix.binaries }} + runs-on: ${{ matrix.config.os }} + env: + COMPILER: ${{ matrix.config.compiler }} + COMP: ${{ matrix.config.comp }} + EXT: ${{ matrix.config.ext }} + NAME: ${{ matrix.config.simple_name }} + BINARY: ${{ matrix.binaries }} + SDE: ${{ matrix.config.sde }} + strategy: + fail-fast: false + matrix: ${{ fromJson(inputs.matrix) }} + defaults: + run: + shell: ${{ matrix.config.shell }} + steps: + - uses: actions/checkout@v4 + + - name: Download artifact from compilation + uses: actions/download-artifact@v4 + with: + name: ${{ matrix.config.simple_name }} ${{ matrix.binaries }} + path: ${{ matrix.config.simple_name }} ${{ matrix.binaries }} + + - name: Setup msys and install required packages + if: runner.os == 'Windows' + uses: msys2/setup-msys2@v2 + with: + msystem: ${{ matrix.config.msys_sys }} + install: mingw-w64-${{ matrix.config.msys_env }} make git zip + + - name: Create Package + run: | + mkdir stockfish + + - name: Download wiki + run: | + git clone https://github.com/official-stockfish/Stockfish.wiki.git wiki + rm -rf wiki/.git + mv wiki stockfish/ + + - name: Copy files + run: | + mv "${{ matrix.config.simple_name }} ${{ matrix.binaries }}" stockfish-workflow + cd stockfish-workflow + cp -r src ../stockfish/ + cp stockfish-$NAME-$BINARY$EXT ../stockfish/ + cp "Top CPU Contributors.txt" ../stockfish/ + cp Copying.txt ../stockfish/ + cp AUTHORS ../stockfish/ + cp CITATION.cff ../stockfish/ + cp README.md ../stockfish/ + cp CONTRIBUTING.md ../stockfish/ + + - name: Create tar + if: runner.os != 'Windows' + run: | + tar -cvf stockfish-$NAME-$BINARY.tar stockfish + + - name: Create zip + if: runner.os == 'Windows' + run: | + zip -r stockfish-$NAME-$BINARY.zip stockfish + + - name: Release + if: startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag' + uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # @v1 + with: + files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.${{ matrix.config.archive_ext }} + + - name: Get last commit sha + id: last_commit + run: echo "COMMIT_SHA=$(git rev-parse HEAD | cut -c 1-8)" >> $GITHUB_ENV + + - name: Get commit date + id: commit_date + run: echo "COMMIT_DATE=$(git show -s --date=format:'%Y%m%d' --format=%cd HEAD)" >> $GITHUB_ENV + + # Make sure that an old ci that still runs on master doesn't recreate a prerelease + - name: Check Pullable Commits + id: check_commits + run: | + git fetch + CHANGES=$(git rev-list HEAD..origin/master --count) + echo "CHANGES=$CHANGES" >> $GITHUB_ENV + + - name: Prerelease + if: github.ref_name == 'master' && env.CHANGES == '0' + continue-on-error: true + uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # @v1 + with: + name: Stockfish dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} + tag_name: stockfish-dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} + prerelease: true + files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.${{ matrix.config.archive_ext }} From f2984471c90d82284720f681314ff87bf5fd833c Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Thu, 1 Feb 2024 21:09:48 +0100 Subject: [PATCH 1359/1766] Tweak capture scoring for move ordering Move divisor from capture scoring to good capture check and sligthly increase it. This has several effects: - its a speedup because for quience and probcut search the division now never happens. For main search its delayed and can be avoided if a good capture triggers a cutoff - through the higher resolution of scores we have a more granular sorting STC: https://tests.stockfishchess.org/tests/view/65bf2a93c865510db027dc27 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 470016 W: 122150 L: 121173 D: 226693 Ptnml(0-2): 2133, 55705, 118374, 56644, 2152 LTC: https://tests.stockfishchess.org/tests/view/65c1d16dc865510db0281339 LLR: 2.96 (-2.94,2.94) <0.50,2.50> Total: 98988 W: 25121 L: 24667 D: 49200 Ptnml(0-2): 77, 10998, 26884, 11464, 71 closes https://github.com/official-stockfish/Stockfish/pull/5036 Bench: 1233867 --- src/movepick.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index b2638350de1..e86438c3fb8 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -167,9 +167,8 @@ void MovePicker::score() { for (auto& m : *this) if constexpr (Type == CAPTURES) m.value = - (7 * int(PieceValue[pos.piece_on(m.to_sq())]) - + (*captureHistory)[pos.moved_piece(m)][m.to_sq()][type_of(pos.piece_on(m.to_sq()))]) - / 16; + 7 * int(PieceValue[pos.piece_on(m.to_sq())]) + + (*captureHistory)[pos.moved_piece(m)][m.to_sq()][type_of(pos.piece_on(m.to_sq()))]; else if constexpr (Type == QUIETS) { @@ -269,7 +268,8 @@ Move MovePicker::next_move(bool skipQuiets) { case GOOD_CAPTURE : if (select([&]() { // Move losing capture to endBadCaptures to be tried later - return pos.see_ge(*cur, -cur->value) ? true : (*endBadCaptures++ = *cur, false); + return pos.see_ge(*cur, -cur->value / 18) ? true + : (*endBadCaptures++ = *cur, false); })) return *(cur - 1); From c0107b3c27e9542a3319e9af0396794b7b2df890 Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Tue, 6 Feb 2024 16:56:13 +0800 Subject: [PATCH 1360/1766] Remove simple eval With the recent introduction of the dual NNUE, the need for simple eval is no longer there. Passed STC: https://tests.stockfishchess.org/tests/view/65c1f735c865510db0281652 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 85312 W: 22009 L: 21837 D: 41466 Ptnml(0-2): 334, 10155, 21567, 10205, 395 Passed LTC: https://tests.stockfishchess.org/tests/view/65c2d64bc865510db0282810 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 49956 W: 12596 L: 12402 D: 24958 Ptnml(0-2): 28, 5553, 13624, 5743, 30 closes https://github.com/official-stockfish/Stockfish/pull/5037 Bench 1213676 --- src/evaluate.cpp | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 45658798c52..c8656fc2eac 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -199,33 +199,24 @@ Value Eval::evaluate(const Position& pos, int optimism) { assert(!pos.checkers()); - int v; - Color stm = pos.side_to_move(); - int shuffling = pos.rule50_count(); - int simpleEval = simple_eval(pos, stm); - - bool lazy = std::abs(simpleEval) > 2550; - if (lazy) - v = simpleEval; - else - { - bool smallNet = std::abs(simpleEval) > 1050; + int simpleEval = simple_eval(pos, pos.side_to_move()); + bool smallNet = std::abs(simpleEval) > 1050; - int nnueComplexity; + int nnueComplexity; - Value nnue = smallNet ? NNUE::evaluate(pos, true, &nnueComplexity) - : NNUE::evaluate(pos, true, &nnueComplexity); + Value nnue = smallNet ? NNUE::evaluate(pos, true, &nnueComplexity) + : NNUE::evaluate(pos, true, &nnueComplexity); - // Blend optimism and eval with nnue complexity and material imbalance - optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / 512; - nnue -= nnue * (nnueComplexity + std::abs(simpleEval - nnue)) / 32768; + // Blend optimism and eval with nnue complexity and material imbalance + optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / 512; + nnue -= nnue * (nnueComplexity + std::abs(simpleEval - nnue)) / 32768; - int npm = pos.non_pawn_material() / 64; - v = (nnue * (915 + npm + 9 * pos.count()) + optimism * (154 + npm)) / 1024; - } + int npm = pos.non_pawn_material() / 64; + int v = (nnue * (915 + npm + 9 * pos.count()) + optimism * (154 + npm)) / 1024; // Damp down the evaluation linearly when shuffling - v = v * (200 - shuffling) / 214; + int shuffling = pos.rule50_count(); + v = v * (200 - shuffling) / 214; // Guarantee evaluation does not hit the tablebase range v = std::clamp(int(v), VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); From 15093d43c413a9af4b8adfe4d7031f822c58fbf2 Mon Sep 17 00:00:00 2001 From: gahtan-syarif <155860115+gahtan-syarif@users.noreply.github.com> Date: Thu, 8 Feb 2024 04:20:45 +0700 Subject: [PATCH 1361/1766] Simplify opponent movecount reduction This removes the reduction decrease that occured when the previous ply had a movecount greater than 7. Passed STC: https://tests.stockfishchess.org/tests/view/65c3f6dac865510db0283ef6 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 11968 W: 3205 L: 2953 D: 5810 Ptnml(0-2): 38, 1310, 3064, 1506, 66 Passed LTC: https://tests.stockfishchess.org/tests/view/65c42377c865510db0284217 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 35676 W: 9113 L: 8905 D: 17658 Ptnml(0-2): 22, 3893, 9802, 4097, 24 closes https://github.com/official-stockfish/Stockfish/pull/5040 Bench: 1148379 --- AUTHORS | 1 + src/search.cpp | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index cc8edafa4aa..9b1faee7220 100644 --- a/AUTHORS +++ b/AUTHORS @@ -78,6 +78,7 @@ Fauzi Akram Dabat (fauzi2) Felix Wittmann gamander Gabriele Lombardo (gabe) +Gahtan Nahdi Gary Heckman (gheckman) George Sobala (gsobala) gguliash diff --git a/src/search.cpp b/src/search.cpp index 336678c0617..63e7699e3fb 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1108,10 +1108,6 @@ Value Search::Worker::search( if (ss->ttPv) r -= 1 + (ttValue > alpha) + (ttValue > beta && tte->depth() >= depth); - // Decrease reduction if opponent's move count is high (~1 Elo) - if ((ss - 1)->moveCount > 7) - r--; - // Increase reduction for cut nodes (~4 Elo) if (cutNode) r += 2 - (tte->depth() >= depth && ss->ttPv); From 96837bc4396d205536cdaabfc17e4885a48b0588 Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Wed, 7 Feb 2024 22:00:51 +0800 Subject: [PATCH 1362/1766] Remove check extension Passed simplification STC: https://tests.stockfishchess.org/tests/view/65c38d2ac865510db02836cf LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 52288 W: 13578 L: 13371 D: 25339 Ptnml(0-2): 197, 6171, 13265, 6250, 261 Passed simplification LTC: https://tests.stockfishchess.org/tests/view/65c4470ec865510db0284473 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 44958 W: 11255 L: 11055 D: 22648 Ptnml(0-2): 37, 4962, 12274, 5176, 30 closes https://github.com/official-stockfish/Stockfish/pull/5041 Bench: 1116591 --- src/search.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 63e7699e3fb..05afc9ce3dc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1071,10 +1071,6 @@ Value Search::Worker::search( extension = -1; } - // Check extensions (~1 Elo) - else if (givesCheck && depth > 10) - extension = 1; - // Quiet ttMove extensions (~1 Elo) else if (PvNode && move == ttMove && move == ss->killers[0] && (*contHist[0])[movedPiece][move.to_sq()] >= 4339) From 9699f4f79ae9bb193c1f74adf53886a1a56f8a91 Mon Sep 17 00:00:00 2001 From: mstembera Date: Thu, 8 Feb 2024 13:54:40 -0800 Subject: [PATCH 1363/1766] Fix the alignment of the transformer buffer Fixes the issue mentioned in https://github.com/official-stockfish/Stockfish/commit/584d9efedcde330eeb96a99215552ddfb06f52ba#r138417600. Thanks to @cj5716 and @peregrineshahin for spotting this! closes https://github.com/official-stockfish/Stockfish/pull/5042 No functional change --- src/nnue/evaluate_nnue.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index d4a4dbe4c45..5bd7e83d22f 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -197,11 +197,10 @@ Value evaluate(const Position& pos, bool adjusted, int* complexity) { constexpr int delta = 24; #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) - TransformedFeatureType - transformedFeaturesUnaligned[FeatureTransformer < Small ? TransformedFeatureDimensionsSmall - : TransformedFeatureDimensionsBig, - nullptr - > ::BufferSize + alignment / sizeof(TransformedFeatureType)]; + TransformedFeatureType transformedFeaturesUnaligned + [FeatureTransformer < Net_Size == Small ? TransformedFeatureDimensionsSmall + : TransformedFeatureDimensionsBig, + nullptr > ::BufferSize + alignment / sizeof(TransformedFeatureType)]; auto* transformedFeatures = align_ptr_up(&transformedFeaturesUnaligned[0]); #else From 21dff6c2765d075a4e109e5dda98b7bf5af2cec9 Mon Sep 17 00:00:00 2001 From: Disservin Date: Sat, 10 Feb 2024 11:32:10 +0100 Subject: [PATCH 1364/1766] Update CI actions - Update codeql to v3 - Switch from dev-drprasad to native github cli - Update softprops/action-gh-release to node 20 commit `thollander/actions-comment-pull-request` needs to be bumped to node20 too, but the author hasnt done so atm closes https://github.com/official-stockfish/Stockfish/pull/5044 No functional change --- .github/workflows/codeql.yml | 4 ++-- .github/workflows/stockfish.yml | 8 ++++---- .github/workflows/upload_binaries.yml | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 1c3a3a6b468..d949a5a7649 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -33,7 +33,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -48,6 +48,6 @@ jobs: run: make -j build ARCH=x86-64-modern - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index 1c0dd0e1336..22cd9af3fd2 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -26,11 +26,11 @@ jobs: echo "COMMIT_SHA=$(jq -r 'map(select(.prerelease)) | first | .tag_name' <<< $(curl -s https://api.github.com/repos/${{ github.repository_owner }}/Stockfish/releases))" >> $GITHUB_ENV # delete old previous pre-release and tag - - uses: dev-drprasad/delete-tag-and-release@8cd619d00037e4aeb781909c9a6b03940507d0da # @v1.0.1 + - uses: actions/checkout@v4 + - run: gh release delete ${{ env.COMMIT_SHA }} --cleanup-tag if: env.COMMIT_SHA != 'null' - with: - tag_name: ${{ env.COMMIT_SHA }} - github_token: ${{ secrets.GITHUB_TOKEN }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} Matrix: runs-on: ubuntu-latest outputs: diff --git a/.github/workflows/upload_binaries.yml b/.github/workflows/upload_binaries.yml index 7c8f470b125..97bcf96f219 100644 --- a/.github/workflows/upload_binaries.yml +++ b/.github/workflows/upload_binaries.yml @@ -74,7 +74,7 @@ jobs: - name: Release if: startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag' - uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # @v1 + uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981 with: files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.${{ matrix.config.archive_ext }} From 3d5b16df7c2125ba52a25b3d4b69fc1261c4eb80 Mon Sep 17 00:00:00 2001 From: GoldenRare Date: Tue, 6 Feb 2024 05:45:52 -0500 Subject: [PATCH 1365/1766] Remove unnecessary assignments related to adjusted static evaluation In both search and qsearch, there are instances where we do unadjustedStaticEval = ss->staticEval = eval/bestValue = tte->eval(), but immediately after re-assign ss-static and eval/bestValue to some new value, which makes the initial assignment redundant. closes https://github.com/official-stockfish/Stockfish/pull/5045 No functional change --- src/search.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 05afc9ce3dc..b186d8a9ec7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -701,9 +701,9 @@ Value Search::Worker::search( else if (ss->ttHit) { // Never assume anything about values stored in TT - unadjustedStaticEval = ss->staticEval = eval = tte->eval(); - if (eval == VALUE_NONE) - unadjustedStaticEval = ss->staticEval = eval = evaluate(pos, thisThread->optimism[us]); + unadjustedStaticEval = tte->eval(); + if (unadjustedStaticEval == VALUE_NONE) + unadjustedStaticEval = evaluate(pos, thisThread->optimism[us]); else if (PvNode) Eval::NNUE::hint_common_parent_position(pos); @@ -715,7 +715,7 @@ Value Search::Worker::search( } else { - unadjustedStaticEval = ss->staticEval = eval = evaluate(pos, thisThread->optimism[us]); + unadjustedStaticEval = evaluate(pos, thisThread->optimism[us]); ss->staticEval = eval = to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); // Static evaluation is saved as it was before adjustment by correction history @@ -1434,9 +1434,9 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, if (ss->ttHit) { // Never assume anything about values stored in TT - if ((unadjustedStaticEval = ss->staticEval = bestValue = tte->eval()) == VALUE_NONE) - unadjustedStaticEval = ss->staticEval = bestValue = - evaluate(pos, thisThread->optimism[us]); + unadjustedStaticEval = tte->eval(); + if (unadjustedStaticEval == VALUE_NONE) + unadjustedStaticEval = evaluate(pos, thisThread->optimism[us]); ss->staticEval = bestValue = to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); @@ -1448,10 +1448,10 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, else { // In case of null move search, use previous static eval with a different sign - unadjustedStaticEval = ss->staticEval = bestValue = - (ss - 1)->currentMove != Move::null() ? evaluate(pos, thisThread->optimism[us]) - : -(ss - 1)->staticEval; - ss->staticEval = bestValue = + unadjustedStaticEval = (ss - 1)->currentMove != Move::null() + ? evaluate(pos, thisThread->optimism[us]) + : -(ss - 1)->staticEval; + ss->staticEval = bestValue = to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); } From 9068fdc57bcaaa142938e18a529761de1063f786 Mon Sep 17 00:00:00 2001 From: Disservin Date: Sat, 10 Feb 2024 13:58:38 +0100 Subject: [PATCH 1366/1766] Assorted cleanups Assorted cleanups closes https://github.com/official-stockfish/Stockfish/pull/5046 No functional change Co-Authored-By: Shahin M. Shahin <41402573+peregrineshahin@users.noreply.github.com> Co-Authored-By: cj5716 <125858804+cj5716@users.noreply.github.com> --- src/bitboard.h | 3 --- src/evaluate.cpp | 2 +- src/movepick.cpp | 2 +- src/position.h | 2 -- src/search.cpp | 37 ++++++++++++++++--------------------- src/search.h | 18 +++++++----------- 6 files changed, 25 insertions(+), 39 deletions(-) diff --git a/src/bitboard.h b/src/bitboard.h index d028be02906..cdff4c759bc 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -163,7 +163,6 @@ inline Bitboard pawn_attacks_bb(Color c, Square s) { inline Bitboard line_bb(Square s1, Square s2) { assert(is_ok(s1) && is_ok(s2)); - return LineBB[s1][s2]; } @@ -178,7 +177,6 @@ inline Bitboard line_bb(Square s1, Square s2) { inline Bitboard between_bb(Square s1, Square s2) { assert(is_ok(s1) && is_ok(s2)); - return BetweenBB[s1][s2]; } @@ -216,7 +214,6 @@ template inline Bitboard attacks_bb(Square s) { assert((Pt != PAWN) && (is_ok(s))); - return PseudoAttacks[Pt][s]; } diff --git a/src/evaluate.cpp b/src/evaluate.cpp index c8656fc2eac..da0867660af 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -219,7 +219,7 @@ Value Eval::evaluate(const Position& pos, int optimism) { v = v * (200 - shuffling) / 214; // Guarantee evaluation does not hit the tablebase range - v = std::clamp(int(v), VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); + v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); return v; } diff --git a/src/movepick.cpp b/src/movepick.cpp index e86438c3fb8..33791922e4b 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -173,7 +173,7 @@ void MovePicker::score() { else if constexpr (Type == QUIETS) { Piece pc = pos.moved_piece(m); - PieceType pt = type_of(pos.moved_piece(m)); + PieceType pt = type_of(pc); Square from = m.from_sq(); Square to = m.to_sq(); diff --git a/src/position.h b/src/position.h index 7ce3556f0e9..154ed652942 100644 --- a/src/position.h +++ b/src/position.h @@ -252,13 +252,11 @@ inline CastlingRights Position::castling_rights(Color c) const { inline bool Position::castling_impeded(CastlingRights cr) const { assert(cr == WHITE_OO || cr == WHITE_OOO || cr == BLACK_OO || cr == BLACK_OOO); - return pieces() & castlingPath[cr]; } inline Square Position::castling_rook_square(CastlingRights cr) const { assert(cr == WHITE_OO || cr == WHITE_OOO || cr == BLACK_OO || cr == BLACK_OOO); - return castlingRookSquare[cr]; } diff --git a/src/search.cpp b/src/search.cpp index b186d8a9ec7..4e0d808e371 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -67,7 +67,7 @@ constexpr int futility_move_count(bool improving, Depth depth) { Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { auto cv = w.correctionHistory[pos.side_to_move()][pawn_structure_index(pos)]; v += cv * std::abs(cv) / 12890; - return std::clamp(int(v), VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); + return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } // History and stats update bonus, based on depth @@ -297,9 +297,9 @@ void Search::Worker::iterative_deepening() { // Reset aspiration window starting size Value avg = rootMoves[pvIdx].averageScore; - delta = Value(9) + int(avg) * avg / 12480; + delta = 9 + avg * avg / 12480; alpha = std::max(avg - delta, -VALUE_INFINITE); - beta = std::min(avg + delta, int(VALUE_INFINITE)); + beta = std::min(avg + delta, VALUE_INFINITE); // Adjust optimism based on root move's averageScore (~4 Elo) optimism[us] = 131 * avg / (std::abs(avg) + 95); @@ -350,7 +350,7 @@ void Search::Worker::iterative_deepening() { } else if (bestValue >= beta) { - beta = std::min(bestValue + delta, int(VALUE_INFINITE)); + beta = std::min(bestValue + delta, VALUE_INFINITE); ++failedHighCnt; } else @@ -481,7 +481,6 @@ void Search::Worker::clear() { for (auto& h : to) h->fill(-71); - for (size_t i = 1; i < reductions.size(); ++i) reductions[i] = int((20.37 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); } @@ -538,7 +537,7 @@ Value Search::Worker::search( // Check for the available remaining time if (is_mainthread()) - main_manager()->check_time(*this); + main_manager()->check_time(*thisThread); // Used to send selDepth info to GUI (selDepth counts from 1, ply from 0) if (PvNode && thisThread->selDepth < ss->ply + 1) @@ -680,10 +679,8 @@ Value Search::Worker::search( } } - - Value unadjustedStaticEval = VALUE_NONE; - // Step 6. Static evaluation of the position + Value unadjustedStaticEval = VALUE_NONE; if (ss->inCheck) { // Skip early pruning when in check @@ -820,11 +817,10 @@ Value Search::Worker::search( if (cutNode && depth >= 8 && !ttMove) depth -= 2; - probCutBeta = beta + 182 - 68 * improving; - // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search returns a value // much above beta, we can (almost) safely prune the previous move. + probCutBeta = beta + 182 - 68 * improving; if ( !PvNode && depth > 3 && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY @@ -1285,7 +1281,6 @@ Value Search::Worker::search( { if (capture) capturesSearched[captureCount++] = move; - else quietsSearched[quietCount++] = move; } @@ -1424,9 +1419,8 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, && (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER))) return ttValue; - Value unadjustedStaticEval = VALUE_NONE; - // Step 4. Static evaluation of the position + Value unadjustedStaticEval = VALUE_NONE; if (ss->inCheck) bestValue = futilityBase = -VALUE_INFINITE; else @@ -1521,7 +1515,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, // If static eval is much lower than alpha and move is not winning material // we can prune this move. (~2 Elo) - if (futilityBase <= alpha && !pos.see_ge(move, VALUE_ZERO + 1)) + if (futilityBase <= alpha && !pos.see_ge(move, 1)) { bestValue = std::max(bestValue, futilityBase); continue; @@ -1597,7 +1591,6 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, if (ss->inCheck && bestValue == -VALUE_INFINITE) { assert(!MoveList(pos).size()); - return mated_in(ss->ply); // Plies to mate from the root } @@ -1615,6 +1608,10 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, return bestValue; } +Depth Search::Worker::reduction(bool i, Depth d, int mn, int delta) { + int reductionScale = reductions[d] * reductions[mn]; + return (reductionScale + 1177 - delta * 776 / rootDelta) / 1024 + (!i && reductionScale > 842); +} namespace { // Adjusts a mate or TB score from "plies to mate from the root" @@ -1623,7 +1620,6 @@ namespace { Value value_to_tt(Value v, int ply) { assert(v != VALUE_NONE); - return v >= VALUE_TB_WIN_IN_MAX_PLY ? v + ply : v <= VALUE_TB_LOSS_IN_MAX_PLY ? v - ply : v; } @@ -1805,9 +1801,9 @@ Move Skill::pick_best(const RootMoves& rootMoves, size_t multiPV) { for (size_t i = 0; i < multiPV; ++i) { // This is our magic formula - int push = int((weakness * int(topScore - rootMoves[i].score) - + delta * (rng.rand() % int(weakness))) - / 128); + int push = (weakness * int(topScore - rootMoves[i].score) + + delta * (rng.rand() % int(weakness))) + / 128; if (rootMoves[i].score + push >= maxScore) { @@ -1921,7 +1917,6 @@ bool RootMove::extract_ponder_from_tt(const TranspositionTable& tt, Position& po bool ttHit; assert(pv.size() == 1); - if (pv[0] == Move::none()) return false; diff --git a/src/search.h b/src/search.h index 97cb2ca40b2..2d1077f825e 100644 --- a/src/search.h +++ b/src/search.h @@ -47,7 +47,6 @@ enum NodeType { class TranspositionTable; class ThreadPool; class OptionsMap; -class UCI; namespace Search { @@ -124,10 +123,12 @@ struct LimitsType { // The UCI stores the uci options, thread pool, and transposition table. // This struct is used to easily forward data to the Search::Worker class. struct SharedState { - SharedState(const OptionsMap& o, ThreadPool& tp, TranspositionTable& t) : - options(o), - threads(tp), - tt(t) {} + SharedState(const OptionsMap& optionsMap, + ThreadPool& threadPool, + TranspositionTable& transpositionTable) : + options(optionsMap), + threads(threadPool), + tt(transpositionTable) {} const OptionsMap& options; ThreadPool& threads; @@ -209,11 +210,7 @@ class Worker { template Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth = 0); - Depth reduction(bool i, Depth d, int mn, int delta) { - int reductionScale = reductions[d] * reductions[mn]; - return (reductionScale + 1177 - int(delta) * 776 / int(rootDelta)) / 1024 - + (!i && reductionScale > 842); - } + Depth reduction(bool i, Depth d, int mn, int delta); // Get a pointer to the search manager, only allowed to be called by the // main thread. @@ -251,7 +248,6 @@ class Worker { TranspositionTable& tt; friend class Stockfish::ThreadPool; - friend class Stockfish::UCI; friend class SearchManager; }; From 91a4cea437fc0ae177808dd0a9ef791f38229c7b Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 10 Feb 2024 20:17:21 +0300 Subject: [PATCH 1367/1766] Adjust best value in main search depending on depth This patch does similar thing to how it's done for qsearch - in case of fail high adjust result to lower value. Difference is that it is done only for non-pv nodes and it's depth dependent - so lower depth entries will have bigger adjustment and higher depth entries will have smaller adjustment. Passed STC: https://tests.stockfishchess.org/tests/view/65c3c0cbc865510db0283b21 LLR: 2.96 (-2.94,2.94) <0.00,2.00> Total: 112032 W: 29142 L: 28705 D: 54185 Ptnml(0-2): 479, 13152, 28326, 13571, 488 Passed LTC: https://tests.stockfishchess.org/tests/view/65c52e62c865510db02855d5 LLR: 2.96 (-2.94,2.94) <0.50,2.50> Total: 132480 W: 33457 L: 32936 D: 66087 Ptnml(0-2): 67, 14697, 36222, 15156, 98 closes https://github.com/official-stockfish/Stockfish/pull/5047 Bench: 1168241 --- src/search.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 4e0d808e371..7a60e9731ee 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1293,6 +1293,11 @@ Value Search::Worker::search( assert(moveCount || !ss->inCheck || excludedMove || !MoveList(pos).size()); + // Adjust best value for fail high cases at non-pv nodes + if (!PvNode && bestValue >= beta && std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY && + std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY && std::abs(alpha) < VALUE_TB_WIN_IN_MAX_PLY) + bestValue = (bestValue * (depth + 2) + beta) / (depth + 3); + if (!moveCount) bestValue = excludedMove ? alpha : ss->inCheck ? mated_in(ss->ply) : VALUE_DRAW; From 531747ee7889d9b61b9841a57bb6d582459999d6 Mon Sep 17 00:00:00 2001 From: mstembera Date: Sat, 10 Feb 2024 15:06:38 -0800 Subject: [PATCH 1368/1766] Improve thread voting inefficiencies Initialize the unordered map to a reasonable number of buckets and make the move hashes well distributed. For more see https://github.com/official-stockfish/Stockfish/pull/4958#issuecomment-1937351190 Also make bestThreadPV and newThreadPV references so we don't copy entire vectors. closes https://github.com/official-stockfish/Stockfish/pull/5048 No functional change --- src/thread.cpp | 8 +++----- src/types.h | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/thread.cpp b/src/thread.cpp index 3cce7c56295..2e42abd42fc 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -210,10 +210,9 @@ void ThreadPool::start_thinking(const OptionsMap& options, Thread* ThreadPool::get_best_thread() const { - std::unordered_map votes; - Thread* bestThread = threads.front(); Value minScore = VALUE_NONE; + std::unordered_map votes(2 * std::min(size(), bestThread->worker->rootMoves.size())); // Find the minimum score of all threads for (Thread* th : threads) @@ -232,13 +231,12 @@ Thread* ThreadPool::get_best_thread() const { const auto bestThreadScore = bestThread->worker->rootMoves[0].score; const auto newThreadScore = th->worker->rootMoves[0].score; - const auto bestThreadPV = bestThread->worker->rootMoves[0].pv; - const auto newThreadPV = th->worker->rootMoves[0].pv; + const auto& bestThreadPV = bestThread->worker->rootMoves[0].pv; + const auto& newThreadPV = th->worker->rootMoves[0].pv; const auto bestThreadMoveVote = votes[bestThreadPV[0]]; const auto newThreadMoveVote = votes[newThreadPV[0]]; - const bool bestThreadInProvenWin = bestThreadScore >= VALUE_TB_WIN_IN_MAX_PLY; const bool newThreadInProvenWin = newThreadScore >= VALUE_TB_WIN_IN_MAX_PLY; diff --git a/src/types.h b/src/types.h index e83b306dc73..8b0ffb0ca0f 100644 --- a/src/types.h +++ b/src/types.h @@ -397,7 +397,7 @@ class Move { constexpr std::uint16_t raw() const { return data; } struct MoveHash { - std::size_t operator()(const Move& m) const { return m.data; } + std::size_t operator()(const Move& m) const { return make_key(m.data); } }; protected: From c115e5171eed2650b59f9a8da7cd6153e4cff873 Mon Sep 17 00:00:00 2001 From: Gahtan Nahdi <155860115+gahtan-syarif@users.noreply.github.com> Date: Sat, 10 Feb 2024 04:00:36 +0700 Subject: [PATCH 1369/1766] Remove quiet tt move extensions Passed STC: https://tests.stockfishchess.org/tests/view/65c6934cc865510db0286e90 LLR: 2.99 (-2.94,2.94) <-1.75,0.25> Total: 54016 W: 14065 L: 13854 D: 26097 Ptnml(0-2): 231, 6381, 13581, 6576, 239 Passed LTC: https://tests.stockfishchess.org/tests/view/65c72b91c865510db0287a1a LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 55098 W: 13850 L: 13658 D: 27590 Ptnml(0-2): 37, 6257, 14777, 6433, 45 closes https://github.com/official-stockfish/Stockfish/pull/5049 Bench: 1027182 --- src/search.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 7a60e9731ee..b36eb05d423 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1067,11 +1067,6 @@ Value Search::Worker::search( extension = -1; } - // Quiet ttMove extensions (~1 Elo) - else if (PvNode && move == ttMove && move == ss->killers[0] - && (*contHist[0])[movedPiece][move.to_sq()] >= 4339) - extension = 1; - // Recapture extensions (~1 Elo) else if (PvNode && move == ttMove && move.to_sq() == prevSq && thisThread->captureHistory[movedPiece][move.to_sq()] From 7ccde25baf03e77926644b282fed68ba0b5ddf95 Mon Sep 17 00:00:00 2001 From: Disservin Date: Sun, 11 Feb 2024 20:13:19 +0100 Subject: [PATCH 1370/1766] Format code using clang-format No functional change --- src/search.cpp | 4 ++-- src/thread.cpp | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index b36eb05d423..7bae7a4a86b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1289,8 +1289,8 @@ Value Search::Worker::search( assert(moveCount || !ss->inCheck || excludedMove || !MoveList(pos).size()); // Adjust best value for fail high cases at non-pv nodes - if (!PvNode && bestValue >= beta && std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY && - std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY && std::abs(alpha) < VALUE_TB_WIN_IN_MAX_PLY) + if (!PvNode && bestValue >= beta && std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY + && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY && std::abs(alpha) < VALUE_TB_WIN_IN_MAX_PLY) bestValue = (bestValue * (depth + 2) + beta) / (depth + 3); if (!moveCount) diff --git a/src/thread.cpp b/src/thread.cpp index 2e42abd42fc..61beb3994d0 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -212,7 +212,9 @@ Thread* ThreadPool::get_best_thread() const { Thread* bestThread = threads.front(); Value minScore = VALUE_NONE; - std::unordered_map votes(2 * std::min(size(), bestThread->worker->rootMoves.size())); + + std::unordered_map votes( + 2 * std::min(size(), bestThread->worker->rootMoves.size())); // Find the minimum score of all threads for (Thread* th : threads) From 5c0388310731ba4bf7989210cb69be67b53bb43d Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Tue, 13 Feb 2024 18:59:05 +0800 Subject: [PATCH 1371/1766] VVLTC search tune MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Search parameters were tuned using 16k games at VVLTC. They were tuned starting with the new parameters (in search only) of PR #5039. Passed VVLTC: https://tests.stockfishchess.org/tests/view/65c8a8fc1d8e83c78bfcd163 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 20826 W: 5355 L: 5100 D: 10371 Ptnml(0-2): 1, 1941, 6275, 2194, 2 Passed 2nd VVLTC: https://tests.stockfishchess.org/tests/view/65cadc2d1d8e83c78bfcfdaf LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 17710 W: 4611 L: 4352 D: 8747 Ptnml(0-2): 1, 1586, 5422, 1845, 1 STC Elo estimate: https://tests.stockfishchess.org/tests/view/65cb6aed1d8e83c78bfd0802 Elo: -1.46 ± 1.8 (95%) LOS: 5.5% Total: 40000 W: 10267 L: 10435 D: 19298 Ptnml(0-2): 200, 4860, 10023, 4742, 175 nElo: -2.77 ± 3.4 (95%) PairsRatio: 0.97 Bench: 1198939 --- src/search.cpp | 64 +++++++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 7bae7a4a86b..ae1c3414380 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -55,7 +55,7 @@ namespace { // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving) { - Value futilityMult = 116 - 47 * noTtCutNode; + Value futilityMult = 117 - 44 * noTtCutNode; return (futilityMult * d - 3 * futilityMult / 2 * improving); } @@ -66,15 +66,15 @@ constexpr int futility_move_count(bool improving, Depth depth) { // Add correctionHistory value to raw staticEval and guarantee evaluation does not hit the tablebase range Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { auto cv = w.correctionHistory[pos.side_to_move()][pawn_structure_index(pos)]; - v += cv * std::abs(cv) / 12890; + v += cv * std::abs(cv) / 12475; return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } // History and stats update bonus, based on depth -int stat_bonus(Depth d) { return std::min(253 * d - 356, 1117); } +int stat_bonus(Depth d) { return std::min(246 * d - 351, 1136); } // History and stats update malus, based on depth -int stat_malus(Depth d) { return std::min(517 * d - 308, 1206); } +int stat_malus(Depth d) { return std::min(519 * d - 306, 1258); } // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(size_t nodes) { return VALUE_DRAW - 1 + Value(nodes & 0x2); } @@ -297,12 +297,12 @@ void Search::Worker::iterative_deepening() { // Reset aspiration window starting size Value avg = rootMoves[pvIdx].averageScore; - delta = 9 + avg * avg / 12480; + delta = 9 + avg * avg / 12487; alpha = std::max(avg - delta, -VALUE_INFINITE); beta = std::min(avg + delta, VALUE_INFINITE); // Adjust optimism based on root move's averageScore (~4 Elo) - optimism[us] = 131 * avg / (std::abs(avg) + 95); + optimism[us] = 134 * avg / (std::abs(avg) + 97); optimism[~us] = -optimism[us]; // Start with a small aspiration window and, in the case of a fail @@ -482,7 +482,7 @@ void Search::Worker::clear() { h->fill(-71); for (size_t i = 1; i < reductions.size(); ++i) - reductions[i] = int((20.37 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); + reductions[i] = int((18.79 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); } @@ -723,7 +723,7 @@ Value Search::Worker::search( // Use static evaluation difference to improve quiet move ordering (~9 Elo) if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture) { - int bonus = std::clamp(-14 * int((ss - 1)->staticEval + ss->staticEval), -1661, 1495); + int bonus = std::clamp(-14 * int((ss - 1)->staticEval + ss->staticEval), -1723, 1455); bonus = bonus > 0 ? 2 * bonus : bonus / 2; thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) @@ -744,7 +744,7 @@ Value Search::Worker::search( // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. // Adjust razor margin according to cutoffCnt. (~1 Elo) - if (eval < alpha - 450 - (332 - 160 * ((ss + 1)->cutoffCnt > 3)) * depth * depth) + if (eval < alpha - 438 - (332 - 154 * ((ss + 1)->cutoffCnt > 3)) * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -755,22 +755,22 @@ Value Search::Worker::search( // The depth condition is important for mate finding. if (!ss->ttPv && depth < 11 && eval - futility_margin(depth, cutNode && !ss->ttHit, improving) - - (ss - 1)->statScore / 327 + - (ss - 1)->statScore / 314 >= beta - && eval >= beta && eval < 28702 // smaller than TB wins + && eval >= beta && eval < 30016 // smaller than TB wins && (!ttMove || ttCapture)) return beta > VALUE_TB_LOSS_IN_MAX_PLY ? (eval + beta) / 2 : eval; // Step 9. Null move search with verification search (~35 Elo) - if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 17379 - && eval >= beta && eval >= ss->staticEval && ss->staticEval >= beta - 21 * depth + 329 + if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 16620 + && eval >= beta && eval >= ss->staticEval && ss->staticEval >= beta - 21 * depth + 330 && !excludedMove && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly && beta > VALUE_TB_LOSS_IN_MAX_PLY) { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and eval - Depth R = std::min(int(eval - beta) / 148, 6) + depth / 3 + 4; + Depth R = std::min(int(eval - beta) / 154, 6) + depth / 3 + 4; ss->currentMove = Move::null(); ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -820,7 +820,7 @@ Value Search::Worker::search( // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search returns a value // much above beta, we can (almost) safely prune the previous move. - probCutBeta = beta + 182 - 68 * improving; + probCutBeta = beta + 181 - 68 * improving; if ( !PvNode && depth > 3 && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY @@ -876,7 +876,7 @@ Value Search::Worker::search( moves_loop: // When in check, search starts here // Step 12. A small Probcut idea, when we are in check (~4 Elo) - probCutBeta = beta + 446; + probCutBeta = beta + 452; if (ss->inCheck && !PvNode && ttCapture && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 4 && ttValue >= probCutBeta && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) @@ -959,7 +959,7 @@ Value Search::Worker::search( { Piece capturedPiece = pos.piece_on(move.to_sq()); int futilityEval = - ss->staticEval + 279 + 295 * lmrDepth + PieceValue[capturedPiece] + ss->staticEval + 277 + 292 * lmrDepth + PieceValue[capturedPiece] + thisThread->captureHistory[movedPiece][move.to_sq()][type_of(capturedPiece)] / 7; if (futilityEval < alpha) @@ -967,7 +967,7 @@ Value Search::Worker::search( } // SEE based pruning for captures and checks (~11 Elo) - if (!pos.see_ge(move, -204 * depth)) + if (!pos.see_ge(move, -197 * depth)) continue; } else @@ -979,16 +979,16 @@ Value Search::Worker::search( + thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()]; // Continuation history based pruning (~2 Elo) - if (lmrDepth < 6 && history < -4215 * depth) + if (lmrDepth < 6 && history < -4211 * depth) continue; history += 2 * thisThread->mainHistory[us][move.from_to()]; - lmrDepth += history / 6658; + lmrDepth += history / 6437; // Futility pruning: parent node (~13 Elo) if (!ss->inCheck && lmrDepth < 15 - && ss->staticEval + (bestValue < ss->staticEval - 58 ? 139 : 55) + && ss->staticEval + (bestValue < ss->staticEval - 57 ? 144 : 57) + 121 * lmrDepth <= alpha) continue; @@ -1016,11 +1016,11 @@ Value Search::Worker::search( // so changing them requires tests at these types of time controls. // Recursive singular search is avoided. if (!rootNode && move == ttMove && !excludedMove - && depth >= 4 - (thisThread->completedDepth > 29) + ss->ttPv + && depth >= 4 - (thisThread->completedDepth > 30) + ss->ttPv && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - (62 + 52 * (ss->ttPv && !PvNode)) * depth / 64; + Value singularBeta = ttValue - (60 + 54 * (ss->ttPv && !PvNode)) * depth / 64; Depth singularDepth = newDepth / 2; ss->excludedMove = move; @@ -1071,7 +1071,7 @@ Value Search::Worker::search( else if (PvNode && move == ttMove && move.to_sq() == prevSq && thisThread->captureHistory[movedPiece][move.to_sq()] [type_of(pos.piece_on(move.to_sq()))] - > 4356) + > 4394) extension = 1; } @@ -1123,10 +1123,10 @@ Value Search::Worker::search( ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + (*contHist[0])[movedPiece][move.to_sq()] + (*contHist[1])[movedPiece][move.to_sq()] - + (*contHist[3])[movedPiece][move.to_sq()] - 4409; + + (*contHist[3])[movedPiece][move.to_sq()] - 4392; // Decrease/increase reduction for moves with a good/bad history (~8 Elo) - r -= ss->statScore / 14894; + r -= ss->statScore / 14189; // Step 17. Late moves reduction / extension (LMR, ~117 Elo) if (depth >= 2 && moveCount > 1 + rootNode) @@ -1261,7 +1261,7 @@ Value Search::Worker::search( else { // Reduce other moves if we have found at least one score improvement (~2 Elo) - if (depth > 2 && depth < 13 && beta < 13710 && value > -12589) + if (depth > 2 && depth < 13 && beta < 13652 && value > -12761) depth -= 2; assert(depth > 0); @@ -1304,7 +1304,7 @@ Value Search::Worker::search( // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (depth > 5) + (PvNode || cutNode) + ((ss - 1)->statScore < -15401) + int bonus = (depth > 5) + (PvNode || cutNode) + ((ss - 1)->statScore < -15736) + ((ss - 1)->moveCount > 11); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); @@ -1462,7 +1462,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, if (bestValue > alpha) alpha = bestValue; - futilityBase = ss->staticEval + 204; + futilityBase = ss->staticEval + 206; } const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, @@ -1542,7 +1542,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, continue; // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, -75)) + if (!pos.see_ge(move, -74)) continue; } @@ -1610,7 +1610,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth Search::Worker::reduction(bool i, Depth d, int mn, int delta) { int reductionScale = reductions[d] * reductions[mn]; - return (reductionScale + 1177 - delta * 776 / rootDelta) / 1024 + (!i && reductionScale > 842); + return (reductionScale + 1118 - delta * 793 / rootDelta) / 1024 + (!i && reductionScale > 863); } namespace { @@ -1699,7 +1699,7 @@ void update_all_stats(const Position& pos, if (!pos.capture_stage(bestMove)) { - int bestMoveBonus = bestValue > beta + 167 ? quietMoveBonus // larger bonus + int bestMoveBonus = bestValue > beta + 166 ? quietMoveBonus // larger bonus : stat_bonus(depth); // smaller bonus // Increase stats for the best move in case it was a quiet move From f4f0b32d55defe93a79ec7afcce47d1d795879a8 Mon Sep 17 00:00:00 2001 From: Tierynn Byrnes Date: Tue, 6 Feb 2024 14:55:28 +1000 Subject: [PATCH 1372/1766] Refactor timeman.cpp Move optExtra, optConstant and maxConstant into lower scope. closes https://github.com/official-stockfish/Stockfish/pull/5052 No functional change --- AUTHORS | 1 + src/timeman.cpp | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/AUTHORS b/AUTHORS index 9b1faee7220..40a38bd5d89 100644 --- a/AUTHORS +++ b/AUTHORS @@ -216,6 +216,7 @@ Taras Vuk (TarasVuk) Thanar2 thaspel theo77186 +TierynnB Ting-Hsuan Huang (fffelix-huang) Tobias Steinmann Tomasz Sobczyk (Sopel97) diff --git a/src/timeman.cpp b/src/timeman.cpp index 121f8edb985..72a447af5b8 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -28,7 +28,6 @@ namespace Stockfish { - TimePoint TimeManagement::optimum() const { return optimumTime; } TimePoint TimeManagement::maximum() const { return maximumTime; } TimePoint TimeManagement::elapsed(size_t nodes) const { @@ -89,18 +88,19 @@ void TimeManagement::init(Search::LimitsType& limits, TimePoint timeLeft = std::max(TimePoint(1), limits.time[us] + limits.inc[us] * (mtg - 1) - moveOverhead * (2 + mtg)); - // Use extra time with larger increments - double optExtra = std::clamp(1.0 + 12.5 * limits.inc[us] / limits.time[us], 1.0, 1.11); - - // Calculate time constants based on current time left. - double optConstant = std::min(0.00334 + 0.0003 * std::log10(limits.time[us] / 1000.0), 0.0049); - double maxConstant = std::max(3.4 + 3.0 * std::log10(limits.time[us] / 1000.0), 2.76); - // x basetime (+ z increment) // If there is a healthy increment, timeLeft can exceed actual available // game time for the current move, so also cap to 20% of available game time. if (limits.movestogo == 0) { + // Use extra time with larger increments + double optExtra = std::clamp(1.0 + 12.5 * limits.inc[us] / limits.time[us], 1.0, 1.11); + + // Calculate time constants based on current time left. + double optConstant = + std::min(0.00334 + 0.0003 * std::log10(limits.time[us] / 1000.0), 0.0049); + double maxConstant = std::max(3.4 + 3.0 * std::log10(limits.time[us] / 1000.0), 2.76); + optScale = std::min(0.0120 + std::pow(ply + 3.1, 0.44) * optConstant, 0.21 * limits.time[us] / double(timeLeft)) * optExtra; From bf2c7306ac7f83200ba4d894867e3c0c78c0802c Mon Sep 17 00:00:00 2001 From: Disservin Date: Sun, 11 Feb 2024 14:19:44 +0100 Subject: [PATCH 1373/1766] Use node counting to early stop search This introduces a form of node counting which can be used to further tweak the usage of our search time. The current approach stops the search when almost all nodes are searched on a single move. The idea originally came from Koivisto, but the implemention is a bit different, Koivisto scales the optimal time by the nodes effort and then determines if the search should be stopped. We just scale down the `totalTime` and stop the search if we exceed it and the effort is large enough. Passed STC: https://tests.stockfishchess.org/tests/view/65c8e0661d8e83c78bfcd5ec LLR: 2.97 (-2.94,2.94) <0.00,2.00> Total: 88672 W: 22907 L: 22512 D: 43253 Ptnml(0-2): 310, 10163, 23041, 10466, 356 Passed LTC: https://tests.stockfishchess.org/tests/view/65ca632b1d8e83c78bfcf554 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 170856 W: 42910 L: 42320 D: 85626 Ptnml(0-2): 104, 18337, 47960, 18919, 108 closes https://github.com/official-stockfish/Stockfish/pull/5053 Bench: 1198939 --- src/search.cpp | 17 +++++++++++++++++ src/search.h | 2 ++ src/thread.cpp | 2 ++ 3 files changed, 21 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index ae1c3414380..9574465388e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -418,6 +419,10 @@ void Search::Worker::iterative_deepening() { // Do we have time for the next iteration? Can we stop searching now? if (limits.use_time_management() && !threads.stop && !mainThread->stopOnPonderhit) { + auto bestmove = rootMoves[0].pv[0]; + int nodesEffort = effort[bestmove.from_sq()][bestmove.to_sq()] * 100 + / std::max(size_t(1), size_t(nodes)); + double fallingEval = (66 + 14 * (mainThread->bestPreviousAverageScore - bestValue) + 6 * (mainThread->iterValue[iterIdx] - bestValue)) / 616.6; @@ -435,6 +440,13 @@ void Search::Worker::iterative_deepening() { if (rootMoves.size() == 1) totalTime = std::min(500.0, totalTime); + if (completedDepth >= 10 && nodesEffort >= 95 + && mainThread->tm.elapsed(threads.nodes_searched()) > totalTime * 3 / 4 + && !mainThread->ponder) + { + threads.stop = true; + } + // Stop the search if we have exceeded the totalTime if (mainThread->tm.elapsed(threads.nodes_searched()) > totalTime) { @@ -1087,6 +1099,8 @@ Value Search::Worker::search( ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck][capture][movedPiece][move.to_sq()]; + uint64_t nodeCount = rootNode ? uint64_t(nodes) : 0; + // Step 16. Make the move thisThread->nodes.fetch_add(1, std::memory_order_relaxed); pos.do_move(move, st, givesCheck); @@ -1186,6 +1200,9 @@ Value Search::Worker::search( // Step 19. Undo move pos.undo_move(move); + if (rootNode) + effort[move.from_sq()][move.to_sq()] += nodes - nodeCount; + assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); // Step 20. Check for a new best move diff --git a/src/search.h b/src/search.h index 2d1077f825e..4a1c68bb9ce 100644 --- a/src/search.h +++ b/src/search.h @@ -219,6 +219,8 @@ class Worker { return static_cast(manager.get()); } + std::array, SQUARE_NB> effort; + LimitsType limits; size_t pvIdx, pvLast; diff --git a/src/thread.cpp b/src/thread.cpp index 61beb3994d0..95646601106 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "misc.h" #include "movegen.h" @@ -203,6 +204,7 @@ void ThreadPool::start_thinking(const OptionsMap& options, th->worker->rootPos.set(pos.fen(), pos.is_chess960(), &th->worker->rootState); th->worker->rootState = setupStates->back(); th->worker->tbConfig = tbConfig; + th->worker->effort = {}; } main_thread()->start_searching(); From 9d61822b5dda7f99d46ed9ad346afa8c34c302a8 Mon Sep 17 00:00:00 2001 From: Gahtan Nahdi <155860115+gahtan-syarif@users.noreply.github.com> Date: Sat, 10 Feb 2024 03:51:05 +0700 Subject: [PATCH 1374/1766] Remove penalty for quiet ttMove that fails low Passed STC non-reg: https://tests.stockfishchess.org/tests/view/65c691a7c865510db0286e6e LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 234336 W: 60258 L: 60255 D: 113823 Ptnml(0-2): 966, 28141, 58918, 28210, 933 Passed LTC non-reg: https://tests.stockfishchess.org/tests/view/65c8d0d31d8e83c78bfcd4a6 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 235206 W: 59134 L: 59132 D: 116940 Ptnml(0-2): 135, 26908, 63517, 26906, 137 https://github.com/official-stockfish/Stockfish/pull/5054 Bench: 1287996 --- src/search.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 9574465388e..cb71acbaa5b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -621,13 +621,6 @@ Value Search::Worker::search( update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, -stat_malus(depth + 1)); } - // Penalty for a quiet ttMove that fails low (~1 Elo) - else if (!ttCapture) - { - int penalty = -stat_malus(depth); - thisThread->mainHistory[us][ttMove.from_to()] << penalty; - update_continuation_histories(ss, pos.moved_piece(ttMove), ttMove.to_sq(), penalty); - } } // Partial workaround for the graph history interaction problem From f3df0cfb84250f03662a6fd50ea20c9677a0a1d0 Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Tue, 13 Feb 2024 17:50:16 +0800 Subject: [PATCH 1375/1766] Simplify TT PV reduction This also removes some incorrect fail-high logic. Passed STC: https://tests.stockfishchess.org/tests/view/65cb3b641d8e83c78bfd04a9 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 87968 W: 22634 L: 22468 D: 42866 Ptnml(0-2): 315, 10436, 22323, 10588, 322 Passed LTC: https://tests.stockfishchess.org/tests/view/65cccee21d8e83c78bfd222c LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 70794 W: 17846 L: 17672 D: 35276 Ptnml(0-2): 44, 7980, 19189, 8126, 58 closes https://github.com/official-stockfish/Stockfish/pull/5055 Bench: 1474424 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index cb71acbaa5b..c407ae6ba6e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1098,9 +1098,9 @@ Value Search::Worker::search( thisThread->nodes.fetch_add(1, std::memory_order_relaxed); pos.do_move(move, st, givesCheck); - // Decrease reduction if position is or has been on the PV (~5 Elo) + // Decrease reduction if position is or has been on the PV (~7 Elo) if (ss->ttPv) - r -= 1 + (ttValue > alpha) + (ttValue > beta && tte->depth() >= depth); + r -= 1 + (ttValue > alpha) + (tte->depth() >= depth); // Increase reduction for cut nodes (~4 Elo) if (cutNode) From 8e75548f2a10969c1c9211056999efbcebe63f9a Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Tue, 6 Feb 2024 11:21:15 -0500 Subject: [PATCH 1376/1766] Update default main net to nn-b1a57edbea57.nnue Created by retraining the previous main net `nn-baff1edbea57.nnue` with: - some of the same options as before: ranger21, more WDL skipping - the addition of T80 nov+dec 2023 data - increasing loss by 15% when prediction is too high, up from 10% - use of torch.compile to speed up training by over 25% ```yaml experiment-name: 2560--S9-514G-T80-augtodec2023-more-wdl-skip-15p-more-loss-high-q-sk28 training-dataset: # https://github.com/official-stockfish/Stockfish/pull/4782 - /data/S6-514G-1ee1aba5ed.binpack - /data/test80-aug2023-2tb7p.v6.min.binpack - /data/test80-sep2023-2tb7p.binpack - /data/test80-oct2023-2tb7p.binpack - /data/test80-nov2023-2tb7p.binpack - /data/test80-dec2023-2tb7p.binpack early-fen-skipping: 28 start-from-engine-test-net: True nnue-pytorch-branch: linrock/nnue-pytorch/r21-more-wdl-skip-15p-more-loss-high-q-torch-compile num-epochs: 1000 lr: 4.375e-4 gamma: 0.995 start-lambda: 1.0 end-lambda: 0.7 ``` Epoch 819 trained with the above config led to this PR. Use of torch.compile decorators in nnue-pytorch model.py was found to speed up training by at least 25% on Ampere gpus when using recent pytorch compiled with cuda 12: https://catalog.ngc.nvidia.com/orgs/nvidia/containers/pytorch See recent main net PRs for more info on - ranger21 and more WDL skipping: https://github.com/official-stockfish/Stockfish/pull/4942 - increasing loss when Q is too high: https://github.com/official-stockfish/Stockfish/pull/4972 Training data can be found at: https://robotmoon.com/nnue-training-data/ Passed STC: https://tests.stockfishchess.org/tests/view/65cd76151d8e83c78bfd2f52 LLR: 2.98 (-2.94,2.94) <0.00,2.00> Total: 78336 W: 20504 L: 20115 D: 37717 Ptnml(0-2): 317, 9225, 19721, 9562, 343 Passed LTC: https://tests.stockfishchess.org/tests/view/65ce5be61d8e83c78bfd43e9 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 41016 W: 10492 L: 10159 D: 20365 Ptnml(0-2): 22, 4533, 11071, 4854, 28 closes https://github.com/official-stockfish/Stockfish/pull/5056 Bench: 1351997 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 729baa6bcb8..53928bf6494 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ Value evaluate(const Position& pos, int optimism); // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. -#define EvalFileDefaultNameBig "nn-baff1edbea57.nnue" +#define EvalFileDefaultNameBig "nn-b1a57edbea57.nnue" #define EvalFileDefaultNameSmall "nn-baff1ede1f90.nnue" struct EvalFile { From fc41f64dfd8a61d0e275ddbecec292833458b86a Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Tue, 13 Feb 2024 17:46:37 +0800 Subject: [PATCH 1377/1766] Simplify PV node reduction Reduce less on PV nodes even with an upperbound TT entry. Passed STC: https://tests.stockfishchess.org/tests/view/65cb3a861d8e83c78bfd0497 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 118752 W: 30441 L: 30307 D: 58004 Ptnml(0-2): 476, 14179, 29921, 14335, 465 Passed LTC: https://tests.stockfishchess.org/tests/view/65cd3b951d8e83c78bfd2b0d LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 155058 W: 38549 L: 38464 D: 78045 Ptnml(0-2): 85, 17521, 42219, 17632, 72 closes https://github.com/official-stockfish/Stockfish/pull/5057 Bench: 1303971 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index c407ae6ba6e..55a92947d32 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1111,7 +1111,7 @@ Value Search::Worker::search( r++; // Decrease reduction for PvNodes (~3 Elo) - if (PvNode && tte->bound() != BOUND_UPPER) + if (PvNode) r--; // Increase reduction on repetition (~1 Elo) From d07033d5da9c4e1a383f8be45bd9be43ce7b2f95 Mon Sep 17 00:00:00 2001 From: Disservin Date: Sat, 24 Feb 2024 11:56:33 +0100 Subject: [PATCH 1378/1766] Expose EvalFileSmall option for small net Since https://github.com/official-stockfish/fishtest/pull/1870 has been merged it's time for this update. 5k Fixed Games showed no problems. https://tests.stockfishchess.org/tests/view/65d9cc274c0e22b904f574d7 closes https://github.com/official-stockfish/Stockfish/pull/5068 No functional change --- src/evaluate.cpp | 13 +++---------- src/uci.cpp | 3 +++ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index da0867660af..f22c0d06cbe 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -77,11 +77,7 @@ NNUE::EvalFiles NNUE::load_networks(const std::string& rootDirectory, for (auto& [netSize, evalFile] : evalFiles) { - // Replace with - // options[evalFile.optionName] - // once fishtest supports the uci option EvalFileSmall - std::string user_eval_file = - netSize == Small ? evalFile.defaultName : options[evalFile.optionName]; + std::string user_eval_file = options[evalFile.optionName]; if (user_eval_file.empty()) user_eval_file = evalFile.defaultName; @@ -149,11 +145,8 @@ void NNUE::verify(const OptionsMap& optio for (const auto& [netSize, evalFile] : evalFiles) { - // Replace with - // options[evalFile.optionName] - // once fishtest supports the uci option EvalFileSmall - std::string user_eval_file = - netSize == Small ? evalFile.defaultName : options[evalFile.optionName]; + std::string user_eval_file = options[evalFile.optionName]; + if (user_eval_file.empty()) user_eval_file = evalFile.defaultName; diff --git a/src/uci.cpp b/src/uci.cpp index d1d69d69752..35f725b5e89 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -83,6 +83,9 @@ UCI::UCI(int argc, char** argv) : options["EvalFile"] << Option(EvalFileDefaultNameBig, [this](const Option&) { evalFiles = Eval::NNUE::load_networks(cli.binaryDirectory, options, evalFiles); }); + options["EvalFileSmall"] << Option(EvalFileDefaultNameSmall, [this](const Option&) { + evalFiles = Eval::NNUE::load_networks(cli.binaryDirectory, options, evalFiles); + }); threads.set({options, threads, tt}); From bec83a1869ae6d1bbcfc7d82d569e12823b03bfa Mon Sep 17 00:00:00 2001 From: Disservin Date: Sat, 24 Feb 2024 17:54:06 +0100 Subject: [PATCH 1379/1766] Update Top CPU Contributors closes https://github.com/official-stockfish/Stockfish/pull/5069 No functional change --- Top CPU Contributors.txt | 189 +++++++++++++++++++++------------------ 1 file changed, 104 insertions(+), 85 deletions(-) diff --git a/Top CPU Contributors.txt b/Top CPU Contributors.txt index 74c471b7404..11636e840b5 100644 --- a/Top CPU Contributors.txt +++ b/Top CPU Contributors.txt @@ -1,139 +1,146 @@ -Contributors to Fishtest with >10,000 CPU hours, as of 2023-06-20. +Contributors to Fishtest with >10,000 CPU hours, as of 2024-02-24. Thank you! Username CPU Hours Games played ------------------------------------------------------------------ -noobpwnftw 37457426 2850540907 -technologov 14135647 742892808 -linrock 4423514 303254809 +noobpwnftw 39302472 3055513453 +technologov 20845762 994893444 +linrock 8616428 560281417 mlang 3026000 200065824 +okrout 2332151 222639518 +pemo 1800019 60274069 dew 1689162 100033738 -okrout 1578136 148855886 -pemo 1508508 48814305 -grandphish2 1461406 91540343 -TueRens 1194790 70400852 -JojoM 947612 61773190 +TueRens 1474943 75121774 +grandphish2 1463002 91616949 +JojoM 1109702 72927902 +olafm 978631 71037944 +sebastronomy 939955 44920556 tvijlbrief 796125 51897690 -sebastronomy 742434 38218524 +gvreuls 711320 49142318 mibere 703840 46867607 -gvreuls 651026 42988582 -oz 543438 39314736 -cw 517858 34869755 +oz 646268 46293638 +rpngn 572571 38928563 +leszek 531858 39316505 +cw 518116 34894291 fastgm 503862 30260818 -leszek 467278 33514883 -CSU_Dynasty 464940 31177118 -ctoks 434416 28506889 -crunchy 427035 27344275 -maximmasiutin 424795 26577722 -bcross 415722 29060963 -olafm 395922 32268020 -rpngn 348378 24560289 -velislav 342567 22138992 +CSU_Dynasty 468784 31385034 +ctoks 434591 28520597 +maximmasiutin 429983 27066286 +crunchy 427414 27371625 +bcross 415724 29061187 +velislav 342588 22140902 +mgrabiak 338763 23999170 Fisherman 327231 21829379 -mgrabiak 300612 20608380 +robal 299836 20213182 Dantist 296386 18031762 -nordlandia 246201 16189678 -robal 241300 15656382 +ncfish1 267604 17881149 +nordlandia 249322 16420192 marrco 234581 17714473 -ncfish1 227517 15233777 +tolkki963 233490 19773930 glinscott 208125 13277240 drabel 204167 13930674 mhoram 202894 12601997 bking_US 198894 11876016 +Calis007 188631 12795784 Thanar 179852 12365359 +Fifis 176209 10638245 vdv 175544 9904472 spams 157128 10319326 +DesolatedDodo 156659 10210328 +armo9494 155355 10566898 sqrt2 147963 9724586 -DesolatedDodo 146350 9536172 -Calis007 143165 9478764 -vdbergh 138650 9064413 +jcAEie 140086 10603658 +vdbergh 139746 9172061 CoffeeOne 137100 5024116 -armo9494 136191 9460264 malala 136182 8002293 xoto 133759 9159372 davar 129023 8376525 DMBK 122960 8980062 dsmith 122059 7570238 +javran 121564 10144656 amicic 119661 7938029 +sschnee 118107 7389266 +Wolfgang 114616 8070494 Data 113305 8220352 BrunoBanani 112960 7436849 +Wencey 111502 5991676 +cuistot 108503 7006992 CypressChess 108331 7759788 skiminki 107583 7218170 -jcAEie 105675 8238962 MaZePallas 102823 6633619 sterni1971 100532 5880772 sunu 100167 7040199 zeryl 99331 6221261 -thirdlife 99124 2242380 +thirdlife 99156 2245320 ElbertoOne 99028 7023771 -cuistot 98853 6069816 +Dubslow 98600 6903242 +markkulix 97010 7643900 bigpen0r 94809 6529203 brabos 92118 6186135 -Wolfgang 91939 6105872 +Maxim 90818 3283364 psk 89957 5984901 -sschnee 88235 5268000 +megaman7de 88822 6052132 racerschmacer 85805 6122790 -Fifis 85722 5709729 -Dubslow 84986 6042456 +maposora 85710 7778146 Vizvezdenec 83761 5344740 0x3C33 82614 5271253 BRAVONE 81239 5054681 nssy 76497 5259388 jromang 76106 5236025 teddybaer 75125 5407666 -tolkki963 74762 5149662 -megaman7de 74351 4940352 -Wencey 74181 4711488 Pking_cda 73776 5293873 -yurikvelo 73150 5004382 -markkulix 72607 5304642 +yurikvelo 73516 5036928 +MarcusTullius 71053 4803477 Bobo1239 70579 4794999 solarlight 70517 5028306 dv8silencer 70287 3883992 +Spprtr 69646 4806763 +Mineta 66325 4537742 manap 66273 4121774 +szupaw 65468 5669742 tinker 64333 4268790 qurashee 61208 3429862 -Mineta 59357 4418202 -Spprtr 58723 3911011 -AGI 58147 4325994 +woutboat 59496 4906352 +AGI 58195 4329580 robnjr 57262 4053117 Freja 56938 3733019 MaxKlaxxMiner 56879 3423958 -MarcusTullius 56746 3762951 ttruscott 56010 3680085 rkl 55132 4164467 +jmdana 54697 4012593 renouve 53811 3501516 -javran 53785 4627608 +notchris 52433 4044590 finfish 51360 3370515 eva42 51272 3599691 eastorwest 51117 3454811 +Goatminola 51004 4432492 rap 49985 3219146 pb00067 49733 3298934 +GPUex 48686 3684998 OuaisBla 48626 3445134 ronaldjerum 47654 3240695 biffhero 46564 3111352 +oryx 45533 3539290 VoyagerOne 45476 3452465 -jmdana 44893 3065205 -maposora 44597 4039578 -oryx 44570 3454238 speedycpu 43842 3003273 jbwiebe 43305 2805433 -GPUex 42378 3133332 Antihistamine 41788 2761312 mhunt 41735 2691355 homyur 39893 2850481 gri 39871 2515779 Garf 37741 2999686 SC 37299 2731694 +Sylvain27 36520 1467082 csnodgrass 36207 2688994 +Gaster319 35655 3149442 strelock 34716 2074055 -szupaw 34102 2880346 EthanOConnor 33370 2090311 slakovv 32915 2021889 +gopeto 31884 2076712 Gelma 31771 1551204 -gopeto 31671 2060990 kdave 31157 2198362 manapbk 30987 1810399 +ZacHFX 30551 2238078 Prcuvu 30377 2170122 anst 30301 2190091 jkiiski 30136 1904470 @@ -142,27 +149,31 @@ hyperbolic.tom 29840 2017394 chuckstablers 29659 2093438 Pyafue 29650 1902349 belzedar94 28846 1811530 +votoanthuan 27978 2285818 +shawnxu 27438 2465810 chriswk 26902 1868317 xwziegtm 26897 2124586 achambord 26582 1767323 Patrick_G 26276 1801617 yorkman 26193 1992080 -Ulysses 25288 1689730 +Ulysses 25397 1701264 +Jopo12321 25227 1652482 SFTUser 25182 1675689 -nabildanial 24942 1519409 +nabildanial 25068 1531665 Sharaf_DG 24765 1786697 -Maxim 24705 1502062 rodneyc 24376 1416402 +jsys14 24297 1721230 agg177 23890 1395014 -Goatminola 23763 1956036 -Ente 23639 1671638 -Jopo12321 23467 1483172 +srowen 23842 1342508 +Ente 23752 1678188 +jojo2357 23479 2061238 JanErik 23408 1703875 Isidor 23388 1680691 Norabor 23371 1603244 cisco2015 22920 1763301 -jsys14 22824 1591906 Zirie 22542 1472937 +Nullvalue 22490 1970374 +AndreasKrug 22485 1769491 team-oh 22272 1636708 Roady 22220 1465606 MazeOfGalious 21978 1629593 @@ -173,79 +184,83 @@ dex 21612 1467203 nesoneg 21494 1463031 user213718 21454 1404128 sphinx 21211 1384728 -AndreasKrug 21097 1634811 +qoo_charly_cai 21135 1514907 jjoshua2 21001 1423089 Zake9298 20938 1565848 horst.prack 20878 1465656 0xB00B1ES 20590 1208666 +Serpensin 20487 1729674 +Dinde 20440 1292390 j3corre 20405 941444 Adrian.Schmidt123 20316 1281436 wei 19973 1745989 -notchris 19958 1800128 -Serpensin 19840 1697528 -Gaster319 19712 1677310 fishtester 19617 1257388 rstoesser 19569 1293588 eudhan 19274 1283717 -votoanthuan 19108 1609992 vulcan 18871 1729392 Karpovbot 18766 1053178 -qoo_charly_cai 18543 1284937 +WoodMan777 18556 1628264 jundery 18445 1115855 ville 17883 1384026 chris 17698 1487385 purplefishies 17595 1092533 dju 17414 981289 +ols 17291 1042003 iisiraider 17275 1049015 +Skiff84 17111 950248 DragonLord 17014 1162790 redstone59 16842 1461780 -Alb11747 16787 1213926 +Karby 16839 1010124 +Alb11747 16787 1213990 +pirt 16493 1237199 +Naven94 16414 951718 +wizardassassin 16392 1148672 IgorLeMasson 16064 1147232 -Karby 15982 979610 scuzzi 15757 968735 ako027ako 15671 1173203 Nikolay.IT 15154 1068349 Andrew Grant 15114 895539 -Naven94 15054 834762 OssumOpossum 14857 1007129 -ZacHFX 14783 1021842 +LunaticBFF57 14525 1190310 enedene 14476 905279 +IslandLambda 14393 958196 bpfliegel 14233 882523 +YELNAMRON 14230 1128094 mpx86 14019 759568 jpulman 13982 870599 -Skiff84 13826 721996 +getraideBFF 13871 1172846 +Nesa92 13806 1116101 crocogoat 13803 1117422 -Nesa92 13786 1114691 joster 13710 946160 mbeier 13650 1044928 Hjax 13535 915487 -Nullvalue 13468 1140498 Dark_wizzie 13422 1007152 Rudolphous 13244 883140 -pirt 13100 1009897 Machariel 13010 863104 infinigon 12991 943216 mabichito 12903 749391 thijsk 12886 722107 AdrianSA 12860 804972 Flopzee 12698 894821 +mschmidt 12644 863193 korposzczur 12606 838168 +tsim67 12570 890180 +Jackfish 12553 836958 fatmurphy 12547 853210 +Oakwen 12503 853105 SapphireBrand 12416 969604 -Oakwen 12399 844109 deflectooor 12386 579392 modolief 12386 896470 +TataneSan 12358 609332 Farseer 12249 694108 -Jackfish 12213 805008 pgontarz 12151 848794 dbernier 12103 860824 -getraideBFF 12072 1024966 +FormazChar 11989 907809 stocky 11954 699440 -mschmidt 11941 803401 -MooTheCow 11870 773598 -FormazChar 11766 885707 +somethingintheshadows 11940 989472 +MooTheCow 11892 776126 +3cho 11842 1036786 whelanh 11557 245188 -3cho 11494 1031076 infinity 11470 727027 aga 11412 695127 torbjo 11395 729145 @@ -253,15 +268,19 @@ Thomas A. Anderson 11372 732094 savage84 11358 670860 d64 11263 789184 ali-al-zhrani 11245 779246 +ckaz 11170 680866 snicolet 11106 869170 dapper 11032 771402 -ols 10947 624903 -Karmatron 10828 677458 +Ethnikoi 10993 945906 +Snuuka 10938 435504 +Karmatron 10859 678058 basepi 10637 744851 +jibarbosa 10628 857100 Cubox 10621 826448 +mecevdimitar 10609 787318 michaelrpg 10509 739239 +Def9Infinity 10427 686978 OIVAS7572 10420 995586 -jojo2357 10419 929708 -WoodMan777 10380 873720 +wxt9861 10412 1013864 Garruk 10365 706465 dzjp 10343 732529 From 5c2b3859579e20cb1abc8d53ae7ee229fbc1e335 Mon Sep 17 00:00:00 2001 From: "Robert Nurnberg @ elitebook" Date: Sat, 24 Feb 2024 17:55:08 +0100 Subject: [PATCH 1380/1766] Update the WDL model Based on 130M positions from 2.1M games. ``` Look recursively in directory pgns for games from SPRT tests using books matching "UHO_4060_v..epd|UHO_Lichess_4852_v1.epd" for SF revisions between 8e75548f2a10969c1c9211056999efbcebe63f9a (from 2024-02-17 17:11:46 +0100) and HEAD (from 2024-02-17 17:13:07 +0100). Based on 127920843 positions from 2109240 games, NormalizeToPawnValue should change from 345 to 356. ``` The patch only affects the UCI-reported cp and wdl values. closes https://github.com/official-stockfish/Stockfish/pull/5070 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 35f725b5e89..4d4ea689715 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -45,7 +45,7 @@ namespace Stockfish { constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; -constexpr int NormalizeToPawnValue = 345; +constexpr int NormalizeToPawnValue = 356; constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048; UCI::UCI(int argc, char** argv) : @@ -380,8 +380,8 @@ int win_rate_model(Value v, int ply) { // The coefficients of a third-order polynomial fit is based on the fishtest data // for two parameters that need to transform eval to the argument of a logistic // function. - constexpr double as[] = {-2.00568292, 10.45906746, 1.67438883, 334.45864705}; - constexpr double bs[] = {-4.97134419, 36.15096345, -82.25513499, 117.35186805}; + constexpr double as[] = {-1.06249702, 7.42016937, 0.89425629, 348.60356174}; + constexpr double bs[] = {-5.33122190, 39.57831533, -90.84473771, 123.40620748}; // Enforce that NormalizeToPawnValue corresponds to a 50% win rate at move 32. static_assert(NormalizeToPawnValue == int(0.5 + as[0] + as[1] + as[2] + as[3])); From e67cc979fd2c0e66dfc2b2f2daa0117458cfc462 Mon Sep 17 00:00:00 2001 From: Disservin Date: Wed, 21 Feb 2024 22:17:23 +0100 Subject: [PATCH 1381/1766] Stockfish 16.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Official release version of Stockfish 16.1 Bench: 1303971 --- Stockfish 16.1 Today, we have the pleasure to announce Stockfish 16.1. As always, you can freely download it at https://stockfishchess.org/download and use it in the GUI of your choice[1]. Don't forget to join our Discord server[2] to get in touch with the community of developers and users of the project! *Quality of chess play* In our testing against its predecessor, Stockfish 16.1 shows a notable improvement in performance, with an Elo gain of up to 27 points and winning over 2 times more game pairs[3] than it loses. *Update highlights* *Improved evaluation* - Updated neural network architecture: The neural network architecture has undergone two updates and is currently in its 8th version[4]. - Removal of handcrafted evaluation (HCE): This release marks the removal of the traditional handcrafted evaluation and the transition to a fully neural network-based approach[5]. - Dual NNUE: For the first time, Stockfish includes a secondary neural network[6], used to quickly evaluate positions that are easily decided. *UCI Options removed* `Use NNUE` and `UCI_AnalyseMode`[7] have been removed as they no longer had any effect. `SlowMover`[8] has also been removed in favor of `Move Overhead`. *More binaries* We now offer 13 new binaries. These new binaries include `avx512`, `vnni256`, `vnni512`, `m1-apple-silicon`, and `armv8-dotprod`, which take advantage of specific CPU instructions for improved performance. For most users, using `sse41-popcnt` (formerly `modern`), `avx2`, or `bmi2` should be enough, but if your CPU supports these new instructions, feel free to try them! *Development changes* - Updated testing book: This new book[9], now derived exclusively from the open Lichess database[10], is 10 times larger than its predecessor, and has been used to test potential improvements to Stockfish over the past few months. - Consolidation of repositories: Aiming to simplify access to our resources, we have moved most Stockfish-related repositories into the official Stockfish organization[11] on GitHub. - Growing maintainer team: We welcome Disservin[12] to the team of maintainers of the project! This extra pair of hands will ensure the lasting success of Stockfish. *Thank you* The Stockfish project builds on a thriving community of enthusiasts (thanks everybody!) who contribute their expertise, time, and resources to build a free and open-source chess engine that is robust, widely available, and very strong. We would like to express our gratitude for the 10k stars[13] that light up our GitHub project! Thank you for your support and encouragement – your recognition means a lot to us. We invite our chess fans to join the Fishtest testing framework[14], and programmers to contribute to the project either directly to Stockfish[15] (C++), to Fishtest[16] (HTML, CSS, JavaScript, and Python), to our trainer nnue-pytorch[17] (C++ and Python), or to our website[18] (HTML, CSS/SCSS, and JavaScript). The Stockfish team [1] https://github.com/official-stockfish/Stockfish/wiki/Download-and-usage#download-a-chess-gui [2] https://discord.gg/GWDRS3kU6R [3] https://tests.stockfishchess.org/tests/view/65d666051d8e83c78bfddbd8 [4] https://github.com/official-stockfish/nnue-pytorch/blob/master/docs/nnue.md#sfnnv8-architecture [5] https://github.com/official-stockfish/Stockfish/commit/af110e0 [6] https://github.com/official-stockfish/Stockfish/commit/584d9ef [7] https://github.com/official-stockfish/Stockfish/commit/c53d2ec [8] https://github.com/official-stockfish/Stockfish/commit/536d692 [9] https://github.com/official-stockfish/books/commit/426eca4 [10] https://database.lichess.org/ [11] https://github.com/official-stockfish/ [12] https://github.com/Disservin [13] https://github.com/official-stockfish/Stockfish/stargazers [14] https://github.com/official-stockfish/fishtest/wiki/Running-the-worker [15] https://github.com/official-stockfish/Stockfish [16] https://github.com/official-stockfish/fishtest [17] https://github.com/official-stockfish/nnue-pytorch [18] https://github.com/official-stockfish/stockfish-web --- src/misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index 4885a5cd35c..1d089971090 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -75,7 +75,7 @@ namespace Stockfish { namespace { // Version number or dev. -constexpr std::string_view version = "dev"; +constexpr std::string_view version = "16.1"; // Our fancy logging facility. The trick here is to replace cin.rdbuf() and // cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We From abcc090a625d7891146645ee493aec5ee8046dc5 Mon Sep 17 00:00:00 2001 From: Disservin Date: Sat, 24 Feb 2024 20:40:48 +0100 Subject: [PATCH 1382/1766] Restore development closes https://github.com/official-stockfish/Stockfish/pull/5073 No functional change --- src/misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index 1d089971090..4885a5cd35c 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -75,7 +75,7 @@ namespace Stockfish { namespace { // Version number or dev. -constexpr std::string_view version = "16.1"; +constexpr std::string_view version = "dev"; // Our fancy logging facility. The trick here is to replace cin.rdbuf() and // cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We From c83c7f4e713c31a9d0811cdb8fb6469dc3a330be Mon Sep 17 00:00:00 2001 From: Disservin Date: Sat, 24 Feb 2024 20:30:40 +0100 Subject: [PATCH 1383/1766] Make binaries executable again in CI closes https://github.com/official-stockfish/Stockfish/pull/5072 No functional change --- .github/workflows/upload_binaries.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/upload_binaries.yml b/.github/workflows/upload_binaries.yml index 97bcf96f219..0dfd7244961 100644 --- a/.github/workflows/upload_binaries.yml +++ b/.github/workflows/upload_binaries.yml @@ -65,6 +65,7 @@ jobs: - name: Create tar if: runner.os != 'Windows' run: | + chmod +x ./stockfish/stockfish-$NAME-$BINARY$EXT tar -cvf stockfish-$NAME-$BINARY.tar stockfish - name: Create zip From 0c22d5bb1ab735a5191f713c18428d66a17535df Mon Sep 17 00:00:00 2001 From: Disservin Date: Sun, 25 Feb 2024 11:16:55 +0100 Subject: [PATCH 1384/1766] Update Actions to Node20 ensure our CI continues to run after Node16 is obsolote on github. closes https://github.com/official-stockfish/Stockfish/pull/5074 No functional change --- .github/workflows/clang-format.yml | 4 ++-- .github/workflows/upload_binaries.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 0eb3fc70dab..e20e0d5d623 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -30,7 +30,7 @@ jobs: - name: Comment on PR if: steps.clang-format.outcome == 'failure' - uses: thollander/actions-comment-pull-request@1d3973dc4b8e1399c0620d3f2b1aa5e795465308 # @v2.4.3 + uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 # @v2.5.0 with: message: | clang-format 17 needs to be run on this PR. @@ -42,7 +42,7 @@ jobs: - name: Comment on PR if: steps.clang-format.outcome != 'failure' - uses: thollander/actions-comment-pull-request@1d3973dc4b8e1399c0620d3f2b1aa5e795465308 # @v2.4.3 + uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 # @v2.5.0 with: message: | _(execution **${{ github.run_id }}** / attempt **${{ github.run_attempt }}**)_ diff --git a/.github/workflows/upload_binaries.yml b/.github/workflows/upload_binaries.yml index 0dfd7244961..015b514ce4b 100644 --- a/.github/workflows/upload_binaries.yml +++ b/.github/workflows/upload_binaries.yml @@ -98,7 +98,7 @@ jobs: - name: Prerelease if: github.ref_name == 'master' && env.CHANGES == '0' continue-on-error: true - uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # @v1 + uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981 with: name: Stockfish dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} tag_name: stockfish-dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} From f77eddfa2ff6d5f496b398a9d743a5f02d358dc8 Mon Sep 17 00:00:00 2001 From: Gahtan Nahdi <155860115+gahtan-syarif@users.noreply.github.com> Date: Tue, 27 Feb 2024 19:27:40 +0700 Subject: [PATCH 1385/1766] Join conditions for move sorting heuristics closes https://github.com/official-stockfish/Stockfish/pull/5078 No functional change. --- src/search.cpp | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 55a92947d32..3de1f69b817 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -607,20 +607,17 @@ Value Search::Worker::search( && (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER))) { // If ttMove is quiet, update move sorting heuristics on TT hit (~2 Elo) - if (ttMove) + if (ttMove && ttValue >= beta) { - if (ttValue >= beta) - { - // Bonus for a quiet ttMove that fails high (~2 Elo) - if (!ttCapture) - update_quiet_stats(pos, ss, *this, ttMove, stat_bonus(depth)); - - // Extra penalty for early quiet moves of - // the previous ply (~0 Elo on STC, ~2 Elo on LTC). - if (prevSq != SQ_NONE && (ss - 1)->moveCount <= 2 && !priorCapture) - update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, - -stat_malus(depth + 1)); - } + // Bonus for a quiet ttMove that fails high (~2 Elo) + if (!ttCapture) + update_quiet_stats(pos, ss, *this, ttMove, stat_bonus(depth)); + + // Extra penalty for early quiet moves of + // the previous ply (~0 Elo on STC, ~2 Elo on LTC). + if (prevSq != SQ_NONE && (ss - 1)->moveCount <= 2 && !priorCapture) + update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, + -stat_malus(depth + 1)); } // Partial workaround for the graph history interaction problem From 0a3eb1d8fa1815f1f800e38b990247b9e58e27f5 Mon Sep 17 00:00:00 2001 From: Disservin Date: Tue, 20 Feb 2024 22:47:26 +0100 Subject: [PATCH 1386/1766] Document TT code more Slight refactor of the TT code with the goal to make it easier to understand / tweak. Passed Non-Regression STC: https://tests.stockfishchess.org/tests/view/65d51e401d8e83c78bfdc427 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 56416 W: 14750 L: 14550 D: 27116 Ptnml(0-2): 227, 6386, 14796, 6558, 241 closes https://github.com/official-stockfish/Stockfish/pull/5061 No functional change --- src/tt.cpp | 33 ++++++++++++++++++++------------- src/tt.h | 25 ++++++++++++++++++------- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/src/tt.cpp b/src/tt.cpp index f3f58979d1b..8ef06e6355c 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -19,6 +19,7 @@ #include "tt.h" #include +#include #include #include #include @@ -53,6 +54,18 @@ void TTEntry::save( } +uint8_t TTEntry::relative_age(const uint8_t generation8) const { + // Due to our packed storage format for generation and its cyclic + // nature we add GENERATION_CYCLE (256 is the modulus, plus what + // is needed to keep the unrelated lowest n bits from affecting + // the result) to calculate the entry age correctly even after + // generation8 overflows into the next cycle. + + return (TranspositionTable::GENERATION_CYCLE + generation8 - genBound8) + & TranspositionTable::GENERATION_MASK; +} + + // Sets the size of the transposition table, // measured in megabytes. Transposition table consists of a power of 2 number // of clusters and each cluster consists of ClusterSize number of TTEntry. @@ -111,24 +124,18 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const { for (int i = 0; i < ClusterSize; ++i) if (tte[i].key16 == key16 || !tte[i].depth8) { - tte[i].genBound8 = - uint8_t(generation8 | (tte[i].genBound8 & (GENERATION_DELTA - 1))); // Refresh + constexpr uint8_t lowerBits = GENERATION_DELTA - 1; - return found = bool(tte[i].depth8), &tte[i]; + // Refresh with new generation, keeping the lower bits the same. + tte[i].genBound8 = uint8_t(generation8 | (tte[i].genBound8 & lowerBits)); + return found = bool(tte[i].depth8), &tte[i]; } // Find an entry to be replaced according to the replacement strategy TTEntry* replace = tte; for (int i = 1; i < ClusterSize; ++i) - // Due to our packed storage format for generation and its cyclic - // nature we add GENERATION_CYCLE (256 is the modulus, plus what - // is needed to keep the unrelated lowest n bits from affecting - // the result) to calculate the entry age correctly even after - // generation8 overflows into the next cycle. - if (replace->depth8 - - ((GENERATION_CYCLE + generation8 - replace->genBound8) & GENERATION_MASK) - > tte[i].depth8 - - ((GENERATION_CYCLE + generation8 - tte[i].genBound8) & GENERATION_MASK)) + if (replace->depth8 - replace->relative_age(generation8) + > tte[i].depth8 - tte[i].relative_age(generation8)) replace = &tte[i]; return found = false, replace; @@ -137,7 +144,7 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const { // Returns an approximation of the hashtable // occupation during a search. The hash is x permill full, as per UCI protocol. - +// Only counts entries which match the current generation. int TranspositionTable::hashfull() const { int cnt = 0; diff --git a/src/tt.h b/src/tt.h index 4115ee7ae51..554a81a572f 100644 --- a/src/tt.h +++ b/src/tt.h @@ -46,6 +46,8 @@ struct TTEntry { 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, uint8_t generation8); + // The returned age is a multiple of TranspositionTable::GENERATION_DELTA + uint8_t relative_age(const uint8_t generation8) const; private: friend class TranspositionTable; @@ -76,16 +78,25 @@ class TranspositionTable { static_assert(sizeof(Cluster) == 32, "Unexpected Cluster size"); // Constants used to refresh the hash table periodically - static constexpr unsigned GENERATION_BITS = 3; // nb of bits reserved for other things - static constexpr int GENERATION_DELTA = - (1 << GENERATION_BITS); // increment for generation field - static constexpr int GENERATION_CYCLE = 255 + (1 << GENERATION_BITS); // cycle length - static constexpr int GENERATION_MASK = - (0xFF << GENERATION_BITS) & 0xFF; // mask to pull out generation number + + // We have 8 bits available where the lowest 3 bits are + // reserved for other things. + static constexpr unsigned GENERATION_BITS = 3; + // increment for generation field + static constexpr int GENERATION_DELTA = (1 << GENERATION_BITS); + // cycle length + static constexpr int GENERATION_CYCLE = 255 + GENERATION_DELTA; + // mask to pull out generation number + static constexpr int GENERATION_MASK = (0xFF << GENERATION_BITS) & 0xFF; public: ~TranspositionTable() { aligned_large_pages_free(table); } - void new_search() { generation8 += GENERATION_DELTA; } // Lower bits are used for other things + + void new_search() { + // increment by delta to keep lower bits as is + generation8 += GENERATION_DELTA; + } + TTEntry* probe(const Key key, bool& found) const; int hashfull() const; void resize(size_t mbSize, int threadCount); From 7831131591fca89714a376099d6581ec0242244f Mon Sep 17 00:00:00 2001 From: mstembera Date: Thu, 29 Feb 2024 14:27:00 -0800 Subject: [PATCH 1387/1766] Only evaluate the PSQT part of the small net for large evals. Thanks to Viren6 for suggesting to set complexity to 0. STC https://tests.stockfishchess.org/tests/view/65d7d6709b2da0226a5a203f LLR: 2.92 (-2.94,2.94) <0.00,2.00> Total: 328384 W: 85316 L: 84554 D: 158514 Ptnml(0-2): 1414, 39076, 82486, 39766, 1450 LTC https://tests.stockfishchess.org/tests/view/65dce6d290f639b028a54d2e LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 165162 W: 41918 L: 41330 D: 81914 Ptnml(0-2): 102, 18332, 45124, 18922, 101 closes https://github.com/official-stockfish/Stockfish/pull/5083 bench: 1504003 --- src/evaluate.cpp | 5 +- src/nnue/evaluate_nnue.cpp | 47 +++--- src/nnue/evaluate_nnue.h | 5 +- src/nnue/nnue_accumulator.h | 1 + src/nnue/nnue_feature_transformer.h | 252 +++++++++++++++------------- src/position.cpp | 23 ++- 6 files changed, 189 insertions(+), 144 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index f22c0d06cbe..cd026036b4c 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -194,11 +194,12 @@ Value Eval::evaluate(const Position& pos, int optimism) { int simpleEval = simple_eval(pos, pos.side_to_move()); bool smallNet = std::abs(simpleEval) > 1050; + bool psqtOnly = std::abs(simpleEval) > 2500; int nnueComplexity; - Value nnue = smallNet ? NNUE::evaluate(pos, true, &nnueComplexity) - : NNUE::evaluate(pos, true, &nnueComplexity); + Value nnue = smallNet ? NNUE::evaluate(pos, true, &nnueComplexity, psqtOnly) + : NNUE::evaluate(pos, true, &nnueComplexity, false); // Blend optimism and eval with nnue complexity and material imbalance optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / 512; diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 5bd7e83d22f..efcf5b01734 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -179,16 +179,16 @@ write_parameters(std::ostream& stream, NetSize netSize, const std::string& netDe void hint_common_parent_position(const Position& pos) { - int simpleEval = simple_eval(pos, pos.side_to_move()); - if (std::abs(simpleEval) > 1050) - featureTransformerSmall->hint_common_access(pos); + int simpleEvalAbs = std::abs(simple_eval(pos, pos.side_to_move())); + if (simpleEvalAbs > 1050) + featureTransformerSmall->hint_common_access(pos, simpleEvalAbs > 2500); else - featureTransformerBig->hint_common_access(pos); + featureTransformerBig->hint_common_access(pos, false); } // Evaluation function. Perform differential calculation. template -Value evaluate(const Position& pos, bool adjusted, int* complexity) { +Value evaluate(const Position& pos, bool adjusted, int* complexity, bool psqtOnly) { // We manually align the arrays on the stack because with gcc < 9.3 // overaligning stack variables with alignas() doesn't work correctly. @@ -213,15 +213,19 @@ Value evaluate(const Position& pos, bool adjusted, int* complexity) { ASSERT_ALIGNED(transformedFeatures, alignment); - const int bucket = (pos.count() - 1) / 4; - const auto psqt = Net_Size == Small - ? featureTransformerSmall->transform(pos, transformedFeatures, bucket) - : featureTransformerBig->transform(pos, transformedFeatures, bucket); - const auto positional = Net_Size == Small ? networkSmall[bucket]->propagate(transformedFeatures) - : networkBig[bucket]->propagate(transformedFeatures); + const int bucket = (pos.count() - 1) / 4; + const auto psqt = + Net_Size == Small + ? featureTransformerSmall->transform(pos, transformedFeatures, bucket, psqtOnly) + : featureTransformerBig->transform(pos, transformedFeatures, bucket, psqtOnly); + + const auto positional = + !psqtOnly ? (Net_Size == Small ? networkSmall[bucket]->propagate(transformedFeatures) + : networkBig[bucket]->propagate(transformedFeatures)) + : 0; if (complexity) - *complexity = std::abs(psqt - positional) / OutputScale; + *complexity = !psqtOnly ? std::abs(psqt - positional) / OutputScale : 0; // Give more value to positional evaluation when adjusted flag is set if (adjusted) @@ -231,8 +235,8 @@ Value evaluate(const Position& pos, bool adjusted, int* complexity) { return static_cast((psqt + positional) / OutputScale); } -template Value evaluate(const Position& pos, bool adjusted, int* complexity); -template Value evaluate(const Position& pos, bool adjusted, int* complexity); +template Value evaluate(const Position& pos, bool adjusted, int* complexity, bool psqtOnly); +template Value evaluate(const Position& pos, bool adjusted, int* complexity, bool psqtOnly); struct NnueEvalTrace { static_assert(LayerStacks == PSQTBuckets); @@ -265,8 +269,9 @@ static NnueEvalTrace trace_evaluate(const Position& pos) { t.correctBucket = (pos.count() - 1) / 4; for (IndexType bucket = 0; bucket < LayerStacks; ++bucket) { - const auto materialist = featureTransformerBig->transform(pos, transformedFeatures, bucket); - const auto positional = networkBig[bucket]->propagate(transformedFeatures); + const auto materialist = + featureTransformerBig->transform(pos, transformedFeatures, bucket, false); + const auto positional = networkBig[bucket]->propagate(transformedFeatures); t.psqt[bucket] = static_cast(materialist / OutputScale); t.positional[bucket] = static_cast(positional / OutputScale); @@ -370,16 +375,18 @@ std::string trace(Position& pos) { auto st = pos.state(); pos.remove_piece(sq); - st->accumulatorBig.computed[WHITE] = false; - st->accumulatorBig.computed[BLACK] = false; + st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] = + st->accumulatorBig.computedPSQT[WHITE] = st->accumulatorBig.computedPSQT[BLACK] = + false; Value eval = evaluate(pos); eval = pos.side_to_move() == WHITE ? eval : -eval; v = base - eval; pos.put_piece(pc, sq); - st->accumulatorBig.computed[WHITE] = false; - st->accumulatorBig.computed[BLACK] = false; + st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] = + st->accumulatorBig.computedPSQT[WHITE] = st->accumulatorBig.computedPSQT[BLACK] = + false; } writeSquare(f, r, pc, v); diff --git a/src/nnue/evaluate_nnue.h b/src/nnue/evaluate_nnue.h index ea88f890227..c7b378604c5 100644 --- a/src/nnue/evaluate_nnue.h +++ b/src/nnue/evaluate_nnue.h @@ -76,7 +76,10 @@ using LargePagePtr = std::unique_ptr>; std::string trace(Position& pos); template -Value evaluate(const Position& pos, bool adjusted = false, int* complexity = nullptr); +Value evaluate(const Position& pos, + bool adjusted = false, + int* complexity = nullptr, + bool psqtOnly = false); void hint_common_parent_position(const Position& pos); std::optional load_eval(std::istream& stream, NetSize netSize); diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index 0b05d00da28..c0746b4ee86 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -34,6 +34,7 @@ struct alignas(CacheLineSize) Accumulator { std::int16_t accumulation[2][Size]; std::int32_t psqtAccumulation[2][PSQTBuckets]; bool computed[2]; + bool computedPSQT[2]; }; } // namespace Stockfish::Eval::NNUE diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 3399b82df6a..b42f160475f 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -250,18 +250,21 @@ class FeatureTransformer { } // Convert input features - std::int32_t transform(const Position& pos, OutputType* output, int bucket) const { - update_accumulator(pos); - update_accumulator(pos); + std::int32_t + transform(const Position& pos, OutputType* output, int bucket, bool psqtOnly) const { + update_accumulator(pos, psqtOnly); + update_accumulator(pos, psqtOnly); const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()}; - const auto& accumulation = (pos.state()->*accPtr).accumulation; const auto& psqtAccumulation = (pos.state()->*accPtr).psqtAccumulation; - - const auto psqt = + const auto psqt = (psqtAccumulation[perspectives[0]][bucket] - psqtAccumulation[perspectives[1]][bucket]) / 2; + if (psqtOnly) + return psqt; + + const auto& accumulation = (pos.state()->*accPtr).accumulation; for (IndexType p = 0; p < 2; ++p) { @@ -312,20 +315,22 @@ class FeatureTransformer { return psqt; } // end of function transform() - void hint_common_access(const Position& pos) const { - hint_common_access_for_perspective(pos); - hint_common_access_for_perspective(pos); + void hint_common_access(const Position& pos, bool psqtOnly) const { + hint_common_access_for_perspective(pos, psqtOnly); + hint_common_access_for_perspective(pos, psqtOnly); } private: template [[nodiscard]] std::pair - try_find_computed_accumulator(const Position& pos) const { + try_find_computed_accumulator(const Position& pos, bool psqtOnly) const { // Look for a usable accumulator of an earlier position. We keep track // of the estimated gain in terms of features to be added/subtracted. StateInfo *st = pos.state(), *next = nullptr; int gain = FeatureSet::refresh_cost(pos); - while (st->previous && !(st->*accPtr).computed[Perspective]) + while (st->previous + && (!(st->*accPtr).computedPSQT[Perspective] + || (!psqtOnly && !(st->*accPtr).computed[Perspective]))) { // This governs when a full feature refresh is needed and how many // updates are better than just one full refresh. @@ -347,7 +352,8 @@ class FeatureTransformer { template void update_accumulator_incremental(const Position& pos, StateInfo* computed_st, - StateInfo* states_to_update[N]) const { + StateInfo* states_to_update[N], + bool psqtOnly) const { static_assert(N > 0); assert(states_to_update[N - 1] == nullptr); @@ -383,7 +389,8 @@ class FeatureTransformer { for (; i >= 0; --i) { - (states_to_update[i]->*accPtr).computed[Perspective] = true; + (states_to_update[i]->*accPtr).computed[Perspective] = !psqtOnly; + (states_to_update[i]->*accPtr).computedPSQT[Perspective] = true; const StateInfo* end_state = i == 0 ? computed_st : states_to_update[i - 1]; @@ -403,31 +410,34 @@ class FeatureTransformer { { assert(states_to_update[0]); - auto accIn = - reinterpret_cast(&(st->*accPtr).accumulation[Perspective][0]); - auto accOut = reinterpret_cast( - &(states_to_update[0]->*accPtr).accumulation[Perspective][0]); + if (!psqtOnly) + { + auto accIn = + reinterpret_cast(&(st->*accPtr).accumulation[Perspective][0]); + auto accOut = reinterpret_cast( + &(states_to_update[0]->*accPtr).accumulation[Perspective][0]); - const IndexType offsetR0 = HalfDimensions * removed[0][0]; - auto columnR0 = reinterpret_cast(&weights[offsetR0]); - const IndexType offsetA = HalfDimensions * added[0][0]; - auto columnA = reinterpret_cast(&weights[offsetA]); + const IndexType offsetR0 = HalfDimensions * removed[0][0]; + auto columnR0 = reinterpret_cast(&weights[offsetR0]); + const IndexType offsetA = HalfDimensions * added[0][0]; + auto columnA = reinterpret_cast(&weights[offsetA]); - if (removed[0].size() == 1) - { - for (IndexType k = 0; k < HalfDimensions * sizeof(std::int16_t) / sizeof(vec_t); - ++k) - accOut[k] = vec_add_16(vec_sub_16(accIn[k], columnR0[k]), columnA[k]); - } - else - { - const IndexType offsetR1 = HalfDimensions * removed[0][1]; - auto columnR1 = reinterpret_cast(&weights[offsetR1]); + if (removed[0].size() == 1) + { + for (IndexType k = 0; k < HalfDimensions * sizeof(std::int16_t) / sizeof(vec_t); + ++k) + accOut[k] = vec_add_16(vec_sub_16(accIn[k], columnR0[k]), columnA[k]); + } + else + { + const IndexType offsetR1 = HalfDimensions * removed[0][1]; + auto columnR1 = reinterpret_cast(&weights[offsetR1]); - for (IndexType k = 0; k < HalfDimensions * sizeof(std::int16_t) / sizeof(vec_t); - ++k) - accOut[k] = vec_sub_16(vec_add_16(accIn[k], columnA[k]), - vec_add_16(columnR0[k], columnR1[k])); + for (IndexType k = 0; k < HalfDimensions * sizeof(std::int16_t) / sizeof(vec_t); + ++k) + accOut[k] = vec_sub_16(vec_add_16(accIn[k], columnA[k]), + vec_add_16(columnR0[k], columnR1[k])); + } } auto accPsqtIn = @@ -461,41 +471,43 @@ class FeatureTransformer { } else { - for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) - { - // Load accumulator - auto accTileIn = reinterpret_cast( - &(st->*accPtr).accumulation[Perspective][j * TileHeight]); - for (IndexType k = 0; k < NumRegs; ++k) - acc[k] = vec_load(&accTileIn[k]); - - for (IndexType i = 0; states_to_update[i]; ++i) + if (!psqtOnly) + for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) { - // Difference calculation for the deactivated features - for (const auto index : removed[i]) - { - const IndexType offset = HalfDimensions * index + j * TileHeight; - auto column = reinterpret_cast(&weights[offset]); - for (IndexType k = 0; k < NumRegs; ++k) - acc[k] = vec_sub_16(acc[k], column[k]); - } + // Load accumulator + auto accTileIn = reinterpret_cast( + &(st->*accPtr).accumulation[Perspective][j * TileHeight]); + for (IndexType k = 0; k < NumRegs; ++k) + acc[k] = vec_load(&accTileIn[k]); - // Difference calculation for the activated features - for (const auto index : added[i]) + for (IndexType i = 0; states_to_update[i]; ++i) { - const IndexType offset = HalfDimensions * index + j * TileHeight; - auto column = reinterpret_cast(&weights[offset]); + // Difference calculation for the deactivated features + for (const auto index : removed[i]) + { + const IndexType offset = HalfDimensions * index + j * TileHeight; + auto column = reinterpret_cast(&weights[offset]); + for (IndexType k = 0; k < NumRegs; ++k) + acc[k] = vec_sub_16(acc[k], column[k]); + } + + // Difference calculation for the activated features + for (const auto index : added[i]) + { + const IndexType offset = HalfDimensions * index + j * TileHeight; + auto column = reinterpret_cast(&weights[offset]); + for (IndexType k = 0; k < NumRegs; ++k) + acc[k] = vec_add_16(acc[k], column[k]); + } + + // Store accumulator + auto accTileOut = + reinterpret_cast(&(states_to_update[i]->*accPtr) + .accumulation[Perspective][j * TileHeight]); for (IndexType k = 0; k < NumRegs; ++k) - acc[k] = vec_add_16(acc[k], column[k]); + vec_store(&accTileOut[k], acc[k]); } - - // Store accumulator - auto accTileOut = reinterpret_cast( - &(states_to_update[i]->*accPtr).accumulation[Perspective][j * TileHeight]); - for (IndexType k = 0; k < NumRegs; ++k) - vec_store(&accTileOut[k], acc[k]); } - } for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j) { @@ -537,8 +549,10 @@ class FeatureTransformer { #else for (IndexType i = 0; states_to_update[i]; ++i) { - std::memcpy((states_to_update[i]->*accPtr).accumulation[Perspective], - (st->*accPtr).accumulation[Perspective], HalfDimensions * sizeof(BiasType)); + if (!psqtOnly) + std::memcpy((states_to_update[i]->*accPtr).accumulation[Perspective], + (st->*accPtr).accumulation[Perspective], + HalfDimensions * sizeof(BiasType)); for (std::size_t k = 0; k < PSQTBuckets; ++k) (states_to_update[i]->*accPtr).psqtAccumulation[Perspective][k] = @@ -549,10 +563,12 @@ class FeatureTransformer { // Difference calculation for the deactivated features for (const auto index : removed[i]) { - const IndexType offset = HalfDimensions * index; - - for (IndexType j = 0; j < HalfDimensions; ++j) - (st->*accPtr).accumulation[Perspective][j] -= weights[offset + j]; + if (!psqtOnly) + { + const IndexType offset = HalfDimensions * index; + for (IndexType j = 0; j < HalfDimensions; ++j) + (st->*accPtr).accumulation[Perspective][j] -= weights[offset + j]; + } for (std::size_t k = 0; k < PSQTBuckets; ++k) (st->*accPtr).psqtAccumulation[Perspective][k] -= @@ -562,10 +578,12 @@ class FeatureTransformer { // Difference calculation for the activated features for (const auto index : added[i]) { - const IndexType offset = HalfDimensions * index; - - for (IndexType j = 0; j < HalfDimensions; ++j) - (st->*accPtr).accumulation[Perspective][j] += weights[offset + j]; + if (!psqtOnly) + { + const IndexType offset = HalfDimensions * index; + for (IndexType j = 0; j < HalfDimensions; ++j) + (st->*accPtr).accumulation[Perspective][j] += weights[offset + j]; + } for (std::size_t k = 0; k < PSQTBuckets; ++k) (st->*accPtr).psqtAccumulation[Perspective][k] += @@ -576,7 +594,7 @@ class FeatureTransformer { } template - void update_accumulator_refresh(const Position& pos) const { + void update_accumulator_refresh(const Position& pos, bool psqtOnly) const { #ifdef VECTOR // Gcc-10.2 unnecessarily spills AVX2 registers if this array // is defined in the VECTOR code below, once in each branch @@ -587,32 +605,34 @@ class FeatureTransformer { // Refresh the accumulator // Could be extracted to a separate function because it's done in 2 places, // but it's unclear if compilers would correctly handle register allocation. - auto& accumulator = pos.state()->*accPtr; - accumulator.computed[Perspective] = true; + auto& accumulator = pos.state()->*accPtr; + accumulator.computed[Perspective] = !psqtOnly; + accumulator.computedPSQT[Perspective] = true; FeatureSet::IndexList active; FeatureSet::append_active_indices(pos, active); #ifdef VECTOR - for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) - { - auto biasesTile = reinterpret_cast(&biases[j * TileHeight]); - for (IndexType k = 0; k < NumRegs; ++k) - acc[k] = biasesTile[k]; - - for (const auto index : active) + if (!psqtOnly) + for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) { - const IndexType offset = HalfDimensions * index + j * TileHeight; - auto column = reinterpret_cast(&weights[offset]); + auto biasesTile = reinterpret_cast(&biases[j * TileHeight]); + for (IndexType k = 0; k < NumRegs; ++k) + acc[k] = biasesTile[k]; - for (unsigned k = 0; k < NumRegs; ++k) - acc[k] = vec_add_16(acc[k], column[k]); - } + for (const auto index : active) + { + const IndexType offset = HalfDimensions * index + j * TileHeight; + auto column = reinterpret_cast(&weights[offset]); - auto accTile = - reinterpret_cast(&accumulator.accumulation[Perspective][j * TileHeight]); - for (unsigned k = 0; k < NumRegs; k++) - vec_store(&accTile[k], acc[k]); - } + for (unsigned k = 0; k < NumRegs; ++k) + acc[k] = vec_add_16(acc[k], column[k]); + } + + auto accTile = + reinterpret_cast(&accumulator.accumulation[Perspective][j * TileHeight]); + for (unsigned k = 0; k < NumRegs; k++) + vec_store(&accTile[k], acc[k]); + } for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j) { @@ -635,18 +655,21 @@ class FeatureTransformer { } #else - std::memcpy(accumulator.accumulation[Perspective], biases, - HalfDimensions * sizeof(BiasType)); + if (!psqtOnly) + std::memcpy(accumulator.accumulation[Perspective], biases, + HalfDimensions * sizeof(BiasType)); for (std::size_t k = 0; k < PSQTBuckets; ++k) accumulator.psqtAccumulation[Perspective][k] = 0; for (const auto index : active) { - const IndexType offset = HalfDimensions * index; - - for (IndexType j = 0; j < HalfDimensions; ++j) - accumulator.accumulation[Perspective][j] += weights[offset + j]; + if (!psqtOnly) + { + const IndexType offset = HalfDimensions * index; + for (IndexType j = 0; j < HalfDimensions; ++j) + accumulator.accumulation[Perspective][j] += weights[offset + j]; + } for (std::size_t k = 0; k < PSQTBuckets; ++k) accumulator.psqtAccumulation[Perspective][k] += @@ -656,7 +679,7 @@ class FeatureTransformer { } template - void hint_common_access_for_perspective(const Position& pos) const { + void hint_common_access_for_perspective(const Position& pos, bool psqtOnly) const { // Works like update_accumulator, but performs less work. // Updates ONLY the accumulator for pos. @@ -664,27 +687,31 @@ class FeatureTransformer { // Look for a usable accumulator of an earlier position. We keep track // of the estimated gain in terms of features to be added/subtracted. // Fast early exit. - if ((pos.state()->*accPtr).computed[Perspective]) + if ((pos.state()->*accPtr).computed[Perspective] + || (psqtOnly && (pos.state()->*accPtr).computedPSQT[Perspective])) return; - auto [oldest_st, _] = try_find_computed_accumulator(pos); + auto [oldest_st, _] = try_find_computed_accumulator(pos, psqtOnly); - if ((oldest_st->*accPtr).computed[Perspective]) + if ((oldest_st->*accPtr).computed[Perspective] + || (psqtOnly && (oldest_st->*accPtr).computedPSQT[Perspective])) { // Only update current position accumulator to minimize work. StateInfo* states_to_update[2] = {pos.state(), nullptr}; - update_accumulator_incremental(pos, oldest_st, states_to_update); + update_accumulator_incremental(pos, oldest_st, states_to_update, + psqtOnly); } else - update_accumulator_refresh(pos); + update_accumulator_refresh(pos, psqtOnly); } template - void update_accumulator(const Position& pos) const { + void update_accumulator(const Position& pos, bool psqtOnly) const { - auto [oldest_st, next] = try_find_computed_accumulator(pos); + auto [oldest_st, next] = try_find_computed_accumulator(pos, psqtOnly); - if ((oldest_st->*accPtr).computed[Perspective]) + if ((oldest_st->*accPtr).computed[Perspective] + || (psqtOnly && (oldest_st->*accPtr).computedPSQT[Perspective])) { if (next == nullptr) return; @@ -697,12 +724,11 @@ class FeatureTransformer { StateInfo* states_to_update[3] = {next, next == pos.state() ? nullptr : pos.state(), nullptr}; - update_accumulator_incremental(pos, oldest_st, states_to_update); + update_accumulator_incremental(pos, oldest_st, states_to_update, + psqtOnly); } else - { - update_accumulator_refresh(pos); - } + update_accumulator_refresh(pos, psqtOnly); } alignas(CacheLineSize) BiasType biases[HalfDimensions]; diff --git a/src/position.cpp b/src/position.cpp index c89b1eb0889..2263afe7669 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -680,10 +680,14 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { ++st->pliesFromNull; // Used by NNUE - st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] = - st->accumulatorSmall.computed[WHITE] = st->accumulatorSmall.computed[BLACK] = false; - auto& dp = st->dirtyPiece; - dp.dirty_num = 1; + st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] = + st->accumulatorBig.computedPSQT[WHITE] = st->accumulatorBig.computedPSQT[BLACK] = + st->accumulatorSmall.computed[WHITE] = st->accumulatorSmall.computed[BLACK] = + st->accumulatorSmall.computedPSQT[WHITE] = st->accumulatorSmall.computedPSQT[BLACK] = + false; + + auto& dp = st->dirtyPiece; + dp.dirty_num = 1; Color us = sideToMove; Color them = ~us; @@ -965,10 +969,13 @@ void Position::do_null_move(StateInfo& newSt, TranspositionTable& tt) { newSt.previous = st; st = &newSt; - st->dirtyPiece.dirty_num = 0; - st->dirtyPiece.piece[0] = NO_PIECE; // Avoid checks in UpdateAccumulator() - st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] = - st->accumulatorSmall.computed[WHITE] = st->accumulatorSmall.computed[BLACK] = false; + st->dirtyPiece.dirty_num = 0; + st->dirtyPiece.piece[0] = NO_PIECE; // Avoid checks in UpdateAccumulator() + st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] = + st->accumulatorBig.computedPSQT[WHITE] = st->accumulatorBig.computedPSQT[BLACK] = + st->accumulatorSmall.computed[WHITE] = st->accumulatorSmall.computed[BLACK] = + st->accumulatorSmall.computedPSQT[WHITE] = st->accumulatorSmall.computedPSQT[BLACK] = + false; if (st->epSquare != SQ_NONE) { From 6d0d430860fb72137339c001b991f2f4440e45eb Mon Sep 17 00:00:00 2001 From: Gahtan Nahdi <155860115+gahtan-syarif@users.noreply.github.com> Date: Sun, 18 Feb 2024 13:47:52 +0700 Subject: [PATCH 1388/1766] Simplify IIR Simplified depth reduction for PV nodes without a ttMove to 3. Passed STC non-reg: https://tests.stockfishchess.org/tests/view/65d1a90a1d8e83c78bfd855a LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 363168 W: 93648 L: 93791 D: 175729 Ptnml(0-2): 1557, 43692, 91221, 43565, 1549 Passed LTC non-reg: https://tests.stockfishchess.org/tests/view/65d5612d1d8e83c78bfdc8e2 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 58818 W: 14946 L: 14761 D: 29111 Ptnml(0-2): 36, 6595, 15962, 6780, 36 closes https://github.com/official-stockfish/Stockfish/pull/5062 Bench: 1505827 --- src/search.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 3de1f69b817..951e206c4b9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -805,13 +805,11 @@ Value Search::Worker::search( } // Step 10. Internal iterative reductions (~9 Elo) - // For PV nodes without a ttMove, we decrease depth by 2, - // or by 4 if the current position is present in the TT and - // the stored depth is greater than or equal to the current depth. - // Use qsearch if depth <= 0. + // For PV nodes without a ttMove, we decrease depth by 3. if (PvNode && !ttMove) - depth -= 2 + 2 * (ss->ttHit && tte->depth() >= depth); + depth -= 3; + // Use qsearch if depth <= 0. if (depth <= 0) return qsearch(pos, ss, alpha, beta); From b0ac8a4e3bad2ed1b066850b6f151ddbe48d1dc6 Mon Sep 17 00:00:00 2001 From: Gahtan Nahdi <155860115+gahtan-syarif@users.noreply.github.com> Date: Thu, 22 Feb 2024 04:37:26 +0700 Subject: [PATCH 1389/1766] Simplify extension when ttMove is assumed to fail high over current beta Simplify extension value to -3 when ttMove is assumed to fail high over current beta. Passed non-reg STC: https://tests.stockfishchess.org/tests/view/65d66ed81d8e83c78bfddcba LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 235136 W: 60711 L: 60708 D: 113717 Ptnml(0-2): 969, 27904, 59874, 27797, 1024 Passed non-reg LTC: https://tests.stockfishchess.org/tests/view/65da2994944f2a78d4733107 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 662850 W: 166161 L: 166602 D: 330087 Ptnml(0-2): 394, 74895, 181274, 74482, 380 closes https://github.com/official-stockfish/Stockfish/pull/5088 Bench: 1553115 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 951e206c4b9..70baffe348f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1056,7 +1056,7 @@ Value Search::Worker::search( // If the ttMove is assumed to fail high over current beta (~7 Elo) else if (ttValue >= beta) - extension = -2 - !PvNode; + extension = -3; // If we are on a cutNode but the ttMove is not assumed to fail high over current beta (~1 Elo) else if (cutNode) From a615efb19f5dfb4b205ed3a9dd8525e54e8777cc Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Mon, 26 Feb 2024 18:08:22 +0300 Subject: [PATCH 1390/1766] Simplify Time Management Instead of having a formula for using extra time with larger increments. Simply set it to 1 when the increment is lower than 0.5s and to 1.1 when the increment is higher. The values can later on be further improved. Passed STC: https://tests.stockfishchess.org/tests/view/65d25d3c1d8e83c78bfd9293 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 27488 W: 7077 L: 6848 D: 13563 Ptnml(0-2): 96, 3041, 7267, 3218, 122 Passed LTC: https://tests.stockfishchess.org/tests/view/65d2a72c1d8e83c78bfd97fa LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 137568 W: 34612 L: 34512 D: 68444 Ptnml(0-2): 60, 14672, 39221, 14770, 61 Passed VLTC: https://tests.stockfishchess.org/tests/view/65d7d7d39b2da0226a5a205b LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 139650 W: 35229 L: 35134 D: 69287 Ptnml(0-2): 33, 14227, 41218, 14306, 41 Passed also the TCEC TC style suggested by vondele: https://tests.stockfishchess.org/tests/view/65e4ca73416ecd92c162a57d LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 134150 W: 34278 L: 34163 D: 65709 Ptnml(0-2): 561, 15727, 34444, 15722, 621 closes https://github.com/official-stockfish/Stockfish/pull/5076 Bench: 1553115 --- src/timeman.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/timeman.cpp b/src/timeman.cpp index 72a447af5b8..e620dede7c9 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -94,7 +94,7 @@ void TimeManagement::init(Search::LimitsType& limits, if (limits.movestogo == 0) { // Use extra time with larger increments - double optExtra = std::clamp(1.0 + 12.5 * limits.inc[us] / limits.time[us], 1.0, 1.11); + double optExtra = limits.inc[us] < 500 ? 1.0 : 1.1; // Calculate time constants based on current time left. double optConstant = From a96b0d46093c67707e4e75e7aa5aa057b7c131a2 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Mon, 4 Mar 2024 16:13:36 +0300 Subject: [PATCH 1391/1766] Update elo estimates Tests used to change the elo worth of some functions: https://tests.stockfishchess.org/tests/view/65c3f69dc865510db0283eef https://tests.stockfishchess.org/tests/view/65c3f935c865510db0283f2a https://tests.stockfishchess.org/tests/view/65d1489f1d8e83c78bfd7dbf https://tests.stockfishchess.org/tests/view/65ce9d361d8e83c78bfd4951 https://tests.stockfishchess.org/tests/view/65cfcd901d8e83c78bfd6184 closes https://github.com/official-stockfish/Stockfish/pull/5089 No functional change --- src/search.cpp | 8 ++++---- src/timeman.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 70baffe348f..8ed7841e2c1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -614,7 +614,7 @@ Value Search::Worker::search( update_quiet_stats(pos, ss, *this, ttMove, stat_bonus(depth)); // Extra penalty for early quiet moves of - // the previous ply (~0 Elo on STC, ~2 Elo on LTC). + // the previous ply (~1 Elo on STC, ~2 Elo on LTC) if (prevSq != SQ_NONE && (ss - 1)->moveCount <= 2 && !priorCapture) update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, -stat_malus(depth + 1)); @@ -1067,7 +1067,7 @@ Value Search::Worker::search( extension = -1; } - // Recapture extensions (~1 Elo) + // Recapture extensions (~0 Elo on STC, ~1 Elo on LTC) else if (PvNode && move == ttMove && move.to_sq() == prevSq && thisThread->captureHistory[movedPiece][move.to_sq()] [type_of(pos.piece_on(move.to_sq()))] @@ -1105,7 +1105,7 @@ Value Search::Worker::search( if (ttCapture) r++; - // Decrease reduction for PvNodes (~3 Elo) + // Decrease reduction for PvNodes (~0 Elo on STC, ~2 Elo on LTC) if (PvNode) r--; @@ -1167,7 +1167,7 @@ Value Search::Worker::search( // Step 18. Full-depth search when LMR is skipped else if (!PvNode || moveCount > 1) { - // Increase reduction if ttMove is not present (~1 Elo) + // Increase reduction if ttMove is not present (~6 Elo) if (!ttMove) r += 2; diff --git a/src/timeman.cpp b/src/timeman.cpp index e620dede7c9..b64ec77388f 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -89,8 +89,8 @@ void TimeManagement::init(Search::LimitsType& limits, - moveOverhead * (2 + mtg)); // x basetime (+ z increment) - // If there is a healthy increment, timeLeft can exceed actual available - // game time for the current move, so also cap to 20% of available game time. + // If there is a healthy increment, timeLeft can exceed the actual available + // game time for the current move, so also cap to a percentage of available game time. if (limits.movestogo == 0) { // Use extra time with larger increments From bd579ab5d1a931a09a62f2ed33b5149ada7bc65f Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Fri, 1 Mar 2024 10:34:03 -0800 Subject: [PATCH 1392/1766] Update default main net to nn-1ceb1ade0001.nnue Created by retraining the previous main net `nn-b1a57edbea57.nnue` with: - some of the same options as before: - ranger21, more WDL skipping, 15% more loss when Q is too high - removal of the huge 514G pre-interleaved binpack - removal of SF-generated dfrc data (dfrc99-16tb7p-filt-v2.min.binpack) - interleaving many binpacks at training time - training with some bestmove capture positions where SEE < 0 - increased usage of torch.compile to speed up training by up to 40% ```yaml experiment-name: 2560--S10-dfrc0-to-dec2023-skip-more-wdl-15p-more-loss-high-q-see-ge0-sk28 nnue-pytorch-branch: linrock/nnue-pytorch/r21-more-wdl-skip-15p-more-loss-high-q-skip-see-ge0-torch-compile-more start-from-engine-test-net: True early-fen-skipping: 28 training-dataset: # similar, not the exact same as: # https://github.com/official-stockfish/Stockfish/pull/4635 - /data/S5-5af/leela96.v2.min.binpack - /data/S5-5af/test60-2021-11-12-novdec-12tb7p.v6-dd.min.binpack - /data/S5-5af/test77-2021-12-dec-16tb7p.v6-dd.min.binpack - /data/S5-5af/test78-2022-01-to-05-jantomay-16tb7p.v6-dd.min.binpack - /data/S5-5af/test78-2022-06-to-09-juntosep-16tb7p.v6-dd.min.binpack - /data/S5-5af/test79-2022-04-apr-16tb7p.v6-dd.min.binpack - /data/S5-5af/test79-2022-05-may-16tb7p.v6-dd.min.binpack - /data/S5-5af/test80-2022-06-jun-16tb7p.v6-dd.min.unmin.binpack - /data/S5-5af/test80-2022-07-jul-16tb7p.v6-dd.min.binpack - /data/S5-5af/test80-2022-08-aug-16tb7p.v6-dd.min.binpack - /data/S5-5af/test80-2022-09-sep-16tb7p.v6-dd.min.unmin.binpack - /data/S5-5af/test80-2022-10-oct-16tb7p.v6-dd.min.binpack - /data/S5-5af/test80-2022-11-nov-16tb7p.v6-dd.min.binpack - /data/S5-5af/test80-2023-01-jan-16tb7p.v6-sk20.min.binpack - /data/S5-5af/test80-2023-02-feb-16tb7p.v6-dd.min.binpack - /data/S5-5af/test80-2023-03-mar-2tb7p.min.unmin.binpack - /data/S5-5af/test80-2023-04-apr-2tb7p.binpack - /data/S5-5af/test80-2023-05-may-2tb7p.min.dd.binpack # https://github.com/official-stockfish/Stockfish/pull/4782 - /data/S6-1ee1aba5ed/test80-2023-06-jun-2tb7p.binpack - /data/S6-1ee1aba5ed/test80-2023-07-jul-2tb7p.min.binpack # https://github.com/official-stockfish/Stockfish/pull/4972 - /data/S8-baff1edbea57/test80-2023-08-aug-2tb7p.v6.min.binpack - /data/S8-baff1edbea57/test80-2023-09-sep-2tb7p.binpack - /data/S8-baff1edbea57/test80-2023-10-oct-2tb7p.binpack # https://github.com/official-stockfish/Stockfish/pull/5056 - /data/S9-b1a57edbea57/test80-2023-11-nov-2tb7p.binpack - /data/S9-b1a57edbea57/test80-2023-12-dec-2tb7p.binpack num-epochs: 800 lr: 4.375e-4 gamma: 0.995 start-lambda: 1.0 end-lambda: 0.7 ``` This particular net was reached at epoch 759. Use of more torch.compile decorators in nnue-pytorch model.py than in the previous main net training run sped up training by up to 40% on Tesla gpus when using recent pytorch compiled with cuda 12: https://github.com/linrock/nnue-tools/blob/7fb9831/Dockerfile Skipping positions with bestmove captures where static exchange evaluation is >= 0 is based on the implementation from Sopel's NNUE training & experimentation log: https://docs.google.com/document/d/1gTlrr02qSNKiXNZ_SuO4-RjK4MXBiFlLE6jvNqqMkAY Experiment 293 - only skip captures with see>=0 Positions with bestmove captures where score == 0 are always skipped for compatibility with minimized binpacks, since the original minimizer sets scores to 0 for slight improvements in compression. The trainer branch used was: https://github.com/linrock/nnue-pytorch/tree/r21-more-wdl-skip-15p-more-loss-high-q-skip-see-ge0-torch-compile-more Binpacks were renamed to be sorted chronologically by default when sorted by name. The binpack data are otherwise the same as binpacks with similar names in the prior naming convention. Training data can be found at: https://robotmoon.com/nnue-training-data/ Passed STC: https://tests.stockfishchess.org/tests/view/65e3ddd1f2ef6c733362ae5c LLR: 2.92 (-2.94,2.94) <0.00,2.00> Total: 149792 W: 39153 L: 38661 D: 71978 Ptnml(0-2): 675, 17586, 37905, 18032, 698 Passed LTC: https://tests.stockfishchess.org/tests/view/65e4d91c416ecd92c162a69b LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 64416 W: 16517 L: 16135 D: 31764 Ptnml(0-2): 38, 7218, 17313, 7602, 37 closes https://github.com/official-stockfish/Stockfish/pull/5090 Bench: 1373183 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 53928bf6494..33fed3d5f5f 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -39,7 +39,7 @@ Value evaluate(const Position& pos, int optimism); // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro, as it is used in the Makefile. -#define EvalFileDefaultNameBig "nn-b1a57edbea57.nnue" +#define EvalFileDefaultNameBig "nn-1ceb1ade0001.nnue" #define EvalFileDefaultNameSmall "nn-baff1ede1f90.nnue" struct EvalFile { From 1db969e6200afe4f023469a56aa5edf755d92bbb Mon Sep 17 00:00:00 2001 From: rn5f107s2 Date: Thu, 15 Feb 2024 23:01:02 +0100 Subject: [PATCH 1393/1766] Reduce futility_margin if opponents last move was bad This reduces the futiltiy_margin if our opponents last move was bad by around ~1/3 when not improving and ~1/2.7 when improving, the idea being to retroactively futility prune moves that were played, but turned out to be bad. A bad move is being defined as their staticEval before their move being lower as our staticEval now is. If the depth is 2 and we are improving the opponent worsening flag is not set, in order to not risk having a too low futility_margin, due to the fact that when these conditions are met the futility_margin already drops quite low. Passed STC: https://tests.stockfishchess.org/tests/live_elo/65e3977bf2ef6c733362aae3 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 122432 W: 31884 L: 31436 D: 59112 Ptnml(0-2): 467, 14404, 31035, 14834, 476 Passed LTC: https://tests.stockfishchess.org/tests/live_elo/65e47f40f2ef6c733362b6d2 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 421692 W: 106572 L: 105452 D: 209668 Ptnml(0-2): 216, 47217, 114865, 48327, 221 closes https://github.com/official-stockfish/Stockfish/pull/5092 Bench: 1565939 --- src/search.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 8ed7841e2c1..f135a0900a6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -53,11 +53,13 @@ using namespace Search; namespace { - // Futility margin -Value futility_margin(Depth d, bool noTtCutNode, bool improving) { - Value futilityMult = 117 - 44 * noTtCutNode; - return (futilityMult * d - 3 * futilityMult / 2 * improving); +Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) { + Value futilityMult = 117 - 44 * noTtCutNode; + Value improvingDeduction = 3 * improving * futilityMult / 2; + Value worseningDeduction = (331 + 45 * improving) * oppWorsening * futilityMult / 1024; + + return futilityMult * d - improvingDeduction - worseningDeduction; } constexpr int futility_move_count(bool improving, Depth depth) { @@ -533,7 +535,7 @@ Value Search::Worker::search( Move ttMove, move, excludedMove, bestMove; Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue, probCutBeta; - bool givesCheck, improving, priorCapture; + bool givesCheck, improving, priorCapture, opponenWorsening; bool capture, moveCountPruning, ttCapture; Piece movedPiece; int moveCount, captureCount, quietCount; @@ -742,6 +744,8 @@ Value Search::Worker::search( ? ss->staticEval > (ss - 2)->staticEval : (ss - 4)->staticEval != VALUE_NONE && ss->staticEval > (ss - 4)->staticEval; + opponenWorsening = ss->staticEval + (ss - 1)->staticEval > 2 && (depth != 2 || !improving); + // Step 7. Razoring (~1 Elo) // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. @@ -756,7 +760,7 @@ Value Search::Worker::search( // Step 8. Futility pruning: child node (~40 Elo) // The depth condition is important for mate finding. if (!ss->ttPv && depth < 11 - && eval - futility_margin(depth, cutNode && !ss->ttHit, improving) + && eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponenWorsening) - (ss - 1)->statScore / 314 >= beta && eval >= beta && eval < 30016 // smaller than TB wins From 6136d094c5f46456964889754ae2d6098834b14f Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Thu, 7 Mar 2024 11:57:18 +0300 Subject: [PATCH 1394/1766] Introduce double extensions for PV nodes Our double/triple extensions were allowed only for non-pv nodes. This patch allows them to be done for PV nodes, with some stricter conditions. Passed STC: https://tests.stockfishchess.org/tests/view/65d657ec1d8e83c78bfddab8 LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 339424 W: 88097 L: 87318 D: 164009 Ptnml(0-2): 1573, 39935, 85729, 41090, 1385 Passed LTC: https://tests.stockfishchess.org/tests/view/65dd63824b19edc854ebc433 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 459564 W: 115812 L: 114614 D: 229138 Ptnml(0-2): 248, 51441, 125173, 52705, 215 closes https://github.com/official-stockfish/Stockfish/pull/5093 Bench: 1714391 --- src/search.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index f135a0900a6..ff32ecc1a0e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1042,6 +1042,8 @@ Value Search::Worker::search( extension = 2 + (value < singularBeta - 78 && !ttCapture); depth += depth < 16; } + if (PvNode && !ttCapture && ss->multipleExtensions <= 5 && value < singularBeta - 50) + extension = 2; } // Multi-cut pruning From 748791f80dbc29793e473e3e9eda83ffa0afcfaa Mon Sep 17 00:00:00 2001 From: "Shahin M. Shahin" <41402573+peregrineshahin@users.noreply.github.com> Date: Wed, 6 Mar 2024 20:56:55 +0300 Subject: [PATCH 1395/1766] Fix `go mate x` in multithreading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes two issues with master for go mate x: - when running go mate x in losing positions, master always goes to the maximal depth, arguably against what the UCI protocol demands - when running go mate x in winning positions with multiple threads, master may return non-mate scores from the search (this issue is present in stockfish since at least sf16) The issues are fixed by (a) also checking if score is mate -x and by (b) only letting mainthread stop the search for go mate x commands, and by not looking for a best thread but using mainthread as per the default. Related: niklasf/python-chess#1070 More diagnostics can be found here peregrineshahin#6 (comment) closes https://github.com/official-stockfish/Stockfish/pull/5094 No functional change Co-Authored-By: Robert Nürnberg <28635489+robertnurnberg@users.noreply.github.com> --- src/search.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ff32ecc1a0e..7bf3e7f96f9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -187,7 +187,7 @@ void Search::Worker::start_searching() { Skill skill = Skill(options["Skill Level"], options["UCI_LimitStrength"] ? int(options["UCI_Elo"]) : 0); - if (int(options["MultiPV"]) == 1 && !limits.depth && !skill.enabled() + if (int(options["MultiPV"]) == 1 && !limits.depth && !limits.mate && !skill.enabled() && rootMoves[0].pv[0] != Move::none()) bestThread = threads.get_best_thread()->worker.get(); @@ -399,14 +399,18 @@ void Search::Worker::iterative_deepening() { lastBestMoveDepth = rootDepth; } - // Have we found a "mate in x"? - if (limits.mate && bestValue >= VALUE_MATE_IN_MAX_PLY - && VALUE_MATE - bestValue <= 2 * limits.mate) - threads.stop = true; - if (!mainThread) continue; + // Have we found a "mate in x"? + if (limits.mate && rootMoves[0].score == rootMoves[0].uciScore + && ((rootMoves[0].score >= VALUE_MATE_IN_MAX_PLY + && VALUE_MATE - rootMoves[0].score <= 2 * limits.mate) + || (rootMoves[0].score != -VALUE_INFINITE + && rootMoves[0].score <= VALUE_TB_LOSS_IN_MAX_PLY + && VALUE_MATE + rootMoves[0].score <= 2 * limits.mate))) + threads.stop = true; + // If the skill level is enabled and time is up, pick a sub-optimal best move if (skill.enabled() && skill.time_to_pick(rootDepth)) skill.pick_best(rootMoves, multiPV); From 0f01a516d2ddd475bbe3bccab176dbbccb879053 Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Mon, 4 Mar 2024 18:48:02 +0800 Subject: [PATCH 1396/1766] VLTC time management tune Result of 35k games of SPSA tuning at 180+1.8. Tuning attempt can be found here: https://tests.stockfishchess.org/tests/view/65e40599f2ef6c733362b03b Passed VLTC 180+1.8: https://tests.stockfishchess.org/tests/view/65e5a6f5416ecd92c162b5d4 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 31950 W: 8225 L: 7949 D: 15776 Ptnml(0-2): 3, 3195, 9309, 3459, 9 Passed VLTC 240+2.4: https://tests.stockfishchess.org/tests/view/65e714de0ec64f0526c3d1f1 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 65108 W: 16558 L: 16202 D: 32348 Ptnml(0-2): 7, 6366, 19449, 6728, 4 closes https://github.com/official-stockfish/Stockfish/pull/5095 Bench: 1714391 --- src/search.cpp | 20 ++++++++++---------- src/timeman.cpp | 14 +++++++------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 7bf3e7f96f9..335149d576f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -429,15 +429,15 @@ void Search::Worker::iterative_deepening() { int nodesEffort = effort[bestmove.from_sq()][bestmove.to_sq()] * 100 / std::max(size_t(1), size_t(nodes)); - double fallingEval = (66 + 14 * (mainThread->bestPreviousAverageScore - bestValue) - + 6 * (mainThread->iterValue[iterIdx] - bestValue)) - / 616.6; - fallingEval = std::clamp(fallingEval, 0.51, 1.51); + double fallingEval = (1067 + 223 * (mainThread->bestPreviousAverageScore - bestValue) + + 97 * (mainThread->iterValue[iterIdx] - bestValue)) + / 10000.0; + fallingEval = std::clamp(fallingEval, 0.580, 1.667); // If the bestMove is stable over several iterations, reduce time accordingly - timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.56 : 0.69; - double reduction = (1.4 + mainThread->previousTimeReduction) / (2.17 * timeReduction); - double bestMoveInstability = 1 + 1.79 * totBestMoveChanges / threads.size(); + timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.495 : 0.687; + double reduction = (1.48 + mainThread->previousTimeReduction) / (2.17 * timeReduction); + double bestMoveInstability = 1 + 1.88 * totBestMoveChanges / threads.size(); double totalTime = mainThread->tm.optimum() * fallingEval * reduction * bestMoveInstability; @@ -446,8 +446,8 @@ void Search::Worker::iterative_deepening() { if (rootMoves.size() == 1) totalTime = std::min(500.0, totalTime); - if (completedDepth >= 10 && nodesEffort >= 95 - && mainThread->tm.elapsed(threads.nodes_searched()) > totalTime * 3 / 4 + if (completedDepth >= 10 && nodesEffort >= 97 + && mainThread->tm.elapsed(threads.nodes_searched()) > totalTime * 0.739 && !mainThread->ponder) { threads.stop = true; @@ -464,7 +464,7 @@ void Search::Worker::iterative_deepening() { threads.stop = true; } else if (!mainThread->ponder - && mainThread->tm.elapsed(threads.nodes_searched()) > totalTime * 0.50) + && mainThread->tm.elapsed(threads.nodes_searched()) > totalTime * 0.506) threads.increaseDepth = false; else threads.increaseDepth = true; diff --git a/src/timeman.cpp b/src/timeman.cpp index b64ec77388f..4607344eab9 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -94,17 +94,17 @@ void TimeManagement::init(Search::LimitsType& limits, if (limits.movestogo == 0) { // Use extra time with larger increments - double optExtra = limits.inc[us] < 500 ? 1.0 : 1.1; + double optExtra = limits.inc[us] < 500 ? 1.0 : 1.13; // Calculate time constants based on current time left. double optConstant = - std::min(0.00334 + 0.0003 * std::log10(limits.time[us] / 1000.0), 0.0049); - double maxConstant = std::max(3.4 + 3.0 * std::log10(limits.time[us] / 1000.0), 2.76); + std::min(0.00308 + 0.000319 * std::log10(limits.time[us] / 1000.0), 0.00506); + double maxConstant = std::max(3.39 + 3.01 * std::log10(limits.time[us] / 1000.0), 2.93); - optScale = std::min(0.0120 + std::pow(ply + 3.1, 0.44) * optConstant, - 0.21 * limits.time[us] / double(timeLeft)) + optScale = std::min(0.0122 + std::pow(ply + 2.95, 0.462) * optConstant, + 0.213 * limits.time[us] / double(timeLeft)) * optExtra; - maxScale = std::min(6.9, maxConstant + ply / 12.2); + maxScale = std::min(6.64, maxConstant + ply / 12.0); } // x moves in y seconds (+ z increment) @@ -117,7 +117,7 @@ void TimeManagement::init(Search::LimitsType& limits, // Limit the maximum possible time for this move optimumTime = TimePoint(optScale * timeLeft); maximumTime = - TimePoint(std::min(0.84 * limits.time[us] - moveOverhead, maxScale * optimumTime)) - 10; + TimePoint(std::min(0.825 * limits.time[us] - moveOverhead, maxScale * optimumTime)) - 10; if (options["Ponder"]) optimumTime += optimumTime / 4; From 632f1c21cd271e7c4c242fdafa328a55ec63b9cb Mon Sep 17 00:00:00 2001 From: "Robert Nurnberg @ elitebook" Date: Thu, 7 Mar 2024 22:01:40 +0100 Subject: [PATCH 1397/1766] Fix wrong constant usage in go mate Fixes an oversight in https://github.com/official-stockfish/Stockfish/pull/5094 In theory, master could stop search when run with `go mate 247` and return a TB loss (not a mate score). Also fixes the spelling of opponenWorsening. closes https://github.com/official-stockfish/Stockfish/pull/5096 No functional change --- src/search.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 335149d576f..533cf61b613 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -407,7 +407,7 @@ void Search::Worker::iterative_deepening() { && ((rootMoves[0].score >= VALUE_MATE_IN_MAX_PLY && VALUE_MATE - rootMoves[0].score <= 2 * limits.mate) || (rootMoves[0].score != -VALUE_INFINITE - && rootMoves[0].score <= VALUE_TB_LOSS_IN_MAX_PLY + && rootMoves[0].score <= VALUE_MATED_IN_MAX_PLY && VALUE_MATE + rootMoves[0].score <= 2 * limits.mate))) threads.stop = true; @@ -539,7 +539,7 @@ Value Search::Worker::search( Move ttMove, move, excludedMove, bestMove; Depth extension, newDepth; Value bestValue, value, ttValue, eval, maxValue, probCutBeta; - bool givesCheck, improving, priorCapture, opponenWorsening; + bool givesCheck, improving, priorCapture, opponentWorsening; bool capture, moveCountPruning, ttCapture; Piece movedPiece; int moveCount, captureCount, quietCount; @@ -748,7 +748,7 @@ Value Search::Worker::search( ? ss->staticEval > (ss - 2)->staticEval : (ss - 4)->staticEval != VALUE_NONE && ss->staticEval > (ss - 4)->staticEval; - opponenWorsening = ss->staticEval + (ss - 1)->staticEval > 2 && (depth != 2 || !improving); + opponentWorsening = ss->staticEval + (ss - 1)->staticEval > 2 && (depth != 2 || !improving); // Step 7. Razoring (~1 Elo) // If eval is really low check with qsearch if it can exceed alpha, if it can't, @@ -764,7 +764,7 @@ Value Search::Worker::search( // Step 8. Futility pruning: child node (~40 Elo) // The depth condition is important for mate finding. if (!ss->ttPv && depth < 11 - && eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponenWorsening) + && eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening) - (ss - 1)->statScore / 314 >= beta && eval >= beta && eval < 30016 // smaller than TB wins @@ -1046,7 +1046,8 @@ Value Search::Worker::search( extension = 2 + (value < singularBeta - 78 && !ttCapture); depth += depth < 16; } - if (PvNode && !ttCapture && ss->multipleExtensions <= 5 && value < singularBeta - 50) + if (PvNode && !ttCapture && ss->multipleExtensions <= 5 + && value < singularBeta - 50) extension = 2; } From b6dfd6bd54979b5ba96716c2b246d84e5fa5b9fb Mon Sep 17 00:00:00 2001 From: Disservin Date: Sat, 9 Mar 2024 12:14:57 +0100 Subject: [PATCH 1398/1766] Assorted cleanups - fix naming convention for `workingDirectory` - use type alias for `EvalFiles` everywhere - move `ponderMode` into `LimitsType` - move limits parsing into standalone static function closes https://github.com/official-stockfish/Stockfish/pull/5098 No functional change --- src/main.cpp | 3 +-- src/nnue/evaluate_nnue.cpp | 7 +++---- src/nnue/evaluate_nnue.h | 11 ++--------- src/search.h | 2 ++ src/thread.cpp | 5 ++--- src/thread.h | 3 +-- src/uci.cpp | 15 ++++++++++----- src/uci.h | 13 +++++-------- 8 files changed, 26 insertions(+), 33 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index de07d6a8738..6ce656d7fe8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,7 +17,6 @@ */ #include -#include #include "bitboard.h" #include "evaluate.h" @@ -40,7 +39,7 @@ int main(int argc, char* argv[]) { Tune::init(uci.options); - uci.evalFiles = Eval::NNUE::load_networks(uci.workingDirectory(), uci.options, uci.evalFiles); + uci.evalFiles = Eval::NNUE::load_networks(uci.working_directory(), uci.options, uci.evalFiles); uci.loop(); diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index efcf5b01734..1efd83bf684 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include "../evaluate.h" #include "../misc.h" @@ -452,9 +451,9 @@ bool save_eval(std::ostream& stream, } // Save eval, to a file given by its name -bool save_eval(const std::optional& filename, - NetSize netSize, - const std::unordered_map& evalFiles) { +bool save_eval(const std::optional& filename, + NetSize netSize, + const EvalFiles& evalFiles) { std::string actualFilename; std::string msg; diff --git a/src/nnue/evaluate_nnue.h b/src/nnue/evaluate_nnue.h index c7b378604c5..febe8f9d9b9 100644 --- a/src/nnue/evaluate_nnue.h +++ b/src/nnue/evaluate_nnue.h @@ -26,8 +26,8 @@ #include #include #include -#include +#include "../evaluate.h" #include "../misc.h" #include "../types.h" #include "nnue_architecture.h" @@ -35,11 +35,6 @@ namespace Stockfish { class Position; - -namespace Eval { -struct EvalFile; -} - } namespace Stockfish::Eval::NNUE { @@ -87,9 +82,7 @@ bool save_eval(std::ostream& stream, NetSize netSize, const std::string& name, const std::string& netDescription); -bool save_eval(const std::optional& filename, - NetSize netSize, - const std::unordered_map&); +bool save_eval(const std::optional& filename, NetSize netSize, const EvalFiles&); } // namespace Stockfish::Eval::NNUE diff --git a/src/search.h b/src/search.h index 4a1c68bb9ce..bb9f63fff82 100644 --- a/src/search.h +++ b/src/search.h @@ -109,6 +109,7 @@ struct LimitsType { time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] = npmsec = movetime = TimePoint(0); movestogo = depth = mate = perft = infinite = 0; nodes = 0; + ponderMode = false; } bool use_time_management() const { return time[WHITE] || time[BLACK]; } @@ -117,6 +118,7 @@ struct LimitsType { TimePoint time[COLOR_NB], inc[COLOR_NB], npmsec, movetime, startTime; int movestogo, depth, mate, perft, infinite; uint64_t nodes; + bool ponderMode; }; diff --git a/src/thread.cpp b/src/thread.cpp index 95646601106..b62f5d8a39d 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -163,13 +163,12 @@ void ThreadPool::clear() { void ThreadPool::start_thinking(const OptionsMap& options, Position& pos, StateListPtr& states, - Search::LimitsType limits, - bool ponderMode) { + Search::LimitsType limits) { main_thread()->wait_for_search_finished(); main_manager()->stopOnPonderhit = stop = abortedSearch = false; - main_manager()->ponder = ponderMode; + main_manager()->ponder = limits.ponderMode; increaseDepth = true; diff --git a/src/thread.h b/src/thread.h index a2a1d18c454..0d4c252ccc3 100644 --- a/src/thread.h +++ b/src/thread.h @@ -79,8 +79,7 @@ class ThreadPool { } } - void - start_thinking(const OptionsMap&, Position&, StateListPtr&, Search::LimitsType, bool = false); + void start_thinking(const OptionsMap&, Position&, StateListPtr&, Search::LimitsType); void clear(); void set(Search::SharedState); diff --git a/src/uci.cpp b/src/uci.cpp index 4d4ea689715..357369bf562 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -175,11 +175,9 @@ void UCI::loop() { } while (token != "quit" && cli.argc == 1); // The command-line arguments are one-shot } -void UCI::go(Position& pos, std::istringstream& is, StateListPtr& states) { - +Search::LimitsType UCI::parse_limits(const Position& pos, std::istream& is) { Search::LimitsType limits; std::string token; - bool ponderMode = false; limits.startTime = now(); // The search starts as early as possible @@ -211,7 +209,14 @@ void UCI::go(Position& pos, std::istringstream& is, StateListPtr& states) { else if (token == "infinite") limits.infinite = 1; else if (token == "ponder") - ponderMode = true; + limits.ponderMode = true; + + return limits; +} + +void UCI::go(Position& pos, std::istringstream& is, StateListPtr& states) { + + Search::LimitsType limits = parse_limits(pos, is); Eval::NNUE::verify(options, evalFiles); @@ -221,7 +226,7 @@ void UCI::go(Position& pos, std::istringstream& is, StateListPtr& states) { return; } - threads.start_thinking(options, pos, states, limits, ponderMode); + threads.start_thinking(options, pos, states, limits); } void UCI::bench(Position& pos, std::istream& args, StateListPtr& states) { diff --git a/src/uci.h b/src/uci.h index 9d5f524ad65..f25bb8d517f 100644 --- a/src/uci.h +++ b/src/uci.h @@ -21,7 +21,6 @@ #include #include -#include #include "evaluate.h" #include "misc.h" @@ -29,13 +28,10 @@ #include "thread.h" #include "tt.h" #include "ucioption.h" +#include "search.h" namespace Stockfish { -namespace Eval::NNUE { -enum NetSize : int; -} - class Move; enum Square : int; using Value = int; @@ -53,11 +49,12 @@ class UCI { static std::string wdl(Value v, int ply); static Move to_move(const Position& pos, std::string& str); - const std::string& workingDirectory() const { return cli.workingDirectory; } + static Search::LimitsType parse_limits(const Position& pos, std::istream& is); - OptionsMap options; + const std::string& working_directory() const { return cli.workingDirectory; } - std::unordered_map evalFiles; + OptionsMap options; + Eval::NNUE::EvalFiles evalFiles; private: TranspositionTable tt; From 10e27329783c9f92bad8b7bd7fe496382a7fc0cd Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Mon, 11 Mar 2024 09:58:21 +0100 Subject: [PATCH 1399/1766] VVLTC search tune Result of 32k games of tuning at 60+0.6 8-thread. Link to the tuning attempt: https://tests.stockfishchess.org/tests/view/65def7b04b19edc854ebdec8 Passed VVLTC first SPRT: https://tests.stockfishchess.org/tests/view/65e51b53416ecd92c162ab7f LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 37570 W: 9613 L: 9342 D: 18615 Ptnml(0-2): 2, 3454, 11601, 3727, 1 Passed VVLTC second SPRT: https://tests.stockfishchess.org/tests/view/65e87d1c0ec64f0526c3eb39 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 123158 W: 31463 L: 31006 D: 60689 Ptnml(0-2): 5, 11589, 37935, 12044, 6 Note: The small net and psqt-only thresholds have been moved to evaluate.h. The reasoning is that these values are used in both `evaluate.cpp` and `evaluate_nnue.cpp`, and thus unifying their usage avoids inconsistencies during testing, where one occurrence is changed without the other (this happened during the search tune SPRT). closes https://github.com/official-stockfish/Stockfish/pull/5101 Bench: 1741218 --- src/evaluate.cpp | 4 +- src/evaluate.h | 2 + src/nnue/evaluate_nnue.cpp | 4 +- src/search.cpp | 75 +++++++++++++++++++------------------- 4 files changed, 43 insertions(+), 42 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index cd026036b4c..1eb58fd24b3 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -193,8 +193,8 @@ Value Eval::evaluate(const Position& pos, int optimism) { assert(!pos.checkers()); int simpleEval = simple_eval(pos, pos.side_to_move()); - bool smallNet = std::abs(simpleEval) > 1050; - bool psqtOnly = std::abs(simpleEval) > 2500; + bool smallNet = std::abs(simpleEval) > SmallNetThreshold; + bool psqtOnly = std::abs(simpleEval) > PsqtOnlyThreshold; int nnueComplexity; diff --git a/src/evaluate.h b/src/evaluate.h index 33fed3d5f5f..a690a3bbaf8 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -31,6 +31,8 @@ class OptionsMap; namespace Eval { +constexpr inline int SmallNetThreshold = 1139, PsqtOnlyThreshold = 2500; + std::string trace(Position& pos); int simple_eval(const Position& pos, Color c); diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp index 1efd83bf684..854fed06e82 100644 --- a/src/nnue/evaluate_nnue.cpp +++ b/src/nnue/evaluate_nnue.cpp @@ -179,8 +179,8 @@ write_parameters(std::ostream& stream, NetSize netSize, const std::string& netDe void hint_common_parent_position(const Position& pos) { int simpleEvalAbs = std::abs(simple_eval(pos, pos.side_to_move())); - if (simpleEvalAbs > 1050) - featureTransformerSmall->hint_common_access(pos, simpleEvalAbs > 2500); + if (simpleEvalAbs > Eval::SmallNetThreshold) + featureTransformerSmall->hint_common_access(pos, simpleEvalAbs > Eval::PsqtOnlyThreshold); else featureTransformerBig->hint_common_access(pos, false); } diff --git a/src/search.cpp b/src/search.cpp index 533cf61b613..75a747d999a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -55,7 +55,7 @@ namespace { // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) { - Value futilityMult = 117 - 44 * noTtCutNode; + Value futilityMult = 121 - 43 * noTtCutNode; Value improvingDeduction = 3 * improving * futilityMult / 2; Value worseningDeduction = (331 + 45 * improving) * oppWorsening * futilityMult / 1024; @@ -69,15 +69,15 @@ constexpr int futility_move_count(bool improving, Depth depth) { // Add correctionHistory value to raw staticEval and guarantee evaluation does not hit the tablebase range Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { auto cv = w.correctionHistory[pos.side_to_move()][pawn_structure_index(pos)]; - v += cv * std::abs(cv) / 12475; + v += cv * std::abs(cv) / 10759; return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } // History and stats update bonus, based on depth -int stat_bonus(Depth d) { return std::min(246 * d - 351, 1136); } +int stat_bonus(Depth d) { return std::min(249 * d - 327, 1192); } // History and stats update malus, based on depth -int stat_malus(Depth d) { return std::min(519 * d - 306, 1258); } +int stat_malus(Depth d) { return std::min(516 * d - 299, 1432); } // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(size_t nodes) { return VALUE_DRAW - 1 + Value(nodes & 0x2); } @@ -300,12 +300,12 @@ void Search::Worker::iterative_deepening() { // Reset aspiration window starting size Value avg = rootMoves[pvIdx].averageScore; - delta = 9 + avg * avg / 12487; + delta = 9 + avg * avg / 12804; alpha = std::max(avg - delta, -VALUE_INFINITE); beta = std::min(avg + delta, VALUE_INFINITE); // Adjust optimism based on root move's averageScore (~4 Elo) - optimism[us] = 134 * avg / (std::abs(avg) + 97); + optimism[us] = 131 * avg / (std::abs(avg) + 90); optimism[~us] = -optimism[us]; // Start with a small aspiration window and, in the case of a fail @@ -500,7 +500,7 @@ void Search::Worker::clear() { h->fill(-71); for (size_t i = 1; i < reductions.size(); ++i) - reductions[i] = int((18.79 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); + reductions[i] = int((19.02 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); } @@ -731,7 +731,7 @@ Value Search::Worker::search( // Use static evaluation difference to improve quiet move ordering (~9 Elo) if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture) { - int bonus = std::clamp(-14 * int((ss - 1)->staticEval + ss->staticEval), -1723, 1455); + int bonus = std::clamp(-14 * int((ss - 1)->staticEval + ss->staticEval), -1621, 1237); bonus = bonus > 0 ? 2 * bonus : bonus / 2; thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) @@ -754,7 +754,7 @@ Value Search::Worker::search( // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. // Adjust razor margin according to cutoffCnt. (~1 Elo) - if (eval < alpha - 438 - (332 - 154 * ((ss + 1)->cutoffCnt > 3)) * depth * depth) + if (eval < alpha - 462 - (296 - 145 * ((ss + 1)->cutoffCnt > 3)) * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -763,24 +763,23 @@ Value Search::Worker::search( // Step 8. Futility pruning: child node (~40 Elo) // The depth condition is important for mate finding. - if (!ss->ttPv && depth < 11 + if (!ss->ttPv && depth < 12 && eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening) - - (ss - 1)->statScore / 314 + - (ss - 1)->statScore / 287 >= beta - && eval >= beta && eval < 30016 // smaller than TB wins - && (!ttMove || ttCapture)) + && eval >= beta && eval < VALUE_TB_WIN_IN_MAX_PLY && (!ttMove || ttCapture)) return beta > VALUE_TB_LOSS_IN_MAX_PLY ? (eval + beta) / 2 : eval; // Step 9. Null move search with verification search (~35 Elo) - if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 16620 - && eval >= beta && eval >= ss->staticEval && ss->staticEval >= beta - 21 * depth + 330 + if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 16211 + && eval >= beta && eval >= ss->staticEval && ss->staticEval >= beta - 20 * depth + 314 && !excludedMove && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly && beta > VALUE_TB_LOSS_IN_MAX_PLY) { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and eval - Depth R = std::min(int(eval - beta) / 154, 6) + depth / 3 + 4; + Depth R = std::min(int(eval - beta) / 151, 6) + depth / 3 + 4; ss->currentMove = Move::null(); ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -828,7 +827,7 @@ Value Search::Worker::search( // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search returns a value // much above beta, we can (almost) safely prune the previous move. - probCutBeta = beta + 181 - 68 * improving; + probCutBeta = beta + 164 - 62 * improving; if ( !PvNode && depth > 3 && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY @@ -884,7 +883,7 @@ Value Search::Worker::search( moves_loop: // When in check, search starts here // Step 12. A small Probcut idea, when we are in check (~4 Elo) - probCutBeta = beta + 452; + probCutBeta = beta + 410; if (ss->inCheck && !PvNode && ttCapture && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 4 && ttValue >= probCutBeta && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) @@ -967,7 +966,7 @@ Value Search::Worker::search( { Piece capturedPiece = pos.piece_on(move.to_sq()); int futilityEval = - ss->staticEval + 277 + 292 * lmrDepth + PieceValue[capturedPiece] + ss->staticEval + 298 + 288 * lmrDepth + PieceValue[capturedPiece] + thisThread->captureHistory[movedPiece][move.to_sq()][type_of(capturedPiece)] / 7; if (futilityEval < alpha) @@ -975,7 +974,7 @@ Value Search::Worker::search( } // SEE based pruning for captures and checks (~11 Elo) - if (!pos.see_ge(move, -197 * depth)) + if (!pos.see_ge(move, -202 * depth)) continue; } else @@ -987,17 +986,17 @@ Value Search::Worker::search( + thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()]; // Continuation history based pruning (~2 Elo) - if (lmrDepth < 6 && history < -4211 * depth) + if (lmrDepth < 6 && history < -4125 * depth) continue; history += 2 * thisThread->mainHistory[us][move.from_to()]; - lmrDepth += history / 6437; + lmrDepth += history / 5686; // Futility pruning: parent node (~13 Elo) if (!ss->inCheck && lmrDepth < 15 - && ss->staticEval + (bestValue < ss->staticEval - 57 ? 144 : 57) - + 121 * lmrDepth + && ss->staticEval + (bestValue < ss->staticEval - 55 ? 153 : 58) + + 118 * lmrDepth <= alpha) continue; @@ -1024,11 +1023,11 @@ Value Search::Worker::search( // so changing them requires tests at these types of time controls. // Recursive singular search is avoided. if (!rootNode && move == ttMove && !excludedMove - && depth >= 4 - (thisThread->completedDepth > 30) + ss->ttPv + && depth >= 4 - (thisThread->completedDepth > 29) + ss->ttPv && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - (60 + 54 * (ss->ttPv && !PvNode)) * depth / 64; + Value singularBeta = ttValue - (58 + 55 * (ss->ttPv && !PvNode)) * depth / 64; Depth singularDepth = newDepth / 2; ss->excludedMove = move; @@ -1044,7 +1043,7 @@ Value Search::Worker::search( if (!PvNode && ss->multipleExtensions <= 16) { extension = 2 + (value < singularBeta - 78 && !ttCapture); - depth += depth < 16; + depth += depth < 14; } if (PvNode && !ttCapture && ss->multipleExtensions <= 5 && value < singularBeta - 50) @@ -1082,7 +1081,7 @@ Value Search::Worker::search( else if (PvNode && move == ttMove && move.to_sq() == prevSq && thisThread->captureHistory[movedPiece][move.to_sq()] [type_of(pos.piece_on(move.to_sq()))] - > 4394) + > 4315) extension = 1; } @@ -1136,10 +1135,10 @@ Value Search::Worker::search( ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + (*contHist[0])[movedPiece][move.to_sq()] + (*contHist[1])[movedPiece][move.to_sq()] - + (*contHist[3])[movedPiece][move.to_sq()] - 4392; + + (*contHist[3])[movedPiece][move.to_sq()] - 4587; // Decrease/increase reduction for moves with a good/bad history (~8 Elo) - r -= ss->statScore / 14189; + r -= ss->statScore / 12372; // Step 17. Late moves reduction / extension (LMR, ~117 Elo) if (depth >= 2 && moveCount > 1 + rootNode) @@ -1158,7 +1157,7 @@ Value Search::Worker::search( { // Adjust full-depth search based on LMR results - if the result // was good enough search deeper, if it was bad enough search shallower. - const bool doDeeperSearch = value > (bestValue + 49 + 2 * newDepth); // (~1 Elo) + const bool doDeeperSearch = value > (bestValue + 48 + 2 * newDepth); // (~1 Elo) const bool doShallowerSearch = value < bestValue + newDepth; // (~2 Elo) newDepth += doDeeperSearch - doShallowerSearch; @@ -1277,7 +1276,7 @@ Value Search::Worker::search( else { // Reduce other moves if we have found at least one score improvement (~2 Elo) - if (depth > 2 && depth < 13 && beta < 13652 && value > -12761) + if (depth > 2 && depth < 12 && beta < 13665 && value > -12276) depth -= 2; assert(depth > 0); @@ -1320,8 +1319,8 @@ Value Search::Worker::search( // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (depth > 5) + (PvNode || cutNode) + ((ss - 1)->statScore < -15736) - + ((ss - 1)->moveCount > 11); + int bonus = (depth > 5) + (PvNode || cutNode) + ((ss - 1)->statScore < -14446) + + ((ss - 1)->moveCount > 10); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] @@ -1478,7 +1477,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, if (bestValue > alpha) alpha = bestValue; - futilityBase = ss->staticEval + 206; + futilityBase = ss->staticEval + 221; } const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, @@ -1558,7 +1557,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, continue; // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, -74)) + if (!pos.see_ge(move, -79)) continue; } @@ -1626,7 +1625,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth Search::Worker::reduction(bool i, Depth d, int mn, int delta) { int reductionScale = reductions[d] * reductions[mn]; - return (reductionScale + 1118 - delta * 793 / rootDelta) / 1024 + (!i && reductionScale > 863); + return (reductionScale + 1091 - delta * 759 / rootDelta) / 1024 + (!i && reductionScale > 952); } namespace { @@ -1715,7 +1714,7 @@ void update_all_stats(const Position& pos, if (!pos.capture_stage(bestMove)) { - int bestMoveBonus = bestValue > beta + 166 ? quietMoveBonus // larger bonus + int bestMoveBonus = bestValue > beta + 167 ? quietMoveBonus // larger bonus : stat_bonus(depth); // smaller bonus // Increase stats for the best move in case it was a quiet move From f072634e245774f957b715118ecb586264cf04f1 Mon Sep 17 00:00:00 2001 From: Gahtan Nahdi <155860115+gahtan-syarif@users.noreply.github.com> Date: Mon, 11 Mar 2024 06:25:07 +0700 Subject: [PATCH 1400/1766] Simplify opponentWorsening condition Passed non-reg STC: https://tests.stockfishchess.org/tests/view/65ea18650ec64f0526c4033a LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 226624 W: 58601 L: 58589 D: 109434 Ptnml(0-2): 1030, 27193, 56819, 27275, 995 Passed non-reg LTC: https://tests.stockfishchess.org/tests/view/65eb7a220ec64f0526c4161a LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 243882 W: 61462 L: 61469 D: 120951 Ptnml(0-2): 197, 27559, 66419, 27586, 180 closes https://github.com/official-stockfish/Stockfish/pull/5102 Bench: 1601012 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 75a747d999a..16f45df16dd 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -748,7 +748,7 @@ Value Search::Worker::search( ? ss->staticEval > (ss - 2)->staticEval : (ss - 4)->staticEval != VALUE_NONE && ss->staticEval > (ss - 4)->staticEval; - opponentWorsening = ss->staticEval + (ss - 1)->staticEval > 2 && (depth != 2 || !improving); + opponentWorsening = ss->staticEval + (ss - 1)->staticEval > 2; // Step 7. Razoring (~1 Elo) // If eval is really low check with qsearch if it can exceed alpha, if it can't, From 1a26d698de33ed50f182b325b359da61bab67abe Mon Sep 17 00:00:00 2001 From: Disservin Date: Sat, 9 Mar 2024 14:42:37 +0100 Subject: [PATCH 1401/1766] Refactor Network Usage Continuing from PR #4968, this update improves how Stockfish handles network usage, making it easier to manage and modify networks in the future. With the introduction of a dedicated Network class, creating networks has become straightforward. See uci.cpp: ```cpp NN::NetworkBig({EvalFileDefaultNameBig, "None", ""}, NN::embeddedNNUEBig) ``` The new `Network` encapsulates all network-related logic, significantly reducing the complexity previously required to support multiple network types, such as the distinction between small and big networks #4915. Non-Regression STC: https://tests.stockfishchess.org/tests/view/65edd26c0ec64f0526c43584 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 33760 W: 8887 L: 8661 D: 16212 Ptnml(0-2): 143, 3795, 8808, 3961, 173 Non-Regression SMP STC: https://tests.stockfishchess.org/tests/view/65ed71970ec64f0526c42fdd LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 59088 W: 15121 L: 14931 D: 29036 Ptnml(0-2): 110, 6640, 15829, 6880, 85 Compiled with `make -j profile-build` ``` bash ./bench_parallel.sh ./stockfish ./stockfish-nnue 13 50 sf_base = 1568540 +/- 7637 (95%) sf_test = 1573129 +/- 7301 (95%) diff = 4589 +/- 8720 (95%) speedup = 0.29260% +/- 0.556% (95%) ``` Compiled with `make -j build` ``` bash ./bench_parallel.sh ./stockfish ./stockfish-nnue 13 50 sf_base = 1472653 +/- 7293 (95%) sf_test = 1491928 +/- 7661 (95%) diff = 19275 +/- 7154 (95%) speedup = 1.30886% +/- 0.486% (95%) ``` closes https://github.com/official-stockfish/Stockfish/pull/5100 No functional change --- src/Makefile | 8 +- src/evaluate.cpp | 164 +----------- src/evaluate.h | 32 +-- src/main.cpp | 3 - src/misc.h | 25 ++ src/nnue/evaluate_nnue.cpp | 488 ----------------------------------- src/nnue/evaluate_nnue.h | 89 ------- src/nnue/network.cpp | 422 ++++++++++++++++++++++++++++++ src/nnue/network.h | 128 +++++++++ src/nnue/nnue_architecture.h | 7 +- src/nnue/nnue_misc.cpp | 202 +++++++++++++++ src/nnue/nnue_misc.h | 63 +++++ src/search.cpp | 31 ++- src/search.h | 32 ++- src/thread.cpp | 10 +- src/thread.h | 17 +- src/uci.cpp | 45 ++-- src/uci.h | 8 +- 18 files changed, 948 insertions(+), 826 deletions(-) delete mode 100644 src/nnue/evaluate_nnue.cpp delete mode 100644 src/nnue/evaluate_nnue.h create mode 100644 src/nnue/network.cpp create mode 100644 src/nnue/network.h create mode 100644 src/nnue/nnue_misc.cpp create mode 100644 src/nnue/nnue_misc.h diff --git a/src/Makefile b/src/Makefile index 907b6155028..bd04d2c410e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -55,15 +55,15 @@ PGOBENCH = $(WINE_PATH) ./$(EXE) bench SRCS = benchmark.cpp bitboard.cpp evaluate.cpp main.cpp \ misc.cpp movegen.cpp movepick.cpp position.cpp \ search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \ - nnue/evaluate_nnue.cpp nnue/features/half_ka_v2_hm.cpp + nnue/nnue_misc.cpp nnue/features/half_ka_v2_hm.cpp nnue/network.cpp HEADERS = benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h \ - nnue/evaluate_nnue.h nnue/features/half_ka_v2_hm.h nnue/layers/affine_transform.h \ + nnue/nnue_misc.h nnue/features/half_ka_v2_hm.h nnue/layers/affine_transform.h \ nnue/layers/affine_transform_sparse_input.h nnue/layers/clipped_relu.h nnue/layers/simd.h \ nnue/layers/sqr_clipped_relu.h nnue/nnue_accumulator.h nnue/nnue_architecture.h \ nnue/nnue_common.h nnue/nnue_feature_transformer.h position.h \ search.h syzygy/tbprobe.h thread.h thread_win32_osx.h timeman.h \ - tt.h tune.h types.h uci.h ucioption.h perft.h + tt.h tune.h types.h uci.h ucioption.h perft.h nnue/network.cpp OBJS = $(notdir $(SRCS:.cpp=.o)) @@ -502,7 +502,7 @@ endif # In earlier NDK versions, you'll need to pass -fno-addrsig if using GNU binutils. # Currently we don't know how to make PGO builds with the NDK yet. ifeq ($(COMP),ndk) - CXXFLAGS += -stdlib=libc++ -fPIE + CXXFLAGS += -stdlib=libc++ -fPIE -mcmodel=large comp=clang ifeq ($(arch),armv7) CXX=armv7a-linux-androideabi16-clang++ diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 1eb58fd24b3..56abe6cb9c8 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -22,161 +22,18 @@ #include #include #include -#include #include #include -#include #include -#include -#include -#include "incbin/incbin.h" -#include "misc.h" -#include "nnue/evaluate_nnue.h" -#include "nnue/nnue_architecture.h" +#include "nnue/network.h" +#include "nnue/nnue_misc.h" #include "position.h" #include "types.h" #include "uci.h" -#include "ucioption.h" - -// Macro to embed the default efficiently updatable neural network (NNUE) file -// data in the engine binary (using incbin.h, by Dale Weiler). -// This macro invocation will declare the following three variables -// const unsigned char gEmbeddedNNUEData[]; // a pointer to the embedded data -// const unsigned char *const gEmbeddedNNUEEnd; // a marker to the end -// const unsigned int gEmbeddedNNUESize; // the size of the embedded file -// Note that this does not work in Microsoft Visual Studio. -#if !defined(_MSC_VER) && !defined(NNUE_EMBEDDING_OFF) -INCBIN(EmbeddedNNUEBig, EvalFileDefaultNameBig); -INCBIN(EmbeddedNNUESmall, EvalFileDefaultNameSmall); -#else -const unsigned char gEmbeddedNNUEBigData[1] = {0x0}; -const unsigned char* const gEmbeddedNNUEBigEnd = &gEmbeddedNNUEBigData[1]; -const unsigned int gEmbeddedNNUEBigSize = 1; -const unsigned char gEmbeddedNNUESmallData[1] = {0x0}; -const unsigned char* const gEmbeddedNNUESmallEnd = &gEmbeddedNNUESmallData[1]; -const unsigned int gEmbeddedNNUESmallSize = 1; -#endif - namespace Stockfish { -namespace Eval { - - -// Tries to load a NNUE network at startup time, or when the engine -// receives a UCI command "setoption name EvalFile value nn-[a-z0-9]{12}.nnue" -// The name of the NNUE network is always retrieved from the EvalFile option. -// We search the given network in three locations: internally (the default -// network may be embedded in the binary), in the active working directory and -// in the engine directory. Distro packagers may define the DEFAULT_NNUE_DIRECTORY -// variable to have the engine search in a special directory in their distro. -NNUE::EvalFiles NNUE::load_networks(const std::string& rootDirectory, - const OptionsMap& options, - NNUE::EvalFiles evalFiles) { - - for (auto& [netSize, evalFile] : evalFiles) - { - std::string user_eval_file = options[evalFile.optionName]; - - if (user_eval_file.empty()) - user_eval_file = evalFile.defaultName; - -#if defined(DEFAULT_NNUE_DIRECTORY) - std::vector dirs = {"", "", rootDirectory, - stringify(DEFAULT_NNUE_DIRECTORY)}; -#else - std::vector dirs = {"", "", rootDirectory}; -#endif - - for (const std::string& directory : dirs) - { - if (evalFile.current != user_eval_file) - { - if (directory != "") - { - std::ifstream stream(directory + user_eval_file, std::ios::binary); - auto description = NNUE::load_eval(stream, netSize); - - if (description.has_value()) - { - evalFile.current = user_eval_file; - evalFile.netDescription = description.value(); - } - } - - if (directory == "" && user_eval_file == evalFile.defaultName) - { - // C++ way to prepare a buffer for a memory stream - class MemoryBuffer: public std::basic_streambuf { - public: - MemoryBuffer(char* p, size_t n) { - setg(p, p, p + n); - setp(p, p + n); - } - }; - - MemoryBuffer buffer( - const_cast(reinterpret_cast( - netSize == Small ? gEmbeddedNNUESmallData : gEmbeddedNNUEBigData)), - size_t(netSize == Small ? gEmbeddedNNUESmallSize : gEmbeddedNNUEBigSize)); - (void) gEmbeddedNNUEBigEnd; // Silence warning on unused variable - (void) gEmbeddedNNUESmallEnd; - - std::istream stream(&buffer); - auto description = NNUE::load_eval(stream, netSize); - - if (description.has_value()) - { - evalFile.current = user_eval_file; - evalFile.netDescription = description.value(); - } - } - } - } - } - - return evalFiles; -} - -// Verifies that the last net used was loaded successfully -void NNUE::verify(const OptionsMap& options, - const std::unordered_map& evalFiles) { - - for (const auto& [netSize, evalFile] : evalFiles) - { - std::string user_eval_file = options[evalFile.optionName]; - - if (user_eval_file.empty()) - user_eval_file = evalFile.defaultName; - - if (evalFile.current != user_eval_file) - { - std::string msg1 = - "Network evaluation parameters compatible with the engine must be available."; - std::string msg2 = - "The network file " + user_eval_file + " was not loaded successfully."; - std::string msg3 = "The UCI option EvalFile might need to specify the full path, " - "including the directory name, to the network file."; - std::string msg4 = "The default net can be downloaded from: " - "https://tests.stockfishchess.org/api/nn/" - + evalFile.defaultName; - std::string msg5 = "The engine will be terminated now."; - - sync_cout << "info string ERROR: " << msg1 << sync_endl; - sync_cout << "info string ERROR: " << msg2 << sync_endl; - sync_cout << "info string ERROR: " << msg3 << sync_endl; - sync_cout << "info string ERROR: " << msg4 << sync_endl; - sync_cout << "info string ERROR: " << msg5 << sync_endl; - - exit(EXIT_FAILURE); - } - - sync_cout << "info string NNUE evaluation using " << user_eval_file << sync_endl; - } -} -} - // Returns a static, purely materialistic evaluation of the position from // the point of view of the given color. It can be divided by PawnValue to get // an approximation of the material advantage on the board in terms of pawns. @@ -188,7 +45,7 @@ int Eval::simple_eval(const Position& pos, Color c) { // Evaluate is the evaluator for the outer world. It returns a static evaluation // of the position from the point of view of the side to move. -Value Eval::evaluate(const Position& pos, int optimism) { +Value Eval::evaluate(const Eval::NNUE::Networks& networks, const Position& pos, int optimism) { assert(!pos.checkers()); @@ -198,8 +55,8 @@ Value Eval::evaluate(const Position& pos, int optimism) { int nnueComplexity; - Value nnue = smallNet ? NNUE::evaluate(pos, true, &nnueComplexity, psqtOnly) - : NNUE::evaluate(pos, true, &nnueComplexity, false); + Value nnue = smallNet ? networks.small.evaluate(pos, true, &nnueComplexity, psqtOnly) + : networks.big.evaluate(pos, true, &nnueComplexity, false); // Blend optimism and eval with nnue complexity and material imbalance optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / 512; @@ -222,23 +79,22 @@ Value Eval::evaluate(const Position& pos, int optimism) { // a string (suitable for outputting to stdout) that contains the detailed // descriptions and values of each evaluation term. Useful for debugging. // Trace scores are from white's point of view -std::string Eval::trace(Position& pos) { +std::string Eval::trace(Position& pos, const Eval::NNUE::Networks& networks) { if (pos.checkers()) return "Final evaluation: none (in check)"; std::stringstream ss; ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2); - ss << '\n' << NNUE::trace(pos) << '\n'; + ss << '\n' << NNUE::trace(pos, networks) << '\n'; ss << std::showpoint << std::showpos << std::fixed << std::setprecision(2) << std::setw(15); - Value v; - v = NNUE::evaluate(pos, false); - v = pos.side_to_move() == WHITE ? v : -v; + Value v = networks.big.evaluate(pos, false); + v = pos.side_to_move() == WHITE ? v : -v; ss << "NNUE evaluation " << 0.01 * UCI::to_cp(v) << " (white side)\n"; - v = evaluate(pos, VALUE_ZERO); + v = evaluate(networks, pos, VALUE_ZERO); v = pos.side_to_move() == WHITE ? v : -v; ss << "Final evaluation " << 0.01 * UCI::to_cp(v) << " (white side)"; ss << " [with scaled NNUE, ...]"; diff --git a/src/evaluate.h b/src/evaluate.h index a690a3bbaf8..754a92eb7eb 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -20,51 +20,33 @@ #define EVALUATE_H_INCLUDED #include -#include #include "types.h" namespace Stockfish { class Position; -class OptionsMap; namespace Eval { constexpr inline int SmallNetThreshold = 1139, PsqtOnlyThreshold = 2500; -std::string trace(Position& pos); - -int simple_eval(const Position& pos, Color c); -Value evaluate(const Position& pos, int optimism); - // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the -// name of the macro, as it is used in the Makefile. +// name of the macro or the location where this macro is defined, as it is used +// in the Makefile/Fishtest. #define EvalFileDefaultNameBig "nn-1ceb1ade0001.nnue" #define EvalFileDefaultNameSmall "nn-baff1ede1f90.nnue" -struct EvalFile { - // UCI option name - std::string optionName; - // Default net name, will use one of the macros above - std::string defaultName; - // Selected net name, either via uci option or default - std::string current; - // Net description extracted from the net file - std::string netDescription; -}; - namespace NNUE { +struct Networks; +} -enum NetSize : int; - -using EvalFiles = std::unordered_map; +std::string trace(Position& pos, const Eval::NNUE::Networks& networks); -EvalFiles load_networks(const std::string&, const OptionsMap&, EvalFiles); -void verify(const OptionsMap&, const EvalFiles&); +int simple_eval(const Position& pos, Color c); +Value evaluate(const NNUE::Networks& networks, const Position& pos, int optimism); -} // namespace NNUE } // namespace Eval diff --git a/src/main.cpp b/src/main.cpp index 6ce656d7fe8..33d5d375fca 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,7 +19,6 @@ #include #include "bitboard.h" -#include "evaluate.h" #include "misc.h" #include "position.h" #include "tune.h" @@ -39,8 +38,6 @@ int main(int argc, char* argv[]) { Tune::init(uci.options); - uci.evalFiles = Eval::NNUE::load_networks(uci.working_directory(), uci.options, uci.evalFiles); - uci.loop(); return 0; diff --git a/src/misc.h b/src/misc.h index f73e7889a8d..9ad5c3ca57e 100644 --- a/src/misc.h +++ b/src/misc.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -49,6 +50,30 @@ void* aligned_large_pages_alloc(size_t size); // nop if mem == nullptr void aligned_large_pages_free(void* mem); +// Deleter for automating release of memory area +template +struct AlignedDeleter { + void operator()(T* ptr) const { + ptr->~T(); + std_aligned_free(ptr); + } +}; + +template +struct LargePageDeleter { + void operator()(T* ptr) const { + ptr->~T(); + aligned_large_pages_free(ptr); + } +}; + +template +using AlignedPtr = std::unique_ptr>; + +template +using LargePagePtr = std::unique_ptr>; + + void dbg_hit_on(bool cond, int slot = 0); void dbg_mean_of(int64_t value, int slot = 0); void dbg_stdev_of(int64_t value, int slot = 0); diff --git a/src/nnue/evaluate_nnue.cpp b/src/nnue/evaluate_nnue.cpp deleted file mode 100644 index 854fed06e82..00000000000 --- a/src/nnue/evaluate_nnue.cpp +++ /dev/null @@ -1,488 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -// Code for calculating NNUE evaluation function - -#include "evaluate_nnue.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../evaluate.h" -#include "../misc.h" -#include "../position.h" -#include "../types.h" -#include "../uci.h" -#include "nnue_accumulator.h" -#include "nnue_common.h" - -namespace Stockfish::Eval::NNUE { - -// Input feature converter -LargePagePtr> - featureTransformerBig; -LargePagePtr> - featureTransformerSmall; - -// Evaluation function -AlignedPtr> networkBig[LayerStacks]; -AlignedPtr> networkSmall[LayerStacks]; - -// Evaluation function file names - -namespace Detail { - -// Initialize the evaluation function parameters -template -void initialize(AlignedPtr& pointer) { - - pointer.reset(reinterpret_cast(std_aligned_alloc(alignof(T), sizeof(T)))); - std::memset(pointer.get(), 0, sizeof(T)); -} - -template -void initialize(LargePagePtr& pointer) { - - static_assert(alignof(T) <= 4096, - "aligned_large_pages_alloc() may fail for such a big alignment requirement of T"); - pointer.reset(reinterpret_cast(aligned_large_pages_alloc(sizeof(T)))); - std::memset(pointer.get(), 0, sizeof(T)); -} - -// Read evaluation function parameters -template -bool read_parameters(std::istream& stream, T& reference) { - - std::uint32_t header; - header = read_little_endian(stream); - if (!stream || header != T::get_hash_value()) - return false; - return reference.read_parameters(stream); -} - -// Write evaluation function parameters -template -bool write_parameters(std::ostream& stream, const T& reference) { - - write_little_endian(stream, T::get_hash_value()); - return reference.write_parameters(stream); -} - -} // namespace Detail - - -// Initialize the evaluation function parameters -static void initialize(NetSize netSize) { - - if (netSize == Small) - { - Detail::initialize(featureTransformerSmall); - for (std::size_t i = 0; i < LayerStacks; ++i) - Detail::initialize(networkSmall[i]); - } - else - { - Detail::initialize(featureTransformerBig); - for (std::size_t i = 0; i < LayerStacks; ++i) - Detail::initialize(networkBig[i]); - } -} - -// Read network header -static bool read_header(std::istream& stream, std::uint32_t* hashValue, std::string* desc) { - std::uint32_t version, size; - - version = read_little_endian(stream); - *hashValue = read_little_endian(stream); - size = read_little_endian(stream); - if (!stream || version != Version) - return false; - desc->resize(size); - stream.read(&(*desc)[0], size); - return !stream.fail(); -} - -// Write network header -static bool write_header(std::ostream& stream, std::uint32_t hashValue, const std::string& desc) { - write_little_endian(stream, Version); - write_little_endian(stream, hashValue); - write_little_endian(stream, std::uint32_t(desc.size())); - stream.write(&desc[0], desc.size()); - return !stream.fail(); -} - -// Read network parameters -static bool read_parameters(std::istream& stream, NetSize netSize, std::string& netDescription) { - - std::uint32_t hashValue; - if (!read_header(stream, &hashValue, &netDescription)) - return false; - if (hashValue != HashValue[netSize]) - return false; - if (netSize == Big && !Detail::read_parameters(stream, *featureTransformerBig)) - return false; - if (netSize == Small && !Detail::read_parameters(stream, *featureTransformerSmall)) - return false; - for (std::size_t i = 0; i < LayerStacks; ++i) - { - if (netSize == Big && !Detail::read_parameters(stream, *(networkBig[i]))) - return false; - if (netSize == Small && !Detail::read_parameters(stream, *(networkSmall[i]))) - return false; - } - return stream && stream.peek() == std::ios::traits_type::eof(); -} - -// Write network parameters -static bool -write_parameters(std::ostream& stream, NetSize netSize, const std::string& netDescription) { - - if (!write_header(stream, HashValue[netSize], netDescription)) - return false; - if (netSize == Big && !Detail::write_parameters(stream, *featureTransformerBig)) - return false; - if (netSize == Small && !Detail::write_parameters(stream, *featureTransformerSmall)) - return false; - for (std::size_t i = 0; i < LayerStacks; ++i) - { - if (netSize == Big && !Detail::write_parameters(stream, *(networkBig[i]))) - return false; - if (netSize == Small && !Detail::write_parameters(stream, *(networkSmall[i]))) - return false; - } - return bool(stream); -} - -void hint_common_parent_position(const Position& pos) { - - int simpleEvalAbs = std::abs(simple_eval(pos, pos.side_to_move())); - if (simpleEvalAbs > Eval::SmallNetThreshold) - featureTransformerSmall->hint_common_access(pos, simpleEvalAbs > Eval::PsqtOnlyThreshold); - else - featureTransformerBig->hint_common_access(pos, false); -} - -// Evaluation function. Perform differential calculation. -template -Value evaluate(const Position& pos, bool adjusted, int* complexity, bool psqtOnly) { - - // We manually align the arrays on the stack because with gcc < 9.3 - // overaligning stack variables with alignas() doesn't work correctly. - - constexpr uint64_t alignment = CacheLineSize; - constexpr int delta = 24; - -#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) - TransformedFeatureType transformedFeaturesUnaligned - [FeatureTransformer < Net_Size == Small ? TransformedFeatureDimensionsSmall - : TransformedFeatureDimensionsBig, - nullptr > ::BufferSize + alignment / sizeof(TransformedFeatureType)]; - - auto* transformedFeatures = align_ptr_up(&transformedFeaturesUnaligned[0]); -#else - - alignas(alignment) TransformedFeatureType - transformedFeatures[FeatureTransformer < Net_Size == Small ? TransformedFeatureDimensionsSmall - : TransformedFeatureDimensionsBig, - nullptr > ::BufferSize]; -#endif - - ASSERT_ALIGNED(transformedFeatures, alignment); - - const int bucket = (pos.count() - 1) / 4; - const auto psqt = - Net_Size == Small - ? featureTransformerSmall->transform(pos, transformedFeatures, bucket, psqtOnly) - : featureTransformerBig->transform(pos, transformedFeatures, bucket, psqtOnly); - - const auto positional = - !psqtOnly ? (Net_Size == Small ? networkSmall[bucket]->propagate(transformedFeatures) - : networkBig[bucket]->propagate(transformedFeatures)) - : 0; - - if (complexity) - *complexity = !psqtOnly ? std::abs(psqt - positional) / OutputScale : 0; - - // Give more value to positional evaluation when adjusted flag is set - if (adjusted) - return static_cast(((1024 - delta) * psqt + (1024 + delta) * positional) - / (1024 * OutputScale)); - else - return static_cast((psqt + positional) / OutputScale); -} - -template Value evaluate(const Position& pos, bool adjusted, int* complexity, bool psqtOnly); -template Value evaluate(const Position& pos, bool adjusted, int* complexity, bool psqtOnly); - -struct NnueEvalTrace { - static_assert(LayerStacks == PSQTBuckets); - - Value psqt[LayerStacks]; - Value positional[LayerStacks]; - std::size_t correctBucket; -}; - -static NnueEvalTrace trace_evaluate(const Position& pos) { - - // We manually align the arrays on the stack because with gcc < 9.3 - // overaligning stack variables with alignas() doesn't work correctly. - constexpr uint64_t alignment = CacheLineSize; - -#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) - TransformedFeatureType transformedFeaturesUnaligned - [FeatureTransformer::BufferSize - + alignment / sizeof(TransformedFeatureType)]; - - auto* transformedFeatures = align_ptr_up(&transformedFeaturesUnaligned[0]); -#else - alignas(alignment) TransformedFeatureType - transformedFeatures[FeatureTransformer::BufferSize]; -#endif - - ASSERT_ALIGNED(transformedFeatures, alignment); - - NnueEvalTrace t{}; - t.correctBucket = (pos.count() - 1) / 4; - for (IndexType bucket = 0; bucket < LayerStacks; ++bucket) - { - const auto materialist = - featureTransformerBig->transform(pos, transformedFeatures, bucket, false); - const auto positional = networkBig[bucket]->propagate(transformedFeatures); - - t.psqt[bucket] = static_cast(materialist / OutputScale); - t.positional[bucket] = static_cast(positional / OutputScale); - } - - return t; -} - -constexpr std::string_view PieceToChar(" PNBRQK pnbrqk"); - - -// Converts a Value into (centi)pawns and writes it in a buffer. -// The buffer must have capacity for at least 5 chars. -static void format_cp_compact(Value v, char* buffer) { - - buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' '); - - int cp = std::abs(UCI::to_cp(v)); - if (cp >= 10000) - { - buffer[1] = '0' + cp / 10000; - cp %= 10000; - buffer[2] = '0' + cp / 1000; - cp %= 1000; - buffer[3] = '0' + cp / 100; - buffer[4] = ' '; - } - else if (cp >= 1000) - { - buffer[1] = '0' + cp / 1000; - cp %= 1000; - buffer[2] = '0' + cp / 100; - cp %= 100; - buffer[3] = '.'; - buffer[4] = '0' + cp / 10; - } - else - { - buffer[1] = '0' + cp / 100; - cp %= 100; - buffer[2] = '.'; - buffer[3] = '0' + cp / 10; - cp %= 10; - buffer[4] = '0' + cp / 1; - } -} - - -// Converts a Value into pawns, always keeping two decimals -static void format_cp_aligned_dot(Value v, std::stringstream& stream) { - - const double pawns = std::abs(0.01 * UCI::to_cp(v)); - - stream << (v < 0 ? '-' - : v > 0 ? '+' - : ' ') - << std::setiosflags(std::ios::fixed) << std::setw(6) << std::setprecision(2) << pawns; -} - - -// Returns a string with the value of each piece on a board, -// and a table for (PSQT, Layers) values bucket by bucket. -std::string trace(Position& pos) { - - std::stringstream ss; - - char board[3 * 8 + 1][8 * 8 + 2]; - std::memset(board, ' ', sizeof(board)); - for (int row = 0; row < 3 * 8 + 1; ++row) - board[row][8 * 8 + 1] = '\0'; - - // A lambda to output one box of the board - auto writeSquare = [&board](File file, Rank rank, Piece pc, Value value) { - const int x = int(file) * 8; - const int y = (7 - int(rank)) * 3; - for (int i = 1; i < 8; ++i) - board[y][x + i] = board[y + 3][x + i] = '-'; - for (int i = 1; i < 3; ++i) - board[y + i][x] = board[y + i][x + 8] = '|'; - board[y][x] = board[y][x + 8] = board[y + 3][x + 8] = board[y + 3][x] = '+'; - if (pc != NO_PIECE) - board[y + 1][x + 4] = PieceToChar[pc]; - if (value != VALUE_NONE) - format_cp_compact(value, &board[y + 2][x + 2]); - }; - - // We estimate the value of each piece by doing a differential evaluation from - // the current base eval, simulating the removal of the piece from its square. - Value base = evaluate(pos); - base = pos.side_to_move() == WHITE ? base : -base; - - for (File f = FILE_A; f <= FILE_H; ++f) - for (Rank r = RANK_1; r <= RANK_8; ++r) - { - Square sq = make_square(f, r); - Piece pc = pos.piece_on(sq); - Value v = VALUE_NONE; - - if (pc != NO_PIECE && type_of(pc) != KING) - { - auto st = pos.state(); - - pos.remove_piece(sq); - st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] = - st->accumulatorBig.computedPSQT[WHITE] = st->accumulatorBig.computedPSQT[BLACK] = - false; - - Value eval = evaluate(pos); - eval = pos.side_to_move() == WHITE ? eval : -eval; - v = base - eval; - - pos.put_piece(pc, sq); - st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] = - st->accumulatorBig.computedPSQT[WHITE] = st->accumulatorBig.computedPSQT[BLACK] = - false; - } - - writeSquare(f, r, pc, v); - } - - ss << " NNUE derived piece values:\n"; - for (int row = 0; row < 3 * 8 + 1; ++row) - ss << board[row] << '\n'; - ss << '\n'; - - auto t = trace_evaluate(pos); - - ss << " NNUE network contributions " - << (pos.side_to_move() == WHITE ? "(White to move)" : "(Black to move)") << std::endl - << "+------------+------------+------------+------------+\n" - << "| Bucket | Material | Positional | Total |\n" - << "| | (PSQT) | (Layers) | |\n" - << "+------------+------------+------------+------------+\n"; - - for (std::size_t bucket = 0; bucket < LayerStacks; ++bucket) - { - ss << "| " << bucket << " "; - ss << " | "; - format_cp_aligned_dot(t.psqt[bucket], ss); - ss << " " - << " | "; - format_cp_aligned_dot(t.positional[bucket], ss); - ss << " " - << " | "; - format_cp_aligned_dot(t.psqt[bucket] + t.positional[bucket], ss); - ss << " " - << " |"; - if (bucket == t.correctBucket) - ss << " <-- this bucket is used"; - ss << '\n'; - } - - ss << "+------------+------------+------------+------------+\n"; - - return ss.str(); -} - - -// Load eval, from a file stream or a memory stream -std::optional load_eval(std::istream& stream, NetSize netSize) { - - initialize(netSize); - std::string netDescription; - return read_parameters(stream, netSize, netDescription) ? std::make_optional(netDescription) - : std::nullopt; -} - -// Save eval, to a file stream or a memory stream -bool save_eval(std::ostream& stream, - NetSize netSize, - const std::string& name, - const std::string& netDescription) { - - if (name.empty() || name == "None") - return false; - - return write_parameters(stream, netSize, netDescription); -} - -// Save eval, to a file given by its name -bool save_eval(const std::optional& filename, - NetSize netSize, - const EvalFiles& evalFiles) { - - std::string actualFilename; - std::string msg; - - if (filename.has_value()) - actualFilename = filename.value(); - else - { - if (evalFiles.at(netSize).current - != (netSize == Small ? EvalFileDefaultNameSmall : EvalFileDefaultNameBig)) - { - msg = "Failed to export a net. " - "A non-embedded net can only be saved if the filename is specified"; - - sync_cout << msg << sync_endl; - return false; - } - actualFilename = (netSize == Small ? EvalFileDefaultNameSmall : EvalFileDefaultNameBig); - } - - std::ofstream stream(actualFilename, std::ios_base::binary); - bool saved = save_eval(stream, netSize, evalFiles.at(netSize).current, - evalFiles.at(netSize).netDescription); - - msg = saved ? "Network saved successfully to " + actualFilename : "Failed to export a net"; - - sync_cout << msg << sync_endl; - return saved; -} - - -} // namespace Stockfish::Eval::NNUE diff --git a/src/nnue/evaluate_nnue.h b/src/nnue/evaluate_nnue.h deleted file mode 100644 index febe8f9d9b9..00000000000 --- a/src/nnue/evaluate_nnue.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -// header used in NNUE evaluation function - -#ifndef NNUE_EVALUATE_NNUE_H_INCLUDED -#define NNUE_EVALUATE_NNUE_H_INCLUDED - -#include -#include -#include -#include -#include - -#include "../evaluate.h" -#include "../misc.h" -#include "../types.h" -#include "nnue_architecture.h" -#include "nnue_feature_transformer.h" - -namespace Stockfish { -class Position; -} - -namespace Stockfish::Eval::NNUE { - -// Hash value of evaluation function structure -constexpr std::uint32_t HashValue[2] = { - FeatureTransformer::get_hash_value() - ^ Network::get_hash_value(), - FeatureTransformer::get_hash_value() - ^ Network::get_hash_value()}; - -// Deleter for automating release of memory area -template -struct AlignedDeleter { - void operator()(T* ptr) const { - ptr->~T(); - std_aligned_free(ptr); - } -}; - -template -struct LargePageDeleter { - void operator()(T* ptr) const { - ptr->~T(); - aligned_large_pages_free(ptr); - } -}; - -template -using AlignedPtr = std::unique_ptr>; - -template -using LargePagePtr = std::unique_ptr>; - -std::string trace(Position& pos); -template -Value evaluate(const Position& pos, - bool adjusted = false, - int* complexity = nullptr, - bool psqtOnly = false); -void hint_common_parent_position(const Position& pos); - -std::optional load_eval(std::istream& stream, NetSize netSize); -bool save_eval(std::ostream& stream, - NetSize netSize, - const std::string& name, - const std::string& netDescription); -bool save_eval(const std::optional& filename, NetSize netSize, const EvalFiles&); - -} // namespace Stockfish::Eval::NNUE - -#endif // #ifndef NNUE_EVALUATE_NNUE_H_INCLUDED diff --git a/src/nnue/network.cpp b/src/nnue/network.cpp new file mode 100644 index 00000000000..5d4e0954d78 --- /dev/null +++ b/src/nnue/network.cpp @@ -0,0 +1,422 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "network.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../evaluate.h" +#include "../incbin/incbin.h" +#include "../misc.h" +#include "../position.h" +#include "../types.h" +#include "nnue_architecture.h" +#include "nnue_common.h" +#include "nnue_misc.h" + +namespace { +// Macro to embed the default efficiently updatable neural network (NNUE) file +// data in the engine binary (using incbin.h, by Dale Weiler). +// This macro invocation will declare the following three variables +// const unsigned char gEmbeddedNNUEData[]; // a pointer to the embedded data +// const unsigned char *const gEmbeddedNNUEEnd; // a marker to the end +// const unsigned int gEmbeddedNNUESize; // the size of the embedded file +// Note that this does not work in Microsoft Visual Studio. +#if !defined(_MSC_VER) && !defined(NNUE_EMBEDDING_OFF) +INCBIN(EmbeddedNNUEBig, EvalFileDefaultNameBig); +INCBIN(EmbeddedNNUESmall, EvalFileDefaultNameSmall); +#else +const unsigned char gEmbeddedNNUEBigData[1] = {0x0}; +const unsigned char* const gEmbeddedNNUEBigEnd = &gEmbeddedNNUEBigData[1]; +const unsigned int gEmbeddedNNUEBigSize = 1; +const unsigned char gEmbeddedNNUESmallData[1] = {0x0}; +const unsigned char* const gEmbeddedNNUESmallEnd = &gEmbeddedNNUESmallData[1]; +const unsigned int gEmbeddedNNUESmallSize = 1; +#endif +} + + +namespace Stockfish::Eval::NNUE { + +const EmbeddedNNUE embeddedNNUEBig(gEmbeddedNNUEBigData, gEmbeddedNNUEBigEnd, gEmbeddedNNUEBigSize); +const EmbeddedNNUE + embeddedNNUESmall(gEmbeddedNNUESmallData, gEmbeddedNNUESmallEnd, gEmbeddedNNUESmallSize); + + +namespace Detail { + +// Initialize the evaluation function parameters +template +void initialize(AlignedPtr& pointer) { + + pointer.reset(reinterpret_cast(std_aligned_alloc(alignof(T), sizeof(T)))); + std::memset(pointer.get(), 0, sizeof(T)); +} + +template +void initialize(LargePagePtr& pointer) { + + static_assert(alignof(T) <= 4096, + "aligned_large_pages_alloc() may fail for such a big alignment requirement of T"); + pointer.reset(reinterpret_cast(aligned_large_pages_alloc(sizeof(T)))); + std::memset(pointer.get(), 0, sizeof(T)); +} + +// Read evaluation function parameters +template +bool read_parameters(std::istream& stream, T& reference) { + + std::uint32_t header; + header = read_little_endian(stream); + if (!stream || header != T::get_hash_value()) + return false; + return reference.read_parameters(stream); +} + +// Write evaluation function parameters +template +bool write_parameters(std::ostream& stream, const T& reference) { + + write_little_endian(stream, T::get_hash_value()); + return reference.write_parameters(stream); +} + +} // namespace Detail + + +template +void Network::load(const std::string& rootDirectory, std::string evalfilePath) { +#if defined(DEFAULT_NNUE_DIRECTORY) + std::vector dirs = {"", "", rootDirectory, + stringify(DEFAULT_NNUE_DIRECTORY)}; +#else + std::vector dirs = {"", "", rootDirectory}; +#endif + + if (evalfilePath.empty()) + evalfilePath = evalFile.defaultName; + + for (const auto& directory : dirs) + { + if (evalFile.current != evalfilePath) + { + if (directory != "") + { + load_user_net(directory, evalfilePath); + } + + if (directory == "" && evalfilePath == evalFile.defaultName) + { + load_internal(); + } + } + } +} + + +template +bool Network::save(const std::optional& filename) const { + std::string actualFilename; + std::string msg; + + if (filename.has_value()) + actualFilename = filename.value(); + else + { + if (evalFile.current != evalFile.defaultName) + { + msg = "Failed to export a net. " + "A non-embedded net can only be saved if the filename is specified"; + + sync_cout << msg << sync_endl; + return false; + } + + actualFilename = evalFile.defaultName; + } + + std::ofstream stream(actualFilename, std::ios_base::binary); + bool saved = save(stream, evalFile.current, evalFile.netDescription); + + msg = saved ? "Network saved successfully to " + actualFilename : "Failed to export a net"; + + sync_cout << msg << sync_endl; + return saved; +} + + +template +Value Network::evaluate(const Position& pos, + bool adjusted, + int* complexity, + bool psqtOnly) const { + // We manually align the arrays on the stack because with gcc < 9.3 + // overaligning stack variables with alignas() doesn't work correctly. + + constexpr uint64_t alignment = CacheLineSize; + constexpr int delta = 24; + +#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) + TransformedFeatureType transformedFeaturesUnaligned + [FeatureTransformer::BufferSize + + alignment / sizeof(TransformedFeatureType)]; + + auto* transformedFeatures = align_ptr_up(&transformedFeaturesUnaligned[0]); +#else + alignas(alignment) TransformedFeatureType transformedFeatures + [FeatureTransformer::BufferSize]; +#endif + + ASSERT_ALIGNED(transformedFeatures, alignment); + + const int bucket = (pos.count() - 1) / 4; + const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket, psqtOnly); + const auto positional = !psqtOnly ? (network[bucket]->propagate(transformedFeatures)) : 0; + + if (complexity) + *complexity = !psqtOnly ? std::abs(psqt - positional) / OutputScale : 0; + + // Give more value to positional evaluation when adjusted flag is set + if (adjusted) + return static_cast(((1024 - delta) * psqt + (1024 + delta) * positional) + / (1024 * OutputScale)); + else + return static_cast((psqt + positional) / OutputScale); +} + + +template +void Network::verify(std::string evalfilePath) const { + if (evalfilePath.empty()) + evalfilePath = evalFile.defaultName; + + if (evalFile.current != evalfilePath) + { + std::string msg1 = + "Network evaluation parameters compatible with the engine must be available."; + std::string msg2 = "The network file " + evalfilePath + " was not loaded successfully."; + std::string msg3 = "The UCI option EvalFile might need to specify the full path, " + "including the directory name, to the network file."; + std::string msg4 = "The default net can be downloaded from: " + "https://tests.stockfishchess.org/api/nn/" + + evalFile.defaultName; + std::string msg5 = "The engine will be terminated now."; + + sync_cout << "info string ERROR: " << msg1 << sync_endl; + sync_cout << "info string ERROR: " << msg2 << sync_endl; + sync_cout << "info string ERROR: " << msg3 << sync_endl; + sync_cout << "info string ERROR: " << msg4 << sync_endl; + sync_cout << "info string ERROR: " << msg5 << sync_endl; + exit(EXIT_FAILURE); + } + + sync_cout << "info string NNUE evaluation using " << evalfilePath << sync_endl; +} + + +template +void Network::hint_common_access(const Position& pos, bool psqtOnl) const { + featureTransformer->hint_common_access(pos, psqtOnl); +} + + +template +NnueEvalTrace Network::trace_evaluate(const Position& pos) const { + // We manually align the arrays on the stack because with gcc < 9.3 + // overaligning stack variables with alignas() doesn't work correctly. + constexpr uint64_t alignment = CacheLineSize; + +#if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) + TransformedFeatureType transformedFeaturesUnaligned + [FeatureTransformer::BufferSize + + alignment / sizeof(TransformedFeatureType)]; + + auto* transformedFeatures = align_ptr_up(&transformedFeaturesUnaligned[0]); +#else + alignas(alignment) TransformedFeatureType transformedFeatures + [FeatureTransformer::BufferSize]; +#endif + + ASSERT_ALIGNED(transformedFeatures, alignment); + + NnueEvalTrace t{}; + t.correctBucket = (pos.count() - 1) / 4; + for (IndexType bucket = 0; bucket < LayerStacks; ++bucket) + { + const auto materialist = + featureTransformer->transform(pos, transformedFeatures, bucket, false); + const auto positional = network[bucket]->propagate(transformedFeatures); + + t.psqt[bucket] = static_cast(materialist / OutputScale); + t.positional[bucket] = static_cast(positional / OutputScale); + } + + return t; +} + + +template +void Network::load_user_net(const std::string& dir, + const std::string& evalfilePath) { + std::ifstream stream(dir + evalfilePath, std::ios::binary); + auto description = load(stream); + + if (description.has_value()) + { + evalFile.current = evalfilePath; + evalFile.netDescription = description.value(); + } +} + + +template +void Network::load_internal() { + // C++ way to prepare a buffer for a memory stream + class MemoryBuffer: public std::basic_streambuf { + public: + MemoryBuffer(char* p, size_t n) { + setg(p, p, p + n); + setp(p, p + n); + } + }; + + MemoryBuffer buffer(const_cast(reinterpret_cast(embedded.data)), + size_t(embedded.size)); + + std::istream stream(&buffer); + auto description = load(stream); + + if (description.has_value()) + { + evalFile.current = evalFile.defaultName; + evalFile.netDescription = description.value(); + } +} + + +template +void Network::initialize() { + Detail::initialize(featureTransformer); + for (std::size_t i = 0; i < LayerStacks; ++i) + Detail::initialize(network[i]); +} + + +template +bool Network::save(std::ostream& stream, + const std::string& name, + const std::string& netDescription) const { + if (name.empty() || name == "None") + return false; + + return write_parameters(stream, netDescription); +} + + +template +std::optional Network::load(std::istream& stream) { + initialize(); + std::string description; + + return read_parameters(stream, description) ? std::make_optional(description) : std::nullopt; +} + + +// Read network header +template +bool Network::read_header(std::istream& stream, + std::uint32_t* hashValue, + std::string* desc) const { + std::uint32_t version, size; + + version = read_little_endian(stream); + *hashValue = read_little_endian(stream); + size = read_little_endian(stream); + if (!stream || version != Version) + return false; + desc->resize(size); + stream.read(&(*desc)[0], size); + return !stream.fail(); +} + + +// Write network header +template +bool Network::write_header(std::ostream& stream, + std::uint32_t hashValue, + const std::string& desc) const { + write_little_endian(stream, Version); + write_little_endian(stream, hashValue); + write_little_endian(stream, std::uint32_t(desc.size())); + stream.write(&desc[0], desc.size()); + return !stream.fail(); +} + + +template +bool Network::read_parameters(std::istream& stream, + std::string& netDescription) const { + std::uint32_t hashValue; + if (!read_header(stream, &hashValue, &netDescription)) + return false; + if (hashValue != Network::hash) + return false; + if (!Detail::read_parameters(stream, *featureTransformer)) + return false; + for (std::size_t i = 0; i < LayerStacks; ++i) + { + if (!Detail::read_parameters(stream, *(network[i]))) + return false; + } + return stream && stream.peek() == std::ios::traits_type::eof(); +} + + +template +bool Network::write_parameters(std::ostream& stream, + const std::string& netDescription) const { + if (!write_header(stream, Network::hash, netDescription)) + return false; + if (!Detail::write_parameters(stream, *featureTransformer)) + return false; + for (std::size_t i = 0; i < LayerStacks; ++i) + { + if (!Detail::write_parameters(stream, *(network[i]))) + return false; + } + return bool(stream); +} + +// Explicit template instantiation + +template class Network< + NetworkArchitecture, + FeatureTransformer>; + +template class Network< + NetworkArchitecture, + FeatureTransformer>; + +} // namespace Stockfish::Eval::NNUE diff --git a/src/nnue/network.h b/src/nnue/network.h new file mode 100644 index 00000000000..c1ed7717914 --- /dev/null +++ b/src/nnue/network.h @@ -0,0 +1,128 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef NETWORK_H_INCLUDED +#define NETWORK_H_INCLUDED + +#include +#include +#include +#include +#include + +#include "../misc.h" +#include "../position.h" +#include "../types.h" +#include "nnue_architecture.h" +#include "nnue_feature_transformer.h" +#include "nnue_misc.h" + +namespace Stockfish::Eval::NNUE { + +struct EmbeddedNNUE { + EmbeddedNNUE(const unsigned char* embeddedData, + const unsigned char* embeddedEnd, + const unsigned int embeddedSize) : + data(embeddedData), + end(embeddedEnd), + size(embeddedSize) {} + const unsigned char* data; + const unsigned char* end; + const unsigned int size; +}; + +extern const EmbeddedNNUE embeddedNNUEBig; +extern const EmbeddedNNUE embeddedNNUESmall; + +template +class Network { + public: + Network(EvalFile file, EmbeddedNNUE embeddedEval) : + evalFile(file), + embedded(embeddedEval) {} + + void load(const std::string& rootDirectory, std::string evalfilePath); + bool save(const std::optional& filename) const; + + + Value evaluate(const Position& pos, + bool adjusted = false, + int* complexity = nullptr, + bool psqtOnly = false) const; + + + void hint_common_access(const Position& pos, bool psqtOnl) const; + + void verify(std::string evalfilePath) const; + NnueEvalTrace trace_evaluate(const Position& pos) const; + + private: + void load_user_net(const std::string&, const std::string&); + void load_internal(); + + void initialize(); + + bool save(std::ostream&, const std::string&, const std::string&) const; + std::optional load(std::istream&); + + bool read_header(std::istream&, std::uint32_t*, std::string*) const; + bool write_header(std::ostream&, std::uint32_t, const std::string&) const; + + bool read_parameters(std::istream&, std::string&) const; + bool write_parameters(std::ostream&, const std::string&) const; + + // Input feature converter + LargePagePtr featureTransformer; + + // Evaluation function + AlignedPtr network[LayerStacks]; + + EvalFile evalFile; + EmbeddedNNUE embedded; + + // Hash value of evaluation function structure + static constexpr std::uint32_t hash = Transformer::get_hash_value() ^ Arch::get_hash_value(); +}; + +// Definitions of the network types +using SmallFeatureTransformer = + FeatureTransformer; +using SmallNetworkArchitecture = + NetworkArchitecture; + +using BigFeatureTransformer = + FeatureTransformer; +using BigNetworkArchitecture = NetworkArchitecture; + +using NetworkBig = Network; +using NetworkSmall = Network; + + +struct Networks { + Networks(NetworkBig&& nB, NetworkSmall&& nS) : + big(std::move(nB)), + small(std::move(nS)) {} + + NetworkBig big; + NetworkSmall small; +}; + + +} // namespace Stockfish + +#endif diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index b222ab997db..05efb813754 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -37,11 +37,6 @@ namespace Stockfish::Eval::NNUE { // Input features used in evaluation function using FeatureSet = Features::HalfKAv2_hm; -enum NetSize : int { - Big, - Small -}; - // Number of input feature dimensions after conversion constexpr IndexType TransformedFeatureDimensionsBig = 2560; constexpr int L2Big = 15; @@ -55,7 +50,7 @@ constexpr IndexType PSQTBuckets = 8; constexpr IndexType LayerStacks = 8; template -struct Network { +struct NetworkArchitecture { static constexpr IndexType TransformedFeatureDimensions = L1; static constexpr int FC_0_OUTPUTS = L2; static constexpr int FC_1_OUTPUTS = L3; diff --git a/src/nnue/nnue_misc.cpp b/src/nnue/nnue_misc.cpp new file mode 100644 index 00000000000..c443aaf19b2 --- /dev/null +++ b/src/nnue/nnue_misc.cpp @@ -0,0 +1,202 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// Code for calculating NNUE evaluation function + +#include "nnue_misc.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../evaluate.h" +#include "../position.h" +#include "../types.h" +#include "../uci.h" +#include "network.h" +#include "nnue_accumulator.h" + +namespace Stockfish::Eval::NNUE { + + +constexpr std::string_view PieceToChar(" PNBRQK pnbrqk"); + + +void hint_common_parent_position(const Position& pos, const Networks& networks) { + + int simpleEvalAbs = std::abs(simple_eval(pos, pos.side_to_move())); + if (simpleEvalAbs > Eval::SmallNetThreshold) + networks.small.hint_common_access(pos, simpleEvalAbs > Eval::PsqtOnlyThreshold); + else + networks.big.hint_common_access(pos, false); +} + + +// Converts a Value into (centi)pawns and writes it in a buffer. +// The buffer must have capacity for at least 5 chars. +static void format_cp_compact(Value v, char* buffer) { + + buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' '); + + int cp = std::abs(UCI::to_cp(v)); + if (cp >= 10000) + { + buffer[1] = '0' + cp / 10000; + cp %= 10000; + buffer[2] = '0' + cp / 1000; + cp %= 1000; + buffer[3] = '0' + cp / 100; + buffer[4] = ' '; + } + else if (cp >= 1000) + { + buffer[1] = '0' + cp / 1000; + cp %= 1000; + buffer[2] = '0' + cp / 100; + cp %= 100; + buffer[3] = '.'; + buffer[4] = '0' + cp / 10; + } + else + { + buffer[1] = '0' + cp / 100; + cp %= 100; + buffer[2] = '.'; + buffer[3] = '0' + cp / 10; + cp %= 10; + buffer[4] = '0' + cp / 1; + } +} + + +// Converts a Value into pawns, always keeping two decimals +static void format_cp_aligned_dot(Value v, std::stringstream& stream) { + + const double pawns = std::abs(0.01 * UCI::to_cp(v)); + + stream << (v < 0 ? '-' + : v > 0 ? '+' + : ' ') + << std::setiosflags(std::ios::fixed) << std::setw(6) << std::setprecision(2) << pawns; +} + + +// Returns a string with the value of each piece on a board, +// and a table for (PSQT, Layers) values bucket by bucket. +std::string trace(Position& pos, const Eval::NNUE::Networks& networks) { + + std::stringstream ss; + + char board[3 * 8 + 1][8 * 8 + 2]; + std::memset(board, ' ', sizeof(board)); + for (int row = 0; row < 3 * 8 + 1; ++row) + board[row][8 * 8 + 1] = '\0'; + + // A lambda to output one box of the board + auto writeSquare = [&board](File file, Rank rank, Piece pc, Value value) { + const int x = int(file) * 8; + const int y = (7 - int(rank)) * 3; + for (int i = 1; i < 8; ++i) + board[y][x + i] = board[y + 3][x + i] = '-'; + for (int i = 1; i < 3; ++i) + board[y + i][x] = board[y + i][x + 8] = '|'; + board[y][x] = board[y][x + 8] = board[y + 3][x + 8] = board[y + 3][x] = '+'; + if (pc != NO_PIECE) + board[y + 1][x + 4] = PieceToChar[pc]; + if (value != VALUE_NONE) + format_cp_compact(value, &board[y + 2][x + 2]); + }; + + // We estimate the value of each piece by doing a differential evaluation from + // the current base eval, simulating the removal of the piece from its square. + Value base = networks.big.evaluate(pos); + base = pos.side_to_move() == WHITE ? base : -base; + + for (File f = FILE_A; f <= FILE_H; ++f) + for (Rank r = RANK_1; r <= RANK_8; ++r) + { + Square sq = make_square(f, r); + Piece pc = pos.piece_on(sq); + Value v = VALUE_NONE; + + if (pc != NO_PIECE && type_of(pc) != KING) + { + auto st = pos.state(); + + pos.remove_piece(sq); + st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] = + st->accumulatorBig.computedPSQT[WHITE] = st->accumulatorBig.computedPSQT[BLACK] = + false; + + Value eval = networks.big.evaluate(pos); + eval = pos.side_to_move() == WHITE ? eval : -eval; + v = base - eval; + + pos.put_piece(pc, sq); + st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] = + st->accumulatorBig.computedPSQT[WHITE] = st->accumulatorBig.computedPSQT[BLACK] = + false; + } + + writeSquare(f, r, pc, v); + } + + ss << " NNUE derived piece values:\n"; + for (int row = 0; row < 3 * 8 + 1; ++row) + ss << board[row] << '\n'; + ss << '\n'; + + auto t = networks.big.trace_evaluate(pos); + + ss << " NNUE network contributions " + << (pos.side_to_move() == WHITE ? "(White to move)" : "(Black to move)") << std::endl + << "+------------+------------+------------+------------+\n" + << "| Bucket | Material | Positional | Total |\n" + << "| | (PSQT) | (Layers) | |\n" + << "+------------+------------+------------+------------+\n"; + + for (std::size_t bucket = 0; bucket < LayerStacks; ++bucket) + { + ss << "| " << bucket << " "; + ss << " | "; + format_cp_aligned_dot(t.psqt[bucket], ss); + ss << " " + << " | "; + format_cp_aligned_dot(t.positional[bucket], ss); + ss << " " + << " | "; + format_cp_aligned_dot(t.psqt[bucket] + t.positional[bucket], ss); + ss << " " + << " |"; + if (bucket == t.correctBucket) + ss << " <-- this bucket is used"; + ss << '\n'; + } + + ss << "+------------+------------+------------+------------+\n"; + + return ss.str(); +} + + +} // namespace Stockfish::Eval::NNUE diff --git a/src/nnue/nnue_misc.h b/src/nnue/nnue_misc.h new file mode 100644 index 00000000000..5eab02184c6 --- /dev/null +++ b/src/nnue/nnue_misc.h @@ -0,0 +1,63 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef NNUE_MISC_H_INCLUDED +#define NNUE_MISC_H_INCLUDED + +#include +#include + +#include "../types.h" +#include "nnue_architecture.h" + +namespace Stockfish { + +class Position; + +namespace Eval::NNUE { + +struct EvalFile { + // Default net name, will use one of the EvalFileDefaultName* macros defined + // in evaluate.h + std::string defaultName; + // Selected net name, either via uci option or default + std::string current; + // Net description extracted from the net file + std::string netDescription; +}; + + +struct NnueEvalTrace { + static_assert(LayerStacks == PSQTBuckets); + + Value psqt[LayerStacks]; + Value positional[LayerStacks]; + std::size_t correctBucket; +}; + + +struct Networks; + + +std::string trace(Position& pos, const Networks& networks); +void hint_common_parent_position(const Position& pos, const Networks& networks); + +} // namespace Stockfish::Eval::NNUE +} // namespace Stockfish + +#endif // #ifndef NNUE_MISC_H_INCLUDED diff --git a/src/search.cpp b/src/search.cpp index 16f45df16dd..f4ec8fb16dc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -27,15 +27,15 @@ #include #include #include -#include #include +#include #include "evaluate.h" #include "misc.h" #include "movegen.h" #include "movepick.h" -#include "nnue/evaluate_nnue.h" #include "nnue/nnue_common.h" +#include "nnue/nnue_misc.h" #include "position.h" #include "syzygy/tbprobe.h" #include "thread.h" @@ -135,7 +135,8 @@ Search::Worker::Worker(SharedState& sharedState, manager(std::move(sm)), options(sharedState.options), threads(sharedState.threads), - tt(sharedState.tt) { + tt(sharedState.tt), + networks(sharedState.networks) { clear(); } @@ -566,8 +567,9 @@ Value Search::Worker::search( // Step 2. Check for aborted search and immediate draw if (threads.stop.load(std::memory_order_relaxed) || pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) - return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos, thisThread->optimism[us]) - : value_draw(thisThread->nodes); + return (ss->ply >= MAX_PLY && !ss->inCheck) + ? evaluate(networks, pos, thisThread->optimism[us]) + : value_draw(thisThread->nodes); // 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 @@ -700,7 +702,7 @@ Value Search::Worker::search( { // Providing the hint that this node's accumulator will be used often // brings significant Elo gain (~13 Elo). - Eval::NNUE::hint_common_parent_position(pos); + Eval::NNUE::hint_common_parent_position(pos, networks); unadjustedStaticEval = eval = ss->staticEval; } else if (ss->ttHit) @@ -708,9 +710,9 @@ Value Search::Worker::search( // Never assume anything about values stored in TT unadjustedStaticEval = tte->eval(); if (unadjustedStaticEval == VALUE_NONE) - unadjustedStaticEval = evaluate(pos, thisThread->optimism[us]); + unadjustedStaticEval = evaluate(networks, pos, thisThread->optimism[us]); else if (PvNode) - Eval::NNUE::hint_common_parent_position(pos); + Eval::NNUE::hint_common_parent_position(pos, networks); ss->staticEval = eval = to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); @@ -720,7 +722,7 @@ Value Search::Worker::search( } else { - unadjustedStaticEval = evaluate(pos, thisThread->optimism[us]); + unadjustedStaticEval = evaluate(networks, pos, thisThread->optimism[us]); ss->staticEval = eval = to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); // Static evaluation is saved as it was before adjustment by correction history @@ -877,7 +879,7 @@ Value Search::Worker::search( } } - Eval::NNUE::hint_common_parent_position(pos); + Eval::NNUE::hint_common_parent_position(pos, networks); } moves_loop: // When in check, search starts here @@ -1413,8 +1415,9 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, // Step 2. Check for an immediate draw or maximum ply reached if (pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) - return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos, thisThread->optimism[us]) - : VALUE_DRAW; + return (ss->ply >= MAX_PLY && !ss->inCheck) + ? evaluate(networks, pos, thisThread->optimism[us]) + : VALUE_DRAW; assert(0 <= ss->ply && ss->ply < MAX_PLY); @@ -1445,7 +1448,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, // Never assume anything about values stored in TT unadjustedStaticEval = tte->eval(); if (unadjustedStaticEval == VALUE_NONE) - unadjustedStaticEval = evaluate(pos, thisThread->optimism[us]); + unadjustedStaticEval = evaluate(networks, pos, thisThread->optimism[us]); ss->staticEval = bestValue = to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); @@ -1458,7 +1461,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, { // In case of null move search, use previous static eval with a different sign unadjustedStaticEval = (ss - 1)->currentMove != Move::null() - ? evaluate(pos, thisThread->optimism[us]) + ? evaluate(networks, pos, thisThread->optimism[us]) : -(ss - 1)->staticEval; ss->staticEval = bestValue = to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); diff --git a/src/search.h b/src/search.h index bb9f63fff82..4908e535c6a 100644 --- a/src/search.h +++ b/src/search.h @@ -25,8 +25,8 @@ #include #include #include -#include #include +#include #include "misc.h" #include "movepick.h" @@ -37,6 +37,10 @@ namespace Stockfish { +namespace Eval::NNUE { +struct Networks; +} + // Different node types, used as a template parameter enum NodeType { NonPV, @@ -125,16 +129,20 @@ struct LimitsType { // The UCI stores the uci options, thread pool, and transposition table. // This struct is used to easily forward data to the Search::Worker class. struct SharedState { - SharedState(const OptionsMap& optionsMap, - ThreadPool& threadPool, - TranspositionTable& transpositionTable) : + SharedState(const OptionsMap& optionsMap, + ThreadPool& threadPool, + TranspositionTable& transpositionTable, + const Eval::NNUE::Networks& nets) : options(optionsMap), threads(threadPool), - tt(transpositionTable) {} + tt(transpositionTable), + networks(nets) {} - const OptionsMap& options; - ThreadPool& threads; - TranspositionTable& tt; + + const OptionsMap& options; + ThreadPool& threads; + TranspositionTable& tt; + const Eval::NNUE::Networks& networks; }; class Worker; @@ -176,6 +184,7 @@ class NullSearchManager: public ISearchManager { void check_time(Search::Worker&) override {} }; + // Search::Worker is the class that does the actual search. // It is instantiated once per thread, and it is responsible for keeping track // of the search history, and storing data required for the search. @@ -247,9 +256,10 @@ class Worker { Tablebases::Config tbConfig; - const OptionsMap& options; - ThreadPool& threads; - TranspositionTable& tt; + const OptionsMap& options; + ThreadPool& threads; + TranspositionTable& tt; + const Eval::NNUE::Networks& networks; friend class Stockfish::ThreadPool; friend class SearchManager; diff --git a/src/thread.cpp b/src/thread.cpp index b62f5d8a39d..a3823d0cfd0 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -19,12 +19,12 @@ #include "thread.h" #include +#include #include #include #include #include #include -#include #include "misc.h" #include "movegen.h" @@ -62,6 +62,7 @@ Thread::~Thread() { stdThread.join(); } + // Wakes up the thread that will start the search void Thread::start_searching() { mutex.lock(); @@ -109,6 +110,13 @@ void Thread::idle_loop() { } } +Search::SearchManager* ThreadPool::main_manager() { + return static_cast(main_thread()->worker.get()->manager.get()); +} + +uint64_t ThreadPool::nodes_searched() const { return accumulate(&Search::Worker::nodes); } +uint64_t ThreadPool::tb_hits() const { return accumulate(&Search::Worker::tbHits); } + // Creates/destroys threads to match the requested number. // Created and launched threads will immediately go to sleep in idle_loop. // Upon resizing, threads are recreated to allow for binding if necessary. diff --git a/src/thread.h b/src/thread.h index 0d4c252ccc3..81fcc72a7ee 100644 --- a/src/thread.h +++ b/src/thread.h @@ -33,6 +33,7 @@ namespace Stockfish { + class OptionsMap; using Value = int; @@ -83,15 +84,13 @@ class ThreadPool { void clear(); void set(Search::SharedState); - Search::SearchManager* main_manager() const { - return static_cast(main_thread()->worker.get()->manager.get()); - }; - Thread* main_thread() const { return threads.front(); } - uint64_t nodes_searched() const { return accumulate(&Search::Worker::nodes); } - uint64_t tb_hits() const { return accumulate(&Search::Worker::tbHits); } - Thread* get_best_thread() const; - void start_searching(); - void wait_for_search_finished() const; + Search::SearchManager* main_manager(); + Thread* main_thread() const { return threads.front(); } + uint64_t nodes_searched() const; + uint64_t tb_hits() const; + Thread* get_best_thread() const; + void start_searching(); + void wait_for_search_finished() const; std::atomic_bool stop, abortedSearch, increaseDepth; diff --git a/src/uci.cpp b/src/uci.cpp index 357369bf562..fda336b0d8e 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -22,25 +22,25 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include "benchmark.h" #include "evaluate.h" #include "movegen.h" -#include "nnue/evaluate_nnue.h" -#include "nnue/nnue_architecture.h" +#include "nnue/network.h" +#include "nnue/nnue_common.h" +#include "perft.h" #include "position.h" #include "search.h" #include "syzygy/tbprobe.h" #include "types.h" #include "ucioption.h" -#include "perft.h" namespace Stockfish { @@ -48,17 +48,20 @@ constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKB constexpr int NormalizeToPawnValue = 356; constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048; -UCI::UCI(int argc, char** argv) : - cli(argc, argv) { - evalFiles = {{Eval::NNUE::Big, {"EvalFile", EvalFileDefaultNameBig, "None", ""}}, - {Eval::NNUE::Small, {"EvalFileSmall", EvalFileDefaultNameSmall, "None", ""}}}; +namespace NN = Eval::NNUE; +UCI::UCI(int argc, char** argv) : + networks(NN::Networks( + NN::NetworkBig({EvalFileDefaultNameBig, "None", ""}, NN::embeddedNNUEBig), + NN::NetworkSmall({EvalFileDefaultNameSmall, "None", ""}, NN::embeddedNNUESmall))), + cli(argc, argv) { + options["Debug Log File"] << Option("", [](const Option& o) { start_logger(o); }); options["Threads"] << Option(1, 1, 1024, [this](const Option&) { - threads.set({options, threads, tt}); + threads.set({options, threads, tt, networks}); }); options["Hash"] << Option(16, 1, MaxHashMB, [this](const Option& o) { @@ -80,14 +83,17 @@ UCI::UCI(int argc, char** argv) : options["SyzygyProbeDepth"] << Option(1, 1, 100); options["Syzygy50MoveRule"] << Option(true); options["SyzygyProbeLimit"] << Option(7, 0, 7); - options["EvalFile"] << Option(EvalFileDefaultNameBig, [this](const Option&) { - evalFiles = Eval::NNUE::load_networks(cli.binaryDirectory, options, evalFiles); + options["EvalFile"] << Option(EvalFileDefaultNameBig, [this](const Option& o) { + networks.big.load(cli.binaryDirectory, o); }); - options["EvalFileSmall"] << Option(EvalFileDefaultNameSmall, [this](const Option&) { - evalFiles = Eval::NNUE::load_networks(cli.binaryDirectory, options, evalFiles); + options["EvalFileSmall"] << Option(EvalFileDefaultNameSmall, [this](const Option& o) { + networks.small.load(cli.binaryDirectory, o); }); - threads.set({options, threads, tt}); + networks.big.load(cli.binaryDirectory, options["EvalFile"]); + networks.small.load(cli.binaryDirectory, options["EvalFileSmall"]); + + threads.set({options, threads, tt, networks}); search_clear(); // After threads are up } @@ -157,7 +163,7 @@ void UCI::loop() { std::string f; if (is >> std::skipws >> f) filename = f; - Eval::NNUE::save_eval(filename, Eval::NNUE::Big, evalFiles); + networks.big.save(filename); } else if (token == "--help" || token == "help" || token == "--license" || token == "license") sync_cout @@ -218,7 +224,8 @@ void UCI::go(Position& pos, std::istringstream& is, StateListPtr& states) { Search::LimitsType limits = parse_limits(pos, is); - Eval::NNUE::verify(options, evalFiles); + networks.big.verify(options["EvalFile"]); + networks.small.verify(options["EvalFileSmall"]); if (limits.perft) { @@ -283,9 +290,11 @@ void UCI::trace_eval(Position& pos) { Position p; p.set(pos.fen(), options["UCI_Chess960"], &states->back()); - Eval::NNUE::verify(options, evalFiles); + networks.big.verify(options["EvalFile"]); + networks.small.verify(options["EvalFileSmall"]); + - sync_cout << "\n" << Eval::trace(p) << sync_endl; + sync_cout << "\n" << Eval::trace(p, networks) << sync_endl; } void UCI::search_clear() { diff --git a/src/uci.h b/src/uci.h index f25bb8d517f..dd55862ad17 100644 --- a/src/uci.h +++ b/src/uci.h @@ -22,13 +22,13 @@ #include #include -#include "evaluate.h" #include "misc.h" +#include "nnue/network.h" #include "position.h" +#include "search.h" #include "thread.h" #include "tt.h" #include "ucioption.h" -#include "search.h" namespace Stockfish { @@ -53,8 +53,8 @@ class UCI { const std::string& working_directory() const { return cli.workingDirectory; } - OptionsMap options; - Eval::NNUE::EvalFiles evalFiles; + OptionsMap options; + Eval::NNUE::Networks networks; private: TranspositionTable tt; From daa3ef9148e8a141d2562900924d578d6462ba72 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sat, 9 Mar 2024 19:49:59 +0300 Subject: [PATCH 1402/1766] Simplify increaseDepth to boolean expression Non-functional Simplification, maintaining the same logic as before. Big thanks to @peregrineshahin for helping with the code. Passed non-regression bounds: https://tests.stockfishchess.org/tests/view/65ec93860ec64f0526c42375 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 101088 W: 26196 L: 26047 D: 48845 Ptnml(0-2): 405, 11580, 26473, 11633, 453 closes https://github.com/official-stockfish/Stockfish/pull/5103 No functional change --- src/search.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index f4ec8fb16dc..a3e53e025fa 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -464,11 +464,10 @@ void Search::Worker::iterative_deepening() { else threads.stop = true; } - else if (!mainThread->ponder - && mainThread->tm.elapsed(threads.nodes_searched()) > totalTime * 0.506) - threads.increaseDepth = false; else - threads.increaseDepth = true; + threads.increaseDepth = + mainThread->ponder + || mainThread->tm.elapsed(threads.nodes_searched()) <= totalTime * 0.506; } mainThread->iterValue[iterIdx] = bestValue; From 627974c99fcd5a3dcbd5a8e0eb12f2afeb2d0a9a Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Tue, 12 Mar 2024 17:10:28 +0300 Subject: [PATCH 1403/1766] Search + Eval + Movepick Tune Passed STC: https://tests.stockfishchess.org/tests/view/65ef15220ec64f0526c44b04 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 24480 W: 6459 L: 6153 D: 11868 Ptnml(0-2): 101, 2798, 6184, 3008, 149 Passed LTC: https://tests.stockfishchess.org/tests/view/65ef4bac0ec64f0526c44f50 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 53316 W: 13561 L: 13203 D: 26552 Ptnml(0-2): 27, 5925, 14408, 6259, 39 closes https://github.com/official-stockfish/Stockfish/pull/5104 Bench: 1715522 --- src/evaluate.cpp | 8 ++++---- src/evaluate.h | 2 +- src/movepick.cpp | 16 ++++++++-------- src/search.cpp | 24 ++++++++++++------------ src/tt.cpp | 4 ++-- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 56abe6cb9c8..f4d18d8e4e0 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -59,15 +59,15 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, const Position& pos, : networks.big.evaluate(pos, true, &nnueComplexity, false); // Blend optimism and eval with nnue complexity and material imbalance - optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / 512; - nnue -= nnue * (nnueComplexity + std::abs(simpleEval - nnue)) / 32768; + optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / 524; + nnue -= nnue * (nnueComplexity + std::abs(simpleEval - nnue)) / 31950; int npm = pos.non_pawn_material() / 64; - int v = (nnue * (915 + npm + 9 * pos.count()) + optimism * (154 + npm)) / 1024; + int v = (nnue * (927 + npm + 9 * pos.count()) + optimism * (159 + npm)) / 1000; // Damp down the evaluation linearly when shuffling int shuffling = pos.rule50_count(); - v = v * (200 - shuffling) / 214; + v = v * (195 - shuffling) / 228; // Guarantee evaluation does not hit the tablebase range v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); diff --git a/src/evaluate.h b/src/evaluate.h index 754a92eb7eb..bd11e0c1675 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -29,7 +29,7 @@ class Position; namespace Eval { -constexpr inline int SmallNetThreshold = 1139, PsqtOnlyThreshold = 2500; +constexpr inline int SmallNetThreshold = 1136, PsqtOnlyThreshold = 2656; // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the diff --git a/src/movepick.cpp b/src/movepick.cpp index 33791922e4b..c1119cf11eb 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -190,18 +190,18 @@ void MovePicker::score() { m.value += bool(pos.check_squares(pt) & to) * 16384; // bonus for escaping from capture - m.value += threatenedPieces & from ? (pt == QUEEN && !(to & threatenedByRook) ? 50000 - : pt == ROOK && !(to & threatenedByMinor) ? 25000 - : !(to & threatenedByPawn) ? 15000 + m.value += threatenedPieces & from ? (pt == QUEEN && !(to & threatenedByRook) ? 51000 + : pt == ROOK && !(to & threatenedByMinor) ? 24950 + : !(to & threatenedByPawn) ? 14450 : 0) : 0; // malus for putting piece en prise m.value -= !(threatenedPieces & from) - ? (pt == QUEEN ? bool(to & threatenedByRook) * 50000 - + bool(to & threatenedByMinor) * 10000 - : pt == ROOK ? bool(to & threatenedByMinor) * 25000 - : pt != PAWN ? bool(to & threatenedByPawn) * 15000 + ? (pt == QUEEN ? bool(to & threatenedByRook) * 48150 + + bool(to & threatenedByMinor) * 10650 + : pt == ROOK ? bool(to & threatenedByMinor) * 24500 + : pt != PAWN ? bool(to & threatenedByPawn) * 14950 : 0) : 0; } @@ -241,7 +241,7 @@ Move MovePicker::select(Pred filter) { // moves left, picking the move with the highest score from a list of generated moves. Move MovePicker::next_move(bool skipQuiets) { - auto quiet_threshold = [](Depth d) { return -3330 * d; }; + auto quiet_threshold = [](Depth d) { return -3550 * d; }; top: switch (stage) diff --git a/src/search.cpp b/src/search.cpp index a3e53e025fa..5cc6796888d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -55,8 +55,8 @@ namespace { // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) { - Value futilityMult = 121 - 43 * noTtCutNode; - Value improvingDeduction = 3 * improving * futilityMult / 2; + Value futilityMult = 122 - 46 * noTtCutNode; + Value improvingDeduction = 57 * improving * futilityMult / 32; Value worseningDeduction = (331 + 45 * improving) * oppWorsening * futilityMult / 1024; return futilityMult * d - improvingDeduction - worseningDeduction; @@ -69,7 +69,7 @@ constexpr int futility_move_count(bool improving, Depth depth) { // Add correctionHistory value to raw staticEval and guarantee evaluation does not hit the tablebase range Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { auto cv = w.correctionHistory[pos.side_to_move()][pawn_structure_index(pos)]; - v += cv * std::abs(cv) / 10759; + v += cv * std::abs(cv) / 11450; return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } @@ -77,7 +77,7 @@ Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { int stat_bonus(Depth d) { return std::min(249 * d - 327, 1192); } // History and stats update malus, based on depth -int stat_malus(Depth d) { return std::min(516 * d - 299, 1432); } +int stat_malus(Depth d) { return std::min(516 * d - 299, 1254); } // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(size_t nodes) { return VALUE_DRAW - 1 + Value(nodes & 0x2); } @@ -301,12 +301,12 @@ void Search::Worker::iterative_deepening() { // Reset aspiration window starting size Value avg = rootMoves[pvIdx].averageScore; - delta = 9 + avg * avg / 12804; + delta = 9 + avg * avg / 12800; alpha = std::max(avg - delta, -VALUE_INFINITE); beta = std::min(avg + delta, VALUE_INFINITE); // Adjust optimism based on root move's averageScore (~4 Elo) - optimism[us] = 131 * avg / (std::abs(avg) + 90); + optimism[us] = 130 * avg / (std::abs(avg) + 90); optimism[~us] = -optimism[us]; // Start with a small aspiration window and, in the case of a fail @@ -500,7 +500,7 @@ void Search::Worker::clear() { h->fill(-71); for (size_t i = 1; i < reductions.size(); ++i) - reductions[i] = int((19.02 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); + reductions[i] = int((19.80 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); } @@ -732,12 +732,12 @@ Value Search::Worker::search( // Use static evaluation difference to improve quiet move ordering (~9 Elo) if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture) { - int bonus = std::clamp(-14 * int((ss - 1)->staticEval + ss->staticEval), -1621, 1237); + int bonus = std::clamp(-14 * int((ss - 1)->staticEval + ss->staticEval), -1621, 1238); bonus = bonus > 0 ? 2 * bonus : bonus / 2; thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) thisThread->pawnHistory[pawn_structure_index(pos)][pos.piece_on(prevSq)][prevSq] - << bonus / 4; + << bonus / 2; } // Set up the improving flag, which is true if current static evaluation is @@ -828,7 +828,7 @@ Value Search::Worker::search( // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search returns a value // much above beta, we can (almost) safely prune the previous move. - probCutBeta = beta + 164 - 62 * improving; + probCutBeta = beta + 168 - 64 * improving; if ( !PvNode && depth > 3 && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY @@ -1139,7 +1139,7 @@ Value Search::Worker::search( + (*contHist[3])[movedPiece][move.to_sq()] - 4587; // Decrease/increase reduction for moves with a good/bad history (~8 Elo) - r -= ss->statScore / 12372; + r -= ss->statScore / 14956; // Step 17. Late moves reduction / extension (LMR, ~117 Elo) if (depth >= 2 && moveCount > 1 + rootNode) @@ -1627,7 +1627,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth Search::Worker::reduction(bool i, Depth d, int mn, int delta) { int reductionScale = reductions[d] * reductions[mn]; - return (reductionScale + 1091 - delta * 759 / rootDelta) / 1024 + (!i && reductionScale > 952); + return (reductionScale + 1091 - delta * 759 / rootDelta) / 1024 + (!i && reductionScale > 950); } namespace { diff --git a/src/tt.cpp b/src/tt.cpp index 8ef06e6355c..e62e0c17053 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -134,8 +134,8 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const { // Find an entry to be replaced according to the replacement strategy TTEntry* replace = tte; for (int i = 1; i < ClusterSize; ++i) - if (replace->depth8 - replace->relative_age(generation8) - > tte[i].depth8 - tte[i].relative_age(generation8)) + if (replace->depth8 - replace->relative_age(generation8) * 2 + > tte[i].depth8 - tte[i].relative_age(generation8) * 2) replace = &tte[i]; return found = false, replace; From 55df0ee00914f6bb5e9ecce4ace47f00b758098c Mon Sep 17 00:00:00 2001 From: Disservin Date: Tue, 12 Mar 2024 18:20:19 +0100 Subject: [PATCH 1404/1766] Fix Raspberry Pi Compilation Reported by @Torom over discord. > dev build fails on Raspberry Pi 5 with clang ``` clang++ -o stockfish benchmark.o bitboard.o evaluate.o main.o misc.o movegen.o movepick.o position.o search.o thread.o timeman.o tt.o uci.o ucioption.o tune.o tbprobe.o nnue_misc.o half_ka_v2_hm.o network.o -fprofile-instr-generate -latomic -lpthread -Wall -Wcast-qual -fno-exceptions -std=c++17 -fprofile-instr-generate -pedantic -Wextra -Wshadow -Wmissing-prototypes -Wconditional-uninitialized -DUSE_PTHREADS -DNDEBUG -O3 -funroll-loops -DIS_64BIT -DUSE_POPCNT -DUSE_NEON=8 -march=armv8.2-a+dotprod -DUSE_NEON_DOTPROD -DGIT_SHA=627974c9 -DGIT_DATE=20240312 -DARCH=armv8-dotprod -flto=full /tmp/lto-llvm-e9300e.o: in function `_GLOBAL__sub_I_network.cpp': ld-temp.o:(.text.startup+0x704c): relocation truncated to fit: R_AARCH64_LDST64_ABS_LO12_NC against symbol `gEmbeddedNNUEBigEnd' defined in .rodata section in /tmp/lto-llvm-e9300e.o /usr/bin/ld: ld-temp.o:(.text.startup+0x704c): warning: one possible cause of this error is that the symbol is being referenced in the indicated code as if it had a larger alignment than was declared where it was defined ld-temp.o:(.text.startup+0x7068): relocation truncated to fit: R_AARCH64_LDST64_ABS_LO12_NC against symbol `gEmbeddedNNUESmallEnd' defined in .rodata section in /tmp/lto-llvm-e9300e.o /usr/bin/ld: ld-temp.o:(.text.startup+0x7068): warning: one possible cause of this error is that the symbol is being referenced in the indicated code as if it had a larger alignment than was declared where it was defined clang: error: linker command failed with exit code 1 (use -v to see invocation) make[2]: *** [Makefile:1051: stockfish] Error 1 make[2]: Leaving directory '/home/torsten/chess/Stockfish_master/src' make[1]: *** [Makefile:1058: clang-profile-make] Error 2 make[1]: Leaving directory '/home/torsten/chess/Stockfish_master/src' make: *** [Makefile:886: profile-build] Error 2 ``` closes https://github.com/official-stockfish/Stockfish/pull/5106 No functional change --- src/Makefile | 2 +- src/nnue/network.cpp | 28 ++++++++++++++++++++++++---- src/nnue/network.h | 24 ++++++++---------------- src/uci.cpp | 4 ++-- 4 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/Makefile b/src/Makefile index bd04d2c410e..75f31108159 100644 --- a/src/Makefile +++ b/src/Makefile @@ -502,7 +502,7 @@ endif # In earlier NDK versions, you'll need to pass -fno-addrsig if using GNU binutils. # Currently we don't know how to make PGO builds with the NDK yet. ifeq ($(COMP),ndk) - CXXFLAGS += -stdlib=libc++ -fPIE -mcmodel=large + CXXFLAGS += -stdlib=libc++ -fPIE comp=clang ifeq ($(arch),armv7) CXX=armv7a-linux-androideabi16-clang++ diff --git a/src/nnue/network.cpp b/src/nnue/network.cpp index 5d4e0954d78..bea3e7cb398 100644 --- a/src/nnue/network.cpp +++ b/src/nnue/network.cpp @@ -55,15 +55,33 @@ const unsigned char gEmbeddedNNUESmallData[1] = {0x0}; const unsigned char* const gEmbeddedNNUESmallEnd = &gEmbeddedNNUESmallData[1]; const unsigned int gEmbeddedNNUESmallSize = 1; #endif + +struct EmbeddedNNUE { + EmbeddedNNUE(const unsigned char* embeddedData, + const unsigned char* embeddedEnd, + const unsigned int embeddedSize) : + data(embeddedData), + end(embeddedEnd), + size(embeddedSize) {} + const unsigned char* data; + const unsigned char* end; + const unsigned int size; +}; + +using namespace Stockfish::Eval::NNUE; + +EmbeddedNNUE get_embedded(EmbeddedNNUEType type) { + if (type == EmbeddedNNUEType::BIG) + return EmbeddedNNUE(gEmbeddedNNUEBigData, gEmbeddedNNUEBigEnd, gEmbeddedNNUEBigSize); + else + return EmbeddedNNUE(gEmbeddedNNUESmallData, gEmbeddedNNUESmallEnd, gEmbeddedNNUESmallSize); +} + } namespace Stockfish::Eval::NNUE { -const EmbeddedNNUE embeddedNNUEBig(gEmbeddedNNUEBigData, gEmbeddedNNUEBigEnd, gEmbeddedNNUEBigSize); -const EmbeddedNNUE - embeddedNNUESmall(gEmbeddedNNUESmallData, gEmbeddedNNUESmallEnd, gEmbeddedNNUESmallSize); - namespace Detail { @@ -302,6 +320,8 @@ void Network::load_internal() { } }; + const auto embedded = get_embedded(embeddedType); + MemoryBuffer buffer(const_cast(reinterpret_cast(embedded.data)), size_t(embedded.size)); diff --git a/src/nnue/network.h b/src/nnue/network.h index c1ed7717914..21e1c622205 100644 --- a/src/nnue/network.h +++ b/src/nnue/network.h @@ -34,27 +34,19 @@ namespace Stockfish::Eval::NNUE { -struct EmbeddedNNUE { - EmbeddedNNUE(const unsigned char* embeddedData, - const unsigned char* embeddedEnd, - const unsigned int embeddedSize) : - data(embeddedData), - end(embeddedEnd), - size(embeddedSize) {} - const unsigned char* data; - const unsigned char* end; - const unsigned int size; + +enum class EmbeddedNNUEType { + BIG, + SMALL, }; -extern const EmbeddedNNUE embeddedNNUEBig; -extern const EmbeddedNNUE embeddedNNUESmall; template class Network { public: - Network(EvalFile file, EmbeddedNNUE embeddedEval) : + Network(EvalFile file, EmbeddedNNUEType type) : evalFile(file), - embedded(embeddedEval) {} + embeddedType(type) {} void load(const std::string& rootDirectory, std::string evalfilePath); bool save(const std::optional& filename) const; @@ -92,8 +84,8 @@ class Network { // Evaluation function AlignedPtr network[LayerStacks]; - EvalFile evalFile; - EmbeddedNNUE embedded; + EvalFile evalFile; + EmbeddedNNUEType embeddedType; // Hash value of evaluation function structure static constexpr std::uint32_t hash = Transformer::get_hash_value() ^ Arch::get_hash_value(); diff --git a/src/uci.cpp b/src/uci.cpp index fda336b0d8e..cf0e3f09cc7 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -54,8 +54,8 @@ namespace NN = Eval::NNUE; UCI::UCI(int argc, char** argv) : networks(NN::Networks( - NN::NetworkBig({EvalFileDefaultNameBig, "None", ""}, NN::embeddedNNUEBig), - NN::NetworkSmall({EvalFileDefaultNameSmall, "None", ""}, NN::embeddedNNUESmall))), + NN::NetworkBig({EvalFileDefaultNameBig, "None", ""}, NN::EmbeddedNNUEType::BIG), + NN::NetworkSmall({EvalFileDefaultNameSmall, "None", ""}, NN::EmbeddedNNUEType::SMALL))), cli(argc, argv) { options["Debug Log File"] << Option("", [](const Option& o) { start_logger(o); }); From ee2ee6bdc42af80430e7468da4208e379b40c393 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Wed, 13 Mar 2024 19:21:13 +0300 Subject: [PATCH 1405/1766] Give more bonuses to quiet move that caused a fail low Give extra bonus if search result is far below static evaluation of position. Passed STC: https://tests.stockfishchess.org/tests/view/65edf1250ec64f0526c43787 LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 90816 W: 23713 L: 23307 D: 43796 Ptnml(0-2): 401, 10725, 22742, 11147, 393 Passed LTC: https://tests.stockfishchess.org/tests/view/65ef5ed70ec64f0526c450af LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 66618 W: 16950 L: 16565 D: 33103 Ptnml(0-2): 35, 7372, 18139, 7699, 64 closes https://github.com/official-stockfish/Stockfish/pull/5111 Bench: 2002517 --- src/search.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 5cc6796888d..5bd59712a5b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1321,7 +1321,8 @@ Value Search::Worker::search( else if (!priorCapture && prevSq != SQ_NONE) { int bonus = (depth > 5) + (PvNode || cutNode) + ((ss - 1)->statScore < -14446) - + ((ss - 1)->moveCount > 10); + + ((ss - 1)->moveCount > 11) + + (!ss->inCheck && bestValue <= ss->staticEval - 150); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] From 23493de08272226394fb69c4f31182b48b0e739e Mon Sep 17 00:00:00 2001 From: Lemmy <10430540+TierynnB@users.noreply.github.com> Date: Mon, 11 Mar 2024 07:57:22 +1000 Subject: [PATCH 1406/1766] Sudden Death - Improve TM Due to the 50 estimated move horizon, once a sudden death game got below 1 second, the time allocation for optimumTime would go into the negative and SF would start instamoving. To counter this, once limits.time is below 1 second, the move horizon will start decreasing, at a rate of 1 move per 20ms. This was just what seemed a reasonable rate of decay. Fishtest sudden death TC 5+0 https://tests.stockfishchess.org/tests/live_elo/65ee2cdf0ec64f0526c43bbb LLR: 2.99 (-2.94,2.94) <0.00,2.00> Total: 3348 W: 1070 L: 727 D:1551 Ptnml(0-2): 46, 277, 738, 514, 99 Fishtest SD TC 10+0 https://tests.stockfishchess.org/tests/live_elo/65ee401e0ec64f0526c43cf7 LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 3780 W: 1097 L: 808 D: 1875 Ptnml(0-2): 11, 353, 919, 550, 57 Neutral Non-Regression STC 10+0.1 https://tests.stockfishchess.org/tests/live_elo/65ee45ff0ec64f0526c43d68 LLR: 2.95 (-2.94,2.94) <-1.75, 0.25> Total: 123616 W: 32054 L: 31927 D:59635 Ptnml(0-2): 493, 14323, 32105, 14338, 549 Neutral Non-Regression LTC 60+0.6 https://tests.stockfishchess.org/tests/live_elo/65ef1eec0ec64f0526c44bc4 LLR: 2.95 (-2.94,2.94) <-1.75, 0.25> Total: 130482 W: 32961 L: 32855 D:64666 Ptnml(0-2): 88, 13412, 38123, 13542, 76 closes https://github.com/official-stockfish/Stockfish/pull/5112 Bench: 2002517 --- src/timeman.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/timeman.cpp b/src/timeman.cpp index 4607344eab9..229ff3e9d6b 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -84,6 +84,12 @@ void TimeManagement::init(Search::LimitsType& limits, // Maximum move horizon of 50 moves int mtg = limits.movestogo ? std::min(limits.movestogo, 50) : 50; + // if less than one second, gradually reduce mtg + if (limits.time[us] < 1000 && (double(mtg) / limits.time[us] > 0.05)) + { + mtg = limits.time[us] * 0.05; + } + // Make sure timeLeft is > 0 since we may use it as a divisor TimePoint timeLeft = std::max(TimePoint(1), limits.time[us] + limits.inc[us] * (mtg - 1) - moveOverhead * (2 + mtg)); From abd82396a1ceef4d49c8be30c366964bf18a74e2 Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Wed, 13 Mar 2024 15:55:56 +0800 Subject: [PATCH 1407/1766] Make effort part of RootMove struct Also includes several small cleanups. Passed STC: https://tests.stockfishchess.org/tests/view/65f15cfe0ec64f0526c473a0 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 71136 W: 18456 L: 18273 D: 34407 Ptnml(0-2): 311, 8014, 18708, 8251, 284 closes https://github.com/official-stockfish/Stockfish/pull/5114 No functional change --- src/search.cpp | 13 ++++--------- src/search.h | 3 +-- src/thread.cpp | 2 -- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5bd59712a5b..fc92d1a9c32 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -214,7 +214,7 @@ void Search::Worker::start_searching() { // consumed, the user stops the search, or the maximum search depth is reached. void Search::Worker::iterative_deepening() { - SearchManager* mainThread = (thread_idx == 0 ? main_manager() : nullptr); + SearchManager* mainThread = (is_mainthread() ? main_manager() : nullptr); Move pv[MAX_PLY + 1]; @@ -426,9 +426,7 @@ void Search::Worker::iterative_deepening() { // Do we have time for the next iteration? Can we stop searching now? if (limits.use_time_management() && !threads.stop && !mainThread->stopOnPonderhit) { - auto bestmove = rootMoves[0].pv[0]; - int nodesEffort = effort[bestmove.from_sq()][bestmove.to_sq()] * 100 - / std::max(size_t(1), size_t(nodes)); + int nodesEffort = rootMoves[0].effort * 100 / std::max(size_t(1), size_t(nodes)); double fallingEval = (1067 + 223 * (mainThread->bestPreviousAverageScore - bestValue) + 97 * (mainThread->iterValue[iterIdx] - bestValue)) @@ -450,9 +448,7 @@ void Search::Worker::iterative_deepening() { if (completedDepth >= 10 && nodesEffort >= 97 && mainThread->tm.elapsed(threads.nodes_searched()) > totalTime * 0.739 && !mainThread->ponder) - { threads.stop = true; - } // Stop the search if we have exceeded the totalTime if (mainThread->tm.elapsed(threads.nodes_searched()) > totalTime) @@ -1199,9 +1195,6 @@ Value Search::Worker::search( // Step 19. Undo move pos.undo_move(move); - if (rootNode) - effort[move.from_sq()][move.to_sq()] += nodes - nodeCount; - assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); // Step 20. Check for a new best move @@ -1216,6 +1209,8 @@ Value Search::Worker::search( RootMove& rm = *std::find(thisThread->rootMoves.begin(), thisThread->rootMoves.end(), move); + rm.effort += nodes - nodeCount; + rm.averageScore = rm.averageScore != -VALUE_INFINITE ? (2 * value + rm.averageScore) / 3 : value; diff --git a/src/search.h b/src/search.h index 4908e535c6a..22f75ffd4d8 100644 --- a/src/search.h +++ b/src/search.h @@ -89,6 +89,7 @@ struct RootMove { return m.score != score ? m.score < score : m.previousScore < previousScore; } + uint64_t effort = 0; Value score = -VALUE_INFINITE; Value previousScore = -VALUE_INFINITE; Value averageScore = -VALUE_INFINITE; @@ -230,8 +231,6 @@ class Worker { return static_cast(manager.get()); } - std::array, SQUARE_NB> effort; - LimitsType limits; size_t pvIdx, pvLast; diff --git a/src/thread.cpp b/src/thread.cpp index a3823d0cfd0..d968271f1e6 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -19,7 +19,6 @@ #include "thread.h" #include -#include #include #include #include @@ -211,7 +210,6 @@ void ThreadPool::start_thinking(const OptionsMap& options, th->worker->rootPos.set(pos.fen(), pos.is_chess960(), &th->worker->rootState); th->worker->rootState = setupStates->back(); th->worker->tbConfig = tbConfig; - th->worker->effort = {}; } main_thread()->start_searching(); From fb07281f5590bc216ecbacd468aa0d06fdead70c Mon Sep 17 00:00:00 2001 From: Disservin Date: Thu, 14 Mar 2024 10:38:20 +0100 Subject: [PATCH 1408/1766] Fix false positives from ThreadSanitizer Since Linux Kernel 6.5 we are getting false positives from the ci, lower the ALSR entropy to disable ALSR, which works as a temporary workaround. https://github.com/google/sanitizers/issues/1716 https://bugs.launchpad.net/ubuntu/+source/linux/+bug/2056762 closes https://github.com/official-stockfish/Stockfish/pull/5115 No functional change --- tests/instrumented.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/instrumented.sh b/tests/instrumented.sh index 2a3eadc074e..525c7e04085 100755 --- a/tests/instrumented.sh +++ b/tests/instrumented.sh @@ -8,6 +8,13 @@ error() } trap 'error ${LINENO}' ERR +# Since Linux Kernel 6.5 we are getting false positives from the ci, +# lower the ALSR entropy to disable ALSR, which works as a temporary workaround. +# https://github.com/google/sanitizers/issues/1716 +# https://bugs.launchpad.net/ubuntu/+source/linux/+bug/2056762 +sudo sysctl -w vm.mmap_rnd_bits=28 + + # define suitable post and prefixes for testing options case $1 in --valgrind) From ed604600042a4f2c0023ac9a189215e50fc7aa0f Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Fri, 15 Mar 2024 18:55:40 +0300 Subject: [PATCH 1409/1766] Clamp history bonus to stats range Before, one always had to keep track of the bonus one assigns to a history to stop the stats from overflowing. This is a quality of life improvement. Since this would often go unnoticed during benching. Passed non-regression bounds: https://tests.stockfishchess.org/tests/view/65ef2af40ec64f0526c44cbc LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 179232 W: 46513 L: 46450 D: 86269 Ptnml(0-2): 716, 20323, 47452, 20432, 693 closes https://github.com/official-stockfish/Stockfish/pull/5116 No functional change --- src/movepick.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/movepick.h b/src/movepick.h index 357918a90f2..a16fdcd273e 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -19,17 +19,17 @@ #ifndef MOVEPICK_H_INCLUDED #define MOVEPICK_H_INCLUDED +#include #include #include -#include #include #include #include #include // IWYU pragma: keep #include "movegen.h" -#include "types.h" #include "position.h" +#include "types.h" namespace Stockfish { @@ -69,10 +69,11 @@ class StatsEntry { operator const T&() const { return entry; } void operator<<(int bonus) { - assert(std::abs(bonus) <= D); // Ensure range is [-D, D] static_assert(D <= std::numeric_limits::max(), "D overflows T"); - entry += bonus - entry * std::abs(bonus) / D; + // Make sure that bonus is in range [-D, D] + int clampedBonus = std::clamp(bonus, -D, D); + entry += clampedBonus - entry * std::abs(clampedBonus) / D; assert(std::abs(entry) <= D); } From 134e6d7bb400a372d168806e0f6f60d5e23a4cbf Mon Sep 17 00:00:00 2001 From: Disservin Date: Sun, 17 Mar 2024 10:33:03 +0100 Subject: [PATCH 1410/1766] Consistent use of anonymous namespace Also change `bindThisThread` to match the current code style for function naming. closes https://github.com/official-stockfish/Stockfish/pull/5118 No functional change --- src/misc.cpp | 8 ++++--- src/misc.h | 2 +- src/nnue/nnue_misc.cpp | 7 +++--- src/thread.cpp | 2 +- src/tt.cpp | 2 +- src/tune.cpp | 52 +++++++++++++++++++++++------------------- 6 files changed, 41 insertions(+), 32 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index 4885a5cd35c..270d25ad4bc 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -596,14 +596,15 @@ namespace WinProcGroup { #ifndef _WIN32 -void bindThisThread(size_t) {} +void bind_this_thread(size_t) {} #else +namespace { // Retrieves logical processor information using Windows-specific // API and returns the best node id for the thread with index idx. Original // code from Texel by Peter Österlund. -static int best_node(size_t idx) { +int best_node(size_t idx) { int threads = 0; int nodes = 0; @@ -668,10 +669,11 @@ static int best_node(size_t idx) { // then return -1 and let the OS to decide what to do. return idx < groups.size() ? groups[idx] : -1; } +} // Sets the group affinity of the current thread -void bindThisThread(size_t idx) { +void bind_this_thread(size_t idx) { // Use only local variables to be thread-safe int node = best_node(idx); diff --git a/src/misc.h b/src/misc.h index 9ad5c3ca57e..de34ee111f7 100644 --- a/src/misc.h +++ b/src/misc.h @@ -200,7 +200,7 @@ inline uint64_t mul_hi64(uint64_t a, uint64_t b) { // called to set group affinity for each thread. Original code from Texel by // Peter Österlund. namespace WinProcGroup { -void bindThisThread(size_t idx); +void bind_this_thread(size_t idx); } diff --git a/src/nnue/nnue_misc.cpp b/src/nnue/nnue_misc.cpp index c443aaf19b2..7005a61025e 100644 --- a/src/nnue/nnue_misc.cpp +++ b/src/nnue/nnue_misc.cpp @@ -51,10 +51,10 @@ void hint_common_parent_position(const Position& pos, const Networks& networks) networks.big.hint_common_access(pos, false); } - +namespace { // Converts a Value into (centi)pawns and writes it in a buffer. // The buffer must have capacity for at least 5 chars. -static void format_cp_compact(Value v, char* buffer) { +void format_cp_compact(Value v, char* buffer) { buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' '); @@ -90,7 +90,7 @@ static void format_cp_compact(Value v, char* buffer) { // Converts a Value into pawns, always keeping two decimals -static void format_cp_aligned_dot(Value v, std::stringstream& stream) { +void format_cp_aligned_dot(Value v, std::stringstream& stream) { const double pawns = std::abs(0.01 * UCI::to_cp(v)); @@ -99,6 +99,7 @@ static void format_cp_aligned_dot(Value v, std::stringstream& stream) { : ' ') << std::setiosflags(std::ios::fixed) << std::setw(6) << std::setprecision(2) << pawns; } +} // Returns a string with the value of each piece on a board, diff --git a/src/thread.cpp b/src/thread.cpp index d968271f1e6..90add4ad059 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -91,7 +91,7 @@ void Thread::idle_loop() { // just check if running threads are below a threshold, in this case, all this // NUMA machinery is not needed. if (nthreads > 8) - WinProcGroup::bindThisThread(idx); + WinProcGroup::bind_this_thread(idx); while (true) { diff --git a/src/tt.cpp b/src/tt.cpp index e62e0c17053..9d4d2eca47c 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -95,7 +95,7 @@ void TranspositionTable::clear(size_t threadCount) { threads.emplace_back([this, idx, threadCount]() { // Thread binding gives faster search on systems with a first-touch policy if (threadCount > 8) - WinProcGroup::bindThisThread(idx); + WinProcGroup::bind_this_thread(idx); // Each thread will zero its part of the hash table const size_t stride = size_t(clusterCount / threadCount), start = size_t(stride * idx), diff --git a/src/tune.cpp b/src/tune.cpp index 88b3b7912d5..3e5ebe5e6c3 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -30,37 +30,22 @@ using std::string; namespace Stockfish { -bool Tune::update_on_last; -const Option* LastOption = nullptr; -OptionsMap* Tune::options; -static std::map TuneResults; +bool Tune::update_on_last; +const Option* LastOption = nullptr; +OptionsMap* Tune::options; -string Tune::next(string& names, bool pop) { - - string name; - - do - { - string token = names.substr(0, names.find(',')); - - if (pop) - names.erase(0, token.size() + 1); - std::stringstream ws(token); - name += (ws >> token, token); // Remove trailing whitespace - - } while (std::count(name.begin(), name.end(), '(') - std::count(name.begin(), name.end(), ')')); - - return name; -} +namespace { +std::map TuneResults; -static void on_tune(const Option& o) { +void on_tune(const Option& o) { if (!Tune::update_on_last || LastOption == &o) Tune::read_options(); } -static void make_option(OptionsMap* options, const string& n, int v, const SetRange& r) { + +void make_option(OptionsMap* options, const string& n, int v, const SetRange& r) { // Do not generate option when there is nothing to tune (ie. min = max) if (r(v).first == r(v).second) @@ -77,6 +62,27 @@ static void make_option(OptionsMap* options, const string& n, int v, const SetRa << (r(v).second - r(v).first) / 20.0 << "," << "0.0020" << std::endl; } +} + +string Tune::next(string& names, bool pop) { + + string name; + + do + { + string token = names.substr(0, names.find(',')); + + if (pop) + names.erase(0, token.size() + 1); + + std::stringstream ws(token); + name += (ws >> token, token); // Remove trailing whitespace + + } while (std::count(name.begin(), name.end(), '(') - std::count(name.begin(), name.end(), ')')); + + return name; +} + template<> void Tune::Entry::init_option() { From 117e08c26454f2107a13d35945e3508ca6c0de5d Mon Sep 17 00:00:00 2001 From: Disservin Date: Sun, 17 Mar 2024 12:34:02 +0100 Subject: [PATCH 1411/1766] Fix header name in Makefile No functional change --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 75f31108159..672171bcd59 100644 --- a/src/Makefile +++ b/src/Makefile @@ -63,7 +63,7 @@ HEADERS = benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h \ nnue/layers/sqr_clipped_relu.h nnue/nnue_accumulator.h nnue/nnue_architecture.h \ nnue/nnue_common.h nnue/nnue_feature_transformer.h position.h \ search.h syzygy/tbprobe.h thread.h thread_win32_osx.h timeman.h \ - tt.h tune.h types.h uci.h ucioption.h perft.h nnue/network.cpp + tt.h tune.h types.h uci.h ucioption.h perft.h nnue/network.h OBJS = $(notdir $(SRCS:.cpp=.o)) From 9b92ada935ddf920491156be22f609afaca4d840 Mon Sep 17 00:00:00 2001 From: Robert Nurnberg Date: Sun, 17 Mar 2024 15:39:01 +0100 Subject: [PATCH 1412/1766] Base WDL model on material count and normalize evals dynamically This PR proposes to change the parameter dependence of Stockfish's internal WDL model from full move counter to material count. In addition it ensures that an evaluation of 100 centipawns always corresponds to a 50% win probability at fishtest LTC, whereas for master this holds only at move number 32. See also https://github.com/official-stockfish/Stockfish/pull/4920 and the discussion therein. The new model was fitted based on about 340M positions extracted from 5.6M fishtest LTC games from the last three weeks, involving SF versions from e67cc979fd2c0e66dfc2b2f2daa0117458cfc462 (SF 16.1) to current master. The involved commands are for [WDL_model](https://github.com/official-stockfish/WDL_model) are: ``` ./updateWDL.sh --firstrev e67cc979fd2c0e66dfc2b2f2daa0117458cfc462 python scoreWDL.py updateWDL.json --plot save --pgnName update_material.png --momType "material" --momTarget 58 --materialMin 10 --modelFitting optimizeProbability ``` The anchor `58` for the material count value was chosen to be as close as possible to the observed average material count of fishtest LTC games at move 32 (`43`), while not changing the value of `NormalizeToPawnValue` compared to the move-based WDL model by more than 1. The patch only affects the displayed cp and wdl values. closes https://github.com/official-stockfish/Stockfish/pull/5121 No functional change --- src/evaluate.cpp | 4 +- src/nnue/nnue_misc.cpp | 18 ++++---- src/search.cpp | 7 +-- src/uci.cpp | 99 +++++++++++++++++++++++++----------------- src/uci.h | 6 +-- 5 files changed, 76 insertions(+), 58 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index f4d18d8e4e0..c7adf50946f 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -92,11 +92,11 @@ std::string Eval::trace(Position& pos, const Eval::NNUE::Networks& networks) { Value v = networks.big.evaluate(pos, false); v = pos.side_to_move() == WHITE ? v : -v; - ss << "NNUE evaluation " << 0.01 * UCI::to_cp(v) << " (white side)\n"; + ss << "NNUE evaluation " << 0.01 * UCI::to_cp(v, pos) << " (white side)\n"; v = evaluate(networks, pos, VALUE_ZERO); v = pos.side_to_move() == WHITE ? v : -v; - ss << "Final evaluation " << 0.01 * UCI::to_cp(v) << " (white side)"; + ss << "Final evaluation " << 0.01 * UCI::to_cp(v, pos) << " (white side)"; ss << " [with scaled NNUE, ...]"; ss << "\n"; diff --git a/src/nnue/nnue_misc.cpp b/src/nnue/nnue_misc.cpp index 7005a61025e..725d90d27d6 100644 --- a/src/nnue/nnue_misc.cpp +++ b/src/nnue/nnue_misc.cpp @@ -54,11 +54,11 @@ void hint_common_parent_position(const Position& pos, const Networks& networks) namespace { // Converts a Value into (centi)pawns and writes it in a buffer. // The buffer must have capacity for at least 5 chars. -void format_cp_compact(Value v, char* buffer) { +void format_cp_compact(Value v, char* buffer, const Position& pos) { buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' '); - int cp = std::abs(UCI::to_cp(v)); + int cp = std::abs(UCI::to_cp(v, pos)); if (cp >= 10000) { buffer[1] = '0' + cp / 10000; @@ -90,9 +90,9 @@ void format_cp_compact(Value v, char* buffer) { // Converts a Value into pawns, always keeping two decimals -void format_cp_aligned_dot(Value v, std::stringstream& stream) { +void format_cp_aligned_dot(Value v, std::stringstream& stream, const Position& pos) { - const double pawns = std::abs(0.01 * UCI::to_cp(v)); + const double pawns = std::abs(0.01 * UCI::to_cp(v, pos)); stream << (v < 0 ? '-' : v > 0 ? '+' @@ -114,7 +114,7 @@ std::string trace(Position& pos, const Eval::NNUE::Networks& networks) { board[row][8 * 8 + 1] = '\0'; // A lambda to output one box of the board - auto writeSquare = [&board](File file, Rank rank, Piece pc, Value value) { + auto writeSquare = [&board, &pos](File file, Rank rank, Piece pc, Value value) { const int x = int(file) * 8; const int y = (7 - int(rank)) * 3; for (int i = 1; i < 8; ++i) @@ -125,7 +125,7 @@ std::string trace(Position& pos, const Eval::NNUE::Networks& networks) { if (pc != NO_PIECE) board[y + 1][x + 4] = PieceToChar[pc]; if (value != VALUE_NONE) - format_cp_compact(value, &board[y + 2][x + 2]); + format_cp_compact(value, &board[y + 2][x + 2], pos); }; // We estimate the value of each piece by doing a differential evaluation from @@ -180,13 +180,13 @@ std::string trace(Position& pos, const Eval::NNUE::Networks& networks) { { ss << "| " << bucket << " "; ss << " | "; - format_cp_aligned_dot(t.psqt[bucket], ss); + format_cp_aligned_dot(t.psqt[bucket], ss, pos); ss << " " << " | "; - format_cp_aligned_dot(t.positional[bucket], ss); + format_cp_aligned_dot(t.positional[bucket], ss, pos); ss << " " << " | "; - format_cp_aligned_dot(t.psqt[bucket] + t.positional[bucket], ss); + format_cp_aligned_dot(t.psqt[bucket] + t.positional[bucket], ss, pos); ss << " " << " |"; if (bucket == t.correctBucket) diff --git a/src/search.cpp b/src/search.cpp index fc92d1a9c32..9929ec27ed2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -155,7 +155,8 @@ void Search::Worker::start_searching() { { rootMoves.emplace_back(Move::none()); sync_cout << "info depth 0 score " - << UCI::value(rootPos.checkers() ? -VALUE_MATE : VALUE_DRAW) << sync_endl; + << UCI::to_score(rootPos.checkers() ? -VALUE_MATE : VALUE_DRAW, rootPos) + << sync_endl; } else { @@ -1898,10 +1899,10 @@ std::string SearchManager::pv(const Search::Worker& worker, ss << "info" << " depth " << d << " seldepth " << rootMoves[i].selDepth << " multipv " << i + 1 - << " score " << UCI::value(v); + << " score " << UCI::to_score(v, pos); if (worker.options["UCI_ShowWDL"]) - ss << UCI::wdl(v, pos.game_ply()); + ss << UCI::wdl(v, pos); if (i == pvIdx && !tb && updated) // tablebase- and previous-scores are exact ss << (rootMoves[i].scoreLowerbound diff --git a/src/uci.cpp b/src/uci.cpp index cf0e3f09cc7..cc03005ffc0 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "benchmark.h" @@ -44,9 +45,8 @@ namespace Stockfish { -constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; -constexpr int NormalizeToPawnValue = 356; -constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048; +constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; +constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048; namespace NN = Eval::NNUE; @@ -338,15 +338,43 @@ void UCI::position(Position& pos, std::istringstream& is, StateListPtr& states) } } -int UCI::to_cp(Value v) { return 100 * v / NormalizeToPawnValue; } +namespace { +std::pair win_rate_params(const Position& pos) { + + int material = pos.count() + 3 * pos.count() + 3 * pos.count() + + 5 * pos.count() + 9 * pos.count(); + + // The fitted model only uses data for material counts in [10, 78], and is anchored at count 58. + double m = std::clamp(material, 10, 78) / 58.0; + + // Return a = p_a(material) and b = p_b(material), see github.com/official-stockfish/WDL_model + constexpr double as[] = {-185.71965483, 504.85014385, -438.58295743, 474.04604627}; + constexpr double bs[] = {89.23542728, -137.02141296, 73.28669021, 47.53376190}; + + double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3]; + double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3]; -std::string UCI::value(Value v) { + return {a, b}; +} + +// The win rate model is 1 / (1 + exp((a - eval) / b)), where a = p_a(material) and b = p_b(material). +// It fits the LTC fishtest statistics rather accurately. +int win_rate_model(Value v, const Position& pos) { + + auto [a, b] = win_rate_params(pos); + + // Return the win rate in per mille units, rounded to the nearest integer. + return int(0.5 + 1000 / (1 + std::exp((a - double(v)) / b))); +} +} + +std::string UCI::to_score(Value v, const Position& pos) { assert(-VALUE_INFINITE < v && v < VALUE_INFINITE); std::stringstream ss; if (std::abs(v) < VALUE_TB_WIN_IN_MAX_PLY) - ss << "cp " << to_cp(v); + ss << "cp " << to_cp(v, pos); else if (std::abs(v) <= VALUE_TB) { const int ply = VALUE_TB - std::abs(v); // recompute ss->ply @@ -358,6 +386,30 @@ std::string UCI::value(Value v) { return ss.str(); } +// Turns a Value to an integer centipawn number, +// without treatment of mate and similar special scores. +int UCI::to_cp(Value v, const Position& pos) { + + // In general, the score can be defined via the the WDL as + // (log(1/L - 1) - log(1/W - 1)) / ((log(1/L - 1) + log(1/W - 1)) + // Based on our win_rate_model, this simply yields v / a. + + auto [a, b] = win_rate_params(pos); + + return std::round(100 * int(v) / a); +} + +std::string UCI::wdl(Value v, const Position& pos) { + std::stringstream ss; + + int wdl_w = win_rate_model(v, pos); + int wdl_l = win_rate_model(-v, pos); + int wdl_d = 1000 - wdl_w - wdl_l; + ss << " wdl " << wdl_w << " " << wdl_d << " " << wdl_l; + + return ss.str(); +} + std::string UCI::square(Square s) { return std::string{char('a' + file_of(s)), char('1' + rank_of(s))}; } @@ -383,41 +435,6 @@ std::string UCI::move(Move m, bool chess960) { return move; } -namespace { -// The win rate model returns the probability of winning (in per mille units) given an -// eval and a game ply. It fits the LTC fishtest statistics rather accurately. -int win_rate_model(Value v, int ply) { - - // The fitted model only uses data for moves in [8, 120], and is anchored at move 32. - double m = std::clamp(ply / 2 + 1, 8, 120) / 32.0; - - // The coefficients of a third-order polynomial fit is based on the fishtest data - // for two parameters that need to transform eval to the argument of a logistic - // function. - constexpr double as[] = {-1.06249702, 7.42016937, 0.89425629, 348.60356174}; - constexpr double bs[] = {-5.33122190, 39.57831533, -90.84473771, 123.40620748}; - - // Enforce that NormalizeToPawnValue corresponds to a 50% win rate at move 32. - static_assert(NormalizeToPawnValue == int(0.5 + as[0] + as[1] + as[2] + as[3])); - - double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3]; - double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3]; - - // Return the win rate in per mille units, rounded to the nearest integer. - return int(0.5 + 1000 / (1 + std::exp((a - double(v)) / b))); -} -} - -std::string UCI::wdl(Value v, int ply) { - std::stringstream ss; - - int wdl_w = win_rate_model(v, ply); - int wdl_l = win_rate_model(-v, ply); - int wdl_d = 1000 - wdl_w - wdl_l; - ss << " wdl " << wdl_w << " " << wdl_d << " " << wdl_l; - - return ss.str(); -} Move UCI::to_move(const Position& pos, std::string& str) { if (str.length() == 5) diff --git a/src/uci.h b/src/uci.h index dd55862ad17..237928d9abc 100644 --- a/src/uci.h +++ b/src/uci.h @@ -42,11 +42,11 @@ class UCI { void loop(); - static int to_cp(Value v); - static std::string value(Value v); + static int to_cp(Value v, const Position& pos); + static std::string to_score(Value v, const Position& pos); static std::string square(Square s); static std::string move(Move m, bool chess960); - static std::string wdl(Value v, int ply); + static std::string wdl(Value v, const Position& pos); static Move to_move(const Position& pos, std::string& str); static Search::LimitsType parse_limits(const Position& pos, std::istream& is); From 1a6c22c5114c6ae9f916a69e0446b6c9eb864d72 Mon Sep 17 00:00:00 2001 From: Gahtan Nahdi <155860115+gahtan-syarif@users.noreply.github.com> Date: Fri, 15 Mar 2024 16:50:27 +0700 Subject: [PATCH 1413/1766] Evaluation adjustment for different eval types Gives different eval scaling parameters for the three different types of evaluation (bignet, smallnet, psqtOnly). Passed STC: https://tests.stockfishchess.org/tests/view/65f4b0020ec64f0526c4a3bd LLR: 2.96 (-2.94,2.94) <0.00,2.00> Total: 168064 W: 43507 L: 42987 D: 81570 Ptnml(0-2): 662, 19871, 42445, 20393, 661 Passed LTC: https://tests.stockfishchess.org/tests/view/65f6be1a0ec64f0526c4c361 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 162564 W: 41188 L: 40604 D: 80772 Ptnml(0-2): 120, 18112, 44216, 18732, 102 closes https://github.com/official-stockfish/Stockfish/pull/5122 Bench: 2113576 --- src/evaluate.cpp | 37 +++++++++++++++++++++++++------------ src/evaluate.h | 2 +- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index c7adf50946f..3d1643fbd54 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -52,22 +52,35 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, const Position& pos, int simpleEval = simple_eval(pos, pos.side_to_move()); bool smallNet = std::abs(simpleEval) > SmallNetThreshold; bool psqtOnly = std::abs(simpleEval) > PsqtOnlyThreshold; - - int nnueComplexity; + int nnueComplexity; + int v; Value nnue = smallNet ? networks.small.evaluate(pos, true, &nnueComplexity, psqtOnly) : networks.big.evaluate(pos, true, &nnueComplexity, false); - // Blend optimism and eval with nnue complexity and material imbalance - optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / 524; - nnue -= nnue * (nnueComplexity + std::abs(simpleEval - nnue)) / 31950; - - int npm = pos.non_pawn_material() / 64; - int v = (nnue * (927 + npm + 9 * pos.count()) + optimism * (159 + npm)) / 1000; - - // Damp down the evaluation linearly when shuffling - int shuffling = pos.rule50_count(); - v = v * (195 - shuffling) / 228; + const auto adjustEval = [&](int optDiv, int nnueDiv, int pawnCountConstant, int pawnCountMul, + int npmConstant, int evalDiv, int shufflingConstant, + int shufflingDiv) { + // Blend optimism and eval with nnue complexity and material imbalance + optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / optDiv; + nnue -= nnue * (nnueComplexity + std::abs(simpleEval - nnue)) / nnueDiv; + + int npm = pos.non_pawn_material() / 64; + v = (nnue * (npm + pawnCountConstant + pawnCountMul * pos.count()) + + optimism * (npmConstant + npm)) + / evalDiv; + + // Damp down the evaluation linearly when shuffling + int shuffling = pos.rule50_count(); + v = v * (shufflingConstant - shuffling) / shufflingDiv; + }; + + if (!smallNet) + adjustEval(513, 32395, 919, 11, 145, 1036, 178, 204); + else if (psqtOnly) + adjustEval(517, 32857, 908, 7, 155, 1019, 224, 238); + else + adjustEval(499, 32793, 903, 9, 147, 1067, 208, 211); // Guarantee evaluation does not hit the tablebase range v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); diff --git a/src/evaluate.h b/src/evaluate.h index bd11e0c1675..29dff40d05b 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -29,7 +29,7 @@ class Position; namespace Eval { -constexpr inline int SmallNetThreshold = 1136, PsqtOnlyThreshold = 2656; +constexpr inline int SmallNetThreshold = 1050, PsqtOnlyThreshold = 2500; // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the From 8e61d70499a9cebf3b7d97cf74295be5261ac0af Mon Sep 17 00:00:00 2001 From: Gahtan Nahdi <155860115+gahtan-syarif@users.noreply.github.com> Date: Tue, 19 Mar 2024 02:45:09 +0700 Subject: [PATCH 1414/1766] Remove reduction increase on repetition Passed non-reg STC: https://tests.stockfishchess.org/tests/view/65f89ae30ec64f0526c4e0ff LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 173568 W: 45005 L: 44936 D: 83627 Ptnml(0-2): 684, 19878, 45628, 19873, 721 Passed non-reg LTC: https://tests.stockfishchess.org/tests/view/65fa0f370ec64f0526c4f42d LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 21138 W: 5432 L: 5216 D: 10490 Ptnml(0-2): 13, 2107, 6112, 2325, 12 closes https://github.com/official-stockfish/Stockfish/pull/5123 Bench: 2109005 --- src/search.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 9929ec27ed2..ca36c570b91 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1117,10 +1117,6 @@ Value Search::Worker::search( if (PvNode) r--; - // Increase reduction on repetition (~1 Elo) - if (move == (ss - 4)->currentMove && pos.has_repeated()) - r += 2; - // Increase reduction if next ply has a lot of fail high (~5 Elo) if ((ss + 1)->cutoffCnt > 3) r++; From 7e427639ce2868cb31f36c9b1de3071f61a63618 Mon Sep 17 00:00:00 2001 From: Disservin Date: Wed, 20 Mar 2024 16:36:10 +0100 Subject: [PATCH 1415/1766] Add cmath header to movepick.h No functional change --- src/movepick.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/movepick.h b/src/movepick.h index a16fdcd273e..b81f76e18f2 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include From d99f89506bd0ed535fb1c55dbb7cc8f7c29444d4 Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Sun, 17 Mar 2024 11:20:41 +0800 Subject: [PATCH 1416/1766] VVLTC search tune MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This set of parameters was derived from 3 tuning attempts: https://tests.stockfishchess.org/tests/view/65d19ab61d8e83c78bfd8436 (80+0.8 x8, ~40k games) Then tuned with one of linrock's early L1-3072 nets: https://tests.stockfishchess.org/tests/view/65def7b04b19edc854ebdec8 (VVLTC, ~36k games) Starting from the result of this tuning, the parameters were then tuned with the current master net: https://tests.stockfishchess.org/tests/view/65f11c420ec64f0526c46fc4 (VVLTC, ~45k games) Additionally, at the start of the third tuning phase, 2 parameters were manually changed: Notably, the triple extension margin was decreased from 78 to 22. This idea was given by Vizvezdenec: https://tests.stockfishchess.org/tests/view/65f0a2360ec64f0526c46752. The PvNode extension margin was also adjusted from 50 to 40. This tune also differs from previous tuning attempts by tuning the evaluation thresholds for smallnet and psqt-only. The former was increased through the tuning, and this is hypothesized to scale better at VVLTC, although there is not much evidence of it. Passed VVLTC 1st sprt: https://tests.stockfishchess.org/tests/view/65f6761d0ec64f0526c4be88 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 44688 W: 11421 L: 11140 D: 22127 Ptnml(0-2): 1, 4170, 13722, 4449, 2 Passed VVLTC 2nd sprt: https://tests.stockfishchess.org/tests/view/65fa31a30ec64f0526c4f611 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 27450 W: 7057 L: 6778 D: 13615 Ptnml(0-2): 4, 2545, 8346, 2828, 2 STC Elo estimate: https://tests.stockfishchess.org/tests/view/65fd3e540ec64f0526c521ae Elo: -7.84 ± 1.8 (95%) LOS: 0.0% Total: 40000 W: 9899 L: 10802 D: 19299 Ptnml(0-2): 203, 5221, 10025, 4378, 173 nElo: -14.91 ± 3.4 (95%) PairsRatio: 0.84 closes https://github.com/official-stockfish/Stockfish/pull/5130 Bench: 1876107 --- src/evaluate.h | 2 +- src/search.cpp | 74 +++++++++++++++++++++++++------------------------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/evaluate.h b/src/evaluate.h index 29dff40d05b..5f2b2c5422a 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -29,7 +29,7 @@ class Position; namespace Eval { -constexpr inline int SmallNetThreshold = 1050, PsqtOnlyThreshold = 2500; +constexpr inline int SmallNetThreshold = 1165, PsqtOnlyThreshold = 2500; // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the diff --git a/src/search.cpp b/src/search.cpp index ca36c570b91..f0e7f847f24 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -55,9 +55,9 @@ namespace { // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) { - Value futilityMult = 122 - 46 * noTtCutNode; - Value improvingDeduction = 57 * improving * futilityMult / 32; - Value worseningDeduction = (331 + 45 * improving) * oppWorsening * futilityMult / 1024; + Value futilityMult = 118 - 44 * noTtCutNode; + Value improvingDeduction = 53 * improving * futilityMult / 32; + Value worseningDeduction = (309 + 47 * improving) * oppWorsening * futilityMult / 1024; return futilityMult * d - improvingDeduction - worseningDeduction; } @@ -69,15 +69,15 @@ constexpr int futility_move_count(bool improving, Depth depth) { // Add correctionHistory value to raw staticEval and guarantee evaluation does not hit the tablebase range Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { auto cv = w.correctionHistory[pos.side_to_move()][pawn_structure_index(pos)]; - v += cv * std::abs(cv) / 11450; + v += cv * std::abs(cv) / 11175; return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } // History and stats update bonus, based on depth -int stat_bonus(Depth d) { return std::min(249 * d - 327, 1192); } +int stat_bonus(Depth d) { return std::min(223 * d - 332, 1258); } // History and stats update malus, based on depth -int stat_malus(Depth d) { return std::min(516 * d - 299, 1254); } +int stat_malus(Depth d) { return std::min(536 * d - 299, 1353); } // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(size_t nodes) { return VALUE_DRAW - 1 + Value(nodes & 0x2); } @@ -302,12 +302,12 @@ void Search::Worker::iterative_deepening() { // Reset aspiration window starting size Value avg = rootMoves[pvIdx].averageScore; - delta = 9 + avg * avg / 12800; + delta = 10 + avg * avg / 12493; alpha = std::max(avg - delta, -VALUE_INFINITE); beta = std::min(avg + delta, VALUE_INFINITE); // Adjust optimism based on root move's averageScore (~4 Elo) - optimism[us] = 130 * avg / (std::abs(avg) + 90); + optimism[us] = 132 * avg / (std::abs(avg) + 89); optimism[~us] = -optimism[us]; // Start with a small aspiration window and, in the case of a fail @@ -494,7 +494,7 @@ void Search::Worker::clear() { for (StatsType c : {NoCaptures, Captures}) for (auto& to : continuationHistory[inCheck][c]) for (auto& h : to) - h->fill(-71); + h->fill(-67); for (size_t i = 1; i < reductions.size(); ++i) reductions[i] = int((19.80 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); @@ -729,7 +729,7 @@ Value Search::Worker::search( // Use static evaluation difference to improve quiet move ordering (~9 Elo) if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture) { - int bonus = std::clamp(-14 * int((ss - 1)->staticEval + ss->staticEval), -1621, 1238); + int bonus = std::clamp(-13 * int((ss - 1)->staticEval + ss->staticEval), -1578, 1291); bonus = bonus > 0 ? 2 * bonus : bonus / 2; thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) @@ -752,7 +752,7 @@ Value Search::Worker::search( // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. // Adjust razor margin according to cutoffCnt. (~1 Elo) - if (eval < alpha - 462 - (296 - 145 * ((ss + 1)->cutoffCnt > 3)) * depth * depth) + if (eval < alpha - 488 - (289 - 142 * ((ss + 1)->cutoffCnt > 3)) * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -763,13 +763,13 @@ Value Search::Worker::search( // The depth condition is important for mate finding. if (!ss->ttPv && depth < 12 && eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening) - - (ss - 1)->statScore / 287 + - (ss - 1)->statScore / 267 >= beta && eval >= beta && eval < VALUE_TB_WIN_IN_MAX_PLY && (!ttMove || ttCapture)) return beta > VALUE_TB_LOSS_IN_MAX_PLY ? (eval + beta) / 2 : eval; // Step 9. Null move search with verification search (~35 Elo) - if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 16211 + if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 16878 && eval >= beta && eval >= ss->staticEval && ss->staticEval >= beta - 20 * depth + 314 && !excludedMove && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly && beta > VALUE_TB_LOSS_IN_MAX_PLY) @@ -777,7 +777,7 @@ Value Search::Worker::search( assert(eval - beta >= 0); // Null move dynamic reduction based on depth and eval - Depth R = std::min(int(eval - beta) / 151, 6) + depth / 3 + 4; + Depth R = std::min(int(eval - beta) / 144, 6) + depth / 3 + 4; ss->currentMove = Move::null(); ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -825,7 +825,7 @@ Value Search::Worker::search( // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search returns a value // much above beta, we can (almost) safely prune the previous move. - probCutBeta = beta + 168 - 64 * improving; + probCutBeta = beta + 170 - 64 * improving; if ( !PvNode && depth > 3 && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY @@ -881,7 +881,7 @@ Value Search::Worker::search( moves_loop: // When in check, search starts here // Step 12. A small Probcut idea, when we are in check (~4 Elo) - probCutBeta = beta + 410; + probCutBeta = beta + 409; if (ss->inCheck && !PvNode && ttCapture && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 4 && ttValue >= probCutBeta && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) @@ -964,7 +964,7 @@ Value Search::Worker::search( { Piece capturedPiece = pos.piece_on(move.to_sq()); int futilityEval = - ss->staticEval + 298 + 288 * lmrDepth + PieceValue[capturedPiece] + ss->staticEval + 297 + 284 * lmrDepth + PieceValue[capturedPiece] + thisThread->captureHistory[movedPiece][move.to_sq()][type_of(capturedPiece)] / 7; if (futilityEval < alpha) @@ -972,7 +972,7 @@ Value Search::Worker::search( } // SEE based pruning for captures and checks (~11 Elo) - if (!pos.see_ge(move, -202 * depth)) + if (!pos.see_ge(move, -203 * depth)) continue; } else @@ -984,24 +984,24 @@ Value Search::Worker::search( + thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()]; // Continuation history based pruning (~2 Elo) - if (lmrDepth < 6 && history < -4125 * depth) + if (lmrDepth < 6 && history < -4040 * depth) continue; history += 2 * thisThread->mainHistory[us][move.from_to()]; - lmrDepth += history / 5686; + lmrDepth += history / 5637; // Futility pruning: parent node (~13 Elo) if (!ss->inCheck && lmrDepth < 15 - && ss->staticEval + (bestValue < ss->staticEval - 55 ? 153 : 58) - + 118 * lmrDepth + && ss->staticEval + (bestValue < ss->staticEval - 59 ? 141 : 58) + + 125 * lmrDepth <= alpha) continue; lmrDepth = std::max(lmrDepth, 0); // Prune moves with negative SEE (~4 Elo) - if (!pos.see_ge(move, -26 * lmrDepth * lmrDepth)) + if (!pos.see_ge(move, -27 * lmrDepth * lmrDepth)) continue; } } @@ -1021,11 +1021,11 @@ Value Search::Worker::search( // so changing them requires tests at these types of time controls. // Recursive singular search is avoided. if (!rootNode && move == ttMove && !excludedMove - && depth >= 4 - (thisThread->completedDepth > 29) + ss->ttPv + && depth >= 4 - (thisThread->completedDepth > 30) + ss->ttPv && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - (58 + 55 * (ss->ttPv && !PvNode)) * depth / 64; + Value singularBeta = ttValue - (58 + 58 * (ss->ttPv && !PvNode)) * depth / 64; Depth singularDepth = newDepth / 2; ss->excludedMove = move; @@ -1040,11 +1040,11 @@ Value Search::Worker::search( // We make sure to limit the extensions in some way to avoid a search explosion if (!PvNode && ss->multipleExtensions <= 16) { - extension = 2 + (value < singularBeta - 78 && !ttCapture); + extension = 2 + (value < singularBeta - 22 && !ttCapture); depth += depth < 14; } if (PvNode && !ttCapture && ss->multipleExtensions <= 5 - && value < singularBeta - 50) + && value < singularBeta - 37) extension = 2; } @@ -1079,7 +1079,7 @@ Value Search::Worker::search( else if (PvNode && move == ttMove && move.to_sq() == prevSq && thisThread->captureHistory[movedPiece][move.to_sq()] [type_of(pos.piece_on(move.to_sq()))] - > 4315) + > 4026) extension = 1; } @@ -1129,10 +1129,10 @@ Value Search::Worker::search( ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + (*contHist[0])[movedPiece][move.to_sq()] + (*contHist[1])[movedPiece][move.to_sq()] - + (*contHist[3])[movedPiece][move.to_sq()] - 4587; + + (*contHist[3])[movedPiece][move.to_sq()] - 4723; // Decrease/increase reduction for moves with a good/bad history (~8 Elo) - r -= ss->statScore / 14956; + r -= ss->statScore / 13659; // Step 17. Late moves reduction / extension (LMR, ~117 Elo) if (depth >= 2 && moveCount > 1 + rootNode) @@ -1151,7 +1151,7 @@ Value Search::Worker::search( { // Adjust full-depth search based on LMR results - if the result // was good enough search deeper, if it was bad enough search shallower. - const bool doDeeperSearch = value > (bestValue + 48 + 2 * newDepth); // (~1 Elo) + const bool doDeeperSearch = value > (bestValue + 47 + 2 * newDepth); // (~1 Elo) const bool doShallowerSearch = value < bestValue + newDepth; // (~2 Elo) newDepth += doDeeperSearch - doShallowerSearch; @@ -1269,7 +1269,7 @@ Value Search::Worker::search( else { // Reduce other moves if we have found at least one score improvement (~2 Elo) - if (depth > 2 && depth < 12 && beta < 13665 && value > -12276) + if (depth > 2 && depth < 12 && beta < 14206 && value > -12077) depth -= 2; assert(depth > 0); @@ -1312,7 +1312,7 @@ Value Search::Worker::search( // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (depth > 5) + (PvNode || cutNode) + ((ss - 1)->statScore < -14446) + int bonus = (depth > 5) + (PvNode || cutNode) + ((ss - 1)->statScore < -14963) + ((ss - 1)->moveCount > 11) + (!ss->inCheck && bestValue <= ss->staticEval - 150); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, @@ -1472,7 +1472,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, if (bestValue > alpha) alpha = bestValue; - futilityBase = ss->staticEval + 221; + futilityBase = ss->staticEval + 226; } const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, @@ -1552,7 +1552,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, continue; // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, -79)) + if (!pos.see_ge(move, -78)) continue; } @@ -1620,7 +1620,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth Search::Worker::reduction(bool i, Depth d, int mn, int delta) { int reductionScale = reductions[d] * reductions[mn]; - return (reductionScale + 1091 - delta * 759 / rootDelta) / 1024 + (!i && reductionScale > 950); + return (reductionScale + 1107 - delta * 725 / rootDelta) / 1024 + (!i && reductionScale > 956); } namespace { @@ -1709,7 +1709,7 @@ void update_all_stats(const Position& pos, if (!pos.capture_stage(bestMove)) { - int bestMoveBonus = bestValue > beta + 167 ? quietMoveBonus // larger bonus + int bestMoveBonus = bestValue > beta + 173 ? quietMoveBonus // larger bonus : stat_bonus(depth); // smaller bonus // Increase stats for the best move in case it was a quiet move From 5001d49f42033bcf36a6c57401f891791031b4d8 Mon Sep 17 00:00:00 2001 From: mstembera Date: Thu, 21 Mar 2024 01:25:59 -0700 Subject: [PATCH 1417/1766] Update nnue_feature_transformer.h Unroll update_accumulator_refresh to process two active indices simultaneously. The compiler might not unroll effectively because the number of active indices isn't known at compile time. STC https://tests.stockfishchess.org/tests/view/65faa8850ec64f0526c4fca9 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 130464 W: 33882 L: 33431 D: 63151 Ptnml(0-2): 539, 14591, 34501, 15082, 519 closes https://github.com/official-stockfish/Stockfish/pull/5125 No functional change --- src/nnue/nnue_feature_transformer.h | 33 +++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index b42f160475f..888edebbdb3 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -619,8 +619,22 @@ class FeatureTransformer { for (IndexType k = 0; k < NumRegs; ++k) acc[k] = biasesTile[k]; - for (const auto index : active) + int i = 0; + for (; i < int(active.size()) - 1; i += 2) { + IndexType index0 = active[i]; + IndexType index1 = active[i + 1]; + const IndexType offset0 = HalfDimensions * index0 + j * TileHeight; + const IndexType offset1 = HalfDimensions * index1 + j * TileHeight; + auto column0 = reinterpret_cast(&weights[offset0]); + auto column1 = reinterpret_cast(&weights[offset1]); + + for (unsigned k = 0; k < NumRegs; ++k) + acc[k] = vec_add_16(acc[k], vec_add_16(column0[k], column1[k])); + } + for (; i < int(active.size()); ++i) + { + IndexType index = active[i]; const IndexType offset = HalfDimensions * index + j * TileHeight; auto column = reinterpret_cast(&weights[offset]); @@ -639,8 +653,23 @@ class FeatureTransformer { for (std::size_t k = 0; k < NumPsqtRegs; ++k) psqt[k] = vec_zero_psqt(); - for (const auto index : active) + int i = 0; + for (; i < int(active.size()) - 1; i += 2) + { + IndexType index0 = active[i]; + IndexType index1 = active[i + 1]; + const IndexType offset0 = PSQTBuckets * index0 + j * PsqtTileHeight; + const IndexType offset1 = PSQTBuckets * index1 + j * PsqtTileHeight; + auto columnPsqt0 = reinterpret_cast(&psqtWeights[offset0]); + auto columnPsqt1 = reinterpret_cast(&psqtWeights[offset1]); + + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + psqt[k] = + vec_add_psqt_32(psqt[k], vec_add_psqt_32(columnPsqt0[k], columnPsqt1[k])); + } + for (; i < int(active.size()); ++i) { + IndexType index = active[i]; const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); From 7998570414ba489b3dfe239b424c200041396e7f Mon Sep 17 00:00:00 2001 From: Disservin Date: Sat, 23 Mar 2024 10:34:32 +0100 Subject: [PATCH 1418/1766] Add functionality to export small net Usage ``` export_net ``` closes https://github.com/official-stockfish/Stockfish/pull/5133 No functional change --- src/uci.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/uci.cpp b/src/uci.cpp index cc03005ffc0..25884f577bd 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -159,11 +159,16 @@ void UCI::loop() { sync_cout << compiler_info() << sync_endl; else if (token == "export_net") { - std::optional filename; - std::string f; - if (is >> std::skipws >> f) - filename = f; - networks.big.save(filename); + std::pair, std::string> files[2]; + + if (is >> std::skipws >> files[0].second) + files[0].first = files[0].second; + + if (is >> std::skipws >> files[1].second) + files[1].first = files[1].second; + + networks.big.save(files[0].first); + networks.small.save(files[1].first); } else if (token == "--help" || token == "help" || token == "--license" || token == "license") sync_cout From d49b3738bc89353a9318d54af400f02c91d2d69a Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sun, 24 Mar 2024 14:02:27 +0300 Subject: [PATCH 1419/1766] Tweak the stats bonus and malus For depth 1 we don't have a negative score anymore. Passed STC: https://tests.stockfishchess.org/tests/view/65fb055c0ec64f0526c5024f LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 117120 W: 30468 L: 30023 D: 56629 Ptnml(0-2): 526, 13759, 29539, 14216, 520 Passed LTC: https://tests.stockfishchess.org/tests/view/65fdca4b0ec64f0526c5293f LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 54816 W: 13955 L: 13595 D: 27266 Ptnml(0-2): 30, 6046, 14897, 6404, 31 closes https://github.com/official-stockfish/Stockfish/pull/5134 Bench: 1876428 --- src/search.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index f0e7f847f24..e1508a7f06d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -74,10 +74,10 @@ Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { } // History and stats update bonus, based on depth -int stat_bonus(Depth d) { return std::min(223 * d - 332, 1258); } +int stat_bonus(Depth d) { return std::clamp(245 * d - 320, 0, 1296); } // History and stats update malus, based on depth -int stat_malus(Depth d) { return std::min(536 * d - 299, 1353); } +int stat_malus(Depth d) { return (d < 4 ? 554 * d - 303 : 1203); } // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(size_t nodes) { return VALUE_DRAW - 1 + Value(nodes & 0x2); } @@ -1709,7 +1709,7 @@ void update_all_stats(const Position& pos, if (!pos.capture_stage(bestMove)) { - int bestMoveBonus = bestValue > beta + 173 ? quietMoveBonus // larger bonus + int bestMoveBonus = bestValue > beta + 168 ? quietMoveBonus // larger bonus : stat_bonus(depth); // smaller bonus // Increase stats for the best move in case it was a quiet move From ed24e3a0a6e6958b26fefc4c43078c0bb46f5376 Mon Sep 17 00:00:00 2001 From: Gahtan Nahdi <155860115+gahtan-syarif@users.noreply.github.com> Date: Sat, 23 Mar 2024 03:48:44 +0700 Subject: [PATCH 1420/1766] Remove material imbalance from nnue eval Passed non-reg STC: https://tests.stockfishchess.org/tests/view/65fdf11f0ec64f0526c52b57 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 76480 W: 19893 L: 19712 D: 36875 Ptnml(0-2): 339, 9107, 19157, 9308, 329 Passed non-reg LTC: https://tests.stockfishchess.org/tests/view/65fee22e0ec64f0526c53885 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 150948 W: 38078 L: 37988 D: 74882 Ptnml(0-2): 111, 16997, 41148, 17127, 91 closes https://github.com/official-stockfish/Stockfish/pull/5135 Bench: 2103324 --- src/evaluate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 3d1643fbd54..bc705b857df 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -63,7 +63,7 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, const Position& pos, int shufflingDiv) { // Blend optimism and eval with nnue complexity and material imbalance optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / optDiv; - nnue -= nnue * (nnueComplexity + std::abs(simpleEval - nnue)) / nnueDiv; + nnue -= nnue * (nnueComplexity * 5 / 3) / nnueDiv; int npm = pos.non_pawn_material() / 64; v = (nnue * (npm + pawnCountConstant + pawnCountMul * pos.count()) From e636f73ab83da13fd352658e1eeecd369479c491 Mon Sep 17 00:00:00 2001 From: xoto10 <23479932+xoto10@users.noreply.github.com> Date: Wed, 27 Mar 2024 08:20:49 +0000 Subject: [PATCH 1421/1766] Vary time use with eval Adjust time use depending on the current eval. Passed STC : LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 101696 W: 26651 L: 26238 D: 48807 Ptnml(0-2): 400, 11602, 26459, 11959, 428 https://tests.stockfishchess.org/tests/live_elo/660187a50ec64f0526c557f6 Passed LTC : LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 60648 W: 15550 L: 15187 D: 29911 Ptnml(0-2): 40, 6356, 17171, 6715, 42 https://tests.stockfishchess.org/tests/live_elo/660298ed0ec64f0526c566d0 Values were found using two tunes with the final values taken from the ltc tune after 62k games : stc - https://tests.stockfishchess.org/tests/view/65fb526b0ec64f0526c50694 ltc - https://tests.stockfishchess.org/tests/view/65fd36e60ec64f0526c5214b Ideas for future work; * tune these values with the other TM adjustments * try narrower bands * calculate adjustment for exact eval by interpolation closes https://github.com/official-stockfish/Stockfish/pull/5138 No functional change --- src/search.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index e1508a7f06d..f045bc47962 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -53,6 +53,9 @@ using namespace Search; namespace { +static constexpr double EvalLevel[10] = {1.043, 1.017, 0.952, 1.009, 0.971, + 1.002, 0.992, 0.947, 1.046, 1.001}; + // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) { Value futilityMult = 118 - 44 * noTtCutNode; @@ -438,9 +441,10 @@ void Search::Worker::iterative_deepening() { timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.495 : 0.687; double reduction = (1.48 + mainThread->previousTimeReduction) / (2.17 * timeReduction); double bestMoveInstability = 1 + 1.88 * totBestMoveChanges / threads.size(); + int el = std::clamp((bestValue + 750) / 150, 0, 9); - double totalTime = - mainThread->tm.optimum() * fallingEval * reduction * bestMoveInstability; + double totalTime = mainThread->tm.optimum() * fallingEval * reduction + * bestMoveInstability * EvalLevel[el]; // Cap used time in case of a single legal move for a better viewer experience if (rootMoves.size() == 1) From 0ef5d05102ce0632d7b076706d26eb4eba7fcb70 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Fri, 29 Mar 2024 00:17:37 +0300 Subject: [PATCH 1422/1766] Adjust best value after a pruned quiet move Logic somewhat similar to how we adjust best value after pruned captures in qsearch, but in search this patch does it after pruned quiet moves and also to not full scale of futility value but to smth in between best value and futility value. Passed STC: https://tests.stockfishchess.org/tests/view/6601cf900ec64f0526c55c30 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 59936 W: 15722 L: 15369 D: 28845 Ptnml(0-2): 182, 7097, 15112, 7340, 237 Passed LTC: https://tests.stockfishchess.org/tests/view/66029b2d0ec64f0526c566f1 LLR: 2.96 (-2.94,2.94) <0.50,2.50> Total: 118362 W: 29953 L: 29460 D: 58949 Ptnml(0-2): 68, 13159, 32249, 13622, 83 closes https://github.com/official-stockfish/Stockfish/pull/5141 bench 1772608 --- src/search.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index f045bc47962..dfb27285104 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -995,12 +995,17 @@ Value Search::Worker::search( lmrDepth += history / 5637; + Value futilityValue = + ss->staticEval + (bestValue < ss->staticEval - 59 ? 141 : 58) + 125 * lmrDepth; + // Futility pruning: parent node (~13 Elo) - if (!ss->inCheck && lmrDepth < 15 - && ss->staticEval + (bestValue < ss->staticEval - 59 ? 141 : 58) - + 125 * lmrDepth - <= alpha) + if (!ss->inCheck && lmrDepth < 15 && futilityValue <= alpha) + { + if (bestValue <= futilityValue && abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY + && futilityValue < VALUE_TB_WIN_IN_MAX_PLY) + bestValue = (bestValue + futilityValue * 3) / 4; continue; + } lmrDepth = std::max(lmrDepth, 0); From e13e4cfb8340cdb26a00679681a0f163c6b4f0a9 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Wed, 27 Mar 2024 02:58:59 -0700 Subject: [PATCH 1423/1766] Simplify NMP Condition Remove eval >= ss->staticEval condition for Null Move Pruning. Passed non-regression STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 44000 W: 11420 L: 11202 D: 21378 Ptnml(0-2): 174, 5243, 10978, 5401, 204 https://tests.stockfishchess.org/tests/live_elo/6603ee490ec64f0526c57984 Passed non-regression LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 82956 W: 20978 L: 20818 D: 41160 Ptnml(0-2): 54, 9353, 22499, 9523, 49 https://tests.stockfishchess.org/tests/live_elo/660464b50ec64f0526c5804d closes https://github.com/official-stockfish/Stockfish/pull/5142 Bench 1759189 --- AUTHORS | 1 + src/search.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index 40a38bd5d89..abae401c1ef 100644 --- a/AUTHORS +++ b/AUTHORS @@ -204,6 +204,7 @@ sf-x Shahin M. Shahin (peregrine) Shane Booth (shane31) Shawn Varghese (xXH4CKST3RXx) +Shawn Xu (xu-shawn) Siad Daboul (Topologist) Stefan Geschwentner (locutus2) Stefano Cardanobile (Stefano80) diff --git a/src/search.cpp b/src/search.cpp index dfb27285104..fb5a2f003c7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -774,8 +774,8 @@ Value Search::Worker::search( // Step 9. Null move search with verification search (~35 Elo) if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 16878 - && eval >= beta && eval >= ss->staticEval && ss->staticEval >= beta - 20 * depth + 314 - && !excludedMove && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly + && eval >= beta && ss->staticEval >= beta - 20 * depth + 314 && !excludedMove + && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly && beta > VALUE_TB_LOSS_IN_MAX_PLY) { assert(eval - beta >= 0); From 68d58d94da15d175269eaa06b3d224062cf72a70 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 29 Mar 2024 10:25:41 +0100 Subject: [PATCH 1424/1766] Fix usage of abs vs std::abs close https://github.com/official-stockfish/Stockfish/pull/5143 No functional change --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index fb5a2f003c7..3f882aabdf5 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1001,7 +1001,7 @@ Value Search::Worker::search( // Futility pruning: parent node (~13 Elo) if (!ss->inCheck && lmrDepth < 15 && futilityValue <= alpha) { - if (bestValue <= futilityValue && abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY + if (bestValue <= futilityValue && std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY && futilityValue < VALUE_TB_WIN_IN_MAX_PLY) bestValue = (bestValue + futilityValue * 3) / 4; continue; From ec598b380db41fa54100cf3de51fe4be17eaf08b Mon Sep 17 00:00:00 2001 From: Disservin Date: Fri, 29 Mar 2024 10:49:53 +0100 Subject: [PATCH 1425/1766] Improve prerelease creation workflow In the last couple of months we sometimes saw duplicated prereleases uploaded to GitHub, possibly due to some racy behavior when concurrent jobs create a prerelease. This now creates an empty prerelease at the beginning of the CI and the binaries are later just attached to this one. closes https://github.com/official-stockfish/Stockfish/pull/5144 No functional change --- .github/workflows/stockfish.yml | 38 ++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index 22cd9af3fd2..13d57f9ed9b 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -16,6 +16,8 @@ jobs: if: github.repository == 'official-stockfish/Stockfish' && (github.ref == 'refs/heads/master' || (startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag')) runs-on: ubuntu-latest steps: + - uses: actions/checkout@v4 + # returns null if no pre-release exists - name: Get Commit SHA of Latest Pre-release run: | @@ -23,14 +25,40 @@ jobs: sudo apt-get update sudo apt-get install -y curl jq - echo "COMMIT_SHA=$(jq -r 'map(select(.prerelease)) | first | .tag_name' <<< $(curl -s https://api.github.com/repos/${{ github.repository_owner }}/Stockfish/releases))" >> $GITHUB_ENV + echo "COMMIT_SHA_TAG=$(jq -r 'map(select(.prerelease)) | first | .tag_name' <<< $(curl -s https://api.github.com/repos/${{ github.repository_owner }}/Stockfish/releases))" >> $GITHUB_ENV - # delete old previous pre-release and tag - - uses: actions/checkout@v4 - - run: gh release delete ${{ env.COMMIT_SHA }} --cleanup-tag - if: env.COMMIT_SHA != 'null' + # delete old previous pre-release and tag + - run: gh release delete ${{ env.COMMIT_SHA_TAG }} --cleanup-tag + if: env.COMMIT_SHA_TAG != 'null' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # Make sure that an old ci that still runs on master doesn't recreate a prerelease + - name: Check Pullable Commits + id: check_commits + run: | + git fetch + CHANGES=$(git rev-list HEAD..origin/master --count) + echo "CHANGES=$CHANGES" >> $GITHUB_ENV + + - name: Get last commit SHA + id: last_commit + run: echo "COMMIT_SHA=$(git rev-parse HEAD | cut -c 1-8)" >> $GITHUB_ENV + + - name: Get commit date + id: commit_date + run: echo "COMMIT_DATE=$(git show -s --date=format:'%Y%m%d' --format=%cd HEAD)" >> $GITHUB_ENV + + # Create a new pre-release, the other upload_binaries.yml will upload the binaries + # to this pre-release. + - name: Create Prerelease + if: github.ref_name == 'master' && env.CHANGES == '0' + uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981 + with: + name: Stockfish dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} + tag_name: stockfish-dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} + prerelease: true + Matrix: runs-on: ubuntu-latest outputs: From c964942da225ace51e1446deb29e7f43bf21360e Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 30 Mar 2024 10:54:36 +0100 Subject: [PATCH 1426/1766] Avoid a note related to an ABI change current master triggers a gcc note: parameter passing for argument of type 'std::pair' when C++17 is enabled changed to match C++14 in GCC 10.1 while this is inconsequential, and just informative https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111516 we can easily avoid it. closes https://github.com/official-stockfish/Stockfish/pull/5145 No functional change --- src/uci.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/uci.cpp b/src/uci.cpp index 25884f577bd..ee95d5be5e6 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -344,7 +344,13 @@ void UCI::position(Position& pos, std::istringstream& is, StateListPtr& states) } namespace { -std::pair win_rate_params(const Position& pos) { + +struct WinRateParams { + double a; + double b; +}; + +WinRateParams win_rate_params(const Position& pos) { int material = pos.count() + 3 * pos.count() + 3 * pos.count() + 5 * pos.count() + 9 * pos.count(); From 0716b845fdef8a20102b07eaec074b8da8162523 Mon Sep 17 00:00:00 2001 From: Viren6 <94880762+Viren6@users.noreply.github.com> Date: Tue, 2 Apr 2024 04:38:54 +0100 Subject: [PATCH 1427/1766] Update NNUE architecture to SFNNv9 and net nn-ae6a388e4a1a.nnue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Part 1: PyTorch Training, linrock Trained with a 10-stage sequence from scratch, starting in May 2023: https://github.com/linrock/nnue-tools/blob/master/exp-sequences/3072-10stage-SFNNv9.yml While the training methods were similar to the L1-2560 training sequence, the last two stages introduced min-v2 binpacks, where bestmove capture and in-check position scores were not zeroed during minimization, for compatibility with skipping SEE >= 0 positions and future research. Training data can be found at: https://robotmoon.com/nnue-training-data This net was tested at epoch 679 of the 10th training stage: https://tests.stockfishchess.org/tests/view/65f32e460ec64f0526c48dbc Part 2: SPSA Training, Viren6 The net was then SPSA tuned. This consisted of the output weights (32 * 8) and biases (8) as well as the L3 biases (32 * 8) and L2 biases (16 * 8), totalling 648 params in total. The SPSA tune can be found here: https://tests.stockfishchess.org/tests/view/65fc33ba0ec64f0526c512e3 With the help of Disservin , the initial weights were extracted with: https://github.com/Viren6/Stockfish/tree/new228 The net was saved with the tuned weights using: https://github.com/Viren6/Stockfish/tree/new241 Earlier nets of the SPSA failed STC compared to the base 3072 net of part 1: https://tests.stockfishchess.org/tests/view/65ff356e0ec64f0526c53c98 Therefore it is suspected that the SPSA at VVLTC has added extra scaling on top of the scaling of increasing the L1 size. Passed VVLTC 1: https://tests.stockfishchess.org/tests/view/6604a9020ec64f0526c583da LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 53042 W: 13554 L: 13256 D: 26232 Ptnml(0-2): 12, 5147, 15903, 5449, 10 Passed VVLTC 2: https://tests.stockfishchess.org/tests/view/660ad1b60ec64f0526c5dd23 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 17506 W: 4574 L: 4315 D: 8617 Ptnml(0-2): 1, 1567, 5362, 1818, 5 STC Elo estimate: https://tests.stockfishchess.org/tests/view/660b834d01aaec5069f87cb0 Elo: -7.66 ± 3.8 (95%) LOS: 0.0% Total: 9618 W: 2440 L: 2652 D: 4526 Ptnml(0-2): 80, 1281, 2261, 1145, 42 nElo: -13.94 ± 6.9 (95%) PairsRatio: 0.87 closes https://tests.stockfishchess.org/tests/view/660b834d01aaec5069f87cb0 bench 1823302 Co-Authored-By: Linmiao Xu --- src/evaluate.h | 2 +- src/nnue/nnue_architecture.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evaluate.h b/src/evaluate.h index 5f2b2c5422a..97ca4c4b665 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -35,7 +35,7 @@ constexpr inline int SmallNetThreshold = 1165, PsqtOnlyThreshold = 2500; // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro or the location where this macro is defined, as it is used // in the Makefile/Fishtest. -#define EvalFileDefaultNameBig "nn-1ceb1ade0001.nnue" +#define EvalFileDefaultNameBig "nn-ae6a388e4a1a.nnue" #define EvalFileDefaultNameSmall "nn-baff1ede1f90.nnue" namespace NNUE { diff --git a/src/nnue/nnue_architecture.h b/src/nnue/nnue_architecture.h index 05efb813754..7f73f87fd5e 100644 --- a/src/nnue/nnue_architecture.h +++ b/src/nnue/nnue_architecture.h @@ -38,7 +38,7 @@ namespace Stockfish::Eval::NNUE { using FeatureSet = Features::HalfKAv2_hm; // Number of input feature dimensions after conversion -constexpr IndexType TransformedFeatureDimensionsBig = 2560; +constexpr IndexType TransformedFeatureDimensionsBig = 3072; constexpr int L2Big = 15; constexpr int L3Big = 32; From 299707d2c2cbf1694bb21ed4a375b54ef35d719e Mon Sep 17 00:00:00 2001 From: Disservin Date: Sun, 17 Mar 2024 12:33:14 +0100 Subject: [PATCH 1428/1766] Split UCI into UCIEngine and Engine This is another refactor which aims to decouple uci from stockfish. A new engine class manages all engine related logic and uci is a "small" wrapper around it. In the future we should also try to remove the need for the Position object in the uci and replace the options with an actual options struct instead of using a map. Also convert the std::string's in the Info structs a string_view. closes #5147 No functional change --- src/Makefile | 4 +- src/engine.cpp | 153 +++++++++++++++++++++++++++++++++++++++++ src/engine.h | 84 ++++++++++++++++++++++ src/evaluate.cpp | 4 +- src/main.cpp | 5 +- src/misc.cpp | 31 +++++---- src/misc.h | 10 +-- src/nnue/nnue_misc.cpp | 4 +- src/perft.h | 2 +- src/position.cpp | 6 +- src/search.cpp | 16 +++-- src/tune.h | 2 +- src/uci.cpp | 146 ++++++++++++++------------------------- src/uci.h | 27 +++----- 14 files changed, 343 insertions(+), 151 deletions(-) create mode 100644 src/engine.cpp create mode 100644 src/engine.h diff --git a/src/Makefile b/src/Makefile index 672171bcd59..6315bda82df 100644 --- a/src/Makefile +++ b/src/Makefile @@ -55,7 +55,7 @@ PGOBENCH = $(WINE_PATH) ./$(EXE) bench SRCS = benchmark.cpp bitboard.cpp evaluate.cpp main.cpp \ misc.cpp movegen.cpp movepick.cpp position.cpp \ search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \ - nnue/nnue_misc.cpp nnue/features/half_ka_v2_hm.cpp nnue/network.cpp + nnue/nnue_misc.cpp nnue/features/half_ka_v2_hm.cpp nnue/network.cpp engine.cpp HEADERS = benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h \ nnue/nnue_misc.h nnue/features/half_ka_v2_hm.h nnue/layers/affine_transform.h \ @@ -63,7 +63,7 @@ HEADERS = benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h \ nnue/layers/sqr_clipped_relu.h nnue/nnue_accumulator.h nnue/nnue_architecture.h \ nnue/nnue_common.h nnue/nnue_feature_transformer.h position.h \ search.h syzygy/tbprobe.h thread.h thread_win32_osx.h timeman.h \ - tt.h tune.h types.h uci.h ucioption.h perft.h nnue/network.h + tt.h tune.h types.h uci.h ucioption.h perft.h nnue/network.h engine.h OBJS = $(notdir $(SRCS:.cpp=.o)) diff --git a/src/engine.cpp b/src/engine.cpp new file mode 100644 index 00000000000..79a2c604742 --- /dev/null +++ b/src/engine.cpp @@ -0,0 +1,153 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "engine.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "benchmark.h" +#include "evaluate.h" +#include "movegen.h" +#include "nnue/network.h" +#include "nnue/nnue_common.h" +#include "perft.h" +#include "position.h" +#include "search.h" +#include "syzygy/tbprobe.h" +#include "types.h" +#include "ucioption.h" + +namespace Stockfish { + +namespace NN = Eval::NNUE; + +constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; + +Engine::Engine(std::string path) : + binaryDirectory(CommandLine::get_binary_directory(path)), + states(new std::deque(1)), + networks(NN::Networks( + NN::NetworkBig({EvalFileDefaultNameBig, "None", ""}, NN::EmbeddedNNUEType::BIG), + NN::NetworkSmall({EvalFileDefaultNameSmall, "None", ""}, NN::EmbeddedNNUEType::SMALL))) { + Tune::init(options); + pos.set(StartFEN, false, &states->back()); +} + +void Engine::go(const Search::LimitsType& limits) { + verify_networks(); + + if (limits.perft) + { + perft(pos.fen(), limits.perft, options["UCI_Chess960"]); + return; + } + + threads.start_thinking(options, pos, states, limits); +} +void Engine::stop() { threads.stop = true; } + +void Engine::search_clear() { + wait_for_search_finished(); + + tt.clear(options["Threads"]); + threads.clear(); + + // @TODO wont work multiple instances + Tablebases::init(options["SyzygyPath"]); // Free mapped files +} + +void Engine::wait_for_search_finished() { threads.main_thread()->wait_for_search_finished(); } + +void Engine::set_position(const std::string& fen, const std::vector& moves) { + // Drop the old state and create a new one + states = StateListPtr(new std::deque(1)); + pos.set(fen, options["UCI_Chess960"], &states->back()); + + for (const auto& move : moves) + { + auto m = UCIEngine::to_move(pos, move); + + if (m == Move::none()) + break; + + states->emplace_back(); + pos.do_move(m, states->back()); + } +} + +// modifiers + +void Engine::resize_threads() { threads.set({options, threads, tt, networks}); } + +void Engine::set_tt_size(size_t mb) { + wait_for_search_finished(); + tt.resize(mb, options["Threads"]); +} + +void Engine::set_ponderhit(bool b) { threads.main_manager()->ponder = b; } + +// network related + +void Engine::verify_networks() { + networks.big.verify(options["EvalFile"]); + networks.small.verify(options["EvalFileSmall"]); +} + +void Engine::load_networks() { + networks.big.load(binaryDirectory, options["EvalFile"]); + networks.small.load(binaryDirectory, options["EvalFileSmall"]); +} + +void Engine::load_big_network(const std::string& file) { networks.big.load(binaryDirectory, file); } + +void Engine::load_small_network(const std::string& file) { + networks.small.load(binaryDirectory, file); +} + +void Engine::save_network(const std::pair, std::string> files[2]) { + networks.big.save(files[0].first); + networks.small.save(files[1].first); +} + +// utility functions + +OptionsMap& Engine::get_options() { return options; } + +uint64_t Engine::nodes_searched() const { return threads.nodes_searched(); } + +void Engine::trace_eval() { + StateListPtr trace_states(new std::deque(1)); + Position p; + p.set(pos.fen(), options["UCI_Chess960"], &trace_states->back()); + + verify_networks(); + + sync_cout << "\n" << Eval::trace(p, networks) << sync_endl; +} + +} \ No newline at end of file diff --git a/src/engine.h b/src/engine.h new file mode 100644 index 00000000000..6afc423de93 --- /dev/null +++ b/src/engine.h @@ -0,0 +1,84 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef ENGINE_H_INCLUDED +#define ENGINE_H_INCLUDED + +#include "misc.h" +#include "nnue/network.h" +#include "position.h" +#include "search.h" +#include "thread.h" +#include "tt.h" +#include "ucioption.h" + +namespace Stockfish { + +class Engine { + public: + Engine(std::string path = ""); + ~Engine() { wait_for_search_finished(); } + + // non blocking call to start searching + void go(const Search::LimitsType&); + // non blocking call to stop searching + void stop(); + + // blocking call to wait for search to finish + void wait_for_search_finished(); + // set a new position, moves are in UCI format + void set_position(const std::string& fen, const std::vector& moves); + + // modifiers + + void resize_threads(); + void set_tt_size(size_t mb); + void set_ponderhit(bool); + void search_clear(); + + // network related + + void verify_networks(); + void load_networks(); + void load_big_network(const std::string& file); + void load_small_network(const std::string& file); + void save_network(const std::pair, std::string> files[2]); + + // utility functions + + void trace_eval(); + // nodes since last search clear + uint64_t nodes_searched() const; + OptionsMap& get_options(); + + private: + const std::string binaryDirectory; + + Position pos; + StateListPtr states; + + OptionsMap options; + ThreadPool threads; + TranspositionTable tt; + Eval::NNUE::Networks networks; +}; + +} // namespace Stockfish + + +#endif // #ifndef ENGINE_H_INCLUDED \ No newline at end of file diff --git a/src/evaluate.cpp b/src/evaluate.cpp index bc705b857df..dcbfedb499a 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -105,11 +105,11 @@ std::string Eval::trace(Position& pos, const Eval::NNUE::Networks& networks) { Value v = networks.big.evaluate(pos, false); v = pos.side_to_move() == WHITE ? v : -v; - ss << "NNUE evaluation " << 0.01 * UCI::to_cp(v, pos) << " (white side)\n"; + ss << "NNUE evaluation " << 0.01 * UCIEngine::to_cp(v, pos) << " (white side)\n"; v = evaluate(networks, pos, VALUE_ZERO); v = pos.side_to_move() == WHITE ? v : -v; - ss << "Final evaluation " << 0.01 * UCI::to_cp(v, pos) << " (white side)"; + ss << "Final evaluation " << 0.01 * UCIEngine::to_cp(v, pos) << " (white side)"; ss << " [with scaled NNUE, ...]"; ss << "\n"; diff --git a/src/main.cpp b/src/main.cpp index 33d5d375fca..4e72c00398a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -34,10 +34,7 @@ int main(int argc, char* argv[]) { Bitboards::init(); Position::init(); - UCI uci(argc, argv); - - Tune::init(uci.options); - + UCIEngine uci(argc, argv); uci.loop(); return 0; diff --git a/src/misc.cpp b/src/misc.cpp index 270d25ad4bc..1abb81b14c2 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -723,13 +723,9 @@ void bind_this_thread(size_t idx) { #define GETCWD getcwd #endif -CommandLine::CommandLine(int _argc, char** _argv) : - argc(_argc), - argv(_argv) { - std::string pathSeparator; - // Extract the path+name of the executable binary - std::string argv0 = argv[0]; +std::string CommandLine::get_binary_directory(std::string argv0) { + std::string pathSeparator; #ifdef _WIN32 pathSeparator = "\\"; @@ -745,15 +741,11 @@ CommandLine::CommandLine(int _argc, char** _argv) : #endif // Extract the working directory - workingDirectory = ""; - char buff[40000]; - char* cwd = GETCWD(buff, 40000); - if (cwd) - workingDirectory = cwd; + auto workingDirectory = CommandLine::get_working_directory(); // Extract the binary directory path from argv0 - binaryDirectory = argv0; - size_t pos = binaryDirectory.find_last_of("\\/"); + auto binaryDirectory = argv0; + size_t pos = binaryDirectory.find_last_of("\\/"); if (pos == std::string::npos) binaryDirectory = "." + pathSeparator; else @@ -762,6 +754,19 @@ CommandLine::CommandLine(int _argc, char** _argv) : // Pattern replacement: "./" at the start of path is replaced by the working directory if (binaryDirectory.find("." + pathSeparator) == 0) binaryDirectory.replace(0, 1, workingDirectory); + + return binaryDirectory; } +std::string CommandLine::get_working_directory() { + std::string workingDirectory = ""; + char buff[40000]; + char* cwd = GETCWD(buff, 40000); + if (cwd) + workingDirectory = cwd; + + return workingDirectory; +} + + } // namespace Stockfish diff --git a/src/misc.h b/src/misc.h index de34ee111f7..d75b236ff71 100644 --- a/src/misc.h +++ b/src/misc.h @@ -206,13 +206,15 @@ void bind_this_thread(size_t idx); struct CommandLine { public: - CommandLine(int, char**); + CommandLine(int _argc, char** _argv) : + argc(_argc), + argv(_argv) {} + + static std::string get_binary_directory(std::string argv0); + static std::string get_working_directory(); int argc; char** argv; - - std::string binaryDirectory; // path of the executable directory - std::string workingDirectory; // path of the working directory }; namespace Utility { diff --git a/src/nnue/nnue_misc.cpp b/src/nnue/nnue_misc.cpp index 725d90d27d6..3fa6e1b6180 100644 --- a/src/nnue/nnue_misc.cpp +++ b/src/nnue/nnue_misc.cpp @@ -58,7 +58,7 @@ void format_cp_compact(Value v, char* buffer, const Position& pos) { buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' '); - int cp = std::abs(UCI::to_cp(v, pos)); + int cp = std::abs(UCIEngine::to_cp(v, pos)); if (cp >= 10000) { buffer[1] = '0' + cp / 10000; @@ -92,7 +92,7 @@ void format_cp_compact(Value v, char* buffer, const Position& pos) { // Converts a Value into pawns, always keeping two decimals void format_cp_aligned_dot(Value v, std::stringstream& stream, const Position& pos) { - const double pawns = std::abs(0.01 * UCI::to_cp(v, pos)); + const double pawns = std::abs(0.01 * UCIEngine::to_cp(v, pos)); stream << (v < 0 ? '-' : v > 0 ? '+' diff --git a/src/perft.h b/src/perft.h index 2edc3ad0a6e..2dbab828a18 100644 --- a/src/perft.h +++ b/src/perft.h @@ -51,7 +51,7 @@ uint64_t perft(Position& pos, Depth depth) { pos.undo_move(m); } if (Root) - sync_cout << UCI::move(m, pos.is_chess960()) << ": " << cnt << sync_endl; + sync_cout << UCIEngine::move(m, pos.is_chess960()) << ": " << cnt << sync_endl; } return nodes; } diff --git a/src/position.cpp b/src/position.cpp index 2263afe7669..fd1678959d5 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -78,7 +78,7 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) { << std::setw(16) << pos.key() << std::setfill(' ') << std::dec << "\nCheckers: "; for (Bitboard b = pos.checkers(); b;) - os << UCI::square(pop_lsb(b)) << " "; + os << UCIEngine::square(pop_lsb(b)) << " "; if (int(Tablebases::MaxCardinality) >= popcount(pos.pieces()) && !pos.can_castle(ANY_CASTLING)) { @@ -431,8 +431,8 @@ string Position::fen() const { if (!can_castle(ANY_CASTLING)) ss << '-'; - ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(ep_square()) + " ") << st->rule50 - << " " << 1 + (gamePly - (sideToMove == BLACK)) / 2; + ss << (ep_square() == SQ_NONE ? " - " : " " + UCIEngine::square(ep_square()) + " ") + << st->rule50 << " " << 1 + (gamePly - (sideToMove == BLACK)) / 2; return ss.str(); } diff --git a/src/search.cpp b/src/search.cpp index 3f882aabdf5..efc00750613 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -158,7 +158,7 @@ void Search::Worker::start_searching() { { rootMoves.emplace_back(Move::none()); sync_cout << "info depth 0 score " - << UCI::to_score(rootPos.checkers() ? -VALUE_MATE : VALUE_DRAW, rootPos) + << UCIEngine::to_score(rootPos.checkers() ? -VALUE_MATE : VALUE_DRAW, rootPos) << sync_endl; } else @@ -204,11 +204,13 @@ void Search::Worker::start_searching() { sync_cout << main_manager()->pv(*bestThread, threads, tt, bestThread->completedDepth) << sync_endl; - sync_cout << "bestmove " << UCI::move(bestThread->rootMoves[0].pv[0], rootPos.is_chess960()); + sync_cout << "bestmove " + << UCIEngine::move(bestThread->rootMoves[0].pv[0], rootPos.is_chess960()); if (bestThread->rootMoves[0].pv.size() > 1 || bestThread->rootMoves[0].extract_ponder_from_tt(tt, rootPos)) - std::cout << " ponder " << UCI::move(bestThread->rootMoves[0].pv[1], rootPos.is_chess960()); + std::cout << " ponder " + << UCIEngine::move(bestThread->rootMoves[0].pv[1], rootPos.is_chess960()); std::cout << sync_endl; } @@ -933,7 +935,7 @@ Value Search::Worker::search( if (rootNode && is_mainthread() && main_manager()->tm.elapsed(threads.nodes_searched()) > 3000) sync_cout << "info depth " << depth << " currmove " - << UCI::move(move, pos.is_chess960()) << " currmovenumber " + << UCIEngine::move(move, pos.is_chess960()) << " currmovenumber " << moveCount + thisThread->pvIdx << sync_endl; if (PvNode) (ss + 1)->pv = nullptr; @@ -1904,10 +1906,10 @@ std::string SearchManager::pv(const Search::Worker& worker, ss << "info" << " depth " << d << " seldepth " << rootMoves[i].selDepth << " multipv " << i + 1 - << " score " << UCI::to_score(v, pos); + << " score " << UCIEngine::to_score(v, pos); if (worker.options["UCI_ShowWDL"]) - ss << UCI::wdl(v, pos); + ss << UCIEngine::wdl(v, pos); if (i == pvIdx && !tb && updated) // tablebase- and previous-scores are exact ss << (rootMoves[i].scoreLowerbound @@ -1918,7 +1920,7 @@ std::string SearchManager::pv(const Search::Worker& worker, << " tbhits " << tbHits << " time " << time << " pv"; for (Move m : rootMoves[i].pv) - ss << " " << UCI::move(m, pos.is_chess960()); + ss << " " << UCIEngine::move(m, pos.is_chess960()); } return ss.str(); diff --git a/src/tune.h b/src/tune.h index b88c085fd4b..079614db28a 100644 --- a/src/tune.h +++ b/src/tune.h @@ -158,7 +158,7 @@ class Tune { for (auto& e : instance().list) e->init_option(); read_options(); - } // Deferred, due to UCI::Options access + } // Deferred, due to UCIEngine::Options access static void read_options() { for (auto& e : instance().list) e->read_option(); diff --git a/src/uci.cpp b/src/uci.cpp index ee95d5be5e6..ed23c00a49d 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -32,6 +32,7 @@ #include #include "benchmark.h" +#include "engine.h" #include "evaluate.h" #include "movegen.h" #include "nnue/network.h" @@ -49,27 +50,19 @@ constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048; -namespace NN = Eval::NNUE; - - -UCI::UCI(int argc, char** argv) : - networks(NN::Networks( - NN::NetworkBig({EvalFileDefaultNameBig, "None", ""}, NN::EmbeddedNNUEType::BIG), - NN::NetworkSmall({EvalFileDefaultNameSmall, "None", ""}, NN::EmbeddedNNUEType::SMALL))), +UCIEngine::UCIEngine(int argc, char** argv) : + engine(argv[0]), cli(argc, argv) { + auto& options = engine.get_options(); + options["Debug Log File"] << Option("", [](const Option& o) { start_logger(o); }); - options["Threads"] << Option(1, 1, 1024, [this](const Option&) { - threads.set({options, threads, tt, networks}); - }); + options["Threads"] << Option(1, 1, 1024, [this](const Option&) { engine.resize_threads(); }); - options["Hash"] << Option(16, 1, MaxHashMB, [this](const Option& o) { - threads.main_thread()->wait_for_search_finished(); - tt.resize(o, options["Threads"]); - }); + options["Hash"] << Option(16, 1, MaxHashMB, [this](const Option& o) { engine.set_tt_size(o); }); - options["Clear Hash"] << Option([this](const Option&) { search_clear(); }); + options["Clear Hash"] << Option([this](const Option&) { engine.search_clear(); }); options["Ponder"] << Option(false); options["MultiPV"] << Option(1, 1, MAX_MOVES); options["Skill Level"] << Option(20, 0, 20); @@ -83,22 +76,17 @@ UCI::UCI(int argc, char** argv) : options["SyzygyProbeDepth"] << Option(1, 1, 100); options["Syzygy50MoveRule"] << Option(true); options["SyzygyProbeLimit"] << Option(7, 0, 7); - options["EvalFile"] << Option(EvalFileDefaultNameBig, [this](const Option& o) { - networks.big.load(cli.binaryDirectory, o); - }); - options["EvalFileSmall"] << Option(EvalFileDefaultNameSmall, [this](const Option& o) { - networks.small.load(cli.binaryDirectory, o); - }); - - networks.big.load(cli.binaryDirectory, options["EvalFile"]); - networks.small.load(cli.binaryDirectory, options["EvalFileSmall"]); - - threads.set({options, threads, tt, networks}); - - search_clear(); // After threads are up + options["EvalFile"] << Option(EvalFileDefaultNameBig, + [this](const Option& o) { engine.load_big_network(o); }); + options["EvalFileSmall"] << Option(EvalFileDefaultNameSmall, + [this](const Option& o) { engine.load_small_network(o); }); + + engine.load_networks(); + engine.resize_threads(); + engine.search_clear(); // After threads are up } -void UCI::loop() { +void UCIEngine::loop() { Position pos; std::string token, cmd; @@ -121,27 +109,27 @@ void UCI::loop() { is >> std::skipws >> token; if (token == "quit" || token == "stop") - threads.stop = true; + engine.stop(); // The GUI sends 'ponderhit' to tell that the user has played the expected move. // So, 'ponderhit' is sent if pondering was done on the same move that the user // has played. The search should continue, but should also switch from pondering // to the normal search. else if (token == "ponderhit") - threads.main_manager()->ponder = false; // Switch to the normal search + engine.set_ponderhit(false); else if (token == "uci") sync_cout << "id name " << engine_info(true) << "\n" - << options << "\nuciok" << sync_endl; + << engine.get_options() << "\nuciok" << sync_endl; else if (token == "setoption") setoption(is); else if (token == "go") - go(pos, is, states); + go(pos, is); else if (token == "position") - position(pos, is, states); + position(is); else if (token == "ucinewgame") - search_clear(); + engine.search_clear(); else if (token == "isready") sync_cout << "readyok" << sync_endl; @@ -150,11 +138,11 @@ void UCI::loop() { else if (token == "flip") pos.flip(); else if (token == "bench") - bench(pos, is, states); + bench(pos, is); else if (token == "d") sync_cout << pos << sync_endl; else if (token == "eval") - trace_eval(pos); + engine.trace_eval(); else if (token == "compiler") sync_cout << compiler_info() << sync_endl; else if (token == "export_net") @@ -167,8 +155,7 @@ void UCI::loop() { if (is >> std::skipws >> files[1].second) files[1].first = files[1].second; - networks.big.save(files[0].first); - networks.small.save(files[1].first); + engine.save_network(files); } else if (token == "--help" || token == "help" || token == "--license" || token == "license") sync_cout @@ -186,7 +173,7 @@ void UCI::loop() { } while (token != "quit" && cli.argc == 1); // The command-line arguments are one-shot } -Search::LimitsType UCI::parse_limits(const Position& pos, std::istream& is) { +Search::LimitsType UCIEngine::parse_limits(const Position& pos, std::istream& is) { Search::LimitsType limits; std::string token; @@ -225,23 +212,13 @@ Search::LimitsType UCI::parse_limits(const Position& pos, std::istream& is) { return limits; } -void UCI::go(Position& pos, std::istringstream& is, StateListPtr& states) { +void UCIEngine::go(Position& pos, std::istringstream& is) { Search::LimitsType limits = parse_limits(pos, is); - - networks.big.verify(options["EvalFile"]); - networks.small.verify(options["EvalFileSmall"]); - - if (limits.perft) - { - perft(pos.fen(), limits.perft, options["UCI_Chess960"]); - return; - } - - threads.start_thinking(options, pos, states, limits); + engine.go(limits); } -void UCI::bench(Position& pos, std::istream& args, StateListPtr& states) { +void UCIEngine::bench(Position& pos, std::istream& args) { std::string token; uint64_t num, nodes = 0, cnt = 1; @@ -263,20 +240,20 @@ void UCI::bench(Position& pos, std::istream& args, StateListPtr& states) { << std::endl; if (token == "go") { - go(pos, is, states); - threads.main_thread()->wait_for_search_finished(); - nodes += threads.nodes_searched(); + go(pos, is); + engine.wait_for_search_finished(); + nodes += engine.nodes_searched(); } else - trace_eval(pos); + engine.trace_eval(); } else if (token == "setoption") setoption(is); else if (token == "position") - position(pos, is, states); + position(is); else if (token == "ucinewgame") { - search_clear(); // Search::clear() may take a while + engine.search_clear(); // search_clear may take a while elapsed = now(); } } @@ -290,33 +267,13 @@ void UCI::bench(Position& pos, std::istream& args, StateListPtr& states) { << "\nNodes/second : " << 1000 * nodes / elapsed << std::endl; } -void UCI::trace_eval(Position& pos) { - StateListPtr states(new std::deque(1)); - Position p; - p.set(pos.fen(), options["UCI_Chess960"], &states->back()); - - networks.big.verify(options["EvalFile"]); - networks.small.verify(options["EvalFileSmall"]); - - sync_cout << "\n" << Eval::trace(p, networks) << sync_endl; +void UCIEngine::setoption(std::istringstream& is) { + engine.wait_for_search_finished(); + engine.get_options().setoption(is); } -void UCI::search_clear() { - threads.main_thread()->wait_for_search_finished(); - - tt.clear(options["Threads"]); - threads.clear(); - Tablebases::init(options["SyzygyPath"]); // Free mapped files -} - -void UCI::setoption(std::istringstream& is) { - threads.main_thread()->wait_for_search_finished(); - options.setoption(is); -} - -void UCI::position(Position& pos, std::istringstream& is, StateListPtr& states) { - Move m; +void UCIEngine::position(std::istringstream& is) { std::string token, fen; is >> token; @@ -332,15 +289,14 @@ void UCI::position(Position& pos, std::istringstream& is, StateListPtr& states) else return; - states = StateListPtr(new std::deque(1)); // Drop the old state and create a new one - pos.set(fen, options["UCI_Chess960"], &states->back()); + std::vector moves; - // Parse the move list, if any - while (is >> token && (m = to_move(pos, token)) != Move::none()) + while (is >> token) { - states->emplace_back(); - pos.do_move(m, states->back()); + moves.push_back(token); } + + engine.set_position(fen, moves); } namespace { @@ -379,7 +335,7 @@ int win_rate_model(Value v, const Position& pos) { } } -std::string UCI::to_score(Value v, const Position& pos) { +std::string UCIEngine::to_score(Value v, const Position& pos) { assert(-VALUE_INFINITE < v && v < VALUE_INFINITE); std::stringstream ss; @@ -399,7 +355,7 @@ std::string UCI::to_score(Value v, const Position& pos) { // Turns a Value to an integer centipawn number, // without treatment of mate and similar special scores. -int UCI::to_cp(Value v, const Position& pos) { +int UCIEngine::to_cp(Value v, const Position& pos) { // In general, the score can be defined via the the WDL as // (log(1/L - 1) - log(1/W - 1)) / ((log(1/L - 1) + log(1/W - 1)) @@ -410,7 +366,7 @@ int UCI::to_cp(Value v, const Position& pos) { return std::round(100 * int(v) / a); } -std::string UCI::wdl(Value v, const Position& pos) { +std::string UCIEngine::wdl(Value v, const Position& pos) { std::stringstream ss; int wdl_w = win_rate_model(v, pos); @@ -421,11 +377,11 @@ std::string UCI::wdl(Value v, const Position& pos) { return ss.str(); } -std::string UCI::square(Square s) { +std::string UCIEngine::square(Square s) { return std::string{char('a' + file_of(s)), char('1' + rank_of(s))}; } -std::string UCI::move(Move m, bool chess960) { +std::string UCIEngine::move(Move m, bool chess960) { if (m == Move::none()) return "(none)"; @@ -447,7 +403,7 @@ std::string UCI::move(Move m, bool chess960) { } -Move UCI::to_move(const Position& pos, std::string& str) { +Move UCIEngine::to_move(const Position& pos, std::string str) { if (str.length() == 5) str[4] = char(tolower(str[4])); // The promotion piece character must be lowercased diff --git a/src/uci.h b/src/uci.h index 237928d9abc..c4e90b48d35 100644 --- a/src/uci.h +++ b/src/uci.h @@ -22,6 +22,7 @@ #include #include +#include "engine.h" #include "misc.h" #include "nnue/network.h" #include "position.h" @@ -36,9 +37,9 @@ class Move; enum Square : int; using Value = int; -class UCI { +class UCIEngine { public: - UCI(int argc, char** argv); + UCIEngine(int argc, char** argv); void loop(); @@ -47,25 +48,17 @@ class UCI { static std::string square(Square s); static std::string move(Move m, bool chess960); static std::string wdl(Value v, const Position& pos); - static Move to_move(const Position& pos, std::string& str); + static Move to_move(const Position& pos, std::string str); static Search::LimitsType parse_limits(const Position& pos, std::istream& is); - const std::string& working_directory() const { return cli.workingDirectory; } - - OptionsMap options; - Eval::NNUE::Networks networks; - private: - TranspositionTable tt; - ThreadPool threads; - CommandLine cli; - - void go(Position& pos, std::istringstream& is, StateListPtr& states); - void bench(Position& pos, std::istream& args, StateListPtr& states); - void position(Position& pos, std::istringstream& is, StateListPtr& states); - void trace_eval(Position& pos); - void search_clear(); + Engine engine; + CommandLine cli; + + void go(Position& pos, std::istringstream& is); + void bench(Position& pos, std::istream& args); + void position(std::istringstream& is); void setoption(std::istringstream& is); }; From 9032c6cbe74ccf7e8963755501e7e6cc473ae471 Mon Sep 17 00:00:00 2001 From: Disservin Date: Sat, 23 Mar 2024 10:22:20 +0100 Subject: [PATCH 1429/1766] Transform search output to engine callbacks Part 2 of the Split UCI into UCIEngine and Engine refactor. This creates function callbacks for search to use when an update should occur. The benching in uci.cpp for example does this to extract the total nodes searched. No functional change --- src/Makefile | 4 +- src/engine.cpp | 42 +++++++++++-------- src/engine.h | 26 +++++++++--- src/main.cpp | 5 ++- src/score.cpp | 48 +++++++++++++++++++++ src/score.h | 69 ++++++++++++++++++++++++++++++ src/search.cpp | 82 +++++++++++++++++++----------------- src/search.h | 55 +++++++++++++++++++++--- src/thread.cpp | 16 +++---- src/thread.h | 2 +- src/uci.cpp | 112 +++++++++++++++++++++++++++++++++++++++---------- src/uci.h | 17 +++++--- 12 files changed, 373 insertions(+), 105 deletions(-) create mode 100644 src/score.cpp create mode 100644 src/score.h diff --git a/src/Makefile b/src/Makefile index 6315bda82df..550f5404d14 100644 --- a/src/Makefile +++ b/src/Makefile @@ -55,7 +55,7 @@ PGOBENCH = $(WINE_PATH) ./$(EXE) bench SRCS = benchmark.cpp bitboard.cpp evaluate.cpp main.cpp \ misc.cpp movegen.cpp movepick.cpp position.cpp \ search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \ - nnue/nnue_misc.cpp nnue/features/half_ka_v2_hm.cpp nnue/network.cpp engine.cpp + nnue/nnue_misc.cpp nnue/features/half_ka_v2_hm.cpp nnue/network.cpp engine.cpp score.cpp HEADERS = benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h \ nnue/nnue_misc.h nnue/features/half_ka_v2_hm.h nnue/layers/affine_transform.h \ @@ -63,7 +63,7 @@ HEADERS = benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h \ nnue/layers/sqr_clipped_relu.h nnue/nnue_accumulator.h nnue/nnue_architecture.h \ nnue/nnue_common.h nnue/nnue_feature_transformer.h position.h \ search.h syzygy/tbprobe.h thread.h thread_win32_osx.h timeman.h \ - tt.h tune.h types.h uci.h ucioption.h perft.h nnue/network.h engine.h + tt.h tune.h types.h uci.h ucioption.h perft.h nnue/network.h engine.h score.h OBJS = $(notdir $(SRCS:.cpp=.o)) diff --git a/src/engine.cpp b/src/engine.cpp index 79a2c604742..12fa5c3fd02 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -18,21 +18,15 @@ #include "engine.h" -#include -#include -#include -#include -#include -#include #include #include -#include -#include +#include +#include +#include #include -#include "benchmark.h" #include "evaluate.h" -#include "movegen.h" +#include "misc.h" #include "nnue/network.h" #include "nnue/nnue_common.h" #include "perft.h" @@ -40,6 +34,7 @@ #include "search.h" #include "syzygy/tbprobe.h" #include "types.h" +#include "uci.h" #include "ucioption.h" namespace Stockfish { @@ -54,7 +49,6 @@ Engine::Engine(std::string path) : networks(NN::Networks( NN::NetworkBig({EvalFileDefaultNameBig, "None", ""}, NN::EmbeddedNNUEType::BIG), NN::NetworkSmall({EvalFileDefaultNameSmall, "None", ""}, NN::EmbeddedNNUEType::SMALL))) { - Tune::init(options); pos.set(StartFEN, false, &states->back()); } @@ -77,10 +71,26 @@ void Engine::search_clear() { tt.clear(options["Threads"]); threads.clear(); - // @TODO wont work multiple instances + // @TODO wont work with multiple instances Tablebases::init(options["SyzygyPath"]); // Free mapped files } +void Engine::set_on_update_no_moves(std::function&& f) { + updateContext.onUpdateNoMoves = std::move(f); +} + +void Engine::set_on_update_full(std::function&& f) { + updateContext.onUpdateFull = std::move(f); +} + +void Engine::set_on_iter(std::function&& f) { + updateContext.onIter = std::move(f); +} + +void Engine::set_on_bestmove(std::function&& f) { + updateContext.onBestmove = std::move(f); +} + void Engine::wait_for_search_finished() { threads.main_thread()->wait_for_search_finished(); } void Engine::set_position(const std::string& fen, const std::vector& moves) { @@ -102,7 +112,7 @@ void Engine::set_position(const std::string& fen, const std::vector // modifiers -void Engine::resize_threads() { threads.set({options, threads, tt, networks}); } +void Engine::resize_threads() { threads.set({options, threads, tt, networks}, updateContext); } void Engine::set_tt_size(size_t mb) { wait_for_search_finished(); @@ -113,7 +123,7 @@ void Engine::set_ponderhit(bool b) { threads.main_manager()->ponder = b; } // network related -void Engine::verify_networks() { +void Engine::verify_networks() const { networks.big.verify(options["EvalFile"]); networks.small.verify(options["EvalFileSmall"]); } @@ -138,9 +148,7 @@ void Engine::save_network(const std::pair, std::strin OptionsMap& Engine::get_options() { return options; } -uint64_t Engine::nodes_searched() const { return threads.nodes_searched(); } - -void Engine::trace_eval() { +void Engine::trace_eval() const { StateListPtr trace_states(new std::deque(1)); Position p; p.set(pos.fen(), options["UCI_Chess960"], &trace_states->back()); diff --git a/src/engine.h b/src/engine.h index 6afc423de93..f74209d9095 100644 --- a/src/engine.h +++ b/src/engine.h @@ -19,7 +19,14 @@ #ifndef ENGINE_H_INCLUDED #define ENGINE_H_INCLUDED -#include "misc.h" +#include +#include +#include +#include +#include +#include +#include + #include "nnue/network.h" #include "position.h" #include "search.h" @@ -31,6 +38,10 @@ namespace Stockfish { class Engine { public: + using InfoShort = Search::InfoShort; + using InfoFull = Search::InfoFull; + using InfoIter = Search::InfoIteration; + Engine(std::string path = ""); ~Engine() { wait_for_search_finished(); } @@ -51,9 +62,14 @@ class Engine { void set_ponderhit(bool); void search_clear(); + void set_on_update_no_moves(std::function&&); + void set_on_update_full(std::function&&); + void set_on_iter(std::function&&); + void set_on_bestmove(std::function&&); + // network related - void verify_networks(); + void verify_networks() const; void load_networks(); void load_big_network(const std::string& file); void load_small_network(const std::string& file); @@ -61,9 +77,7 @@ class Engine { // utility functions - void trace_eval(); - // nodes since last search clear - uint64_t nodes_searched() const; + void trace_eval() const; OptionsMap& get_options(); private: @@ -76,6 +90,8 @@ class Engine { ThreadPool threads; TranspositionTable tt; Eval::NNUE::Networks networks; + + Search::SearchManager::UpdateContext updateContext; }; } // namespace Stockfish diff --git a/src/main.cpp b/src/main.cpp index 4e72c00398a..a6a3d1c4e85 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,9 +21,9 @@ #include "bitboard.h" #include "misc.h" #include "position.h" -#include "tune.h" #include "types.h" #include "uci.h" +#include "tune.h" using namespace Stockfish; @@ -35,6 +35,9 @@ int main(int argc, char* argv[]) { Position::init(); UCIEngine uci(argc, argv); + + Tune::init(uci.engine_options()); + uci.loop(); return 0; diff --git a/src/score.cpp b/src/score.cpp new file mode 100644 index 00000000000..d1a8a6abe4d --- /dev/null +++ b/src/score.cpp @@ -0,0 +1,48 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "score.h" + +#include +#include +#include + +#include "uci.h" + +namespace Stockfish { + +Score::Score(Value v, const Position& pos) { + assert(-VALUE_INFINITE < v && v < VALUE_INFINITE); + + if (std::abs(v) < VALUE_TB_WIN_IN_MAX_PLY) + { + score = InternalUnits{UCIEngine::to_cp(v, pos)}; + } + else if (std::abs(v) <= VALUE_TB) + { + auto distance = VALUE_TB - std::abs(v); + score = (v > 0) ? TBWin{distance} : TBWin{-distance}; + } + else + { + auto distance = VALUE_MATE - std::abs(v); + score = (v > 0) ? Mate{distance} : Mate{-distance}; + } +} + +} \ No newline at end of file diff --git a/src/score.h b/src/score.h new file mode 100644 index 00000000000..b94d9f7fb6b --- /dev/null +++ b/src/score.h @@ -0,0 +1,69 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef SCORE_H_INCLUDED +#define SCORE_H_INCLUDED + +#include +#include + +#include "types.h" + +namespace Stockfish { + +class Position; + +class Score { + public: + struct Mate { + int plies; + }; + + struct TBWin { + int plies; + }; + + struct InternalUnits { + int value; + }; + + Score() = default; + Score(Value v, const Position& pos); + + template + bool is() const { + return std::holds_alternative(score); + } + + template + T get() const { + return std::get(score); + } + + template + decltype(auto) visit(F&& f) const { + return std::visit(std::forward(f), score); + } + + private: + std::variant score; +}; + +} + +#endif // #ifndef SCORE_H_INCLUDED diff --git a/src/search.cpp b/src/search.cpp index efc00750613..51cd1ae11dc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -26,8 +26,7 @@ #include #include #include -#include -#include +#include #include #include "evaluate.h" @@ -157,9 +156,8 @@ void Search::Worker::start_searching() { if (rootMoves.empty()) { rootMoves.emplace_back(Move::none()); - sync_cout << "info depth 0 score " - << UCIEngine::to_score(rootPos.checkers() ? -VALUE_MATE : VALUE_DRAW, rootPos) - << sync_endl; + main_manager()->updates.onUpdateNoMoves( + {0, {rootPos.checkers() ? -VALUE_MATE : VALUE_DRAW, rootPos}}); } else { @@ -201,18 +199,16 @@ void Search::Worker::start_searching() { // Send again PV info if we have a new best thread if (bestThread != this) - sync_cout << main_manager()->pv(*bestThread, threads, tt, bestThread->completedDepth) - << sync_endl; + main_manager()->pv(*bestThread, threads, tt, bestThread->completedDepth); - sync_cout << "bestmove " - << UCIEngine::move(bestThread->rootMoves[0].pv[0], rootPos.is_chess960()); + std::string ponder; if (bestThread->rootMoves[0].pv.size() > 1 || bestThread->rootMoves[0].extract_ponder_from_tt(tt, rootPos)) - std::cout << " ponder " - << UCIEngine::move(bestThread->rootMoves[0].pv[1], rootPos.is_chess960()); + ponder = UCIEngine::move(bestThread->rootMoves[0].pv[1], rootPos.is_chess960()); - std::cout << sync_endl; + auto bestmove = UCIEngine::move(bestThread->rootMoves[0].pv[0], rootPos.is_chess960()); + main_manager()->updates.onBestmove(bestmove, ponder); } // Main iterative deepening loop. It calls search() @@ -345,7 +341,7 @@ void Search::Worker::iterative_deepening() { // the UI) before a re-search. if (mainThread && multiPV == 1 && (bestValue <= alpha || bestValue >= beta) && mainThread->tm.elapsed(threads.nodes_searched()) > 3000) - sync_cout << main_manager()->pv(*this, threads, tt, rootDepth) << sync_endl; + main_manager()->pv(*this, threads, tt, rootDepth); // In case of failing low/high increase aspiration window and // re-search, otherwise exit the loop. @@ -382,7 +378,7 @@ void Search::Worker::iterative_deepening() { // had time to fully search other root-moves. Thus we suppress this output and // below pick a proven score/PV for this thread (from the previous iteration). && !(threads.abortedSearch && rootMoves[0].uciScore <= VALUE_TB_LOSS_IN_MAX_PLY)) - sync_cout << main_manager()->pv(*this, threads, tt, rootDepth) << sync_endl; + main_manager()->pv(*this, threads, tt, rootDepth); } if (!threads.stop) @@ -934,9 +930,10 @@ Value Search::Worker::search( if (rootNode && is_mainthread() && main_manager()->tm.elapsed(threads.nodes_searched()) > 3000) - sync_cout << "info depth " << depth << " currmove " - << UCIEngine::move(move, pos.is_chess960()) << " currmovenumber " - << moveCount + thisThread->pvIdx << sync_endl; + { + main_manager()->updates.onIter( + {depth, UCIEngine::move(move, pos.is_chess960()), moveCount + thisThread->pvIdx}); + } if (PvNode) (ss + 1)->pv = nullptr; @@ -1871,11 +1868,10 @@ void SearchManager::check_time(Search::Worker& worker) { worker.threads.stop = worker.threads.abortedSearch = true; } -std::string SearchManager::pv(const Search::Worker& worker, - const ThreadPool& threads, - const TranspositionTable& tt, - Depth depth) const { - std::stringstream ss; +void SearchManager::pv(const Search::Worker& worker, + const ThreadPool& threads, + const TranspositionTable& tt, + Depth depth) const { const auto nodes = threads.nodes_searched(); const auto& rootMoves = worker.rootMoves; @@ -1901,29 +1897,39 @@ std::string SearchManager::pv(const Search::Worker& worker, bool tb = worker.tbConfig.rootInTB && std::abs(v) <= VALUE_TB; v = tb ? rootMoves[i].tbScore : v; - if (ss.rdbuf()->in_avail()) // Not at first line - ss << "\n"; + std::string pv; + for (Move m : rootMoves[i].pv) + pv += UCIEngine::move(m, pos.is_chess960()) + " "; + + // remove last whitespace + if (!pv.empty()) + pv.pop_back(); - ss << "info" - << " depth " << d << " seldepth " << rootMoves[i].selDepth << " multipv " << i + 1 - << " score " << UCIEngine::to_score(v, pos); + auto wdl = worker.options["UCI_ShowWDL"] ? UCIEngine::wdl(v, pos) : ""; + auto bound = rootMoves[i].scoreLowerbound + ? "lowerbound" + : (rootMoves[i].scoreUpperbound ? "upperbound" : ""); - if (worker.options["UCI_ShowWDL"]) - ss << UCIEngine::wdl(v, pos); + InfoFull info; + + info.depth = d; + info.selDepth = rootMoves[i].selDepth; + info.multiPV = i + 1; + info.score = {v, pos}; + info.wdl = wdl; if (i == pvIdx && !tb && updated) // tablebase- and previous-scores are exact - ss << (rootMoves[i].scoreLowerbound - ? " lowerbound" - : (rootMoves[i].scoreUpperbound ? " upperbound" : "")); + info.bound = bound; - ss << " nodes " << nodes << " nps " << nodes * 1000 / time << " hashfull " << tt.hashfull() - << " tbhits " << tbHits << " time " << time << " pv"; + info.timeMs = time; + info.nodes = nodes; + info.nps = nodes * 1000 / time; + info.tbHits = tbHits; + info.pv = pv; + info.hashfull = tt.hashfull(); - for (Move m : rootMoves[i].pv) - ss << " " << UCIEngine::move(m, pos.is_chess960()); + updates.onUpdateFull(info); } - - return ss.str(); } // Called in case we have no ponder move before exiting the search, diff --git a/src/search.h b/src/search.h index 22f75ffd4d8..d1464840310 100644 --- a/src/search.h +++ b/src/search.h @@ -24,13 +24,15 @@ #include #include #include +#include #include -#include +#include #include #include "misc.h" #include "movepick.h" #include "position.h" +#include "score.h" #include "syzygy/tbprobe.h" #include "timeman.h" #include "types.h" @@ -139,7 +141,6 @@ struct SharedState { tt(transpositionTable), networks(nets) {} - const OptionsMap& options; ThreadPool& threads; TranspositionTable& tt; @@ -156,16 +157,56 @@ class ISearchManager { virtual void check_time(Search::Worker&) = 0; }; +struct InfoShort { + int depth; + Score score; +}; + +struct InfoFull: InfoShort { + int selDepth; + size_t multiPV; + std::string_view wdl; + std::string_view bound; + size_t timeMs; + size_t nodes; + size_t nps; + size_t tbHits; + std::string_view pv; + int hashfull; +}; + +struct InfoIteration { + int depth; + std::string_view currmove; + size_t currmovenumber; +}; + // SearchManager manages the search from the main thread. It is responsible for // keeping track of the time, and storing data strictly related to the main thread. class SearchManager: public ISearchManager { public: + using UpdateShort = std::function; + using UpdateFull = std::function; + using UpdateIter = std::function; + using UpdateBestmove = std::function; + + struct UpdateContext { + UpdateShort onUpdateNoMoves; + UpdateFull onUpdateFull; + UpdateIter onIter; + UpdateBestmove onBestmove; + }; + + + SearchManager(const UpdateContext& updateContext) : + updates(updateContext) {} + void check_time(Search::Worker& worker) override; - std::string pv(const Search::Worker& worker, - const ThreadPool& threads, - const TranspositionTable& tt, - Depth depth) const; + void pv(const Search::Worker& worker, + const ThreadPool& threads, + const TranspositionTable& tt, + Depth depth) const; Stockfish::TimeManagement tm; int callsCnt; @@ -178,6 +219,8 @@ class SearchManager: public ISearchManager { bool stopOnPonderhit; size_t id; + + const UpdateContext& updates; }; class NullSearchManager: public ISearchManager { diff --git a/src/thread.cpp b/src/thread.cpp index 90add4ad059..85a2bcbb167 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -119,7 +119,8 @@ uint64_t ThreadPool::tb_hits() const { return accumulate(&Search::Worker::tbHits // Creates/destroys threads to match the requested number. // Created and launched threads will immediately go to sleep in idle_loop. // Upon resizing, threads are recreated to allow for binding if necessary. -void ThreadPool::set(Search::SharedState sharedState) { +void ThreadPool::set(Search::SharedState sharedState, + const Search::SearchManager::UpdateContext& updateContext) { if (threads.size() > 0) // destroy any existing thread(s) { @@ -133,14 +134,15 @@ void ThreadPool::set(Search::SharedState sharedState) { if (requested > 0) // create new thread(s) { - threads.push_back(new Thread( - sharedState, std::unique_ptr(new Search::SearchManager()), 0)); - + auto manager = std::make_unique(updateContext); + threads.push_back(new Thread(sharedState, std::move(manager), 0)); while (threads.size() < requested) - threads.push_back(new Thread( - sharedState, std::unique_ptr(new Search::NullSearchManager()), - threads.size())); + { + auto null_manager = std::make_unique(); + threads.push_back(new Thread(sharedState, std::move(null_manager), threads.size())); + } + clear(); main_thread()->wait_for_search_finished(); diff --git a/src/thread.h b/src/thread.h index 81fcc72a7ee..223652aec99 100644 --- a/src/thread.h +++ b/src/thread.h @@ -82,7 +82,7 @@ class ThreadPool { void start_thinking(const OptionsMap&, Position&, StateListPtr&, Search::LimitsType); void clear(); - void set(Search::SharedState); + void set(Search::SharedState, const Search::SearchManager::UpdateContext&); Search::SearchManager* main_manager(); Thread* main_thread() const { return threads.front(); } diff --git a/src/uci.cpp b/src/uci.cpp index ed23c00a49d..d6936d38b23 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -19,15 +19,14 @@ #include "uci.h" #include -#include #include #include #include -#include #include #include #include #include +#include #include #include @@ -35,10 +34,8 @@ #include "engine.h" #include "evaluate.h" #include "movegen.h" -#include "nnue/network.h" -#include "nnue/nnue_common.h" -#include "perft.h" #include "position.h" +#include "score.h" #include "search.h" #include "syzygy/tbprobe.h" #include "types.h" @@ -49,6 +46,13 @@ namespace Stockfish { constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048; +template +struct overload: Ts... { + using Ts::operator()...; +}; + +template +overload(Ts...) -> overload; UCIEngine::UCIEngine(int argc, char** argv) : engine(argv[0]), @@ -81,6 +85,12 @@ UCIEngine::UCIEngine(int argc, char** argv) : options["EvalFileSmall"] << Option(EvalFileDefaultNameSmall, [this](const Option& o) { engine.load_small_network(o); }); + + engine.set_on_iter([](const auto& i) { on_iter(i); }); + engine.set_on_update_no_moves([](const auto& i) { on_update_no_moves(i); }); + engine.set_on_update_full([&](const auto& i) { on_update_full(i, options["UCI_ShowWDL"]); }); + engine.set_on_bestmove([](const auto& bm, const auto& p) { on_bestmove(bm, p); }); + engine.load_networks(); engine.resize_threads(); engine.search_clear(); // After threads are up @@ -221,6 +231,13 @@ void UCIEngine::go(Position& pos, std::istringstream& is) { void UCIEngine::bench(Position& pos, std::istream& args) { std::string token; uint64_t num, nodes = 0, cnt = 1; + uint64_t nodesSearched = 0; + const auto& options = engine.get_options(); + + engine.set_on_update_full([&](const auto& i) { + nodesSearched = i.nodes; + on_update_full(i, options["UCI_ShowWDL"]); + }); std::vector list = setup_bench(pos, args); @@ -242,7 +259,8 @@ void UCIEngine::bench(Position& pos, std::istream& args) { { go(pos, is); engine.wait_for_search_finished(); - nodes += engine.nodes_searched(); + nodes += nodesSearched; + nodesSearched = 0; } else engine.trace_eval(); @@ -265,6 +283,9 @@ void UCIEngine::bench(Position& pos, std::istream& args) { std::cerr << "\n===========================" << "\nTotal time (ms) : " << elapsed << "\nNodes searched : " << nodes << "\nNodes/second : " << 1000 * nodes / elapsed << std::endl; + + // reset callback, to not capture a dangling reference to nodesSearched + engine.set_on_update_full([&](const auto& i) { on_update_full(i, options["UCI_ShowWDL"]); }); } @@ -335,22 +356,22 @@ int win_rate_model(Value v, const Position& pos) { } } -std::string UCIEngine::to_score(Value v, const Position& pos) { - assert(-VALUE_INFINITE < v && v < VALUE_INFINITE); - - std::stringstream ss; - - if (std::abs(v) < VALUE_TB_WIN_IN_MAX_PLY) - ss << "cp " << to_cp(v, pos); - else if (std::abs(v) <= VALUE_TB) - { - const int ply = VALUE_TB - std::abs(v); // recompute ss->ply - ss << "cp " << (v > 0 ? 20000 - ply : -20000 + ply); - } - else - ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2; - - return ss.str(); +std::string UCIEngine::format_score(const Score& s) { + constexpr int TB_CP = 20000; + const auto format = + overload{[](Score::Mate mate) -> std::string { + auto m = (mate.plies > 0 ? (mate.plies + 1) : -mate.plies) / 2; + return std::string("mate ") + std::to_string(m); + }, + [](Score::TBWin tb) -> std::string { + return std::string("cp ") + + std::to_string((tb.plies > 0 ? TB_CP - tb.plies : -TB_CP + tb.plies)); + }, + [](Score::InternalUnits units) -> std::string { + return std::string("cp ") + std::to_string(units.value); + }}; + + return s.visit(format); } // Turns a Value to an integer centipawn number, @@ -414,4 +435,51 @@ Move UCIEngine::to_move(const Position& pos, std::string str) { return Move::none(); } +void UCIEngine::on_update_no_moves(const Engine::InfoShort& info) { + sync_cout << "info depth" << info.depth << " score " << format_score(info.score) << sync_endl; +} + +void UCIEngine::on_update_full(const Engine::InfoFull& info, bool showWDL) { + std::stringstream ss; + + ss << "info"; + ss << " depth " << info.depth // + << " seldepth " << info.selDepth // + << " multipv " << info.multiPV // + << " score " << format_score(info.score); // + + if (showWDL) + ss << " wdl " << info.wdl; + + if (!info.bound.empty()) + ss << " " << info.bound; + + ss << " nodes " << info.nodes // + << " nps " << info.nps // + << " hashfull " << info.hashfull // + << " tbhits " << info.tbHits // + << " time " << info.timeMs // + << " pv " << info.pv; // + + sync_cout << ss.str() << sync_endl; +} + +void UCIEngine::on_iter(const Engine::InfoIter& info) { + std::stringstream ss; + + ss << "info"; + ss << " depth " << info.depth // + << " currmove " << info.currmove // + << " currmovenumber " << info.currmovenumber; // + + sync_cout << ss.str() << sync_endl; +} + +void UCIEngine::on_bestmove(std::string_view bestmove, std::string_view ponder) { + sync_cout << "bestmove " << bestmove; + if (!ponder.empty()) + std::cout << " ponder " << ponder; + std::cout << sync_endl; +} + } // namespace Stockfish diff --git a/src/uci.h b/src/uci.h index c4e90b48d35..fa8c57fd912 100644 --- a/src/uci.h +++ b/src/uci.h @@ -21,19 +21,17 @@ #include #include +#include #include "engine.h" #include "misc.h" -#include "nnue/network.h" -#include "position.h" #include "search.h" -#include "thread.h" -#include "tt.h" -#include "ucioption.h" namespace Stockfish { +class Position; class Move; +class Score; enum Square : int; using Value = int; @@ -44,7 +42,7 @@ class UCIEngine { void loop(); static int to_cp(Value v, const Position& pos); - static std::string to_score(Value v, const Position& pos); + static std::string format_score(const Score& s); static std::string square(Square s); static std::string move(Move m, bool chess960); static std::string wdl(Value v, const Position& pos); @@ -52,6 +50,8 @@ class UCIEngine { static Search::LimitsType parse_limits(const Position& pos, std::istream& is); + auto& engine_options() { return engine.get_options(); } + private: Engine engine; CommandLine cli; @@ -60,6 +60,11 @@ class UCIEngine { void bench(Position& pos, std::istream& args); void position(std::istringstream& is); void setoption(std::istringstream& is); + + static void on_update_no_moves(const Engine::InfoShort& info); + static void on_update_full(const Engine::InfoFull& info, bool showWDL); + static void on_iter(const Engine::InfoIter& info); + static void on_bestmove(std::string_view bestmove, std::string_view ponder); }; } // namespace Stockfish From 1adf8e1ae662f2eecc5d652ece7b9f53014cf057 Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Sat, 6 Apr 2024 22:39:29 +0800 Subject: [PATCH 1430/1766] VVLTC search tune MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parameters were tuned in 3 stages: * Using an earlier L1-3072 net, and with triple extension margin manually set to 0: https://tests.stockfishchess.org/tests/view/65ffdf5d0ec64f0526c544f2 (~30k games) * Continue tuning, but with the previous master net (L1-2560). https://tests.stockfishchess.org/tests/view/660663f00ec64f0526c59c41 (~27k games) * Starting with the parameters from step 2, use the current L1-3072 net, and allow the triple extension margin to be tuned starting from 0: https://tests.stockfishchess.org/tests/view/660c16b8216a13d9498e7536 (40k games) Passed VVLTC 1st sprt: https://tests.stockfishchess.org/tests/view/66115eacbfeb43334bf7eddd LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 27138 W: 7045 L: 6789 D: 13304 Ptnml(0-2): 1, 2421, 8471, 2673, 3 Passed VVLTC 2nd sprt: https://tests.stockfishchess.org/tests/view/661483623eb00c8ccc0049c1 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 26242 W: 6807 L: 6535 D: 12900 Ptnml(0-2): 0, 2353, 8143, 2625, 0 STC Elo estimate: https://tests.stockfishchess.org/tests/view/66175ca55a4693796d96608c Elo: -10.53 ± 2.4 (95%) LOS: 0.0% Total: 21584 W: 5294 L: 5948 D: 10342 Ptnml(0-2): 102, 2937, 5363, 2293, 97 nElo: -19.99 ± 4.7 (95%) PairsRatio: 0.79 closes https://github.com/official-stockfish/Stockfish/pull/5162 Bench: 1381387 --- src/evaluate.h | 2 +- src/search.cpp | 76 +++++++++++++++++++++++++------------------------- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/evaluate.h b/src/evaluate.h index 97ca4c4b665..da9c7074ec1 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -29,7 +29,7 @@ class Position; namespace Eval { -constexpr inline int SmallNetThreshold = 1165, PsqtOnlyThreshold = 2500; +constexpr inline int SmallNetThreshold = 1274, PsqtOnlyThreshold = 2389; // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the diff --git a/src/search.cpp b/src/search.cpp index 51cd1ae11dc..a131c958e7b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -58,8 +58,8 @@ static constexpr double EvalLevel[10] = {1.043, 1.017, 0.952, 1.009, 0.971, // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) { Value futilityMult = 118 - 44 * noTtCutNode; - Value improvingDeduction = 53 * improving * futilityMult / 32; - Value worseningDeduction = (309 + 47 * improving) * oppWorsening * futilityMult / 1024; + Value improvingDeduction = 52 * improving * futilityMult / 32; + Value worseningDeduction = (310 + 48 * improving) * oppWorsening * futilityMult / 1024; return futilityMult * d - improvingDeduction - worseningDeduction; } @@ -71,15 +71,15 @@ constexpr int futility_move_count(bool improving, Depth depth) { // Add correctionHistory value to raw staticEval and guarantee evaluation does not hit the tablebase range Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { auto cv = w.correctionHistory[pos.side_to_move()][pawn_structure_index(pos)]; - v += cv * std::abs(cv) / 11175; + v += cv * std::abs(cv) / 9260; return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } // History and stats update bonus, based on depth -int stat_bonus(Depth d) { return std::clamp(245 * d - 320, 0, 1296); } +int stat_bonus(Depth d) { return std::clamp(211 * d - 315, 0, 1291); } // History and stats update malus, based on depth -int stat_malus(Depth d) { return (d < 4 ? 554 * d - 303 : 1203); } +int stat_malus(Depth d) { return (d < 4 ? 572 * d - 285 : 1372); } // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(size_t nodes) { return VALUE_DRAW - 1 + Value(nodes & 0x2); } @@ -303,12 +303,12 @@ void Search::Worker::iterative_deepening() { // Reset aspiration window starting size Value avg = rootMoves[pvIdx].averageScore; - delta = 10 + avg * avg / 12493; + delta = 11 + avg * avg / 11254; alpha = std::max(avg - delta, -VALUE_INFINITE); beta = std::min(avg + delta, VALUE_INFINITE); // Adjust optimism based on root move's averageScore (~4 Elo) - optimism[us] = 132 * avg / (std::abs(avg) + 89); + optimism[us] = 125 * avg / (std::abs(avg) + 91); optimism[~us] = -optimism[us]; // Start with a small aspiration window and, in the case of a fail @@ -496,10 +496,10 @@ void Search::Worker::clear() { for (StatsType c : {NoCaptures, Captures}) for (auto& to : continuationHistory[inCheck][c]) for (auto& h : to) - h->fill(-67); + h->fill(-65); for (size_t i = 1; i < reductions.size(); ++i) - reductions[i] = int((19.80 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); + reductions[i] = int((20.14 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); } @@ -731,7 +731,7 @@ Value Search::Worker::search( // Use static evaluation difference to improve quiet move ordering (~9 Elo) if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture) { - int bonus = std::clamp(-13 * int((ss - 1)->staticEval + ss->staticEval), -1578, 1291); + int bonus = std::clamp(-14 * int((ss - 1)->staticEval + ss->staticEval), -1644, 1384); bonus = bonus > 0 ? 2 * bonus : bonus / 2; thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) @@ -754,7 +754,7 @@ Value Search::Worker::search( // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. // Adjust razor margin according to cutoffCnt. (~1 Elo) - if (eval < alpha - 488 - (289 - 142 * ((ss + 1)->cutoffCnt > 3)) * depth * depth) + if (eval < alpha - 471 - (276 - 148 * ((ss + 1)->cutoffCnt > 3)) * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -765,21 +765,21 @@ Value Search::Worker::search( // The depth condition is important for mate finding. if (!ss->ttPv && depth < 12 && eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening) - - (ss - 1)->statScore / 267 + - (ss - 1)->statScore / 284 >= beta && eval >= beta && eval < VALUE_TB_WIN_IN_MAX_PLY && (!ttMove || ttCapture)) return beta > VALUE_TB_LOSS_IN_MAX_PLY ? (eval + beta) / 2 : eval; // Step 9. Null move search with verification search (~35 Elo) - if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 16878 - && eval >= beta && ss->staticEval >= beta - 20 * depth + 314 && !excludedMove + if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 18001 + && eval >= beta && ss->staticEval >= beta - 21 * depth + 315 && !excludedMove && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly && beta > VALUE_TB_LOSS_IN_MAX_PLY) { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and eval - Depth R = std::min(int(eval - beta) / 144, 6) + depth / 3 + 4; + Depth R = std::min(int(eval - beta) / 152, 6) + depth / 3 + 4; ss->currentMove = Move::null(); ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -827,7 +827,7 @@ Value Search::Worker::search( // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search returns a value // much above beta, we can (almost) safely prune the previous move. - probCutBeta = beta + 170 - 64 * improving; + probCutBeta = beta + 169 - 63 * improving; if ( !PvNode && depth > 3 && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY @@ -883,7 +883,7 @@ Value Search::Worker::search( moves_loop: // When in check, search starts here // Step 12. A small Probcut idea, when we are in check (~4 Elo) - probCutBeta = beta + 409; + probCutBeta = beta + 436; if (ss->inCheck && !PvNode && ttCapture && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 4 && ttValue >= probCutBeta && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) @@ -967,7 +967,7 @@ Value Search::Worker::search( { Piece capturedPiece = pos.piece_on(move.to_sq()); int futilityEval = - ss->staticEval + 297 + 284 * lmrDepth + PieceValue[capturedPiece] + ss->staticEval + 287 + 277 * lmrDepth + PieceValue[capturedPiece] + thisThread->captureHistory[movedPiece][move.to_sq()][type_of(capturedPiece)] / 7; if (futilityEval < alpha) @@ -975,7 +975,7 @@ Value Search::Worker::search( } // SEE based pruning for captures and checks (~11 Elo) - if (!pos.see_ge(move, -203 * depth)) + if (!pos.see_ge(move, -199 * depth)) continue; } else @@ -987,15 +987,15 @@ Value Search::Worker::search( + thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()]; // Continuation history based pruning (~2 Elo) - if (lmrDepth < 6 && history < -4040 * depth) + if (lmrDepth < 6 && history < -4173 * depth) continue; history += 2 * thisThread->mainHistory[us][move.from_to()]; - lmrDepth += history / 5637; + lmrDepth += history / 5285; Value futilityValue = - ss->staticEval + (bestValue < ss->staticEval - 59 ? 141 : 58) + 125 * lmrDepth; + ss->staticEval + (bestValue < ss->staticEval - 54 ? 128 : 58) + 131 * lmrDepth; // Futility pruning: parent node (~13 Elo) if (!ss->inCheck && lmrDepth < 15 && futilityValue <= alpha) @@ -1009,7 +1009,7 @@ Value Search::Worker::search( lmrDepth = std::max(lmrDepth, 0); // Prune moves with negative SEE (~4 Elo) - if (!pos.see_ge(move, -27 * lmrDepth * lmrDepth)) + if (!pos.see_ge(move, -26 * lmrDepth * lmrDepth)) continue; } } @@ -1029,11 +1029,11 @@ Value Search::Worker::search( // so changing them requires tests at these types of time controls. // Recursive singular search is avoided. if (!rootNode && move == ttMove && !excludedMove - && depth >= 4 - (thisThread->completedDepth > 30) + ss->ttPv + && depth >= 4 - (thisThread->completedDepth > 32) + ss->ttPv && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - (58 + 58 * (ss->ttPv && !PvNode)) * depth / 64; + Value singularBeta = ttValue - (64 + 59 * (ss->ttPv && !PvNode)) * depth / 64; Depth singularDepth = newDepth / 2; ss->excludedMove = move; @@ -1048,11 +1048,11 @@ Value Search::Worker::search( // We make sure to limit the extensions in some way to avoid a search explosion if (!PvNode && ss->multipleExtensions <= 16) { - extension = 2 + (value < singularBeta - 22 && !ttCapture); + extension = 2 + (value < singularBeta - 11 && !ttCapture); depth += depth < 14; } if (PvNode && !ttCapture && ss->multipleExtensions <= 5 - && value < singularBeta - 37) + && value < singularBeta - 38) extension = 2; } @@ -1087,7 +1087,7 @@ Value Search::Worker::search( else if (PvNode && move == ttMove && move.to_sq() == prevSq && thisThread->captureHistory[movedPiece][move.to_sq()] [type_of(pos.piece_on(move.to_sq()))] - > 4026) + > 3807) extension = 1; } @@ -1137,10 +1137,10 @@ Value Search::Worker::search( ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + (*contHist[0])[movedPiece][move.to_sq()] + (*contHist[1])[movedPiece][move.to_sq()] - + (*contHist[3])[movedPiece][move.to_sq()] - 4723; + + (*contHist[3])[movedPiece][move.to_sq()] - 5007; // Decrease/increase reduction for moves with a good/bad history (~8 Elo) - r -= ss->statScore / 13659; + r -= ss->statScore / 12901; // Step 17. Late moves reduction / extension (LMR, ~117 Elo) if (depth >= 2 && moveCount > 1 + rootNode) @@ -1159,7 +1159,7 @@ Value Search::Worker::search( { // Adjust full-depth search based on LMR results - if the result // was good enough search deeper, if it was bad enough search shallower. - const bool doDeeperSearch = value > (bestValue + 47 + 2 * newDepth); // (~1 Elo) + const bool doDeeperSearch = value > (bestValue + 42 + 2 * newDepth); // (~1 Elo) const bool doShallowerSearch = value < bestValue + newDepth; // (~2 Elo) newDepth += doDeeperSearch - doShallowerSearch; @@ -1277,7 +1277,7 @@ Value Search::Worker::search( else { // Reduce other moves if we have found at least one score improvement (~2 Elo) - if (depth > 2 && depth < 12 && beta < 14206 && value > -12077) + if (depth > 2 && depth < 12 && beta < 13132 && value > -13295) depth -= 2; assert(depth > 0); @@ -1320,9 +1320,9 @@ Value Search::Worker::search( // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (depth > 5) + (PvNode || cutNode) + ((ss - 1)->statScore < -14963) + int bonus = (depth > 5) + (PvNode || cutNode) + ((ss - 1)->statScore < -14761) + ((ss - 1)->moveCount > 11) - + (!ss->inCheck && bestValue <= ss->staticEval - 150); + + (!ss->inCheck && bestValue <= ss->staticEval - 144); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] @@ -1480,7 +1480,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, if (bestValue > alpha) alpha = bestValue; - futilityBase = ss->staticEval + 226; + futilityBase = ss->staticEval + 246; } const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, @@ -1560,7 +1560,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, continue; // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, -78)) + if (!pos.see_ge(move, -79)) continue; } @@ -1628,7 +1628,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth Search::Worker::reduction(bool i, Depth d, int mn, int delta) { int reductionScale = reductions[d] * reductions[mn]; - return (reductionScale + 1107 - delta * 725 / rootDelta) / 1024 + (!i && reductionScale > 956); + return (reductionScale + 1123 - delta * 832 / rootDelta) / 1024 + (!i && reductionScale > 1025); } namespace { @@ -1717,7 +1717,7 @@ void update_all_stats(const Position& pos, if (!pos.capture_stage(bestMove)) { - int bestMoveBonus = bestValue > beta + 168 ? quietMoveBonus // larger bonus + int bestMoveBonus = bestValue > beta + 185 ? quietMoveBonus // larger bonus : stat_bonus(depth); // smaller bonus // Increase stats for the best move in case it was a quiet move From 94484db6e83ad791b8782fd120f32db2ab72bf11 Mon Sep 17 00:00:00 2001 From: mstembera Date: Mon, 8 Apr 2024 13:07:41 -0700 Subject: [PATCH 1431/1766] Avoid permuting inputs during transform() Avoid permuting inputs during transform() and instead do it once at load time. Affects AVX2 and newer Intel architectures only. https://tests.stockfishchess.org/tests/view/661306613eb00c8ccc0033c7 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 108480 W: 28319 L: 27898 D: 52263 Ptnml(0-2): 436, 12259, 28438, 12662, 445 speedups measured such as e.g. ``` Result of 100 runs ================== base (./stockfish.master ) = 1241128 +/- 3757 test (./stockfish.patch ) = 1247713 +/- 3689 diff = +6585 +/- 2583 speedup = +0.0053 P(speedup > 0) = 1.0000 ``` closes https://github.com/official-stockfish/Stockfish/pull/5160 No functional change --- src/nnue/nnue_feature_transformer.h | 78 +++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 10 deletions(-) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 888edebbdb3..3101c8d2689 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -60,10 +60,9 @@ using psqt_vec_t = __m256i; #define vec_set_16(a) _mm512_set1_epi16(a) #define vec_max_16(a, b) _mm512_max_epi16(a, b) #define vec_min_16(a, b) _mm512_min_epi16(a, b) -inline vec_t vec_msb_pack_16(vec_t a, vec_t b) { - vec_t compacted = _mm512_packs_epi16(_mm512_srli_epi16(a, 7), _mm512_srli_epi16(b, 7)); - return _mm512_permutexvar_epi64(_mm512_setr_epi64(0, 2, 4, 6, 1, 3, 5, 7), compacted); -} + // Inverse permuted at load time + #define vec_msb_pack_16(a, b) \ + _mm512_packs_epi16(_mm512_srli_epi16(a, 7), _mm512_srli_epi16(b, 7)) #define vec_load_psqt(a) _mm256_load_si256(a) #define vec_store_psqt(a, b) _mm256_store_si256(a, b) #define vec_add_psqt_32(a, b) _mm256_add_epi32(a, b) @@ -84,10 +83,9 @@ using psqt_vec_t = __m256i; #define vec_set_16(a) _mm256_set1_epi16(a) #define vec_max_16(a, b) _mm256_max_epi16(a, b) #define vec_min_16(a, b) _mm256_min_epi16(a, b) -inline vec_t vec_msb_pack_16(vec_t a, vec_t b) { - vec_t compacted = _mm256_packs_epi16(_mm256_srli_epi16(a, 7), _mm256_srli_epi16(b, 7)); - return _mm256_permute4x64_epi64(compacted, 0b11011000); -} + // Inverse permuted at load time + #define vec_msb_pack_16(a, b) \ + _mm256_packs_epi16(_mm256_srli_epi16(a, 7), _mm256_srli_epi16(b, 7)) #define vec_load_psqt(a) _mm256_load_si256(a) #define vec_store_psqt(a, b) _mm256_store_si256(a, b) #define vec_add_psqt_32(a, b) _mm256_add_epi32(a, b) @@ -229,6 +227,62 @@ class FeatureTransformer { return FeatureSet::HashValue ^ (OutputDimensions * 2); } + static constexpr void order_packs([[maybe_unused]] uint64_t* v) { +#if defined(USE_AVX512) // _mm512_packs_epi16 ordering + uint64_t tmp0, tmp1; + tmp0 = v[2], tmp1 = v[3]; + v[2] = v[8], v[3] = v[9]; + v[8] = v[4], v[9] = v[5]; + v[4] = tmp0, v[5] = tmp1; + tmp0 = v[6], tmp1 = v[7]; + v[6] = v[10], v[7] = v[11]; + v[10] = v[12], v[11] = v[13]; + v[12] = tmp0, v[13] = tmp1; +#elif defined(USE_AVX2) // _mm256_packs_epi16 ordering + std::swap(v[2], v[4]); + std::swap(v[3], v[5]); +#endif + } + + static constexpr void inverse_order_packs([[maybe_unused]] uint64_t* v) { +#if defined(USE_AVX512) // Inverse _mm512_packs_epi16 ordering + uint64_t tmp0, tmp1; + tmp0 = v[2], tmp1 = v[3]; + v[2] = v[4], v[3] = v[5]; + v[4] = v[8], v[5] = v[9]; + v[8] = tmp0, v[9] = tmp1; + tmp0 = v[6], tmp1 = v[7]; + v[6] = v[12], v[7] = v[13]; + v[12] = v[10], v[13] = v[11]; + v[10] = tmp0, v[11] = tmp1; +#elif defined(USE_AVX2) // Inverse _mm256_packs_epi16 ordering + std::swap(v[2], v[4]); + std::swap(v[3], v[5]); +#endif + } + + void permute_weights([[maybe_unused]] void (*order_fn)(uint64_t*)) const { +#if defined(USE_AVX2) + #if defined(USE_AVX512) + constexpr IndexType di = 16; + #else + constexpr IndexType di = 8; + #endif + uint64_t* b = reinterpret_cast(const_cast(&biases[0])); + for (IndexType i = 0; i < HalfDimensions * sizeof(BiasType) / sizeof(uint64_t); i += di) + order_fn(&b[i]); + + for (IndexType j = 0; j < InputDimensions; ++j) + { + uint64_t* w = + reinterpret_cast(const_cast(&weights[j * HalfDimensions])); + for (IndexType i = 0; i < HalfDimensions * sizeof(WeightType) / sizeof(uint64_t); + i += di) + order_fn(&w[i]); + } +#endif + } + // Read network parameters bool read_parameters(std::istream& stream) { @@ -236,16 +290,20 @@ class FeatureTransformer { read_leb_128(stream, weights, HalfDimensions * InputDimensions); read_leb_128(stream, psqtWeights, PSQTBuckets * InputDimensions); + permute_weights(inverse_order_packs); return !stream.fail(); } // Write network parameters bool write_parameters(std::ostream& stream) const { + permute_weights(order_packs); + write_leb_128(stream, biases, HalfDimensions); write_leb_128(stream, weights, HalfDimensions * InputDimensions); write_leb_128(stream, psqtWeights, PSQTBuckets * InputDimensions); + permute_weights(inverse_order_packs); return !stream.fail(); } @@ -276,8 +334,8 @@ class FeatureTransformer { static_assert((HalfDimensions / 2) % OutputChunkSize == 0); constexpr IndexType NumOutputChunks = HalfDimensions / 2 / OutputChunkSize; - vec_t Zero = vec_zero(); - vec_t One = vec_set_16(127); + const vec_t Zero = vec_zero(); + const vec_t One = vec_set_16(127); const vec_t* in0 = reinterpret_cast(&(accumulation[perspectives[p]][0])); const vec_t* in1 = From de2244284b301c5bf15b248b5e3538aee92bb295 Mon Sep 17 00:00:00 2001 From: Disservin Date: Fri, 5 Apr 2024 11:34:11 +0200 Subject: [PATCH 1432/1766] Remove COMPILER from Makefile The same functionality is available by using COMPCXX and having another variable which does the same is just confusing. There was only one mention on Stockfish Wiki about this which has been changed to COMPCXX. closes https://github.com/official-stockfish/Stockfish/pull/5154 No functional change --- .github/workflows/arm_compilation.yml | 4 ++-- .github/workflows/compilation.yml | 6 +++--- .github/workflows/sanitizers.yml | 4 ++-- .github/workflows/tests.yml | 6 +++--- .github/workflows/upload_binaries.yml | 2 +- src/Makefile | 5 ----- 6 files changed, 11 insertions(+), 16 deletions(-) diff --git a/.github/workflows/arm_compilation.yml b/.github/workflows/arm_compilation.yml index ef141971d2b..3934ac2d636 100644 --- a/.github/workflows/arm_compilation.yml +++ b/.github/workflows/arm_compilation.yml @@ -10,7 +10,7 @@ jobs: name: ${{ matrix.config.name }} ${{ matrix.binaries }} runs-on: ${{ matrix.config.os }} env: - COMPILER: ${{ matrix.config.compiler }} + COMPCXX: ${{ matrix.config.compiler }} COMP: ${{ matrix.config.comp }} EMU: ${{ matrix.config.emu }} EXT: ${{ matrix.config.ext }} @@ -62,7 +62,7 @@ jobs: if [ $COMP == ndk ]; then export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH fi - $COMPILER -v + $COMPCXX -v - name: Test help target run: make help diff --git a/.github/workflows/compilation.yml b/.github/workflows/compilation.yml index 964b5f05edc..3524d5e9f2e 100644 --- a/.github/workflows/compilation.yml +++ b/.github/workflows/compilation.yml @@ -10,7 +10,7 @@ jobs: name: ${{ matrix.config.name }} ${{ matrix.binaries }} runs-on: ${{ matrix.config.os }} env: - COMPILER: ${{ matrix.config.compiler }} + COMPCXX: ${{ matrix.config.compiler }} COMP: ${{ matrix.config.comp }} EXT: ${{ matrix.config.ext }} NAME: ${{ matrix.config.simple_name }} @@ -50,7 +50,7 @@ jobs: run: make net - name: Check compiler - run: $COMPILER -v + run: $COMPCXX -v - name: Test help target run: make help @@ -59,7 +59,7 @@ jobs: run: git --version - name: Check compiler - run: $COMPILER -v + run: $COMPCXX -v - name: Show g++ cpu info if: runner.os != 'macOS' diff --git a/.github/workflows/sanitizers.yml b/.github/workflows/sanitizers.yml index 7ab1f997ad7..612f1275ce7 100644 --- a/.github/workflows/sanitizers.yml +++ b/.github/workflows/sanitizers.yml @@ -6,7 +6,7 @@ jobs: name: ${{ matrix.sanitizers.name }} runs-on: ${{ matrix.config.os }} env: - COMPILER: ${{ matrix.config.compiler }} + COMPCXX: ${{ matrix.config.compiler }} COMP: ${{ matrix.config.comp }} CXXFLAGS: "-Werror" strategy: @@ -47,7 +47,7 @@ jobs: run: make net - name: Check compiler - run: $COMPILER -v + run: $COMPCXX -v - name: Test help target run: make help diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 702e86e5f74..328c9cf94b1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -6,7 +6,7 @@ jobs: name: ${{ matrix.config.name }} runs-on: ${{ matrix.config.os }} env: - COMPILER: ${{ matrix.config.compiler }} + COMPCXX: ${{ matrix.config.compiler }} COMP: ${{ matrix.config.comp }} CXXFLAGS: "-Werror" strategy: @@ -172,9 +172,9 @@ jobs: if [ $COMP == ndk ]; then export PATH=${{ env.ANDROID_NDK_BIN }}:$PATH fi - $COMPILER -v + $COMPCXX -v else - echo "$COMPILER -v" > script.sh + echo "$COMPCXX -v" > script.sh docker run --rm --platform ${{ matrix.config.platform }} -v ${{ github.workspace }}/src:/app sf_builder fi diff --git a/.github/workflows/upload_binaries.yml b/.github/workflows/upload_binaries.yml index 015b514ce4b..acf91a8f331 100644 --- a/.github/workflows/upload_binaries.yml +++ b/.github/workflows/upload_binaries.yml @@ -11,7 +11,7 @@ jobs: name: ${{ matrix.config.name }} ${{ matrix.binaries }} runs-on: ${{ matrix.config.os }} env: - COMPILER: ${{ matrix.config.compiler }} + COMPCXX: ${{ matrix.config.compiler }} COMP: ${{ matrix.config.comp }} EXT: ${{ matrix.config.ext }} NAME: ${{ matrix.config.simple_name }} diff --git a/src/Makefile b/src/Makefile index 550f5404d14..45f38b01322 100644 --- a/src/Makefile +++ b/src/Makefile @@ -546,11 +546,6 @@ else endif endif -### Travis CI script uses COMPILER to overwrite CXX -ifdef COMPILER - COMPCXX=$(COMPILER) -endif - ### Allow overwriting CXX from command line ifdef COMPCXX CXX=$(COMPCXX) From d6bdcec52c33a67970721c4443136b42265a6148 Mon Sep 17 00:00:00 2001 From: gab8192 Date: Wed, 3 Apr 2024 23:41:24 +0200 Subject: [PATCH 1433/1766] Remove an useless assignment The assignment (ss + 1)->excludedMove = Move::none() can be simplified away because when that line is reached, (ss + 1)->excludedMove is always already none. The only moment stack[x]->excludedMove is modified, is during singular search, but it is reset to none right after the singular search is finished. closes https://github.com/official-stockfish/Stockfish/pull/5153 No functional change --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index a131c958e7b..3d84eb36017 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -585,7 +585,7 @@ Value Search::Worker::search( assert(0 <= ss->ply && ss->ply < MAX_PLY); - (ss + 1)->excludedMove = bestMove = Move::none(); + bestMove = Move::none(); (ss + 2)->killers[0] = (ss + 2)->killers[1] = Move::none(); (ss + 2)->cutoffCnt = 0; ss->multipleExtensions = (ss - 1)->multipleExtensions; From 249eec67152d334d76c0f981907a6f5787289443 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Thu, 11 Apr 2024 14:00:46 +0300 Subject: [PATCH 1434/1766] Simplify the depth-dependent part of the best value adjustment formula in main search Passed STC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 139648 W: 36171 L: 36061 D: 67416 Ptnml(0-2): 545, 16685, 35282, 16739, 573 https://tests.stockfishchess.org/tests/view/660d953b8ff4a059828d625d Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 222894 W: 56519 L: 56505 D: 109870 Ptnml(0-2): 112, 25145, 60971, 25055, 164 https://tests.stockfishchess.org/tests/view/660fd4afbfeb43334bf7d558 closes https://github.com/official-stockfish/Stockfish/pull/5164 bench: 1479416 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 3d84eb36017..24805aa70ea 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -585,7 +585,7 @@ Value Search::Worker::search( assert(0 <= ss->ply && ss->ply < MAX_PLY); - bestMove = Move::none(); + bestMove = Move::none(); (ss + 2)->killers[0] = (ss + 2)->killers[1] = Move::none(); (ss + 2)->cutoffCnt = 0; ss->multipleExtensions = (ss - 1)->multipleExtensions; @@ -1307,7 +1307,7 @@ Value Search::Worker::search( // Adjust best value for fail high cases at non-pv nodes if (!PvNode && bestValue >= beta && std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY && std::abs(alpha) < VALUE_TB_WIN_IN_MAX_PLY) - bestValue = (bestValue * (depth + 2) + beta) / (depth + 3); + bestValue = (bestValue * depth + beta) / (depth + 1); if (!moveCount) bestValue = excludedMove ? alpha : ss->inCheck ? mated_in(ss->ply) : VALUE_DRAW; From e58b3b4665469a793a0976d7a28f61fcd771b565 Mon Sep 17 00:00:00 2001 From: "Robert Nurnberg @ elitebook" Date: Fri, 12 Apr 2024 08:39:39 +0200 Subject: [PATCH 1435/1766] Fix wrong mate sign introduced yesterday by the UCI refactoring 9032c6cbe fixes #5166 closes https://github.com/official-stockfish/Stockfish/pull/5167 No functional change --- src/uci.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uci.cpp b/src/uci.cpp index d6936d38b23..a328ccb0868 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -360,12 +360,12 @@ std::string UCIEngine::format_score(const Score& s) { constexpr int TB_CP = 20000; const auto format = overload{[](Score::Mate mate) -> std::string { - auto m = (mate.plies > 0 ? (mate.plies + 1) : -mate.plies) / 2; + auto m = (mate.plies > 0 ? (mate.plies + 1) : mate.plies) / 2; return std::string("mate ") + std::to_string(m); }, [](Score::TBWin tb) -> std::string { return std::string("cp ") - + std::to_string((tb.plies > 0 ? TB_CP - tb.plies : -TB_CP + tb.plies)); + + std::to_string((tb.plies > 0 ? TB_CP - tb.plies : -TB_CP - tb.plies)); }, [](Score::InternalUnits units) -> std::string { return std::string("cp ") + std::to_string(units.value); From 14f6eab07d1d1e1a59372974e5534128676e9440 Mon Sep 17 00:00:00 2001 From: "Shahin M. Shahin" <41402573+peregrineshahin@users.noreply.github.com> Date: Fri, 12 Apr 2024 13:32:31 +0300 Subject: [PATCH 1436/1766] Fix some more UCI output further fall-out of the refactoring, fixes: * the position object in UCI is not never getting updated if position token is used * duplicate string of " wdl " See also: https://discord.com/channels/435943710472011776/1032922913499783169/1228227522945351690 https://discord.com/channels/435943710472011776/813919248455827515/1228288106449338398 closes https://github.com/official-stockfish/Stockfish/pull/5168 No functional change Co-Authored-By: disservin <45608332+disservin@users.noreply.github.com> --- src/uci.cpp | 9 +++++---- src/uci.h | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/uci.cpp b/src/uci.cpp index a328ccb0868..a15bc7d4b5c 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -137,7 +137,7 @@ void UCIEngine::loop() { else if (token == "go") go(pos, is); else if (token == "position") - position(is); + position(pos, is); else if (token == "ucinewgame") engine.search_clear(); else if (token == "isready") @@ -268,7 +268,7 @@ void UCIEngine::bench(Position& pos, std::istream& args) { else if (token == "setoption") setoption(is); else if (token == "position") - position(is); + position(pos, is); else if (token == "ucinewgame") { engine.search_clear(); // search_clear may take a while @@ -294,7 +294,7 @@ void UCIEngine::setoption(std::istringstream& is) { engine.get_options().setoption(is); } -void UCIEngine::position(std::istringstream& is) { +void UCIEngine::position(Position& pos, std::istringstream& is) { std::string token, fen; is >> token; @@ -317,6 +317,7 @@ void UCIEngine::position(std::istringstream& is) { moves.push_back(token); } + pos.set(fen, engine.get_options()["UCI_Chess960"], pos.state()); engine.set_position(fen, moves); } @@ -393,7 +394,7 @@ std::string UCIEngine::wdl(Value v, const Position& pos) { int wdl_w = win_rate_model(v, pos); int wdl_l = win_rate_model(-v, pos); int wdl_d = 1000 - wdl_w - wdl_l; - ss << " wdl " << wdl_w << " " << wdl_d << " " << wdl_l; + ss << wdl_w << " " << wdl_d << " " << wdl_l; return ss.str(); } diff --git a/src/uci.h b/src/uci.h index fa8c57fd912..fa359db4549 100644 --- a/src/uci.h +++ b/src/uci.h @@ -58,7 +58,7 @@ class UCIEngine { void go(Position& pos, std::istringstream& is); void bench(Position& pos, std::istream& args); - void position(std::istringstream& is); + void position(Position& pos, std::istringstream& is); void setoption(std::istringstream& is); static void on_update_no_moves(const Engine::InfoShort& info); From 4912f5b0b5f2656bc5fcdb0af480765ad5aa8932 Mon Sep 17 00:00:00 2001 From: Disservin Date: Fri, 12 Apr 2024 19:11:10 +0200 Subject: [PATCH 1437/1766] Remove duplicated Position object in UCIEngine Also fixes searchmoves. Drop the need of a Position object in uci.cpp. A side note, it is still required for the static functions, but these should be moved to a different namespace/class later on, since sf kinda relies on them. closes https://github.com/official-stockfish/Stockfish/pull/5169 No functional change --- src/benchmark.cpp | 6 ++---- src/benchmark.h | 4 +--- src/engine.cpp | 16 ++++++++++++++-- src/engine.h | 3 +++ src/search.h | 11 ++++++----- src/thread.cpp | 16 +++++++++++++--- src/uci.cpp | 48 +++++++++++++++++++++++------------------------ src/uci.h | 9 +++++---- 8 files changed, 67 insertions(+), 46 deletions(-) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 50f8612d912..267a6b4b2a9 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -23,8 +23,6 @@ #include #include -#include "position.h" - namespace { // clang-format off @@ -108,7 +106,7 @@ namespace Stockfish { // bench 64 1 100000 default nodes : search default positions for 100K nodes each // bench 64 4 5000 current movetime : search current position with 4 threads for 5 sec // bench 16 1 5 blah perft : run a perft 5 on positions in file "blah" -std::vector setup_bench(const Position& current, std::istream& is) { +std::vector setup_bench(const std::string& currentFen, std::istream& is) { std::vector fens, list; std::string go, token; @@ -126,7 +124,7 @@ std::vector setup_bench(const Position& current, std::istream& is) fens = Defaults; else if (fenFile == "current") - fens.push_back(current.fen()); + fens.push_back(currentFen); else { diff --git a/src/benchmark.h b/src/benchmark.h index 86f8a0ad50b..8905fcb1825 100644 --- a/src/benchmark.h +++ b/src/benchmark.h @@ -25,9 +25,7 @@ namespace Stockfish { -class Position; - -std::vector setup_bench(const Position&, std::istream&); +std::vector setup_bench(const std::string&, std::istream&); } // namespace Stockfish diff --git a/src/engine.cpp b/src/engine.cpp index 12fa5c3fd02..325b971ea46 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include "evaluate.h" #include "misc.h" @@ -146,8 +148,6 @@ void Engine::save_network(const std::pair, std::strin // utility functions -OptionsMap& Engine::get_options() { return options; } - void Engine::trace_eval() const { StateListPtr trace_states(new std::deque(1)); Position p; @@ -158,4 +158,16 @@ void Engine::trace_eval() const { sync_cout << "\n" << Eval::trace(p, networks) << sync_endl; } +OptionsMap& Engine::get_options() { return options; } + +std::string Engine::fen() const { return pos.fen(); } + +void Engine::flip() { pos.flip(); } + +std::string Engine::visualize() const { + std::stringstream ss; + ss << pos; + return ss.str(); +} + } \ No newline at end of file diff --git a/src/engine.h b/src/engine.h index f74209d9095..7122ee59d0b 100644 --- a/src/engine.h +++ b/src/engine.h @@ -79,6 +79,9 @@ class Engine { void trace_eval() const; OptionsMap& get_options(); + std::string fen() const; + void flip(); + std::string visualize() const; private: const std::string binaryDirectory; diff --git a/src/search.h b/src/search.h index d1464840310..d30a06feafa 100644 --- a/src/search.h +++ b/src/search.h @@ -28,6 +28,7 @@ #include #include #include +#include #include "misc.h" #include "movepick.h" @@ -121,11 +122,11 @@ struct LimitsType { bool use_time_management() const { return time[WHITE] || time[BLACK]; } - std::vector searchmoves; - TimePoint time[COLOR_NB], inc[COLOR_NB], npmsec, movetime, startTime; - int movestogo, depth, mate, perft, infinite; - uint64_t nodes; - bool ponderMode; + std::vector searchmoves; + TimePoint time[COLOR_NB], inc[COLOR_NB], npmsec, movetime, startTime; + int movestogo, depth, mate, perft, infinite; + uint64_t nodes; + bool ponderMode; }; diff --git a/src/thread.cpp b/src/thread.cpp index 85a2bcbb167..1438c9f9d5c 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "misc.h" #include "movegen.h" @@ -33,6 +34,7 @@ #include "tt.h" #include "types.h" #include "ucioption.h" +#include "uci.h" namespace Stockfish { @@ -182,10 +184,18 @@ void ThreadPool::start_thinking(const OptionsMap& options, increaseDepth = true; Search::RootMoves rootMoves; + const auto legalmoves = MoveList(pos); - for (const auto& m : MoveList(pos)) - if (limits.searchmoves.empty() - || std::count(limits.searchmoves.begin(), limits.searchmoves.end(), m)) + for (const auto& uciMove : limits.searchmoves) + { + auto move = UCIEngine::to_move(pos, uciMove); + + if (std::find(legalmoves.begin(), legalmoves.end(), move) != legalmoves.end()) + rootMoves.emplace_back(move); + } + + if (rootMoves.empty()) + for (const auto& m : legalmoves) rootMoves.emplace_back(m); Tablebases::Config tbConfig = Tablebases::rank_root_moves(options, pos, rootMoves); diff --git a/src/uci.cpp b/src/uci.cpp index a15bc7d4b5c..8f697836913 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -22,8 +22,6 @@ #include #include #include -#include -#include #include #include #include @@ -98,11 +96,7 @@ UCIEngine::UCIEngine(int argc, char** argv) : void UCIEngine::loop() { - Position pos; - std::string token, cmd; - StateListPtr states(new std::deque(1)); - - pos.set(StartFEN, false, &states->back()); + std::string token, cmd; for (int i = 1; i < cli.argc; ++i) cmd += std::string(cli.argv[i]) + " "; @@ -135,9 +129,9 @@ void UCIEngine::loop() { else if (token == "setoption") setoption(is); else if (token == "go") - go(pos, is); + go(is); else if (token == "position") - position(pos, is); + position(is); else if (token == "ucinewgame") engine.search_clear(); else if (token == "isready") @@ -146,11 +140,11 @@ void UCIEngine::loop() { // Add custom non-UCI commands, mainly for debugging purposes. // These commands must not be used during a search! else if (token == "flip") - pos.flip(); + engine.flip(); else if (token == "bench") - bench(pos, is); + bench(is); else if (token == "d") - sync_cout << pos << sync_endl; + sync_cout << engine.visualize() << sync_endl; else if (token == "eval") engine.trace_eval(); else if (token == "compiler") @@ -183,7 +177,7 @@ void UCIEngine::loop() { } while (token != "quit" && cli.argc == 1); // The command-line arguments are one-shot } -Search::LimitsType UCIEngine::parse_limits(const Position& pos, std::istream& is) { +Search::LimitsType UCIEngine::parse_limits(std::istream& is) { Search::LimitsType limits; std::string token; @@ -192,7 +186,7 @@ Search::LimitsType UCIEngine::parse_limits(const Position& pos, std::istream& is while (is >> token) if (token == "searchmoves") // Needs to be the last command on the line while (is >> token) - limits.searchmoves.push_back(to_move(pos, token)); + limits.searchmoves.push_back(to_lower(token)); else if (token == "wtime") is >> limits.time[WHITE]; @@ -222,13 +216,13 @@ Search::LimitsType UCIEngine::parse_limits(const Position& pos, std::istream& is return limits; } -void UCIEngine::go(Position& pos, std::istringstream& is) { +void UCIEngine::go(std::istringstream& is) { - Search::LimitsType limits = parse_limits(pos, is); + Search::LimitsType limits = parse_limits(is); engine.go(limits); } -void UCIEngine::bench(Position& pos, std::istream& args) { +void UCIEngine::bench(std::istream& args) { std::string token; uint64_t num, nodes = 0, cnt = 1; uint64_t nodesSearched = 0; @@ -239,7 +233,7 @@ void UCIEngine::bench(Position& pos, std::istream& args) { on_update_full(i, options["UCI_ShowWDL"]); }); - std::vector list = setup_bench(pos, args); + std::vector list = setup_bench(engine.fen(), args); num = count_if(list.begin(), list.end(), [](const std::string& s) { return s.find("go ") == 0 || s.find("eval") == 0; }); @@ -253,11 +247,11 @@ void UCIEngine::bench(Position& pos, std::istream& args) { if (token == "go" || token == "eval") { - std::cerr << "\nPosition: " << cnt++ << '/' << num << " (" << pos.fen() << ")" + std::cerr << "\nPosition: " << cnt++ << '/' << num << " (" << engine.fen() << ")" << std::endl; if (token == "go") { - go(pos, is); + go(is); engine.wait_for_search_finished(); nodes += nodesSearched; nodesSearched = 0; @@ -268,7 +262,7 @@ void UCIEngine::bench(Position& pos, std::istream& args) { else if (token == "setoption") setoption(is); else if (token == "position") - position(pos, is); + position(is); else if (token == "ucinewgame") { engine.search_clear(); // search_clear may take a while @@ -294,7 +288,7 @@ void UCIEngine::setoption(std::istringstream& is) { engine.get_options().setoption(is); } -void UCIEngine::position(Position& pos, std::istringstream& is) { +void UCIEngine::position(std::istringstream& is) { std::string token, fen; is >> token; @@ -317,7 +311,6 @@ void UCIEngine::position(Position& pos, std::istringstream& is) { moves.push_back(token); } - pos.set(fen, engine.get_options()["UCI_Chess960"], pos.state()); engine.set_position(fen, moves); } @@ -425,9 +418,14 @@ std::string UCIEngine::move(Move m, bool chess960) { } +std::string UCIEngine::to_lower(std::string str) { + std::transform(str.begin(), str.end(), str.begin(), [](auto c) { return std::tolower(c); }); + + return str; +} + Move UCIEngine::to_move(const Position& pos, std::string str) { - if (str.length() == 5) - str[4] = char(tolower(str[4])); // The promotion piece character must be lowercased + str = to_lower(str); for (const auto& m : MoveList(pos)) if (str == move(m, pos.is_chess960())) diff --git a/src/uci.h b/src/uci.h index fa359db4549..ee8c2814aee 100644 --- a/src/uci.h +++ b/src/uci.h @@ -46,9 +46,10 @@ class UCIEngine { static std::string square(Square s); static std::string move(Move m, bool chess960); static std::string wdl(Value v, const Position& pos); + static std::string to_lower(std::string str); static Move to_move(const Position& pos, std::string str); - static Search::LimitsType parse_limits(const Position& pos, std::istream& is); + static Search::LimitsType parse_limits(std::istream& is); auto& engine_options() { return engine.get_options(); } @@ -56,9 +57,9 @@ class UCIEngine { Engine engine; CommandLine cli; - void go(Position& pos, std::istringstream& is); - void bench(Position& pos, std::istream& args); - void position(Position& pos, std::istringstream& is); + void go(std::istringstream& is); + void bench(std::istream& args); + void position(std::istringstream& is); void setoption(std::istringstream& is); static void on_update_no_moves(const Engine::InfoShort& info); From c55ae376f62de80fd20822954aaa6c7cd23eb2fa Mon Sep 17 00:00:00 2001 From: Disservin Date: Sat, 13 Apr 2024 21:54:10 +0200 Subject: [PATCH 1438/1766] Fix wrong sign for 200 TB score Fix another case of 9032c6cbe74ccf7e8963755501e7e6cc473ae471 * TB values can have a distance of 0, mainly when we are in a tb position but haven't found mate. * Add a missing whitespace to UCIEngine::on_update_no_moves() Closes https://github.com/official-stockfish/Stockfish/pull/5172 No functional change --- src/score.cpp | 2 +- src/score.h | 7 ++++--- src/uci.cpp | 6 +++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/score.cpp b/src/score.cpp index d1a8a6abe4d..292f53406e2 100644 --- a/src/score.cpp +++ b/src/score.cpp @@ -36,7 +36,7 @@ Score::Score(Value v, const Position& pos) { else if (std::abs(v) <= VALUE_TB) { auto distance = VALUE_TB - std::abs(v); - score = (v > 0) ? TBWin{distance} : TBWin{-distance}; + score = (v > 0) ? Tablebase{distance, true} : Tablebase{-distance, false}; } else { diff --git a/src/score.h b/src/score.h index b94d9f7fb6b..2eb40f7e08e 100644 --- a/src/score.h +++ b/src/score.h @@ -34,8 +34,9 @@ class Score { int plies; }; - struct TBWin { - int plies; + struct Tablebase { + int plies; + bool win; }; struct InternalUnits { @@ -61,7 +62,7 @@ class Score { } private: - std::variant score; + std::variant score; }; } diff --git a/src/uci.cpp b/src/uci.cpp index 8f697836913..8e20207b79e 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -357,9 +357,9 @@ std::string UCIEngine::format_score(const Score& s) { auto m = (mate.plies > 0 ? (mate.plies + 1) : mate.plies) / 2; return std::string("mate ") + std::to_string(m); }, - [](Score::TBWin tb) -> std::string { + [](Score::Tablebase tb) -> std::string { return std::string("cp ") - + std::to_string((tb.plies > 0 ? TB_CP - tb.plies : -TB_CP - tb.plies)); + + std::to_string((tb.win ? TB_CP - tb.plies : -TB_CP - tb.plies)); }, [](Score::InternalUnits units) -> std::string { return std::string("cp ") + std::to_string(units.value); @@ -435,7 +435,7 @@ Move UCIEngine::to_move(const Position& pos, std::string str) { } void UCIEngine::on_update_no_moves(const Engine::InfoShort& info) { - sync_cout << "info depth" << info.depth << " score " << format_score(info.score) << sync_endl; + sync_cout << "info depth " << info.depth << " score " << format_score(info.score) << sync_endl; } void UCIEngine::on_update_full(const Engine::InfoFull& info, bool showWDL) { From 432995ad82119070afa0bf720eb65d800bcbf817 Mon Sep 17 00:00:00 2001 From: Disservin Date: Sun, 7 Apr 2024 14:26:23 +0200 Subject: [PATCH 1439/1766] Update outdated comments closes https://github.com/official-stockfish/Stockfish/pull/5158 No functional change --- src/position.cpp | 1 - src/search.cpp | 4 ++-- src/tt.cpp | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index fd1678959d5..78e62bda303 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -744,7 +744,6 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // Update board and piece lists remove_piece(capsq); - // Update material hash key and prefetch access to materialTable k ^= Zobrist::psq[captured][capsq]; st->materialKey ^= Zobrist::psq[captured][pieceCount[captured]]; diff --git a/src/search.cpp b/src/search.cpp index 24805aa70ea..0eb0f45e1f9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1083,7 +1083,7 @@ Value Search::Worker::search( extension = -1; } - // Recapture extensions (~0 Elo on STC, ~1 Elo on LTC) + // Extension for capturing the previous moved piece (~0 Elo on STC, ~1 Elo on LTC) else if (PvNode && move == ttMove && move.to_sq() == prevSq && thisThread->captureHistory[movedPiece][move.to_sq()] [type_of(pos.piece_on(move.to_sq()))] @@ -1147,7 +1147,7 @@ Value Search::Worker::search( { // In general we want to cap the LMR depth search at newDepth, but when // reduction is negative, we allow this move a limited search extension - // beyond the first move depth. This may lead to hidden multiple extensions. + // beyond the first move depth. // To prevent problems when the max value is less than the min value, // std::clamp has been replaced by a more robust implementation. Depth d = std::max(1, std::min(newDepth - r, newDepth + 1)); diff --git a/src/tt.cpp b/src/tt.cpp index 9d4d2eca47c..41ed4591f39 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -67,7 +67,7 @@ uint8_t TTEntry::relative_age(const uint8_t generation8) const { // Sets the size of the transposition table, -// measured in megabytes. Transposition table consists of a power of 2 number +// measured in megabytes. Transposition table consists // of clusters and each cluster consists of ClusterSize number of TTEntry. void TranspositionTable::resize(size_t mbSize, int threadCount) { aligned_large_pages_free(table); From d3fc1d835e5144cc98d6a7658fb8cfd9370792f1 Mon Sep 17 00:00:00 2001 From: Disservin Date: Wed, 10 Apr 2024 23:10:07 +0200 Subject: [PATCH 1440/1766] Refactor elapsed time checks in search Small improvement of the elapsed time usage in search, makes the code easier to read overall. Also Search::Worker::iterative_deepening() now only checks the elapsed time once, instead of 3 times in a row. Non Regression STC: https://tests.stockfishchess.org/tests/view/6617005d5a4693796d965c3c LLR: 2.97 (-2.94,2.94) <-1.75,0.25> Total: 61024 W: 16002 L: 15806 D: 29216 Ptnml(0-2): 243, 6874, 16102, 7030, 263 closes https://github.com/official-stockfish/Stockfish/pull/5163 No functional change --- src/search.cpp | 28 +++++++++++++++------------- src/search.h | 2 ++ src/timeman.cpp | 3 --- src/timeman.h | 6 ++++-- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 0eb0f45e1f9..00636865fe7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -340,7 +340,7 @@ void Search::Worker::iterative_deepening() { // When failing high/low give some update (without cluttering // the UI) before a re-search. if (mainThread && multiPV == 1 && (bestValue <= alpha || bestValue >= beta) - && mainThread->tm.elapsed(threads.nodes_searched()) > 3000) + && elapsed() > 3000) main_manager()->pv(*this, threads, tt, rootDepth); // In case of failing low/high increase aspiration window and @@ -371,8 +371,7 @@ void Search::Worker::iterative_deepening() { std::stable_sort(rootMoves.begin() + pvFirst, rootMoves.begin() + pvIdx + 1); if (mainThread - && (threads.stop || pvIdx + 1 == multiPV - || mainThread->tm.elapsed(threads.nodes_searched()) > 3000) + && (threads.stop || pvIdx + 1 == multiPV || elapsed() > 3000) // A thread that aborted search can have mated-in/TB-loss PV and score // that cannot be trusted, i.e. it can be delayed or refuted if we would have // had time to fully search other root-moves. Thus we suppress this output and @@ -448,13 +447,14 @@ void Search::Worker::iterative_deepening() { if (rootMoves.size() == 1) totalTime = std::min(500.0, totalTime); - if (completedDepth >= 10 && nodesEffort >= 97 - && mainThread->tm.elapsed(threads.nodes_searched()) > totalTime * 0.739 + auto elapsedTime = elapsed(); + + if (completedDepth >= 10 && nodesEffort >= 97 && elapsedTime > totalTime * 0.739 && !mainThread->ponder) threads.stop = true; // Stop the search if we have exceeded the totalTime - if (mainThread->tm.elapsed(threads.nodes_searched()) > totalTime) + if (elapsedTime > totalTime) { // If we are allowed to ponder do not stop the search now but // keep pondering until the GUI sends "ponderhit" or "stop". @@ -464,9 +464,7 @@ void Search::Worker::iterative_deepening() { threads.stop = true; } else - threads.increaseDepth = - mainThread->ponder - || mainThread->tm.elapsed(threads.nodes_searched()) <= totalTime * 0.506; + threads.increaseDepth = mainThread->ponder || elapsedTime <= totalTime * 0.506; } mainThread->iterValue[iterIdx] = bestValue; @@ -928,8 +926,7 @@ Value Search::Worker::search( ss->moveCount = ++moveCount; - if (rootNode && is_mainthread() - && main_manager()->tm.elapsed(threads.nodes_searched()) > 3000) + if (rootNode && is_mainthread() && elapsed() > 3000) { main_manager()->updates.onIter( {depth, UCIEngine::move(move, pos.is_chess960()), moveCount + thisThread->pvIdx}); @@ -1631,6 +1628,11 @@ Depth Search::Worker::reduction(bool i, Depth d, int mn, int delta) { return (reductionScale + 1123 - delta * 832 / rootDelta) / 1024 + (!i && reductionScale > 1025); } +TimePoint Search::Worker::elapsed() const { + return main_manager()->tm.elapsed([this]() { return threads.nodes_searched(); }); +} + + namespace { // Adjusts a mate or TB score from "plies to mate from the root" // to "plies to mate from the current position". Standard scores are unchanged. @@ -1845,7 +1847,7 @@ void SearchManager::check_time(Search::Worker& worker) { static TimePoint lastInfoTime = now(); - TimePoint elapsed = tm.elapsed(worker.threads.nodes_searched()); + TimePoint elapsed = tm.elapsed([&worker]() { return worker.threads.nodes_searched(); }); TimePoint tick = worker.limits.startTime + elapsed; if (tick - lastInfoTime >= 1000) @@ -1877,7 +1879,7 @@ void SearchManager::pv(const Search::Worker& worker, const auto& rootMoves = worker.rootMoves; const auto& pos = worker.rootPos; size_t pvIdx = worker.pvIdx; - TimePoint time = tm.elapsed(nodes) + 1; + TimePoint time = tm.elapsed([nodes]() { return nodes; }) + 1; size_t multiPV = std::min(size_t(worker.options["MultiPV"]), rootMoves.size()); uint64_t tbHits = threads.tb_hits() + (worker.tbConfig.rootInTB ? rootMoves.size() : 0); diff --git a/src/search.h b/src/search.h index d30a06feafa..3ceaf5ddd0b 100644 --- a/src/search.h +++ b/src/search.h @@ -275,6 +275,8 @@ class Worker { return static_cast(manager.get()); } + TimePoint elapsed() const; + LimitsType limits; size_t pvIdx, pvLast; diff --git a/src/timeman.cpp b/src/timeman.cpp index 229ff3e9d6b..c651745f02e 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -30,9 +30,6 @@ namespace Stockfish { TimePoint TimeManagement::optimum() const { return optimumTime; } TimePoint TimeManagement::maximum() const { return maximumTime; } -TimePoint TimeManagement::elapsed(size_t nodes) const { - return useNodesTime ? TimePoint(nodes) : now() - startTime; -} void TimeManagement::clear() { availableNodes = 0; // When in 'nodes as time' mode diff --git a/src/timeman.h b/src/timeman.h index b07712a25c2..35c3cfc0680 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -19,7 +19,6 @@ #ifndef TIMEMAN_H_INCLUDED #define TIMEMAN_H_INCLUDED -#include #include #include "misc.h" @@ -41,7 +40,10 @@ class TimeManagement { TimePoint optimum() const; TimePoint maximum() const; - TimePoint elapsed(std::size_t nodes) const; + template + TimePoint elapsed(FUNC nodes) const { + return useNodesTime ? TimePoint(nodes()) : now() - startTime; + } void clear(); void advance_nodes_time(std::int64_t nodes); From 9021a61807ae8f869ffd7ba55d1b4f0404379dca Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Fri, 12 Apr 2024 00:00:59 +0300 Subject: [PATCH 1441/1766] Trivial cleanup Make naming and declaration of futilityValue in search consistent between different places. closes https://github.com/official-stockfish/Stockfish/pull/5165 No functional change. --- src/search.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 00636865fe7..6813c1a5f61 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -963,11 +963,11 @@ Value Search::Worker::search( if (!givesCheck && lmrDepth < 7 && !ss->inCheck) { Piece capturedPiece = pos.piece_on(move.to_sq()); - int futilityEval = - ss->staticEval + 287 + 277 * lmrDepth + PieceValue[capturedPiece] + Value futilityValue = + ss->staticEval + 288 + 277 * lmrDepth + PieceValue[capturedPiece] + thisThread->captureHistory[movedPiece][move.to_sq()][type_of(capturedPiece)] / 7; - if (futilityEval < alpha) + if (futilityValue <= alpha) continue; } @@ -1389,7 +1389,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Key posKey; Move ttMove, move, bestMove; Depth ttDepth; - Value bestValue, value, ttValue, futilityValue, futilityBase; + Value bestValue, value, ttValue, futilityBase; bool pvHit, givesCheck, capture; int moveCount; Color us = pos.side_to_move(); @@ -1518,7 +1518,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, if (moveCount > 2) continue; - futilityValue = futilityBase + PieceValue[pos.piece_on(move.to_sq())]; + Value futilityValue = futilityBase + PieceValue[pos.piece_on(move.to_sq())]; // If static eval + value of piece we are going to capture is much lower // than alpha we can prune this move. (~2 Elo) From d0e72c19fa878645afd3d2f573a2587b02e26d47 Mon Sep 17 00:00:00 2001 From: Gahtan Nahdi <155860115+gahtan-syarif@users.noreply.github.com> Date: Mon, 15 Apr 2024 11:55:28 +0700 Subject: [PATCH 1442/1766] fix clang compiler warning for avx512 build Initialize variable in constexpr function to get rid of clang compiler warning for avx512 build. closes https://github.com/official-stockfish/Stockfish/pull/5176 Non-functional change --- src/nnue/nnue_feature_transformer.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 3101c8d2689..0a0f4217fdb 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -229,8 +229,7 @@ class FeatureTransformer { static constexpr void order_packs([[maybe_unused]] uint64_t* v) { #if defined(USE_AVX512) // _mm512_packs_epi16 ordering - uint64_t tmp0, tmp1; - tmp0 = v[2], tmp1 = v[3]; + uint64_t tmp0 = v[2], tmp1 = v[3]; v[2] = v[8], v[3] = v[9]; v[8] = v[4], v[9] = v[5]; v[4] = tmp0, v[5] = tmp1; @@ -246,8 +245,7 @@ class FeatureTransformer { static constexpr void inverse_order_packs([[maybe_unused]] uint64_t* v) { #if defined(USE_AVX512) // Inverse _mm512_packs_epi16 ordering - uint64_t tmp0, tmp1; - tmp0 = v[2], tmp1 = v[3]; + uint64_t tmp0 = v[2], tmp1 = v[3]; v[2] = v[4], v[3] = v[5]; v[4] = v[8], v[5] = v[9]; v[8] = tmp0, v[9] = tmp1; From 6fc7da44ad9c7e2ba6062d5c79daafd29a4dcd6f Mon Sep 17 00:00:00 2001 From: "Robert Nurnberg @ elitebook" Date: Tue, 16 Apr 2024 08:23:42 +0200 Subject: [PATCH 1443/1766] update the WDL model The patch only changes the displayed cp and wdl values. closes https://github.com/official-stockfish/Stockfish/pull/5178 No functional change --- src/uci.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uci.cpp b/src/uci.cpp index 8e20207b79e..c707f6dc403 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -330,8 +330,8 @@ WinRateParams win_rate_params(const Position& pos) { double m = std::clamp(material, 10, 78) / 58.0; // Return a = p_a(material) and b = p_b(material), see github.com/official-stockfish/WDL_model - constexpr double as[] = {-185.71965483, 504.85014385, -438.58295743, 474.04604627}; - constexpr double bs[] = {89.23542728, -137.02141296, 73.28669021, 47.53376190}; + constexpr double as[] = {-150.77043883, 394.96159472, -321.73403766, 406.15850091}; + constexpr double bs[] = {62.33245393, -91.02264855, 45.88486850, 51.63461272}; double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3]; double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3]; From 1a8de45b8c2887e8d5efe61498f3acccf5f36116 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 20 Apr 2024 15:33:07 +0200 Subject: [PATCH 1444/1766] Improve CI the recent refactoring has shown some limitations of our testing, hence we add a couple of more tests including: * expected mate score * expected mated score * expected in TB win score * expected in TB loss score * expected info line output * expected info line output (wdl) closes https://github.com/official-stockfish/Stockfish/pull/5181 No functional change --- .github/workflows/sanitizers.yml | 3 + tests/instrumented.sh | 116 +++++++++++++++++++++++++++++-- 2 files changed, 112 insertions(+), 7 deletions(-) diff --git a/.github/workflows/sanitizers.yml b/.github/workflows/sanitizers.yml index 612f1275ce7..78260a182bf 100644 --- a/.github/workflows/sanitizers.yml +++ b/.github/workflows/sanitizers.yml @@ -31,6 +31,9 @@ jobs: - name: Run under valgrind-thread make_option: "" instrumented_option: valgrind-thread + - name: Run non-instrumented + make_option: "" + instrumented_option: none defaults: run: working-directory: src diff --git a/tests/instrumented.sh b/tests/instrumented.sh index 525c7e04085..ac534c16ab4 100755 --- a/tests/instrumented.sh +++ b/tests/instrumented.sh @@ -21,14 +21,14 @@ case $1 in echo "valgrind testing started" prefix='' exeprefix='valgrind --error-exitcode=42 --errors-for-leak-kinds=all --leak-check=full' - postfix='1>/dev/null' + postfix='' threads="1" ;; --valgrind-thread) echo "valgrind-thread testing started" prefix='' exeprefix='valgrind --fair-sched=try --error-exitcode=42' - postfix='1>/dev/null' + postfix='' threads="2" ;; --sanitizer-undefined) @@ -112,7 +112,12 @@ diff $network verify.nnue # more general testing, following an uci protocol exchange cat << EOF > game.exp set timeout 240 + # to correctly catch eof we need the following line + # expect_before timeout { exit 2 } eof { exit 3 } + expect_before timeout { exit 2 } + spawn $exeprefix ./stockfish + expect "Stockfish" send "uci\n" expect "uciok" @@ -125,27 +130,101 @@ cat << EOF > game.exp send "go nodes 1000\n" expect "bestmove" + send "ucinewgame\n" send "position startpos moves e2e4 e7e6\n" send "go nodes 1000\n" expect "bestmove" + send "ucinewgame\n" send "position fen 5rk1/1K4p1/8/8/3B4/8/8/8 b - - 0 1\n" send "go depth 10\n" expect "bestmove" - send "setoption name UCI_ShowWDL value true\n" - send "position startpos\n" + send "ucinewgame\n" + send "position fen 5rk1/1K4p1/8/8/3B4/8/8/8 b - - 0 1\n" send "flip\n" - send "go depth 5\n" + send "go depth 10\n" expect "bestmove" - send "setoption name Skill Level value 10\n" + send "ucinewgame\n" send "position startpos\n" send "go depth 5\n" + expect -re {info depth \d+ seldepth \d+ multipv \d+ score cp \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv} + expect "bestmove" + + send "ucinewgame\n" + send "setoption name UCI_ShowWDL value true\n" + send "position startpos\n" + send "go depth 9\n" + expect -re {info depth 1 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv} + expect -re {info depth 2 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv} + expect -re {info depth 3 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv} + expect -re {info depth 4 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv} + expect -re {info depth 5 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv} + expect -re {info depth 6 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv} + expect -re {info depth 7 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv} + expect -re {info depth 8 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv} + expect -re {info depth 9 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv} expect "bestmove" send "setoption name Clear Hash\n" + send "ucinewgame\n" + send "position fen 5K2/8/2qk4/2nPp3/3r4/6B1/B7/3R4 w - e6\n" + send "go depth 18\n" + expect "score mate 1" + expect "pv d5e6" + expect "bestmove d5e6" + + send "ucinewgame\n" + send "position fen 2brrb2/8/p7/Q7/1p1kpPp1/1P1pN1K1/3P4/8 b - -\n" + send "go depth 18\n" + expect "score mate -1" + expect "bestmove" + + send "ucinewgame\n" + send "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - -\n" + send "go depth 18\n" + expect "score mate 2 * pv c6d7 * f7f5" + expect "bestmove c6d7" + + send "ucinewgame\n" + send "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - -\n" + send "go mate 2\n" + expect "score mate 2 * pv c6d7" + expect "bestmove c6d7" + + send "ucinewgame\n" + send "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - -\n" + send "go nodes 10000\n" + expect "score mate 2 * pv c6d7 * f7f5" + expect "bestmove c6d7" + + send "ucinewgame\n" + send "position fen 1NR2B2/5p2/5p2/1p1kpp2/1P2rp2/2P1pB2/2P1P1K1/8 b - - \n" + send "go depth 18\n" + expect "score mate -2" + expect "pv d5e6 c8d8" + expect "bestmove d5e6" + + send "ucinewgame\n" + send "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - - moves c6d7 f2f1q\n" + send "go depth 18\n" + expect "score mate 1 * pv f7f5" + expect "bestmove f7f5" + + send "ucinewgame\n" + send "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - -\n" + send "go depth 18 searchmoves c6d7\n" + expect "score mate 2 * pv c6d7 * f7f5" + expect "bestmove c6d7" + + send "ucinewgame\n" + send "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - - moves c6d7\n" + send "go depth 18 searchmoves e3e2\n" + expect "score mate -1 * pv e3e2 f7f5" + expect "bestmove e3e2" + send "setoption name EvalFile value verify.nnue\n" send "position startpos\n" send "go depth 5\n" @@ -154,6 +233,13 @@ cat << EOF > game.exp send "setoption name MultiPV value 4\n" send "position startpos\n" send "go depth 5\n" + expect "bestmove" + + send "setoption name Skill Level value 10\n" + send "position startpos\n" + send "go depth 5\n" + expect "bestmove" + send "setoption name Skill Level value 20\n" send "quit\n" expect eof @@ -171,17 +257,30 @@ fi cat << EOF > syzygy.exp set timeout 240 + # to correctly catch eof we need the following line + # expect_before timeout { exit 2 } eof { exit 3 } + expect_before timeout { exit 2 } spawn $exeprefix ./stockfish + expect "Stockfish" send "uci\n" send "setoption name SyzygyPath value ../tests/syzygy/\n" - expect "info string Found 35 tablebases" {} timeout {exit 1} + expect "info string Found 35 tablebases" send "bench 128 1 8 default depth\n" + expect "Nodes searched :" send "ucinewgame\n" send "position fen 4k3/PP6/8/8/8/8/8/4K3 w - - 0 1\n" send "go depth 5\n" + expect -re {score cp 20000|score mate} expect "bestmove" + send "ucinewgame\n" send "position fen 8/1P6/2B5/8/4K3/8/6k1/8 w - - 0 1\n" send "go depth 5\n" + expect -re {score cp 20000|score mate} + expect "bestmove" + send "ucinewgame\n" + send "position fen 8/1P6/2B5/8/4K3/8/6k1/8 b - - 0 1\n" + send "go depth 5\n" + expect -re {score cp -20000|score mate} expect "bestmove" send "quit\n" expect eof @@ -194,6 +293,9 @@ EOF for exp in game.exp syzygy.exp do + echo "======== $exp ==============" + cat $exp + echo "============================" echo "$prefix expect $exp $postfix" eval "$prefix expect $exp $postfix" From 56a9cc512e5ffb2310ad6e4676c77ce0485f31f3 Mon Sep 17 00:00:00 2001 From: Disservin Date: Sat, 20 Apr 2024 20:37:39 +0200 Subject: [PATCH 1445/1766] Move ALSR change to CI Workflow file It makes more sense to not (potentially) change the developers alsr entropy setting to make the test run through. This should be an active choice even if the test then might fail locally for them. closes https://github.com/official-stockfish/Stockfish/pull/5182 No functional change --- .github/workflows/sanitizers.yml | 8 ++++++++ tests/instrumented.sh | 7 ------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/sanitizers.yml b/.github/workflows/sanitizers.yml index 78260a182bf..b75c06cfbbe 100644 --- a/.github/workflows/sanitizers.yml +++ b/.github/workflows/sanitizers.yml @@ -58,6 +58,14 @@ jobs: - name: Check git run: git --version + # Since Linux Kernel 6.5 we are getting false positives from the ci, + # lower the ALSR entropy to disable ALSR, which works as a temporary workaround. + # https://github.com/google/sanitizers/issues/1716 + # https://bugs.launchpad.net/ubuntu/+source/linux/+bug/2056762 + + - name: Lower ALSR entropy + run: sudo sysctl -w vm.mmap_rnd_bits=28 + # Sanitizers - name: ${{ matrix.sanitizers.name }} diff --git a/tests/instrumented.sh b/tests/instrumented.sh index ac534c16ab4..4c63fc5714e 100755 --- a/tests/instrumented.sh +++ b/tests/instrumented.sh @@ -8,13 +8,6 @@ error() } trap 'error ${LINENO}' ERR -# Since Linux Kernel 6.5 we are getting false positives from the ci, -# lower the ALSR entropy to disable ALSR, which works as a temporary workaround. -# https://github.com/google/sanitizers/issues/1716 -# https://bugs.launchpad.net/ubuntu/+source/linux/+bug/2056762 -sudo sysctl -w vm.mmap_rnd_bits=28 - - # define suitable post and prefixes for testing options case $1 in --valgrind) From d47aa639bd614b37a59f87e6ab68496580f0cf3e Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Thu, 18 Apr 2024 18:50:09 +0800 Subject: [PATCH 1446/1766] Tweak TT aging and replacement strategies We change the definition of "age" from "age of this position" to "age of this TT entry". In this way, despite being on the same position, when we save into TT, we always prefer the new entry as compared to the old one. Passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 152256 W: 39597 L: 39110 D: 73549 Ptnml(0-2): 556, 17562, 39398, 18063, 549 https://tests.stockfishchess.org/tests/view/6620faee3fe04ce4cefbf215 Passed LTC: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 51564 W: 13242 L: 12895 D: 25427 Ptnml(0-2): 24, 5464, 14463, 5803, 28 https://tests.stockfishchess.org/tests/view/66231ab53fe04ce4cefc153e closes #5184 Bench 1479416 --- src/tt.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/tt.cpp b/src/tt.cpp index 41ed4591f39..4885a781a5e 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -40,7 +40,8 @@ void TTEntry::save( move16 = m; // Overwrite less valuable entries (cheapest checks first) - if (b == BOUND_EXACT || uint16_t(k) != key16 || d - DEPTH_OFFSET + 2 * pv > depth8 - 4) + if (b == BOUND_EXACT || uint16_t(k) != key16 || d - DEPTH_OFFSET + 2 * pv > depth8 - 4 + || relative_age(generation8)) { assert(d > DEPTH_OFFSET); assert(d < 256 + DEPTH_OFFSET); @@ -123,13 +124,7 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const { for (int i = 0; i < ClusterSize; ++i) if (tte[i].key16 == key16 || !tte[i].depth8) - { - constexpr uint8_t lowerBits = GENERATION_DELTA - 1; - - // Refresh with new generation, keeping the lower bits the same. - tte[i].genBound8 = uint8_t(generation8 | (tte[i].genBound8 & lowerBits)); - return found = bool(tte[i].depth8), &tte[i]; - } + return found = bool(tte[i].depth8), &tte[i]; // Find an entry to be replaced according to the replacement strategy TTEntry* replace = tte; From ddd250b9d655117920dd65a973cea2f8b3c57fce Mon Sep 17 00:00:00 2001 From: Disservin Date: Mon, 22 Apr 2024 19:24:10 +0200 Subject: [PATCH 1447/1766] Restore NPS output for Perft Previously it was possible to also get the node counter after running a bench with perft, i.e. `./stockfish bench 1 1 5 current perft`, caused by a small regression from the uci refactoring. ``` Nodes searched: 4865609 =========================== Total time (ms) : 18 Nodes searched : 4865609 Nodes/second : 270311611 ```` closes https://github.com/official-stockfish/Stockfish/pull/5188 No functional change --- src/benchmark.cpp | 2 +- src/benchmark.h | 2 +- src/engine.cpp | 14 ++++++++------ src/engine.h | 4 ++++ src/perft.h | 7 +++---- src/uci.cpp | 26 ++++++++++++++++++++++---- src/uci.h | 10 ++++++---- 7 files changed, 45 insertions(+), 20 deletions(-) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 267a6b4b2a9..3622ac8afc8 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -93,7 +93,7 @@ const std::vector Defaults = { } // namespace -namespace Stockfish { +namespace Stockfish::Benchmark { // Builds a list of UCI commands to be run by bench. There // are five parameters: TT size in MB, number of search threads that diff --git a/src/benchmark.h b/src/benchmark.h index 8905fcb1825..b1eba40f38b 100644 --- a/src/benchmark.h +++ b/src/benchmark.h @@ -23,7 +23,7 @@ #include #include -namespace Stockfish { +namespace Stockfish::Benchmark { std::vector setup_bench(const std::string&, std::istream&); diff --git a/src/engine.cpp b/src/engine.cpp index 325b971ea46..4625e00a816 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include "evaluate.h" #include "misc.h" @@ -54,14 +55,15 @@ Engine::Engine(std::string path) : pos.set(StartFEN, false, &states->back()); } -void Engine::go(const Search::LimitsType& limits) { +std::uint64_t Engine::perft(const std::string& fen, Depth depth, bool isChess960) { verify_networks(); - if (limits.perft) - { - perft(pos.fen(), limits.perft, options["UCI_Chess960"]); - return; - } + return Benchmark::perft(fen, depth, isChess960); +} + +void Engine::go(const Search::LimitsType& limits) { + assert(limits.perft == 0); + verify_networks(); threads.start_thinking(options, pos, states, limits); } diff --git a/src/engine.h b/src/engine.h index 7122ee59d0b..041f5678585 100644 --- a/src/engine.h +++ b/src/engine.h @@ -26,6 +26,7 @@ #include #include #include +#include #include "nnue/network.h" #include "position.h" @@ -33,6 +34,7 @@ #include "thread.h" #include "tt.h" #include "ucioption.h" +#include "syzygy/tbprobe.h" // for Stockfish::Depth namespace Stockfish { @@ -45,6 +47,8 @@ class Engine { Engine(std::string path = ""); ~Engine() { wait_for_search_finished(); } + std::uint64_t perft(const std::string& fen, Depth depth, bool isChess960); + // non blocking call to start searching void go(const Search::LimitsType&); // non blocking call to stop searching diff --git a/src/perft.h b/src/perft.h index 2dbab828a18..e907742da05 100644 --- a/src/perft.h +++ b/src/perft.h @@ -26,7 +26,7 @@ #include "types.h" #include "uci.h" -namespace Stockfish { +namespace Stockfish::Benchmark { // Utility to verify move generation. All the leaf nodes up // to the given depth are generated and counted, and the sum is returned. @@ -56,13 +56,12 @@ uint64_t perft(Position& pos, Depth depth) { return nodes; } -inline void perft(const std::string& fen, Depth depth, bool isChess960) { +inline uint64_t perft(const std::string& fen, Depth depth, bool isChess960) { StateListPtr states(new std::deque(1)); Position p; p.set(fen, isChess960, &states->back()); - uint64_t nodes = perft(p, depth); - sync_cout << "\nNodes searched: " << nodes << "\n" << sync_endl; + return perft(p, depth); } } diff --git a/src/uci.cpp b/src/uci.cpp index c707f6dc403..cb686a027db 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -219,7 +219,11 @@ Search::LimitsType UCIEngine::parse_limits(std::istream& is) { void UCIEngine::go(std::istringstream& is) { Search::LimitsType limits = parse_limits(is); - engine.go(limits); + + if (limits.perft) + perft(limits); + else + engine.go(limits); } void UCIEngine::bench(std::istream& args) { @@ -233,7 +237,7 @@ void UCIEngine::bench(std::istream& args) { on_update_full(i, options["UCI_ShowWDL"]); }); - std::vector list = setup_bench(engine.fen(), args); + std::vector list = Benchmark::setup_bench(engine.fen(), args); num = count_if(list.begin(), list.end(), [](const std::string& s) { return s.find("go ") == 0 || s.find("eval") == 0; }); @@ -251,8 +255,16 @@ void UCIEngine::bench(std::istream& args) { << std::endl; if (token == "go") { - go(is); - engine.wait_for_search_finished(); + Search::LimitsType limits = parse_limits(is); + + if (limits.perft) + nodes = perft(limits); + else + { + engine.go(limits); + engine.wait_for_search_finished(); + } + nodes += nodesSearched; nodesSearched = 0; } @@ -288,6 +300,12 @@ void UCIEngine::setoption(std::istringstream& is) { engine.get_options().setoption(is); } +std::uint64_t UCIEngine::perft(const Search::LimitsType& limits) { + auto nodes = engine.perft(engine.fen(), limits.perft, engine.get_options()["UCI_Chess960"]); + sync_cout << "\nNodes searched: " << nodes << "\n" << sync_endl; + return nodes; +} + void UCIEngine::position(std::istringstream& is) { std::string token, fen; diff --git a/src/uci.h b/src/uci.h index ee8c2814aee..55d580f9727 100644 --- a/src/uci.h +++ b/src/uci.h @@ -22,6 +22,7 @@ #include #include #include +#include #include "engine.h" #include "misc.h" @@ -57,10 +58,11 @@ class UCIEngine { Engine engine; CommandLine cli; - void go(std::istringstream& is); - void bench(std::istream& args); - void position(std::istringstream& is); - void setoption(std::istringstream& is); + void go(std::istringstream& is); + void bench(std::istream& args); + void position(std::istringstream& is); + void setoption(std::istringstream& is); + std::uint64_t perft(const Search::LimitsType&); static void on_update_no_moves(const Engine::InfoShort& info); static void on_update_full(const Engine::InfoFull& info, bool showWDL); From fcba524793222fcdb1ca4254697b15e168f39ad2 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Mon, 22 Apr 2024 14:34:22 +0300 Subject: [PATCH 1448/1766] Tune Search Parameters Parameters Tune, adding also another tunable parameter (npmDiv) to be variable for different nets (bignet, smallnet, psqtOnly smallnet). P.s: The changed values are only the parameters where there is agreement among the different time controls, so in other words, the tunings are telling us that changing these specific values to this specific direction is good in all time controls, so there shouldn't be a high risk of regressing at longer time controls. Passed STC: LLR: 2.97 (-2.94,2.94) <0.00,2.00> Total: 39552 W: 10329 L: 9999 D: 19224 Ptnml(0-2): 156, 4592, 9989, 4844, 195 https://tests.stockfishchess.org/tests/view/661be9a0bd68065432a088c0 Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 56394 W: 14439 L: 14078 D: 27877 Ptnml(0-2): 30, 6152, 15480, 6497, 38 https://tests.stockfishchess.org/tests/view/661c746296961e72eb565406 closes https://github.com/official-stockfish/Stockfish/pull/5187 Bench: 1836777 --- src/evaluate.cpp | 14 +++++++------- src/movepick.cpp | 10 +++++----- src/search.cpp | 46 +++++++++++++++++++++++----------------------- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index dcbfedb499a..ec120a480a3 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -58,14 +58,14 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, const Position& pos, Value nnue = smallNet ? networks.small.evaluate(pos, true, &nnueComplexity, psqtOnly) : networks.big.evaluate(pos, true, &nnueComplexity, false); - const auto adjustEval = [&](int optDiv, int nnueDiv, int pawnCountConstant, int pawnCountMul, - int npmConstant, int evalDiv, int shufflingConstant, - int shufflingDiv) { + const auto adjustEval = [&](int optDiv, int nnueDiv, int npmDiv, int pawnCountConstant, + int pawnCountMul, int npmConstant, int evalDiv, + int shufflingConstant, int shufflingDiv) { // Blend optimism and eval with nnue complexity and material imbalance optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / optDiv; nnue -= nnue * (nnueComplexity * 5 / 3) / nnueDiv; - int npm = pos.non_pawn_material() / 64; + int npm = pos.non_pawn_material() / npmDiv; v = (nnue * (npm + pawnCountConstant + pawnCountMul * pos.count()) + optimism * (npmConstant + npm)) / evalDiv; @@ -76,11 +76,11 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, const Position& pos, }; if (!smallNet) - adjustEval(513, 32395, 919, 11, 145, 1036, 178, 204); + adjustEval(524, 32395, 66, 942, 11, 139, 1058, 178, 204); else if (psqtOnly) - adjustEval(517, 32857, 908, 7, 155, 1019, 224, 238); + adjustEval(517, 32857, 65, 908, 7, 155, 1006, 224, 238); else - adjustEval(499, 32793, 903, 9, 147, 1067, 208, 211); + adjustEval(515, 32793, 63, 944, 9, 140, 1067, 206, 206); // Guarantee evaluation does not hit the tablebase range v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); diff --git a/src/movepick.cpp b/src/movepick.cpp index c1119cf11eb..4a93662db43 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -190,8 +190,8 @@ void MovePicker::score() { m.value += bool(pos.check_squares(pt) & to) * 16384; // bonus for escaping from capture - m.value += threatenedPieces & from ? (pt == QUEEN && !(to & threatenedByRook) ? 51000 - : pt == ROOK && !(to & threatenedByMinor) ? 24950 + m.value += threatenedPieces & from ? (pt == QUEEN && !(to & threatenedByRook) ? 51700 + : pt == ROOK && !(to & threatenedByMinor) ? 25600 : !(to & threatenedByPawn) ? 14450 : 0) : 0; @@ -200,7 +200,7 @@ void MovePicker::score() { m.value -= !(threatenedPieces & from) ? (pt == QUEEN ? bool(to & threatenedByRook) * 48150 + bool(to & threatenedByMinor) * 10650 - : pt == ROOK ? bool(to & threatenedByMinor) * 24500 + : pt == ROOK ? bool(to & threatenedByMinor) * 24335 : pt != PAWN ? bool(to & threatenedByPawn) * 14950 : 0) : 0; @@ -241,7 +241,7 @@ Move MovePicker::select(Pred filter) { // moves left, picking the move with the highest score from a list of generated moves. Move MovePicker::next_move(bool skipQuiets) { - auto quiet_threshold = [](Depth d) { return -3550 * d; }; + auto quiet_threshold = [](Depth d) { return -3560 * d; }; top: switch (stage) @@ -310,7 +310,7 @@ Move MovePicker::next_move(bool skipQuiets) { return *cur != refutations[0] && *cur != refutations[1] && *cur != refutations[2]; })) { - if ((cur - 1)->value > -8000 || (cur - 1)->value <= quiet_threshold(depth)) + if ((cur - 1)->value > -7998 || (cur - 1)->value <= quiet_threshold(depth)) return *(cur - 1); // Remaining quiets are bad diff --git a/src/search.cpp b/src/search.cpp index 6813c1a5f61..183b7bcee5d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -57,9 +57,9 @@ static constexpr double EvalLevel[10] = {1.043, 1.017, 0.952, 1.009, 0.971, // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) { - Value futilityMult = 118 - 44 * noTtCutNode; + Value futilityMult = 118 - 45 * noTtCutNode; Value improvingDeduction = 52 * improving * futilityMult / 32; - Value worseningDeduction = (310 + 48 * improving) * oppWorsening * futilityMult / 1024; + Value worseningDeduction = (316 + 48 * improving) * oppWorsening * futilityMult / 1024; return futilityMult * d - improvingDeduction - worseningDeduction; } @@ -76,10 +76,10 @@ Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { } // History and stats update bonus, based on depth -int stat_bonus(Depth d) { return std::clamp(211 * d - 315, 0, 1291); } +int stat_bonus(Depth d) { return std::clamp(214 * d - 318, 16, 1304); } // History and stats update malus, based on depth -int stat_malus(Depth d) { return (d < 4 ? 572 * d - 285 : 1372); } +int stat_malus(Depth d) { return (d < 4 ? 572 * d - 284 : 1355); } // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(size_t nodes) { return VALUE_DRAW - 1 + Value(nodes & 0x2); } @@ -303,12 +303,12 @@ void Search::Worker::iterative_deepening() { // Reset aspiration window starting size Value avg = rootMoves[pvIdx].averageScore; - delta = 11 + avg * avg / 11254; + delta = 10 + avg * avg / 11480; alpha = std::max(avg - delta, -VALUE_INFINITE); beta = std::min(avg + delta, VALUE_INFINITE); // Adjust optimism based on root move's averageScore (~4 Elo) - optimism[us] = 125 * avg / (std::abs(avg) + 91); + optimism[us] = 122 * avg / (std::abs(avg) + 92); optimism[~us] = -optimism[us]; // Start with a small aspiration window and, in the case of a fail @@ -752,7 +752,7 @@ Value Search::Worker::search( // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. // Adjust razor margin according to cutoffCnt. (~1 Elo) - if (eval < alpha - 471 - (276 - 148 * ((ss + 1)->cutoffCnt > 3)) * depth * depth) + if (eval < alpha - 471 - (275 - 148 * ((ss + 1)->cutoffCnt > 3)) * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -763,14 +763,14 @@ Value Search::Worker::search( // The depth condition is important for mate finding. if (!ss->ttPv && depth < 12 && eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening) - - (ss - 1)->statScore / 284 + - (ss - 1)->statScore / 286 >= beta && eval >= beta && eval < VALUE_TB_WIN_IN_MAX_PLY && (!ttMove || ttCapture)) return beta > VALUE_TB_LOSS_IN_MAX_PLY ? (eval + beta) / 2 : eval; // Step 9. Null move search with verification search (~35 Elo) if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 18001 - && eval >= beta && ss->staticEval >= beta - 21 * depth + 315 && !excludedMove + && eval >= beta && ss->staticEval >= beta - 21 * depth + 312 && !excludedMove && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly && beta > VALUE_TB_LOSS_IN_MAX_PLY) { @@ -881,7 +881,7 @@ Value Search::Worker::search( moves_loop: // When in check, search starts here // Step 12. A small Probcut idea, when we are in check (~4 Elo) - probCutBeta = beta + 436; + probCutBeta = beta + 452; if (ss->inCheck && !PvNode && ttCapture && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 4 && ttValue >= probCutBeta && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) @@ -964,7 +964,7 @@ Value Search::Worker::search( { Piece capturedPiece = pos.piece_on(move.to_sq()); Value futilityValue = - ss->staticEval + 288 + 277 * lmrDepth + PieceValue[capturedPiece] + ss->staticEval + 285 + 277 * lmrDepth + PieceValue[capturedPiece] + thisThread->captureHistory[movedPiece][move.to_sq()][type_of(capturedPiece)] / 7; if (futilityValue <= alpha) @@ -972,7 +972,7 @@ Value Search::Worker::search( } // SEE based pruning for captures and checks (~11 Elo) - if (!pos.see_ge(move, -199 * depth)) + if (!pos.see_ge(move, -203 * depth)) continue; } else @@ -992,10 +992,10 @@ Value Search::Worker::search( lmrDepth += history / 5285; Value futilityValue = - ss->staticEval + (bestValue < ss->staticEval - 54 ? 128 : 58) + 131 * lmrDepth; + ss->staticEval + (bestValue < ss->staticEval - 54 ? 128 : 57) + 131 * lmrDepth; // Futility pruning: parent node (~13 Elo) - if (!ss->inCheck && lmrDepth < 15 && futilityValue <= alpha) + if (!ss->inCheck && lmrDepth < 14 && futilityValue <= alpha) { if (bestValue <= futilityValue && std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY && futilityValue < VALUE_TB_WIN_IN_MAX_PLY) @@ -1006,7 +1006,7 @@ Value Search::Worker::search( lmrDepth = std::max(lmrDepth, 0); // Prune moves with negative SEE (~4 Elo) - if (!pos.see_ge(move, -26 * lmrDepth * lmrDepth)) + if (!pos.see_ge(move, -27 * lmrDepth * lmrDepth)) continue; } } @@ -1026,11 +1026,11 @@ Value Search::Worker::search( // so changing them requires tests at these types of time controls. // Recursive singular search is avoided. if (!rootNode && move == ttMove && !excludedMove - && depth >= 4 - (thisThread->completedDepth > 32) + ss->ttPv + && depth >= 4 - (thisThread->completedDepth > 33) + ss->ttPv && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - (64 + 59 * (ss->ttPv && !PvNode)) * depth / 64; + Value singularBeta = ttValue - (65 + 59 * (ss->ttPv && !PvNode)) * depth / 63; Depth singularDepth = newDepth / 2; ss->excludedMove = move; @@ -1134,10 +1134,10 @@ Value Search::Worker::search( ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + (*contHist[0])[movedPiece][move.to_sq()] + (*contHist[1])[movedPiece][move.to_sq()] - + (*contHist[3])[movedPiece][move.to_sq()] - 5007; + + (*contHist[3])[movedPiece][move.to_sq()] - 5024; // Decrease/increase reduction for moves with a good/bad history (~8 Elo) - r -= ss->statScore / 12901; + r -= ss->statScore / 13182; // Step 17. Late moves reduction / extension (LMR, ~117 Elo) if (depth >= 2 && moveCount > 1 + rootNode) @@ -1274,7 +1274,7 @@ Value Search::Worker::search( else { // Reduce other moves if we have found at least one score improvement (~2 Elo) - if (depth > 2 && depth < 12 && beta < 13132 && value > -13295) + if (depth > 2 && depth < 12 && beta < 13546 && value > -13478) depth -= 2; assert(depth > 0); @@ -1319,7 +1319,7 @@ Value Search::Worker::search( { int bonus = (depth > 5) + (PvNode || cutNode) + ((ss - 1)->statScore < -14761) + ((ss - 1)->moveCount > 11) - + (!ss->inCheck && bestValue <= ss->staticEval - 144); + + (!ss->inCheck && bestValue <= ss->staticEval - 142); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] @@ -1477,7 +1477,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, if (bestValue > alpha) alpha = bestValue; - futilityBase = ss->staticEval + 246; + futilityBase = ss->staticEval + 250; } const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, @@ -1625,7 +1625,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth Search::Worker::reduction(bool i, Depth d, int mn, int delta) { int reductionScale = reductions[d] * reductions[mn]; - return (reductionScale + 1123 - delta * 832 / rootDelta) / 1024 + (!i && reductionScale > 1025); + return (reductionScale + 1150 - delta * 832 / rootDelta) / 1024 + (!i && reductionScale > 1025); } TimePoint Search::Worker::elapsed() const { From 49ef4c935a5cb0e4d94096e6354caa06b36b3e3c Mon Sep 17 00:00:00 2001 From: gab8192 Date: Sat, 20 Apr 2024 21:26:00 +0200 Subject: [PATCH 1449/1766] Implement accumulator refresh table For each thread persist an accumulator cache for the network, where each cache contains multiple entries for each of the possible king squares. When the accumulator needs to be refreshed, the cached entry is used to more efficiently update the accumulator, instead of rebuilding it from scratch. This idea, was first described by Luecx (author of Koivisto) and is commonly referred to as "Finny Tables". When the accumulator needs to be refreshed, instead of filling it with biases and adding every piece from scratch, we... 1. Take the `AccumulatorRefreshEntry` associated with the new king bucket 2. Calculate the features to activate and deactivate (from differences between bitboards in the entry and bitboards of the actual position) 3. Apply the updates on the refresh entry 4. Copy the content of the refresh entry accumulator to the accumulator we were refreshing 5. Copy the bitboards from the position to the refresh entry, to match the newly updated accumulator Results at STC: https://tests.stockfishchess.org/tests/view/662301573fe04ce4cefc1386 (first version) https://tests.stockfishchess.org/tests/view/6627fa063fe04ce4cefc6560 (final) Non-Regression between first and final: https://tests.stockfishchess.org/tests/view/662801e33fe04ce4cefc660a STC SMP: https://tests.stockfishchess.org/tests/view/662808133fe04ce4cefc667c closes https://github.com/official-stockfish/Stockfish/pull/5183 No functional change --- src/evaluate.cpp | 19 ++- src/evaluate.h | 8 +- src/nnue/features/half_ka_v2_hm.cpp | 4 +- src/nnue/features/half_ka_v2_hm.h | 8 +- src/nnue/network.cpp | 45 ++++--- src/nnue/network.h | 24 ++-- src/nnue/nnue_accumulator.h | 70 +++++++++- src/nnue/nnue_feature_transformer.h | 192 ++++++++++++++++++++++++++-- src/nnue/nnue_misc.cpp | 17 ++- src/nnue/nnue_misc.h | 9 +- src/search.cpp | 26 ++-- src/search.h | 7 +- 12 files changed, 349 insertions(+), 80 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index ec120a480a3..f5746ca5199 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -25,12 +25,14 @@ #include #include #include +#include #include "nnue/network.h" #include "nnue/nnue_misc.h" #include "position.h" #include "types.h" #include "uci.h" +#include "nnue/nnue_accumulator.h" namespace Stockfish { @@ -45,7 +47,10 @@ int Eval::simple_eval(const Position& pos, Color c) { // Evaluate is the evaluator for the outer world. It returns a static evaluation // of the position from the point of view of the side to move. -Value Eval::evaluate(const Eval::NNUE::Networks& networks, const Position& pos, int optimism) { +Value Eval::evaluate(const Eval::NNUE::Networks& networks, + const Position& pos, + Eval::NNUE::AccumulatorCaches& caches, + int optimism) { assert(!pos.checkers()); @@ -55,8 +60,8 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, const Position& pos, int nnueComplexity; int v; - Value nnue = smallNet ? networks.small.evaluate(pos, true, &nnueComplexity, psqtOnly) - : networks.big.evaluate(pos, true, &nnueComplexity, false); + Value nnue = smallNet ? networks.small.evaluate(pos, nullptr, true, &nnueComplexity, psqtOnly) + : networks.big.evaluate(pos, &caches.big, true, &nnueComplexity, false); const auto adjustEval = [&](int optDiv, int nnueDiv, int npmDiv, int pawnCountConstant, int pawnCountMul, int npmConstant, int evalDiv, @@ -94,20 +99,22 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, const Position& pos, // Trace scores are from white's point of view std::string Eval::trace(Position& pos, const Eval::NNUE::Networks& networks) { + auto caches = std::make_unique(); + if (pos.checkers()) return "Final evaluation: none (in check)"; std::stringstream ss; ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2); - ss << '\n' << NNUE::trace(pos, networks) << '\n'; + ss << '\n' << NNUE::trace(pos, networks, *caches) << '\n'; ss << std::showpoint << std::showpos << std::fixed << std::setprecision(2) << std::setw(15); - Value v = networks.big.evaluate(pos, false); + Value v = networks.big.evaluate(pos, &caches->big, false); v = pos.side_to_move() == WHITE ? v : -v; ss << "NNUE evaluation " << 0.01 * UCIEngine::to_cp(v, pos) << " (white side)\n"; - v = evaluate(networks, pos, VALUE_ZERO); + v = evaluate(networks, pos, *caches, VALUE_ZERO); v = pos.side_to_move() == WHITE ? v : -v; ss << "Final evaluation " << 0.01 * UCIEngine::to_cp(v, pos) << " (white side)"; ss << " [with scaled NNUE, ...]"; diff --git a/src/evaluate.h b/src/evaluate.h index da9c7074ec1..38615ff7d68 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -40,14 +40,16 @@ constexpr inline int SmallNetThreshold = 1274, PsqtOnlyThreshold = 2389; namespace NNUE { struct Networks; +struct AccumulatorCaches; } std::string trace(Position& pos, const Eval::NNUE::Networks& networks); int simple_eval(const Position& pos, Color c); -Value evaluate(const NNUE::Networks& networks, const Position& pos, int optimism); - - +Value evaluate(const NNUE::Networks& networks, + const Position& pos, + Eval::NNUE::AccumulatorCaches& caches, + int optimism); } // namespace Eval } // namespace Stockfish diff --git a/src/nnue/features/half_ka_v2_hm.cpp b/src/nnue/features/half_ka_v2_hm.cpp index 5789db4844a..71782a7b731 100644 --- a/src/nnue/features/half_ka_v2_hm.cpp +++ b/src/nnue/features/half_ka_v2_hm.cpp @@ -23,7 +23,7 @@ #include "../../bitboard.h" #include "../../position.h" #include "../../types.h" -#include "../nnue_common.h" +#include "../nnue_accumulator.h" namespace Stockfish::Eval::NNUE::Features { @@ -49,6 +49,8 @@ void HalfKAv2_hm::append_active_indices(const Position& pos, IndexList& active) // Explicit template instantiations template void HalfKAv2_hm::append_active_indices(const Position& pos, IndexList& active); template void HalfKAv2_hm::append_active_indices(const Position& pos, IndexList& active); +template IndexType HalfKAv2_hm::make_index(Square s, Piece pc, Square ksq); +template IndexType HalfKAv2_hm::make_index(Square s, Piece pc, Square ksq); // Get a list of indices for recently changed features template diff --git a/src/nnue/features/half_ka_v2_hm.h b/src/nnue/features/half_ka_v2_hm.h index 8363184f430..96349704745 100644 --- a/src/nnue/features/half_ka_v2_hm.h +++ b/src/nnue/features/half_ka_v2_hm.h @@ -63,10 +63,6 @@ class HalfKAv2_hm { {PS_NONE, PS_B_PAWN, PS_B_KNIGHT, PS_B_BISHOP, PS_B_ROOK, PS_B_QUEEN, PS_KING, PS_NONE, PS_NONE, PS_W_PAWN, PS_W_KNIGHT, PS_W_BISHOP, PS_W_ROOK, PS_W_QUEEN, PS_KING, PS_NONE}}; - // Index of a feature for a given king position and another piece on some square - template - static IndexType make_index(Square s, Piece pc, Square ksq); - public: // Feature name static constexpr const char* Name = "HalfKAv2_hm(Friend)"; @@ -126,6 +122,10 @@ class HalfKAv2_hm { static constexpr IndexType MaxActiveDimensions = 32; using IndexList = ValueList; + // Index of a feature for a given king position and another piece on some square + template + static IndexType make_index(Square s, Piece pc, Square ksq); + // Get a list of indices for active features template static void append_active_indices(const Position& pos, IndexList& active); diff --git a/src/nnue/network.cpp b/src/nnue/network.cpp index bea3e7cb398..656ad97a1e3 100644 --- a/src/nnue/network.cpp +++ b/src/nnue/network.cpp @@ -186,10 +186,11 @@ bool Network::save(const std::optional& filename template -Value Network::evaluate(const Position& pos, - bool adjusted, - int* complexity, - bool psqtOnly) const { +Value Network::evaluate(const Position& pos, + AccumulatorCaches::Cache* cache, + bool adjusted, + int* complexity, + bool psqtOnly) const { // We manually align the arrays on the stack because with gcc < 9.3 // overaligning stack variables with alignas() doesn't work correctly. @@ -197,20 +198,21 @@ Value Network::evaluate(const Position& pos, constexpr int delta = 24; #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) - TransformedFeatureType transformedFeaturesUnaligned - [FeatureTransformer::BufferSize - + alignment / sizeof(TransformedFeatureType)]; + TransformedFeatureType + transformedFeaturesUnaligned[FeatureTransformer::BufferSize + + alignment / sizeof(TransformedFeatureType)]; auto* transformedFeatures = align_ptr_up(&transformedFeaturesUnaligned[0]); #else - alignas(alignment) TransformedFeatureType transformedFeatures - [FeatureTransformer::BufferSize]; + alignas(alignment) TransformedFeatureType + transformedFeatures[FeatureTransformer::BufferSize]; #endif ASSERT_ALIGNED(transformedFeatures, alignment); const int bucket = (pos.count() - 1) / 4; - const auto psqt = featureTransformer->transform(pos, transformedFeatures, bucket, psqtOnly); + const auto psqt = + featureTransformer->transform(pos, cache, transformedFeatures, bucket, psqtOnly); const auto positional = !psqtOnly ? (network[bucket]->propagate(transformedFeatures)) : 0; if (complexity) @@ -255,26 +257,29 @@ void Network::verify(std::string evalfilePath) const { template -void Network::hint_common_access(const Position& pos, bool psqtOnl) const { - featureTransformer->hint_common_access(pos, psqtOnl); +void Network::hint_common_access(const Position& pos, + AccumulatorCaches::Cache* cache, + bool psqtOnl) const { + featureTransformer->hint_common_access(pos, cache, psqtOnl); } - template -NnueEvalTrace Network::trace_evaluate(const Position& pos) const { +NnueEvalTrace +Network::trace_evaluate(const Position& pos, + AccumulatorCaches::Cache* cache) const { // We manually align the arrays on the stack because with gcc < 9.3 // overaligning stack variables with alignas() doesn't work correctly. constexpr uint64_t alignment = CacheLineSize; #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) - TransformedFeatureType transformedFeaturesUnaligned - [FeatureTransformer::BufferSize - + alignment / sizeof(TransformedFeatureType)]; + TransformedFeatureType + transformedFeaturesUnaligned[FeatureTransformer::BufferSize + + alignment / sizeof(TransformedFeatureType)]; auto* transformedFeatures = align_ptr_up(&transformedFeaturesUnaligned[0]); #else - alignas(alignment) TransformedFeatureType transformedFeatures - [FeatureTransformer::BufferSize]; + alignas(alignment) TransformedFeatureType + transformedFeatures[FeatureTransformer::BufferSize]; #endif ASSERT_ALIGNED(transformedFeatures, alignment); @@ -284,7 +289,7 @@ NnueEvalTrace Network::trace_evaluate(const Position& pos) co for (IndexType bucket = 0; bucket < LayerStacks; ++bucket) { const auto materialist = - featureTransformer->transform(pos, transformedFeatures, bucket, false); + featureTransformer->transform(pos, cache, transformedFeatures, bucket, false); const auto positional = network[bucket]->propagate(transformedFeatures); t.psqt[bucket] = static_cast(materialist / OutputScale); diff --git a/src/nnue/network.h b/src/nnue/network.h index 21e1c622205..df59732d955 100644 --- a/src/nnue/network.h +++ b/src/nnue/network.h @@ -31,10 +31,10 @@ #include "nnue_architecture.h" #include "nnue_feature_transformer.h" #include "nnue_misc.h" +#include "nnue_accumulator.h" namespace Stockfish::Eval::NNUE { - enum class EmbeddedNNUEType { BIG, SMALL, @@ -43,6 +43,8 @@ enum class EmbeddedNNUEType { template class Network { + static constexpr IndexType FTDimensions = Arch::TransformedFeatureDimensions; + public: Network(EvalFile file, EmbeddedNNUEType type) : evalFile(file), @@ -51,17 +53,20 @@ class Network { void load(const std::string& rootDirectory, std::string evalfilePath); bool save(const std::optional& filename) const; + Value evaluate(const Position& pos, + AccumulatorCaches::Cache* cache, + bool adjusted = false, + int* complexity = nullptr, + bool psqtOnly = false) const; - Value evaluate(const Position& pos, - bool adjusted = false, - int* complexity = nullptr, - bool psqtOnly = false) const; - - void hint_common_access(const Position& pos, bool psqtOnl) const; + void hint_common_access(const Position& pos, + AccumulatorCaches::Cache* cache, + bool psqtOnl) const; void verify(std::string evalfilePath) const; - NnueEvalTrace trace_evaluate(const Position& pos) const; + NnueEvalTrace trace_evaluate(const Position& pos, + AccumulatorCaches::Cache* cache) const; private: void load_user_net(const std::string&, const std::string&); @@ -89,6 +94,9 @@ class Network { // Hash value of evaluation function structure static constexpr std::uint32_t hash = Transformer::get_hash_value() ^ Arch::get_hash_value(); + + template + friend struct AccumulatorCaches::Cache; }; // Definitions of the network types diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index c0746b4ee86..8d73dbef5ad 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -28,13 +28,75 @@ namespace Stockfish::Eval::NNUE { +using BiasType = std::int16_t; +using PSQTWeightType = std::int32_t; +using IndexType = std::uint32_t; + // Class that holds the result of affine transformation of input features template struct alignas(CacheLineSize) Accumulator { - std::int16_t accumulation[2][Size]; - std::int32_t psqtAccumulation[2][PSQTBuckets]; - bool computed[2]; - bool computedPSQT[2]; + std::int16_t accumulation[COLOR_NB][Size]; + std::int32_t psqtAccumulation[COLOR_NB][PSQTBuckets]; + bool computed[COLOR_NB]; + bool computedPSQT[COLOR_NB]; +}; + + +// AccumulatorCaches struct provides per-thread accumulator caches, where each +// cache contains multiple entries for each of the possible king squares. +// When the accumulator needs to be refreshed, the cached entry is used to more +// efficiently update the accumulator, instead of rebuilding it from scratch. +// This idea, was first described by Luecx (author of Koivisto) and +// is commonly referred to as "Finny Tables". +struct AccumulatorCaches { + + template + struct alignas(CacheLineSize) Cache { + + struct alignas(CacheLineSize) Entry { + BiasType accumulation[COLOR_NB][Size]; + PSQTWeightType psqtAccumulation[COLOR_NB][PSQTBuckets]; + Bitboard byColorBB[COLOR_NB][COLOR_NB]; + Bitboard byTypeBB[COLOR_NB][PIECE_TYPE_NB]; + + // To initialize a refresh entry, we set all its bitboards empty, + // so we put the biases in the accumulation, without any weights on top + void clear(const BiasType* biases) { + + std::memset(byColorBB, 0, sizeof(byColorBB)); + std::memset(byTypeBB, 0, sizeof(byTypeBB)); + + std::memcpy(accumulation[WHITE], biases, Size * sizeof(BiasType)); + std::memcpy(accumulation[BLACK], biases, Size * sizeof(BiasType)); + + std::memset(psqtAccumulation, 0, sizeof(psqtAccumulation)); + } + }; + + template + void clear(const Network& network) { + for (auto& entry : entries) + entry.clear(network.featureTransformer->biases); + } + + void clear(const BiasType* biases) { + for (auto& entry : entries) + entry.clear(biases); + } + + Entry& operator[](Square sq) { return entries[sq]; } + + std::array entries; + }; + + template + void clear(const Networks& networks) { + big.clear(networks.big); + } + + // When adding a new cache for a network, i.e. the smallnet + // the appropriate condition must be added to FeatureTransformer::update_accumulator_refresh. + Cache big; }; } // namespace Stockfish::Eval::NNUE diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 0a0f4217fdb..88f0e4031a4 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -195,10 +195,10 @@ template StateInfo::*accPtr> class FeatureTransformer { - private: // Number of output dimensions for one side static constexpr IndexType HalfDimensions = TransformedFeatureDimensions; + private: #ifdef VECTOR static constexpr int NumRegs = BestRegisterCount(); @@ -306,10 +306,13 @@ class FeatureTransformer { } // Convert input features - std::int32_t - transform(const Position& pos, OutputType* output, int bucket, bool psqtOnly) const { - update_accumulator(pos, psqtOnly); - update_accumulator(pos, psqtOnly); + std::int32_t transform(const Position& pos, + AccumulatorCaches::Cache* cache, + OutputType* output, + int bucket, + bool psqtOnly) const { + update_accumulator(pos, cache, psqtOnly); + update_accumulator(pos, cache, psqtOnly); const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()}; const auto& psqtAccumulation = (pos.state()->*accPtr).psqtAccumulation; @@ -371,9 +374,11 @@ class FeatureTransformer { return psqt; } // end of function transform() - void hint_common_access(const Position& pos, bool psqtOnly) const { - hint_common_access_for_perspective(pos, psqtOnly); - hint_common_access_for_perspective(pos, psqtOnly); + void hint_common_access(const Position& pos, + AccumulatorCaches::Cache* cache, + bool psqtOnly) const { + hint_common_access_for_perspective(pos, cache, psqtOnly); + hint_common_access_for_perspective(pos, cache, psqtOnly); } private: @@ -650,7 +655,161 @@ class FeatureTransformer { } template - void update_accumulator_refresh(const Position& pos, bool psqtOnly) const { + void update_accumulator_refresh_cache(const Position& pos, + AccumulatorCaches::Cache* cache) const { + assert(cache != nullptr); + + Square ksq = pos.square(Perspective); + + auto& entry = (*cache)[ksq]; + + auto& accumulator = pos.state()->*accPtr; + accumulator.computed[Perspective] = true; + accumulator.computedPSQT[Perspective] = true; + + FeatureSet::IndexList removed, added; + for (Color c : {WHITE, BLACK}) + { + for (PieceType pt = PAWN; pt <= KING; ++pt) + { + const Piece piece = make_piece(c, pt); + const Bitboard oldBB = + entry.byColorBB[Perspective][c] & entry.byTypeBB[Perspective][pt]; + const Bitboard newBB = pos.pieces(c, pt); + Bitboard toRemove = oldBB & ~newBB; + Bitboard toAdd = newBB & ~oldBB; + + while (toRemove) + { + Square sq = pop_lsb(toRemove); + removed.push_back(FeatureSet::make_index(sq, piece, ksq)); + } + while (toAdd) + { + Square sq = pop_lsb(toAdd); + added.push_back(FeatureSet::make_index(sq, piece, ksq)); + } + } + } + +#ifdef VECTOR + vec_t acc[NumRegs]; + psqt_vec_t psqt[NumPsqtRegs]; + + for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) + { + auto entryTile = + reinterpret_cast(&entry.accumulation[Perspective][j * TileHeight]); + for (IndexType k = 0; k < NumRegs; ++k) + acc[k] = entryTile[k]; + + for (int i = 0; i < int(added.size()); ++i) + { + IndexType index = added[i]; + const IndexType offset = HalfDimensions * index + j * TileHeight; + auto column = reinterpret_cast(&weights[offset]); + + for (unsigned k = 0; k < NumRegs; ++k) + acc[k] = vec_add_16(acc[k], column[k]); + } + for (int i = 0; i < int(removed.size()); ++i) + { + IndexType index = removed[i]; + const IndexType offset = HalfDimensions * index + j * TileHeight; + auto column = reinterpret_cast(&weights[offset]); + + for (unsigned k = 0; k < NumRegs; ++k) + acc[k] = vec_sub_16(acc[k], column[k]); + } + + for (IndexType k = 0; k < NumRegs; k++) + vec_store(&entryTile[k], acc[k]); + } + + for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j) + { + auto entryTilePsqt = reinterpret_cast( + &entry.psqtAccumulation[Perspective][j * PsqtTileHeight]); + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + psqt[k] = entryTilePsqt[k]; + + for (int i = 0; i < int(added.size()); ++i) + { + IndexType index = added[i]; + const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; + auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); + + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]); + } + for (int i = 0; i < int(removed.size()); ++i) + { + IndexType index = removed[i]; + const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; + auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); + + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + psqt[k] = vec_sub_psqt_32(psqt[k], columnPsqt[k]); + } + + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + vec_store_psqt(&entryTilePsqt[k], psqt[k]); + } + +#else + + for (const auto index : added) + { + const IndexType offset = HalfDimensions * index; + for (IndexType j = 0; j < HalfDimensions; ++j) + entry.accumulation[Perspective][j] += weights[offset + j]; + + for (std::size_t k = 0; k < PSQTBuckets; ++k) + entry.psqtAccumulation[Perspective][k] += psqtWeights[index * PSQTBuckets + k]; + } + for (const auto index : removed) + { + const IndexType offset = HalfDimensions * index; + for (IndexType j = 0; j < HalfDimensions; ++j) + entry.accumulation[Perspective][j] -= weights[offset + j]; + + for (std::size_t k = 0; k < PSQTBuckets; ++k) + entry.psqtAccumulation[Perspective][k] -= psqtWeights[index * PSQTBuckets + k]; + } + +#endif + + // The accumulator of the refresh entry has been updated. + // Now copy its content to the actual accumulator we were refreshing + + std::memcpy(accumulator.psqtAccumulation[Perspective], entry.psqtAccumulation[Perspective], + sizeof(int32_t) * PSQTBuckets); + + std::memcpy(accumulator.accumulation[Perspective], entry.accumulation[Perspective], + sizeof(BiasType) * HalfDimensions); + + for (Color c : {WHITE, BLACK}) + entry.byColorBB[Perspective][c] = pos.pieces(c); + + for (PieceType pt = PAWN; pt <= KING; ++pt) + entry.byTypeBB[Perspective][pt] = pos.pieces(pt); + } + + template + void + update_accumulator_refresh(const Position& pos, + [[maybe_unused]] AccumulatorCaches::Cache* cache, + bool psqtOnly) const { + + // When we are refreshing the accumulator of the big net, + // redirect to the version of refresh that uses the refresh table. + // Using the cache for the small net is not beneficial. + if constexpr (HalfDimensions == Eval::NNUE::TransformedFeatureDimensionsBig) + { + update_accumulator_refresh_cache(pos, cache); + return; + } + #ifdef VECTOR // Gcc-10.2 unnecessarily spills AVX2 registers if this array // is defined in the VECTOR code below, once in each branch @@ -764,7 +923,9 @@ class FeatureTransformer { } template - void hint_common_access_for_perspective(const Position& pos, bool psqtOnly) const { + void hint_common_access_for_perspective(const Position& pos, + AccumulatorCaches::Cache* cache, + bool psqtOnly) const { // Works like update_accumulator, but performs less work. // Updates ONLY the accumulator for pos. @@ -787,11 +948,13 @@ class FeatureTransformer { psqtOnly); } else - update_accumulator_refresh(pos, psqtOnly); + update_accumulator_refresh(pos, cache, psqtOnly); } template - void update_accumulator(const Position& pos, bool psqtOnly) const { + void update_accumulator(const Position& pos, + AccumulatorCaches::Cache* cache, + bool psqtOnly) const { auto [oldest_st, next] = try_find_computed_accumulator(pos, psqtOnly); @@ -813,9 +976,12 @@ class FeatureTransformer { psqtOnly); } else - update_accumulator_refresh(pos, psqtOnly); + update_accumulator_refresh(pos, cache, psqtOnly); } + template + friend struct AccumulatorCaches::Cache; + alignas(CacheLineSize) BiasType biases[HalfDimensions]; alignas(CacheLineSize) WeightType weights[HalfDimensions * InputDimensions]; alignas(CacheLineSize) PSQTWeightType psqtWeights[InputDimensions * PSQTBuckets]; diff --git a/src/nnue/nnue_misc.cpp b/src/nnue/nnue_misc.cpp index 3fa6e1b6180..51838fefa44 100644 --- a/src/nnue/nnue_misc.cpp +++ b/src/nnue/nnue_misc.cpp @@ -42,13 +42,15 @@ namespace Stockfish::Eval::NNUE { constexpr std::string_view PieceToChar(" PNBRQK pnbrqk"); -void hint_common_parent_position(const Position& pos, const Networks& networks) { +void hint_common_parent_position(const Position& pos, + const Networks& networks, + AccumulatorCaches& caches) { int simpleEvalAbs = std::abs(simple_eval(pos, pos.side_to_move())); if (simpleEvalAbs > Eval::SmallNetThreshold) - networks.small.hint_common_access(pos, simpleEvalAbs > Eval::PsqtOnlyThreshold); + networks.small.hint_common_access(pos, nullptr, simpleEvalAbs > Eval::PsqtOnlyThreshold); else - networks.big.hint_common_access(pos, false); + networks.big.hint_common_access(pos, &caches.big, false); } namespace { @@ -104,7 +106,8 @@ void format_cp_aligned_dot(Value v, std::stringstream& stream, const Position& p // Returns a string with the value of each piece on a board, // and a table for (PSQT, Layers) values bucket by bucket. -std::string trace(Position& pos, const Eval::NNUE::Networks& networks) { +std::string +trace(Position& pos, const Eval::NNUE::Networks& networks, Eval::NNUE::AccumulatorCaches& caches) { std::stringstream ss; @@ -130,7 +133,7 @@ std::string trace(Position& pos, const Eval::NNUE::Networks& networks) { // We estimate the value of each piece by doing a differential evaluation from // the current base eval, simulating the removal of the piece from its square. - Value base = networks.big.evaluate(pos); + Value base = networks.big.evaluate(pos, &caches.big); base = pos.side_to_move() == WHITE ? base : -base; for (File f = FILE_A; f <= FILE_H; ++f) @@ -149,7 +152,7 @@ std::string trace(Position& pos, const Eval::NNUE::Networks& networks) { st->accumulatorBig.computedPSQT[WHITE] = st->accumulatorBig.computedPSQT[BLACK] = false; - Value eval = networks.big.evaluate(pos); + Value eval = networks.big.evaluate(pos, &caches.big); eval = pos.side_to_move() == WHITE ? eval : -eval; v = base - eval; @@ -167,7 +170,7 @@ std::string trace(Position& pos, const Eval::NNUE::Networks& networks) { ss << board[row] << '\n'; ss << '\n'; - auto t = networks.big.trace_evaluate(pos); + auto t = networks.big.trace_evaluate(pos, &caches.big); ss << " NNUE network contributions " << (pos.side_to_move() == WHITE ? "(White to move)" : "(Black to move)") << std::endl diff --git a/src/nnue/nnue_misc.h b/src/nnue/nnue_misc.h index 5eab02184c6..27a93f88435 100644 --- a/src/nnue/nnue_misc.h +++ b/src/nnue/nnue_misc.h @@ -50,12 +50,13 @@ struct NnueEvalTrace { std::size_t correctBucket; }; - struct Networks; +struct AccumulatorCaches; - -std::string trace(Position& pos, const Networks& networks); -void hint_common_parent_position(const Position& pos, const Networks& networks); +std::string trace(Position& pos, const Networks& networks, AccumulatorCaches& caches); +void hint_common_parent_position(const Position& pos, + const Networks& networks, + AccumulatorCaches& caches); } // namespace Stockfish::Eval::NNUE } // namespace Stockfish diff --git a/src/search.cpp b/src/search.cpp index 183b7bcee5d..893daab20e6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -33,6 +33,8 @@ #include "misc.h" #include "movegen.h" #include "movepick.h" +#include "nnue/network.h" +#include "nnue/nnue_accumulator.h" #include "nnue/nnue_common.h" #include "nnue/nnue_misc.h" #include "position.h" @@ -135,6 +137,7 @@ Search::Worker::Worker(SharedState& sharedState, // Unpack the SharedState struct into member variables thread_idx(thread_id), manager(std::move(sm)), + refreshTable(), options(sharedState.options), threads(sharedState.threads), tt(sharedState.tt), @@ -143,6 +146,10 @@ Search::Worker::Worker(SharedState& sharedState, } void Search::Worker::start_searching() { + + // Initialize accumulator refresh entries + refreshTable.clear(networks); + // Non-main threads go directly to iterative_deepening() if (!is_mainthread()) { @@ -564,7 +571,7 @@ Value Search::Worker::search( if (threads.stop.load(std::memory_order_relaxed) || pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) return (ss->ply >= MAX_PLY && !ss->inCheck) - ? evaluate(networks, pos, thisThread->optimism[us]) + ? evaluate(networks, pos, refreshTable, thisThread->optimism[us]) : value_draw(thisThread->nodes); // Step 3. Mate distance pruning. Even if we mate at the next move our score @@ -698,7 +705,7 @@ Value Search::Worker::search( { // Providing the hint that this node's accumulator will be used often // brings significant Elo gain (~13 Elo). - Eval::NNUE::hint_common_parent_position(pos, networks); + Eval::NNUE::hint_common_parent_position(pos, networks, refreshTable); unadjustedStaticEval = eval = ss->staticEval; } else if (ss->ttHit) @@ -706,9 +713,9 @@ Value Search::Worker::search( // Never assume anything about values stored in TT unadjustedStaticEval = tte->eval(); if (unadjustedStaticEval == VALUE_NONE) - unadjustedStaticEval = evaluate(networks, pos, thisThread->optimism[us]); + unadjustedStaticEval = evaluate(networks, pos, refreshTable, thisThread->optimism[us]); else if (PvNode) - Eval::NNUE::hint_common_parent_position(pos, networks); + Eval::NNUE::hint_common_parent_position(pos, networks, refreshTable); ss->staticEval = eval = to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); @@ -718,7 +725,7 @@ Value Search::Worker::search( } else { - unadjustedStaticEval = evaluate(networks, pos, thisThread->optimism[us]); + unadjustedStaticEval = evaluate(networks, pos, refreshTable, thisThread->optimism[us]); ss->staticEval = eval = to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); // Static evaluation is saved as it was before adjustment by correction history @@ -875,7 +882,7 @@ Value Search::Worker::search( } } - Eval::NNUE::hint_common_parent_position(pos, networks); + Eval::NNUE::hint_common_parent_position(pos, networks, refreshTable); } moves_loop: // When in check, search starts here @@ -1413,7 +1420,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, // Step 2. Check for an immediate draw or maximum ply reached if (pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) return (ss->ply >= MAX_PLY && !ss->inCheck) - ? evaluate(networks, pos, thisThread->optimism[us]) + ? evaluate(networks, pos, refreshTable, thisThread->optimism[us]) : VALUE_DRAW; assert(0 <= ss->ply && ss->ply < MAX_PLY); @@ -1445,7 +1452,8 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, // Never assume anything about values stored in TT unadjustedStaticEval = tte->eval(); if (unadjustedStaticEval == VALUE_NONE) - unadjustedStaticEval = evaluate(networks, pos, thisThread->optimism[us]); + unadjustedStaticEval = + evaluate(networks, pos, refreshTable, thisThread->optimism[us]); ss->staticEval = bestValue = to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); @@ -1458,7 +1466,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, { // In case of null move search, use previous static eval with a different sign unadjustedStaticEval = (ss - 1)->currentMove != Move::null() - ? evaluate(networks, pos, thisThread->optimism[us]) + ? evaluate(networks, pos, refreshTable, thisThread->optimism[us]) : -(ss - 1)->staticEval; ss->staticEval = bestValue = to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); diff --git a/src/search.h b/src/search.h index 3ceaf5ddd0b..0fd778b47e6 100644 --- a/src/search.h +++ b/src/search.h @@ -26,9 +26,9 @@ #include #include #include +#include #include #include -#include #include "misc.h" #include "movepick.h" @@ -37,6 +37,7 @@ #include "syzygy/tbprobe.h" #include "timeman.h" #include "types.h" +#include "nnue/nnue_accumulator.h" namespace Stockfish { @@ -301,6 +302,10 @@ class Worker { Tablebases::Config tbConfig; + // Used by NNUE + + Eval::NNUE::AccumulatorCaches refreshTable; + const OptionsMap& options; ThreadPool& threads; TranspositionTable& tt; From 886ed90ec3599cdf0dc4e7d07b0543a27028c6c0 Mon Sep 17 00:00:00 2001 From: xoto10 <23479932+xoto10@users.noreply.github.com> Date: Sun, 28 Apr 2024 16:27:40 +0100 Subject: [PATCH 1450/1766] Use less time on recaptures Credit for the idea goes to peregrine on discord. Passed STC 10+0.1: https://tests.stockfishchess.org/tests/view/662652623fe04ce4cefc48cf LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 75712 W: 19793 L: 19423 D: 36496 Ptnml(0-2): 258, 8487, 20023, 8803, 285 Passed LTC 60+0.6: https://tests.stockfishchess.org/tests/view/6627495e3fe04ce4cefc59b6 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 49788 W: 12743 L: 12404 D: 24641 Ptnml(0-2): 29, 5141, 14215, 5480, 29 The code was updated slightly and tested for non-regression against the original code at STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 41952 W: 10912 L: 10698 D: 20342 Ptnml(0-2): 133, 4825, 10835, 5061, 122 https://tests.stockfishchess.org/tests/view/662d84f56115ff6764c7e438 closes https://github.com/official-stockfish/Stockfish/pull/5189 Bench: 1836777 --- src/engine.cpp | 12 ++++++++++-- src/engine.h | 11 +++++++---- src/search.cpp | 7 ++++--- src/search.h | 4 ++-- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/engine.cpp b/src/engine.cpp index 4625e00a816..72a37ce9b0b 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -53,6 +53,7 @@ Engine::Engine(std::string path) : NN::NetworkBig({EvalFileDefaultNameBig, "None", ""}, NN::EmbeddedNNUEType::BIG), NN::NetworkSmall({EvalFileDefaultNameSmall, "None", ""}, NN::EmbeddedNNUEType::SMALL))) { pos.set(StartFEN, false, &states->back()); + capSq = SQ_NONE; } std::uint64_t Engine::perft(const std::string& fen, Depth depth, bool isChess960) { @@ -61,9 +62,10 @@ std::uint64_t Engine::perft(const std::string& fen, Depth depth, bool isChess960 return Benchmark::perft(fen, depth, isChess960); } -void Engine::go(const Search::LimitsType& limits) { +void Engine::go(Search::LimitsType& limits) { assert(limits.perft == 0); verify_networks(); + limits.capSq = capSq; threads.start_thinking(options, pos, states, limits); } @@ -102,6 +104,7 @@ void Engine::set_position(const std::string& fen, const std::vector states = StateListPtr(new std::deque(1)); pos.set(fen, options["UCI_Chess960"], &states->back()); + capSq = SQ_NONE; for (const auto& move : moves) { auto m = UCIEngine::to_move(pos, move); @@ -111,6 +114,11 @@ void Engine::set_position(const std::string& fen, const std::vector states->emplace_back(); pos.do_move(m, states->back()); + + capSq = SQ_NONE; + DirtyPiece& dp = states->back().dirtyPiece; + if (dp.dirty_num > 1 && dp.to[1] == SQ_NONE) + capSq = m.to_sq(); } } @@ -172,4 +180,4 @@ std::string Engine::visualize() const { return ss.str(); } -} \ No newline at end of file +} diff --git a/src/engine.h b/src/engine.h index 041f5678585..64a814cb4aa 100644 --- a/src/engine.h +++ b/src/engine.h @@ -20,24 +20,26 @@ #define ENGINE_H_INCLUDED #include +#include #include #include #include #include #include #include -#include #include "nnue/network.h" #include "position.h" #include "search.h" +#include "syzygy/tbprobe.h" // for Stockfish::Depth #include "thread.h" #include "tt.h" #include "ucioption.h" -#include "syzygy/tbprobe.h" // for Stockfish::Depth namespace Stockfish { +enum Square : int; + class Engine { public: using InfoShort = Search::InfoShort; @@ -50,7 +52,7 @@ class Engine { std::uint64_t perft(const std::string& fen, Depth depth, bool isChess960); // non blocking call to start searching - void go(const Search::LimitsType&); + void go(Search::LimitsType&); // non blocking call to stop searching void stop(); @@ -92,6 +94,7 @@ class Engine { Position pos; StateListPtr states; + Square capSq; OptionsMap options; ThreadPool threads; @@ -104,4 +107,4 @@ class Engine { } // namespace Stockfish -#endif // #ifndef ENGINE_H_INCLUDED \ No newline at end of file +#endif // #ifndef ENGINE_H_INCLUDED diff --git a/src/search.cpp b/src/search.cpp index 893daab20e6..396e5aa06c8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -54,8 +54,8 @@ using namespace Search; namespace { -static constexpr double EvalLevel[10] = {1.043, 1.017, 0.952, 1.009, 0.971, - 1.002, 0.992, 0.947, 1.046, 1.001}; +static constexpr double EvalLevel[10] = {0.981, 0.956, 0.895, 0.949, 0.913, + 0.942, 0.933, 0.890, 0.984, 0.941}; // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) { @@ -446,9 +446,10 @@ void Search::Worker::iterative_deepening() { double reduction = (1.48 + mainThread->previousTimeReduction) / (2.17 * timeReduction); double bestMoveInstability = 1 + 1.88 * totBestMoveChanges / threads.size(); int el = std::clamp((bestValue + 750) / 150, 0, 9); + double recapture = limits.capSq == rootMoves[0].pv[0].to_sq() ? 0.955 : 1.005; double totalTime = mainThread->tm.optimum() * fallingEval * reduction - * bestMoveInstability * EvalLevel[el]; + * bestMoveInstability * EvalLevel[el] * recapture; // Cap used time in case of a single legal move for a better viewer experience if (rootMoves.size() == 1) diff --git a/src/search.h b/src/search.h index 0fd778b47e6..9b3528c8741 100644 --- a/src/search.h +++ b/src/search.h @@ -109,8 +109,7 @@ struct RootMove { using RootMoves = std::vector; -// LimitsType struct stores information sent by GUI about available time to -// search the current move, maximum depth/time, or if we are in analysis mode. +// LimitsType struct stores information sent by the caller about the analysis required. struct LimitsType { // Init explicitly due to broken value-initialization of non POD in MSVC @@ -128,6 +127,7 @@ struct LimitsType { int movestogo, depth, mate, perft, infinite; uint64_t nodes; bool ponderMode; + Square capSq; }; From 3502c8ae426506453ca64e87e48d962b327c2356 Mon Sep 17 00:00:00 2001 From: Disservin Date: Thu, 25 Apr 2024 19:20:57 +0200 Subject: [PATCH 1451/1766] Fix missing initialization of AccumulatorCaches in Eval::trace Add a constructor to `AccumulatorCaches` instead of just calling `clear(networks)` to prevent similar issues from appearing in the future. fixes https://github.com/official-stockfish/Stockfish/issues/5190 closes https://github.com/official-stockfish/Stockfish/pull/5191 No functional change --- src/evaluate.cpp | 2 +- src/nnue/nnue_accumulator.h | 5 +++++ src/search.cpp | 4 ++-- src/search.h | 7 +++---- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index f5746ca5199..6e101e7830a 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -99,7 +99,7 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, // Trace scores are from white's point of view std::string Eval::trace(Position& pos, const Eval::NNUE::Networks& networks) { - auto caches = std::make_unique(); + auto caches = std::make_unique(networks); if (pos.checkers()) return "Final evaluation: none (in check)"; diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index 8d73dbef5ad..f65385688de 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -50,6 +50,11 @@ struct alignas(CacheLineSize) Accumulator { // is commonly referred to as "Finny Tables". struct AccumulatorCaches { + template + AccumulatorCaches(const Networks& networks) { + clear(networks); + } + template struct alignas(CacheLineSize) Cache { diff --git a/src/search.cpp b/src/search.cpp index 396e5aa06c8..11373707b34 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -137,11 +137,11 @@ Search::Worker::Worker(SharedState& sharedState, // Unpack the SharedState struct into member variables thread_idx(thread_id), manager(std::move(sm)), - refreshTable(), options(sharedState.options), threads(sharedState.threads), tt(sharedState.tt), - networks(sharedState.networks) { + networks(sharedState.networks), + refreshTable(networks) { clear(); } diff --git a/src/search.h b/src/search.h index 9b3528c8741..444e3b8bb1d 100644 --- a/src/search.h +++ b/src/search.h @@ -302,15 +302,14 @@ class Worker { Tablebases::Config tbConfig; - // Used by NNUE - - Eval::NNUE::AccumulatorCaches refreshTable; - const OptionsMap& options; ThreadPool& threads; TranspositionTable& tt; const Eval::NNUE::Networks& networks; + // Used by NNUE + Eval::NNUE::AccumulatorCaches refreshTable; + friend class Stockfish::ThreadPool; friend class SearchManager; }; From bc45cbc820a53a9fc405c06ca67bd7be3970344e Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 27 Apr 2024 18:09:45 +0200 Subject: [PATCH 1452/1766] Output some basic info about the used networks Adds size in memory as well as layer sizes as in info string NNUE evaluation using nn-ae6a388e4a1a.nnue (132MiB, (22528, 3072, 15, 32, 1)) info string NNUE evaluation using nn-baff1ede1f90.nnue (6MiB, (22528, 128, 15, 32, 1)) For example, the size in MiB is useful to keep the fishtest memory sizes up-to-date, the L1-L3 sizes give a useful hint about the architecture used. closes https://github.com/official-stockfish/Stockfish/pull/5193 No functional change --- src/nnue/network.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/nnue/network.cpp b/src/nnue/network.cpp index 656ad97a1e3..42320bae1ab 100644 --- a/src/nnue/network.cpp +++ b/src/nnue/network.cpp @@ -252,7 +252,11 @@ void Network::verify(std::string evalfilePath) const { exit(EXIT_FAILURE); } - sync_cout << "info string NNUE evaluation using " << evalfilePath << sync_endl; + size_t size = sizeof(*featureTransformer) + sizeof(*network) * LayerStacks; + sync_cout << "info string NNUE evaluation using " << evalfilePath << " (" + << size / (1024 * 1024) << "MiB, (" << featureTransformer->InputDimensions << ", " + << network[0]->TransformedFeatureDimensions << ", " << network[0]->FC_0_OUTPUTS + << ", " << network[0]->FC_1_OUTPUTS << ", 1))" << sync_endl; } From 940a3a7383f48cea7aacbbe335671aa0d3ead1ae Mon Sep 17 00:00:00 2001 From: mstembera Date: Thu, 25 Apr 2024 18:20:08 -0700 Subject: [PATCH 1453/1766] Cache small net w/ psqtOnly support Caching the small net in the same way as the big net allows them to share the same code path and completely removes update_accumulator_refresh(). STC: https://tests.stockfishchess.org/tests/view/662bfb5ed46f72253dcfed85 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 151712 W: 39252 L: 39158 D: 73302 Ptnml(0-2): 565, 17474, 39683, 17570, 564 closes https://github.com/official-stockfish/Stockfish/pull/5194 Bench: 1836777 --- src/evaluate.cpp | 2 +- src/nnue/network.cpp | 4 +- src/nnue/network.h | 2 +- src/nnue/nnue_accumulator.h | 6 +- src/nnue/nnue_feature_transformer.h | 263 ++++++++-------------------- src/nnue/nnue_misc.cpp | 2 +- 6 files changed, 86 insertions(+), 193 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 6e101e7830a..345925f6b2a 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -60,7 +60,7 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, int nnueComplexity; int v; - Value nnue = smallNet ? networks.small.evaluate(pos, nullptr, true, &nnueComplexity, psqtOnly) + Value nnue = smallNet ? networks.small.evaluate(pos, &caches.small, true, &nnueComplexity, psqtOnly) : networks.big.evaluate(pos, &caches.big, true, &nnueComplexity, false); const auto adjustEval = [&](int optDiv, int nnueDiv, int npmDiv, int pawnCountConstant, diff --git a/src/nnue/network.cpp b/src/nnue/network.cpp index 42320bae1ab..2eca18bd15d 100644 --- a/src/nnue/network.cpp +++ b/src/nnue/network.cpp @@ -263,8 +263,8 @@ void Network::verify(std::string evalfilePath) const { template void Network::hint_common_access(const Position& pos, AccumulatorCaches::Cache* cache, - bool psqtOnl) const { - featureTransformer->hint_common_access(pos, cache, psqtOnl); + bool psqtOnly) const { + featureTransformer->hint_common_access(pos, cache, psqtOnly); } template diff --git a/src/nnue/network.h b/src/nnue/network.h index df59732d955..053b7d19c82 100644 --- a/src/nnue/network.h +++ b/src/nnue/network.h @@ -62,7 +62,7 @@ class Network { void hint_common_access(const Position& pos, AccumulatorCaches::Cache* cache, - bool psqtOnl) const; + bool psqtOnly) const; void verify(std::string evalfilePath) const; NnueEvalTrace trace_evaluate(const Position& pos, diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index f65385688de..dd313958fe6 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -63,6 +63,7 @@ struct AccumulatorCaches { PSQTWeightType psqtAccumulation[COLOR_NB][PSQTBuckets]; Bitboard byColorBB[COLOR_NB][COLOR_NB]; Bitboard byTypeBB[COLOR_NB][PIECE_TYPE_NB]; + bool psqtOnly; // To initialize a refresh entry, we set all its bitboards empty, // so we put the biases in the accumulation, without any weights on top @@ -70,6 +71,7 @@ struct AccumulatorCaches { std::memset(byColorBB, 0, sizeof(byColorBB)); std::memset(byTypeBB, 0, sizeof(byTypeBB)); + psqtOnly = false; std::memcpy(accumulation[WHITE], biases, Size * sizeof(BiasType)); std::memcpy(accumulation[BLACK], biases, Size * sizeof(BiasType)); @@ -97,11 +99,11 @@ struct AccumulatorCaches { template void clear(const Networks& networks) { big.clear(networks.big); + small.clear(networks.small); } - // When adding a new cache for a network, i.e. the smallnet - // the appropriate condition must be added to FeatureTransformer::update_accumulator_refresh. Cache big; + Cache small; }; } // namespace Stockfish::Eval::NNUE diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 88f0e4031a4..60957ebeb77 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -656,75 +656,84 @@ class FeatureTransformer { template void update_accumulator_refresh_cache(const Position& pos, - AccumulatorCaches::Cache* cache) const { + AccumulatorCaches::Cache* cache, + bool psqtOnly) const { assert(cache != nullptr); Square ksq = pos.square(Perspective); - auto& entry = (*cache)[ksq]; - - auto& accumulator = pos.state()->*accPtr; - accumulator.computed[Perspective] = true; - accumulator.computedPSQT[Perspective] = true; - FeatureSet::IndexList removed, added; - for (Color c : {WHITE, BLACK}) + + if (entry.psqtOnly && !psqtOnly) { - for (PieceType pt = PAWN; pt <= KING; ++pt) + entry.clear(biases); + FeatureSet::append_active_indices(pos, added); + } + else + { + for (Color c : {WHITE, BLACK}) { - const Piece piece = make_piece(c, pt); - const Bitboard oldBB = - entry.byColorBB[Perspective][c] & entry.byTypeBB[Perspective][pt]; - const Bitboard newBB = pos.pieces(c, pt); - Bitboard toRemove = oldBB & ~newBB; - Bitboard toAdd = newBB & ~oldBB; - - while (toRemove) - { - Square sq = pop_lsb(toRemove); - removed.push_back(FeatureSet::make_index(sq, piece, ksq)); - } - while (toAdd) + for (PieceType pt = PAWN; pt <= KING; ++pt) { - Square sq = pop_lsb(toAdd); - added.push_back(FeatureSet::make_index(sq, piece, ksq)); + const Piece piece = make_piece(c, pt); + const Bitboard oldBB = + entry.byColorBB[Perspective][c] & entry.byTypeBB[Perspective][pt]; + const Bitboard newBB = pos.pieces(c, pt); + Bitboard toRemove = oldBB & ~newBB; + Bitboard toAdd = newBB & ~oldBB; + + while (toRemove) + { + Square sq = pop_lsb(toRemove); + removed.push_back(FeatureSet::make_index(sq, piece, ksq)); + } + while (toAdd) + { + Square sq = pop_lsb(toAdd); + added.push_back(FeatureSet::make_index(sq, piece, ksq)); + } } } } + auto& accumulator = pos.state()->*accPtr; + accumulator.computed[Perspective] = !psqtOnly; + accumulator.computedPSQT[Perspective] = true; + #ifdef VECTOR vec_t acc[NumRegs]; psqt_vec_t psqt[NumPsqtRegs]; - for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) - { - auto entryTile = - reinterpret_cast(&entry.accumulation[Perspective][j * TileHeight]); - for (IndexType k = 0; k < NumRegs; ++k) - acc[k] = entryTile[k]; - - for (int i = 0; i < int(added.size()); ++i) + if (!psqtOnly) + for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) { - IndexType index = added[i]; - const IndexType offset = HalfDimensions * index + j * TileHeight; - auto column = reinterpret_cast(&weights[offset]); + auto entryTile = + reinterpret_cast(&entry.accumulation[Perspective][j * TileHeight]); + for (IndexType k = 0; k < NumRegs; ++k) + acc[k] = entryTile[k]; - for (unsigned k = 0; k < NumRegs; ++k) - acc[k] = vec_add_16(acc[k], column[k]); - } - for (int i = 0; i < int(removed.size()); ++i) - { - IndexType index = removed[i]; - const IndexType offset = HalfDimensions * index + j * TileHeight; - auto column = reinterpret_cast(&weights[offset]); + for (int i = 0; i < int(added.size()); ++i) + { + IndexType index = added[i]; + const IndexType offset = HalfDimensions * index + j * TileHeight; + auto column = reinterpret_cast(&weights[offset]); - for (unsigned k = 0; k < NumRegs; ++k) - acc[k] = vec_sub_16(acc[k], column[k]); - } + for (unsigned k = 0; k < NumRegs; ++k) + acc[k] = vec_add_16(acc[k], column[k]); + } + for (int i = 0; i < int(removed.size()); ++i) + { + IndexType index = removed[i]; + const IndexType offset = HalfDimensions * index + j * TileHeight; + auto column = reinterpret_cast(&weights[offset]); - for (IndexType k = 0; k < NumRegs; k++) - vec_store(&entryTile[k], acc[k]); - } + for (unsigned k = 0; k < NumRegs; ++k) + acc[k] = vec_sub_16(acc[k], column[k]); + } + + for (IndexType k = 0; k < NumRegs; k++) + vec_store(&entryTile[k], acc[k]); + } for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j) { @@ -760,18 +769,24 @@ class FeatureTransformer { for (const auto index : added) { - const IndexType offset = HalfDimensions * index; - for (IndexType j = 0; j < HalfDimensions; ++j) - entry.accumulation[Perspective][j] += weights[offset + j]; + if (!psqtOnly) + { + const IndexType offset = HalfDimensions * index; + for (IndexType j = 0; j < HalfDimensions; ++j) + entry.accumulation[Perspective][j] += weights[offset + j]; + } for (std::size_t k = 0; k < PSQTBuckets; ++k) entry.psqtAccumulation[Perspective][k] += psqtWeights[index * PSQTBuckets + k]; } for (const auto index : removed) { - const IndexType offset = HalfDimensions * index; - for (IndexType j = 0; j < HalfDimensions; ++j) - entry.accumulation[Perspective][j] -= weights[offset + j]; + if (!psqtOnly) + { + const IndexType offset = HalfDimensions * index; + for (IndexType j = 0; j < HalfDimensions; ++j) + entry.accumulation[Perspective][j] -= weights[offset + j]; + } for (std::size_t k = 0; k < PSQTBuckets; ++k) entry.psqtAccumulation[Perspective][k] -= psqtWeights[index * PSQTBuckets + k]; @@ -782,144 +797,20 @@ class FeatureTransformer { // The accumulator of the refresh entry has been updated. // Now copy its content to the actual accumulator we were refreshing + if (!psqtOnly) + std::memcpy(accumulator.accumulation[Perspective], entry.accumulation[Perspective], + sizeof(BiasType) * HalfDimensions); + std::memcpy(accumulator.psqtAccumulation[Perspective], entry.psqtAccumulation[Perspective], sizeof(int32_t) * PSQTBuckets); - std::memcpy(accumulator.accumulation[Perspective], entry.accumulation[Perspective], - sizeof(BiasType) * HalfDimensions); - for (Color c : {WHITE, BLACK}) entry.byColorBB[Perspective][c] = pos.pieces(c); for (PieceType pt = PAWN; pt <= KING; ++pt) entry.byTypeBB[Perspective][pt] = pos.pieces(pt); - } - - template - void - update_accumulator_refresh(const Position& pos, - [[maybe_unused]] AccumulatorCaches::Cache* cache, - bool psqtOnly) const { - - // When we are refreshing the accumulator of the big net, - // redirect to the version of refresh that uses the refresh table. - // Using the cache for the small net is not beneficial. - if constexpr (HalfDimensions == Eval::NNUE::TransformedFeatureDimensionsBig) - { - update_accumulator_refresh_cache(pos, cache); - return; - } -#ifdef VECTOR - // Gcc-10.2 unnecessarily spills AVX2 registers if this array - // is defined in the VECTOR code below, once in each branch - vec_t acc[NumRegs]; - psqt_vec_t psqt[NumPsqtRegs]; -#endif - - // Refresh the accumulator - // Could be extracted to a separate function because it's done in 2 places, - // but it's unclear if compilers would correctly handle register allocation. - auto& accumulator = pos.state()->*accPtr; - accumulator.computed[Perspective] = !psqtOnly; - accumulator.computedPSQT[Perspective] = true; - FeatureSet::IndexList active; - FeatureSet::append_active_indices(pos, active); - -#ifdef VECTOR - if (!psqtOnly) - for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) - { - auto biasesTile = reinterpret_cast(&biases[j * TileHeight]); - for (IndexType k = 0; k < NumRegs; ++k) - acc[k] = biasesTile[k]; - - int i = 0; - for (; i < int(active.size()) - 1; i += 2) - { - IndexType index0 = active[i]; - IndexType index1 = active[i + 1]; - const IndexType offset0 = HalfDimensions * index0 + j * TileHeight; - const IndexType offset1 = HalfDimensions * index1 + j * TileHeight; - auto column0 = reinterpret_cast(&weights[offset0]); - auto column1 = reinterpret_cast(&weights[offset1]); - - for (unsigned k = 0; k < NumRegs; ++k) - acc[k] = vec_add_16(acc[k], vec_add_16(column0[k], column1[k])); - } - for (; i < int(active.size()); ++i) - { - IndexType index = active[i]; - const IndexType offset = HalfDimensions * index + j * TileHeight; - auto column = reinterpret_cast(&weights[offset]); - - for (unsigned k = 0; k < NumRegs; ++k) - acc[k] = vec_add_16(acc[k], column[k]); - } - - auto accTile = - reinterpret_cast(&accumulator.accumulation[Perspective][j * TileHeight]); - for (unsigned k = 0; k < NumRegs; k++) - vec_store(&accTile[k], acc[k]); - } - - for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j) - { - for (std::size_t k = 0; k < NumPsqtRegs; ++k) - psqt[k] = vec_zero_psqt(); - - int i = 0; - for (; i < int(active.size()) - 1; i += 2) - { - IndexType index0 = active[i]; - IndexType index1 = active[i + 1]; - const IndexType offset0 = PSQTBuckets * index0 + j * PsqtTileHeight; - const IndexType offset1 = PSQTBuckets * index1 + j * PsqtTileHeight; - auto columnPsqt0 = reinterpret_cast(&psqtWeights[offset0]); - auto columnPsqt1 = reinterpret_cast(&psqtWeights[offset1]); - - for (std::size_t k = 0; k < NumPsqtRegs; ++k) - psqt[k] = - vec_add_psqt_32(psqt[k], vec_add_psqt_32(columnPsqt0[k], columnPsqt1[k])); - } - for (; i < int(active.size()); ++i) - { - IndexType index = active[i]; - const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; - auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); - - for (std::size_t k = 0; k < NumPsqtRegs; ++k) - psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]); - } - - auto accTilePsqt = reinterpret_cast( - &accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); - for (std::size_t k = 0; k < NumPsqtRegs; ++k) - vec_store_psqt(&accTilePsqt[k], psqt[k]); - } - -#else - if (!psqtOnly) - std::memcpy(accumulator.accumulation[Perspective], biases, - HalfDimensions * sizeof(BiasType)); - - for (std::size_t k = 0; k < PSQTBuckets; ++k) - accumulator.psqtAccumulation[Perspective][k] = 0; - - for (const auto index : active) - { - if (!psqtOnly) - { - const IndexType offset = HalfDimensions * index; - for (IndexType j = 0; j < HalfDimensions; ++j) - accumulator.accumulation[Perspective][j] += weights[offset + j]; - } - - for (std::size_t k = 0; k < PSQTBuckets; ++k) - accumulator.psqtAccumulation[Perspective][k] += - psqtWeights[index * PSQTBuckets + k]; - } -#endif + entry.psqtOnly = psqtOnly; } template @@ -948,7 +839,7 @@ class FeatureTransformer { psqtOnly); } else - update_accumulator_refresh(pos, cache, psqtOnly); + update_accumulator_refresh_cache(pos, cache, psqtOnly); } template @@ -976,7 +867,7 @@ class FeatureTransformer { psqtOnly); } else - update_accumulator_refresh(pos, cache, psqtOnly); + update_accumulator_refresh_cache(pos, cache, psqtOnly); } template diff --git a/src/nnue/nnue_misc.cpp b/src/nnue/nnue_misc.cpp index 51838fefa44..e92dcc71086 100644 --- a/src/nnue/nnue_misc.cpp +++ b/src/nnue/nnue_misc.cpp @@ -48,7 +48,7 @@ void hint_common_parent_position(const Position& pos, int simpleEvalAbs = std::abs(simple_eval(pos, pos.side_to_move())); if (simpleEvalAbs > Eval::SmallNetThreshold) - networks.small.hint_common_access(pos, nullptr, simpleEvalAbs > Eval::PsqtOnlyThreshold); + networks.small.hint_common_access(pos, &caches.small, simpleEvalAbs > Eval::PsqtOnlyThreshold); else networks.big.hint_common_access(pos, &caches.big, false); } From a129c0695be921acfbb3f5c966eef756d0b6f843 Mon Sep 17 00:00:00 2001 From: mstembera Date: Sun, 28 Apr 2024 10:28:25 -0700 Subject: [PATCH 1454/1766] Combine remove and add in update_accumulator_refresh_cache() Combine remove and add in update_accumulator_refresh_cache(). Move remove before add to match other parts of the code. STC: https://tests.stockfishchess.org/tests/view/662d96dc6115ff6764c7f4ca LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 364032 W: 94421 L: 93624 D: 175987 Ptnml(0-2): 1261, 41983, 94811, 42620, 1341 closes https://github.com/official-stockfish/Stockfish/pull/5194 Bench: 1836777 --- src/evaluate.cpp | 5 +-- src/nnue/nnue_accumulator.h | 2 +- src/nnue/nnue_feature_transformer.h | 53 ++++++++++++++++++----------- src/nnue/nnue_misc.cpp | 3 +- 4 files changed, 39 insertions(+), 24 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 345925f6b2a..fe6b83aa111 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -60,8 +60,9 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, int nnueComplexity; int v; - Value nnue = smallNet ? networks.small.evaluate(pos, &caches.small, true, &nnueComplexity, psqtOnly) - : networks.big.evaluate(pos, &caches.big, true, &nnueComplexity, false); + Value nnue = smallNet + ? networks.small.evaluate(pos, &caches.small, true, &nnueComplexity, psqtOnly) + : networks.big.evaluate(pos, &caches.big, true, &nnueComplexity, false); const auto adjustEval = [&](int optDiv, int nnueDiv, int npmDiv, int pawnCountConstant, int pawnCountMul, int npmConstant, int evalDiv, diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index dd313958fe6..a2b3b98988e 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -102,7 +102,7 @@ struct AccumulatorCaches { small.clear(networks.small); } - Cache big; + Cache big; Cache small; }; diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 60957ebeb77..6b3f78a9a4b 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -660,8 +660,8 @@ class FeatureTransformer { bool psqtOnly) const { assert(cache != nullptr); - Square ksq = pos.square(Perspective); - auto& entry = (*cache)[ksq]; + Square ksq = pos.square(Perspective); + auto& entry = (*cache)[ksq]; FeatureSet::IndexList removed, added; if (entry.psqtOnly && !psqtOnly) @@ -712,16 +712,20 @@ class FeatureTransformer { for (IndexType k = 0; k < NumRegs; ++k) acc[k] = entryTile[k]; - for (int i = 0; i < int(added.size()); ++i) + int i0 = 0; + for (; i0 < int(std::min(removed.size(), added.size())); ++i0) { - IndexType index = added[i]; - const IndexType offset = HalfDimensions * index + j * TileHeight; - auto column = reinterpret_cast(&weights[offset]); + IndexType indexR = removed[i0]; + const IndexType offsetR = HalfDimensions * indexR + j * TileHeight; + auto columnR = reinterpret_cast(&weights[offsetR]); + IndexType indexA = added[i0]; + const IndexType offsetA = HalfDimensions * indexA + j * TileHeight; + auto columnA = reinterpret_cast(&weights[offsetA]); for (unsigned k = 0; k < NumRegs; ++k) - acc[k] = vec_add_16(acc[k], column[k]); + acc[k] = vec_add_16(vec_sub_16(acc[k], columnR[k]), columnA[k]); } - for (int i = 0; i < int(removed.size()); ++i) + for (int i = i0; i < int(removed.size()); ++i) { IndexType index = removed[i]; const IndexType offset = HalfDimensions * index + j * TileHeight; @@ -730,6 +734,15 @@ class FeatureTransformer { for (unsigned k = 0; k < NumRegs; ++k) acc[k] = vec_sub_16(acc[k], column[k]); } + for (int i = i0; i < int(added.size()); ++i) + { + IndexType index = added[i]; + const IndexType offset = HalfDimensions * index + j * TileHeight; + auto column = reinterpret_cast(&weights[offset]); + + for (unsigned k = 0; k < NumRegs; ++k) + acc[k] = vec_add_16(acc[k], column[k]); + } for (IndexType k = 0; k < NumRegs; k++) vec_store(&entryTile[k], acc[k]); @@ -742,23 +755,23 @@ class FeatureTransformer { for (std::size_t k = 0; k < NumPsqtRegs; ++k) psqt[k] = entryTilePsqt[k]; - for (int i = 0; i < int(added.size()); ++i) + for (int i = 0; i < int(removed.size()); ++i) { - IndexType index = added[i]; + IndexType index = removed[i]; const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); for (std::size_t k = 0; k < NumPsqtRegs; ++k) - psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]); + psqt[k] = vec_sub_psqt_32(psqt[k], columnPsqt[k]); } - for (int i = 0; i < int(removed.size()); ++i) + for (int i = 0; i < int(added.size()); ++i) { - IndexType index = removed[i]; + IndexType index = added[i]; const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); for (std::size_t k = 0; k < NumPsqtRegs; ++k) - psqt[k] = vec_sub_psqt_32(psqt[k], columnPsqt[k]); + psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]); } for (std::size_t k = 0; k < NumPsqtRegs; ++k) @@ -767,29 +780,29 @@ class FeatureTransformer { #else - for (const auto index : added) + for (const auto index : removed) { if (!psqtOnly) { const IndexType offset = HalfDimensions * index; for (IndexType j = 0; j < HalfDimensions; ++j) - entry.accumulation[Perspective][j] += weights[offset + j]; + entry.accumulation[Perspective][j] -= weights[offset + j]; } for (std::size_t k = 0; k < PSQTBuckets; ++k) - entry.psqtAccumulation[Perspective][k] += psqtWeights[index * PSQTBuckets + k]; + entry.psqtAccumulation[Perspective][k] -= psqtWeights[index * PSQTBuckets + k]; } - for (const auto index : removed) + for (const auto index : added) { if (!psqtOnly) { const IndexType offset = HalfDimensions * index; for (IndexType j = 0; j < HalfDimensions; ++j) - entry.accumulation[Perspective][j] -= weights[offset + j]; + entry.accumulation[Perspective][j] += weights[offset + j]; } for (std::size_t k = 0; k < PSQTBuckets; ++k) - entry.psqtAccumulation[Perspective][k] -= psqtWeights[index * PSQTBuckets + k]; + entry.psqtAccumulation[Perspective][k] += psqtWeights[index * PSQTBuckets + k]; } #endif diff --git a/src/nnue/nnue_misc.cpp b/src/nnue/nnue_misc.cpp index e92dcc71086..21685d0f2a3 100644 --- a/src/nnue/nnue_misc.cpp +++ b/src/nnue/nnue_misc.cpp @@ -48,7 +48,8 @@ void hint_common_parent_position(const Position& pos, int simpleEvalAbs = std::abs(simple_eval(pos, pos.side_to_move())); if (simpleEvalAbs > Eval::SmallNetThreshold) - networks.small.hint_common_access(pos, &caches.small, simpleEvalAbs > Eval::PsqtOnlyThreshold); + networks.small.hint_common_access(pos, &caches.small, + simpleEvalAbs > Eval::PsqtOnlyThreshold); else networks.big.hint_common_access(pos, &caches.big, false); } From 834e8ff619b212baf402c3922f8fde9af979cd0c Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Sun, 28 Apr 2024 08:53:28 +0800 Subject: [PATCH 1455/1766] Penalise the TT move in multicut Passed STC: LLR: 2.99 (-2.94,2.94) <0.00,2.00> Total: 185504 W: 48079 L: 47533 D: 89892 Ptnml(0-2): 716, 21866, 46988, 22520, 662 https://tests.stockfishchess.org/tests/view/662d9e1d6115ff6764c7f83d Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 75612 W: 19351 L: 18948 D: 37313 Ptnml(0-2): 46, 8363, 20592, 8752, 53 https://tests.stockfishchess.org/tests/view/662dc9dc6115ff6764c80fea closes https://github.com/official-stockfish/Stockfish/pull/5195 Bench: 1415435 --- src/search.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 11373707b34..ad59b35a545 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1067,7 +1067,12 @@ Value Search::Worker::search( // we assume this expected cut-node is not singular (multiple moves fail high), // and we can prune the whole subtree by returning a softbound. else if (singularBeta >= beta) + { + if (!ttCapture) + update_quiet_stats(pos, ss, *this, ttMove, -stat_malus(depth)); + return singularBeta; + } // Negative extensions // If other moves failed high over (ttValue - margin) without the ttMove on a reduced search, From 48a3b7c0ee7d32441a5a4519c85bd1e93e467f6e Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Sun, 28 Apr 2024 16:04:28 +0200 Subject: [PATCH 1456/1766] Simplify non-pawn material divisor to a constant Passed STC: https://tests.stockfishchess.org/tests/view/662942603fe04ce4cefc7aba LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 272832 W: 70456 L: 70497 D: 131879 Ptnml(0-2): 1020, 32619, 69154, 32628, 995 Passed LTC: https://tests.stockfishchess.org/tests/view/662dfe3b6115ff6764c829eb LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 100254 W: 25446 L: 25303 D: 49505 Ptnml(0-2): 121, 11292, 27166, 11419, 129 closes https://github.com/official-stockfish/Stockfish/pull/5198 Bench: 1544645 --- src/evaluate.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index fe6b83aa111..1d41f3a266b 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -64,14 +64,14 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, ? networks.small.evaluate(pos, &caches.small, true, &nnueComplexity, psqtOnly) : networks.big.evaluate(pos, &caches.big, true, &nnueComplexity, false); - const auto adjustEval = [&](int optDiv, int nnueDiv, int npmDiv, int pawnCountConstant, - int pawnCountMul, int npmConstant, int evalDiv, - int shufflingConstant, int shufflingDiv) { + const auto adjustEval = [&](int optDiv, int nnueDiv, int pawnCountConstant, int pawnCountMul, + int npmConstant, int evalDiv, int shufflingConstant, + int shufflingDiv) { // Blend optimism and eval with nnue complexity and material imbalance optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / optDiv; nnue -= nnue * (nnueComplexity * 5 / 3) / nnueDiv; - int npm = pos.non_pawn_material() / npmDiv; + int npm = pos.non_pawn_material() / 64; v = (nnue * (npm + pawnCountConstant + pawnCountMul * pos.count()) + optimism * (npmConstant + npm)) / evalDiv; @@ -82,11 +82,11 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, }; if (!smallNet) - adjustEval(524, 32395, 66, 942, 11, 139, 1058, 178, 204); + adjustEval(524, 32395, 942, 11, 139, 1058, 178, 204); else if (psqtOnly) - adjustEval(517, 32857, 65, 908, 7, 155, 1006, 224, 238); + adjustEval(517, 32857, 908, 7, 155, 1006, 224, 238); else - adjustEval(515, 32793, 63, 944, 9, 140, 1067, 206, 206); + adjustEval(515, 32793, 944, 9, 140, 1067, 206, 206); // Guarantee evaluation does not hit the tablebase range v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); From 0fe64286457549d2f80cd7792088375aaa9bee55 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Sun, 28 Apr 2024 16:53:47 +0200 Subject: [PATCH 1457/1766] More reduction at cut nodes which are not a former PV node But the tt move and first killer are excluded. This idea is based on following LMR condition tuning https://tests.stockfishchess.org/tests/view/66228bed3fe04ce4cefc0c71 by using only the two largest terms P[0] and P[1]. Passed STC: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 173248 W: 45091 L: 44565 D: 83592 Ptnml(0-2): 693, 20534, 43673, 21002, 722 https://tests.stockfishchess.org/tests/view/6629603b3fe04ce4cefc7d37 Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 722394 W: 183231 L: 181487 D: 357676 Ptnml(0-2): 462, 80650, 197252, 82348, 485 https://tests.stockfishchess.org/tests/view/662cbe45d46f72253dcff7bf closes https://github.com/official-stockfish/Stockfish/pull/5199 Bench: 1619613 --- src/search.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index ad59b35a545..3718c37813b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1123,6 +1123,9 @@ Value Search::Worker::search( if (ss->ttPv) r -= 1 + (ttValue > alpha) + (tte->depth() >= depth); + else if (cutNode && move != ttMove && move != ss->killers[0]) + r++; + // Increase reduction for cut nodes (~4 Elo) if (cutNode) r += 2 - (tte->depth() >= depth && ss->ttPv); From 5d720325596699ceba2743776cb39f9cea1754f5 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Sat, 20 Apr 2024 00:29:01 -0500 Subject: [PATCH 1458/1766] Use capture history to better judge which sacrifices to explore This idea has been bouncing around a while. @Vizvezdenec tried it a couple years ago in Stockfish without results, but its recent arrival in Ethereal inspired him and thence me to try it afresh in Stockfish. (Also factor out the now-common code with futpruning for captures.) STC: https://tests.stockfishchess.org/tests/view/662355bc3fe04ce4cefc18ac LLR: 2.92 (-2.94,2.94) <0.00,2.00> Total: 45760 W: 11970 L: 11640 D: 22150 Ptnml(0-2): 124, 5371, 11625, 5571, 189 LTC: https://tests.stockfishchess.org/tests/view/662dda396115ff6764c817c9 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 243828 W: 62042 L: 61287 D: 120499 Ptnml(0-2): 211, 27202, 66329, 27965, 207 closes https://github.com/official-stockfish/Stockfish/pull/5200 Bench: 1480008 --- src/search.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 3718c37813b..e4f170be61d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -967,20 +967,22 @@ Value Search::Worker::search( if (capture || givesCheck) { + Piece capturedPiece = pos.piece_on(move.to_sq()); + int captHist = + thisThread->captureHistory[movedPiece][move.to_sq()][type_of(capturedPiece)]; + // Futility pruning for captures (~2 Elo) if (!givesCheck && lmrDepth < 7 && !ss->inCheck) { - Piece capturedPiece = pos.piece_on(move.to_sq()); - Value futilityValue = - ss->staticEval + 285 + 277 * lmrDepth + PieceValue[capturedPiece] - + thisThread->captureHistory[movedPiece][move.to_sq()][type_of(capturedPiece)] - / 7; + Value futilityValue = ss->staticEval + 285 + 277 * lmrDepth + + PieceValue[capturedPiece] + captHist / 7; if (futilityValue <= alpha) continue; } // SEE based pruning for captures and checks (~11 Elo) - if (!pos.see_ge(move, -203 * depth)) + int seeHist = std::clamp(captHist / 32, -199 * depth, 199 * depth); + if (!pos.see_ge(move, -203 * depth - seeHist)) continue; } else From eb20de36c05b4101af37b2bf3783c570a47bb1cc Mon Sep 17 00:00:00 2001 From: Ciekce <44617491+Ciekce@users.noreply.github.com> Date: Mon, 29 Apr 2024 01:45:56 +0100 Subject: [PATCH 1459/1766] Avoid unnecessary creation of accumulator cache Saves a (currently) 800 KB allocation and deallocation when running `eval`, not particularly significant and zero impact on play but not necessary either. closes https://github.com/official-stockfish/Stockfish/pull/5201 No functional change --- AUTHORS | 1 + src/evaluate.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index abae401c1ef..36b2b6f7942 100644 --- a/AUTHORS +++ b/AUTHORS @@ -46,6 +46,7 @@ Bryan Cross (crossbr) candirufish Chess13234 Chris Cain (ceebo) +Ciekce clefrks Clemens L. (rn5f107s2) Cody Ho (aesrentai) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 1d41f3a266b..e3aa249ca41 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -100,11 +100,11 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, // Trace scores are from white's point of view std::string Eval::trace(Position& pos, const Eval::NNUE::Networks& networks) { - auto caches = std::make_unique(networks); - if (pos.checkers()) return "Final evaluation: none (in check)"; + auto caches = std::make_unique(networks); + std::stringstream ss; ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2); ss << '\n' << NNUE::trace(pos, networks, *caches) << '\n'; From 6a9b8a0c7b913b9d4c4474bae7804184d20e8c4a Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Sun, 28 Apr 2024 16:33:59 +0800 Subject: [PATCH 1460/1766] Optimise NNUE Accumulator updates Passed STC: https://tests.stockfishchess.org/tests/view/662e3c6a5e9274400985a741 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 86176 W: 22284 L: 21905 D: 41987 Ptnml(0-2): 254, 9572, 23051, 9963, 248 closes https://github.com/official-stockfish/Stockfish/pull/5202 No functional change --- src/nnue/nnue_feature_transformer.h | 76 ++++++++++++++--------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 6b3f78a9a4b..402a47a815d 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -404,19 +404,25 @@ class FeatureTransformer { return {st, next}; } - // NOTE: The parameter states_to_update is an array of position states, ending with nullptr. + // NOTE: The parameter states_to_update is an array of position states. // All states must be sequential, that is states_to_update[i] must either be reachable - // by repeatedly applying ->previous from states_to_update[i+1] or - // states_to_update[i] == nullptr. + // by repeatedly applying ->previous from states_to_update[i+1]. // computed_st must be reachable by repeatedly applying ->previous on - // states_to_update[0], if not nullptr. + // states_to_update[0]. template void update_accumulator_incremental(const Position& pos, StateInfo* computed_st, StateInfo* states_to_update[N], bool psqtOnly) const { static_assert(N > 0); - assert(states_to_update[N - 1] == nullptr); + assert([&]() { + for (size_t i = 0; i < N; ++i) + { + if (states_to_update[i] == nullptr) + return false; + } + return true; + }()); #ifdef VECTOR // Gcc-10.2 unnecessarily spills AVX2 registers if this array @@ -425,11 +431,7 @@ class FeatureTransformer { psqt_vec_t psqt[NumPsqtRegs]; #endif - if (states_to_update[0] == nullptr) - return; - // Update incrementally going back through states_to_update. - // Gather all features to be updated. const Square ksq = pos.square(Perspective); @@ -437,28 +439,18 @@ class FeatureTransformer { // That might depend on the feature set and generally relies on the // feature set's update cost calculation to be correct and never allow // updates with more added/removed features than MaxActiveDimensions. - FeatureSet::IndexList removed[N - 1], added[N - 1]; + FeatureSet::IndexList removed[N], added[N]; + for (int i = N - 1; i >= 0; --i) { - int i = - N - - 2; // Last potential state to update. Skip last element because it must be nullptr. - while (states_to_update[i] == nullptr) - --i; - - StateInfo* st2 = states_to_update[i]; - - for (; i >= 0; --i) - { - (states_to_update[i]->*accPtr).computed[Perspective] = !psqtOnly; - (states_to_update[i]->*accPtr).computedPSQT[Perspective] = true; + (states_to_update[i]->*accPtr).computed[Perspective] = !psqtOnly; + (states_to_update[i]->*accPtr).computedPSQT[Perspective] = true; - const StateInfo* end_state = i == 0 ? computed_st : states_to_update[i - 1]; + const StateInfo* end_state = i == 0 ? computed_st : states_to_update[i - 1]; - for (; st2 != end_state; st2 = st2->previous) - FeatureSet::append_changed_indices(ksq, st2->dirtyPiece, - removed[i], added[i]); - } + for (StateInfo* st2 = states_to_update[i]; st2 != end_state; st2 = st2->previous) + FeatureSet::append_changed_indices(ksq, st2->dirtyPiece, removed[i], + added[i]); } StateInfo* st = computed_st; @@ -466,8 +458,7 @@ class FeatureTransformer { // Now update the accumulators listed in states_to_update[], where the last element is a sentinel. #ifdef VECTOR - if (states_to_update[1] == nullptr && (removed[0].size() == 1 || removed[0].size() == 2) - && added[0].size() == 1) + if (N == 1 && (removed[0].size() == 1 || removed[0].size() == 2) && added[0].size() == 1) { assert(states_to_update[0]); @@ -541,7 +532,7 @@ class FeatureTransformer { for (IndexType k = 0; k < NumRegs; ++k) acc[k] = vec_load(&accTileIn[k]); - for (IndexType i = 0; states_to_update[i]; ++i) + for (IndexType i = 0; i < N; ++i) { // Difference calculation for the deactivated features for (const auto index : removed[i]) @@ -578,7 +569,7 @@ class FeatureTransformer { for (std::size_t k = 0; k < NumPsqtRegs; ++k) psqt[k] = vec_load_psqt(&accTilePsqtIn[k]); - for (IndexType i = 0; states_to_update[i]; ++i) + for (IndexType i = 0; i < N; ++i) { // Difference calculation for the deactivated features for (const auto index : removed[i]) @@ -608,7 +599,7 @@ class FeatureTransformer { } } #else - for (IndexType i = 0; states_to_update[i]; ++i) + for (IndexType i = 0; i < N; ++i) { if (!psqtOnly) std::memcpy((states_to_update[i]->*accPtr).accumulation[Perspective], @@ -847,8 +838,8 @@ class FeatureTransformer { || (psqtOnly && (oldest_st->*accPtr).computedPSQT[Perspective])) { // Only update current position accumulator to minimize work. - StateInfo* states_to_update[2] = {pos.state(), nullptr}; - update_accumulator_incremental(pos, oldest_st, states_to_update, + StateInfo* states_to_update[1] = {pos.state()}; + update_accumulator_incremental(pos, oldest_st, states_to_update, psqtOnly); } else @@ -873,11 +864,20 @@ class FeatureTransformer { // 1. for the current position // 2. the next accumulator after the computed one // The heuristic may change in the future. - StateInfo* states_to_update[3] = {next, next == pos.state() ? nullptr : pos.state(), - nullptr}; + if (next == pos.state()) + { + StateInfo* states_to_update[1] = {next}; - update_accumulator_incremental(pos, oldest_st, states_to_update, - psqtOnly); + update_accumulator_incremental(pos, oldest_st, states_to_update, + psqtOnly); + } + else + { + StateInfo* states_to_update[2] = {next, pos.state()}; + + update_accumulator_incremental(pos, oldest_st, states_to_update, + psqtOnly); + } } else update_accumulator_refresh_cache(pos, cache, psqtOnly); From be142337d843ef3afc675e27628ab8e896c32cce Mon Sep 17 00:00:00 2001 From: mstembera Date: Mon, 29 Apr 2024 20:37:54 -0700 Subject: [PATCH 1461/1766] Accumulator cache bugfix and cleanup STC: https://tests.stockfishchess.org/tests/view/663068913a05f1bf7a511dc2 LLR: 2.98 (-2.94,2.94) <-1.75,0.25> Total: 70304 W: 18211 L: 18026 D: 34067 Ptnml(0-2): 232, 7966, 18582, 8129, 243 1) Fixes a bug introduced in https://github.com/official-stockfish/Stockfish/pull/5194. Only one psqtOnly flag was used for two perspectives which was causing wrong entries to be cleared and marked. 2) The finny caches should be cleared like histories and not at the start of every search. closes https://github.com/official-stockfish/Stockfish/pull/5203 No functional change --- src/nnue/nnue_accumulator.h | 28 ++++++++++++--------------- src/nnue/nnue_feature_transformer.h | 30 ++++++++++++++--------------- src/search.cpp | 5 ++--- 3 files changed, 28 insertions(+), 35 deletions(-) diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index a2b3b98988e..179feba553e 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -59,31 +59,27 @@ struct AccumulatorCaches { struct alignas(CacheLineSize) Cache { struct alignas(CacheLineSize) Entry { - BiasType accumulation[COLOR_NB][Size]; - PSQTWeightType psqtAccumulation[COLOR_NB][PSQTBuckets]; - Bitboard byColorBB[COLOR_NB][COLOR_NB]; - Bitboard byTypeBB[COLOR_NB][PIECE_TYPE_NB]; + BiasType accumulation[Size]; + PSQTWeightType psqtAccumulation[PSQTBuckets]; + Bitboard byColorBB[COLOR_NB]; + Bitboard byTypeBB[PIECE_TYPE_NB]; bool psqtOnly; // To initialize a refresh entry, we set all its bitboards empty, // so we put the biases in the accumulation, without any weights on top void clear(const BiasType* biases) { - std::memset(byColorBB, 0, sizeof(byColorBB)); - std::memset(byTypeBB, 0, sizeof(byTypeBB)); - psqtOnly = false; - - std::memcpy(accumulation[WHITE], biases, Size * sizeof(BiasType)); - std::memcpy(accumulation[BLACK], biases, Size * sizeof(BiasType)); - - std::memset(psqtAccumulation, 0, sizeof(psqtAccumulation)); + std::memcpy(accumulation, biases, sizeof(accumulation)); + std::memset((uint8_t*) this + offsetof(Entry, psqtAccumulation), 0, + sizeof(Entry) - offsetof(Entry, psqtAccumulation)); } }; template void clear(const Network& network) { - for (auto& entry : entries) - entry.clear(network.featureTransformer->biases); + for (auto& entries1D : entries) + for (auto& entry : entries1D) + entry.clear(network.featureTransformer->biases); } void clear(const BiasType* biases) { @@ -91,9 +87,9 @@ struct AccumulatorCaches { entry.clear(biases); } - Entry& operator[](Square sq) { return entries[sq]; } + std::array& operator[](Square sq) { return entries[sq]; } - std::array entries; + std::array, SQUARE_NB> entries; }; template diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 402a47a815d..4647ecd066d 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -652,7 +652,7 @@ class FeatureTransformer { assert(cache != nullptr); Square ksq = pos.square(Perspective); - auto& entry = (*cache)[ksq]; + auto& entry = (*cache)[ksq][Perspective]; FeatureSet::IndexList removed, added; if (entry.psqtOnly && !psqtOnly) @@ -666,9 +666,8 @@ class FeatureTransformer { { for (PieceType pt = PAWN; pt <= KING; ++pt) { - const Piece piece = make_piece(c, pt); - const Bitboard oldBB = - entry.byColorBB[Perspective][c] & entry.byTypeBB[Perspective][pt]; + const Piece piece = make_piece(c, pt); + const Bitboard oldBB = entry.byColorBB[c] & entry.byTypeBB[pt]; const Bitboard newBB = pos.pieces(c, pt); Bitboard toRemove = oldBB & ~newBB; Bitboard toAdd = newBB & ~oldBB; @@ -698,8 +697,7 @@ class FeatureTransformer { if (!psqtOnly) for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) { - auto entryTile = - reinterpret_cast(&entry.accumulation[Perspective][j * TileHeight]); + auto entryTile = reinterpret_cast(&entry.accumulation[j * TileHeight]); for (IndexType k = 0; k < NumRegs; ++k) acc[k] = entryTile[k]; @@ -741,8 +739,8 @@ class FeatureTransformer { for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j) { - auto entryTilePsqt = reinterpret_cast( - &entry.psqtAccumulation[Perspective][j * PsqtTileHeight]); + auto entryTilePsqt = + reinterpret_cast(&entry.psqtAccumulation[j * PsqtTileHeight]); for (std::size_t k = 0; k < NumPsqtRegs; ++k) psqt[k] = entryTilePsqt[k]; @@ -777,11 +775,11 @@ class FeatureTransformer { { const IndexType offset = HalfDimensions * index; for (IndexType j = 0; j < HalfDimensions; ++j) - entry.accumulation[Perspective][j] -= weights[offset + j]; + entry.accumulation[j] -= weights[offset + j]; } for (std::size_t k = 0; k < PSQTBuckets; ++k) - entry.psqtAccumulation[Perspective][k] -= psqtWeights[index * PSQTBuckets + k]; + entry.psqtAccumulation[k] -= psqtWeights[index * PSQTBuckets + k]; } for (const auto index : added) { @@ -789,11 +787,11 @@ class FeatureTransformer { { const IndexType offset = HalfDimensions * index; for (IndexType j = 0; j < HalfDimensions; ++j) - entry.accumulation[Perspective][j] += weights[offset + j]; + entry.accumulation[j] += weights[offset + j]; } for (std::size_t k = 0; k < PSQTBuckets; ++k) - entry.psqtAccumulation[Perspective][k] += psqtWeights[index * PSQTBuckets + k]; + entry.psqtAccumulation[k] += psqtWeights[index * PSQTBuckets + k]; } #endif @@ -802,17 +800,17 @@ class FeatureTransformer { // Now copy its content to the actual accumulator we were refreshing if (!psqtOnly) - std::memcpy(accumulator.accumulation[Perspective], entry.accumulation[Perspective], + std::memcpy(accumulator.accumulation[Perspective], entry.accumulation, sizeof(BiasType) * HalfDimensions); - std::memcpy(accumulator.psqtAccumulation[Perspective], entry.psqtAccumulation[Perspective], + std::memcpy(accumulator.psqtAccumulation[Perspective], entry.psqtAccumulation, sizeof(int32_t) * PSQTBuckets); for (Color c : {WHITE, BLACK}) - entry.byColorBB[Perspective][c] = pos.pieces(c); + entry.byColorBB[c] = pos.pieces(c); for (PieceType pt = PAWN; pt <= KING; ++pt) - entry.byTypeBB[Perspective][pt] = pos.pieces(pt); + entry.byTypeBB[pt] = pos.pieces(pt); entry.psqtOnly = psqtOnly; } diff --git a/src/search.cpp b/src/search.cpp index e4f170be61d..b8e515f0267 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -147,9 +147,6 @@ Search::Worker::Worker(SharedState& sharedState, void Search::Worker::start_searching() { - // Initialize accumulator refresh entries - refreshTable.clear(networks); - // Non-main threads go directly to iterative_deepening() if (!is_mainthread()) { @@ -506,6 +503,8 @@ void Search::Worker::clear() { for (size_t i = 1; i < reductions.size(); ++i) reductions[i] = int((20.14 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); + + refreshTable.clear(networks); } From be026bdcb2c71501dffab4a04dabef682661e664 Mon Sep 17 00:00:00 2001 From: Disservin Date: Wed, 1 May 2024 15:10:23 +0200 Subject: [PATCH 1462/1766] Clear Workers after changing the network ensures internal state (e.g. accumulator cache) is consistent with network closes https://github.com/official-stockfish/Stockfish/pull/5204 No functional change --- src/engine.cpp | 10 +++++++--- src/thread.cpp | 3 +++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/engine.cpp b/src/engine.cpp index 72a37ce9b0b..e8da24aa9e8 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -141,14 +141,18 @@ void Engine::verify_networks() const { } void Engine::load_networks() { - networks.big.load(binaryDirectory, options["EvalFile"]); - networks.small.load(binaryDirectory, options["EvalFileSmall"]); + load_big_network(options["EvalFile"]); + load_small_network(options["EvalFileSmall"]); } -void Engine::load_big_network(const std::string& file) { networks.big.load(binaryDirectory, file); } +void Engine::load_big_network(const std::string& file) { + networks.big.load(binaryDirectory, file); + threads.clear(); +} void Engine::load_small_network(const std::string& file) { networks.small.load(binaryDirectory, file); + threads.clear(); } void Engine::save_network(const std::pair, std::string> files[2]) { diff --git a/src/thread.cpp b/src/thread.cpp index 1438c9f9d5c..9052654baf6 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -161,6 +161,9 @@ void ThreadPool::clear() { for (Thread* th : threads) th->worker->clear(); + if (threads.size() == 0) + return; + main_manager()->callsCnt = 0; main_manager()->bestPreviousScore = VALUE_INFINITE; main_manager()->bestPreviousAverageScore = VALUE_INFINITE; From 8ee9905d8beddc01fa70e39c439b076c2d661acb Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Sat, 4 May 2024 09:52:27 +0800 Subject: [PATCH 1463/1766] Remove PSQT-only mode Passed STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 94208 W: 24270 L: 24112 D: 45826 Ptnml(0-2): 286, 11186, 24009, 11330, 293 https://tests.stockfishchess.org/tests/view/6635ddd773559a8aa8582826 Passed LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 114960 W: 29107 L: 28982 D: 56871 Ptnml(0-2): 37, 12683, 31924, 12790, 46 https://tests.stockfishchess.org/tests/view/663604a973559a8aa85881ed closes #5214 Bench 1653939 --- src/evaluate.cpp | 8 +- src/evaluate.h | 2 +- src/nnue/network.cpp | 21 +- src/nnue/network.h | 6 +- src/nnue/nnue_accumulator.h | 2 - src/nnue/nnue_feature_transformer.h | 340 ++++++++++++---------------- src/nnue/nnue_misc.cpp | 13 +- src/position.cpp | 18 +- 8 files changed, 169 insertions(+), 241 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index e3aa249ca41..11999b554b7 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -56,13 +56,11 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, int simpleEval = simple_eval(pos, pos.side_to_move()); bool smallNet = std::abs(simpleEval) > SmallNetThreshold; - bool psqtOnly = std::abs(simpleEval) > PsqtOnlyThreshold; int nnueComplexity; int v; - Value nnue = smallNet - ? networks.small.evaluate(pos, &caches.small, true, &nnueComplexity, psqtOnly) - : networks.big.evaluate(pos, &caches.big, true, &nnueComplexity, false); + Value nnue = smallNet ? networks.small.evaluate(pos, &caches.small, true, &nnueComplexity) + : networks.big.evaluate(pos, &caches.big, true, &nnueComplexity); const auto adjustEval = [&](int optDiv, int nnueDiv, int pawnCountConstant, int pawnCountMul, int npmConstant, int evalDiv, int shufflingConstant, @@ -83,8 +81,6 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, if (!smallNet) adjustEval(524, 32395, 942, 11, 139, 1058, 178, 204); - else if (psqtOnly) - adjustEval(517, 32857, 908, 7, 155, 1006, 224, 238); else adjustEval(515, 32793, 944, 9, 140, 1067, 206, 206); diff --git a/src/evaluate.h b/src/evaluate.h index 38615ff7d68..2d244ff6722 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -29,7 +29,7 @@ class Position; namespace Eval { -constexpr inline int SmallNetThreshold = 1274, PsqtOnlyThreshold = 2389; +constexpr inline int SmallNetThreshold = 1274; // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the diff --git a/src/nnue/network.cpp b/src/nnue/network.cpp index 2eca18bd15d..de2c7eca6d5 100644 --- a/src/nnue/network.cpp +++ b/src/nnue/network.cpp @@ -189,8 +189,7 @@ template Value Network::evaluate(const Position& pos, AccumulatorCaches::Cache* cache, bool adjusted, - int* complexity, - bool psqtOnly) const { + int* complexity) const { // We manually align the arrays on the stack because with gcc < 9.3 // overaligning stack variables with alignas() doesn't work correctly. @@ -210,13 +209,12 @@ Value Network::evaluate(const Position& ASSERT_ALIGNED(transformedFeatures, alignment); - const int bucket = (pos.count() - 1) / 4; - const auto psqt = - featureTransformer->transform(pos, cache, transformedFeatures, bucket, psqtOnly); - const auto positional = !psqtOnly ? (network[bucket]->propagate(transformedFeatures)) : 0; + const int bucket = (pos.count() - 1) / 4; + const auto psqt = featureTransformer->transform(pos, cache, transformedFeatures, bucket); + const auto positional = network[bucket]->propagate(transformedFeatures); if (complexity) - *complexity = !psqtOnly ? std::abs(psqt - positional) / OutputScale : 0; + *complexity = std::abs(psqt - positional) / OutputScale; // Give more value to positional evaluation when adjusted flag is set if (adjusted) @@ -261,10 +259,9 @@ void Network::verify(std::string evalfilePath) const { template -void Network::hint_common_access(const Position& pos, - AccumulatorCaches::Cache* cache, - bool psqtOnly) const { - featureTransformer->hint_common_access(pos, cache, psqtOnly); +void Network::hint_common_access( + const Position& pos, AccumulatorCaches::Cache* cache) const { + featureTransformer->hint_common_access(pos, cache); } template @@ -293,7 +290,7 @@ Network::trace_evaluate(const Position& for (IndexType bucket = 0; bucket < LayerStacks; ++bucket) { const auto materialist = - featureTransformer->transform(pos, cache, transformedFeatures, bucket, false); + featureTransformer->transform(pos, cache, transformedFeatures, bucket); const auto positional = network[bucket]->propagate(transformedFeatures); t.psqt[bucket] = static_cast(materialist / OutputScale); diff --git a/src/nnue/network.h b/src/nnue/network.h index 053b7d19c82..23f56663094 100644 --- a/src/nnue/network.h +++ b/src/nnue/network.h @@ -56,13 +56,11 @@ class Network { Value evaluate(const Position& pos, AccumulatorCaches::Cache* cache, bool adjusted = false, - int* complexity = nullptr, - bool psqtOnly = false) const; + int* complexity = nullptr) const; void hint_common_access(const Position& pos, - AccumulatorCaches::Cache* cache, - bool psqtOnly) const; + AccumulatorCaches::Cache* cache) const; void verify(std::string evalfilePath) const; NnueEvalTrace trace_evaluate(const Position& pos, diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index 179feba553e..b8dcf1e480f 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -38,7 +38,6 @@ struct alignas(CacheLineSize) Accumulator { std::int16_t accumulation[COLOR_NB][Size]; std::int32_t psqtAccumulation[COLOR_NB][PSQTBuckets]; bool computed[COLOR_NB]; - bool computedPSQT[COLOR_NB]; }; @@ -63,7 +62,6 @@ struct AccumulatorCaches { PSQTWeightType psqtAccumulation[PSQTBuckets]; Bitboard byColorBB[COLOR_NB]; Bitboard byTypeBB[PIECE_TYPE_NB]; - bool psqtOnly; // To initialize a refresh entry, we set all its bitboards empty, // so we put the biases in the accumulation, without any weights on top diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 4647ecd066d..018b715e638 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -309,10 +309,9 @@ class FeatureTransformer { std::int32_t transform(const Position& pos, AccumulatorCaches::Cache* cache, OutputType* output, - int bucket, - bool psqtOnly) const { - update_accumulator(pos, cache, psqtOnly); - update_accumulator(pos, cache, psqtOnly); + int bucket) const { + update_accumulator(pos, cache); + update_accumulator(pos, cache); const Color perspectives[2] = {pos.side_to_move(), ~pos.side_to_move()}; const auto& psqtAccumulation = (pos.state()->*accPtr).psqtAccumulation; @@ -320,9 +319,6 @@ class FeatureTransformer { (psqtAccumulation[perspectives[0]][bucket] - psqtAccumulation[perspectives[1]][bucket]) / 2; - if (psqtOnly) - return psqt; - const auto& accumulation = (pos.state()->*accPtr).accumulation; for (IndexType p = 0; p < 2; ++p) @@ -375,23 +371,20 @@ class FeatureTransformer { } // end of function transform() void hint_common_access(const Position& pos, - AccumulatorCaches::Cache* cache, - bool psqtOnly) const { - hint_common_access_for_perspective(pos, cache, psqtOnly); - hint_common_access_for_perspective(pos, cache, psqtOnly); + AccumulatorCaches::Cache* cache) const { + hint_common_access_for_perspective(pos, cache); + hint_common_access_for_perspective(pos, cache); } private: template [[nodiscard]] std::pair - try_find_computed_accumulator(const Position& pos, bool psqtOnly) const { + try_find_computed_accumulator(const Position& pos) const { // Look for a usable accumulator of an earlier position. We keep track // of the estimated gain in terms of features to be added/subtracted. StateInfo *st = pos.state(), *next = nullptr; int gain = FeatureSet::refresh_cost(pos); - while (st->previous - && (!(st->*accPtr).computedPSQT[Perspective] - || (!psqtOnly && !(st->*accPtr).computed[Perspective]))) + while (st->previous && !(st->*accPtr).computed[Perspective]) { // This governs when a full feature refresh is needed and how many // updates are better than just one full refresh. @@ -412,8 +405,7 @@ class FeatureTransformer { template void update_accumulator_incremental(const Position& pos, StateInfo* computed_st, - StateInfo* states_to_update[N], - bool psqtOnly) const { + StateInfo* states_to_update[N]) const { static_assert(N > 0); assert([&]() { for (size_t i = 0; i < N; ++i) @@ -443,8 +435,7 @@ class FeatureTransformer { for (int i = N - 1; i >= 0; --i) { - (states_to_update[i]->*accPtr).computed[Perspective] = !psqtOnly; - (states_to_update[i]->*accPtr).computedPSQT[Perspective] = true; + (states_to_update[i]->*accPtr).computed[Perspective] = true; const StateInfo* end_state = i == 0 ? computed_st : states_to_update[i - 1]; @@ -462,34 +453,31 @@ class FeatureTransformer { { assert(states_to_update[0]); - if (!psqtOnly) - { - auto accIn = - reinterpret_cast(&(st->*accPtr).accumulation[Perspective][0]); - auto accOut = reinterpret_cast( - &(states_to_update[0]->*accPtr).accumulation[Perspective][0]); + auto accIn = + reinterpret_cast(&(st->*accPtr).accumulation[Perspective][0]); + auto accOut = reinterpret_cast( + &(states_to_update[0]->*accPtr).accumulation[Perspective][0]); - const IndexType offsetR0 = HalfDimensions * removed[0][0]; - auto columnR0 = reinterpret_cast(&weights[offsetR0]); - const IndexType offsetA = HalfDimensions * added[0][0]; - auto columnA = reinterpret_cast(&weights[offsetA]); + const IndexType offsetR0 = HalfDimensions * removed[0][0]; + auto columnR0 = reinterpret_cast(&weights[offsetR0]); + const IndexType offsetA = HalfDimensions * added[0][0]; + auto columnA = reinterpret_cast(&weights[offsetA]); - if (removed[0].size() == 1) - { - for (IndexType k = 0; k < HalfDimensions * sizeof(std::int16_t) / sizeof(vec_t); - ++k) - accOut[k] = vec_add_16(vec_sub_16(accIn[k], columnR0[k]), columnA[k]); - } - else - { - const IndexType offsetR1 = HalfDimensions * removed[0][1]; - auto columnR1 = reinterpret_cast(&weights[offsetR1]); + if (removed[0].size() == 1) + { + for (IndexType k = 0; k < HalfDimensions * sizeof(std::int16_t) / sizeof(vec_t); + ++k) + accOut[k] = vec_add_16(vec_sub_16(accIn[k], columnR0[k]), columnA[k]); + } + else + { + const IndexType offsetR1 = HalfDimensions * removed[0][1]; + auto columnR1 = reinterpret_cast(&weights[offsetR1]); - for (IndexType k = 0; k < HalfDimensions * sizeof(std::int16_t) / sizeof(vec_t); - ++k) - accOut[k] = vec_sub_16(vec_add_16(accIn[k], columnA[k]), - vec_add_16(columnR0[k], columnR1[k])); - } + for (IndexType k = 0; k < HalfDimensions * sizeof(std::int16_t) / sizeof(vec_t); + ++k) + accOut[k] = vec_sub_16(vec_add_16(accIn[k], columnA[k]), + vec_add_16(columnR0[k], columnR1[k])); } auto accPsqtIn = @@ -523,43 +511,41 @@ class FeatureTransformer { } else { - if (!psqtOnly) - for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) + for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) + { + // Load accumulator + auto accTileIn = reinterpret_cast( + &(st->*accPtr).accumulation[Perspective][j * TileHeight]); + for (IndexType k = 0; k < NumRegs; ++k) + acc[k] = vec_load(&accTileIn[k]); + + for (IndexType i = 0; i < N; ++i) { - // Load accumulator - auto accTileIn = reinterpret_cast( - &(st->*accPtr).accumulation[Perspective][j * TileHeight]); - for (IndexType k = 0; k < NumRegs; ++k) - acc[k] = vec_load(&accTileIn[k]); + // Difference calculation for the deactivated features + for (const auto index : removed[i]) + { + const IndexType offset = HalfDimensions * index + j * TileHeight; + auto column = reinterpret_cast(&weights[offset]); + for (IndexType k = 0; k < NumRegs; ++k) + acc[k] = vec_sub_16(acc[k], column[k]); + } - for (IndexType i = 0; i < N; ++i) + // Difference calculation for the activated features + for (const auto index : added[i]) { - // Difference calculation for the deactivated features - for (const auto index : removed[i]) - { - const IndexType offset = HalfDimensions * index + j * TileHeight; - auto column = reinterpret_cast(&weights[offset]); - for (IndexType k = 0; k < NumRegs; ++k) - acc[k] = vec_sub_16(acc[k], column[k]); - } - - // Difference calculation for the activated features - for (const auto index : added[i]) - { - const IndexType offset = HalfDimensions * index + j * TileHeight; - auto column = reinterpret_cast(&weights[offset]); - for (IndexType k = 0; k < NumRegs; ++k) - acc[k] = vec_add_16(acc[k], column[k]); - } - - // Store accumulator - auto accTileOut = - reinterpret_cast(&(states_to_update[i]->*accPtr) - .accumulation[Perspective][j * TileHeight]); + const IndexType offset = HalfDimensions * index + j * TileHeight; + auto column = reinterpret_cast(&weights[offset]); for (IndexType k = 0; k < NumRegs; ++k) - vec_store(&accTileOut[k], acc[k]); + acc[k] = vec_add_16(acc[k], column[k]); } + + // Store accumulator + auto accTileOut = reinterpret_cast( + &(states_to_update[i]->*accPtr).accumulation[Perspective][j * TileHeight]); + for (IndexType k = 0; k < NumRegs; ++k) + vec_store(&accTileOut[k], acc[k]); } + } for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j) { @@ -601,10 +587,8 @@ class FeatureTransformer { #else for (IndexType i = 0; i < N; ++i) { - if (!psqtOnly) - std::memcpy((states_to_update[i]->*accPtr).accumulation[Perspective], - (st->*accPtr).accumulation[Perspective], - HalfDimensions * sizeof(BiasType)); + std::memcpy((states_to_update[i]->*accPtr).accumulation[Perspective], + (st->*accPtr).accumulation[Perspective], HalfDimensions * sizeof(BiasType)); for (std::size_t k = 0; k < PSQTBuckets; ++k) (states_to_update[i]->*accPtr).psqtAccumulation[Perspective][k] = @@ -615,12 +599,9 @@ class FeatureTransformer { // Difference calculation for the deactivated features for (const auto index : removed[i]) { - if (!psqtOnly) - { - const IndexType offset = HalfDimensions * index; - for (IndexType j = 0; j < HalfDimensions; ++j) - (st->*accPtr).accumulation[Perspective][j] -= weights[offset + j]; - } + const IndexType offset = HalfDimensions * index; + for (IndexType j = 0; j < HalfDimensions; ++j) + (st->*accPtr).accumulation[Perspective][j] -= weights[offset + j]; for (std::size_t k = 0; k < PSQTBuckets; ++k) (st->*accPtr).psqtAccumulation[Perspective][k] -= @@ -630,12 +611,9 @@ class FeatureTransformer { // Difference calculation for the activated features for (const auto index : added[i]) { - if (!psqtOnly) - { - const IndexType offset = HalfDimensions * index; - for (IndexType j = 0; j < HalfDimensions; ++j) - (st->*accPtr).accumulation[Perspective][j] += weights[offset + j]; - } + const IndexType offset = HalfDimensions * index; + for (IndexType j = 0; j < HalfDimensions; ++j) + (st->*accPtr).accumulation[Perspective][j] += weights[offset + j]; for (std::size_t k = 0; k < PSQTBuckets; ++k) (st->*accPtr).psqtAccumulation[Perspective][k] += @@ -647,96 +625,85 @@ class FeatureTransformer { template void update_accumulator_refresh_cache(const Position& pos, - AccumulatorCaches::Cache* cache, - bool psqtOnly) const { + AccumulatorCaches::Cache* cache) const { assert(cache != nullptr); Square ksq = pos.square(Perspective); auto& entry = (*cache)[ksq][Perspective]; FeatureSet::IndexList removed, added; - if (entry.psqtOnly && !psqtOnly) - { - entry.clear(biases); - FeatureSet::append_active_indices(pos, added); - } - else + for (Color c : {WHITE, BLACK}) { - for (Color c : {WHITE, BLACK}) + for (PieceType pt = PAWN; pt <= KING; ++pt) { - for (PieceType pt = PAWN; pt <= KING; ++pt) - { - const Piece piece = make_piece(c, pt); - const Bitboard oldBB = entry.byColorBB[c] & entry.byTypeBB[pt]; - const Bitboard newBB = pos.pieces(c, pt); - Bitboard toRemove = oldBB & ~newBB; - Bitboard toAdd = newBB & ~oldBB; + const Piece piece = make_piece(c, pt); + const Bitboard oldBB = entry.byColorBB[c] & entry.byTypeBB[pt]; + const Bitboard newBB = pos.pieces(c, pt); + Bitboard toRemove = oldBB & ~newBB; + Bitboard toAdd = newBB & ~oldBB; - while (toRemove) - { - Square sq = pop_lsb(toRemove); - removed.push_back(FeatureSet::make_index(sq, piece, ksq)); - } - while (toAdd) - { - Square sq = pop_lsb(toAdd); - added.push_back(FeatureSet::make_index(sq, piece, ksq)); - } + while (toRemove) + { + Square sq = pop_lsb(toRemove); + removed.push_back(FeatureSet::make_index(sq, piece, ksq)); + } + while (toAdd) + { + Square sq = pop_lsb(toAdd); + added.push_back(FeatureSet::make_index(sq, piece, ksq)); } } } - auto& accumulator = pos.state()->*accPtr; - accumulator.computed[Perspective] = !psqtOnly; - accumulator.computedPSQT[Perspective] = true; + auto& accumulator = pos.state()->*accPtr; + accumulator.computed[Perspective] = true; #ifdef VECTOR vec_t acc[NumRegs]; psqt_vec_t psqt[NumPsqtRegs]; - if (!psqtOnly) - for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) - { - auto entryTile = reinterpret_cast(&entry.accumulation[j * TileHeight]); - for (IndexType k = 0; k < NumRegs; ++k) - acc[k] = entryTile[k]; - - int i0 = 0; - for (; i0 < int(std::min(removed.size(), added.size())); ++i0) - { - IndexType indexR = removed[i0]; - const IndexType offsetR = HalfDimensions * indexR + j * TileHeight; - auto columnR = reinterpret_cast(&weights[offsetR]); - IndexType indexA = added[i0]; - const IndexType offsetA = HalfDimensions * indexA + j * TileHeight; - auto columnA = reinterpret_cast(&weights[offsetA]); - - for (unsigned k = 0; k < NumRegs; ++k) - acc[k] = vec_add_16(vec_sub_16(acc[k], columnR[k]), columnA[k]); - } - for (int i = i0; i < int(removed.size()); ++i) - { - IndexType index = removed[i]; - const IndexType offset = HalfDimensions * index + j * TileHeight; - auto column = reinterpret_cast(&weights[offset]); + for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) + { + auto entryTile = reinterpret_cast(&entry.accumulation[j * TileHeight]); + for (IndexType k = 0; k < NumRegs; ++k) + acc[k] = entryTile[k]; - for (unsigned k = 0; k < NumRegs; ++k) - acc[k] = vec_sub_16(acc[k], column[k]); - } - for (int i = i0; i < int(added.size()); ++i) - { - IndexType index = added[i]; - const IndexType offset = HalfDimensions * index + j * TileHeight; - auto column = reinterpret_cast(&weights[offset]); + int i0 = 0; + for (; i0 < int(std::min(removed.size(), added.size())); ++i0) + { + IndexType indexR = removed[i0]; + const IndexType offsetR = HalfDimensions * indexR + j * TileHeight; + auto columnR = reinterpret_cast(&weights[offsetR]); + IndexType indexA = added[i0]; + const IndexType offsetA = HalfDimensions * indexA + j * TileHeight; + auto columnA = reinterpret_cast(&weights[offsetA]); + + for (unsigned k = 0; k < NumRegs; ++k) + acc[k] = vec_add_16(vec_sub_16(acc[k], columnR[k]), columnA[k]); + } + for (int i = i0; i < int(removed.size()); ++i) + { + IndexType index = removed[i]; + const IndexType offset = HalfDimensions * index + j * TileHeight; + auto column = reinterpret_cast(&weights[offset]); - for (unsigned k = 0; k < NumRegs; ++k) - acc[k] = vec_add_16(acc[k], column[k]); - } + for (unsigned k = 0; k < NumRegs; ++k) + acc[k] = vec_sub_16(acc[k], column[k]); + } + for (int i = i0; i < int(added.size()); ++i) + { + IndexType index = added[i]; + const IndexType offset = HalfDimensions * index + j * TileHeight; + auto column = reinterpret_cast(&weights[offset]); - for (IndexType k = 0; k < NumRegs; k++) - vec_store(&entryTile[k], acc[k]); + for (unsigned k = 0; k < NumRegs; ++k) + acc[k] = vec_add_16(acc[k], column[k]); } + for (IndexType k = 0; k < NumRegs; k++) + vec_store(&entryTile[k], acc[k]); + } + for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j) { auto entryTilePsqt = @@ -771,24 +738,18 @@ class FeatureTransformer { for (const auto index : removed) { - if (!psqtOnly) - { - const IndexType offset = HalfDimensions * index; - for (IndexType j = 0; j < HalfDimensions; ++j) - entry.accumulation[j] -= weights[offset + j]; - } + const IndexType offset = HalfDimensions * index; + for (IndexType j = 0; j < HalfDimensions; ++j) + entry.accumulation[j] -= weights[offset + j]; for (std::size_t k = 0; k < PSQTBuckets; ++k) entry.psqtAccumulation[k] -= psqtWeights[index * PSQTBuckets + k]; } for (const auto index : added) { - if (!psqtOnly) - { - const IndexType offset = HalfDimensions * index; - for (IndexType j = 0; j < HalfDimensions; ++j) - entry.accumulation[j] += weights[offset + j]; - } + const IndexType offset = HalfDimensions * index; + for (IndexType j = 0; j < HalfDimensions; ++j) + entry.accumulation[j] += weights[offset + j]; for (std::size_t k = 0; k < PSQTBuckets; ++k) entry.psqtAccumulation[k] += psqtWeights[index * PSQTBuckets + k]; @@ -799,9 +760,8 @@ class FeatureTransformer { // The accumulator of the refresh entry has been updated. // Now copy its content to the actual accumulator we were refreshing - if (!psqtOnly) - std::memcpy(accumulator.accumulation[Perspective], entry.accumulation, - sizeof(BiasType) * HalfDimensions); + std::memcpy(accumulator.accumulation[Perspective], entry.accumulation, + sizeof(BiasType) * HalfDimensions); std::memcpy(accumulator.psqtAccumulation[Perspective], entry.psqtAccumulation, sizeof(int32_t) * PSQTBuckets); @@ -811,14 +771,11 @@ class FeatureTransformer { for (PieceType pt = PAWN; pt <= KING; ++pt) entry.byTypeBB[pt] = pos.pieces(pt); - - entry.psqtOnly = psqtOnly; } template void hint_common_access_for_perspective(const Position& pos, - AccumulatorCaches::Cache* cache, - bool psqtOnly) const { + AccumulatorCaches::Cache* cache) const { // Works like update_accumulator, but performs less work. // Updates ONLY the accumulator for pos. @@ -826,33 +783,28 @@ class FeatureTransformer { // Look for a usable accumulator of an earlier position. We keep track // of the estimated gain in terms of features to be added/subtracted. // Fast early exit. - if ((pos.state()->*accPtr).computed[Perspective] - || (psqtOnly && (pos.state()->*accPtr).computedPSQT[Perspective])) + if ((pos.state()->*accPtr).computed[Perspective]) return; - auto [oldest_st, _] = try_find_computed_accumulator(pos, psqtOnly); + auto [oldest_st, _] = try_find_computed_accumulator(pos); - if ((oldest_st->*accPtr).computed[Perspective] - || (psqtOnly && (oldest_st->*accPtr).computedPSQT[Perspective])) + if ((oldest_st->*accPtr).computed[Perspective]) { // Only update current position accumulator to minimize work. StateInfo* states_to_update[1] = {pos.state()}; - update_accumulator_incremental(pos, oldest_st, states_to_update, - psqtOnly); + update_accumulator_incremental(pos, oldest_st, states_to_update); } else - update_accumulator_refresh_cache(pos, cache, psqtOnly); + update_accumulator_refresh_cache(pos, cache); } template void update_accumulator(const Position& pos, - AccumulatorCaches::Cache* cache, - bool psqtOnly) const { + AccumulatorCaches::Cache* cache) const { - auto [oldest_st, next] = try_find_computed_accumulator(pos, psqtOnly); + auto [oldest_st, next] = try_find_computed_accumulator(pos); - if ((oldest_st->*accPtr).computed[Perspective] - || (psqtOnly && (oldest_st->*accPtr).computedPSQT[Perspective])) + if ((oldest_st->*accPtr).computed[Perspective]) { if (next == nullptr) return; @@ -866,19 +818,17 @@ class FeatureTransformer { { StateInfo* states_to_update[1] = {next}; - update_accumulator_incremental(pos, oldest_st, states_to_update, - psqtOnly); + update_accumulator_incremental(pos, oldest_st, states_to_update); } else { StateInfo* states_to_update[2] = {next, pos.state()}; - update_accumulator_incremental(pos, oldest_st, states_to_update, - psqtOnly); + update_accumulator_incremental(pos, oldest_st, states_to_update); } } else - update_accumulator_refresh_cache(pos, cache, psqtOnly); + update_accumulator_refresh_cache(pos, cache); } template diff --git a/src/nnue/nnue_misc.cpp b/src/nnue/nnue_misc.cpp index 21685d0f2a3..bf73a58bf07 100644 --- a/src/nnue/nnue_misc.cpp +++ b/src/nnue/nnue_misc.cpp @@ -48,10 +48,9 @@ void hint_common_parent_position(const Position& pos, int simpleEvalAbs = std::abs(simple_eval(pos, pos.side_to_move())); if (simpleEvalAbs > Eval::SmallNetThreshold) - networks.small.hint_common_access(pos, &caches.small, - simpleEvalAbs > Eval::PsqtOnlyThreshold); + networks.small.hint_common_access(pos, &caches.small); else - networks.big.hint_common_access(pos, &caches.big, false); + networks.big.hint_common_access(pos, &caches.big); } namespace { @@ -149,18 +148,14 @@ trace(Position& pos, const Eval::NNUE::Networks& networks, Eval::NNUE::Accumulat auto st = pos.state(); pos.remove_piece(sq); - st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] = - st->accumulatorBig.computedPSQT[WHITE] = st->accumulatorBig.computedPSQT[BLACK] = - false; + st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] = false; Value eval = networks.big.evaluate(pos, &caches.big); eval = pos.side_to_move() == WHITE ? eval : -eval; v = base - eval; pos.put_piece(pc, sq); - st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] = - st->accumulatorBig.computedPSQT[WHITE] = st->accumulatorBig.computedPSQT[BLACK] = - false; + st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] = false; } writeSquare(f, r, pc, v); diff --git a/src/position.cpp b/src/position.cpp index 78e62bda303..b46ba029985 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -680,11 +680,8 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { ++st->pliesFromNull; // Used by NNUE - st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] = - st->accumulatorBig.computedPSQT[WHITE] = st->accumulatorBig.computedPSQT[BLACK] = - st->accumulatorSmall.computed[WHITE] = st->accumulatorSmall.computed[BLACK] = - st->accumulatorSmall.computedPSQT[WHITE] = st->accumulatorSmall.computedPSQT[BLACK] = - false; + st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] = + st->accumulatorSmall.computed[WHITE] = st->accumulatorSmall.computed[BLACK] = false; auto& dp = st->dirtyPiece; dp.dirty_num = 1; @@ -968,13 +965,10 @@ void Position::do_null_move(StateInfo& newSt, TranspositionTable& tt) { newSt.previous = st; st = &newSt; - st->dirtyPiece.dirty_num = 0; - st->dirtyPiece.piece[0] = NO_PIECE; // Avoid checks in UpdateAccumulator() - st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] = - st->accumulatorBig.computedPSQT[WHITE] = st->accumulatorBig.computedPSQT[BLACK] = - st->accumulatorSmall.computed[WHITE] = st->accumulatorSmall.computed[BLACK] = - st->accumulatorSmall.computedPSQT[WHITE] = st->accumulatorSmall.computedPSQT[BLACK] = - false; + st->dirtyPiece.dirty_num = 0; + st->dirtyPiece.piece[0] = NO_PIECE; // Avoid checks in UpdateAccumulator() + st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] = + st->accumulatorSmall.computed[WHITE] = st->accumulatorSmall.computed[BLACK] = false; if (st->epSquare != SQ_NONE) { From 351a2e22dd8ad8bc3b2204e1e80d4d5860a778d6 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 4 May 2024 10:33:26 +0300 Subject: [PATCH 1464/1766] Add extra bonuses to some moves that forced a fail low The previous patch on this idea was giving bonuses to this moves if best value of search is far below current static evaluation. This patch does similar thing but adds extra bonus when best value of search is far below static evaluation before previous move. Passed STC: https://tests.stockfishchess.org/tests/view/66355fc819566d64b481d6a4 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 454144 W: 116575 L: 115656 D: 221913 Ptnml(0-2): 1060, 53410, 117215, 54325, 1062 Passed LTC: https://tests.stockfishchess.org/tests/view/6635c61a73559a8aa858012d LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 136578 W: 34858 L: 34335 D: 67385 closes https://github.com/official-stockfish/Stockfish/pull/5209 Bench: 1614825 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index b8e515f0267..cd80e9392d6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1335,8 +1335,8 @@ Value Search::Worker::search( else if (!priorCapture && prevSq != SQ_NONE) { int bonus = (depth > 5) + (PvNode || cutNode) + ((ss - 1)->statScore < -14761) - + ((ss - 1)->moveCount > 11) - + (!ss->inCheck && bestValue <= ss->staticEval - 142); + + ((ss - 1)->moveCount > 11) + (!ss->inCheck && bestValue <= ss->staticEval - 142) + + (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 77); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] From 741aaf8a38c75535e01a3f5506877654547ebb33 Mon Sep 17 00:00:00 2001 From: Viren6 <94880762+Viren6@users.noreply.github.com> Date: Sat, 4 May 2024 17:29:23 +0100 Subject: [PATCH 1465/1766] Introduce Quadruple Extensions This patch introduces quadruple extensions, with the new condition of not ttPv. It also generalises all margins, so that extensions can still occur if conditions are only partially fulfilled, but with a stricter margin. Failed STC: LLR: -2.94 (-2.94,2.94) <0.00,2.00> Total: 16096 W: 3984 L: 4228 D: 7884 Ptnml(0-2): 72, 2067, 4002, 1847, 60 https://tests.stockfishchess.org/tests/view/66316422d01fb9ac9bcdbdcd Passed VVLTC 1: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 96660 W: 24550 L: 24210 D: 47900 Ptnml(0-2): 5, 8776, 30426, 9120, 3 https://tests.stockfishchess.org/tests/view/66361f2c74fa3f41ef2ee091 Passed VVLTC 2: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 80546 W: 20495 L: 20120 D: 39931 Ptnml(0-2): 6, 7477, 24929, 7858, 3 https://tests.stockfishchess.org/tests/view/66350cf739ba8e443112b3fa closes https://github.com/official-stockfish/Stockfish/pull/5211 bench 2233743 --- src/search.cpp | 23 ++++++++++------------- src/search.h | 1 - 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index cd80e9392d6..06d31510e83 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -593,7 +593,6 @@ Value Search::Worker::search( bestMove = Move::none(); (ss + 2)->killers[0] = (ss + 2)->killers[1] = Move::none(); (ss + 2)->cutoffCnt = 0; - ss->multipleExtensions = (ss - 1)->multipleExtensions; Square prevSq = ((ss - 1)->currentMove).is_ok() ? ((ss - 1)->currentMove).to_sq() : SQ_NONE; ss->statScore = 0; @@ -1049,17 +1048,16 @@ Value Search::Worker::search( if (value < singularBeta) { - extension = 1; - - // We make sure to limit the extensions in some way to avoid a search explosion - if (!PvNode && ss->multipleExtensions <= 16) - { - extension = 2 + (value < singularBeta - 11 && !ttCapture); - depth += depth < 14; - } - if (PvNode && !ttCapture && ss->multipleExtensions <= 5 - && value < singularBeta - 38) - extension = 2; + int doubleMargin = 251 * PvNode - 241 * !ttCapture; + int tripleMargin = + 135 + 234 * PvNode - 248 * !ttCapture + 124 * (ss->ttPv || !ttCapture); + int quadMargin = 447 + 354 * PvNode - 300 * !ttCapture + 206 * ss->ttPv; + + extension = 1 + (value < singularBeta - doubleMargin) + + (value < singularBeta - tripleMargin) + + (value < singularBeta - quadMargin); + + depth += ((!PvNode) && (depth < 14)); } // Multi-cut pruning @@ -1104,7 +1102,6 @@ Value Search::Worker::search( // Add extension to new depth newDepth += extension; - ss->multipleExtensions = (ss - 1)->multipleExtensions + (extension >= 2); // Speculative prefetch as early as possible prefetch(tt.first_entry(pos.key_after(move))); diff --git a/src/search.h b/src/search.h index 444e3b8bb1d..cb73a5afddf 100644 --- a/src/search.h +++ b/src/search.h @@ -74,7 +74,6 @@ struct Stack { bool inCheck; bool ttPv; bool ttHit; - int multipleExtensions; int cutoffCnt; }; From d712ed38d1c6c9c76ad375efbd4b8a0469200c0b Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sat, 4 May 2024 21:43:07 +0300 Subject: [PATCH 1466/1766] Simplify shuffling and optimism divisors to constants Shuffling divisor and Optimism divisors passed STC & LTC separately: shuf STC: https://tests.stockfishchess.org/tests/view/66356316b4e9bdbc7228b995 shuf LTC: https://tests.stockfishchess.org/tests/view/6635815a73559a8aa857c1dc opt STC: https://tests.stockfishchess.org/tests/view/66356326b4e9bdbc7228b9a0 opt LTC: https://tests.stockfishchess.org/tests/view/663615c673559a8aa8589f8a And then passed LTC together: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 178278 W: 45039 L: 44979 D: 88260 Ptnml(0-2): 43, 19776, 49460, 19798, 62 https://tests.stockfishchess.org/tests/view/66363f19cdb7cf5da64e22a3 closes https://github.com/official-stockfish/Stockfish/pull/5212 Bench: 2198243 --- src/evaluate.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 11999b554b7..5be7e7a1bb4 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -62,11 +62,10 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, Value nnue = smallNet ? networks.small.evaluate(pos, &caches.small, true, &nnueComplexity) : networks.big.evaluate(pos, &caches.big, true, &nnueComplexity); - const auto adjustEval = [&](int optDiv, int nnueDiv, int pawnCountConstant, int pawnCountMul, - int npmConstant, int evalDiv, int shufflingConstant, - int shufflingDiv) { + const auto adjustEval = [&](int nnueDiv, int pawnCountConstant, int pawnCountMul, + int npmConstant, int evalDiv, int shufflingConstant) { // Blend optimism and eval with nnue complexity and material imbalance - optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / optDiv; + optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / 584; nnue -= nnue * (nnueComplexity * 5 / 3) / nnueDiv; int npm = pos.non_pawn_material() / 64; @@ -76,13 +75,13 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, // Damp down the evaluation linearly when shuffling int shuffling = pos.rule50_count(); - v = v * (shufflingConstant - shuffling) / shufflingDiv; + v = v * (shufflingConstant - shuffling) / 207; }; if (!smallNet) - adjustEval(524, 32395, 942, 11, 139, 1058, 178, 204); + adjustEval(32395, 942, 11, 139, 1058, 178); else - adjustEval(515, 32793, 944, 9, 140, 1067, 206, 206); + adjustEval(32793, 944, 9, 140, 1067, 206); // Guarantee evaluation does not hit the tablebase range v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); From 6da1590de0980ca569827e2905f5b423e1a00a52 Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Wed, 1 May 2024 18:31:38 +0800 Subject: [PATCH 1467/1766] Some history fixes and tidy-up This adds the functions `update_refutations` and `update_quiet_histories` to better distinguish the two. `update_quiet_stats` now just calls both of these functions. The functional side of this patch is two-fold: 1. Stop refutations being updated when we carry out multicut 2. Update pawn history every time we update other quiet histories Yellow STC: LLR: -2.95 (-2.94,2.94) <0.00,2.00> Total: 238976 W: 61506 L: 61415 D: 116055 Ptnml(0-2): 846, 28628, 60456, 28705, 853 https://tests.stockfishchess.org/tests/view/66321b5ed01fb9ac9bcdca83 However, it passed in <-1.75, 0.25> bounds: $ python3 sprt.py --wins 61506 --losses 61415 --draws 116055 --elo0 -1.75 --elo1 0.25 ELO: 0.132 +- 0.998 [-0.865, 1.13] LLR: 4.15 [-1.75, 0.25] (-2.94, 2.94) H1 Accepted Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 399126 W: 100730 L: 100896 D: 197500 Ptnml(0-2): 116, 44328, 110843, 44158, 118 https://tests.stockfishchess.org/tests/view/66357b0473559a8aa857ba6f closes #5215 Bench 2370967 --- src/search.cpp | 51 +++++++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 06d31510e83..43f18af2156 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -114,8 +114,11 @@ Value value_to_tt(Value v, int ply); Value value_from_tt(Value v, int ply, int r50c); void update_pv(Move* pv, Move move, const Move* childPv); void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus); -void update_quiet_stats( +void update_refutations(const Position& pos, Stack* ss, Search::Worker& workerThread, Move move); +void update_quiet_histories( const Position& pos, Stack* ss, Search::Worker& workerThread, Move move, int bonus); +void update_quiet_stats( + const Position& pos, Stack* ss, Search::Worker& workerThread, Move move, int bonus); void update_all_stats(const Position& pos, Stack* ss, Search::Worker& workerThread, @@ -1068,7 +1071,7 @@ Value Search::Worker::search( else if (singularBeta >= beta) { if (!ttCapture) - update_quiet_stats(pos, ss, *this, ttMove, -stat_malus(depth)); + update_quiet_histories(pos, ss, *this, ttMove, -stat_malus(depth)); return singularBeta; } @@ -1724,7 +1727,6 @@ void update_all_stats(const Position& pos, int captureCount, Depth depth) { - Color us = pos.side_to_move(); CapturePieceToHistory& captureHistory = workerThread.captureHistory; Piece moved_piece = pos.moved_piece(bestMove); PieceType captured; @@ -1737,23 +1739,11 @@ void update_all_stats(const Position& pos, int bestMoveBonus = bestValue > beta + 185 ? quietMoveBonus // larger bonus : stat_bonus(depth); // smaller bonus - // Increase stats for the best move in case it was a quiet move update_quiet_stats(pos, ss, workerThread, bestMove, bestMoveBonus); - int pIndex = pawn_structure_index(pos); - workerThread.pawnHistory[pIndex][moved_piece][bestMove.to_sq()] << quietMoveBonus; - // Decrease stats for all non-best quiet moves for (int i = 0; i < quietCount; ++i) - { - workerThread - .pawnHistory[pIndex][pos.moved_piece(quietsSearched[i])][quietsSearched[i].to_sq()] - << -quietMoveMalus; - - workerThread.mainHistory[us][quietsSearched[i].from_to()] << -quietMoveMalus; - update_continuation_histories(ss, pos.moved_piece(quietsSearched[i]), - quietsSearched[i].to_sq(), -quietMoveMalus); - } + update_quiet_histories(pos, ss, workerThread, quietsSearched[i], -quietMoveMalus); } else { @@ -1794,10 +1784,8 @@ void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { } } - // Updates move sorting heuristics -void update_quiet_stats( - const Position& pos, Stack* ss, Search::Worker& workerThread, Move move, int bonus) { +void update_refutations(const Position& pos, Stack* ss, Search::Worker& workerThread, Move move) { // Update killers if (ss->killers[0] != move) @@ -1806,10 +1794,6 @@ void update_quiet_stats( ss->killers[0] = move; } - Color us = pos.side_to_move(); - workerThread.mainHistory[us][move.from_to()] << bonus; - update_continuation_histories(ss, pos.moved_piece(move), move.to_sq(), bonus); - // Update countermove history if (((ss - 1)->currentMove).is_ok()) { @@ -1817,6 +1801,27 @@ void update_quiet_stats( workerThread.counterMoves[pos.piece_on(prevSq)][prevSq] = move; } } + +void update_quiet_histories( + const Position& pos, Stack* ss, Search::Worker& workerThread, Move move, int bonus) { + + Color us = pos.side_to_move(); + workerThread.mainHistory[us][move.from_to()] << bonus; + + update_continuation_histories(ss, pos.moved_piece(move), move.to_sq(), bonus); + + int pIndex = pawn_structure_index(pos); + workerThread.pawnHistory[pIndex][pos.moved_piece(move)][move.to_sq()] << bonus; +} + +// Updates move sorting heuristics +void update_quiet_stats( + const Position& pos, Stack* ss, Search::Worker& workerThread, Move move, int bonus) { + + update_refutations(pos, ss, workerThread, move); + update_quiet_histories(pos, ss, workerThread, move, bonus); +} + } // When playing with strength handicap, choose the best move among a set of RootMoves From f1612612457fd90f9842b2432d795ee6e2e26ebc Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 5 May 2024 05:20:05 +0300 Subject: [PATCH 1468/1766] Adjust history usage in moves loop pruning After experiments with conthist 5 addition failed really bad divions by 2 passed as a gainer. Passed STC: https://tests.stockfishchess.org/tests/view/6636d7114b68b70d858035ce LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 35936 W: 9287 L: 8976 D: 17673 Ptnml(0-2): 81, 4129, 9234, 4446, 78 Passed LTC: https://tests.stockfishchess.org/tests/view/6636ddb64b68b70d858040a8 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 82428 W: 21035 L: 20622 D: 40771 Ptnml(0-2): 29, 8985, 22775, 9394, 31 closes https://github.com/official-stockfish/Stockfish/pull/5217 Bench: 2309253 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 43f18af2156..a60f4d36ee4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -991,7 +991,7 @@ Value Search::Worker::search( int history = (*contHist[0])[movedPiece][move.to_sq()] + (*contHist[1])[movedPiece][move.to_sq()] - + (*contHist[3])[movedPiece][move.to_sq()] + + (*contHist[3])[movedPiece][move.to_sq()] / 2 + thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()]; // Continuation history based pruning (~2 Elo) From 61f12a4c383a76c5304aa2cf9cb6e47d5aae0606 Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Wed, 1 May 2024 15:54:17 +0800 Subject: [PATCH 1469/1766] Simplify accumulator refreshes Passed Non-Regression STC: https://tests.stockfishchess.org/tests/view/6631f5d5d01fb9ac9bcdc7d0 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 57472 W: 14979 L: 14784 D: 27709 Ptnml(0-2): 185, 6486, 15192, 6695, 178 closes https://github.com/official-stockfish/Stockfish/pull/5207 No functional change --- src/nnue/nnue_feature_transformer.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 018b715e638..2b11adefbbb 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -668,20 +668,20 @@ class FeatureTransformer { for (IndexType k = 0; k < NumRegs; ++k) acc[k] = entryTile[k]; - int i0 = 0; - for (; i0 < int(std::min(removed.size(), added.size())); ++i0) + int i = 0; + for (; i < int(std::min(removed.size(), added.size())); ++i) { - IndexType indexR = removed[i0]; + IndexType indexR = removed[i]; const IndexType offsetR = HalfDimensions * indexR + j * TileHeight; auto columnR = reinterpret_cast(&weights[offsetR]); - IndexType indexA = added[i0]; + IndexType indexA = added[i]; const IndexType offsetA = HalfDimensions * indexA + j * TileHeight; auto columnA = reinterpret_cast(&weights[offsetA]); for (unsigned k = 0; k < NumRegs; ++k) acc[k] = vec_add_16(vec_sub_16(acc[k], columnR[k]), columnA[k]); } - for (int i = i0; i < int(removed.size()); ++i) + for (; i < int(removed.size()); ++i) { IndexType index = removed[i]; const IndexType offset = HalfDimensions * index + j * TileHeight; @@ -690,7 +690,7 @@ class FeatureTransformer { for (unsigned k = 0; k < NumRegs; ++k) acc[k] = vec_sub_16(acc[k], column[k]); } - for (int i = i0; i < int(added.size()); ++i) + for (; i < int(added.size()); ++i) { IndexType index = added[i]; const IndexType offset = HalfDimensions * index + j * TileHeight; From 070e564c389eb2c263f3982060ab5899b67d0a62 Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Sun, 5 May 2024 07:36:48 +0800 Subject: [PATCH 1470/1766] VVLTC search tune This patch is the result of two tuning stages: 1. ~32k games at 60+0.6 th8: https://tests.stockfishchess.org/tests/view/662d9dea6115ff6764c7f817 2. ~193k games at 80+0.8 th6, based on PR #5211: https://tests.stockfishchess.org/tests/view/663587e273559a8aa857ca00. Based on extensive VVLTC tuning and testing both before and after #5211, it is observed that introduction of new extensions positively affected the search tune results. Passed VVLTC 70+0.7 th7 1st sprt: https://tests.stockfishchess.org/tests/view/6636c6f04b68b70d85801409 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 18566 W: 4864 L: 4620 D: 9082 Ptnml(0-2): 0, 1608, 5827, 1844, 4 Passed VVLTC 70+0.7 th7 2nd sprt: https://tests.stockfishchess.org/tests/view/6636d4b84b68b70d85802ab7 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 43142 W: 11141 L: 10838 D: 21163 Ptnml(0-2): 4, 3915, 13427, 4224, 1 Passed VVLTC 70+0.7 3rd sprt: https://tests.stockfishchess.org/tests/view/66376b4f9819650825aa230b LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 40322 W: 10374 L: 10076 D: 19872 Ptnml(0-2): 1, 3660, 12544, 3952, 4 The first two sprts were run against passed #5211. The third sprt was run against latest master. closes https://github.com/official-stockfish/Stockfish/pull/5216 Bench: 2180675 --- src/search.cpp | 80 +++++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index a60f4d36ee4..6830e4b1279 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -59,9 +59,9 @@ static constexpr double EvalLevel[10] = {0.981, 0.956, 0.895, 0.949, 0.913, // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) { - Value futilityMult = 118 - 45 * noTtCutNode; - Value improvingDeduction = 52 * improving * futilityMult / 32; - Value worseningDeduction = (316 + 48 * improving) * oppWorsening * futilityMult / 1024; + Value futilityMult = 126 - 46 * noTtCutNode; + Value improvingDeduction = 58 * improving * futilityMult / 32; + Value worseningDeduction = (323 + 52 * improving) * oppWorsening * futilityMult / 1024; return futilityMult * d - improvingDeduction - worseningDeduction; } @@ -73,15 +73,15 @@ constexpr int futility_move_count(bool improving, Depth depth) { // Add correctionHistory value to raw staticEval and guarantee evaluation does not hit the tablebase range Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { auto cv = w.correctionHistory[pos.side_to_move()][pawn_structure_index(pos)]; - v += cv * std::abs(cv) / 9260; + v += cv * std::abs(cv) / 7350; return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } // History and stats update bonus, based on depth -int stat_bonus(Depth d) { return std::clamp(214 * d - 318, 16, 1304); } +int stat_bonus(Depth d) { return std::clamp(208 * d - 297, 16, 1406); } // History and stats update malus, based on depth -int stat_malus(Depth d) { return (d < 4 ? 572 * d - 284 : 1355); } +int stat_malus(Depth d) { return (d < 4 ? 520 * d - 312 : 1479); } // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(size_t nodes) { return VALUE_DRAW - 1 + Value(nodes & 0x2); } @@ -310,12 +310,12 @@ void Search::Worker::iterative_deepening() { // Reset aspiration window starting size Value avg = rootMoves[pvIdx].averageScore; - delta = 10 + avg * avg / 11480; + delta = 10 + avg * avg / 9530; alpha = std::max(avg - delta, -VALUE_INFINITE); beta = std::min(avg + delta, VALUE_INFINITE); // Adjust optimism based on root move's averageScore (~4 Elo) - optimism[us] = 122 * avg / (std::abs(avg) + 92); + optimism[us] = 119 * avg / (std::abs(avg) + 88); optimism[~us] = -optimism[us]; // Start with a small aspiration window and, in the case of a fail @@ -502,10 +502,10 @@ void Search::Worker::clear() { for (StatsType c : {NoCaptures, Captures}) for (auto& to : continuationHistory[inCheck][c]) for (auto& h : to) - h->fill(-65); + h->fill(-60); for (size_t i = 1; i < reductions.size(); ++i) - reductions[i] = int((20.14 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); + reductions[i] = int((18.93 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); refreshTable.clear(networks); } @@ -738,7 +738,7 @@ Value Search::Worker::search( // Use static evaluation difference to improve quiet move ordering (~9 Elo) if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture) { - int bonus = std::clamp(-14 * int((ss - 1)->staticEval + ss->staticEval), -1644, 1384); + int bonus = std::clamp(-13 * int((ss - 1)->staticEval + ss->staticEval), -1796, 1526); bonus = bonus > 0 ? 2 * bonus : bonus / 2; thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) @@ -761,7 +761,7 @@ Value Search::Worker::search( // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. // Adjust razor margin according to cutoffCnt. (~1 Elo) - if (eval < alpha - 471 - (275 - 148 * ((ss + 1)->cutoffCnt > 3)) * depth * depth) + if (eval < alpha - 433 - (302 - 141 * ((ss + 1)->cutoffCnt > 3)) * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -770,23 +770,23 @@ Value Search::Worker::search( // Step 8. Futility pruning: child node (~40 Elo) // The depth condition is important for mate finding. - if (!ss->ttPv && depth < 12 + if (!ss->ttPv && depth < 11 && eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening) - - (ss - 1)->statScore / 286 + - (ss - 1)->statScore / 254 >= beta && eval >= beta && eval < VALUE_TB_WIN_IN_MAX_PLY && (!ttMove || ttCapture)) return beta > VALUE_TB_LOSS_IN_MAX_PLY ? (eval + beta) / 2 : eval; // Step 9. Null move search with verification search (~35 Elo) - if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 18001 - && eval >= beta && ss->staticEval >= beta - 21 * depth + 312 && !excludedMove + if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 16993 + && eval >= beta && ss->staticEval >= beta - 19 * depth + 326 && !excludedMove && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly && beta > VALUE_TB_LOSS_IN_MAX_PLY) { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and eval - Depth R = std::min(int(eval - beta) / 152, 6) + depth / 3 + 4; + Depth R = std::min(int(eval - beta) / 134, 6) + depth / 3 + 4; ss->currentMove = Move::null(); ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -834,7 +834,7 @@ Value Search::Worker::search( // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search returns a value // much above beta, we can (almost) safely prune the previous move. - probCutBeta = beta + 169 - 63 * improving; + probCutBeta = beta + 159 - 66 * improving; if ( !PvNode && depth > 3 && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY @@ -890,7 +890,7 @@ Value Search::Worker::search( moves_loop: // When in check, search starts here // Step 12. A small Probcut idea, when we are in check (~4 Elo) - probCutBeta = beta + 452; + probCutBeta = beta + 420; if (ss->inCheck && !PvNode && ttCapture && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 4 && ttValue >= probCutBeta && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) @@ -975,15 +975,15 @@ Value Search::Worker::search( // Futility pruning for captures (~2 Elo) if (!givesCheck && lmrDepth < 7 && !ss->inCheck) { - Value futilityValue = ss->staticEval + 285 + 277 * lmrDepth + Value futilityValue = ss->staticEval + 295 + 280 * lmrDepth + PieceValue[capturedPiece] + captHist / 7; if (futilityValue <= alpha) continue; } // SEE based pruning for captures and checks (~11 Elo) - int seeHist = std::clamp(captHist / 32, -199 * depth, 199 * depth); - if (!pos.see_ge(move, -203 * depth - seeHist)) + int seeHist = std::clamp(captHist / 32, -197 * depth, 196 * depth); + if (!pos.see_ge(move, -186 * depth - seeHist)) continue; } else @@ -995,18 +995,18 @@ Value Search::Worker::search( + thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()]; // Continuation history based pruning (~2 Elo) - if (lmrDepth < 6 && history < -4173 * depth) + if (lmrDepth < 6 && history < -4081 * depth) continue; history += 2 * thisThread->mainHistory[us][move.from_to()]; - lmrDepth += history / 5285; + lmrDepth += history / 4768; Value futilityValue = - ss->staticEval + (bestValue < ss->staticEval - 54 ? 128 : 57) + 131 * lmrDepth; + ss->staticEval + (bestValue < ss->staticEval - 52 ? 134 : 54) + 142 * lmrDepth; // Futility pruning: parent node (~13 Elo) - if (!ss->inCheck && lmrDepth < 14 && futilityValue <= alpha) + if (!ss->inCheck && lmrDepth < 13 && futilityValue <= alpha) { if (bestValue <= futilityValue && std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY && futilityValue < VALUE_TB_WIN_IN_MAX_PLY) @@ -1017,7 +1017,7 @@ Value Search::Worker::search( lmrDepth = std::max(lmrDepth, 0); // Prune moves with negative SEE (~4 Elo) - if (!pos.see_ge(move, -27 * lmrDepth * lmrDepth)) + if (!pos.see_ge(move, -28 * lmrDepth * lmrDepth)) continue; } } @@ -1037,11 +1037,11 @@ Value Search::Worker::search( // so changing them requires tests at these types of time controls. // Recursive singular search is avoided. if (!rootNode && move == ttMove && !excludedMove - && depth >= 4 - (thisThread->completedDepth > 33) + ss->ttPv + && depth >= 4 - (thisThread->completedDepth > 32) + ss->ttPv && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - (65 + 59 * (ss->ttPv && !PvNode)) * depth / 63; + Value singularBeta = ttValue - (65 + 52 * (ss->ttPv && !PvNode)) * depth / 63; Depth singularDepth = newDepth / 2; ss->excludedMove = move; @@ -1099,7 +1099,7 @@ Value Search::Worker::search( else if (PvNode && move == ttMove && move.to_sq() == prevSq && thisThread->captureHistory[movedPiece][move.to_sq()] [type_of(pos.piece_on(move.to_sq()))] - > 3807) + > 4016) extension = 1; } @@ -1151,10 +1151,10 @@ Value Search::Worker::search( ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + (*contHist[0])[movedPiece][move.to_sq()] + (*contHist[1])[movedPiece][move.to_sq()] - + (*contHist[3])[movedPiece][move.to_sq()] - 5024; + + (*contHist[3])[movedPiece][move.to_sq()] - 5078; // Decrease/increase reduction for moves with a good/bad history (~8 Elo) - r -= ss->statScore / 13182; + r -= ss->statScore / 12076; // Step 17. Late moves reduction / extension (LMR, ~117 Elo) if (depth >= 2 && moveCount > 1 + rootNode) @@ -1173,7 +1173,7 @@ Value Search::Worker::search( { // Adjust full-depth search based on LMR results - if the result // was good enough search deeper, if it was bad enough search shallower. - const bool doDeeperSearch = value > (bestValue + 42 + 2 * newDepth); // (~1 Elo) + const bool doDeeperSearch = value > (bestValue + 40 + 2 * newDepth); // (~1 Elo) const bool doShallowerSearch = value < bestValue + newDepth; // (~2 Elo) newDepth += doDeeperSearch - doShallowerSearch; @@ -1291,7 +1291,7 @@ Value Search::Worker::search( else { // Reduce other moves if we have found at least one score improvement (~2 Elo) - if (depth > 2 && depth < 12 && beta < 13546 && value > -13478) + if (depth > 2 && depth < 13 && beta < 15868 && value > -14630) depth -= 2; assert(depth > 0); @@ -1334,8 +1334,8 @@ Value Search::Worker::search( // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (depth > 5) + (PvNode || cutNode) + ((ss - 1)->statScore < -14761) - + ((ss - 1)->moveCount > 11) + (!ss->inCheck && bestValue <= ss->staticEval - 142) + int bonus = (depth > 5) + (PvNode || cutNode) + ((ss - 1)->statScore < -14455) + + ((ss - 1)->moveCount > 10) + (!ss->inCheck && bestValue <= ss->staticEval - 130) + (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 77); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); @@ -1495,7 +1495,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, if (bestValue > alpha) alpha = bestValue; - futilityBase = ss->staticEval + 250; + futilityBase = ss->staticEval + 270; } const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, @@ -1575,7 +1575,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, continue; // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, -79)) + if (!pos.see_ge(move, -69)) continue; } @@ -1643,7 +1643,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth Search::Worker::reduction(bool i, Depth d, int mn, int delta) { int reductionScale = reductions[d] * reductions[mn]; - return (reductionScale + 1150 - delta * 832 / rootDelta) / 1024 + (!i && reductionScale > 1025); + return (reductionScale + 1318 - delta * 760 / rootDelta) / 1024 + (!i && reductionScale > 1066); } TimePoint Search::Worker::elapsed() const { @@ -1736,7 +1736,7 @@ void update_all_stats(const Position& pos, if (!pos.capture_stage(bestMove)) { - int bestMoveBonus = bestValue > beta + 185 ? quietMoveBonus // larger bonus + int bestMoveBonus = bestValue > beta + 165 ? quietMoveBonus // larger bonus : stat_bonus(depth); // smaller bonus update_quiet_stats(pos, ss, workerThread, bestMove, bestMoveBonus); From 2d5e248f58595c81c1d075f5874e4c18ca8b1998 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Tue, 7 May 2024 15:03:58 +0300 Subject: [PATCH 1471/1766] Tweak reduction formula based on depth The idea came to me by checking for trends from the megafauzi tunes, since the values of the divisor for this specific formula were as follows: stc: 15990 mtc: 16117 ltc: 14805 vltc: 12719 new vltc passed by Muzhen: 12076 This shows a clear trend related to time control, the higher it is, the lower the optimum value for the divisor seems to be. So I tried a simple formula, using educated guesses based on some calculations, tests show it works pretty fine, and it can still be further tuned at VLTC in the future to scale even better. Passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 431360 W: 110791 L: 109898 D: 210671 Ptnml(0-2): 1182, 50846, 110698, 51805, 1149 https://tests.stockfishchess.org/tests/view/663770409819650825aa269f Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 114114 W: 29109 L: 28625 D: 56380 Ptnml(0-2): 105, 12628, 31101, 13124, 99 https://tests.stockfishchess.org/tests/view/66378c099819650825aa73f6 https://github.com/official-stockfish/Stockfish/pull/5223 bench: 2273551 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 6830e4b1279..2c3fc56e3b2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1154,7 +1154,7 @@ Value Search::Worker::search( + (*contHist[3])[movedPiece][move.to_sq()] - 5078; // Decrease/increase reduction for moves with a good/bad history (~8 Elo) - r -= ss->statScore / 12076; + r -= ss->statScore / std::max(21000 - (depth * 305), 12000); // Step 17. Late moves reduction / extension (LMR, ~117 Elo) if (depth >= 2 && moveCount > 1 + rootNode) From 3bdfa0fb4a837f51f142cc1e862837c6f9167796 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Tue, 7 May 2024 15:03:58 +0300 Subject: [PATCH 1472/1766] Depth dependent statscore based reductions Test a modification of Fawzi's PR #5223, against that PR. parameters locally tuned with nevergrad4sf. passed STC: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 1047424 W: 271478 L: 269649 D: 506297 Ptnml(0-2): 3851, 124543, 265290, 125982, 4046 https://tests.stockfishchess.org/tests/view/663b0889ca93dad645f7c58c passed LTC: LLR: 2.96 (-2.94,2.94) <0.50,2.50> Total: 796236 W: 201712 L: 199825 D: 394699 Ptnml(0-2): 361, 88381, 218778, 90206, 392 https://tests.stockfishchess.org/tests/view/663be6adca93dad645f7f509 https://github.com/official-stockfish/Stockfish/pull/5228 Bench: 3346224 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 2c3fc56e3b2..3eec00b0852 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1154,7 +1154,7 @@ Value Search::Worker::search( + (*contHist[3])[movedPiece][move.to_sq()] - 5078; // Decrease/increase reduction for moves with a good/bad history (~8 Elo) - r -= ss->statScore / std::max(21000 - (depth * 305), 12000); + r -= ss->statScore / (17662 - std::min(depth, 16) * 105); // Step 17. Late moves reduction / extension (LMR, ~117 Elo) if (depth >= 2 && moveCount > 1 + rootNode) From d1b8d8bab377eb873385bb4f8662062398f16686 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Wed, 8 May 2024 21:59:03 +0300 Subject: [PATCH 1473/1766] Refactor quiet moves pruning in qsearch Make it formula more in line with what we use in search - current formula is more or less the one we used years ago for search but since then it was remade, this patch remakes qsearch formula to almost exactly the same as we use in search - with sum of conthist 0, 1 and pawn structure history. Passed STC: https://tests.stockfishchess.org/tests/view/6639c8421343f0cb16716206 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 84992 W: 22414 L: 22019 D: 40559 Ptnml(0-2): 358, 9992, 21440, 10309, 397 Passed LTC: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 119136 W: 30407 L: 29916 D: 58813 Ptnml(0-2): 46, 13192, 32622, 13641, 67 closes https://github.com/official-stockfish/Stockfish/pull/5224 Bench: 2138659 --- src/search.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 3eec00b0852..633f9b51513 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1570,8 +1570,9 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, break; // Continuation history based pruning (~3 Elo) - if (!capture && (*contHist[0])[pos.moved_piece(move)][move.to_sq()] < 0 - && (*contHist[1])[pos.moved_piece(move)][move.to_sq()] < 0) + if (!capture && (*contHist[0])[pos.moved_piece(move)][move.to_sq()] + + (*contHist[1])[pos.moved_piece(move)][move.to_sq()] + + thisThread->pawnHistory[pawn_structure_index(pos)][pos.moved_piece(move)][move.to_sq()] <= 4000) continue; // Do not search moves with bad enough SEE values (~5 Elo) From db147fe2586527a854516016699949af53dc5b17 Mon Sep 17 00:00:00 2001 From: rn5f107s2 Date: Wed, 8 May 2024 22:08:56 +0200 Subject: [PATCH 1474/1766] IIR on cutnodes if there is a ttMove but the ttBound is upper If there is an upper bound stored in the transposition table, but we still have a ttMove, the upperbound indicates that the last time the ttMove was tried, it failed low. This fail low indicates that the ttMove may not be good, so this patch introduces a depth reduction of one for cutnodes with such ttMoves. Passed STC: https://tests.stockfishchess.org/tests/view/663be4d1ca93dad645f7f45f LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 139424 W: 35900 L: 35433 D: 68091 Ptnml(0-2): 425, 16357, 35743, 16700, 487 Passed LTC: https://tests.stockfishchess.org/tests/view/663bec95ca93dad645f7f5c8 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 129690 W: 32902 L: 32390 D: 64398 Ptnml(0-2): 63, 14304, 35610, 14794, 74 closes https://github.com/official-stockfish/Stockfish/pull/5227 bench 2257437 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 633f9b51513..767ea2380b4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -828,8 +828,8 @@ Value Search::Worker::search( return qsearch(pos, ss, alpha, beta); // For cutNodes without a ttMove, we decrease depth by 2 if depth is high enough. - if (cutNode && depth >= 8 && !ttMove) - depth -= 2; + if (cutNode && depth >= 8 && (!ttMove || tte->bound() == BOUND_UPPER)) + depth -= 1 + !ttMove; // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search returns a value From 2dbb44e28d2e5b3c72ddbbd6f436d41f75031a22 Mon Sep 17 00:00:00 2001 From: MinetaS Date: Wed, 8 May 2024 03:26:09 +0900 Subject: [PATCH 1475/1766] Fix nodestime 1. The current time management system utilizes limits.inc and limits.time, which can represent either milliseconds or node count, depending on whether the nodestime option is active. There have been several modifications which brought Elo gain for typical uses (i.e. real-time matches), however some of these changes overlooked such distinction. This patch adjusts constants and multiplication/division to more accurately simulate real TC conditions when nodestime is used. 2. The advance_nodes_time function has a bug that can extend the time limit when availableNodes reaches exact zero. This patch fixes the bug by initializing the variable to -1 and make sure it does not go below zero. 3. elapsed_time function is newly introduced to print PV in the UCI output based on real time. This makes PV output more consistent with the behavior of trivial use cases. closes https://github.com/official-stockfish/Stockfish/pull/5186 No functional changes --- src/search.cpp | 22 ++++++++++++++++------ src/search.h | 1 + src/timeman.cpp | 46 +++++++++++++++++++++++++++------------------- src/timeman.h | 5 +++-- 4 files changed, 47 insertions(+), 27 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 767ea2380b4..684b760ecfb 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -190,8 +190,8 @@ void Search::Worker::start_searching() { // When playing in 'nodes as time' mode, subtract the searched nodes from // the available ones before exiting. if (limits.npmsec) - main_manager()->tm.advance_nodes_time(limits.inc[rootPos.side_to_move()] - - threads.nodes_searched()); + main_manager()->tm.advance_nodes_time(threads.nodes_searched() + - limits.inc[rootPos.side_to_move()]); Worker* bestThread = this; Skill skill = @@ -347,7 +347,7 @@ void Search::Worker::iterative_deepening() { // When failing high/low give some update (without cluttering // the UI) before a re-search. if (mainThread && multiPV == 1 && (bestValue <= alpha || bestValue >= beta) - && elapsed() > 3000) + && elapsed_time() > 3000) main_manager()->pv(*this, threads, tt, rootDepth); // In case of failing low/high increase aspiration window and @@ -378,7 +378,7 @@ void Search::Worker::iterative_deepening() { std::stable_sort(rootMoves.begin() + pvFirst, rootMoves.begin() + pvIdx + 1); if (mainThread - && (threads.stop || pvIdx + 1 == multiPV || elapsed() > 3000) + && (threads.stop || pvIdx + 1 == multiPV || elapsed_time() > 3000) // A thread that aborted search can have mated-in/TB-loss PV and score // that cannot be trusted, i.e. it can be delayed or refuted if we would have // had time to fully search other root-moves. Thus we suppress this output and @@ -935,7 +935,7 @@ Value Search::Worker::search( ss->moveCount = ++moveCount; - if (rootNode && is_mainthread() && elapsed() > 3000) + if (rootNode && is_mainthread() && elapsed_time() > 3000) { main_manager()->updates.onIter( {depth, UCIEngine::move(move, pos.is_chess960()), moveCount + thisThread->pvIdx}); @@ -1647,10 +1647,20 @@ Depth Search::Worker::reduction(bool i, Depth d, int mn, int delta) { return (reductionScale + 1318 - delta * 760 / rootDelta) / 1024 + (!i && reductionScale > 1066); } +// elapsed() returns the time elapsed since the search started. If the +// 'nodestime' option is enabled, it will return the count of nodes searched +// instead. This function is called to check whether the search should be +// stopped based on predefined thresholds like time limits or nodes searched. +// +// elapsed_time() returns the actual time elapsed since the start of the search. +// This function is intended for use only when printing PV outputs, and not used +// for making decisions within the search algorithm itself. TimePoint Search::Worker::elapsed() const { return main_manager()->tm.elapsed([this]() { return threads.nodes_searched(); }); } +TimePoint Search::Worker::elapsed_time() const { return main_manager()->tm.elapsed_time(); } + namespace { // Adjusts a mate or TB score from "plies to mate from the root" @@ -1900,7 +1910,7 @@ void SearchManager::pv(const Search::Worker& worker, const auto& rootMoves = worker.rootMoves; const auto& pos = worker.rootPos; size_t pvIdx = worker.pvIdx; - TimePoint time = tm.elapsed([nodes]() { return nodes; }) + 1; + TimePoint time = tm.elapsed_time() + 1; size_t multiPV = std::min(size_t(worker.options["MultiPV"]), rootMoves.size()); uint64_t tbHits = threads.tb_hits() + (worker.tbConfig.rootInTB ? rootMoves.size() : 0); diff --git a/src/search.h b/src/search.h index cb73a5afddf..c824daf931c 100644 --- a/src/search.h +++ b/src/search.h @@ -276,6 +276,7 @@ class Worker { } TimePoint elapsed() const; + TimePoint elapsed_time() const; LimitsType limits; diff --git a/src/timeman.cpp b/src/timeman.cpp index c651745f02e..4feb329b335 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -32,12 +32,12 @@ TimePoint TimeManagement::optimum() const { return optimumTime; } TimePoint TimeManagement::maximum() const { return maximumTime; } void TimeManagement::clear() { - availableNodes = 0; // When in 'nodes as time' mode + availableNodes = -1; // When in 'nodes as time' mode } void TimeManagement::advance_nodes_time(std::int64_t nodes) { assert(useNodesTime); - availableNodes += nodes; + availableNodes = std::max(int64_t(0), availableNodes - nodes); } // Called at the beginning of the search and calculates @@ -48,14 +48,17 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply, const OptionsMap& options) { - // If we have no time, no need to initialize TM, except for the start time, - // which is used by movetime. - startTime = limits.startTime; + TimePoint npmsec = TimePoint(options["nodestime"]); + + // If we have no time, we don't need to fully initialize TM. + // startTime is used by movetime and useNodesTime is used in elapsed calls. + startTime = limits.startTime; + useNodesTime = npmsec != 0; + if (limits.time[us] == 0) return; TimePoint moveOverhead = TimePoint(options["Move Overhead"]); - TimePoint npmsec = TimePoint(options["nodestime"]); // optScale is a percentage of available time to use for the current move. // maxScale is a multiplier applied to optimumTime. @@ -65,26 +68,31 @@ void TimeManagement::init(Search::LimitsType& limits, // to nodes, and use resulting values in time management formulas. // WARNING: to avoid time losses, the given npmsec (nodes per millisecond) // must be much lower than the real engine speed. - if (npmsec) + if (useNodesTime) { - useNodesTime = true; - - if (!availableNodes) // Only once at game start + if (availableNodes == -1) // Only once at game start availableNodes = npmsec * limits.time[us]; // Time is in msec // Convert from milliseconds to nodes limits.time[us] = TimePoint(availableNodes); limits.inc[us] *= npmsec; limits.npmsec = npmsec; + moveOverhead *= npmsec; } + // These numbers are used where multiplications, divisions or comparisons + // with constants are involved. + const int64_t scaleFactor = useNodesTime ? npmsec : 1; + const TimePoint scaledTime = limits.time[us] / scaleFactor; + const TimePoint scaledInc = limits.inc[us] / scaleFactor; + // Maximum move horizon of 50 moves int mtg = limits.movestogo ? std::min(limits.movestogo, 50) : 50; - // if less than one second, gradually reduce mtg - if (limits.time[us] < 1000 && (double(mtg) / limits.time[us] > 0.05)) + // If less than one second, gradually reduce mtg + if (scaledTime < 1000 && double(mtg) / scaledInc > 0.05) { - mtg = limits.time[us] * 0.05; + mtg = scaledTime * 0.05; } // Make sure timeLeft is > 0 since we may use it as a divisor @@ -97,15 +105,15 @@ void TimeManagement::init(Search::LimitsType& limits, if (limits.movestogo == 0) { // Use extra time with larger increments - double optExtra = limits.inc[us] < 500 ? 1.0 : 1.13; + double optExtra = scaledInc < 500 ? 1.0 : 1.13; // Calculate time constants based on current time left. - double optConstant = - std::min(0.00308 + 0.000319 * std::log10(limits.time[us] / 1000.0), 0.00506); - double maxConstant = std::max(3.39 + 3.01 * std::log10(limits.time[us] / 1000.0), 2.93); + double logTimeInSec = std::log10(scaledTime / 1000.0); + double optConstant = std::min(0.00308 + 0.000319 * logTimeInSec, 0.00506); + double maxConstant = std::max(3.39 + 3.01 * logTimeInSec, 2.93); optScale = std::min(0.0122 + std::pow(ply + 2.95, 0.462) * optConstant, - 0.213 * limits.time[us] / double(timeLeft)) + 0.213 * limits.time[us] / timeLeft) * optExtra; maxScale = std::min(6.64, maxConstant + ply / 12.0); } @@ -113,7 +121,7 @@ void TimeManagement::init(Search::LimitsType& limits, // x moves in y seconds (+ z increment) else { - optScale = std::min((0.88 + ply / 116.4) / mtg, 0.88 * limits.time[us] / double(timeLeft)); + optScale = std::min((0.88 + ply / 116.4) / mtg, 0.88 * limits.time[us] / timeLeft); maxScale = std::min(6.3, 1.5 + 0.11 * mtg); } diff --git a/src/timeman.h b/src/timeman.h index 35c3cfc0680..1b6bd849ae2 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -42,8 +42,9 @@ class TimeManagement { TimePoint maximum() const; template TimePoint elapsed(FUNC nodes) const { - return useNodesTime ? TimePoint(nodes()) : now() - startTime; + return useNodesTime ? TimePoint(nodes()) : elapsed_time(); } + TimePoint elapsed_time() const { return now() - startTime; }; void clear(); void advance_nodes_time(std::int64_t nodes); @@ -53,7 +54,7 @@ class TimeManagement { TimePoint optimumTime; TimePoint maximumTime; - std::int64_t availableNodes = 0; // When in 'nodes as time' mode + std::int64_t availableNodes = -1; // When in 'nodes as time' mode bool useNodesTime = false; // True if we are in 'nodes as time' mode }; From 9d6dab06a8274c4e09b437110f86bdb1ea7edb0f Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sun, 5 May 2024 03:10:26 +0300 Subject: [PATCH 1476/1766] simplify moveCountPruning no (significant) speedup upon renewed testing Passed stc: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 88992 W: 22779 L: 22633 D: 43580 Ptnml(0-2): 137, 8706, 26681, 8818, 154 https://tests.stockfishchess.org/tests/view/6636c4844b68b70d85800dae closes https://github.com/official-stockfish/Stockfish/pull/5213 No functional change. --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 684b760ecfb..1d0cb4ab69e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -960,8 +960,7 @@ Value Search::Worker::search( if (!rootNode && pos.non_pawn_material(us) && bestValue > VALUE_TB_LOSS_IN_MAX_PLY) { // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold (~8 Elo) - if (!moveCountPruning) - moveCountPruning = moveCount >= futility_move_count(improving, depth); + moveCountPruning = moveCount >= futility_move_count(improving, depth); // Reduced depth of the next LMR search int lmrDepth = newDepth - r; From 3b4ddf4ae6362ddef063cc644d1466754015482e Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Mon, 6 May 2024 20:18:12 +0300 Subject: [PATCH 1477/1766] Simplify away conthist 3 from statscore Following previous elo gainer that gained by making conthist 3 less important in pruning this patch simplifies away this history from calculation of statscore. Passed STC: https://tests.stockfishchess.org/tests/view/6637aa7e9819650825aa93e0 LLR: 3.00 (-2.94,2.94) <-1.75,0.25> Total: 35392 W: 9352 L: 9120 D: 16920 Ptnml(0-2): 141, 4145, 8888, 4385, 137 Passed LTC: https://tests.stockfishchess.org/tests/view/66383cd8493aaaf4b7ea90c5 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 33948 W: 8714 L: 8503 D: 16731 Ptnml(0-2): 39, 3701, 9270, 3938, 26 closes https://github.com/official-stockfish/Stockfish/pull/5220 Bench: 2508571 --- src/search.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 1d0cb4ab69e..d9f997e8ee5 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1149,8 +1149,7 @@ Value Search::Worker::search( ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + (*contHist[0])[movedPiece][move.to_sq()] - + (*contHist[1])[movedPiece][move.to_sq()] - + (*contHist[3])[movedPiece][move.to_sq()] - 5078; + + (*contHist[1])[movedPiece][move.to_sq()] - 5078; // Decrease/increase reduction for moves with a good/bad history (~8 Elo) r -= ss->statScore / (17662 - std::min(depth, 16) * 105); @@ -1569,9 +1568,12 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, break; // Continuation history based pruning (~3 Elo) - if (!capture && (*contHist[0])[pos.moved_piece(move)][move.to_sq()] - + (*contHist[1])[pos.moved_piece(move)][move.to_sq()] - + thisThread->pawnHistory[pawn_structure_index(pos)][pos.moved_piece(move)][move.to_sq()] <= 4000) + if (!capture + && (*contHist[0])[pos.moved_piece(move)][move.to_sq()] + + (*contHist[1])[pos.moved_piece(move)][move.to_sq()] + + thisThread->pawnHistory[pawn_structure_index(pos)][pos.moved_piece(move)] + [move.to_sq()] + <= 4000) continue; // Do not search moves with bad enough SEE values (~5 Elo) From 23439e4096bc28deb2e4e935f24c5ddb22999dc5 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Tue, 7 May 2024 09:27:04 +0300 Subject: [PATCH 1478/1766] Remove conthist 3 from moves loop pruning Followup to previous gainer that made it twice less impactful there - this patch removes it entirely as a simplification. Passed STC: https://tests.stockfishchess.org/tests/view/6637aa7e9819650825aa93e0 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 26208 W: 6930 L: 6694 D: 12584 Ptnml(0-2): 113, 2997, 6652, 3225, 117 Passed LTC: https://tests.stockfishchess.org/tests/view/66383cba493aaaf4b7ea90c2 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 67866 W: 17294 L: 17118 D: 33454 Ptnml(0-2): 46, 7627, 18415, 7795, 50 closes https://github.com/official-stockfish/Stockfish/pull/5221 Bench: 2691699 --- src/search.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index d9f997e8ee5..448da7e2546 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -990,7 +990,6 @@ Value Search::Worker::search( int history = (*contHist[0])[movedPiece][move.to_sq()] + (*contHist[1])[movedPiece][move.to_sq()] - + (*contHist[3])[movedPiece][move.to_sq()] / 2 + thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()]; // Continuation history based pruning (~2 Elo) From 574ad14b323465314c8d5d5a81af995cb58b07c9 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Thu, 9 May 2024 02:56:43 +0300 Subject: [PATCH 1479/1766] Simplify depth formula based on score improvement Simplify depth formula based on score improvement. This idea was first tried by cj5716 Passed STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 347104 W: 89683 L: 89804 D: 167617 Ptnml(0-2): 1357, 38824, 93307, 38711, 1353 https://tests.stockfishchess.org/tests/view/66378edf9819650825aa75d0 Passed LTC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 63000 W: 15851 L: 15694 D: 31455 Ptnml(0-2): 22, 5396, 20499, 5569, 14 https://tests.stockfishchess.org/tests/view/663c04e5c0b75d7f7b97d461 closes https://github.com/official-stockfish/Stockfish/pull/5225 Bench: 2691699 Co-Authored-By: cj5716 <125858804+cj5716@users.noreply.github.com> --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 448da7e2546..fdf9871cd0c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1288,7 +1288,7 @@ Value Search::Worker::search( else { // Reduce other moves if we have found at least one score improvement (~2 Elo) - if (depth > 2 && depth < 13 && beta < 15868 && value > -14630) + if (depth > 2 && depth < 13 && abs(value) < VALUE_TB_WIN_IN_MAX_PLY) depth -= 2; assert(depth > 0); From c43425b0b1167665b2f9520690e639c80977c067 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Wed, 8 May 2024 14:26:01 -0700 Subject: [PATCH 1480/1766] Simplify Away Negative Extension This patch simplifies away the negative extension applied when the value returned by the transposition table is assumed to fail low over the value of reduced search. Passed STC: LLR: 2.99 (-2.94,2.94) <-1.75,0.25> Total: 248736 W: 64293 L: 64302 D: 120141 Ptnml(0-2): 925, 29833, 62831, 29884, 895 https://tests.stockfishchess.org/tests/view/663bee3bca93dad645f7f64a Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 254970 W: 64289 L: 64308 D: 126373 Ptnml(0-2): 110, 28428, 70422, 28421, 104 https://tests.stockfishchess.org/tests/view/663c11f0c0b75d7f7b97d4bb closes https://github.com/official-stockfish/Stockfish/pull/5226 Bench: 2353057 --- src/search.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index fdf9871cd0c..4572ffc900d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1087,10 +1087,6 @@ Value Search::Worker::search( // If we are on a cutNode but the ttMove is not assumed to fail high over current beta (~1 Elo) else if (cutNode) extension = -2; - - // If the ttMove is assumed to fail low over the value of the reduced search (~1 Elo) - else if (ttValue <= value) - extension = -1; } // Extension for capturing the previous moved piece (~0 Elo on STC, ~1 Elo on LTC) From b8812138e8e4e6ebd9d1c46ca9da15ddab1eb1ae Mon Sep 17 00:00:00 2001 From: xu-shawn <50402888+xu-shawn@users.noreply.github.com> Date: Thu, 9 May 2024 00:11:09 -0700 Subject: [PATCH 1481/1766] Fix usage of abs vs std::abs closes https://github.com/official-stockfish/Stockfish/pull/5229 no functional change --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 4572ffc900d..6c30c3e9181 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1284,7 +1284,7 @@ Value Search::Worker::search( else { // Reduce other moves if we have found at least one score improvement (~2 Elo) - if (depth > 2 && depth < 13 && abs(value) < VALUE_TB_WIN_IN_MAX_PLY) + if (depth > 2 && depth < 13 && std::abs(value) < VALUE_TB_WIN_IN_MAX_PLY) depth -= 2; assert(depth > 0); From 540545d12792dc554e3a4cd1b09633c31a16d31b Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Thu, 9 May 2024 00:38:43 -0700 Subject: [PATCH 1482/1766] simplify away quietCheckEvasions pruning simplifies away the pruning of quiet evasion moves in quiescent search. Passed STC: LLR: 2.98 (-2.94,2.94) <-1.75,0.25> Total: 343520 W: 88356 L: 88470 D: 166694 Ptnml(0-2): 1061, 40073, 89706, 39759, 1161 https://tests.stockfishchess.org/tests/view/663c7ddfc0b75d7f7b980f3b Passed LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 168744 W: 42454 L: 42384 D: 83906 Ptnml(0-2): 75, 18678, 46782, 18776, 61 https://tests.stockfishchess.org/tests/view/663ce34fc0b75d7f7b981ed9 closes https://github.com/official-stockfish/Stockfish/pull/5231 bench 3681552 --- src/search.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 6c30c3e9181..3867a3975df 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1502,8 +1502,6 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory, contHist, &thisThread->pawnHistory); - int quietCheckEvasions = 0; - // Step 5. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. while ((move = mp.next_move()) != Move::none()) @@ -1556,12 +1554,6 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, } } - // We prune after the second quiet check evasion move, where being 'in check' is - // implicitly checked through the counter, and being a 'quiet move' apart from - // being a tt move is assumed after an increment because captures are pushed ahead. - if (quietCheckEvasions > 1) - break; - // Continuation history based pruning (~3 Elo) if (!capture && (*contHist[0])[pos.moved_piece(move)][move.to_sq()] @@ -1585,8 +1577,6 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, &thisThread ->continuationHistory[ss->inCheck][capture][pos.moved_piece(move)][move.to_sq()]; - quietCheckEvasions += !capture && ss->inCheck; - // Step 7. Make and search the move thisThread->nodes.fetch_add(1, std::memory_order_relaxed); pos.do_move(move, st, givesCheck); From 813c5aa5329011e218dad8dc53d61504cecadc3f Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Sun, 12 May 2024 17:49:30 +0800 Subject: [PATCH 1483/1766] VVLTC search tune Tuned at 111k games of VVLTC. Passed VVLTC 1st sprt: https://tests.stockfishchess.org/tests/view/664090c6d163897c63214324 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 83046 W: 21071 L: 20747 D: 41228 Ptnml(0-2): 2, 7574, 26048, 7896, 3 Passed VVLTC 2nd sprt: https://tests.stockfishchess.org/tests/view/6640cb2abaa6260a5688dc17 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 68630 W: 17620 L: 17270 D: 33740 Ptnml(0-2): 4, 6242, 21471, 6596, 2 closes https://github.com/official-stockfish/Stockfish/pull/5240 Bench: 1752471 --- src/search.cpp | 84 +++++++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 3867a3975df..1d9e0d81abd 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -59,9 +59,9 @@ static constexpr double EvalLevel[10] = {0.981, 0.956, 0.895, 0.949, 0.913, // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) { - Value futilityMult = 126 - 46 * noTtCutNode; - Value improvingDeduction = 58 * improving * futilityMult / 32; - Value worseningDeduction = (323 + 52 * improving) * oppWorsening * futilityMult / 1024; + Value futilityMult = 131 - 48 * noTtCutNode; + Value improvingDeduction = 57 * improving * futilityMult / 32; + Value worseningDeduction = (309 + 52 * improving) * oppWorsening * futilityMult / 1024; return futilityMult * d - improvingDeduction - worseningDeduction; } @@ -73,15 +73,15 @@ constexpr int futility_move_count(bool improving, Depth depth) { // Add correctionHistory value to raw staticEval and guarantee evaluation does not hit the tablebase range Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { auto cv = w.correctionHistory[pos.side_to_move()][pawn_structure_index(pos)]; - v += cv * std::abs(cv) / 7350; + v += cv * std::abs(cv) / 7179; return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } // History and stats update bonus, based on depth -int stat_bonus(Depth d) { return std::clamp(208 * d - 297, 16, 1406); } +int stat_bonus(Depth d) { return std::clamp(200 * d - 280, 16, 1495); } // History and stats update malus, based on depth -int stat_malus(Depth d) { return (d < 4 ? 520 * d - 312 : 1479); } +int stat_malus(Depth d) { return (d < 4 ? 586 * d - 284 : 1639); } // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(size_t nodes) { return VALUE_DRAW - 1 + Value(nodes & 0x2); } @@ -310,12 +310,12 @@ void Search::Worker::iterative_deepening() { // Reset aspiration window starting size Value avg = rootMoves[pvIdx].averageScore; - delta = 10 + avg * avg / 9530; + delta = 10 + avg * avg / 9474; alpha = std::max(avg - delta, -VALUE_INFINITE); beta = std::min(avg + delta, VALUE_INFINITE); // Adjust optimism based on root move's averageScore (~4 Elo) - optimism[us] = 119 * avg / (std::abs(avg) + 88); + optimism[us] = 117 * avg / (std::abs(avg) + 88); optimism[~us] = -optimism[us]; // Start with a small aspiration window and, in the case of a fail @@ -502,10 +502,10 @@ void Search::Worker::clear() { for (StatsType c : {NoCaptures, Captures}) for (auto& to : continuationHistory[inCheck][c]) for (auto& h : to) - h->fill(-60); + h->fill(-62); for (size_t i = 1; i < reductions.size(); ++i) - reductions[i] = int((18.93 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); + reductions[i] = int((21.19 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); refreshTable.clear(networks); } @@ -738,7 +738,7 @@ Value Search::Worker::search( // Use static evaluation difference to improve quiet move ordering (~9 Elo) if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture) { - int bonus = std::clamp(-13 * int((ss - 1)->staticEval + ss->staticEval), -1796, 1526); + int bonus = std::clamp(-12 * int((ss - 1)->staticEval + ss->staticEval), -1749, 1602); bonus = bonus > 0 ? 2 * bonus : bonus / 2; thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) @@ -761,7 +761,7 @@ Value Search::Worker::search( // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. // Adjust razor margin according to cutoffCnt. (~1 Elo) - if (eval < alpha - 433 - (302 - 141 * ((ss + 1)->cutoffCnt > 3)) * depth * depth) + if (eval < alpha - 473 - (308 - 138 * ((ss + 1)->cutoffCnt > 3)) * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -772,21 +772,21 @@ Value Search::Worker::search( // The depth condition is important for mate finding. if (!ss->ttPv && depth < 11 && eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening) - - (ss - 1)->statScore / 254 + - (ss - 1)->statScore / 258 >= beta && eval >= beta && eval < VALUE_TB_WIN_IN_MAX_PLY && (!ttMove || ttCapture)) return beta > VALUE_TB_LOSS_IN_MAX_PLY ? (eval + beta) / 2 : eval; // Step 9. Null move search with verification search (~35 Elo) - if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 16993 - && eval >= beta && ss->staticEval >= beta - 19 * depth + 326 && !excludedMove + if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 16079 + && eval >= beta && ss->staticEval >= beta - 21 * depth + 324 && !excludedMove && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly && beta > VALUE_TB_LOSS_IN_MAX_PLY) { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and eval - Depth R = std::min(int(eval - beta) / 134, 6) + depth / 3 + 4; + Depth R = std::min(int(eval - beta) / 144, 6) + depth / 3 + 4; ss->currentMove = Move::null(); ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -834,7 +834,7 @@ Value Search::Worker::search( // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search returns a value // much above beta, we can (almost) safely prune the previous move. - probCutBeta = beta + 159 - 66 * improving; + probCutBeta = beta + 177 - 65 * improving; if ( !PvNode && depth > 3 && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY @@ -890,7 +890,7 @@ Value Search::Worker::search( moves_loop: // When in check, search starts here // Step 12. A small Probcut idea, when we are in check (~4 Elo) - probCutBeta = beta + 420; + probCutBeta = beta + 428; if (ss->inCheck && !PvNode && ttCapture && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 4 && ttValue >= probCutBeta && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) @@ -974,15 +974,15 @@ Value Search::Worker::search( // Futility pruning for captures (~2 Elo) if (!givesCheck && lmrDepth < 7 && !ss->inCheck) { - Value futilityValue = ss->staticEval + 295 + 280 * lmrDepth + Value futilityValue = ss->staticEval + 305 + 272 * lmrDepth + PieceValue[capturedPiece] + captHist / 7; if (futilityValue <= alpha) continue; } // SEE based pruning for captures and checks (~11 Elo) - int seeHist = std::clamp(captHist / 32, -197 * depth, 196 * depth); - if (!pos.see_ge(move, -186 * depth - seeHist)) + int seeHist = std::clamp(captHist / 32, -185 * depth, 182 * depth); + if (!pos.see_ge(move, -176 * depth - seeHist)) continue; } else @@ -993,18 +993,18 @@ Value Search::Worker::search( + thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()]; // Continuation history based pruning (~2 Elo) - if (lmrDepth < 6 && history < -4081 * depth) + if (lmrDepth < 6 && history < -4360 * depth) continue; history += 2 * thisThread->mainHistory[us][move.from_to()]; - lmrDepth += history / 4768; + lmrDepth += history / 4507; Value futilityValue = - ss->staticEval + (bestValue < ss->staticEval - 52 ? 134 : 54) + 142 * lmrDepth; + ss->staticEval + (bestValue < ss->staticEval - 54 ? 142 : 55) + 132 * lmrDepth; // Futility pruning: parent node (~13 Elo) - if (!ss->inCheck && lmrDepth < 13 && futilityValue <= alpha) + if (!ss->inCheck && lmrDepth < 11 && futilityValue <= alpha) { if (bestValue <= futilityValue && std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY && futilityValue < VALUE_TB_WIN_IN_MAX_PLY) @@ -1015,7 +1015,7 @@ Value Search::Worker::search( lmrDepth = std::max(lmrDepth, 0); // Prune moves with negative SEE (~4 Elo) - if (!pos.see_ge(move, -28 * lmrDepth * lmrDepth)) + if (!pos.see_ge(move, -27 * lmrDepth * lmrDepth)) continue; } } @@ -1035,11 +1035,11 @@ Value Search::Worker::search( // so changing them requires tests at these types of time controls. // Recursive singular search is avoided. if (!rootNode && move == ttMove && !excludedMove - && depth >= 4 - (thisThread->completedDepth > 32) + ss->ttPv + && depth >= 4 - (thisThread->completedDepth > 33) + ss->ttPv && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - (65 + 52 * (ss->ttPv && !PvNode)) * depth / 63; + Value singularBeta = ttValue - (59 + 49 * (ss->ttPv && !PvNode)) * depth / 64; Depth singularDepth = newDepth / 2; ss->excludedMove = move; @@ -1049,10 +1049,10 @@ Value Search::Worker::search( if (value < singularBeta) { - int doubleMargin = 251 * PvNode - 241 * !ttCapture; + int doubleMargin = 285 * PvNode - 228 * !ttCapture; int tripleMargin = - 135 + 234 * PvNode - 248 * !ttCapture + 124 * (ss->ttPv || !ttCapture); - int quadMargin = 447 + 354 * PvNode - 300 * !ttCapture + 206 * ss->ttPv; + 121 + 238 * PvNode - 259 * !ttCapture + 117 * (ss->ttPv || !ttCapture); + int quadMargin = 471 + 343 * PvNode - 281 * !ttCapture + 217 * ss->ttPv; extension = 1 + (value < singularBeta - doubleMargin) + (value < singularBeta - tripleMargin) @@ -1093,7 +1093,7 @@ Value Search::Worker::search( else if (PvNode && move == ttMove && move.to_sq() == prevSq && thisThread->captureHistory[movedPiece][move.to_sq()] [type_of(pos.piece_on(move.to_sq()))] - > 4016) + > 4041) extension = 1; } @@ -1144,10 +1144,10 @@ Value Search::Worker::search( ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + (*contHist[0])[movedPiece][move.to_sq()] - + (*contHist[1])[movedPiece][move.to_sq()] - 5078; + + (*contHist[1])[movedPiece][move.to_sq()] - 5313; // Decrease/increase reduction for moves with a good/bad history (~8 Elo) - r -= ss->statScore / (17662 - std::min(depth, 16) * 105); + r -= ss->statScore / (16145 - std::min(depth, 15) * 102); // Step 17. Late moves reduction / extension (LMR, ~117 Elo) if (depth >= 2 && moveCount > 1 + rootNode) @@ -1166,7 +1166,7 @@ Value Search::Worker::search( { // Adjust full-depth search based on LMR results - if the result // was good enough search deeper, if it was bad enough search shallower. - const bool doDeeperSearch = value > (bestValue + 40 + 2 * newDepth); // (~1 Elo) + const bool doDeeperSearch = value > (bestValue + 41 + 2 * newDepth); // (~1 Elo) const bool doShallowerSearch = value < bestValue + newDepth; // (~2 Elo) newDepth += doDeeperSearch - doShallowerSearch; @@ -1327,9 +1327,9 @@ Value Search::Worker::search( // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (depth > 5) + (PvNode || cutNode) + ((ss - 1)->statScore < -14455) - + ((ss - 1)->moveCount > 10) + (!ss->inCheck && bestValue <= ss->staticEval - 130) - + (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 77); + int bonus = (depth > 5) + (PvNode || cutNode) + ((ss - 1)->statScore < -14323) + + ((ss - 1)->moveCount > 10) + (!ss->inCheck && bestValue <= ss->staticEval - 127) + + (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 76); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] @@ -1488,7 +1488,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, if (bestValue > alpha) alpha = bestValue; - futilityBase = ss->staticEval + 270; + futilityBase = ss->staticEval + 259; } const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, @@ -1560,11 +1560,11 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, + (*contHist[1])[pos.moved_piece(move)][move.to_sq()] + thisThread->pawnHistory[pawn_structure_index(pos)][pos.moved_piece(move)] [move.to_sq()] - <= 4000) + <= 4057) continue; // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, -69)) + if (!pos.see_ge(move, -68)) continue; } @@ -1630,7 +1630,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth Search::Worker::reduction(bool i, Depth d, int mn, int delta) { int reductionScale = reductions[d] * reductions[mn]; - return (reductionScale + 1318 - delta * 760 / rootDelta) / 1024 + (!i && reductionScale > 1066); + return (reductionScale + 1284 - delta * 755 / rootDelta) / 1024 + (!i && reductionScale > 1133); } // elapsed() returns the time elapsed since the search started. If the From d3f081ed8ad749cc7e07c0d85b4e8818678f952f Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Thu, 9 May 2024 21:10:24 +0300 Subject: [PATCH 1484/1766] Adjust standpat return value in qsearch Instead of returning value itself return value between it and beta for non pv nodes - analogous to what we do after actual search there. Passed STC: https://tests.stockfishchess.org/tests/view/663cb1b4c0b75d7f7b98188e LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 131552 W: 34131 L: 33673 D: 63748 Ptnml(0-2): 420, 15446, 33600, 15876, 434 Passed LTC: https://tests.stockfishchess.org/tests/view/663cda5dc0b75d7f7b981c6f LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 282798 W: 71658 L: 70833 D: 140307 Ptnml(0-2): 112, 31187, 77979, 32006, 115 closes https://github.com/official-stockfish/Stockfish/pull/5233 Bench: 1606672 --- src/search.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 1d9e0d81abd..ae2b1de23e9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1478,6 +1478,8 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, // Stand pat. Return immediately if static value is at least beta if (bestValue >= beta) { + if (std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY && !PvNode) + bestValue = (3 * bestValue + beta) / 4; if (!ss->ttHit) tte->save(posKey, value_to_tt(bestValue, ss->ply), false, BOUND_LOWER, DEPTH_NONE, Move::none(), unadjustedStaticEval, tt.generation()); From 53f363041cd96be840244f989823781ecd21b658 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Thu, 9 May 2024 13:47:00 -0400 Subject: [PATCH 1485/1766] Simplify npm constants when adjusting eval Passed non-regression STC: https://tests.stockfishchess.org/tests/view/663d0c4f507ebe1c0e91ec8d LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 162784 W: 41987 L: 41906 D: 78891 Ptnml(0-2): 520, 19338, 41591, 19427, 516 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/663d20fd507ebe1c0e91f405 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 457242 W: 115022 L: 115250 D: 226970 Ptnml(0-2): 271, 51566, 125179, 51330, 275 closes https://github.com/official-stockfish/Stockfish/pull/5237 Bench: 2238216 --- src/evaluate.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 5be7e7a1bb4..cfe20601e56 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -62,15 +62,13 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, Value nnue = smallNet ? networks.small.evaluate(pos, &caches.small, true, &nnueComplexity) : networks.big.evaluate(pos, &caches.big, true, &nnueComplexity); - const auto adjustEval = [&](int nnueDiv, int pawnCountConstant, int pawnCountMul, - int npmConstant, int evalDiv, int shufflingConstant) { + const auto adjustEval = [&](int nnueDiv, int pawnCountMul, int evalDiv, int shufflingConstant) { // Blend optimism and eval with nnue complexity and material imbalance optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / 584; nnue -= nnue * (nnueComplexity * 5 / 3) / nnueDiv; int npm = pos.non_pawn_material() / 64; - v = (nnue * (npm + pawnCountConstant + pawnCountMul * pos.count()) - + optimism * (npmConstant + npm)) + v = (nnue * (npm + 943 + pawnCountMul * pos.count()) + optimism * (npm + 140)) / evalDiv; // Damp down the evaluation linearly when shuffling @@ -79,9 +77,9 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, }; if (!smallNet) - adjustEval(32395, 942, 11, 139, 1058, 178); + adjustEval(32395, 11, 1058, 178); else - adjustEval(32793, 944, 9, 140, 1067, 206); + adjustEval(32793, 9, 1067, 206); // Guarantee evaluation does not hit the tablebase range v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); From 0b08953174d222270100690b45fad0dc47c01f98 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Thu, 9 May 2024 14:03:35 -0400 Subject: [PATCH 1486/1766] Re-evaluate some small net positions for more accurate evals Use main net evals when small net evals hint that higher eval accuracy may be worth the slower eval speeds. With Finny caches, re-evals with the main net are less expensive than before. Original idea by mstembera who I've added as co-author to this PR. Based on reEval tests by mstembera: https://tests.stockfishchess.org/tests/view/65e69187b6345c1b934866e5 https://tests.stockfishchess.org/tests/view/65e863aa0ec64f0526c3e991 A few variants of this patch also passed LTC: https://tests.stockfishchess.org/tests/view/663d2108507ebe1c0e91f407 https://tests.stockfishchess.org/tests/view/663e388c3a2f9702074bc152 Passed STC: https://tests.stockfishchess.org/tests/view/663dadbd1a61d6377f190e2c LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 92320 W: 23941 L: 23531 D: 44848 Ptnml(0-2): 430, 10993, 22931, 11349, 457 Passed LTC: https://tests.stockfishchess.org/tests/view/663ef48b2948bf9aa698690c LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 98934 W: 24907 L: 24457 D: 49570 Ptnml(0-2): 48, 10952, 27027, 11382, 58 closes https://github.com/official-stockfish/Stockfish/pull/5238 bench 1876282 Co-Authored-By: mstembera <5421953+mstembera@users.noreply.github.com> --- src/evaluate.cpp | 3 +++ src/evaluate.h | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index cfe20601e56..b5f28d5aede 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -62,6 +62,9 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, Value nnue = smallNet ? networks.small.evaluate(pos, &caches.small, true, &nnueComplexity) : networks.big.evaluate(pos, &caches.big, true, &nnueComplexity); + if (smallNet && (nnue * simpleEval < 0 || std::abs(nnue) < 500)) + nnue = networks.big.evaluate(pos, &caches.big, true, &nnueComplexity); + const auto adjustEval = [&](int nnueDiv, int pawnCountMul, int evalDiv, int shufflingConstant) { // Blend optimism and eval with nnue complexity and material imbalance optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / 584; diff --git a/src/evaluate.h b/src/evaluate.h index 2d244ff6722..afaf35ebe17 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -29,7 +29,7 @@ class Position; namespace Eval { -constexpr inline int SmallNetThreshold = 1274; +constexpr inline int SmallNetThreshold = 1174; // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the From e608eab8dd9f7bd68f192d56d742f621674b8fa8 Mon Sep 17 00:00:00 2001 From: mstembera Date: Sun, 12 May 2024 04:45:01 -0700 Subject: [PATCH 1487/1766] Optimize update_accumulator_refresh_cache() STC https://tests.stockfishchess.org/tests/view/664105df26ac5f9b286d30e6 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 178528 W: 46235 L: 45750 D: 86543 Ptnml(0-2): 505, 17792, 52142, 18363, 462 Combo of two yellow speedups https://tests.stockfishchess.org/tests/view/6640abf9d163897c63214f5c LLR: -2.93 (-2.94,2.94) <0.00,2.00> Total: 355744 W: 91714 L: 91470 D: 172560 Ptnml(0-2): 913, 36233, 103384, 36381, 961 https://tests.stockfishchess.org/tests/view/6628ce073fe04ce4cefc739c LLR: -2.93 (-2.94,2.94) <0.00,2.00> Total: 627040 W: 162001 L: 161339 D: 303700 Ptnml(0-2): 2268, 72379, 163532, 73105, 2236 closes https://github.com/official-stockfish/Stockfish/pull/5239 No functional change --- src/nnue/nnue_feature_transformer.h | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 2b11adefbbb..bcd14e6fbbc 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -664,7 +664,11 @@ class FeatureTransformer { for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) { - auto entryTile = reinterpret_cast(&entry.accumulation[j * TileHeight]); + auto accTile = + reinterpret_cast(&accumulator.accumulation[Perspective][j * TileHeight]); + auto entryTile = + reinterpret_cast(&entry.accumulation[j * TileHeight]); + for (IndexType k = 0; k < NumRegs; ++k) acc[k] = entryTile[k]; @@ -679,7 +683,7 @@ class FeatureTransformer { auto columnA = reinterpret_cast(&weights[offsetA]); for (unsigned k = 0; k < NumRegs; ++k) - acc[k] = vec_add_16(vec_sub_16(acc[k], columnR[k]), columnA[k]); + acc[k] = vec_add_16(acc[k], vec_sub_16(columnA[k], columnR[k])); } for (; i < int(removed.size()); ++i) { @@ -702,12 +706,17 @@ class FeatureTransformer { for (IndexType k = 0; k < NumRegs; k++) vec_store(&entryTile[k], acc[k]); + for (IndexType k = 0; k < NumRegs; k++) + vec_store(&accTile[k], acc[k]); } for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j) { - auto entryTilePsqt = - reinterpret_cast(&entry.psqtAccumulation[j * PsqtTileHeight]); + auto accTilePsqt = reinterpret_cast( + &accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); + auto entryTilePsqt = reinterpret_cast( + &entry.psqtAccumulation[j * PsqtTileHeight]); + for (std::size_t k = 0; k < NumPsqtRegs; ++k) psqt[k] = entryTilePsqt[k]; @@ -732,6 +741,8 @@ class FeatureTransformer { for (std::size_t k = 0; k < NumPsqtRegs; ++k) vec_store_psqt(&entryTilePsqt[k], psqt[k]); + for (std::size_t k = 0; k < NumPsqtRegs; ++k) + vec_store_psqt(&accTilePsqt[k], psqt[k]); } #else @@ -755,8 +766,6 @@ class FeatureTransformer { entry.psqtAccumulation[k] += psqtWeights[index * PSQTBuckets + k]; } -#endif - // The accumulator of the refresh entry has been updated. // Now copy its content to the actual accumulator we were refreshing @@ -765,6 +774,7 @@ class FeatureTransformer { std::memcpy(accumulator.psqtAccumulation[Perspective], entry.psqtAccumulation, sizeof(int32_t) * PSQTBuckets); +#endif for (Color c : {WHITE, BLACK}) entry.byColorBB[c] = pos.pieces(c); From 2682c2127d1360524915f6cd68cbeabfdd19ce26 Mon Sep 17 00:00:00 2001 From: xoto10 <23479932+xoto10@users.noreply.github.com> Date: Mon, 13 May 2024 07:19:18 +0100 Subject: [PATCH 1488/1766] Use 5% less time on first move Stockfish appears to take too much time on the first move of a game and then not enough on moves 2,3,4... Probably caused by most of the factors that increase time usually applying on the first move. Attempts to give more time to the subsequent moves have not worked so far, but this change to simply reduce first move time by 5% worked. STC 10+0.1 : LLR: 2.96 (-2.94,2.94) <0.00,2.00> Total: 78496 W: 20516 L: 20135 D: 37845 Ptnml(0-2): 340, 8859, 20456, 9266, 327 https://tests.stockfishchess.org/tests/view/663d47bf507ebe1c0e9200ba LTC 60+0.6 : LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 94872 W: 24179 L: 23751 D: 46942 Ptnml(0-2): 61, 9743, 27405, 10161, 66 https://tests.stockfishchess.org/tests/view/663e779cbb28828150dd9089 closes https://github.com/official-stockfish/Stockfish/pull/5235 Bench: 1876282 --- src/nnue/nnue_feature_transformer.h | 9 ++++----- src/search.cpp | 3 ++- src/search.h | 1 + src/thread.cpp | 1 + src/timeman.cpp | 11 +++++++---- src/timeman.h | 3 ++- 6 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index bcd14e6fbbc..7b7aada312c 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -666,8 +666,7 @@ class FeatureTransformer { { auto accTile = reinterpret_cast(&accumulator.accumulation[Perspective][j * TileHeight]); - auto entryTile = - reinterpret_cast(&entry.accumulation[j * TileHeight]); + auto entryTile = reinterpret_cast(&entry.accumulation[j * TileHeight]); for (IndexType k = 0; k < NumRegs; ++k) acc[k] = entryTile[k]; @@ -714,9 +713,9 @@ class FeatureTransformer { { auto accTilePsqt = reinterpret_cast( &accumulator.psqtAccumulation[Perspective][j * PsqtTileHeight]); - auto entryTilePsqt = reinterpret_cast( - &entry.psqtAccumulation[j * PsqtTileHeight]); - + auto entryTilePsqt = + reinterpret_cast(&entry.psqtAccumulation[j * PsqtTileHeight]); + for (std::size_t k = 0; k < NumPsqtRegs; ++k) psqt[k] = entryTilePsqt[k]; diff --git a/src/search.cpp b/src/search.cpp index ae2b1de23e9..edbb58c62cc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -157,7 +157,8 @@ void Search::Worker::start_searching() { return; } - main_manager()->tm.init(limits, rootPos.side_to_move(), rootPos.game_ply(), options); + main_manager()->tm.init(limits, rootPos.side_to_move(), rootPos.game_ply(), options, + main_manager()->originalPly); tt.new_search(); if (rootMoves.empty()) diff --git a/src/search.h b/src/search.h index c824daf931c..6e5b22bda32 100644 --- a/src/search.h +++ b/src/search.h @@ -210,6 +210,7 @@ class SearchManager: public ISearchManager { Depth depth) const; Stockfish::TimeManagement tm; + int originalPly; int callsCnt; std::atomic_bool ponder; diff --git a/src/thread.cpp b/src/thread.cpp index 9052654baf6..8724cb49cd1 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -167,6 +167,7 @@ void ThreadPool::clear() { main_manager()->callsCnt = 0; main_manager()->bestPreviousScore = VALUE_INFINITE; main_manager()->bestPreviousAverageScore = VALUE_INFINITE; + main_manager()->originalPly = -1; main_manager()->previousTimeReduction = 1.0; main_manager()->tm.clear(); } diff --git a/src/timeman.cpp b/src/timeman.cpp index 4feb329b335..f389e082d8e 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -44,10 +44,8 @@ void TimeManagement::advance_nodes_time(std::int64_t nodes) { // the bounds of time allowed for the current game ply. We currently support: // 1) x basetime (+ z increment) // 2) x moves in y seconds (+ z increment) -void TimeManagement::init(Search::LimitsType& limits, - Color us, - int ply, - const OptionsMap& options) { +void TimeManagement::init( + Search::LimitsType& limits, Color us, int ply, const OptionsMap& options, int& originalPly) { TimePoint npmsec = TimePoint(options["nodestime"]); // If we have no time, we don't need to fully initialize TM. @@ -58,6 +56,9 @@ void TimeManagement::init(Search::LimitsType& limits, if (limits.time[us] == 0) return; + if (originalPly == -1) + originalPly = ply; + TimePoint moveOverhead = TimePoint(options["Move Overhead"]); // optScale is a percentage of available time to use for the current move. @@ -106,6 +107,8 @@ void TimeManagement::init(Search::LimitsType& limits, { // Use extra time with larger increments double optExtra = scaledInc < 500 ? 1.0 : 1.13; + if (ply - originalPly < 2) + optExtra *= 0.95; // Calculate time constants based on current time left. double logTimeInSec = std::log10(scaledTime / 1000.0); diff --git a/src/timeman.h b/src/timeman.h index 1b6bd849ae2..8f1bb56397d 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -36,7 +36,8 @@ struct LimitsType; // the maximum available time, the game move number, and other parameters. class TimeManagement { public: - void init(Search::LimitsType& limits, Color us, int ply, const OptionsMap& options); + void init( + Search::LimitsType& limits, Color us, int ply, const OptionsMap& options, int& originalPly); TimePoint optimum() const; TimePoint maximum() const; From fa114266fa7ea996c6d2ef12c625547b1aefddc1 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Mon, 13 May 2024 14:08:19 +0300 Subject: [PATCH 1489/1766] Add extra bonus for high-depth condition Passed STC: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 54208 W: 14058 L: 13717 D: 26433 Ptnml(0-2): 166, 6277, 13885, 6602, 174 https://tests.stockfishchess.org/tests/view/664136d8f9f4e8fc783c9b82 Passed LTC: LLR: 2.96 (-2.94,2.94) <0.50,2.50> Total: 112548 W: 28492 L: 28018 D: 56038 Ptnml(0-2): 53, 12186, 31318, 12668, 49 https://tests.stockfishchess.org/tests/view/664143fef9f4e8fc783c9bf6 closes https://github.com/official-stockfish/Stockfish/pull/5242 Bench: 1725980 --- src/search.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index edbb58c62cc..30f718bd704 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -739,7 +739,7 @@ Value Search::Worker::search( // Use static evaluation difference to improve quiet move ordering (~9 Elo) if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture) { - int bonus = std::clamp(-12 * int((ss - 1)->staticEval + ss->staticEval), -1749, 1602); + int bonus = std::clamp(-12 * int((ss - 1)->staticEval + ss->staticEval), -1749, 1584); bonus = bonus > 0 ? 2 * bonus : bonus / 2; thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) @@ -1328,8 +1328,8 @@ Value Search::Worker::search( // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (depth > 5) + (PvNode || cutNode) + ((ss - 1)->statScore < -14323) - + ((ss - 1)->moveCount > 10) + (!ss->inCheck && bestValue <= ss->staticEval - 127) + int bonus = (depth > 4) + (depth > 5) + (PvNode || cutNode) + ((ss - 1)->statScore < -14323) + + ((ss - 1)->moveCount > 10) + (!ss->inCheck && bestValue <= ss->staticEval - 120) + (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 76); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); From 9e45644c50e4650e4603ddef3e8147a8daf3a790 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Tue, 14 May 2024 20:10:01 +0300 Subject: [PATCH 1490/1766] Add extra bonus to pawn history for a move that caused a fail low Basically the same idea as it is for continuation/main history, but it has some tweaks. 1) it has * 2 multiplier for bonus instead of full/half bonus - for whatever reason this seems to work better; 2) attempts with this type of big bonuses scaled somewhat poorly (or were unlucky at longer time controls), but after measuring the fact that average value of pawn history in LMR after adding this bonuses increased by substantial number (for multiplier 1,5 it increased by smth like 400~ from 8192 cap) attempts were made to make default pawn history negative to compensate it - and version with multiplier 2 and initial fill value -900 passed. Passed STC: https://tests.stockfishchess.org/tests/view/66424815f9f4e8fc783cba59 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 115008 W: 30001 L: 29564 D: 55443 Ptnml(0-2): 432, 13629, 28903, 14150, 390 Passed LTC: https://tests.stockfishchess.org/tests/view/6642f5437134c82f3f7a3ffa LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 56448 W: 14432 L: 14067 D: 27949 Ptnml(0-2): 36, 6268, 15254, 6627, 39 Bench: 1857237 --- src/search.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 30f718bd704..09a9cc920ab 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -496,7 +496,7 @@ void Search::Worker::clear() { counterMoves.fill(Move::none()); mainHistory.fill(0); captureHistory.fill(0); - pawnHistory.fill(0); + pawnHistory.fill(-900); correctionHistory.fill(0); for (bool inCheck : {false, true}) @@ -1335,6 +1335,11 @@ Value Search::Worker::search( stat_bonus(depth) * bonus); thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << stat_bonus(depth) * bonus / 2; + + + if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) + thisThread->pawnHistory[pawn_structure_index(pos)][pos.piece_on(prevSq)][prevSq] + << stat_bonus(depth) * bonus * 2; } if (PvNode) From 09dba1f0806a973d1f9f4ebf04b7a45d81683168 Mon Sep 17 00:00:00 2001 From: mstembera Date: Mon, 13 May 2024 15:28:48 -0700 Subject: [PATCH 1491/1766] Call adjustEval with correct parameters after rescore Set smallNet to false after rescoring so we call adjustEval() w/ correct parameters. STC: https://tests.stockfishchess.org/tests/view/664308687134c82f3f7a4003 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 146912 W: 37856 L: 37756 D: 71300 Ptnml(0-2): 566, 17562, 37122, 17618, 588 LTC: https://tests.stockfishchess.org/tests/view/6643a0821f32a966da7485d6 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 390414 W: 98015 L: 98173 D: 194226 Ptnml(0-2): 162, 43555, 107929, 43401, 160 closes https://github.com/official-stockfish/Stockfish/pull/5244 Bench: 1819318 --- src/evaluate.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index b5f28d5aede..de1adc989c7 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -63,7 +63,10 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, : networks.big.evaluate(pos, &caches.big, true, &nnueComplexity); if (smallNet && (nnue * simpleEval < 0 || std::abs(nnue) < 500)) - nnue = networks.big.evaluate(pos, &caches.big, true, &nnueComplexity); + { + nnue = networks.big.evaluate(pos, &caches.big, true, &nnueComplexity); + smallNet = false; + } const auto adjustEval = [&](int nnueDiv, int pawnCountMul, int evalDiv, int shufflingConstant) { // Blend optimism and eval with nnue complexity and material imbalance From 9b90cd88f0ddd568e43161a0ada7daf02fc59c67 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Wed, 15 May 2024 04:10:58 +0200 Subject: [PATCH 1492/1766] Reduce more when improving and ttvalue is lower than alpha More reduction if position is improving but value from TT doesn't exceeds alpha but the tt move is excluded. This idea is based on following LMR condition tuning https://tests.stockfishchess.org/tests/view/66423a1bf9f4e8fc783cba37 by using only three of the four largest terms P[3], P[18] and P[12]. Passed STC: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 27840 W: 7309 L: 7004 D: 13527 Ptnml(0-2): 85, 3219, 7018, 3502, 96 https://tests.stockfishchess.org/tests/view/6643dc1cbc537f56194508ba Passed LTC: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 191280 W: 48656 L: 48020 D: 94604 Ptnml(0-2): 78, 20979, 52903, 21589, 91 https://tests.stockfishchess.org/tests/view/6643e543bc537f5619451683 closes https://github.com/official-stockfish/Stockfish/pull/5245 Bench: 1430835 --- src/search.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 09a9cc920ab..2dadd0dca47 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1134,6 +1134,9 @@ Value Search::Worker::search( if (PvNode) r--; + if (improving && ttValue <= alpha && move != ttMove) + r++; + // Increase reduction if next ply has a lot of fail high (~5 Elo) if ((ss + 1)->cutoffCnt > 3) r++; From 1f3a0fda2e3a0d4aa825dd148c2593fb3631bf82 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Mon, 13 May 2024 18:06:38 -0400 Subject: [PATCH 1493/1766] Use same eval divisor for both nets Passed non-regression STC: https://tests.stockfishchess.org/tests/view/66428f146577e9d2c8a29cf8 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 241024 W: 62173 L: 62177 D: 116674 Ptnml(0-2): 904, 28648, 61407, 28654, 899 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/6643ae6f1f32a966da74977b LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 193710 W: 48762 L: 48717 D: 96231 Ptnml(0-2): 70, 21599, 53481, 21626, 79 closes https://github.com/official-stockfish/Stockfish/pull/5246 Bench: 1700680 --- src/evaluate.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index de1adc989c7..76d630dd9e4 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -68,14 +68,13 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, smallNet = false; } - const auto adjustEval = [&](int nnueDiv, int pawnCountMul, int evalDiv, int shufflingConstant) { + const auto adjustEval = [&](int nnueDiv, int pawnCountMul, int shufflingConstant) { // Blend optimism and eval with nnue complexity and material imbalance optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / 584; nnue -= nnue * (nnueComplexity * 5 / 3) / nnueDiv; int npm = pos.non_pawn_material() / 64; - v = (nnue * (npm + 943 + pawnCountMul * pos.count()) + optimism * (npm + 140)) - / evalDiv; + v = (nnue * (npm + 943 + pawnCountMul * pos.count()) + optimism * (npm + 140)) / 1058; // Damp down the evaluation linearly when shuffling int shuffling = pos.rule50_count(); @@ -83,9 +82,9 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, }; if (!smallNet) - adjustEval(32395, 11, 1058, 178); + adjustEval(32395, 11, 178); else - adjustEval(32793, 9, 1067, 206); + adjustEval(32793, 9, 206); // Guarantee evaluation does not hit the tablebase range v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); From dcb02337844d71e56df57b9a8ba17646f953711c Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Wed, 15 May 2024 14:22:36 +0300 Subject: [PATCH 1494/1766] Simplifying improving and worsening deduction formulas Passed STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 77696 W: 20052 L: 19878 D: 37766 Ptnml(0-2): 222, 9124, 19994, 9274, 234 https://tests.stockfishchess.org/tests/view/66440032bc537f561945171e Passed LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 234414 W: 58874 L: 58871 D: 116669 Ptnml(0-2): 96, 26147, 64742, 26102, 120 https://tests.stockfishchess.org/tests/view/6644094cbc537f5619451735 closes https://github.com/official-stockfish/Stockfish/pull/5248 Bench: 1336738 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 2dadd0dca47..d9041c66a65 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -60,8 +60,8 @@ static constexpr double EvalLevel[10] = {0.981, 0.956, 0.895, 0.949, 0.913, // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) { Value futilityMult = 131 - 48 * noTtCutNode; - Value improvingDeduction = 57 * improving * futilityMult / 32; - Value worseningDeduction = (309 + 52 * improving) * oppWorsening * futilityMult / 1024; + Value improvingDeduction = 2 * improving * futilityMult; + Value worseningDeduction = 330 * oppWorsening * futilityMult / 1024; return futilityMult * d - improvingDeduction - worseningDeduction; } From 541406ab9151891b3a42f49030a6167cfca55599 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Tue, 14 May 2024 16:51:02 -0400 Subject: [PATCH 1495/1766] Use same nnue divisor for both nets Passed non-regression STC: https://tests.stockfishchess.org/tests/view/6643ceeabc537f56194506f6 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 224800 W: 57910 L: 57896 D: 108994 Ptnml(0-2): 673, 26790, 57519, 26686, 732 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/6643ff15bc537f5619451719 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 347658 W: 87574 L: 87688 D: 172396 Ptnml(0-2): 207, 39004, 95488, 38956, 174 closes https://github.com/official-stockfish/Stockfish/pull/5250 Bench: 1804704 --- src/evaluate.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 76d630dd9e4..3ce148627d5 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -68,10 +68,10 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, smallNet = false; } - const auto adjustEval = [&](int nnueDiv, int pawnCountMul, int shufflingConstant) { + const auto adjustEval = [&](int pawnCountMul, int shufflingConstant) { // Blend optimism and eval with nnue complexity and material imbalance optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / 584; - nnue -= nnue * (nnueComplexity * 5 / 3) / nnueDiv; + nnue -= nnue * (nnueComplexity * 5 / 3) / 32395; int npm = pos.non_pawn_material() / 64; v = (nnue * (npm + 943 + pawnCountMul * pos.count()) + optimism * (npm + 140)) / 1058; @@ -82,9 +82,9 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, }; if (!smallNet) - adjustEval(32395, 11, 178); + adjustEval(11, 178); else - adjustEval(32793, 9, 206); + adjustEval(9, 206); // Guarantee evaluation does not hit the tablebase range v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); From e3c9ed77aa62e096d52bb558193279b804f53a84 Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Wed, 15 May 2024 22:32:55 +0800 Subject: [PATCH 1496/1766] Revert "Reduce more when improving and ttvalue is lower than alpha" The patch regressed significantly at longer time controls. Passed VLTC: https://tests.stockfishchess.org/tests/view/6644c7a2bc537f5619453096 LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 43336 W: 11177 L: 10884 D: 21275 Ptnml(0-2): 3, 4432, 12507, 4721, 5 Passed VVLTC: https://tests.stockfishchess.org/tests/view/66450c974aa4fa9a83b6d0b0 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 32394 W: 8350 L: 8072 D: 15972 Ptnml(0-2): 2, 2798, 10317, 3080, 0 closes https://github.com/official-stockfish/Stockfish/pull/5251 Bench: 1594188 --- src/search.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index d9041c66a65..bdcecd1c26c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1134,9 +1134,6 @@ Value Search::Worker::search( if (PvNode) r--; - if (improving && ttValue <= alpha && move != ttMove) - r++; - // Increase reduction if next ply has a lot of fail high (~5 Elo) if ((ss + 1)->cutoffCnt > 3) r++; From 47597641dc8da7c65d0f1d987f784af09d6aec15 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Wed, 15 May 2024 13:22:46 -0400 Subject: [PATCH 1497/1766] Lower smallnet threshold linearly as pawn count decreases Passed STC: https://tests.stockfishchess.org/tests/view/6644f677324e96f42f89d894 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 377920 W: 97135 L: 96322 D: 184463 Ptnml(0-2): 1044, 44259, 97588, 44978, 1091 Passed LTC: https://tests.stockfishchess.org/tests/view/664548af93ce6da3e93b31b3 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 169056 W: 42901 L: 42312 D: 83843 Ptnml(0-2): 58, 18538, 46753, 19115, 64 closes https://github.com/official-stockfish/Stockfish/pull/5252 Bench: 1991750 --- src/evaluate.cpp | 2 +- src/evaluate.h | 2 +- src/nnue/nnue_misc.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 3ce148627d5..498ec161bb0 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -55,7 +55,7 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, assert(!pos.checkers()); int simpleEval = simple_eval(pos, pos.side_to_move()); - bool smallNet = std::abs(simpleEval) > SmallNetThreshold; + bool smallNet = std::abs(simpleEval) > SmallNetThreshold + 6 * pos.count(); int nnueComplexity; int v; diff --git a/src/evaluate.h b/src/evaluate.h index afaf35ebe17..6612ec9daf5 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -29,7 +29,7 @@ class Position; namespace Eval { -constexpr inline int SmallNetThreshold = 1174; +constexpr inline int SmallNetThreshold = 1126; // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the diff --git a/src/nnue/nnue_misc.cpp b/src/nnue/nnue_misc.cpp index bf73a58bf07..8a777912043 100644 --- a/src/nnue/nnue_misc.cpp +++ b/src/nnue/nnue_misc.cpp @@ -47,7 +47,7 @@ void hint_common_parent_position(const Position& pos, AccumulatorCaches& caches) { int simpleEvalAbs = std::abs(simple_eval(pos, pos.side_to_move())); - if (simpleEvalAbs > Eval::SmallNetThreshold) + if (simpleEvalAbs > Eval::SmallNetThreshold + 6 * pos.count()) networks.small.hint_common_access(pos, &caches.small); else networks.big.hint_common_access(pos, &caches.big); From e0227a627288c786fdd3b12452303ff4eabba5b0 Mon Sep 17 00:00:00 2001 From: Rak Laptudirm Date: Wed, 15 May 2024 22:26:12 +0530 Subject: [PATCH 1498/1766] Improve comment closes https://github.com/official-stockfish/Stockfish/pull/5249 No functional change --- src/tt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tt.cpp b/src/tt.cpp index 4885a781a5e..cb46fc8a9a6 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -35,7 +35,7 @@ namespace Stockfish { void TTEntry::save( Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev, uint8_t generation8) { - // Preserve any existing move for the same position + // Preserve the old ttmove if we don't have a new one if (m || uint16_t(k) != key16) move16 = m; From 1b7dea3f851cd5c5411ba6f07a2f935bfb7da8a9 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Wed, 15 May 2024 19:26:48 -0400 Subject: [PATCH 1499/1766] Update default main net to nn-c721dfca8cd3.nnue Created by first retraining the spsa-tuned main net `nn-ae6a388e4a1a.nnue` with: - using v6-dd data without bestmove captures removed - addition of T80 mar2024 data - increasing loss by 20% when Q is too high - torch.compile changes for marginal training speed gains And then SPSA tuning weights of epoch 899 following methods described in: https://github.com/official-stockfish/Stockfish/pull/5149 This net was reached at 92k out of 120k steps in this 70+0.7 th 7 SPSA tuning run: https://tests.stockfishchess.org/tests/view/66413b7df9f4e8fc783c9bbb Thanks to @Viren6 for suggesting usage of: - c value 4 for the weights - c value 128 for the biases Scripts for automating applying fishtest spsa params to exporting tuned .nnue are in: https://github.com/linrock/nnue-tools/tree/master/spsa Before spsa tuning, epoch 899 was nn-f85738aefa84.nnue https://tests.stockfishchess.org/tests/view/663e5c893a2f9702074bc167 After initially training with max-epoch 800, training was resumed with max-epoch 1000. ``` experiment-name: 3072--S11--more-data-v6-dd-t80-mar2024--see-ge0-20p-more-loss-high-q-sk28-l8 nnue-pytorch-branch: linrock/nnue-pytorch/3072-r21-skip-more-wdl-see-ge0-20p-more-loss-high-q-torch-compile-more start-from-engine-test-net: False start-from-model: /data/config/apr2024-3072/nn-ae6a388e4a1a.nnue early-fen-skipping: 28 training-dataset: /data/S11-mar2024/: - leela96.v2.min.binpack - test60-2021-11-12-novdec-12tb7p.v6-dd.min.binpack - test78-2022-01-to-05-jantomay-16tb7p.v6-dd.min.binpack - test80-2022-06-jun-16tb7p.v6-dd.min.binpack - test80-2022-08-aug-16tb7p.v6-dd.min.binpack - test80-2022-09-sep-16tb7p.v6-dd.min.binpack - test80-2023-01-jan-16tb7p.v6-sk20.min.binpack - test80-2023-02-feb-16tb7p.v6-sk20.min.binpack - test80-2023-03-mar-2tb7p.v6-sk16.min.binpack - test80-2023-04-apr-2tb7p.v6-sk16.min.binpack - test80-2023-05-may-2tb7p.v6.min.binpack # https://github.com/official-stockfish/Stockfish/pull/4782 - test80-2023-06-jun-2tb7p.binpack - test80-2023-07-jul-2tb7p.binpack # https://github.com/official-stockfish/Stockfish/pull/4972 - test80-2023-08-aug-2tb7p.v6.min.binpack - test80-2023-09-sep-2tb7p.binpack - test80-2023-10-oct-2tb7p.binpack # S9 new data: https://github.com/official-stockfish/Stockfish/pull/5056 - test80-2023-11-nov-2tb7p.binpack - test80-2023-12-dec-2tb7p.binpack # S10 new data: https://github.com/official-stockfish/Stockfish/pull/5149 - test80-2024-01-jan-2tb7p.binpack - test80-2024-02-feb-2tb7p.binpack # S11 new data - test80-2024-03-mar-2tb7p.binpack /data/filt-v6-dd/: - test77-dec2021-16tb7p-filter-v6-dd.binpack - test78-juntosep2022-16tb7p-filter-v6-dd.binpack - test79-apr2022-16tb7p-filter-v6-dd.binpack - test79-may2022-16tb7p-filter-v6-dd.binpack - test80-jul2022-16tb7p-filter-v6-dd.binpack - test80-oct2022-16tb7p-filter-v6-dd.binpack - test80-nov2022-16tb7p-filter-v6-dd.binpack num-epochs: 1000 lr: 4.375e-4 gamma: 0.995 start-lambda: 0.8 end-lambda: 0.7 ``` Training data can be found at: https://robotmoon.com/nnue-training-data/ Local elo at 25k nodes per move: nn-epoch899.nnue : 4.6 +/- 1.4 Passed STC: https://tests.stockfishchess.org/tests/view/6645454893ce6da3e93b31ae LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 95232 W: 24598 L: 24194 D: 46440 Ptnml(0-2): 294, 11215, 24180, 11647, 280 Passed LTC: https://tests.stockfishchess.org/tests/view/6645522d93ce6da3e93b31df LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 320544 W: 81432 L: 80524 D: 158588 Ptnml(0-2): 164, 35659, 87696, 36611, 142 closes https://github.com/official-stockfish/Stockfish/pull/5254 bench 1995552 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 6612ec9daf5..c87be53c14f 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -35,7 +35,7 @@ constexpr inline int SmallNetThreshold = 1126; // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro or the location where this macro is defined, as it is used // in the Makefile/Fishtest. -#define EvalFileDefaultNameBig "nn-ae6a388e4a1a.nnue" +#define EvalFileDefaultNameBig "nn-c721dfca8cd3.nnue" #define EvalFileDefaultNameSmall "nn-baff1ede1f90.nnue" namespace NNUE { From d92d1f31809afc8aa83cc14fcbd54b95258d09ad Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Thu, 16 May 2024 01:48:56 -0400 Subject: [PATCH 1500/1766] Move smallnet threshold logic into a function Now that the smallnet threshold is no longer a constant, use a function to organize it with other eval code. Passed non-regression STC: https://tests.stockfishchess.org/tests/view/66459fa093ce6da3e93b5ba2 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 217600 W: 56281 L: 56260 D: 105059 Ptnml(0-2): 756, 23787, 59729, 23736, 792 closes https://github.com/official-stockfish/Stockfish/pull/5255 No functional change --- src/evaluate.cpp | 6 +++++- src/evaluate.h | 3 +-- src/nnue/nnue_misc.cpp | 4 +--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 498ec161bb0..09402b8b100 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -44,6 +44,10 @@ int Eval::simple_eval(const Position& pos, Color c) { + (pos.non_pawn_material(c) - pos.non_pawn_material(~c)); } +bool Eval::use_smallnet(const Position& pos) { + int simpleEval = simple_eval(pos, pos.side_to_move()); + return std::abs(simpleEval) > 1126 + 6 * pos.count(); +} // Evaluate is the evaluator for the outer world. It returns a static evaluation // of the position from the point of view of the side to move. @@ -55,7 +59,7 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, assert(!pos.checkers()); int simpleEval = simple_eval(pos, pos.side_to_move()); - bool smallNet = std::abs(simpleEval) > SmallNetThreshold + 6 * pos.count(); + bool smallNet = use_smallnet(pos); int nnueComplexity; int v; diff --git a/src/evaluate.h b/src/evaluate.h index c87be53c14f..4b3e91acf4c 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -29,8 +29,6 @@ class Position; namespace Eval { -constexpr inline int SmallNetThreshold = 1126; - // The default net name MUST follow the format nn-[SHA256 first 12 digits].nnue // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro or the location where this macro is defined, as it is used @@ -46,6 +44,7 @@ struct AccumulatorCaches; std::string trace(Position& pos, const Eval::NNUE::Networks& networks); int simple_eval(const Position& pos, Color c); +bool use_smallnet(const Position& pos); Value evaluate(const NNUE::Networks& networks, const Position& pos, Eval::NNUE::AccumulatorCaches& caches, diff --git a/src/nnue/nnue_misc.cpp b/src/nnue/nnue_misc.cpp index 8a777912043..a13c717c3d8 100644 --- a/src/nnue/nnue_misc.cpp +++ b/src/nnue/nnue_misc.cpp @@ -45,9 +45,7 @@ constexpr std::string_view PieceToChar(" PNBRQK pnbrqk"); void hint_common_parent_position(const Position& pos, const Networks& networks, AccumulatorCaches& caches) { - - int simpleEvalAbs = std::abs(simple_eval(pos, pos.side_to_move())); - if (simpleEvalAbs > Eval::SmallNetThreshold + 6 * pos.count()) + if (Eval::use_smallnet(pos)) networks.small.hint_common_access(pos, &caches.small); else networks.big.hint_common_access(pos, &caches.big); From f5e15441b8e3b8087024d309313e8a4d6c48bba7 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sat, 18 May 2024 01:22:41 +0300 Subject: [PATCH 1501/1766] Early Exit in Bitboards::sliding_attack() he original code checks for occupancy within the loop condition. By moving this check inside the loop and adding an early exit condition, we can avoid unnecessary iterations if a blocking piece is encountered. Passed stc: LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 127200 W: 33129 L: 32700 D: 61371 Ptnml(0-2): 424, 13243, 35826, 13694, 413 https://tests.stockfishchess.org/tests/view/664646006dcff0d1d6b05bca closes https://github.com/official-stockfish/Stockfish/pull/5256 No functional change --- src/bitboard.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 32c626d4773..c842ca1271e 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -124,8 +124,14 @@ Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) { for (Direction d : (pt == ROOK ? RookDirections : BishopDirections)) { Square s = sq; - while (safe_destination(s, d) && !(occupied & s)) + while (safe_destination(s, d)) + { attacks |= (s += d); + if (occupied & s) + { + break; + } + } } return attacks; From 285f1d2a663fb111f7124272403923eab4251982 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Wed, 15 May 2024 22:26:15 -0700 Subject: [PATCH 1502/1766] Tweak NMP Formula Passed STC: LLR: 2.99 (-2.94,2.94) <0.00,2.00> Total: 241728 W: 62440 L: 61811 D: 117477 Ptnml(0-2): 914, 28467, 61458, 29126, 899 https://tests.stockfishchess.org/tests/live_elo/6645992993ce6da3e93b5b99 Passed LTC: LLR: 2.96 (-2.94,2.94) <0.50,2.50> Total: 167850 W: 42620 L: 42030 D: 83200 Ptnml(0-2): 82, 18412, 46354, 18988, 89 https://tests.stockfishchess.org/tests/live_elo/6647c5726dcff0d1d6b05dd3 closes https://github.com/official-stockfish/Stockfish/pull/5257 Bench: 1636018 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index bdcecd1c26c..06c6e1987aa 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -787,7 +787,7 @@ Value Search::Worker::search( assert(eval - beta >= 0); // Null move dynamic reduction based on depth and eval - Depth R = std::min(int(eval - beta) / 144, 6) + depth / 3 + 4; + Depth R = std::min(int(eval - beta) / 144, 6) + depth / 3 + 5; ss->currentMove = Move::null(); ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; From 99dfc63e0321cb8544ce5455993df00a6c817ba3 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Fri, 17 May 2024 19:27:20 -0400 Subject: [PATCH 1503/1766] Use one nnue pawn count multiplier Switch to the value used by the main net. Passed non-regression STC: https://tests.stockfishchess.org/tests/view/6647e8096dcff0d1d6b05e96 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 51040 W: 13249 L: 13044 D: 24747 Ptnml(0-2): 139, 6029, 13016, 6160, 176 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/6647f4a46dcff0d1d6b05eea LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 20460 W: 5195 L: 4972 D: 10293 Ptnml(0-2): 8, 2178, 5637, 2397, 10 https://github.com/official-stockfish/Stockfish/pull/5258 bench 1887462 --- src/evaluate.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 09402b8b100..abb04fcc2e1 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -72,13 +72,13 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, smallNet = false; } - const auto adjustEval = [&](int pawnCountMul, int shufflingConstant) { + const auto adjustEval = [&](int shufflingConstant) { // Blend optimism and eval with nnue complexity and material imbalance optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / 584; nnue -= nnue * (nnueComplexity * 5 / 3) / 32395; int npm = pos.non_pawn_material() / 64; - v = (nnue * (npm + 943 + pawnCountMul * pos.count()) + optimism * (npm + 140)) / 1058; + v = (nnue * (npm + 943 + 11 * pos.count()) + optimism * (npm + 140)) / 1058; // Damp down the evaluation linearly when shuffling int shuffling = pos.rule50_count(); @@ -86,9 +86,9 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, }; if (!smallNet) - adjustEval(11, 178); + adjustEval(178); else - adjustEval(9, 206); + adjustEval(206); // Guarantee evaluation does not hit the tablebase range v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); From 4edd1a389e4146a610098a841841f37f58980213 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Fri, 17 May 2024 17:45:09 -0700 Subject: [PATCH 1504/1766] Simplify Away Quadruple Extensions serendipitous gainer Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 95472 W: 24176 L: 24031 D: 47265 Ptnml(0-2): 52, 10533, 26414, 10692, 45 https://tests.stockfishchess.org/tests/live_elo/6647fa596dcff0d1d6b05efa Passed VVLTC 70+7 th 7: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 6772 W: 1793 L: 1583 D: 3396 Ptnml(0-2): 0, 502, 2172, 712, 0 https://tests.stockfishchess.org/tests/live_elo/6648277a6dcff0d1d6b05ffb Passed VVLTC 70+7 th 7 (2x): https://tests.stockfishchess.org/tests/view/66484c896dcff0d1d6b0619d LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 5424 W: 1469 L: 1254 D: 2701 Ptnml(0-2): 0, 394, 1710, 607, 1 closes https://github.com/official-stockfish/Stockfish/pull/5259 Bench: 1441794 --- src/search.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 06c6e1987aa..2b95043fd37 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1053,11 +1053,9 @@ Value Search::Worker::search( int doubleMargin = 285 * PvNode - 228 * !ttCapture; int tripleMargin = 121 + 238 * PvNode - 259 * !ttCapture + 117 * (ss->ttPv || !ttCapture); - int quadMargin = 471 + 343 * PvNode - 281 * !ttCapture + 217 * ss->ttPv; extension = 1 + (value < singularBeta - doubleMargin) - + (value < singularBeta - tripleMargin) - + (value < singularBeta - quadMargin); + + (value < singularBeta - tripleMargin); depth += ((!PvNode) && (depth < 14)); } From 2694fce928e5eec867d56d853b416c9f389c284d Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Fri, 17 May 2024 21:38:38 -0400 Subject: [PATCH 1505/1766] Simplify away adjustEval lambda Now that only the shuffling constant differs between nets, a lambda for adjusting eval is no longer needed. Passed non-regression STC: https://tests.stockfishchess.org/tests/view/664806ca6dcff0d1d6b05f34 LLR: 2.99 (-2.94,2.94) <-1.75,0.25> Total: 31552 W: 8175 L: 7959 D: 15418 Ptnml(0-2): 76, 3180, 9065, 3362, 93 closes https://github.com/official-stockfish/Stockfish/pull/5260 No functional change --- src/evaluate.cpp | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index abb04fcc2e1..e5ebd45ae03 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -72,23 +72,15 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, smallNet = false; } - const auto adjustEval = [&](int shufflingConstant) { - // Blend optimism and eval with nnue complexity and material imbalance - optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / 584; - nnue -= nnue * (nnueComplexity * 5 / 3) / 32395; - - int npm = pos.non_pawn_material() / 64; - v = (nnue * (npm + 943 + 11 * pos.count()) + optimism * (npm + 140)) / 1058; - - // Damp down the evaluation linearly when shuffling - int shuffling = pos.rule50_count(); - v = v * (shufflingConstant - shuffling) / 207; - }; - - if (!smallNet) - adjustEval(178); - else - adjustEval(206); + // Blend optimism and eval with nnue complexity and material imbalance + optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / 584; + nnue -= nnue * (nnueComplexity * 5 / 3) / 32395; + + int npm = pos.non_pawn_material() / 64; + v = (nnue * (npm + 943 + 11 * pos.count()) + optimism * (npm + 140)) / 1058; + + // Damp down the evaluation linearly when shuffling + v = v * ((smallNet ? 206 : 178) - pos.rule50_count()) / 207; // Guarantee evaluation does not hit the tablebase range v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); From 99f1bacfd6864afca86ae74f33232b9cdfb3828c Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Sat, 18 May 2024 22:15:41 +0800 Subject: [PATCH 1506/1766] VVLTC search tune Tuned with 85k games at VVLTC. VVLTC 1st sprt: https://tests.stockfishchess.org/tests/view/6648b836308cceea45533ad7 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 14880 W: 3890 L: 3652 D: 7338 Ptnml(0-2): 0, 1255, 4694, 1489, 2 VVLTC 2nd sprt: https://tests.stockfishchess.org/tests/view/6648c34f308cceea45533b4f LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 24984 W: 6502 L: 6235 D: 12247 Ptnml(0-2): 1, 2178, 7867, 2445, 1 closes https://github.com/official-stockfish/Stockfish/pull/5264 Bench: 1198142 --- src/search.cpp | 84 +++++++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 2b95043fd37..54990ce6f03 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -59,9 +59,9 @@ static constexpr double EvalLevel[10] = {0.981, 0.956, 0.895, 0.949, 0.913, // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) { - Value futilityMult = 131 - 48 * noTtCutNode; - Value improvingDeduction = 2 * improving * futilityMult; - Value worseningDeduction = 330 * oppWorsening * futilityMult / 1024; + Value futilityMult = 127 - 48 * noTtCutNode; + Value improvingDeduction = 65 * improving * futilityMult / 32; + Value worseningDeduction = 334 * oppWorsening * futilityMult / 1024; return futilityMult * d - improvingDeduction - worseningDeduction; } @@ -73,15 +73,15 @@ constexpr int futility_move_count(bool improving, Depth depth) { // Add correctionHistory value to raw staticEval and guarantee evaluation does not hit the tablebase range Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { auto cv = w.correctionHistory[pos.side_to_move()][pawn_structure_index(pos)]; - v += cv * std::abs(cv) / 7179; + v += cv * std::abs(cv) / 6047; return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } // History and stats update bonus, based on depth -int stat_bonus(Depth d) { return std::clamp(200 * d - 280, 16, 1495); } +int stat_bonus(Depth d) { return std::clamp(187 * d - 288, 17, 1548); } // History and stats update malus, based on depth -int stat_malus(Depth d) { return (d < 4 ? 586 * d - 284 : 1639); } +int stat_malus(Depth d) { return (d < 4 ? 630 * d - 281 : 1741); } // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(size_t nodes) { return VALUE_DRAW - 1 + Value(nodes & 0x2); } @@ -311,12 +311,12 @@ void Search::Worker::iterative_deepening() { // Reset aspiration window starting size Value avg = rootMoves[pvIdx].averageScore; - delta = 10 + avg * avg / 9474; + delta = 10 + avg * avg / 9828; alpha = std::max(avg - delta, -VALUE_INFINITE); beta = std::min(avg + delta, VALUE_INFINITE); // Adjust optimism based on root move's averageScore (~4 Elo) - optimism[us] = 117 * avg / (std::abs(avg) + 88); + optimism[us] = 116 * avg / (std::abs(avg) + 84); optimism[~us] = -optimism[us]; // Start with a small aspiration window and, in the case of a fail @@ -503,10 +503,10 @@ void Search::Worker::clear() { for (StatsType c : {NoCaptures, Captures}) for (auto& to : continuationHistory[inCheck][c]) for (auto& h : to) - h->fill(-62); + h->fill(-60); for (size_t i = 1; i < reductions.size(); ++i) - reductions[i] = int((21.19 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); + reductions[i] = int((21.69 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); refreshTable.clear(networks); } @@ -739,7 +739,7 @@ Value Search::Worker::search( // Use static evaluation difference to improve quiet move ordering (~9 Elo) if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture) { - int bonus = std::clamp(-12 * int((ss - 1)->staticEval + ss->staticEval), -1749, 1584); + int bonus = std::clamp(-11 * int((ss - 1)->staticEval + ss->staticEval), -1729, 1517); bonus = bonus > 0 ? 2 * bonus : bonus / 2; thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) @@ -762,7 +762,7 @@ Value Search::Worker::search( // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. // Adjust razor margin according to cutoffCnt. (~1 Elo) - if (eval < alpha - 473 - (308 - 138 * ((ss + 1)->cutoffCnt > 3)) * depth * depth) + if (eval < alpha - 474 - (326 - 139 * ((ss + 1)->cutoffCnt > 3)) * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -773,21 +773,21 @@ Value Search::Worker::search( // The depth condition is important for mate finding. if (!ss->ttPv && depth < 11 && eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening) - - (ss - 1)->statScore / 258 + - (ss - 1)->statScore / 252 >= beta && eval >= beta && eval < VALUE_TB_WIN_IN_MAX_PLY && (!ttMove || ttCapture)) return beta > VALUE_TB_LOSS_IN_MAX_PLY ? (eval + beta) / 2 : eval; // Step 9. Null move search with verification search (~35 Elo) - if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 16079 - && eval >= beta && ss->staticEval >= beta - 21 * depth + 324 && !excludedMove + if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 15246 + && eval >= beta && ss->staticEval >= beta - 21 * depth + 366 && !excludedMove && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly && beta > VALUE_TB_LOSS_IN_MAX_PLY) { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and eval - Depth R = std::min(int(eval - beta) / 144, 6) + depth / 3 + 5; + Depth R = std::min(int(eval - beta) / 152, 6) + depth / 3 + 5; ss->currentMove = Move::null(); ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -835,7 +835,7 @@ Value Search::Worker::search( // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search returns a value // much above beta, we can (almost) safely prune the previous move. - probCutBeta = beta + 177 - 65 * improving; + probCutBeta = beta + 176 - 65 * improving; if ( !PvNode && depth > 3 && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY @@ -891,7 +891,7 @@ Value Search::Worker::search( moves_loop: // When in check, search starts here // Step 12. A small Probcut idea, when we are in check (~4 Elo) - probCutBeta = beta + 428; + probCutBeta = beta + 440; if (ss->inCheck && !PvNode && ttCapture && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 4 && ttValue >= probCutBeta && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) @@ -975,15 +975,15 @@ Value Search::Worker::search( // Futility pruning for captures (~2 Elo) if (!givesCheck && lmrDepth < 7 && !ss->inCheck) { - Value futilityValue = ss->staticEval + 305 + 272 * lmrDepth + Value futilityValue = ss->staticEval + 276 + 256 * lmrDepth + PieceValue[capturedPiece] + captHist / 7; if (futilityValue <= alpha) continue; } // SEE based pruning for captures and checks (~11 Elo) - int seeHist = std::clamp(captHist / 32, -185 * depth, 182 * depth); - if (!pos.see_ge(move, -176 * depth - seeHist)) + int seeHist = std::clamp(captHist / 32, -177 * depth, 175 * depth); + if (!pos.see_ge(move, -183 * depth - seeHist)) continue; } else @@ -994,18 +994,18 @@ Value Search::Worker::search( + thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()]; // Continuation history based pruning (~2 Elo) - if (lmrDepth < 6 && history < -4360 * depth) + if (lmrDepth < 6 && history < -4076 * depth) continue; history += 2 * thisThread->mainHistory[us][move.from_to()]; - lmrDepth += history / 4507; + lmrDepth += history / 4401; Value futilityValue = - ss->staticEval + (bestValue < ss->staticEval - 54 ? 142 : 55) + 132 * lmrDepth; + ss->staticEval + (bestValue < ss->staticEval - 53 ? 151 : 57) + 140 * lmrDepth; // Futility pruning: parent node (~13 Elo) - if (!ss->inCheck && lmrDepth < 11 && futilityValue <= alpha) + if (!ss->inCheck && lmrDepth < 10 && futilityValue <= alpha) { if (bestValue <= futilityValue && std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY && futilityValue < VALUE_TB_WIN_IN_MAX_PLY) @@ -1016,7 +1016,7 @@ Value Search::Worker::search( lmrDepth = std::max(lmrDepth, 0); // Prune moves with negative SEE (~4 Elo) - if (!pos.see_ge(move, -27 * lmrDepth * lmrDepth)) + if (!pos.see_ge(move, -26 * lmrDepth * lmrDepth)) continue; } } @@ -1036,11 +1036,11 @@ Value Search::Worker::search( // so changing them requires tests at these types of time controls. // Recursive singular search is avoided. if (!rootNode && move == ttMove && !excludedMove - && depth >= 4 - (thisThread->completedDepth > 33) + ss->ttPv + && depth >= 4 - (thisThread->completedDepth > 35) + ss->ttPv && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - (59 + 49 * (ss->ttPv && !PvNode)) * depth / 64; + Value singularBeta = ttValue - (57 + 50 * (ss->ttPv && !PvNode)) * depth / 64; Depth singularDepth = newDepth / 2; ss->excludedMove = move; @@ -1050,14 +1050,14 @@ Value Search::Worker::search( if (value < singularBeta) { - int doubleMargin = 285 * PvNode - 228 * !ttCapture; + int doubleMargin = 298 * PvNode - 209 * !ttCapture; int tripleMargin = - 121 + 238 * PvNode - 259 * !ttCapture + 117 * (ss->ttPv || !ttCapture); + 117 + 252 * PvNode - 270 * !ttCapture + 111 * (ss->ttPv || !ttCapture); extension = 1 + (value < singularBeta - doubleMargin) + (value < singularBeta - tripleMargin); - depth += ((!PvNode) && (depth < 14)); + depth += ((!PvNode) && (depth < 15)); } // Multi-cut pruning @@ -1092,7 +1092,7 @@ Value Search::Worker::search( else if (PvNode && move == ttMove && move.to_sq() == prevSq && thisThread->captureHistory[movedPiece][move.to_sq()] [type_of(pos.piece_on(move.to_sq()))] - > 4041) + > 3748) extension = 1; } @@ -1143,10 +1143,10 @@ Value Search::Worker::search( ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + (*contHist[0])[movedPiece][move.to_sq()] - + (*contHist[1])[movedPiece][move.to_sq()] - 5313; + + (*contHist[1])[movedPiece][move.to_sq()] - 5266; // Decrease/increase reduction for moves with a good/bad history (~8 Elo) - r -= ss->statScore / (16145 - std::min(depth, 15) * 102); + r -= ss->statScore / (14519 - std::min(depth, 15) * 103); // Step 17. Late moves reduction / extension (LMR, ~117 Elo) if (depth >= 2 && moveCount > 1 + rootNode) @@ -1165,7 +1165,7 @@ Value Search::Worker::search( { // Adjust full-depth search based on LMR results - if the result // was good enough search deeper, if it was bad enough search shallower. - const bool doDeeperSearch = value > (bestValue + 41 + 2 * newDepth); // (~1 Elo) + const bool doDeeperSearch = value > (bestValue + 40 + 2 * newDepth); // (~1 Elo) const bool doShallowerSearch = value < bestValue + newDepth; // (~2 Elo) newDepth += doDeeperSearch - doShallowerSearch; @@ -1326,9 +1326,9 @@ Value Search::Worker::search( // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (depth > 4) + (depth > 5) + (PvNode || cutNode) + ((ss - 1)->statScore < -14323) - + ((ss - 1)->moveCount > 10) + (!ss->inCheck && bestValue <= ss->staticEval - 120) - + (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 76); + int bonus = (depth > 4) + (depth > 5) + (PvNode || cutNode) + ((ss - 1)->statScore < -13241) + + ((ss - 1)->moveCount > 10) + (!ss->inCheck && bestValue <= ss->staticEval - 127) + + (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 74); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] @@ -1494,7 +1494,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, if (bestValue > alpha) alpha = bestValue; - futilityBase = ss->staticEval + 259; + futilityBase = ss->staticEval + 264; } const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, @@ -1566,11 +1566,11 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, + (*contHist[1])[pos.moved_piece(move)][move.to_sq()] + thisThread->pawnHistory[pawn_structure_index(pos)][pos.moved_piece(move)] [move.to_sq()] - <= 4057) + <= 4348) continue; // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, -68)) + if (!pos.see_ge(move, -63)) continue; } @@ -1636,7 +1636,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth Search::Worker::reduction(bool i, Depth d, int mn, int delta) { int reductionScale = reductions[d] * reductions[mn]; - return (reductionScale + 1284 - delta * 755 / rootDelta) / 1024 + (!i && reductionScale > 1133); + return (reductionScale + 1147 - delta * 755 / rootDelta) / 1024 + (!i && reductionScale > 1125); } // elapsed() returns the time elapsed since the search started. If the From 2d3258162387bf38551962bf2c9dd1d47e72b4dd Mon Sep 17 00:00:00 2001 From: Viren6 <94880762+Viren6@users.noreply.github.com> Date: Sun, 19 May 2024 01:40:29 +0100 Subject: [PATCH 1507/1766] Revert "Simplify Away Quadruple Extensions" This reverts commit 4edd1a3 The unusual result of (combined) +12.0 +- 3.7 in the 2 VVLTC simplification SPRTs ran was the result of base having only 64MB of hash instead of 512MB (Asymmetric hash). Vizvezdenec was the one to notice this. closes https://github.com/official-stockfish/Stockfish/pull/5265 bench 1404295 Co-Authored-By: Michael Chaly <26898827+Vizvezdenec@users.noreply.github.com> --- src/search.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 54990ce6f03..cbd454efb76 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1053,9 +1053,11 @@ Value Search::Worker::search( int doubleMargin = 298 * PvNode - 209 * !ttCapture; int tripleMargin = 117 + 252 * PvNode - 270 * !ttCapture + 111 * (ss->ttPv || !ttCapture); + int quadMargin = 471 + 343 * PvNode - 281 * !ttCapture + 217 * ss->ttPv; extension = 1 + (value < singularBeta - doubleMargin) - + (value < singularBeta - tripleMargin); + + (value < singularBeta - tripleMargin) + + (value < singularBeta - quadMargin); depth += ((!PvNode) && (depth < 15)); } From 27eb49a2211c90650ef64d5102e6e36ca5e69af0 Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Fri, 17 May 2024 18:05:12 +0800 Subject: [PATCH 1508/1766] Simplify ClippedReLU Removes some max calls Some speedup stats, courtesy of @AndyGrant (albeit measured in an alternate implementation) Dev 749240 nps Base 748495 nps Gain 0.100% 289936 games STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 203040 W: 52213 L: 52179 D: 98648 Ptnml(0-2): 480, 20722, 59139, 20642, 537 https://tests.stockfishchess.org/tests/view/664805fe6dcff0d1d6b05f2c closes #5261 No functional change --- src/nnue/layers/clipped_relu.h | 48 ++++++++++++++++------------------ src/nnue/nnue_misc.cpp | 9 +++---- src/tune.cpp | 6 ++--- src/uci.cpp | 6 ++--- 4 files changed, 30 insertions(+), 39 deletions(-) diff --git a/src/nnue/layers/clipped_relu.h b/src/nnue/layers/clipped_relu.h index 813234c59cd..2ee378ad881 100644 --- a/src/nnue/layers/clipped_relu.h +++ b/src/nnue/layers/clipped_relu.h @@ -65,41 +65,37 @@ class ClippedReLU { if constexpr (InputDimensions % SimdWidth == 0) { constexpr IndexType NumChunks = InputDimensions / SimdWidth; - const __m256i Zero = _mm256_setzero_si256(); const __m256i Offsets = _mm256_set_epi32(7, 3, 6, 2, 5, 1, 4, 0); const auto in = reinterpret_cast(input); const auto out = reinterpret_cast<__m256i*>(output); for (IndexType i = 0; i < NumChunks; ++i) { const __m256i words0 = - _mm256_srai_epi16(_mm256_packs_epi32(_mm256_load_si256(&in[i * 4 + 0]), - _mm256_load_si256(&in[i * 4 + 1])), + _mm256_srli_epi16(_mm256_packus_epi32(_mm256_load_si256(&in[i * 4 + 0]), + _mm256_load_si256(&in[i * 4 + 1])), WeightScaleBits); const __m256i words1 = - _mm256_srai_epi16(_mm256_packs_epi32(_mm256_load_si256(&in[i * 4 + 2]), - _mm256_load_si256(&in[i * 4 + 3])), + _mm256_srli_epi16(_mm256_packus_epi32(_mm256_load_si256(&in[i * 4 + 2]), + _mm256_load_si256(&in[i * 4 + 3])), WeightScaleBits); - _mm256_store_si256( - &out[i], _mm256_permutevar8x32_epi32( - _mm256_max_epi8(_mm256_packs_epi16(words0, words1), Zero), Offsets)); + _mm256_store_si256(&out[i], _mm256_permutevar8x32_epi32( + _mm256_packs_epi16(words0, words1), Offsets)); } } else { constexpr IndexType NumChunks = InputDimensions / (SimdWidth / 2); - const __m128i Zero = _mm_setzero_si128(); const auto in = reinterpret_cast(input); const auto out = reinterpret_cast<__m128i*>(output); for (IndexType i = 0; i < NumChunks; ++i) { - const __m128i words0 = _mm_srai_epi16( - _mm_packs_epi32(_mm_load_si128(&in[i * 4 + 0]), _mm_load_si128(&in[i * 4 + 1])), + const __m128i words0 = _mm_srli_epi16( + _mm_packus_epi32(_mm_load_si128(&in[i * 4 + 0]), _mm_load_si128(&in[i * 4 + 1])), WeightScaleBits); - const __m128i words1 = _mm_srai_epi16( - _mm_packs_epi32(_mm_load_si128(&in[i * 4 + 2]), _mm_load_si128(&in[i * 4 + 3])), + const __m128i words1 = _mm_srli_epi16( + _mm_packus_epi32(_mm_load_si128(&in[i * 4 + 2]), _mm_load_si128(&in[i * 4 + 3])), WeightScaleBits); - const __m128i packedbytes = _mm_packs_epi16(words0, words1); - _mm_store_si128(&out[i], _mm_max_epi8(packedbytes, Zero)); + _mm_store_si128(&out[i], _mm_packs_epi16(words0, words1)); } } constexpr IndexType Start = InputDimensions % SimdWidth == 0 @@ -109,9 +105,7 @@ class ClippedReLU { #elif defined(USE_SSE2) constexpr IndexType NumChunks = InputDimensions / SimdWidth; - #ifdef USE_SSE41 - const __m128i Zero = _mm_setzero_si128(); - #else + #ifndef USE_SSE41 const __m128i k0x80s = _mm_set1_epi8(-128); #endif @@ -119,6 +113,15 @@ class ClippedReLU { const auto out = reinterpret_cast<__m128i*>(output); for (IndexType i = 0; i < NumChunks; ++i) { + #if defined(USE_SSE41) + const __m128i words0 = _mm_srli_epi16( + _mm_packus_epi32(_mm_load_si128(&in[i * 4 + 0]), _mm_load_si128(&in[i * 4 + 1])), + WeightScaleBits); + const __m128i words1 = _mm_srli_epi16( + _mm_packus_epi32(_mm_load_si128(&in[i * 4 + 2]), _mm_load_si128(&in[i * 4 + 3])), + WeightScaleBits); + _mm_store_si128(&out[i], _mm_packs_epi16(words0, words1)); + #else const __m128i words0 = _mm_srai_epi16( _mm_packs_epi32(_mm_load_si128(&in[i * 4 + 0]), _mm_load_si128(&in[i * 4 + 1])), WeightScaleBits); @@ -126,15 +129,8 @@ class ClippedReLU { _mm_packs_epi32(_mm_load_si128(&in[i * 4 + 2]), _mm_load_si128(&in[i * 4 + 3])), WeightScaleBits); const __m128i packedbytes = _mm_packs_epi16(words0, words1); - _mm_store_si128(&out[i], - - #ifdef USE_SSE41 - _mm_max_epi8(packedbytes, Zero) - #else - _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s) + _mm_store_si128(&out[i], _mm_subs_epi8(_mm_adds_epi8(packedbytes, k0x80s), k0x80s)); #endif - - ); } constexpr IndexType Start = NumChunks * SimdWidth; diff --git a/src/nnue/nnue_misc.cpp b/src/nnue/nnue_misc.cpp index a13c717c3d8..b54bbaba3dd 100644 --- a/src/nnue/nnue_misc.cpp +++ b/src/nnue/nnue_misc.cpp @@ -178,14 +178,11 @@ trace(Position& pos, const Eval::NNUE::Networks& networks, Eval::NNUE::Accumulat ss << "| " << bucket << " "; ss << " | "; format_cp_aligned_dot(t.psqt[bucket], ss, pos); - ss << " " - << " | "; + ss << " " << " | "; format_cp_aligned_dot(t.positional[bucket], ss, pos); - ss << " " - << " | "; + ss << " " << " | "; format_cp_aligned_dot(t.psqt[bucket] + t.positional[bucket], ss, pos); - ss << " " - << " |"; + ss << " " << " |"; if (bucket == t.correctBucket) ss << " <-- this bucket is used"; ss << '\n'; diff --git a/src/tune.cpp b/src/tune.cpp index 3e5ebe5e6c3..84f59524fb4 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -59,8 +59,7 @@ void make_option(OptionsMap* options, const string& n, int v, const SetRange& r) // Print formatted parameters, ready to be copy-pasted in Fishtest std::cout << n << "," << v << "," << r(v).first << "," << r(v).second << "," - << (r(v).second - r(v).first) / 20.0 << "," - << "0.0020" << std::endl; + << (r(v).second - r(v).first) / 20.0 << "," << "0.0020" << std::endl; } } @@ -118,7 +117,6 @@ void Tune::Entry::read_option() { namespace Stockfish { -void Tune::read_results() { /* ...insert your values here... */ -} +void Tune::read_results() { /* ...insert your values here... */ } } // namespace Stockfish diff --git a/src/uci.cpp b/src/uci.cpp index cb686a027db..cb9d7b08556 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -286,9 +286,9 @@ void UCIEngine::bench(std::istream& args) { dbg_print(); - std::cerr << "\n===========================" - << "\nTotal time (ms) : " << elapsed << "\nNodes searched : " << nodes - << "\nNodes/second : " << 1000 * nodes / elapsed << std::endl; + std::cerr << "\n===========================" << "\nTotal time (ms) : " << elapsed + << "\nNodes searched : " << nodes << "\nNodes/second : " << 1000 * nodes / elapsed + << std::endl; // reset callback, to not capture a dangling reference to nodesSearched engine.set_on_update_full([&](const auto& i) { on_update_full(i, options["UCI_ShowWDL"]); }); From a3bb7e626d1489bbbcc16014b16065849ec786b5 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Sun, 19 May 2024 10:12:05 +0200 Subject: [PATCH 1509/1766] Tweak continuation history bonus dependent on ply. This patch is based on following tuning https://tests.stockfishchess.org/tests/view/6648b2eb308cceea45533abe by only using the tuned factors for the continuation history. Passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 99904 W: 25865 L: 25457 D: 48582 Ptnml(0-2): 281, 11705, 25578, 12101, 287 https://tests.stockfishchess.org/tests/view/6648c136308cceea45533af8 Passed LTC: LLR: 2.96 (-2.94,2.94) <0.50,2.50> Total: 36402 W: 9362 L: 9039 D: 18001 Ptnml(0-2): 20, 3952, 9951, 4241, 37 https://tests.stockfishchess.org/tests/view/6648ee3cb8fa20e74c39f3fd closes https://github.com/official-stockfish/Stockfish/pull/5267 Bench: 1917762 --- src/search.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index cbd454efb76..5b9c9bb00a1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1779,6 +1779,8 @@ void update_all_stats(const Position& pos, // by moves at ply -1, -2, -3, -4, and -6 with current move. void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { + bonus = bonus * (112 * ss->ply + 136) / (159 * ss->ply + 124); + for (int i : {1, 2, 3, 4, 6}) { // Only update the first 2 continuation histories if we are in check From 4a66a7c9caeca70ea8cd4527de7ec1e839b6cf46 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 19 May 2024 18:48:43 +0300 Subject: [PATCH 1510/1766] Do more aggressive pawn history updates Tweak of recent patch that made pawn history to update for move that caused a fail low - and setting up default value of it to -900. This patch makes it more aggressive - twice bigger updates and default value -1100. Passed STC: https://tests.stockfishchess.org/tests/view/6648c5d4308cceea45533b5d LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 235200 W: 61090 L: 60476 D: 113634 Ptnml(0-2): 763, 27952, 59651, 28376, 858 Passed LTC: https://tests.stockfishchess.org/tests/view/664a1008ae57c1758ac5b523 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 20076 W: 5193 L: 4908 D: 9975 Ptnml(0-2): 7, 2105, 5534, 2380, 12 closes https://github.com/official-stockfish/Stockfish/pull/5268 Bench: 1590474 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5b9c9bb00a1..2618b984797 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -496,7 +496,7 @@ void Search::Worker::clear() { counterMoves.fill(Move::none()); mainHistory.fill(0); captureHistory.fill(0); - pawnHistory.fill(-900); + pawnHistory.fill(-1100); correctionHistory.fill(0); for (bool inCheck : {false, true}) @@ -1339,7 +1339,7 @@ Value Search::Worker::search( if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) thisThread->pawnHistory[pawn_structure_index(pos)][pos.piece_on(prevSq)][prevSq] - << stat_bonus(depth) * bonus * 2; + << stat_bonus(depth) * bonus * 4; } if (PvNode) From 81e21a69f02164fd988d5636a47c8790a1174b81 Mon Sep 17 00:00:00 2001 From: Stefan Geschwentner Date: Sun, 19 May 2024 10:12:05 +0200 Subject: [PATCH 1511/1766] Simplify the recently introduced ply-based cmh bonus factor. Replace it with a constant which is an approximation of the limit of the factor. STC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 120064 W: 30967 L: 30836 D: 58261 Ptnml(0-2): 421, 14238, 30608, 14319, 446 https://tests.stockfishchess.org/tests/view/6649d146b8fa20e74c39f4ad LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 53856 W: 13719 L: 13530 D: 26607 Ptnml(0-2): 31, 5879, 14922, 6062, 34 https://tests.stockfishchess.org/tests/view/664a027fae57c1758ac5b4ee closes https://github.com/official-stockfish/Stockfish/pull/5270 Bench: 1355618 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 2618b984797..7e95dd878c3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1779,7 +1779,7 @@ void update_all_stats(const Position& pos, // by moves at ply -1, -2, -3, -4, and -6 with current move. void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { - bonus = bonus * (112 * ss->ply + 136) / (159 * ss->ply + 124); + bonus = bonus * 45 / 64; for (int i : {1, 2, 3, 4, 6}) { From 4d88a63e607f44e59b9cc56b45984937e5eb123c Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Sun, 19 May 2024 14:01:49 -0400 Subject: [PATCH 1512/1766] Re-eval only if smallnet output flips from simple eval Recent attempts to change the smallnet nnue re-eval threshold did not show much elo difference: https://tests.stockfishchess.org/tests/view/664a29bb25a9058c4d21d53c https://tests.stockfishchess.org/tests/view/664a299925a9058c4d21d53a Passed non-regression STC: https://tests.stockfishchess.org/tests/view/664a3ea95fc7b70b8817aee2 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 22304 W: 5905 L: 5664 D: 10735 Ptnml(0-2): 67, 2602, 5603, 2783, 97 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/664a43d35fc7b70b8817aef4 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 37536 W: 9667 L: 9460 D: 18409 Ptnml(0-2): 25, 4090, 10321, 4317, 15 closes https://github.com/official-stockfish/Stockfish/pull/5271 bench 1287409 --- src/evaluate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index e5ebd45ae03..2cf82eaf5a0 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -66,7 +66,7 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, Value nnue = smallNet ? networks.small.evaluate(pos, &caches.small, true, &nnueComplexity) : networks.big.evaluate(pos, &caches.big, true, &nnueComplexity); - if (smallNet && (nnue * simpleEval < 0 || std::abs(nnue) < 500)) + if (smallNet && nnue * simpleEval < 0) { nnue = networks.big.evaluate(pos, &caches.big, true, &nnueComplexity); smallNet = false; From 0c797367a3a9783ff87422d543eb2106fea3e948 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Mon, 20 May 2024 03:22:40 +0300 Subject: [PATCH 1513/1766] Update correction history in case of successful null move pruning Since null move pruning uses the same position it makes some sense to try to update correction history there in case of fail high. Update value is 4 times less than normal update. Passed STC: https://tests.stockfishchess.org/tests/view/664a011cae57c1758ac5b4dd LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 419360 W: 108390 L: 107505 D: 203465 Ptnml(0-2): 1416, 49603, 106724, 50554, 1383 Passed LTC: https://tests.stockfishchess.org/tests/view/664a53d95fc7b70b8817c65b LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 193518 W: 49076 L: 48434 D: 96008 Ptnml(0-2): 89, 21335, 53263, 21989, 83 closes https://github.com/official-stockfish/Stockfish/pull/5272 bench 1301487 --- src/nnue/nnue_misc.cpp | 9 ++++++--- src/search.cpp | 9 +++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/nnue/nnue_misc.cpp b/src/nnue/nnue_misc.cpp index b54bbaba3dd..a13c717c3d8 100644 --- a/src/nnue/nnue_misc.cpp +++ b/src/nnue/nnue_misc.cpp @@ -178,11 +178,14 @@ trace(Position& pos, const Eval::NNUE::Networks& networks, Eval::NNUE::Accumulat ss << "| " << bucket << " "; ss << " | "; format_cp_aligned_dot(t.psqt[bucket], ss, pos); - ss << " " << " | "; + ss << " " + << " | "; format_cp_aligned_dot(t.positional[bucket], ss, pos); - ss << " " << " | "; + ss << " " + << " | "; format_cp_aligned_dot(t.psqt[bucket] + t.positional[bucket], ss, pos); - ss << " " << " |"; + ss << " " + << " |"; if (bucket == t.correctBucket) ss << " <-- this bucket is used"; ss << '\n'; diff --git a/src/search.cpp b/src/search.cpp index 7e95dd878c3..2ed5d97bb5d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -802,7 +802,16 @@ Value Search::Worker::search( if (nullValue >= beta && nullValue < VALUE_TB_WIN_IN_MAX_PLY) { if (thisThread->nmpMinPly || depth < 16) + { + if (nullValue >= ss->staticEval) + { + auto bonus = std::min(int(nullValue - ss->staticEval) * depth / 32, + CORRECTION_HISTORY_LIMIT / 16); + thisThread->correctionHistory[us][pawn_structure_index(pos)] + << bonus; + } return nullValue; + } assert(!thisThread->nmpMinPly); // Recursive verification is not allowed From b8ccaf038a21effba4613dca95f30eb1bc3d77b9 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Mon, 20 May 2024 03:14:00 +0300 Subject: [PATCH 1514/1766] Use same shuffling Constant for both nets Passed STC: https://tests.stockfishchess.org/tests/view/664a42b15fc7b70b8817aeef LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 87840 W: 22759 L: 22594 D: 42487 Ptnml(0-2): 335, 10351, 22324, 10634, 276 Passed LTC: https://tests.stockfishchess.org/tests/view/664a46995fc7b70b8817af02 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 163122 W: 41443 L: 41367 D: 80312 Ptnml(0-2): 105, 18154, 44927, 18310, 65 closes https://github.com/official-stockfish/Stockfish/pull/5273 bench: 1190174 --- src/evaluate.cpp | 2 +- src/tune.cpp | 6 ++++-- src/uci.cpp | 6 +++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 2cf82eaf5a0..3a24657f9fd 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -80,7 +80,7 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, v = (nnue * (npm + 943 + 11 * pos.count()) + optimism * (npm + 140)) / 1058; // Damp down the evaluation linearly when shuffling - v = v * ((smallNet ? 206 : 178) - pos.rule50_count()) / 207; + v = v * (204 - pos.rule50_count()) / 208; // Guarantee evaluation does not hit the tablebase range v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); diff --git a/src/tune.cpp b/src/tune.cpp index 84f59524fb4..3e5ebe5e6c3 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -59,7 +59,8 @@ void make_option(OptionsMap* options, const string& n, int v, const SetRange& r) // Print formatted parameters, ready to be copy-pasted in Fishtest std::cout << n << "," << v << "," << r(v).first << "," << r(v).second << "," - << (r(v).second - r(v).first) / 20.0 << "," << "0.0020" << std::endl; + << (r(v).second - r(v).first) / 20.0 << "," + << "0.0020" << std::endl; } } @@ -117,6 +118,7 @@ void Tune::Entry::read_option() { namespace Stockfish { -void Tune::read_results() { /* ...insert your values here... */ } +void Tune::read_results() { /* ...insert your values here... */ +} } // namespace Stockfish diff --git a/src/uci.cpp b/src/uci.cpp index cb9d7b08556..cb686a027db 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -286,9 +286,9 @@ void UCIEngine::bench(std::istream& args) { dbg_print(); - std::cerr << "\n===========================" << "\nTotal time (ms) : " << elapsed - << "\nNodes searched : " << nodes << "\nNodes/second : " << 1000 * nodes / elapsed - << std::endl; + std::cerr << "\n===========================" + << "\nTotal time (ms) : " << elapsed << "\nNodes searched : " << nodes + << "\nNodes/second : " << 1000 * nodes / elapsed << std::endl; // reset callback, to not capture a dangling reference to nodesSearched engine.set_on_update_full([&](const auto& i) { on_update_full(i, options["UCI_ShowWDL"]); }); From daf9787de197ce9e5478f3e7ceec8c64cb3d549a Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Tue, 21 May 2024 00:40:55 +0300 Subject: [PATCH 1515/1766] Rescale pawn history updates This patch is somewhat of a continuation of recent pawn history gainers. It makes pawn history updates after search twice smaller. Since on average they make pawn history more negative offset is changed to lower value to remain average value approximately the same. https://tests.stockfishchess.org/tests/view/664b3af9830eb9f886614aab Passed STC: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 170464 W: 44239 L: 43724 D: 82501 Ptnml(0-2): 523, 20278, 43128, 20767, 536 Passed LTC against pending PR : https://tests.stockfishchess.org/tests/view/664b8c58830eb9f886614b64 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 98178 W: 25015 L: 24569 D: 48594 Ptnml(0-2): 48, 10769, 27005, 11223, 44 closes https://github.com/official-stockfish/Stockfish/pull/5275 Bench: 1343175 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 2ed5d97bb5d..4c5e521e779 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -496,7 +496,7 @@ void Search::Worker::clear() { counterMoves.fill(Move::none()); mainHistory.fill(0); captureHistory.fill(0); - pawnHistory.fill(-1100); + pawnHistory.fill(-1300); correctionHistory.fill(0); for (bool inCheck : {false, true}) @@ -1827,7 +1827,7 @@ void update_quiet_histories( update_continuation_histories(ss, pos.moved_piece(move), move.to_sq(), bonus); int pIndex = pawn_structure_index(pos); - workerThread.pawnHistory[pIndex][pos.moved_piece(move)][move.to_sq()] << bonus; + workerThread.pawnHistory[pIndex][pos.moved_piece(move)][move.to_sq()] << bonus / 2; } // Updates move sorting heuristics From f27a9be29c74b1d12babeb8a06ee992a22d67c9a Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sat, 18 May 2024 03:19:36 -0700 Subject: [PATCH 1516/1766] Reduce When TTValue is Above Alpha Passed STC: LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 53376 W: 13818 L: 13476 D: 26082 Ptnml(0-2): 156, 6212, 13626, 6522, 172 https://tests.stockfishchess.org/tests/view/664aa261830eb9f8866145e5 Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 393444 W: 100096 L: 99042 D: 194306 Ptnml(0-2): 191, 43516, 108248, 44582, 185 https://tests.stockfishchess.org/tests/view/664ab54f830eb9f88661463c closes https://github.com/official-stockfish/Stockfish/pull/5276 Bench: 1024562 --- src/search.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 4c5e521e779..2817247d22e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -42,6 +42,7 @@ #include "thread.h" #include "timeman.h" #include "tt.h" +#include "types.h" #include "uci.h" #include "ucioption.h" @@ -833,9 +834,12 @@ Value Search::Worker::search( if (PvNode && !ttMove) depth -= 3; + if (!PvNode && ss->ttHit && (tte->bound() & BOUND_UPPER) && ttValue > alpha + 5 * depth) + depth--; + // Use qsearch if depth <= 0. if (depth <= 0) - return qsearch(pos, ss, alpha, beta); + return qsearch < PvNode ? PV : NonPV > (pos, ss, alpha, beta); // For cutNodes without a ttMove, we decrease depth by 2 if depth is high enough. if (cutNode && depth >= 8 && (!ttMove || tte->bound() == BOUND_UPPER)) From 87bad0c38a2b6a654850e61127dc0667a49acf82 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Tue, 21 May 2024 02:19:54 +0300 Subject: [PATCH 1517/1766] Refine Evaluation Scaling with Piece-Specific Weights Refine Evaluation Scaling with Piece-Specific Weights, instead of the simplified npm method. I took the initial idea from Viren6 , as he worked on it in September of last year. I worked on it, and tuned it, and now it passed both tests. Passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 95712 W: 24731 L: 24325 D: 46656 Ptnml(0-2): 363, 11152, 24357, 11684, 300 https://tests.stockfishchess.org/tests/view/664b5493830eb9f886614af3 Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 204480 W: 52167 L: 51501 D: 100812 Ptnml(0-2): 114, 22579, 56166, 23289, 92 https://tests.stockfishchess.org/tests/view/664b75dd830eb9f886614b44 closes https://github.com/official-stockfish/Stockfish/pull/5277 Bench: 1384337 --- src/evaluate.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 3a24657f9fd..44e69b3fddd 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -76,8 +76,13 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / 584; nnue -= nnue * (nnueComplexity * 5 / 3) / 32395; - int npm = pos.non_pawn_material() / 64; - v = (nnue * (npm + 943 + 11 * pos.count()) + optimism * (npm + 140)) / 1058; + v = (nnue + * (32961 + 381 * pos.count() + 349 * pos.count() + + 392 * pos.count() + 649 * pos.count() + 1211 * pos.count()) + + optimism + * (4835 + 136 * pos.count() + 375 * pos.count() + + 403 * pos.count() + 628 * pos.count() + 1124 * pos.count())) + / 32768; // Damp down the evaluation linearly when shuffling v = v * (204 - pos.rule50_count()) / 208; From c86ec8ec2916924065138770e0201c2cfe6d3e72 Mon Sep 17 00:00:00 2001 From: MinetaS Date: Tue, 21 May 2024 12:42:34 +0900 Subject: [PATCH 1518/1766] Remove cutoffCnt margin adjustment in razoring Passed non-regression STC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 65344 W: 16767 L: 16578 D: 31999 Ptnml(0-2): 198, 7557, 16987, 7718, 212 https://tests.stockfishchess.org/tests/view/664bd895830eb9f886615a26 Passed non-regression LTC: LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 35214 W: 8999 L: 8791 D: 17424 Ptnml(0-2): 16, 3804, 9760, 4010, 17 https://tests.stockfishchess.org/tests/view/664bead5830eb9f886615a52 closes https://github.com/official-stockfish/Stockfish/pull/5278 Bench: 1296223 --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 2817247d22e..a152b931ed7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -762,8 +762,7 @@ Value Search::Worker::search( // Step 7. Razoring (~1 Elo) // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. - // Adjust razor margin according to cutoffCnt. (~1 Elo) - if (eval < alpha - 474 - (326 - 139 * ((ss + 1)->cutoffCnt > 3)) * depth * depth) + if (eval < alpha - 474 - 324 * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) From c14b69790a62aad89fcc471cde482923dfe57f1e Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Tue, 21 May 2024 13:55:20 -0400 Subject: [PATCH 1519/1766] Lower smallnet threshold with updated eval divisors Params found after 30k spsa games at 60+0.6, with initial values from 64k spsa games at 45+0.45 First spsa with 64k / 120k games at 45+0.45: https://tests.stockfishchess.org/tests/view/664a561b5fc7b70b8817c663 https://tests.stockfishchess.org/tests/view/664ae88e830eb9f8866146f9 Second spsa with 30k / 120k games at 60+0.6: https://tests.stockfishchess.org/tests/view/664be227830eb9f886615a36 Values found at 10k games at 60+0.6 also passed STC and LTC: https://tests.stockfishchess.org/tests/view/664bf4bd830eb9f886615a72 https://tests.stockfishchess.org/tests/view/664c0905830eb9f886615abf Passed STC: https://tests.stockfishchess.org/tests/view/664c139e830eb9f886615af2 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 69408 W: 18216 L: 17842 D: 33350 Ptnml(0-2): 257, 8275, 17401, 8379, 392 Passed LTC: https://tests.stockfishchess.org/tests/view/664cdaf7830eb9f886616a24 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 35466 W: 9075 L: 8758 D: 17633 Ptnml(0-2): 27, 3783, 9794, 4104, 25 closes https://github.com/official-stockfish/Stockfish/pull/5280 bench 1301287 --- src/evaluate.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 44e69b3fddd..ca09aaf9e82 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -46,7 +46,7 @@ int Eval::simple_eval(const Position& pos, Color c) { bool Eval::use_smallnet(const Position& pos) { int simpleEval = simple_eval(pos, pos.side_to_move()); - return std::abs(simpleEval) > 1126 + 6 * pos.count(); + return std::abs(simpleEval) > 1018 + 5 * pos.count(); } // Evaluate is the evaluator for the outer world. It returns a static evaluation @@ -73,8 +73,8 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, } // Blend optimism and eval with nnue complexity and material imbalance - optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / 584; - nnue -= nnue * (nnueComplexity * 5 / 3) / 32395; + optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / 620; + nnue -= nnue * (nnueComplexity * 5 / 3) / 32082; v = (nnue * (32961 + 381 * pos.count() + 349 * pos.count() @@ -82,7 +82,7 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, + optimism * (4835 + 136 * pos.count() + 375 * pos.count() + 403 * pos.count() + 628 * pos.count() + 1124 * pos.count())) - / 32768; + / 36860; // Damp down the evaluation linearly when shuffling v = v * (204 - pos.rule50_count()) / 208; From ed79745bb9e7207b604c62758ea45dd5c597ed8d Mon Sep 17 00:00:00 2001 From: Dubslow Date: Tue, 4 Apr 2023 22:55:52 -0500 Subject: [PATCH 1520/1766] Improve comments about DEPTH constants Also "fix" movepicker to allow depths between CHECKS and NO_CHECKS, which makes them easier to tweak (not that they get tweaked hardly ever) (This was more beneficial when there was a third stage to DEPTH_QS, but it's still an improvement now) closes https://github.com/official-stockfish/Stockfish/pull/5205 No functional change --- src/movepick.cpp | 4 ++-- src/search.cpp | 31 ++++++++++++++++++------------- src/tt.cpp | 12 ++++++++---- src/tt.h | 5 ++++- src/types.h | 17 +++++++++++------ 5 files changed, 43 insertions(+), 26 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 4a93662db43..7def0ce84fa 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -361,8 +361,8 @@ Move MovePicker::next_move(bool skipQuiets) { if (select([]() { return true; })) return *(cur - 1); - // If we did not find any move and we do not try checks, we have finished - if (depth != DEPTH_QS_CHECKS) + // If we found no move and the depth is too low to try checks, then we have finished + if (depth <= DEPTH_QS_NORMAL) return Move::none(); ++stage; diff --git a/src/search.cpp b/src/search.cpp index a152b931ed7..87cfdbc2b71 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -733,7 +733,7 @@ Value Search::Worker::search( ss->staticEval = eval = to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); // Static evaluation is saved as it was before adjustment by correction history - tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, Move::none(), + tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_UNSEARCHED, Move::none(), unadjustedStaticEval, tt.generation()); } @@ -1387,8 +1387,11 @@ Value Search::Worker::search( } -// Quiescence search function, which is called by the main search -// function with zero depth, or recursively with further decreasing depth per call. +// Quiescence search function, which is called by the main search function with zero depth, or +// recursively with further decreasing depth per call. With depth <= 0, we "should" be using +// static eval only, but tactical moves may confuse the static eval. To fight this horizon effect, +// we implement this qsearch of tactical moves only. +// See https://www.chessprogramming.org/Horizon_Effect and https://www.chessprogramming.org/Quiescence_Search // (~155 Elo) template Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { @@ -1446,8 +1449,10 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, assert(0 <= ss->ply && ss->ply < MAX_PLY); - // Decide the replacement and cutoff priority of the qsearch TT entries - ttDepth = ss->inCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS : DEPTH_QS_NO_CHECKS; + // Note that unlike regular search, which stores literal depth, in QS we only store the + // current movegen stage. If in check, we search all evasions and thus store + // DEPTH_QS_CHECKS. (Evasions may be quiet, and _CHECKS includes quiets.) + ttDepth = ss->inCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS : DEPTH_QS_NORMAL; // Step 3. Transposition table lookup posKey = pos.key(); @@ -1499,8 +1504,8 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, if (std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY && !PvNode) bestValue = (3 * bestValue + beta) / 4; if (!ss->ttHit) - tte->save(posKey, value_to_tt(bestValue, ss->ply), false, BOUND_LOWER, DEPTH_NONE, - Move::none(), unadjustedStaticEval, tt.generation()); + tte->save(posKey, value_to_tt(bestValue, ss->ply), false, BOUND_LOWER, + DEPTH_UNSEARCHED, Move::none(), unadjustedStaticEval, tt.generation()); return bestValue; } @@ -1514,16 +1519,16 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, (ss - 2)->continuationHistory}; - // Initialize a MovePicker object for the current position, and prepare - // to search the moves. Because the depth is <= 0 here, only captures, - // queen promotions, and other checks (only if depth >= DEPTH_QS_CHECKS) - // will be generated. + // Initialize a MovePicker object for the current position, and prepare to search the moves. + // We presently use two stages of qs movegen, first captures+checks, then captures only. + // (When in check, we simply search all evasions.) + // (Presently, having the checks stage is worth only 1 Elo, and may be removable in the near future, + // which would result in only a single stage of QS movegen.) Square prevSq = ((ss - 1)->currentMove).is_ok() ? ((ss - 1)->currentMove).to_sq() : SQ_NONE; MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory, contHist, &thisThread->pawnHistory); - // Step 5. Loop through all pseudo-legal moves until no moves remain - // or a beta cutoff occurs. + // Step 5. Loop through all pseudo-legal moves until no moves remain or a beta cutoff occurs. while ((move = mp.next_move()) != Move::none()) { assert(move.is_ok()); diff --git a/src/tt.cpp b/src/tt.cpp index cb46fc8a9a6..3f5b9d4d9b0 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -30,6 +30,10 @@ namespace Stockfish { +// DEPTH_ENTRY_OFFSET exists because 1) we use `bool(depth8)` as the occupancy check, but +// 2) we need to store negative depths for QS. (`depth8` is the only field with "spare bits": +// we sacrifice the ability to store depths greater than 1<<8 less the offset, as asserted below.) + // Populates the TTEntry with a new node's data, possibly // overwriting an old position. The update is not atomic and can be racy. void TTEntry::save( @@ -40,14 +44,14 @@ void TTEntry::save( move16 = m; // Overwrite less valuable entries (cheapest checks first) - if (b == BOUND_EXACT || uint16_t(k) != key16 || d - DEPTH_OFFSET + 2 * pv > depth8 - 4 + if (b == BOUND_EXACT || uint16_t(k) != key16 || d - DEPTH_ENTRY_OFFSET + 2 * pv > depth8 - 4 || relative_age(generation8)) { - assert(d > DEPTH_OFFSET); - assert(d < 256 + DEPTH_OFFSET); + assert(d > DEPTH_ENTRY_OFFSET); + assert(d < 256 + DEPTH_ENTRY_OFFSET); key16 = uint16_t(k); - depth8 = uint8_t(d - DEPTH_OFFSET); + depth8 = uint8_t(d - DEPTH_ENTRY_OFFSET); genBound8 = uint8_t(generation8 | uint8_t(pv) << 2 | b); value16 = int16_t(v); eval16 = int16_t(ev); diff --git a/src/tt.h b/src/tt.h index 554a81a572f..7cc876fb9ea 100644 --- a/src/tt.h +++ b/src/tt.h @@ -37,12 +37,15 @@ namespace Stockfish { // move 16 bit // value 16 bit // eval value 16 bit +// +// These fields are in the same order as accessed by TT::probe(), since memory is fastest sequentially. +// Equally, the store order in save() matches this order. 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 + DEPTH_OFFSET); } + Depth depth() const { return Depth(depth8 + DEPTH_ENTRY_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, uint8_t generation8); diff --git a/src/types.h b/src/types.h index 8b0ffb0ca0f..aa4af012b12 100644 --- a/src/types.h +++ b/src/types.h @@ -187,12 +187,17 @@ constexpr Value PieceValue[PIECE_NB] = { using Depth = int; enum : int { - DEPTH_QS_CHECKS = 0, - DEPTH_QS_NO_CHECKS = -1, - - DEPTH_NONE = -6, - - DEPTH_OFFSET = -7 // value used only for TT entry occupancy check + // The following DEPTH_ constants are used for TT entries and QS movegen stages. In regular search, + // TT depth is literal: the search depth (effort) used to make the corresponding TT value. + // In qsearch, however, TT entries only store the current QS movegen stage (which should thus compare + // lower than any regular search depth). + DEPTH_QS_CHECKS = 0, + DEPTH_QS_NORMAL = -1, + // For TT entries where no searching at all was done (whether regular or qsearch) we use + // _UNSEARCHED, which should thus compare lower than any QS or regular depth. _ENTRY_OFFSET is used + // only for the TT entry occupancy check (see tt.cpp), and should thus be lower than _UNSEARCHED. + DEPTH_UNSEARCHED = -6, + DEPTH_ENTRY_OFFSET = -7 }; // clang-format off From 6db47ed71aac3b1667dd68a08c39bfde0fe0a2ab Mon Sep 17 00:00:00 2001 From: Viren6 <94880762+Viren6@users.noreply.github.com> Date: Sun, 19 May 2024 02:58:01 +0100 Subject: [PATCH 1521/1766] Addition of new scaling comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch is intended to prevent patches like 9b90cd8 and the subsequent reversion e3c9ed7 from happening again. Scaling behaviour of the reduction adjustments in the non-linear scaling section have been proven to >8 sigma: STC: https://tests.stockfishchess.org/tests/view/6647b19f6dcff0d1d6b05d52 Elo: 4.28 ± 0.8 (95%) LOS: 100.0% Total: 200000 W: 52555 L: 50094 D: 97351 Ptnml(0-2): 573, 22628, 51248, 24867, 684 nElo: 8.35 ± 1.5 (95%) PairsRatio: 1.10 VLTC: https://tests.stockfishchess.org/tests/view/6647b1b06dcff0d1d6b05d54 Elo: -1.48 ± 1.0 (95%) LOS: 0.2% Total: 100000 W: 25009 L: 25436 D: 49555 Ptnml(0-2): 11, 10716, 28971, 10293, 9 nElo: -3.23 ± 2.2 (95%) PairsRatio: 0.96 The else if condition is moved to the non scaling section based on: https://tests.stockfishchess.org/tests/view/664567a193ce6da3e93b3232 (It has no proven scaling) General comment improvements and removal of a redundant margin condition have also been included. closes https://github.com/official-stockfish/Stockfish/pull/5266 No functional change --- src/search.cpp | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 87cfdbc2b71..0814181864c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -840,7 +840,8 @@ Value Search::Worker::search( if (depth <= 0) return qsearch < PvNode ? PV : NonPV > (pos, ss, alpha, beta); - // For cutNodes without a ttMove, we decrease depth by 2 if depth is high enough. + // For cutNodes, if depth is high enough, decrease depth by 2 if there is no ttMove, or + // by 1 if there is a ttMove with an upper bound. if (cutNode && depth >= 8 && (!ttMove || tte->bound() == BOUND_UPPER)) depth -= 1 + !ttMove; @@ -1042,11 +1043,14 @@ Value Search::Worker::search( // then that move is singular and should be extended. To verify this we do // a reduced search on the position excluding the ttMove and if the result // is lower than ttValue minus a margin, then we will extend the ttMove. + // Recursive singular search is avoided. // Note: the depth margin and singularBeta margin are known for having non-linear // scaling. Their values are optimized to time controls of 180+1.8 and longer // so changing them requires tests at these types of time controls. - // Recursive singular search is avoided. + // Generally, higher singularBeta (i.e closer to ttValue) and lower extension + // margins scale well. + if (!rootNode && move == ttMove && !excludedMove && depth >= 4 - (thisThread->completedDepth > 35) + ss->ttPv && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && (tte->bound() & BOUND_LOWER) @@ -1063,9 +1067,8 @@ Value Search::Worker::search( if (value < singularBeta) { int doubleMargin = 298 * PvNode - 209 * !ttCapture; - int tripleMargin = - 117 + 252 * PvNode - 270 * !ttCapture + 111 * (ss->ttPv || !ttCapture); - int quadMargin = 471 + 343 * PvNode - 281 * !ttCapture + 217 * ss->ttPv; + int tripleMargin = 117 + 252 * PvNode - 270 * !ttCapture + 111 * ss->ttPv; + int quadMargin = 471 + 343 * PvNode - 281 * !ttCapture + 217 * ss->ttPv; extension = 1 + (value < singularBeta - doubleMargin) + (value < singularBeta - tripleMargin) @@ -1127,25 +1130,30 @@ Value Search::Worker::search( thisThread->nodes.fetch_add(1, std::memory_order_relaxed); pos.do_move(move, st, givesCheck); + // These reduction adjustments have proven non-linear scaling. + // They are optimized to time controls of 180 + 1.8 and longer so + // changing them or adding conditions that are similar + // requires tests at these types of time controls. + // Decrease reduction if position is or has been on the PV (~7 Elo) if (ss->ttPv) r -= 1 + (ttValue > alpha) + (tte->depth() >= depth); - else if (cutNode && move != ttMove && move != ss->killers[0]) - r++; + // Decrease reduction for PvNodes (~0 Elo on STC, ~2 Elo on LTC) + if (PvNode) + r--; + + // These reduction adjustments have no proven non-linear scaling. // Increase reduction for cut nodes (~4 Elo) if (cutNode) - r += 2 - (tte->depth() >= depth && ss->ttPv); + r += 2 - (tte->depth() >= depth && ss->ttPv) + + (!ss->ttPv && move != ttMove && move != ss->killers[0]); // Increase reduction if ttMove is a capture (~3 Elo) if (ttCapture) r++; - // Decrease reduction for PvNodes (~0 Elo on STC, ~2 Elo on LTC) - if (PvNode) - r--; - // Increase reduction if next ply has a lot of fail high (~5 Elo) if ((ss + 1)->cutoffCnt > 3) r++; From 1dcffa621065f58982feb462671d79404e51e088 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Tue, 21 May 2024 22:50:44 -0400 Subject: [PATCH 1522/1766] Comment about re-evaluating positions While the smallNet bool is no longer used as of now, setting it to false upon re-evaluation represents the correct eval state. closes https://github.com/official-stockfish/Stockfish/pull/5279 No functional change --- src/evaluate.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index ca09aaf9e82..4c449774838 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -66,6 +66,7 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, Value nnue = smallNet ? networks.small.evaluate(pos, &caches.small, true, &nnueComplexity) : networks.big.evaluate(pos, &caches.big, true, &nnueComplexity); + // Re-evaluate the position when higher eval accuracy is worth the time spent if (smallNet && nnue * simpleEval < 0) { nnue = networks.big.evaluate(pos, &caches.big, true, &nnueComplexity); From c39b98b9e356f6d01d323c6e6d5badd50e31c980 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Tue, 21 May 2024 11:54:53 -0700 Subject: [PATCH 1523/1766] Simplify Away History Updates in Multicut Passed Non-regression STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 44896 W: 11600 L: 11388 D: 21908 Ptnml(0-2): 140, 5230, 11532, 5370, 176 https://tests.stockfishchess.org/tests/view/664cee31830eb9f886616a80 Passed Non-regression LTC: LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 56832 W: 14421 L: 14234 D: 28177 Ptnml(0-2): 37, 6251, 15643, 6458, 27 https://tests.stockfishchess.org/tests/view/664cfd4e830eb9f886616aa6 closes https://github.com/official-stockfish/Stockfish/pull/5281 Bench: 1119412 --- src/search.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 0814181864c..a98468ec644 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1083,12 +1083,7 @@ Value Search::Worker::search( // we assume this expected cut-node is not singular (multiple moves fail high), // and we can prune the whole subtree by returning a softbound. else if (singularBeta >= beta) - { - if (!ttCapture) - update_quiet_histories(pos, ss, *this, ttMove, -stat_malus(depth)); - return singularBeta; - } // Negative extensions // If other moves failed high over (ttValue - margin) without the ttMove on a reduced search, From c6a1e7fd4232ec151206fab16cb7daa23bfd7137 Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Sun, 19 May 2024 13:15:42 +0800 Subject: [PATCH 1524/1766] Optimise pairwise multiplication This speedup was first inspired by a comment by @AndyGrant on my recent PR "If mullo_epi16 would preserve the signedness, then this could be used to remove 50% of the max operations during the halfkp-pairwise mat-mul relu deal." That got me thinking, because although mullo_epi16 did not preserve the signedness, mulhi_epi16 did, and so we could shift left and then use mulhi_epi16, instead of shifting right after the mullo. However, due to some issues with shifting into the sign bit, the FT weights and biases had to be multiplied by 2 for the optimisation to work. Speedup on "Arch=x86-64-bmi2 COMP=clang", courtesy of @Torom Result of 50 runs base (...es/stockfish) = 962946 +/- 1202 test (...ise-max-less) = 979696 +/- 1084 diff = +16750 +/- 1794 speedup = +0.0174 P(speedup > 0) = 1.0000 CPU: 4 x Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz Hyperthreading: on Also a speedup on "COMP=gcc", courtesy of Torom once again Result of 50 runs base (...tockfish_gcc) = 966033 +/- 1574 test (...max-less_gcc) = 983319 +/- 1513 diff = +17286 +/- 2515 speedup = +0.0179 P(speedup > 0) = 1.0000 CPU: 4 x Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz Hyperthreading: on Passed STC: LLR: 2.96 (-2.94,2.94) <0.00,2.00> Total: 67712 W: 17715 L: 17358 D: 32639 Ptnml(0-2): 225, 7472, 18140, 7759, 260 https://tests.stockfishchess.org/tests/view/664c1d75830eb9f886616906 closes https://github.com/official-stockfish/Stockfish/pull/5282 No functional change --- src/nnue/nnue_feature_transformer.h | 80 +++++++++++++++++++---------- 1 file changed, 54 insertions(+), 26 deletions(-) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 7b7aada312c..483b84a8704 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -55,14 +55,14 @@ using psqt_vec_t = __m256i; #define vec_store(a, b) _mm512_store_si512(a, b) #define vec_add_16(a, b) _mm512_add_epi16(a, b) #define vec_sub_16(a, b) _mm512_sub_epi16(a, b) - #define vec_mul_16(a, b) _mm512_mullo_epi16(a, b) + #define vec_mulhi_16(a, b) _mm512_mulhi_epi16(a, b) #define vec_zero() _mm512_setzero_epi32() #define vec_set_16(a) _mm512_set1_epi16(a) #define vec_max_16(a, b) _mm512_max_epi16(a, b) #define vec_min_16(a, b) _mm512_min_epi16(a, b) + #define vec_slli_16(a, b) _mm512_slli_epi16(a, b) // Inverse permuted at load time - #define vec_msb_pack_16(a, b) \ - _mm512_packs_epi16(_mm512_srli_epi16(a, 7), _mm512_srli_epi16(b, 7)) + #define vec_packus_16(a, b) _mm512_packus_epi16(a, b) #define vec_load_psqt(a) _mm256_load_si256(a) #define vec_store_psqt(a, b) _mm256_store_si256(a, b) #define vec_add_psqt_32(a, b) _mm256_add_epi32(a, b) @@ -78,14 +78,14 @@ using psqt_vec_t = __m256i; #define vec_store(a, b) _mm256_store_si256(a, b) #define vec_add_16(a, b) _mm256_add_epi16(a, b) #define vec_sub_16(a, b) _mm256_sub_epi16(a, b) - #define vec_mul_16(a, b) _mm256_mullo_epi16(a, b) + #define vec_mulhi_16(a, b) _mm256_mulhi_epi16(a, b) #define vec_zero() _mm256_setzero_si256() #define vec_set_16(a) _mm256_set1_epi16(a) #define vec_max_16(a, b) _mm256_max_epi16(a, b) #define vec_min_16(a, b) _mm256_min_epi16(a, b) + #define vec_slli_16(a, b) _mm256_slli_epi16(a, b) // Inverse permuted at load time - #define vec_msb_pack_16(a, b) \ - _mm256_packs_epi16(_mm256_srli_epi16(a, 7), _mm256_srli_epi16(b, 7)) + #define vec_packus_16(a, b) _mm256_packus_epi16(a, b) #define vec_load_psqt(a) _mm256_load_si256(a) #define vec_store_psqt(a, b) _mm256_store_si256(a, b) #define vec_add_psqt_32(a, b) _mm256_add_epi32(a, b) @@ -101,12 +101,13 @@ using psqt_vec_t = __m128i; #define vec_store(a, b) *(a) = (b) #define vec_add_16(a, b) _mm_add_epi16(a, b) #define vec_sub_16(a, b) _mm_sub_epi16(a, b) - #define vec_mul_16(a, b) _mm_mullo_epi16(a, b) + #define vec_mulhi_16(a, b) _mm_mulhi_epi16(a, b) #define vec_zero() _mm_setzero_si128() #define vec_set_16(a) _mm_set1_epi16(a) #define vec_max_16(a, b) _mm_max_epi16(a, b) #define vec_min_16(a, b) _mm_min_epi16(a, b) - #define vec_msb_pack_16(a, b) _mm_packs_epi16(_mm_srli_epi16(a, 7), _mm_srli_epi16(b, 7)) + #define vec_slli_16(a, b) _mm_slli_epi16(a, b) + #define vec_packus_16(a, b) _mm_packus_epi16(a, b) #define vec_load_psqt(a) (*(a)) #define vec_store_psqt(a, b) *(a) = (b) #define vec_add_psqt_32(a, b) _mm_add_epi32(a, b) @@ -122,18 +123,14 @@ using psqt_vec_t = int32x4_t; #define vec_store(a, b) *(a) = (b) #define vec_add_16(a, b) vaddq_s16(a, b) #define vec_sub_16(a, b) vsubq_s16(a, b) - #define vec_mul_16(a, b) vmulq_s16(a, b) + #define vec_mulhi_16(a, b) vqdmulhq_s16(a, b) #define vec_zero() \ vec_t { 0 } #define vec_set_16(a) vdupq_n_s16(a) #define vec_max_16(a, b) vmaxq_s16(a, b) #define vec_min_16(a, b) vminq_s16(a, b) -inline vec_t vec_msb_pack_16(vec_t a, vec_t b) { - const int8x8_t shifta = vshrn_n_s16(a, 7); - const int8x8_t shiftb = vshrn_n_s16(b, 7); - const int8x16_t compacted = vcombine_s8(shifta, shiftb); - return *reinterpret_cast(&compacted); -} + #define vec_slli_16(a, b) vshlq_s16(a, vec_set_16(b)) + #define vec_packus_16(a, b) reinterpret_cast(vcombine_u8(vqmovun_s16(a), vqmovun_s16(b))) #define vec_load_psqt(a) (*(a)) #define vec_store_psqt(a, b) *(a) = (b) #define vec_add_psqt_32(a, b) vaddq_s32(a, b) @@ -281,6 +278,19 @@ class FeatureTransformer { #endif } + inline void scale_weights(bool read) const { + for (IndexType j = 0; j < InputDimensions; ++j) + { + WeightType* w = const_cast(&weights[j * HalfDimensions]); + for (IndexType i = 0; i < HalfDimensions; ++i) + w[i] = read ? w[i] * 2 : w[i] / 2; + } + + BiasType* b = const_cast(biases); + for (IndexType i = 0; i < HalfDimensions; ++i) + b[i] = read ? b[i] * 2 : b[i] / 2; + } + // Read network parameters bool read_parameters(std::istream& stream) { @@ -289,6 +299,7 @@ class FeatureTransformer { read_leb_128(stream, psqtWeights, PSQTBuckets * InputDimensions); permute_weights(inverse_order_packs); + scale_weights(true); return !stream.fail(); } @@ -296,12 +307,14 @@ class FeatureTransformer { bool write_parameters(std::ostream& stream) const { permute_weights(order_packs); + scale_weights(false); write_leb_128(stream, biases, HalfDimensions); write_leb_128(stream, weights, HalfDimensions * InputDimensions); write_leb_128(stream, psqtWeights, PSQTBuckets * InputDimensions); permute_weights(inverse_order_packs); + scale_weights(true); return !stream.fail(); } @@ -332,7 +345,7 @@ class FeatureTransformer { constexpr IndexType NumOutputChunks = HalfDimensions / 2 / OutputChunkSize; const vec_t Zero = vec_zero(); - const vec_t One = vec_set_16(127); + const vec_t One = vec_set_16(127 * 2); const vec_t* in0 = reinterpret_cast(&(accumulation[perspectives[p]][0])); const vec_t* in1 = @@ -341,15 +354,30 @@ class FeatureTransformer { for (IndexType j = 0; j < NumOutputChunks; ++j) { - const vec_t sum0a = vec_max_16(vec_min_16(in0[j * 2 + 0], One), Zero); - const vec_t sum0b = vec_max_16(vec_min_16(in0[j * 2 + 1], One), Zero); - const vec_t sum1a = vec_max_16(vec_min_16(in1[j * 2 + 0], One), Zero); - const vec_t sum1b = vec_max_16(vec_min_16(in1[j * 2 + 1], One), Zero); + // What we want to do is multiply inputs in a pairwise manner (after clipping), and then shift right by 9. + // Instead, we shift left by 7, and use mulhi, stripping the bottom 16 bits, effectively shifting right by 16, + // resulting in a net shift of 9 bits. We use mulhi because it maintains the sign of the multiplication (unlike mullo), + // allowing us to make use of packus to clip 2 of the inputs, resulting in a save of 2 "vec_max_16" calls. + // A special case is when we use NEON, where we shift left by 6 instead, because the instruction "vqdmulhq_s16" + // also doubles the return value after the multiplication, adding an extra shift to the left by 1, so we + // compensate by shifting less before the multiplication. + + #if defined(USE_SSE2) + constexpr int shift = 7; + #else + constexpr int shift = 6; + #endif + const vec_t sum0a = + vec_slli_16(vec_max_16(vec_min_16(in0[j * 2 + 0], One), Zero), shift); + const vec_t sum0b = + vec_slli_16(vec_max_16(vec_min_16(in0[j * 2 + 1], One), Zero), shift); + const vec_t sum1a = vec_min_16(in1[j * 2 + 0], One); + const vec_t sum1b = vec_min_16(in1[j * 2 + 1], One); - const vec_t pa = vec_mul_16(sum0a, sum1a); - const vec_t pb = vec_mul_16(sum0b, sum1b); + const vec_t pa = vec_mulhi_16(sum0a, sum1a); + const vec_t pb = vec_mulhi_16(sum0b, sum1b); - out[j] = vec_msb_pack_16(pa, pb); + out[j] = vec_packus_16(pa, pb); } #else @@ -359,9 +387,9 @@ class FeatureTransformer { BiasType sum0 = accumulation[static_cast(perspectives[p])][j + 0]; BiasType sum1 = accumulation[static_cast(perspectives[p])][j + HalfDimensions / 2]; - sum0 = std::clamp(sum0, 0, 127); - sum1 = std::clamp(sum1, 0, 127); - output[offset + j] = static_cast(unsigned(sum0 * sum1) / 128); + sum0 = std::clamp(sum0, 0, 127 * 2); + sum1 = std::clamp(sum1, 0, 127 * 2); + output[offset + j] = static_cast(unsigned(sum0 * sum1) / 512); } #endif From 72a345873d9cf24542dc73cd5a28eba7d23b0d2b Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Wed, 22 May 2024 09:09:04 +0800 Subject: [PATCH 1525/1766] Revert "Reduce When TTValue is Above Alpha" The patch regressed significantly at longer time controls. In particular, the `depth--` behavior was predicted to scale badly based on data from other variations of the patch. Passed VVLTC 1st sprt: https://tests.stockfishchess.org/tests/view/664d45cf830eb9f886616c7d LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 51292 W: 13242 L: 12954 D: 25096 Ptnml(0-2): 5, 4724, 15896, 5020, 1 Passed VVLTC 2nd sprt: https://tests.stockfishchess.org/tests/view/664e641a928b1fb18de4e385 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 41884 W: 10933 L: 10634 D: 20317 Ptnml(0-2): 1, 3759, 13125, 4054, 3 closes https://github.com/official-stockfish/Stockfish/pull/5283 Bench: 1503815 --- src/search.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index a98468ec644..477667306ad 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -833,12 +833,9 @@ Value Search::Worker::search( if (PvNode && !ttMove) depth -= 3; - if (!PvNode && ss->ttHit && (tte->bound() & BOUND_UPPER) && ttValue > alpha + 5 * depth) - depth--; - // Use qsearch if depth <= 0. if (depth <= 0) - return qsearch < PvNode ? PV : NonPV > (pos, ss, alpha, beta); + return qsearch(pos, ss, alpha, beta); // For cutNodes, if depth is high enough, decrease depth by 2 if there is no ttMove, or // by 1 if there is a ttMove with an upper bound. From 365aa85dcea3adee21b5e01a7941b4b18fdc8194 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Tue, 21 May 2024 16:24:49 -0400 Subject: [PATCH 1526/1766] Remove material imbalance param when adjusting optimism Passed non-regression STC: https://tests.stockfishchess.org/tests/view/664d033d830eb9f886616aff LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 102144 W: 26283 L: 26135 D: 49726 Ptnml(0-2): 292, 12201, 25991, 12243, 345 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/664d5c00830eb9f886616cb3 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 250032 W: 63022 L: 63036 D: 123974 Ptnml(0-2): 103, 27941, 68970, 27871, 131 closes https://github.com/official-stockfish/Stockfish/pull/5284 Bench: 1330940 --- src/evaluate.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 4c449774838..7ca470af5ea 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -73,8 +73,8 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, smallNet = false; } - // Blend optimism and eval with nnue complexity and material imbalance - optimism += optimism * (nnueComplexity + std::abs(simpleEval - nnue)) / 620; + // Blend optimism and eval with nnue complexity + optimism += optimism * nnueComplexity / 512; nnue -= nnue * (nnueComplexity * 5 / 3) / 32082; v = (nnue From 61acbfc7d310ed6044ba4fc5ef91a6c382d1c9a6 Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Thu, 23 May 2024 08:28:46 +0800 Subject: [PATCH 1527/1766] VVLTC search tune Parameters were tuned in 2 stages: 1. 127k games at VVLTC: https://tests.stockfishchess.org/tests/view/6649f8dfb8fa20e74c39f52a. 2. 106k games at VVLTC: https://tests.stockfishchess.org/tests/view/664bfb77830eb9f886615a9d. Passed VVLTC 1st sprt: https://tests.stockfishchess.org/tests/view/664e8dd9928b1fb18de4e410 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 20466 W: 5340 L: 5093 D: 10033 Ptnml(0-2): 0, 1796, 6397, 2037, 3 Passed VVLTC 2nd sprt: https://tests.stockfishchess.org/tests/view/664eb4aa928b1fb18de4e47d LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 15854 W: 4186 L: 3934 D: 7734 Ptnml(0-2): 1, 1367, 4938, 1621, 0 closes https://github.com/official-stockfish/Stockfish/pull/5286 Bench: 1558110 --- src/search.cpp | 88 +++++++++++++++++++++++++------------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 477667306ad..563a5710f89 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -60,9 +60,9 @@ static constexpr double EvalLevel[10] = {0.981, 0.956, 0.895, 0.949, 0.913, // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) { - Value futilityMult = 127 - 48 * noTtCutNode; - Value improvingDeduction = 65 * improving * futilityMult / 32; - Value worseningDeduction = 334 * oppWorsening * futilityMult / 1024; + Value futilityMult = 129 - 43 * noTtCutNode; + Value improvingDeduction = 56 * improving * futilityMult / 32; + Value worseningDeduction = 336 * oppWorsening * futilityMult / 1024; return futilityMult * d - improvingDeduction - worseningDeduction; } @@ -74,15 +74,15 @@ constexpr int futility_move_count(bool improving, Depth depth) { // Add correctionHistory value to raw staticEval and guarantee evaluation does not hit the tablebase range Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { auto cv = w.correctionHistory[pos.side_to_move()][pawn_structure_index(pos)]; - v += cv * std::abs(cv) / 6047; + v += cv * std::abs(cv) / 5435; return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } // History and stats update bonus, based on depth -int stat_bonus(Depth d) { return std::clamp(187 * d - 288, 17, 1548); } +int stat_bonus(Depth d) { return std::clamp(205 * d - 283, 18, 1544); } // History and stats update malus, based on depth -int stat_malus(Depth d) { return (d < 4 ? 630 * d - 281 : 1741); } +int stat_malus(Depth d) { return (d < 4 ? 767 * d - 275 : 1911); } // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(size_t nodes) { return VALUE_DRAW - 1 + Value(nodes & 0x2); } @@ -312,12 +312,12 @@ void Search::Worker::iterative_deepening() { // Reset aspiration window starting size Value avg = rootMoves[pvIdx].averageScore; - delta = 10 + avg * avg / 9828; + delta = 9 + avg * avg / 10502; alpha = std::max(avg - delta, -VALUE_INFINITE); beta = std::min(avg + delta, VALUE_INFINITE); // Adjust optimism based on root move's averageScore (~4 Elo) - optimism[us] = 116 * avg / (std::abs(avg) + 84); + optimism[us] = 122 * avg / (std::abs(avg) + 92); optimism[~us] = -optimism[us]; // Start with a small aspiration window and, in the case of a fail @@ -507,7 +507,7 @@ void Search::Worker::clear() { h->fill(-60); for (size_t i = 1; i < reductions.size(); ++i) - reductions[i] = int((21.69 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); + reductions[i] = int((19.90 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); refreshTable.clear(networks); } @@ -740,7 +740,7 @@ Value Search::Worker::search( // Use static evaluation difference to improve quiet move ordering (~9 Elo) if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture) { - int bonus = std::clamp(-11 * int((ss - 1)->staticEval + ss->staticEval), -1729, 1517); + int bonus = std::clamp(-11 * int((ss - 1)->staticEval + ss->staticEval), -1592, 1390); bonus = bonus > 0 ? 2 * bonus : bonus / 2; thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) @@ -762,7 +762,7 @@ Value Search::Worker::search( // Step 7. Razoring (~1 Elo) // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. - if (eval < alpha - 474 - 324 * depth * depth) + if (eval < alpha - 501 - 305 * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -771,23 +771,23 @@ Value Search::Worker::search( // Step 8. Futility pruning: child node (~40 Elo) // The depth condition is important for mate finding. - if (!ss->ttPv && depth < 11 + if (!ss->ttPv && depth < 12 && eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening) - - (ss - 1)->statScore / 252 + - (ss - 1)->statScore / 248 >= beta && eval >= beta && eval < VALUE_TB_WIN_IN_MAX_PLY && (!ttMove || ttCapture)) return beta > VALUE_TB_LOSS_IN_MAX_PLY ? (eval + beta) / 2 : eval; // Step 9. Null move search with verification search (~35 Elo) - if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 15246 - && eval >= beta && ss->staticEval >= beta - 21 * depth + 366 && !excludedMove + if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 13999 + && eval >= beta && ss->staticEval >= beta - 21 * depth + 390 && !excludedMove && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly && beta > VALUE_TB_LOSS_IN_MAX_PLY) { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and eval - Depth R = std::min(int(eval - beta) / 152, 6) + depth / 3 + 5; + Depth R = std::min(int(eval - beta) / 177, 6) + depth / 3 + 5; ss->currentMove = Move::null(); ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -845,7 +845,7 @@ Value Search::Worker::search( // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search returns a value // much above beta, we can (almost) safely prune the previous move. - probCutBeta = beta + 176 - 65 * improving; + probCutBeta = beta + 185 - 60 * improving; if ( !PvNode && depth > 3 && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY @@ -901,7 +901,7 @@ Value Search::Worker::search( moves_loop: // When in check, search starts here // Step 12. A small Probcut idea, when we are in check (~4 Elo) - probCutBeta = beta + 440; + probCutBeta = beta + 361; if (ss->inCheck && !PvNode && ttCapture && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 4 && ttValue >= probCutBeta && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) @@ -985,15 +985,15 @@ Value Search::Worker::search( // Futility pruning for captures (~2 Elo) if (!givesCheck && lmrDepth < 7 && !ss->inCheck) { - Value futilityValue = ss->staticEval + 276 + 256 * lmrDepth + Value futilityValue = ss->staticEval + 283 + 235 * lmrDepth + PieceValue[capturedPiece] + captHist / 7; if (futilityValue <= alpha) continue; } // SEE based pruning for captures and checks (~11 Elo) - int seeHist = std::clamp(captHist / 32, -177 * depth, 175 * depth); - if (!pos.see_ge(move, -183 * depth - seeHist)) + int seeHist = std::clamp(captHist / 32, -183 * depth, 162 * depth); + if (!pos.see_ge(move, -166 * depth - seeHist)) continue; } else @@ -1004,18 +1004,18 @@ Value Search::Worker::search( + thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()]; // Continuation history based pruning (~2 Elo) - if (lmrDepth < 6 && history < -4076 * depth) + if (lmrDepth < 6 && history < -4427 * depth) continue; history += 2 * thisThread->mainHistory[us][move.from_to()]; - lmrDepth += history / 4401; + lmrDepth += history / 3670; Value futilityValue = - ss->staticEval + (bestValue < ss->staticEval - 53 ? 151 : 57) + 140 * lmrDepth; + ss->staticEval + (bestValue < ss->staticEval - 51 ? 149 : 55) + 141 * lmrDepth; // Futility pruning: parent node (~13 Elo) - if (!ss->inCheck && lmrDepth < 10 && futilityValue <= alpha) + if (!ss->inCheck && lmrDepth < 11 && futilityValue <= alpha) { if (bestValue <= futilityValue && std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY && futilityValue < VALUE_TB_WIN_IN_MAX_PLY) @@ -1049,11 +1049,11 @@ Value Search::Worker::search( // margins scale well. if (!rootNode && move == ttMove && !excludedMove - && depth >= 4 - (thisThread->completedDepth > 35) + ss->ttPv + && depth >= 4 - (thisThread->completedDepth > 38) + ss->ttPv && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - (57 + 50 * (ss->ttPv && !PvNode)) * depth / 64; + Value singularBeta = ttValue - (58 + 64 * (ss->ttPv && !PvNode)) * depth / 64; Depth singularDepth = newDepth / 2; ss->excludedMove = move; @@ -1063,15 +1063,15 @@ Value Search::Worker::search( if (value < singularBeta) { - int doubleMargin = 298 * PvNode - 209 * !ttCapture; - int tripleMargin = 117 + 252 * PvNode - 270 * !ttCapture + 111 * ss->ttPv; - int quadMargin = 471 + 343 * PvNode - 281 * !ttCapture + 217 * ss->ttPv; + int doubleMargin = 304 * PvNode - 203 * !ttCapture; + int tripleMargin = 117 + 259 * PvNode - 296 * !ttCapture + 97 * ss->ttPv; + int quadMargin = 486 + 343 * PvNode - 273 * !ttCapture + 232 * ss->ttPv; extension = 1 + (value < singularBeta - doubleMargin) + (value < singularBeta - tripleMargin) + (value < singularBeta - quadMargin); - depth += ((!PvNode) && (depth < 15)); + depth += ((!PvNode) && (depth < 16)); } // Multi-cut pruning @@ -1101,7 +1101,7 @@ Value Search::Worker::search( else if (PvNode && move == ttMove && move.to_sq() == prevSq && thisThread->captureHistory[movedPiece][move.to_sq()] [type_of(pos.piece_on(move.to_sq()))] - > 3748) + > 3988) extension = 1; } @@ -1157,10 +1157,10 @@ Value Search::Worker::search( ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + (*contHist[0])[movedPiece][move.to_sq()] - + (*contHist[1])[movedPiece][move.to_sq()] - 5266; + + (*contHist[1])[movedPiece][move.to_sq()] - 5169; // Decrease/increase reduction for moves with a good/bad history (~8 Elo) - r -= ss->statScore / (14519 - std::min(depth, 15) * 103); + r -= ss->statScore / (12219 - std::min(depth, 13) * 120); // Step 17. Late moves reduction / extension (LMR, ~117 Elo) if (depth >= 2 && moveCount > 1 + rootNode) @@ -1179,7 +1179,7 @@ Value Search::Worker::search( { // Adjust full-depth search based on LMR results - if the result // was good enough search deeper, if it was bad enough search shallower. - const bool doDeeperSearch = value > (bestValue + 40 + 2 * newDepth); // (~1 Elo) + const bool doDeeperSearch = value > (bestValue + 36 + 2 * newDepth); // (~1 Elo) const bool doShallowerSearch = value < bestValue + newDepth; // (~2 Elo) newDepth += doDeeperSearch - doShallowerSearch; @@ -1340,9 +1340,9 @@ Value Search::Worker::search( // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (depth > 4) + (depth > 5) + (PvNode || cutNode) + ((ss - 1)->statScore < -13241) - + ((ss - 1)->moveCount > 10) + (!ss->inCheck && bestValue <= ss->staticEval - 127) - + (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 74); + int bonus = (depth > 4) + (depth > 5) + (PvNode || cutNode) + ((ss - 1)->statScore < -14144) + + ((ss - 1)->moveCount > 9) + (!ss->inCheck && bestValue <= ss->staticEval - 115) + + (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 81); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus); thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] @@ -1513,7 +1513,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, if (bestValue > alpha) alpha = bestValue; - futilityBase = ss->staticEval + 264; + futilityBase = ss->staticEval + 279; } const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, @@ -1585,11 +1585,11 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, + (*contHist[1])[pos.moved_piece(move)][move.to_sq()] + thisThread->pawnHistory[pawn_structure_index(pos)][pos.moved_piece(move)] [move.to_sq()] - <= 4348) + <= 4181) continue; // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, -63)) + if (!pos.see_ge(move, -67)) continue; } @@ -1655,7 +1655,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth Search::Worker::reduction(bool i, Depth d, int mn, int delta) { int reductionScale = reductions[d] * reductions[mn]; - return (reductionScale + 1147 - delta * 755 / rootDelta) / 1024 + (!i && reductionScale > 1125); + return (reductionScale + 1222 - delta * 733 / rootDelta) / 1024 + (!i && reductionScale > 1231); } // elapsed() returns the time elapsed since the search started. If the @@ -1758,7 +1758,7 @@ void update_all_stats(const Position& pos, if (!pos.capture_stage(bestMove)) { - int bestMoveBonus = bestValue > beta + 165 ? quietMoveBonus // larger bonus + int bestMoveBonus = bestValue > beta + 176 ? quietMoveBonus // larger bonus : stat_bonus(depth); // smaller bonus update_quiet_stats(pos, ss, workerThread, bestMove, bestMoveBonus); @@ -1796,7 +1796,7 @@ void update_all_stats(const Position& pos, // by moves at ply -1, -2, -3, -4, and -6 with current move. void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { - bonus = bonus * 45 / 64; + bonus = bonus * 47 / 64; for (int i : {1, 2, 3, 4, 6}) { From 4d876275cf127b9e7cf91cef984deafa2abb47d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Thu, 23 May 2024 22:03:43 +0200 Subject: [PATCH 1528/1766] Simplify material weights in evaluation This patch uses the same material weights for the nnue amplification term and the optimism term in evaluate(). STC: LLR: 2.99 (-2.94,2.94) <-1.75,0.25> Total: 83360 W: 21489 L: 21313 D: 40558 Ptnml(0-2): 303, 9934, 21056, 10058, 329 https://tests.stockfishchess.org/tests/view/664eee69928b1fb18de500d9 LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 192648 W: 48675 L: 48630 D: 95343 Ptnml(0-2): 82, 21484, 53161, 21501, 96 https://tests.stockfishchess.org/tests/view/664fa17aa86388d5e27d7d6e closes https://github.com/official-stockfish/Stockfish/pull/5287 Bench: 1495602 --- src/evaluate.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 7ca470af5ea..75fe0f924a2 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -77,13 +77,10 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, optimism += optimism * nnueComplexity / 512; nnue -= nnue * (nnueComplexity * 5 / 3) / 32082; - v = (nnue - * (32961 + 381 * pos.count() + 349 * pos.count() - + 392 * pos.count() + 649 * pos.count() + 1211 * pos.count()) - + optimism - * (4835 + 136 * pos.count() + 375 * pos.count() - + 403 * pos.count() + 628 * pos.count() + 1124 * pos.count())) - / 36860; + int material = 200 * pos.count() + 350 * pos.count() + 400 * pos.count() + + 640 * pos.count() + 1200 * pos.count(); + + v = (nnue * (34000 + material) + optimism * (4400 + material)) / 36860; // Damp down the evaluation linearly when shuffling v = v * (204 - pos.rule50_count()) / 208; From 8bc3fd3871aaa2437105bdc141d5ac25a88ea885 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Fri, 24 May 2024 10:58:13 -0400 Subject: [PATCH 1529/1766] Lower smallnet threshold with tuned eval params The smallnet threshold is now below the training data range of the current smallnet (simple eval diff > 1k, nn-baff1edelf90.nnue) when no pawns are on the board. Params found with spsa at 93k / 120k games at 60+06: https://tests.stockfishchess.org/tests/view/664fa166a86388d5e27d7d6b Tuned on top of: https://github.com/official-stockfish/Stockfish/pull/5287 Passed STC: https://tests.stockfishchess.org/tests/view/664fc8b7a86388d5e27d8dac LLR: 2.96 (-2.94,2.94) <0.00,2.00> Total: 64672 W: 16731 L: 16371 D: 31570 Ptnml(0-2): 239, 7463, 16517, 7933, 184 Passed LTC: https://tests.stockfishchess.org/tests/view/664fd5f9a86388d5e27d8dfe LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 210648 W: 53489 L: 52813 D: 104346 Ptnml(0-2): 102, 23129, 58164, 23849, 80 closes https://github.com/official-stockfish/Stockfish/pull/5288 Bench: 1717838 --- src/evaluate.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 75fe0f924a2..13a3f211741 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -46,7 +46,7 @@ int Eval::simple_eval(const Position& pos, Color c) { bool Eval::use_smallnet(const Position& pos) { int simpleEval = simple_eval(pos, pos.side_to_move()); - return std::abs(simpleEval) > 1018 + 5 * pos.count(); + return std::abs(simpleEval) > 992 + 6 * pos.count(); } // Evaluate is the evaluator for the outer world. It returns a static evaluation @@ -74,13 +74,15 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, } // Blend optimism and eval with nnue complexity - optimism += optimism * nnueComplexity / 512; - nnue -= nnue * (nnueComplexity * 5 / 3) / 32082; + optimism += optimism * nnueComplexity / 470; + nnue -= nnue * (nnueComplexity * 5 / 3) / 32621; int material = 200 * pos.count() + 350 * pos.count() + 400 * pos.count() + 640 * pos.count() + 1200 * pos.count(); - v = (nnue * (34000 + material) + optimism * (4400 + material)) / 36860; + v = (nnue * (34000 + material + 135 * pos.count()) + + optimism * (4400 + material + 99 * pos.count())) + / 35967; // Damp down the evaluation linearly when shuffling v = v * (204 - pos.rule50_count()) / 208; From 8e1f273c7d10e2b49c07cdc16b09a3d4574acf4c Mon Sep 17 00:00:00 2001 From: "Shahin M. Shahin" <41402573+peregrineshahin@users.noreply.github.com> Date: Fri, 24 May 2024 01:19:16 +0300 Subject: [PATCH 1530/1766] Remove rootDelta branch This makes rootDelta logic easier to understand, recalculating the value where it belongs so removes an unnecessary branch. Passed non-regression STC: https://tests.stockfishchess.org/tests/view/664fc147a86388d5e27d8d8e LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 206016 W: 53120 L: 53089 D: 99807 Ptnml(0-2): 591, 20928, 59888, 21061, 540 closes https://github.com/official-stockfish/Stockfish/pull/5289 No functional change --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 563a5710f89..ed264f55c42 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -330,6 +330,7 @@ void Search::Worker::iterative_deepening() { // for every four searchAgain steps (see issue #2717). Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt - 3 * (searchAgainCounter + 1) / 4); + rootDelta = beta - alpha; bestValue = search(rootPos, ss, alpha, beta, adjustedDepth, false); // Bring the best move to the front. It is critical that sorting @@ -590,8 +591,6 @@ Value Search::Worker::search( if (alpha >= beta) return alpha; } - else - thisThread->rootDelta = beta - alpha; assert(0 <= ss->ply && ss->ply < MAX_PLY); From 5e98a4e43dd1c2698162bc3f848a0a98943f86c6 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Fri, 24 May 2024 22:46:03 -0700 Subject: [PATCH 1531/1766] Simplify Away TT Cutoff Return Value Adjustments Passed Non-regression STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 198432 W: 51161 L: 51119 D: 96152 Ptnml(0-2): 772, 23670, 50273, 23746, 755 https://tests.stockfishchess.org/tests/view/66517b9ea86388d5e27da966 Passed Non-regression LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 234150 W: 59200 L: 59197 D: 115753 Ptnml(0-2): 126, 26200, 64404, 26235, 110 https://tests.stockfishchess.org/tests/view/6653a84da86388d5e27daa63 closes https://github.com/official-stockfish/Stockfish/pull/5292 bench 1555200 --- src/search.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ed264f55c42..d253601dd61 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -637,9 +637,7 @@ Value Search::Worker::search( // Partial workaround for the graph history interaction problem // For high rule50 counts don't produce transposition table cutoffs. if (pos.rule50_count() < 90) - return ttValue >= beta && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY - ? (ttValue * 3 + beta) / 4 - : ttValue; + return ttValue; } // Step 5. Tablebases probe From d0b9411b8275369074bb0de041257db2bccc6430 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Tue, 28 May 2024 13:49:30 +0300 Subject: [PATCH 1532/1766] Tweak return value in futility pruning Tweak the return value formula in futility pruning. Passed STC: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 60544 W: 15791 L: 15440 D: 29313 Ptnml(0-2): 193, 7024, 15520, 7309, 226 https://tests.stockfishchess.org/tests/view/6654ef22a86388d5e27db122 Passed LTC: LLR: 2.96 (-2.94,2.94) <0.50,2.50> Total: 126426 W: 32317 L: 31812 D: 62297 Ptnml(0-2): 55, 13871, 34869, 14350, 68 https://tests.stockfishchess.org/tests/view/66550644a86388d5e27db649 closes https://github.com/official-stockfish/Stockfish/pull/5295 bench: 1856147 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index d253601dd61..0dbc6a3a5db 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -773,7 +773,7 @@ Value Search::Worker::search( - (ss - 1)->statScore / 248 >= beta && eval >= beta && eval < VALUE_TB_WIN_IN_MAX_PLY && (!ttMove || ttCapture)) - return beta > VALUE_TB_LOSS_IN_MAX_PLY ? (eval + beta) / 2 : eval; + return beta > VALUE_TB_LOSS_IN_MAX_PLY ? beta + (eval - beta) / 3 : eval; // Step 9. Null move search with verification search (~35 Elo) if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 13999 From b0287dcb1c436887075962b596cf2068d2ca9ba8 Mon Sep 17 00:00:00 2001 From: Disservin Date: Tue, 28 May 2024 18:00:22 +0200 Subject: [PATCH 1533/1766] apply const to prefetch parameter closes https://github.com/official-stockfish/Stockfish/pull/5296 No functional change --- src/misc.cpp | 6 +++--- src/misc.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index 1abb81b14c2..58f804204b2 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -415,14 +415,14 @@ void start_logger(const std::string& fname) { Logger::start(fname); } #ifdef NO_PREFETCH -void prefetch(void*) {} +void prefetch(const void*) {} #else -void prefetch(void* addr) { +void prefetch(const void* addr) { #if defined(_MSC_VER) - _mm_prefetch((char*) addr, _MM_HINT_T0); + _mm_prefetch((char const*) addr, _MM_HINT_T0); #else __builtin_prefetch(addr); #endif diff --git a/src/misc.h b/src/misc.h index d75b236ff71..3a905dfab49 100644 --- a/src/misc.h +++ b/src/misc.h @@ -40,7 +40,7 @@ std::string compiler_info(); // Preloads the given address in L1/L2 cache. This is a non-blocking // function that doesn't stall the CPU waiting for data to be loaded from memory, // which can be quite slow. -void prefetch(void* addr); +void prefetch(const void* addr); void start_logger(const std::string& fname); void* std_aligned_alloc(size_t alignment, size_t size); From a169c78b6d3b082068deb49a39aaa1fd75464c7f Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Fri, 17 May 2024 12:10:31 +0200 Subject: [PATCH 1534/1766] Improve performance on NUMA systems Allow for NUMA memory replication for NNUE weights. Bind threads to ensure execution on a specific NUMA node. This patch introduces NUMA memory replication, currently only utilized for the NNUE weights. Along with it comes all machinery required to identify NUMA nodes and bind threads to specific processors/nodes. It also comes with small changes to Thread and ThreadPool to allow easier execution of custom functions on the designated thread. Old thread binding (WinProcGroup) machinery is removed because it's incompatible with this patch. Small changes to unrelated parts of the code were made to ensure correctness, like some classes being made unmovable, raw pointers replaced with unique_ptr. etc. Windows 7 and Windows 10 is partially supported. Windows 11 is fully supported. Linux is fully supported, with explicit exclusion of Android. No additional dependencies. ----------------- A new UCI option `NumaPolicy` is introduced. It can take the following values: ``` system - gathers NUMA node information from the system (lscpu or windows api), for each threads binds it to a single NUMA node none - assumes there is 1 NUMA node, never binds threads auto - this is the default value, depends on the number of set threads and NUMA nodes, will only enable binding on multinode systems and when the number of threads reaches a threshold (dependent on node size and count) [[custom]] - // ':'-separated numa nodes // ','-separated cpu indices // supports "first-last" range syntax for cpu indices, for example '0-15,32-47:16-31,48-63' ``` Setting `NumaPolicy` forces recreation of the threads in the ThreadPool, which in turn forces the recreation of the TT. The threads are distributed among NUMA nodes in a round-robin fashion based on fill percentage (i.e. it will strive to fill all NUMA nodes evenly). Threads are bound to NUMA nodes, not specific processors, because that's our only requirement and the OS can schedule them better. Special care is made that maximum memory usage on systems that do not require memory replication stays as previously, that is, unnecessary copies are avoided. On linux the process' processor affinity is respected. This means that if you for example use taskset to restrict Stockfish to a single NUMA node then the `system` and `auto` settings will only see a single NUMA node (more precisely, the processors included in the current affinity mask) and act accordingly. ----------------- We can't ensure that a memory allocation takes place on a given NUMA node without using libnuma on linux, or using appropriate custom allocators on windows (https://learn.microsoft.com/en-us/windows/win32/memory/allocating-memory-from-a-numa-node), so to avoid complications the current implementation relies on first-touch policy. Due to this we also rely on the memory allocator to give us a new chunk of untouched memory from the system. This appears to work reliably on linux, but results may vary. MacOS is not supported, because AFAIK it's not affected, and implementation would be problematic anyway. Windows is supported since Windows 7 (https://learn.microsoft.com/en-us/windows/win32/api/processtopologyapi/nf-processtopologyapi-setthreadgroupaffinity). Until Windows 11/Server 2022 NUMA nodes are split such that they cannot span processor groups. This is because before Windows 11/Server 2022 it's not possible to set thread affinity spanning processor groups. The splitting is done manually in some cases (required after Windows 10 Build 20348). Since Windows 11/Server 2022 we can set affinites spanning processor group so this splitting is not done, so the behaviour is pretty much like on linux. Linux is supported, **without** libnuma requirement. `lscpu` is expected. ----------------- Passed 60+1 @ 256t 16000MB hash: https://tests.stockfishchess.org/tests/view/6654e443a86388d5e27db0d8 ``` LLR: 2.95 (-2.94,2.94) <0.00,10.00> Total: 278 W: 110 L: 29 D: 139 Ptnml(0-2): 0, 1, 56, 82, 0 ``` Passed SMP STC: https://tests.stockfishchess.org/tests/view/6654fc74a86388d5e27db1cd ``` LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 67152 W: 17354 L: 17177 D: 32621 Ptnml(0-2): 64, 7428, 18408, 7619, 57 ``` Passed STC: https://tests.stockfishchess.org/tests/view/6654fb27a86388d5e27db15c ``` LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 131648 W: 34155 L: 34045 D: 63448 Ptnml(0-2): 426, 13878, 37096, 14008, 416 ``` fixes #5253 closes https://github.com/official-stockfish/Stockfish/pull/5285 No functional change --- .github/ci/libcxx17.imp | 1 + src/Makefile | 2 +- src/engine.cpp | 88 +++- src/engine.h | 31 +- src/misc.cpp | 134 +----- src/misc.h | 56 ++- src/nnue/network.cpp | 42 ++ src/nnue/network.h | 6 + src/numa.h | 904 ++++++++++++++++++++++++++++++++++++++++ src/search.cpp | 41 +- src/search.h | 37 +- src/thread.cpp | 192 ++++++--- src/thread.h | 91 +++- src/tt.cpp | 29 +- src/tt.h | 5 +- src/uci.cpp | 42 +- src/uci.h | 3 + src/ucioption.cpp | 2 + src/ucioption.h | 1 + 19 files changed, 1418 insertions(+), 289 deletions(-) create mode 100644 src/numa.h diff --git a/.github/ci/libcxx17.imp b/.github/ci/libcxx17.imp index 7bdcf5bc2de..d3a262b54e8 100644 --- a/.github/ci/libcxx17.imp +++ b/.github/ci/libcxx17.imp @@ -7,6 +7,7 @@ { include: [ "<__fwd/sstream.h>", private, "", public ] }, { include: [ "<__fwd/streambuf.h>", private, "", public ] }, { include: [ "<__fwd/string_view.h>", private, "", public ] }, + { include: [ "<__system_error/errc.h>", private, "", public ] }, # Mappings for includes between public headers { include: [ "", public, "", public ] }, diff --git a/src/Makefile b/src/Makefile index 45f38b01322..5119b615f6b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -63,7 +63,7 @@ HEADERS = benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h \ nnue/layers/sqr_clipped_relu.h nnue/nnue_accumulator.h nnue/nnue_architecture.h \ nnue/nnue_common.h nnue/nnue_feature_transformer.h position.h \ search.h syzygy/tbprobe.h thread.h thread_win32_osx.h timeman.h \ - tt.h tune.h types.h uci.h ucioption.h perft.h nnue/network.h engine.h score.h + tt.h tune.h types.h uci.h ucioption.h perft.h nnue/network.h engine.h score.h numa.h OBJS = $(notdir $(SRCS:.cpp=.o)) diff --git a/src/engine.cpp b/src/engine.cpp index e8da24aa9e8..3fc27223a09 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -18,15 +18,15 @@ #include "engine.h" +#include #include +#include #include #include +#include #include #include #include -#include -#include -#include #include "evaluate.h" #include "misc.h" @@ -48,10 +48,14 @@ constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - Engine::Engine(std::string path) : binaryDirectory(CommandLine::get_binary_directory(path)), + numaContext(NumaConfig::from_system()), states(new std::deque(1)), - networks(NN::Networks( - NN::NetworkBig({EvalFileDefaultNameBig, "None", ""}, NN::EmbeddedNNUEType::BIG), - NN::NetworkSmall({EvalFileDefaultNameSmall, "None", ""}, NN::EmbeddedNNUEType::SMALL))) { + threads(), + networks( + numaContext, + NN::Networks( + NN::NetworkBig({EvalFileDefaultNameBig, "None", ""}, NN::EmbeddedNNUEType::BIG), + NN::NetworkSmall({EvalFileDefaultNameSmall, "None", ""}, NN::EmbeddedNNUEType::SMALL))) { pos.set(StartFEN, false, &states->back()); capSq = SQ_NONE; } @@ -74,7 +78,7 @@ void Engine::stop() { threads.stop = true; } void Engine::search_clear() { wait_for_search_finished(); - tt.clear(options["Threads"]); + tt.clear(threads); threads.clear(); // @TODO wont work with multiple instances @@ -124,11 +128,35 @@ void Engine::set_position(const std::string& fen, const std::vector // modifiers -void Engine::resize_threads() { threads.set({options, threads, tt, networks}, updateContext); } +void Engine::set_numa_config_from_option(const std::string& o) { + if (o == "auto" || o == "system") + { + numaContext.set_numa_config(NumaConfig::from_system()); + } + else if (o == "none") + { + numaContext.set_numa_config(NumaConfig{}); + } + else + { + numaContext.set_numa_config(NumaConfig::from_string(o)); + } + + // Force reallocation of threads in case affinities need to change. + resize_threads(); +} + +void Engine::resize_threads() { + threads.wait_for_search_finished(); + threads.set(numaContext.get_numa_config(), {options, threads, tt, networks}, updateContext); + + // Reallocate the hash with the new threadpool size + set_tt_size(options["Hash"]); +} void Engine::set_tt_size(size_t mb) { wait_for_search_finished(); - tt.resize(mb, options["Threads"]); + tt.resize(mb, threads); } void Engine::set_ponderhit(bool b) { threads.main_manager()->ponder = b; } @@ -136,28 +164,35 @@ void Engine::set_ponderhit(bool b) { threads.main_manager()->ponder = b; } // network related void Engine::verify_networks() const { - networks.big.verify(options["EvalFile"]); - networks.small.verify(options["EvalFileSmall"]); + networks->big.verify(options["EvalFile"]); + networks->small.verify(options["EvalFileSmall"]); } void Engine::load_networks() { - load_big_network(options["EvalFile"]); - load_small_network(options["EvalFileSmall"]); + networks.modify_and_replicate([this](NN::Networks& networks_) { + networks_.big.load(binaryDirectory, options["EvalFile"]); + networks_.small.load(binaryDirectory, options["EvalFileSmall"]); + }); + threads.clear(); } void Engine::load_big_network(const std::string& file) { - networks.big.load(binaryDirectory, file); + networks.modify_and_replicate( + [this, &file](NN::Networks& networks_) { networks_.big.load(binaryDirectory, file); }); threads.clear(); } void Engine::load_small_network(const std::string& file) { - networks.small.load(binaryDirectory, file); + networks.modify_and_replicate( + [this, &file](NN::Networks& networks_) { networks_.small.load(binaryDirectory, file); }); threads.clear(); } void Engine::save_network(const std::pair, std::string> files[2]) { - networks.big.save(files[0].first); - networks.small.save(files[1].first); + networks.modify_and_replicate([&files](NN::Networks& networks_) { + networks_.big.save(files[0].first); + networks_.small.save(files[1].first); + }); } // utility functions @@ -169,7 +204,7 @@ void Engine::trace_eval() const { verify_networks(); - sync_cout << "\n" << Eval::trace(p, networks) << sync_endl; + sync_cout << "\n" << Eval::trace(p, *networks) << sync_endl; } OptionsMap& Engine::get_options() { return options; } @@ -184,4 +219,21 @@ std::string Engine::visualize() const { return ss.str(); } +std::vector> Engine::get_bound_thread_count_by_numa_node() const { + auto counts = threads.get_bound_thread_count_by_numa_node(); + const NumaConfig& cfg = numaContext.get_numa_config(); + std::vector> ratios; + NumaIndex n = 0; + for (; n < counts.size(); ++n) + ratios.emplace_back(counts[n], cfg.num_cpus_in_numa_node(n)); + if (!counts.empty()) + for (; n < cfg.num_numa_nodes(); ++n) + ratios.emplace_back(0, cfg.num_cpus_in_numa_node(n)); + return ratios; +} + +std::string Engine::get_numa_config_as_string() const { + return numaContext.get_numa_config().to_string(); +} + } diff --git a/src/engine.h b/src/engine.h index 64a814cb4aa..91a8a96b0dc 100644 --- a/src/engine.h +++ b/src/engine.h @@ -35,6 +35,7 @@ #include "thread.h" #include "tt.h" #include "ucioption.h" +#include "numa.h" namespace Stockfish { @@ -47,6 +48,13 @@ class Engine { using InfoIter = Search::InfoIteration; Engine(std::string path = ""); + + // Can't be movable due to components holding backreferences to fields + Engine(const Engine&) = delete; + Engine(Engine&&) = delete; + Engine& operator=(const Engine&) = delete; + Engine& operator=(Engine&&) = delete; + ~Engine() { wait_for_search_finished(); } std::uint64_t perft(const std::string& fen, Depth depth, bool isChess960); @@ -63,6 +71,7 @@ class Engine { // modifiers + void set_numa_config_from_option(const std::string& o); void resize_threads(); void set_tt_size(size_t mb); void set_ponderhit(bool); @@ -83,23 +92,27 @@ class Engine { // utility functions - void trace_eval() const; - OptionsMap& get_options(); - std::string fen() const; - void flip(); - std::string visualize() const; + void trace_eval() const; + OptionsMap& get_options(); + std::string fen() const; + void flip(); + std::string visualize() const; + std::vector> get_bound_thread_count_by_numa_node() const; + std::string get_numa_config_as_string() const; private: const std::string binaryDirectory; + NumaReplicationContext numaContext; + Position pos; StateListPtr states; Square capSq; - OptionsMap options; - ThreadPool threads; - TranspositionTable tt; - Eval::NNUE::Networks networks; + OptionsMap options; + ThreadPool threads; + TranspositionTable tt; + NumaReplicated networks; Search::SearchManager::UpdateContext updateContext; }; diff --git a/src/misc.cpp b/src/misc.cpp index 58f804204b2..d48b75e1c28 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -48,6 +48,7 @@ using fun8_t = bool (*)(HANDLE, BOOL, PTOKEN_PRIVILEGES, DWORD, PTOKEN_PRIVILEGE #endif #include +#include #include #include #include @@ -56,6 +57,7 @@ using fun8_t = bool (*)(HANDLE, BOOL, PTOKEN_PRIVILEGES, DWORD, PTOKEN_PRIVILEGE #include #include #include +#include #include "types.h" @@ -592,129 +594,6 @@ void aligned_large_pages_free(void* mem) { std_aligned_free(mem); } #endif -namespace WinProcGroup { - -#ifndef _WIN32 - -void bind_this_thread(size_t) {} - -#else - -namespace { -// Retrieves logical processor information using Windows-specific -// API and returns the best node id for the thread with index idx. Original -// code from Texel by Peter Österlund. -int best_node(size_t idx) { - - int threads = 0; - int nodes = 0; - int cores = 0; - DWORD returnLength = 0; - DWORD byteOffset = 0; - - // Early exit if the needed API is not available at runtime - HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll")); - auto fun1 = (fun1_t) (void (*)()) GetProcAddress(k32, "GetLogicalProcessorInformationEx"); - if (!fun1) - return -1; - - // First call to GetLogicalProcessorInformationEx() to get returnLength. - // We expect the call to fail due to null buffer. - if (fun1(RelationAll, nullptr, &returnLength)) - return -1; - - // Once we know returnLength, allocate the buffer - SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *buffer, *ptr; - ptr = buffer = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*) malloc(returnLength); - - // Second call to GetLogicalProcessorInformationEx(), now we expect to succeed - if (!fun1(RelationAll, buffer, &returnLength)) - { - free(buffer); - return -1; - } - - while (byteOffset < returnLength) - { - if (ptr->Relationship == RelationNumaNode) - nodes++; - - else if (ptr->Relationship == RelationProcessorCore) - { - cores++; - threads += (ptr->Processor.Flags == LTP_PC_SMT) ? 2 : 1; - } - - assert(ptr->Size); - byteOffset += ptr->Size; - ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*) (((char*) ptr) + ptr->Size); - } - - free(buffer); - - std::vector groups; - - // Run as many threads as possible on the same node until the core limit is - // reached, then move on to filling the next node. - for (int n = 0; n < nodes; n++) - for (int i = 0; i < cores / nodes; i++) - groups.push_back(n); - - // In case a core has more than one logical processor (we assume 2) and we - // still have threads to allocate, spread them evenly across available nodes. - for (int t = 0; t < threads - cores; t++) - groups.push_back(t % nodes); - - // If we still have more threads than the total number of logical processors - // then return -1 and let the OS to decide what to do. - return idx < groups.size() ? groups[idx] : -1; -} -} - - -// Sets the group affinity of the current thread -void bind_this_thread(size_t idx) { - - // Use only local variables to be thread-safe - int node = best_node(idx); - - if (node == -1) - return; - - // Early exit if the needed API are not available at runtime - HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll")); - auto fun2 = fun2_t((void (*)()) GetProcAddress(k32, "GetNumaNodeProcessorMaskEx")); - auto fun3 = fun3_t((void (*)()) GetProcAddress(k32, "SetThreadGroupAffinity")); - auto fun4 = fun4_t((void (*)()) GetProcAddress(k32, "GetNumaNodeProcessorMask2")); - auto fun5 = fun5_t((void (*)()) GetProcAddress(k32, "GetMaximumProcessorGroupCount")); - - if (!fun2 || !fun3) - return; - - if (!fun4 || !fun5) - { - GROUP_AFFINITY affinity; - if (fun2(node, &affinity)) // GetNumaNodeProcessorMaskEx - fun3(GetCurrentThread(), &affinity, nullptr); // SetThreadGroupAffinity - } - else - { - // If a numa node has more than one processor group, we assume they are - // sized equal and we spread threads evenly across the groups. - USHORT elements, returnedElements; - elements = fun5(); // GetMaximumProcessorGroupCount - GROUP_AFFINITY* affinity = (GROUP_AFFINITY*) malloc(elements * sizeof(GROUP_AFFINITY)); - if (fun4(node, affinity, elements, &returnedElements)) // GetNumaNodeProcessorMask2 - fun3(GetCurrentThread(), &affinity[idx % returnedElements], - nullptr); // SetThreadGroupAffinity - free(affinity); - } -} - -#endif - -} // namespace WinProcGroup - #ifdef _WIN32 #include #define GETCWD _getcwd @@ -723,6 +602,15 @@ void bind_this_thread(size_t idx) { #define GETCWD getcwd #endif +size_t str_to_size_t(const std::string& s) { + size_t value; + auto result = std::from_chars(s.data(), s.data() + s.size(), value); + + if (result.ec != std::errc()) + std::exit(EXIT_FAILURE); + + return value; +} std::string CommandLine::get_binary_directory(std::string argv0) { std::string pathSeparator; diff --git a/src/misc.h b/src/misc.h index 3a905dfab49..99cbecfdd2c 100644 --- a/src/misc.h +++ b/src/misc.h @@ -24,10 +24,12 @@ #include #include #include +#include #include #include #include #include +#include #define stringify2(x) #x #define stringify(x) stringify2(x) @@ -50,6 +52,8 @@ void* aligned_large_pages_alloc(size_t size); // nop if mem == nullptr void aligned_large_pages_free(void* mem); +size_t str_to_size_t(const std::string& s); + // Deleter for automating release of memory area template struct AlignedDeleter { @@ -73,6 +77,31 @@ using AlignedPtr = std::unique_ptr>; template using LargePagePtr = std::unique_ptr>; +struct PipeDeleter { + void operator()(FILE* file) const { + if (file != nullptr) + { + pclose(file); + } + } +}; + +#if defined(__linux__) + +inline std::optional get_system_command_output(const std::string& command) { + std::unique_ptr pipe(popen(command.c_str(), "r")); + if (!pipe) + return std::nullopt; + + std::string result; + char buffer[1024]; + while (fgets(buffer, sizeof(buffer), pipe.get()) != nullptr) + result += buffer; + + return result; +} + +#endif void dbg_hit_on(bool cond, int slot = 0); void dbg_mean_of(int64_t value, int slot = 0); @@ -88,6 +117,24 @@ inline TimePoint now() { .count(); } +inline std::vector split(const std::string& s, const std::string& delimiter) { + size_t begin = 0; + std::vector res; + + for (;;) + { + const size_t end = s.find(delimiter, begin); + if (end == std::string::npos) + break; + + res.emplace_back(s.substr(begin, end - begin)); + begin = end + delimiter.size(); + } + + res.emplace_back(s.substr(begin)); + + return res; +} enum SyncCout { IO_LOCK, @@ -194,15 +241,6 @@ inline uint64_t mul_hi64(uint64_t a, uint64_t b) { #endif } -// Under Windows it is not possible for a process to run on more than one -// logical processor group. This usually means being limited to using max 64 -// cores. To overcome this, some special platform-specific API should be -// called to set group affinity for each thread. Original code from Texel by -// Peter Österlund. -namespace WinProcGroup { -void bind_this_thread(size_t idx); -} - struct CommandLine { public: diff --git a/src/nnue/network.cpp b/src/nnue/network.cpp index de2c7eca6d5..db864fcd384 100644 --- a/src/nnue/network.cpp +++ b/src/nnue/network.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -123,6 +124,47 @@ bool write_parameters(std::ostream& stream, const T& reference) { } // namespace Detail +template +Network::Network(const Network& other) : + evalFile(other.evalFile), + embeddedType(other.embeddedType) { + if (other.featureTransformer) + { + Detail::initialize(featureTransformer); + *featureTransformer = *other.featureTransformer; + } + for (std::size_t i = 0; i < LayerStacks; ++i) + { + if (other.network[i]) + { + Detail::initialize(network[i]); + *(network[i]) = *(other.network[i]); + } + } +} + +template +Network& +Network::operator=(const Network& other) { + evalFile = other.evalFile; + embeddedType = other.embeddedType; + + if (other.featureTransformer) + { + Detail::initialize(featureTransformer); + *featureTransformer = *other.featureTransformer; + } + for (std::size_t i = 0; i < LayerStacks; ++i) + { + if (other.network[i]) + { + Detail::initialize(network[i]); + *(network[i]) = *(other.network[i]); + } + } + + return *this; +} template void Network::load(const std::string& rootDirectory, std::string evalfilePath) { diff --git a/src/nnue/network.h b/src/nnue/network.h index 23f56663094..f0ccfafcb4c 100644 --- a/src/nnue/network.h +++ b/src/nnue/network.h @@ -50,6 +50,12 @@ class Network { evalFile(file), embeddedType(type) {} + Network(const Network& other); + Network(Network&& other) = default; + + Network& operator=(const Network& other); + Network& operator=(Network&& other) = default; + void load(const std::string& rootDirectory, std::string evalfilePath); bool save(const std::optional& filename) const; diff --git a/src/numa.h b/src/numa.h new file mode 100644 index 00000000000..c04292daf01 --- /dev/null +++ b/src/numa.h @@ -0,0 +1,904 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef NUMA_H_INCLUDED +#define NUMA_H_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// We support linux very well, but we explicitly do NOT support Android, partially because +// there are potential issues with `lscpu`, `popen` availability, and partially because +// there's no NUMA environments running Android and there probably won't be. +#if defined(__linux__) && !defined(__ANDROID__) + #if !defined(_GNU_SOURCE) + #define _GNU_SOURCE + #endif + #include +#elif defined(_WIN32) + +// On Windows each processor group can have up to 64 processors. +// https://learn.microsoft.com/en-us/windows/win32/procthread/processor-groups +static constexpr size_t WIN_PROCESSOR_GROUP_SIZE = 64; + + #if !defined(NOMINMAX) + #define NOMINMAX + #endif + #include + +// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadselectedcpusetmasks +using SetThreadSelectedCpuSetMasks_t = BOOL (*)(HANDLE, PGROUP_AFFINITY, USHORT); + +// https://learn.microsoft.com/en-us/windows/win32/api/processtopologyapi/nf-processtopologyapi-setthreadgroupaffinity +using SetThreadGroupAffinity_t = BOOL (*)(HANDLE, const GROUP_AFFINITY*, PGROUP_AFFINITY); + +#endif + +#include "misc.h" + +namespace Stockfish { + +using CpuIndex = size_t; +using NumaIndex = size_t; + +inline const CpuIndex SYSTEM_THREADS_NB = + std::max(1, std::thread::hardware_concurrency()); + +// We want to abstract the purpose of storing the numa node index somewhat. +// Whoever is using this does not need to know the specifics of the replication +// machinery to be able to access NUMA replicated memory. +class NumaReplicatedAccessToken { + public: + NumaReplicatedAccessToken() : + n(0) {} + + explicit NumaReplicatedAccessToken(NumaIndex idx) : + n(idx) {} + + NumaIndex get_numa_index() const { return n; } + + private: + NumaIndex n; +}; + +// Designed as immutable, because there is no good reason to alter an already existing config +// in a way that doesn't require recreating it completely, and it would be complex and expensive +// to maintain class invariants. +// The CPU (processor) numbers always correspond to the actual numbering used by the system. +// NOTE: the numbering is only valid within the process, as for example on Windows +// every process gets a "virtualized" set of processors that respects the current affinity +// The NUMA node numbers MAY NOT correspond to the system's numbering of the NUMA nodes. +// In particular, empty nodes may be removed, or the user may create custom nodes. +// It is guaranteed that NUMA nodes are NOT empty, i.e. every node exposed by NumaConfig +// has at least one processor assigned. +// +// Until Stockfish doesn't support exceptions all places where an exception should be thrown +// are replaced by std::exit. +class NumaConfig { + public: + NumaConfig() : + highestCpuIndex(0), + customAffinity(false) { + const auto numCpus = SYSTEM_THREADS_NB; + add_cpu_range_to_node(NumaIndex{0}, CpuIndex{0}, numCpus - 1); + } + + static std::set get_process_affinity() { + std::set cpus; + + // For unsupported systems, or in case of a soft error, we may assume all processors + // are available for use. + [[maybe_unused]] auto set_to_all_cpus = [&]() { + for (CpuIndex c = 0; c < SYSTEM_THREADS_NB; ++c) + cpus.insert(c); + }; + +#if defined(__linux__) && !defined(__ANDROID__) + + // cpu_set_t by default holds 1024 entries. This may not be enough soon, + // but there is no easy way to determine how many threads there actually is. + // In this case we just choose a reasonable upper bound. + static constexpr CpuIndex MaxNumCpus = 1024 * 64; + + cpu_set_t* mask = CPU_ALLOC(MaxNumCpus); + if (mask == nullptr) + std::exit(EXIT_FAILURE); + + const size_t masksize = CPU_ALLOC_SIZE(MaxNumCpus); + + CPU_ZERO_S(masksize, mask); + + const int status = sched_getaffinity(0, masksize, mask); + + if (status != 0) + { + CPU_FREE(mask); + std::exit(EXIT_FAILURE); + } + + for (CpuIndex c = 0; c < MaxNumCpus; ++c) + if (CPU_ISSET_S(c, masksize, mask)) + cpus.insert(c); + + CPU_FREE(mask); + +#elif defined(_WIN32) + + // Windows is problematic and weird due to multiple ways of setting affinity, processor groups, + // and behaviour changes between versions. It's unclear if we can support this feature + // on Windows in the same way we do on Linux. + // Apparently when affinity is set via either start /affinity or msys2 taskset + // the function GetNumaProcessorNodeEx completely disregards the processors that we do not + // have affinity more. Moreover, the indices are shifted to start from 0, indicating that Windows + // is providing a whole new mapping of processors to this process. This is problematic in some cases + // but it at least allows us to [probably] support this affinity restriction feature by default. + // So overall, Windows appears to "virtualize" a set of processors and processor groups for every + // process. It's unclear if this assignment can change while the process is running. + // std::thread::hardware_concurrency() returns the number of processors that's consistent + // with GetNumaProcessorNodeEx, so we can just add all of them. + + set_to_all_cpus(); + +#else + + // For other systems we assume the process is allowed to execute on all processors. + set_to_all_cpus(); + +#endif + + return cpus; + } + + // This function queries the system for the mapping of processors to NUMA nodes. + // On Linux we utilize `lscpu` to avoid libnuma. + // On Windows we utilize GetNumaProcessorNodeEx, which has its quirks, see + // comment for Windows implementation of get_process_affinity + static NumaConfig from_system(bool respectProcessAffinity = true) { + NumaConfig cfg = empty(); + + std::set allowedCpus; + + if (respectProcessAffinity) + allowedCpus = get_process_affinity(); + else + { + for (CpuIndex c = 0; c < SYSTEM_THREADS_NB; ++c) + allowedCpus.insert(c); + } + + auto is_cpu_allowed = [&](CpuIndex c) { return allowedCpus.count(c) == 1; }; + +#if defined(__linux__) && !defined(__ANDROID__) + + // On Linux things are straightforward, since there's no processor groups and + // any thread can be scheduled on all processors. + // This command produces output in the following form + // CPU NODE + // 0 0 + // 1 0 + // 2 1 + // 3 1 + // + // On some systems it may use '-' to signify no NUMA node, in which case we assume it's in node 0. + auto lscpuOpt = get_system_command_output("lscpu -e=cpu,node"); + if (lscpuOpt.has_value()) + { + + std::istringstream ss(*lscpuOpt); + + // skip the list header + ss.ignore(std::numeric_limits::max(), '\n'); + + while (true) + { + CpuIndex c; + NumaIndex n; + + ss >> c; + + if (!ss) + break; + + ss >> n; + + if (!ss) + { + ss.clear(); + std::string dummy; + ss >> dummy; + n = 0; + } + + if (is_cpu_allowed(c)) + cfg.add_cpu_to_node(n, c); + } + } + else + { + for (CpuIndex c = 0; c < SYSTEM_THREADS_NB; ++c) + if (is_cpu_allowed(c)) + cfg.add_cpu_to_node(NumaIndex{0}, c); + } + +#elif defined(_WIN32) + + // Since Windows 11 and Windows Server 2022 thread affinities can span + // processor groups and can be set as such by a new WinAPI function. + static const bool CanAffinitySpanProcessorGroups = []() { + HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll")); + auto SetThreadSelectedCpuSetMasks_f = SetThreadSelectedCpuSetMasks_t( + (void (*)()) GetProcAddress(k32, "SetThreadSelectedCpuSetMasks")); + return SetThreadSelectedCpuSetMasks_f != nullptr; + }(); + + WORD numProcGroups = GetActiveProcessorGroupCount(); + for (WORD procGroup = 0; procGroup < numProcGroups; ++procGroup) + { + for (BYTE number = 0; number < WIN_PROCESSOR_GROUP_SIZE; ++number) + { + PROCESSOR_NUMBER procnum; + procnum.Group = procGroup; + procnum.Number = number; + procnum.Reserved = 0; + USHORT nodeNumber; + + // When start /affinity or taskset was used to run this process with restricted affinity + // GetNumaProcessorNodeEx will NOT correspond to the system's processor setup, instead + // it appears to follow a completely new processor assignment, made specifically for this process, + // in which processors that this process has affinity for are remapped, and only those are remapped, + // to form a new set of processors. In other words, we can only get processors + // which we have affinity for this way. This means that the behaviour for + // `respectProcessAffinity == false` may be unexpected when affinity is set from outside, + // while the behaviour for `respectProcessAffinity == true` is given by default. + const BOOL status = GetNumaProcessorNodeEx(&procnum, &nodeNumber); + const CpuIndex c = static_cast(procGroup) * WIN_PROCESSOR_GROUP_SIZE + + static_cast(number); + if (status != 0 && nodeNumber != std::numeric_limits::max() + && is_cpu_allowed(c)) + { + cfg.add_cpu_to_node(nodeNumber, c); + } + } + } + + // Split the NUMA nodes to be contained within a group if necessary. + // This is needed between Windows 10 Build 20348 and Windows 11, because + // the new NUMA allocation behaviour was introduced while there was + // still no way to set thread affinity spanning multiple processor groups. + // See https://learn.microsoft.com/en-us/windows/win32/procthread/numa-support + if (!CanAffinitySpanProcessorGroups) + { + NumaConfig splitCfg = empty(); + + NumaIndex splitNodeIndex = 0; + for (const auto& cpus : cfg.nodes) + { + if (cpus.empty()) + continue; + + size_t lastProcGroupIndex = *(cpus.begin()) / WIN_PROCESSOR_GROUP_SIZE; + for (CpuIndex c : cpus) + { + const size_t procGroupIndex = c / WIN_PROCESSOR_GROUP_SIZE; + if (procGroupIndex != lastProcGroupIndex) + { + splitNodeIndex += 1; + lastProcGroupIndex = procGroupIndex; + } + splitCfg.add_cpu_to_node(splitNodeIndex, c); + } + splitNodeIndex += 1; + } + + cfg = std::move(splitCfg); + } + +#else + + // Fallback for unsupported systems. + for (CpuIndex c = 0; c < SYSTEM_THREADS_NB; ++c) + if (is_cpu_allowed(c)) + cfg.add_cpu_to_node(NumaIndex{0}, c); + +#endif + + // We have to ensure no empty NUMA nodes persist. + cfg.remove_empty_numa_nodes(); + + return cfg; + } + + // ':'-separated numa nodes + // ','-separated cpu indices + // supports "first-last" range syntax for cpu indices + // For example "0-15,128-143:16-31,144-159:32-47,160-175:48-63,176-191" + static NumaConfig from_string(const std::string& s) { + NumaConfig cfg = empty(); + + NumaIndex n = 0; + for (auto&& nodeStr : split(s, ":")) + { + bool addedAnyCpuInThisNode = false; + + for (const std::string& cpuStr : split(nodeStr, ",")) + { + if (cpuStr.empty()) + continue; + + auto parts = split(cpuStr, "-"); + if (parts.size() == 1) + { + const CpuIndex c = CpuIndex{str_to_size_t(parts[0])}; + if (!cfg.add_cpu_to_node(n, c)) + std::exit(EXIT_FAILURE); + } + else if (parts.size() == 2) + { + const CpuIndex cfirst = CpuIndex{str_to_size_t(parts[0])}; + const CpuIndex clast = CpuIndex{str_to_size_t(parts[1])}; + + if (!cfg.add_cpu_range_to_node(n, cfirst, clast)) + std::exit(EXIT_FAILURE); + } + else + { + std::exit(EXIT_FAILURE); + } + + addedAnyCpuInThisNode = true; + } + + if (addedAnyCpuInThisNode) + n += 1; + } + + cfg.customAffinity = true; + + return cfg; + } + + NumaConfig(const NumaConfig&) = delete; + NumaConfig(NumaConfig&&) = default; + NumaConfig& operator=(const NumaConfig&) = delete; + NumaConfig& operator=(NumaConfig&&) = default; + + bool is_cpu_assigned(CpuIndex n) const { return nodeByCpu.count(n) == 1; } + + NumaIndex num_numa_nodes() const { return nodes.size(); } + + CpuIndex num_cpus_in_numa_node(NumaIndex n) const { + assert(n < nodes.size()); + return nodes[n].size(); + } + + CpuIndex num_cpus() const { return nodeByCpu.size(); } + + bool requires_memory_replication() const { return customAffinity || nodes.size() > 1; } + + std::string to_string() const { + std::string str; + + bool isFirstNode = true; + for (auto&& cpus : nodes) + { + if (!isFirstNode) + str += ":"; + + bool isFirstSet = true; + auto rangeStart = cpus.begin(); + for (auto it = cpus.begin(); it != cpus.end(); ++it) + { + auto next = std::next(it); + if (next == cpus.end() || *next != *it + 1) + { + // cpus[i] is at the end of the range (may be of size 1) + if (!isFirstSet) + str += ","; + + const CpuIndex last = *it; + + if (it != rangeStart) + { + const CpuIndex first = *rangeStart; + + str += std::to_string(first); + str += "-"; + str += std::to_string(last); + } + else + str += std::to_string(last); + + rangeStart = next; + isFirstSet = false; + } + } + + isFirstNode = false; + } + + return str; + } + + bool suggests_binding_threads(CpuIndex numThreads) const { + // If we can reasonably determine that the threads can't be contained + // by the OS within the first NUMA node then we advise distributing + // and binding threads. When the threads are not bound we can only use + // NUMA memory replicated objects from the first node, so when the OS + // has to schedule on other nodes we lose performance. + // We also suggest binding if there's enough threads to distribute among nodes + // with minimal disparity. + // We try to ignore small nodes, in particular the empty ones. + + // If the affinity set by the user does not match the affinity given by the OS + // then binding is necessary to ensure the threads are running on correct processors. + if (customAffinity) + return true; + + // We obviously can't distribute a single thread, so a single thread should never be bound. + if (numThreads <= 1) + return false; + + size_t largestNodeSize = 0; + for (auto&& cpus : nodes) + if (cpus.size() > largestNodeSize) + largestNodeSize = cpus.size(); + + auto is_node_small = [largestNodeSize](const std::set& node) { + static constexpr double SmallNodeThreshold = 0.6; + return static_cast(node.size()) / static_cast(largestNodeSize) + <= SmallNodeThreshold; + }; + + size_t numNotSmallNodes = 0; + for (auto&& cpus : nodes) + if (!is_node_small(cpus)) + numNotSmallNodes += 1; + + return (numThreads > largestNodeSize / 2 || numThreads >= numNotSmallNodes * 4) + && nodes.size() > 1; + } + + std::vector distribute_threads_among_numa_nodes(CpuIndex numThreads) const { + std::vector ns; + + if (nodes.size() == 1) + { + // special case for when there's no NUMA nodes + // doesn't buy us much, but let's keep the default path simple + ns.resize(numThreads, NumaIndex{0}); + } + else + { + std::vector occupation(nodes.size(), 0); + for (CpuIndex c = 0; c < numThreads; ++c) + { + NumaIndex bestNode{0}; + float bestNodeFill = std::numeric_limits::max(); + for (NumaIndex n = 0; n < nodes.size(); ++n) + { + float fill = + static_cast(occupation[n] + 1) / static_cast(nodes[n].size()); + // NOTE: Do we want to perhaps fill the first available node up to 50% first before considering other nodes? + // Probably not, because it would interfere with running multiple instances. We basically shouldn't + // favor any particular node. + if (fill < bestNodeFill) + { + bestNode = n; + bestNodeFill = fill; + } + } + ns.emplace_back(bestNode); + occupation[bestNode] += 1; + } + } + + return ns; + } + + NumaReplicatedAccessToken bind_current_thread_to_numa_node(NumaIndex n) const { + if (n >= nodes.size() || nodes[n].size() == 0) + std::exit(EXIT_FAILURE); + +#if defined(__linux__) && !defined(__ANDROID__) + + cpu_set_t* mask = CPU_ALLOC(highestCpuIndex + 1); + if (mask == nullptr) + std::exit(EXIT_FAILURE); + + const size_t masksize = CPU_ALLOC_SIZE(highestCpuIndex + 1); + + CPU_ZERO_S(masksize, mask); + + for (CpuIndex c : nodes[n]) + CPU_SET_S(c, masksize, mask); + + const int status = sched_setaffinity(0, masksize, mask); + + CPU_FREE(mask); + + if (status != 0) + std::exit(EXIT_FAILURE); + + // We yield this thread just to be sure it gets rescheduled. + // This is defensive, allowed because this code is not performance critical. + sched_yield(); + +#elif defined(_WIN32) + + // Requires Windows 11. No good way to set thread affinity spanning processor groups before that. + HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll")); + auto SetThreadSelectedCpuSetMasks_f = SetThreadSelectedCpuSetMasks_t( + (void (*)()) GetProcAddress(k32, "SetThreadSelectedCpuSetMasks")); + auto SetThreadGroupAffinity_f = + SetThreadGroupAffinity_t((void (*)()) GetProcAddress(k32, "SetThreadGroupAffinity")); + + if (SetThreadSelectedCpuSetMasks_f != nullptr) + { + // Only available on Windows 11 and Windows Server 2022 onwards. + const USHORT numProcGroups = + ((highestCpuIndex + 1) + WIN_PROCESSOR_GROUP_SIZE - 1) / WIN_PROCESSOR_GROUP_SIZE; + auto groupAffinities = std::make_unique(numProcGroups); + std::memset(groupAffinities.get(), 0, sizeof(GROUP_AFFINITY) * numProcGroups); + for (WORD i = 0; i < numProcGroups; ++i) + groupAffinities[i].Group = i; + + for (CpuIndex c : nodes[n]) + { + const size_t procGroupIndex = c / WIN_PROCESSOR_GROUP_SIZE; + const size_t idxWithinProcGroup = c % WIN_PROCESSOR_GROUP_SIZE; + groupAffinities[procGroupIndex].Mask |= KAFFINITY(1) << idxWithinProcGroup; + } + + HANDLE hThread = GetCurrentThread(); + + const BOOL status = + SetThreadSelectedCpuSetMasks_f(hThread, groupAffinities.get(), numProcGroups); + if (status == 0) + std::exit(EXIT_FAILURE); + + // We yield this thread just to be sure it gets rescheduled. + // This is defensive, allowed because this code is not performance critical. + SwitchToThread(); + } + else if (SetThreadGroupAffinity_f != nullptr) + { + // On earlier windows version (since windows 7) we can't run a single thread + // on multiple processor groups, so we need to restrict the group. + // We assume the group of the first processor listed for this node. + // Processors from outside this group will not be assigned for this thread. + // Normally this won't be an issue because windows used to assign NUMA nodes + // such that they can't span processor groups. However, since Windows 10 Build 20348 + // the behaviour changed, so there's a small window of versions between this and Windows 11 + // that might exhibit problems with not all processors being utilized. + // We handle this in NumaConfig::from_system by manually splitting the nodes when + // we detect that there's no function to set affinity spanning processor nodes. + // This is required because otherwise our thread distribution code may produce + // suboptimal results. + // See https://learn.microsoft.com/en-us/windows/win32/procthread/numa-support + GROUP_AFFINITY affinity; + std::memset(&affinity, 0, sizeof(GROUP_AFFINITY)); + affinity.Group = static_cast(n); + // We use an ordered set so we're guaranteed to get the smallest cpu number here. + const size_t forcedProcGroupIndex = *(nodes[n].begin()) / WIN_PROCESSOR_GROUP_SIZE; + for (CpuIndex c : nodes[n]) + { + const size_t procGroupIndex = c / WIN_PROCESSOR_GROUP_SIZE; + const size_t idxWithinProcGroup = c % WIN_PROCESSOR_GROUP_SIZE; + // We skip processors that are not in the same proccessor group. + // If everything was set up correctly this will never be an issue, + // but we have to account for bad NUMA node specification. + if (procGroupIndex != forcedProcGroupIndex) + continue; + + affinity.Mask |= KAFFINITY(1) << idxWithinProcGroup; + } + + HANDLE hThread = GetCurrentThread(); + + const BOOL status = SetThreadGroupAffinity_f(hThread, &affinity, nullptr); + if (status == 0) + std::exit(EXIT_FAILURE); + + // We yield this thread just to be sure it gets rescheduled. + // This is defensive, allowed because this code is not performance critical. + SwitchToThread(); + } + +#endif + + return NumaReplicatedAccessToken(n); + } + + template + void execute_on_numa_node(NumaIndex n, FuncT&& f) const { + std::thread th([this, &f, n]() { + bind_current_thread_to_numa_node(n); + std::forward(f)(); + }); + + th.join(); + } + + private: + std::vector> nodes; + std::map nodeByCpu; + CpuIndex highestCpuIndex; + + bool customAffinity; + + static NumaConfig empty() { return NumaConfig(EmptyNodeTag{}); } + + struct EmptyNodeTag {}; + + NumaConfig(EmptyNodeTag) : + highestCpuIndex(0), + customAffinity(false) {} + + void remove_empty_numa_nodes() { + std::vector> newNodes; + for (auto&& cpus : nodes) + if (!cpus.empty()) + newNodes.emplace_back(std::move(cpus)); + nodes = std::move(newNodes); + } + + // Returns true if successful + // Returns false if failed, i.e. when the cpu is already present + // strong guarantee, the structure remains unmodified + bool add_cpu_to_node(NumaIndex n, CpuIndex c) { + if (is_cpu_assigned(c)) + return false; + + while (nodes.size() <= n) + nodes.emplace_back(); + + nodes[n].insert(c); + nodeByCpu[c] = n; + + if (c > highestCpuIndex) + highestCpuIndex = c; + + return true; + } + + // Returns true if successful + // Returns false if failed, i.e. when any of the cpus is already present + // strong guarantee, the structure remains unmodified + bool add_cpu_range_to_node(NumaIndex n, CpuIndex cfirst, CpuIndex clast) { + for (CpuIndex c = cfirst; c <= clast; ++c) + if (is_cpu_assigned(c)) + return false; + + while (nodes.size() <= n) + nodes.emplace_back(); + + for (CpuIndex c = cfirst; c <= clast; ++c) + { + nodes[n].insert(c); + nodeByCpu[c] = n; + } + + if (clast > highestCpuIndex) + highestCpuIndex = clast; + + return true; + } +}; + +class NumaReplicationContext; + +// Instances of this class are tracked by the NumaReplicationContext instance +// NumaReplicationContext informs all tracked instances whenever NUMA configuration changes. +class NumaReplicatedBase { + public: + NumaReplicatedBase(NumaReplicationContext& ctx); + + NumaReplicatedBase(const NumaReplicatedBase&) = delete; + NumaReplicatedBase(NumaReplicatedBase&& other) noexcept; + + NumaReplicatedBase& operator=(const NumaReplicatedBase&) = delete; + NumaReplicatedBase& operator=(NumaReplicatedBase&& other) noexcept; + + virtual void on_numa_config_changed() = 0; + virtual ~NumaReplicatedBase(); + + const NumaConfig& get_numa_config() const; + + private: + NumaReplicationContext* context; +}; + +// We force boxing with a unique_ptr. If this becomes an issue due to added indirection we +// may need to add an option for a custom boxing type. +// When the NUMA config changes the value stored at the index 0 is replicated to other nodes. +template +class NumaReplicated: public NumaReplicatedBase { + public: + using ReplicatorFuncType = std::function; + + NumaReplicated(NumaReplicationContext& ctx) : + NumaReplicatedBase(ctx) { + replicate_from(T{}); + } + + NumaReplicated(NumaReplicationContext& ctx, T&& source) : + NumaReplicatedBase(ctx) { + replicate_from(std::move(source)); + } + + NumaReplicated(const NumaReplicated&) = delete; + NumaReplicated(NumaReplicated&& other) noexcept : + NumaReplicatedBase(std::move(other)), + instances(std::exchange(other.instances, {})) {} + + NumaReplicated& operator=(const NumaReplicated&) = delete; + NumaReplicated& operator=(NumaReplicated&& other) noexcept { + NumaReplicatedBase::operator=(*this, std::move(other)); + instances = std::exchange(other.instances, {}); + + return *this; + } + + NumaReplicated& operator=(T&& source) { + replicate_from(std::move(source)); + + return *this; + } + + ~NumaReplicated() override = default; + + const T& operator[](NumaReplicatedAccessToken token) const { + assert(token.get_numa_index() < instances.size()); + return *(instances[token.get_numa_index()]); + } + + const T& operator*() const { return *(instances[0]); } + + const T* operator->() const { return instances[0].get(); } + + template + void modify_and_replicate(FuncT&& f) { + auto source = std::move(instances[0]); + std::forward(f)(*source); + replicate_from(std::move(*source)); + } + + void on_numa_config_changed() override { + // Use the first one as the source. It doesn't matter which one we use, because they all must + // be identical, but the first one is guaranteed to exist. + auto source = std::move(instances[0]); + replicate_from(std::move(*source)); + } + + private: + std::vector> instances; + + void replicate_from(T&& source) { + instances.clear(); + + const NumaConfig& cfg = get_numa_config(); + if (cfg.requires_memory_replication()) + { + for (NumaIndex n = 0; n < cfg.num_numa_nodes(); ++n) + { + cfg.execute_on_numa_node( + n, [this, &source]() { instances.emplace_back(std::make_unique(source)); }); + } + } + else + { + assert(cfg.num_numa_nodes() == 1); + // We take advantage of the fact that replication is not required + // and reuse the source value, avoiding one copy operation. + instances.emplace_back(std::make_unique(std::move(source))); + } + } +}; + +class NumaReplicationContext { + public: + NumaReplicationContext(NumaConfig&& cfg) : + config(std::move(cfg)) {} + + NumaReplicationContext(const NumaReplicationContext&) = delete; + NumaReplicationContext(NumaReplicationContext&&) = delete; + + NumaReplicationContext& operator=(const NumaReplicationContext&) = delete; + NumaReplicationContext& operator=(NumaReplicationContext&&) = delete; + + ~NumaReplicationContext() { + // The context must outlive replicated objects + if (!trackedReplicatedObjects.empty()) + std::exit(EXIT_FAILURE); + } + + void attach(NumaReplicatedBase* obj) { + assert(trackedReplicatedObjects.count(obj) == 0); + trackedReplicatedObjects.insert(obj); + } + + void detach(NumaReplicatedBase* obj) { + assert(trackedReplicatedObjects.count(obj) == 1); + trackedReplicatedObjects.erase(obj); + } + + // oldObj may be invalid at this point + void move_attached([[maybe_unused]] NumaReplicatedBase* oldObj, NumaReplicatedBase* newObj) { + assert(trackedReplicatedObjects.count(oldObj) == 1); + assert(trackedReplicatedObjects.count(newObj) == 0); + trackedReplicatedObjects.erase(oldObj); + trackedReplicatedObjects.insert(newObj); + } + + void set_numa_config(NumaConfig&& cfg) { + config = std::move(cfg); + for (auto&& obj : trackedReplicatedObjects) + obj->on_numa_config_changed(); + } + + const NumaConfig& get_numa_config() const { return config; } + + private: + NumaConfig config; + + // std::set uses std::less by default, which is required for pointer comparison to be defined. + std::set trackedReplicatedObjects; +}; + +inline NumaReplicatedBase::NumaReplicatedBase(NumaReplicationContext& ctx) : + context(&ctx) { + context->attach(this); +} + +inline NumaReplicatedBase::NumaReplicatedBase(NumaReplicatedBase&& other) noexcept : + context(std::exchange(other.context, nullptr)) { + context->move_attached(&other, this); +} + +inline NumaReplicatedBase& NumaReplicatedBase::operator=(NumaReplicatedBase&& other) noexcept { + context = std::exchange(other.context, nullptr); + + context->move_attached(&other, this); + + return *this; +} + +inline NumaReplicatedBase::~NumaReplicatedBase() { + if (context != nullptr) + context->detach(this); +} + +inline const NumaConfig& NumaReplicatedBase::get_numa_config() const { + return context->get_numa_config(); +} + +} // namespace Stockfish + + +#endif // #ifndef NUMA_H_INCLUDED diff --git a/src/search.cpp b/src/search.cpp index 0dbc6a3a5db..c074e3421ea 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -137,15 +137,17 @@ void update_all_stats(const Position& pos, Search::Worker::Worker(SharedState& sharedState, std::unique_ptr sm, - size_t thread_id) : + size_t thread_id, + NumaReplicatedAccessToken token) : // Unpack the SharedState struct into member variables thread_idx(thread_id), + numaAccessToken(token), manager(std::move(sm)), options(sharedState.options), threads(sharedState.threads), tt(sharedState.tt), networks(sharedState.networks), - refreshTable(networks) { + refreshTable(networks[token]) { clear(); } @@ -428,7 +430,7 @@ void Search::Worker::iterative_deepening() { skill.pick_best(rootMoves, multiPV); // Use part of the gained time from a previous stable move for the current move - for (Thread* th : threads) + for (auto&& th : threads) { totBestMoveChanges += th->worker->bestMoveChanges; th->worker->bestMoveChanges = 0; @@ -510,7 +512,7 @@ void Search::Worker::clear() { for (size_t i = 1; i < reductions.size(); ++i) reductions[i] = int((19.90 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); - refreshTable.clear(networks); + refreshTable.clear(networks[numaAccessToken]); } @@ -576,9 +578,9 @@ Value Search::Worker::search( // Step 2. Check for aborted search and immediate draw if (threads.stop.load(std::memory_order_relaxed) || pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) - return (ss->ply >= MAX_PLY && !ss->inCheck) - ? evaluate(networks, pos, refreshTable, thisThread->optimism[us]) - : value_draw(thisThread->nodes); + return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate( + networks[numaAccessToken], pos, refreshTable, thisThread->optimism[us]) + : value_draw(thisThread->nodes); // 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 @@ -706,7 +708,7 @@ Value Search::Worker::search( { // Providing the hint that this node's accumulator will be used often // brings significant Elo gain (~13 Elo). - Eval::NNUE::hint_common_parent_position(pos, networks, refreshTable); + Eval::NNUE::hint_common_parent_position(pos, networks[numaAccessToken], refreshTable); unadjustedStaticEval = eval = ss->staticEval; } else if (ss->ttHit) @@ -714,9 +716,10 @@ Value Search::Worker::search( // Never assume anything about values stored in TT unadjustedStaticEval = tte->eval(); if (unadjustedStaticEval == VALUE_NONE) - unadjustedStaticEval = evaluate(networks, pos, refreshTable, thisThread->optimism[us]); + unadjustedStaticEval = + evaluate(networks[numaAccessToken], pos, refreshTable, thisThread->optimism[us]); else if (PvNode) - Eval::NNUE::hint_common_parent_position(pos, networks, refreshTable); + Eval::NNUE::hint_common_parent_position(pos, networks[numaAccessToken], refreshTable); ss->staticEval = eval = to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); @@ -726,7 +729,8 @@ Value Search::Worker::search( } else { - unadjustedStaticEval = evaluate(networks, pos, refreshTable, thisThread->optimism[us]); + unadjustedStaticEval = + evaluate(networks[numaAccessToken], pos, refreshTable, thisThread->optimism[us]); ss->staticEval = eval = to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); // Static evaluation is saved as it was before adjustment by correction history @@ -892,7 +896,7 @@ Value Search::Worker::search( } } - Eval::NNUE::hint_common_parent_position(pos, networks, refreshTable); + Eval::NNUE::hint_common_parent_position(pos, networks[numaAccessToken], refreshTable); } moves_loop: // When in check, search starts here @@ -1441,7 +1445,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, // Step 2. Check for an immediate draw or maximum ply reached if (pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) return (ss->ply >= MAX_PLY && !ss->inCheck) - ? evaluate(networks, pos, refreshTable, thisThread->optimism[us]) + ? evaluate(networks[numaAccessToken], pos, refreshTable, thisThread->optimism[us]) : VALUE_DRAW; assert(0 <= ss->ply && ss->ply < MAX_PLY); @@ -1476,7 +1480,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, unadjustedStaticEval = tte->eval(); if (unadjustedStaticEval == VALUE_NONE) unadjustedStaticEval = - evaluate(networks, pos, refreshTable, thisThread->optimism[us]); + evaluate(networks[numaAccessToken], pos, refreshTable, thisThread->optimism[us]); ss->staticEval = bestValue = to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); @@ -1488,10 +1492,11 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, else { // In case of null move search, use previous static eval with a different sign - unadjustedStaticEval = (ss - 1)->currentMove != Move::null() - ? evaluate(networks, pos, refreshTable, thisThread->optimism[us]) - : -(ss - 1)->staticEval; - ss->staticEval = bestValue = + unadjustedStaticEval = + (ss - 1)->currentMove != Move::null() + ? evaluate(networks[numaAccessToken], pos, refreshTable, thisThread->optimism[us]) + : -(ss - 1)->staticEval; + ss->staticEval = bestValue = to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); } diff --git a/src/search.h b/src/search.h index 6e5b22bda32..a61f253c005 100644 --- a/src/search.h +++ b/src/search.h @@ -32,19 +32,17 @@ #include "misc.h" #include "movepick.h" +#include "nnue/network.h" +#include "nnue/nnue_accumulator.h" +#include "numa.h" #include "position.h" #include "score.h" #include "syzygy/tbprobe.h" #include "timeman.h" #include "types.h" -#include "nnue/nnue_accumulator.h" namespace Stockfish { -namespace Eval::NNUE { -struct Networks; -} - // Different node types, used as a template parameter enum NodeType { NonPV, @@ -133,19 +131,19 @@ struct LimitsType { // The UCI stores the uci options, thread pool, and transposition table. // This struct is used to easily forward data to the Search::Worker class. struct SharedState { - SharedState(const OptionsMap& optionsMap, - ThreadPool& threadPool, - TranspositionTable& transpositionTable, - const Eval::NNUE::Networks& nets) : + SharedState(const OptionsMap& optionsMap, + ThreadPool& threadPool, + TranspositionTable& transpositionTable, + const NumaReplicated& nets) : options(optionsMap), threads(threadPool), tt(transpositionTable), networks(nets) {} - const OptionsMap& options; - ThreadPool& threads; - TranspositionTable& tt; - const Eval::NNUE::Networks& networks; + const OptionsMap& options; + ThreadPool& threads; + TranspositionTable& tt; + const NumaReplicated& networks; }; class Worker; @@ -236,7 +234,7 @@ class NullSearchManager: public ISearchManager { // of the search history, and storing data required for the search. class Worker { public: - Worker(SharedState&, std::unique_ptr, size_t); + Worker(SharedState&, std::unique_ptr, size_t, NumaReplicatedAccessToken); // Called at instantiation to initialize Reductions tables // Reset histories, usually before a new game @@ -293,7 +291,8 @@ class Worker { Depth rootDepth, completedDepth; Value rootDelta; - size_t thread_idx; + size_t thread_idx; + NumaReplicatedAccessToken numaAccessToken; // Reductions lookup table initialized at startup std::array reductions; // [depth or moveNumber] @@ -303,10 +302,10 @@ class Worker { Tablebases::Config tbConfig; - const OptionsMap& options; - ThreadPool& threads; - TranspositionTable& tt; - const Eval::NNUE::Networks& networks; + const OptionsMap& options; + ThreadPool& threads; + TranspositionTable& tt; + const NumaReplicated& networks; // Used by NNUE Eval::NNUE::AccumulatorCaches refreshTable; diff --git a/src/thread.cpp b/src/thread.cpp index 8724cb49cd1..5893f4b6d07 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -22,19 +22,17 @@ #include #include #include +#include #include #include -#include -#include "misc.h" #include "movegen.h" #include "search.h" #include "syzygy/tbprobe.h" #include "timeman.h" -#include "tt.h" #include "types.h" -#include "ucioption.h" #include "uci.h" +#include "ucioption.h" namespace Stockfish { @@ -42,13 +40,24 @@ namespace Stockfish { // in idle_loop(). Note that 'searching' and 'exit' should be already set. Thread::Thread(Search::SharedState& sharedState, std::unique_ptr sm, - size_t n) : - worker(std::make_unique(sharedState, std::move(sm), n)), + size_t n, + OptionalThreadToNumaNodeBinder binder) : idx(n), nthreads(sharedState.options["Threads"]), stdThread(&Thread::idle_loop, this) { wait_for_search_finished(); + + run_custom_job([this, &binder, &sharedState, &sm, n]() { + // Use the binder to [maybe] bind the threads to a NUMA node before doing + // the Worker allocation. + // Ideally we would also allocate the SearchManager here, but that's minor. + this->numaAccessToken = binder(); + this->worker = + std::make_unique(sharedState, std::move(sm), n, this->numaAccessToken); + }); + + wait_for_search_finished(); } @@ -66,12 +75,15 @@ Thread::~Thread() { // Wakes up the thread that will start the search void Thread::start_searching() { - mutex.lock(); - searching = true; - mutex.unlock(); // Unlock before notifying saves a few CPU-cycles - cv.notify_one(); // Wake up the thread in idle_loop() + assert(worker != nullptr); + run_custom_job([this]() { worker->start_searching(); }); } +// Wakes up the thread that will start the search +void Thread::clear_worker() { + assert(worker != nullptr); + run_custom_job([this]() { worker->clear(); }); +} // Blocks on the condition variable // until the thread has finished searching. @@ -81,20 +93,20 @@ void Thread::wait_for_search_finished() { cv.wait(lk, [&] { return !searching; }); } +void Thread::run_custom_job(std::function f) { + { + std::unique_lock lk(mutex); + cv.wait(lk, [&] { return !searching; }); + jobFunc = std::move(f); + searching = true; + } + cv.notify_one(); +} // Thread gets parked here, blocked on the // condition variable, when it has no work to do. void Thread::idle_loop() { - - // If OS already scheduled us on a different group than 0 then don't overwrite - // the choice, eventually we are one of many one-threaded processes running on - // some Windows NUMA hardware, for instance in fishtest. To make it simple, - // just check if running threads are below a threshold, in this case, all this - // NUMA machinery is not needed. - if (nthreads > 8) - WinProcGroup::bind_this_thread(idx); - while (true) { std::unique_lock lk(mutex); @@ -105,9 +117,13 @@ void Thread::idle_loop() { if (exit) return; + std::function job = std::move(jobFunc); + jobFunc = nullptr; + lk.unlock(); - worker->start_searching(); + if (job) + job(); } } @@ -121,49 +137,82 @@ uint64_t ThreadPool::tb_hits() const { return accumulate(&Search::Worker::tbHits // Creates/destroys threads to match the requested number. // Created and launched threads will immediately go to sleep in idle_loop. // Upon resizing, threads are recreated to allow for binding if necessary. -void ThreadPool::set(Search::SharedState sharedState, +void ThreadPool::set(const NumaConfig& numaConfig, + Search::SharedState sharedState, const Search::SearchManager::UpdateContext& updateContext) { if (threads.size() > 0) // destroy any existing thread(s) { main_thread()->wait_for_search_finished(); - while (threads.size() > 0) - delete threads.back(), threads.pop_back(); + threads.clear(); + + boundThreadToNumaNode.clear(); } const size_t requested = sharedState.options["Threads"]; if (requested > 0) // create new thread(s) { - auto manager = std::make_unique(updateContext); - threads.push_back(new Thread(sharedState, std::move(manager), 0)); + // Binding threads may be problematic when there's multiple NUMA nodes and + // multiple Stockfish instances running. In particular, if each instance + // runs a single thread then they would all be mapped to the first NUMA node. + // This is undesirable, and so the default behaviour (i.e. when the user does not + // change the NumaConfig UCI setting) is to not bind the threads to processors + // unless we know for sure that we span NUMA nodes and replication is required. + const std::string numaPolicy(sharedState.options["NumaPolicy"]); + const bool doBindThreads = [&]() { + if (numaPolicy == "none") + return false; + + if (numaPolicy == "auto") + return numaConfig.suggests_binding_threads(requested); + + // numaPolicy == "system", or explicitly set by the user + return true; + }(); + + boundThreadToNumaNode = doBindThreads + ? numaConfig.distribute_threads_among_numa_nodes(requested) + : std::vector{}; while (threads.size() < requested) { - auto null_manager = std::make_unique(); - threads.push_back(new Thread(sharedState, std::move(null_manager), threads.size())); + const size_t threadId = threads.size(); + const NumaIndex numaId = doBindThreads ? boundThreadToNumaNode[threadId] : 0; + auto manager = threadId == 0 ? std::unique_ptr( + std::make_unique(updateContext)) + : std::make_unique(); + + // When not binding threads we want to force all access to happen + // from the same NUMA node, because in case of NUMA replicated memory + // accesses we don't want to trash cache in case the threads get scheduled + // on the same NUMA node. + auto binder = doBindThreads ? OptionalThreadToNumaNodeBinder(numaConfig, numaId) + : OptionalThreadToNumaNodeBinder(numaId); + + threads.emplace_back( + std::make_unique(sharedState, std::move(manager), threadId, binder)); } clear(); main_thread()->wait_for_search_finished(); - - // Reallocate the hash with the new threadpool size - sharedState.tt.resize(sharedState.options["Hash"], requested); } } // Sets threadPool data to initial values void ThreadPool::clear() { - - for (Thread* th : threads) - th->worker->clear(); - if (threads.size() == 0) return; + for (auto&& th : threads) + th->clear_worker(); + + for (auto&& th : threads) + th->wait_for_search_finished(); + main_manager()->callsCnt = 0; main_manager()->bestPreviousScore = VALUE_INFINITE; main_manager()->bestPreviousAverageScore = VALUE_INFINITE; @@ -172,6 +221,17 @@ void ThreadPool::clear() { main_manager()->tm.clear(); } +void ThreadPool::run_on_thread(size_t threadId, std::function f) { + assert(threads.size() > threadId); + threads[threadId]->run_custom_job(std::move(f)); +} + +void ThreadPool::wait_on_thread(size_t threadId) { + assert(threads.size() > threadId); + threads[threadId]->wait_for_search_finished(); +} + +size_t ThreadPool::num_threads() const { return threads.size(); } // Wakes up main thread waiting in idle_loop() and // returns immediately. Main thread will wake up other threads and start the search. @@ -216,31 +276,36 @@ void ThreadPool::start_thinking(const OptionsMap& options, // be deduced from a fen string, so set() clears them and they are set from // setupStates->back() later. The rootState is per thread, earlier states are shared // since they are read-only. - for (Thread* th : threads) + for (auto&& th : threads) { - th->worker->limits = limits; - th->worker->nodes = th->worker->tbHits = th->worker->nmpMinPly = - th->worker->bestMoveChanges = 0; - th->worker->rootDepth = th->worker->completedDepth = 0; - th->worker->rootMoves = rootMoves; - th->worker->rootPos.set(pos.fen(), pos.is_chess960(), &th->worker->rootState); - th->worker->rootState = setupStates->back(); - th->worker->tbConfig = tbConfig; + th->run_custom_job([&]() { + th->worker->limits = limits; + th->worker->nodes = th->worker->tbHits = th->worker->nmpMinPly = + th->worker->bestMoveChanges = 0; + th->worker->rootDepth = th->worker->completedDepth = 0; + th->worker->rootMoves = rootMoves; + th->worker->rootPos.set(pos.fen(), pos.is_chess960(), &th->worker->rootState); + th->worker->rootState = setupStates->back(); + th->worker->tbConfig = tbConfig; + }); } + for (auto&& th : threads) + th->wait_for_search_finished(); + main_thread()->start_searching(); } Thread* ThreadPool::get_best_thread() const { - Thread* bestThread = threads.front(); + Thread* bestThread = threads.front().get(); Value minScore = VALUE_NONE; std::unordered_map votes( 2 * std::min(size(), bestThread->worker->rootMoves.size())); // Find the minimum score of all threads - for (Thread* th : threads) + for (auto&& th : threads) minScore = std::min(minScore, th->worker->rootMoves[0].score); // Vote according to score and depth, and select the best thread @@ -248,10 +313,10 @@ Thread* ThreadPool::get_best_thread() const { return (th->worker->rootMoves[0].score - minScore + 14) * int(th->worker->completedDepth); }; - for (Thread* th : threads) - votes[th->worker->rootMoves[0].pv[0]] += thread_voting_value(th); + for (auto&& th : threads) + votes[th->worker->rootMoves[0].pv[0]] += thread_voting_value(th.get()); - for (Thread* th : threads) + for (auto&& th : threads) { const auto bestThreadScore = bestThread->worker->rootMoves[0].score; const auto newThreadScore = th->worker->rootMoves[0].score; @@ -272,26 +337,26 @@ Thread* ThreadPool::get_best_thread() const { // Note that we make sure not to pick a thread with truncated-PV for better viewer experience. const bool betterVotingValue = - thread_voting_value(th) * int(newThreadPV.size() > 2) + thread_voting_value(th.get()) * int(newThreadPV.size() > 2) > thread_voting_value(bestThread) * int(bestThreadPV.size() > 2); if (bestThreadInProvenWin) { // Make sure we pick the shortest mate / TB conversion if (newThreadScore > bestThreadScore) - bestThread = th; + bestThread = th.get(); } else if (bestThreadInProvenLoss) { // Make sure we pick the shortest mated / TB conversion if (newThreadInProvenLoss && newThreadScore < bestThreadScore) - bestThread = th; + bestThread = th.get(); } else if (newThreadInProvenWin || newThreadInProvenLoss || (newThreadScore > VALUE_TB_LOSS_IN_MAX_PLY && (newThreadMoveVote > bestThreadMoveVote || (newThreadMoveVote == bestThreadMoveVote && betterVotingValue)))) - bestThread = th; + bestThread = th.get(); } return bestThread; @@ -302,7 +367,7 @@ Thread* ThreadPool::get_best_thread() const { // Will be invoked by main thread after it has started searching void ThreadPool::start_searching() { - for (Thread* th : threads) + for (auto&& th : threads) if (th != threads.front()) th->start_searching(); } @@ -312,9 +377,28 @@ void ThreadPool::start_searching() { void ThreadPool::wait_for_search_finished() const { - for (Thread* th : threads) + for (auto&& th : threads) if (th != threads.front()) th->wait_for_search_finished(); } +std::vector ThreadPool::get_bound_thread_count_by_numa_node() const { + std::vector counts; + + if (!boundThreadToNumaNode.empty()) + { + NumaIndex highestNumaNode = 0; + for (NumaIndex n : boundThreadToNumaNode) + if (n > highestNumaNode) + highestNumaNode = n; + + counts.resize(highestNumaNode + 1, 0); + + for (NumaIndex n : boundThreadToNumaNode) + counts[n] += 1; + } + + return counts; +} + } // namespace Stockfish diff --git a/src/thread.h b/src/thread.h index 223652aec99..102b229907b 100644 --- a/src/thread.h +++ b/src/thread.h @@ -26,10 +26,12 @@ #include #include #include +#include #include "position.h" #include "search.h" #include "thread_win32_osx.h" +#include "numa.h" namespace Stockfish { @@ -37,6 +39,32 @@ namespace Stockfish { class OptionsMap; using Value = int; +// Sometimes we don't want to actually bind the threads, but the recipent still +// needs to think it runs on *some* NUMA node, such that it can access structures +// that rely on NUMA node knowledge. This class encapsulates this optional process +// such that the recipent does not need to know whether the binding happened or not. +class OptionalThreadToNumaNodeBinder { + public: + OptionalThreadToNumaNodeBinder(NumaIndex n) : + numaConfig(nullptr), + numaId(n) {} + + OptionalThreadToNumaNodeBinder(const NumaConfig& cfg, NumaIndex n) : + numaConfig(&cfg), + numaId(n) {} + + NumaReplicatedAccessToken operator()() const { + if (numaConfig != nullptr) + return numaConfig->bind_current_thread_to_numa_node(numaId); + else + return NumaReplicatedAccessToken(numaId); + } + + private: + const NumaConfig* numaConfig; + NumaIndex numaId; +}; + // Abstraction of a thread. It contains a pointer to the worker and a native thread. // After construction, the native thread is started with idle_loop() // waiting for a signal to start searching. @@ -44,22 +72,35 @@ using Value = int; // the search is finished, it goes back to idle_loop() waiting for a new signal. class Thread { public: - Thread(Search::SharedState&, std::unique_ptr, size_t); + Thread(Search::SharedState&, + std::unique_ptr, + size_t, + OptionalThreadToNumaNodeBinder); virtual ~Thread(); - void idle_loop(); - void start_searching(); + void idle_loop(); + void start_searching(); + void clear_worker(); + void run_custom_job(std::function f); + + // Thread has been slightly altered to allow running custom jobs, so + // this name is no longer correct. However, this class (and ThreadPool) + // require further work to make them properly generic while maintaining + // appropriate specificity regarding search, from the point of view of an + // outside user, so renaming of this function in left for whenever that happens. void wait_for_search_finished(); size_t id() const { return idx; } std::unique_ptr worker; + std::function jobFunc; private: - std::mutex mutex; - std::condition_variable cv; - size_t idx, nthreads; - bool exit = false, searching = true; // Set before starting std::thread - NativeThread stdThread; + std::mutex mutex; + std::condition_variable cv; + size_t idx, nthreads; + bool exit = false, searching = true; // Set before starting std::thread + NativeThread stdThread; + NumaReplicatedAccessToken numaAccessToken; }; @@ -67,31 +108,44 @@ class Thread { // parking and, most importantly, launching a thread. All the access to threads // is done through this class. class ThreadPool { - public: + ThreadPool() {} + ~ThreadPool() { // destroy any existing thread(s) if (threads.size() > 0) { main_thread()->wait_for_search_finished(); - while (threads.size() > 0) - delete threads.back(), threads.pop_back(); + threads.clear(); } } - void start_thinking(const OptionsMap&, Position&, StateListPtr&, Search::LimitsType); - void clear(); - void set(Search::SharedState, const Search::SearchManager::UpdateContext&); + ThreadPool(const ThreadPool&) = delete; + ThreadPool(ThreadPool&&) = delete; + + ThreadPool& operator=(const ThreadPool&) = delete; + ThreadPool& operator=(ThreadPool&&) = delete; + + void start_thinking(const OptionsMap&, Position&, StateListPtr&, Search::LimitsType); + void run_on_thread(size_t threadId, std::function f); + void wait_on_thread(size_t threadId); + size_t num_threads() const; + void clear(); + void set(const NumaConfig& numaConfig, + Search::SharedState, + const Search::SearchManager::UpdateContext&); Search::SearchManager* main_manager(); - Thread* main_thread() const { return threads.front(); } + Thread* main_thread() const { return threads.front().get(); } uint64_t nodes_searched() const; uint64_t tb_hits() const; Thread* get_best_thread() const; void start_searching(); void wait_for_search_finished() const; + std::vector get_bound_thread_count_by_numa_node() const; + std::atomic_bool stop, abortedSearch, increaseDepth; auto cbegin() const noexcept { return threads.cbegin(); } @@ -102,13 +156,14 @@ class ThreadPool { auto empty() const noexcept { return threads.empty(); } private: - StateListPtr setupStates; - std::vector threads; + StateListPtr setupStates; + std::vector> threads; + std::vector boundThreadToNumaNode; uint64_t accumulate(std::atomic Search::Worker::*member) const { uint64_t sum = 0; - for (Thread* th : threads) + for (auto&& th : threads) sum += (th->worker.get()->*member).load(std::memory_order_relaxed); return sum; } diff --git a/src/tt.cpp b/src/tt.cpp index 3f5b9d4d9b0..79274f525b9 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -23,10 +23,10 @@ #include #include #include -#include -#include #include "misc.h" +#include "syzygy/tbprobe.h" +#include "thread.h" namespace Stockfish { @@ -74,7 +74,7 @@ uint8_t TTEntry::relative_age(const uint8_t generation8) const { // Sets the size of the transposition table, // measured in megabytes. Transposition table consists // of clusters and each cluster consists of ClusterSize number of TTEntry. -void TranspositionTable::resize(size_t mbSize, int threadCount) { +void TranspositionTable::resize(size_t mbSize, ThreadPool& threads) { aligned_large_pages_free(table); clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster); @@ -86,32 +86,29 @@ void TranspositionTable::resize(size_t mbSize, int threadCount) { exit(EXIT_FAILURE); } - clear(threadCount); + clear(threads); } // Initializes the entire transposition table to zero, // in a multi-threaded way. -void TranspositionTable::clear(size_t threadCount) { - std::vector threads; +void TranspositionTable::clear(ThreadPool& threads) { + const size_t threadCount = threads.num_threads(); - for (size_t idx = 0; idx < size_t(threadCount); ++idx) + for (size_t i = 0; i < threadCount; ++i) { - threads.emplace_back([this, idx, threadCount]() { - // Thread binding gives faster search on systems with a first-touch policy - if (threadCount > 8) - WinProcGroup::bind_this_thread(idx); - + threads.run_on_thread(i, [this, i, threadCount]() { // Each thread will zero its part of the hash table - const size_t stride = size_t(clusterCount / threadCount), start = size_t(stride * idx), - len = idx != size_t(threadCount) - 1 ? stride : clusterCount - start; + const size_t stride = clusterCount / threadCount; + const size_t start = stride * i; + const size_t len = i + 1 != threadCount ? stride : clusterCount - start; std::memset(&table[start], 0, len * sizeof(Cluster)); }); } - for (std::thread& th : threads) - th.join(); + for (size_t i = 0; i < threadCount; ++i) + threads.wait_on_thread(i); } diff --git a/src/tt.h b/src/tt.h index 7cc876fb9ea..3b09ec4e1d9 100644 --- a/src/tt.h +++ b/src/tt.h @@ -63,6 +63,7 @@ struct TTEntry { int16_t eval16; }; +class ThreadPool; // A TranspositionTable is an array of Cluster, of size clusterCount. Each // cluster consists of ClusterSize number of TTEntry. Each non-empty TTEntry @@ -102,8 +103,8 @@ class TranspositionTable { TTEntry* probe(const Key key, bool& found) const; int hashfull() const; - void resize(size_t mbSize, int threadCount); - void clear(size_t threadCount); + void resize(size_t mbSize, ThreadPool& threads); + void clear(ThreadPool& threads); TTEntry* first_entry(const Key key) const { return &table[mul_hi64(key, clusterCount)].entry[0]; diff --git a/src/uci.cpp b/src/uci.cpp index cb686a027db..ab0dae3946e 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -60,7 +60,16 @@ UCIEngine::UCIEngine(int argc, char** argv) : options["Debug Log File"] << Option("", [](const Option& o) { start_logger(o); }); - options["Threads"] << Option(1, 1, 1024, [this](const Option&) { engine.resize_threads(); }); + options["NumaPolicy"] << Option("auto", [this](const Option& o) { + engine.set_numa_config_from_option(o); + print_numa_config_information(); + print_thread_binding_information(); + }); + + options["Threads"] << Option(1, 1, 1024, [this](const Option&) { + engine.resize_threads(); + print_thread_binding_information(); + }); options["Hash"] << Option(16, 1, MaxHashMB, [this](const Option& o) { engine.set_tt_size(o); }); @@ -123,8 +132,15 @@ void UCIEngine::loop() { engine.set_ponderhit(false); else if (token == "uci") + { sync_cout << "id name " << engine_info(true) << "\n" - << engine.get_options() << "\nuciok" << sync_endl; + << engine.get_options() << sync_endl; + + print_numa_config_information(); + print_thread_binding_information(); + + sync_cout << "uciok" << sync_endl; + } else if (token == "setoption") setoption(is); @@ -177,6 +193,28 @@ void UCIEngine::loop() { } while (token != "quit" && cli.argc == 1); // The command-line arguments are one-shot } +void UCIEngine::print_numa_config_information() const { + auto cfgStr = engine.get_numa_config_as_string(); + sync_cout << "info string Available Processors: " << cfgStr << sync_endl; +} + +void UCIEngine::print_thread_binding_information() const { + auto boundThreadsByNode = engine.get_bound_thread_count_by_numa_node(); + if (!boundThreadsByNode.empty()) + { + sync_cout << "info string NUMA Node Thread Binding: "; + bool isFirst = true; + for (auto&& [current, total] : boundThreadsByNode) + { + if (!isFirst) + std::cout << ":"; + std::cout << current << "/" << total; + isFirst = false; + } + std::cout << sync_endl; + } +} + Search::LimitsType UCIEngine::parse_limits(std::istream& is) { Search::LimitsType limits; std::string token; diff --git a/src/uci.h b/src/uci.h index 55d580f9727..bac62bb90c0 100644 --- a/src/uci.h +++ b/src/uci.h @@ -42,6 +42,9 @@ class UCIEngine { void loop(); + void print_numa_config_information() const; + void print_thread_binding_information() const; + static int to_cp(Value v, const Position& pos); static std::string format_score(const Score& s); static std::string square(Square s); diff --git a/src/ucioption.cpp b/src/ucioption.cpp index e1ffe546525..4819a68db73 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -118,6 +118,8 @@ bool Option::operator==(const char* s) const { return !CaseInsensitiveLess()(currentValue, s) && !CaseInsensitiveLess()(s, currentValue); } +bool Option::operator!=(const char* s) const { return !(*this == s); } + // Inits options and assigns idx in the correct printing order diff --git a/src/ucioption.h b/src/ucioption.h index b575d1646e6..16d46696145 100644 --- a/src/ucioption.h +++ b/src/ucioption.h @@ -67,6 +67,7 @@ class Option { operator int() const; operator std::string() const; bool operator==(const char*) const; + bool operator!=(const char*) const; friend std::ostream& operator<<(std::ostream&, const OptionsMap&); From 41acbcae1a8af4b23be397f7fe7234f3bc49a26e Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Wed, 29 May 2024 16:14:24 +0300 Subject: [PATCH 1535/1766] Simplifying malus for putting piece en prise formula Patch author: @ehsanrashid Passed STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 116192 W: 30229 L: 30094 D: 55869 Ptnml(0-2): 451, 13880, 29351, 13911, 503 https://tests.stockfishchess.org/tests/view/66510a40a86388d5e27da936 Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 441312 W: 111009 L: 111220 D: 219083 Ptnml(0-2): 217, 49390, 121659, 49167, 223 https://tests.stockfishchess.org/tests/view/66530696a86388d5e27da9e3 closes https://github.com/official-stockfish/Stockfish/pull/5304 Bench: 1987574 --- AUTHORS | 1 + src/movepick.cpp | 12 +++++------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/AUTHORS b/AUTHORS index 36b2b6f7942..a232e115f7a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -68,6 +68,7 @@ Douglas Matos Gomes (dsmsgms) Dubslow Eduardo Cáceres (eduherminio) Eelco de Groot (KingDefender) +Ehsan Rashid (erashid) Elvin Liu (solarlight2) erbsenzaehler Ernesto Gatti diff --git a/src/movepick.cpp b/src/movepick.cpp index 7def0ce84fa..55f9ca0e802 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -197,13 +197,11 @@ void MovePicker::score() { : 0; // malus for putting piece en prise - m.value -= !(threatenedPieces & from) - ? (pt == QUEEN ? bool(to & threatenedByRook) * 48150 - + bool(to & threatenedByMinor) * 10650 - : pt == ROOK ? bool(to & threatenedByMinor) * 24335 - : pt != PAWN ? bool(to & threatenedByPawn) * 14950 - : 0) - : 0; + m.value -= (pt == QUEEN ? bool(to & threatenedByRook) * 48150 + + bool(to & threatenedByMinor) * 10650 + : pt == ROOK ? bool(to & threatenedByMinor) * 24335 + : pt != PAWN ? bool(to & threatenedByPawn) * 14950 + : 0); } else // Type == EVASIONS From c7b80f6c8a7b8267e019fc4ecb496f14f5256f3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Mon, 27 May 2024 04:32:04 +0200 Subject: [PATCH 1536/1766] Merge pawn count terms using their average This simplification patch merges the pawn count terms in the eval formula with the material term, updating the offset constant for the nnue part of the formula from 34000 to 34300 because the average pawn count in middlegame positions evaluated during search is around 8. STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 138240 W: 35834 L: 35723 D: 66683 Ptnml(0-2): 527, 16587, 34817, 16626, 563 https://tests.stockfishchess.org/tests/view/6653f474a86388d5e27daaac LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 454272 W: 114787 L: 115012 D: 224473 Ptnml(0-2): 246, 51168, 124553, 50903, 266 https://tests.stockfishchess.org/tests/view/6654f256a86388d5e27db131 closes https://github.com/official-stockfish/Stockfish/pull/5303 Bench: 1279635 --- src/evaluate.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 13a3f211741..849b7bb683d 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -77,12 +77,10 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, optimism += optimism * nnueComplexity / 470; nnue -= nnue * (nnueComplexity * 5 / 3) / 32621; - int material = 200 * pos.count() + 350 * pos.count() + 400 * pos.count() + int material = 300 * pos.count() + 350 * pos.count() + 400 * pos.count() + 640 * pos.count() + 1200 * pos.count(); - v = (nnue * (34000 + material + 135 * pos.count()) - + optimism * (4400 + material + 99 * pos.count())) - / 35967; + v = (nnue * (34300 + material) + optimism * (4400 + material)) / 35967; // Damp down the evaluation linearly when shuffling v = v * (204 - pos.rule50_count()) / 208; From c14297a483f7905d61e6f22068d33b199916257a Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Mon, 27 May 2024 01:21:32 -0700 Subject: [PATCH 1537/1766] Tune Fail Low Bonus Fractional bonus idea is from @Ergodice on [discord](https://discord.com/channels/435943710472011776/735707599353151579/1244039134499180614). Values are tuned for 149k games at LTC. SPSA tune: https://tests.stockfishchess.org/tests/view/6652d5d5a86388d5e27da9d6 Failed STC: LLR: -2.95 (-2.94,2.94) <0.00,2.00> Total: 67424 W: 17364 L: 17528 D: 32532 Ptnml(0-2): 238, 8043, 17299, 7909, 223 https://tests.stockfishchess.org/tests/view/66551e1ba86388d5e27db9f9 Passed LTC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 146910 W: 37141 L: 36695 D: 73074 Ptnml(0-2): 84, 16201, 40441, 16643, 86 https://tests.stockfishchess.org/tests/view/66559949a86388d5e27dcc5d Passed VLTC: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 27248 W: 6924 L: 6633 D: 13691 Ptnml(0-2): 5, 2744, 7835, 3035, 5 https://tests.stockfishchess.org/tests/view/66563f4da86388d5e27dd27a closes https://github.com/official-stockfish/Stockfish/pull/5299 Bench: 1390709 --- src/search.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c074e3421ea..425782ebf27 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1341,18 +1341,19 @@ Value Search::Worker::search( // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (depth > 4) + (depth > 5) + (PvNode || cutNode) + ((ss - 1)->statScore < -14144) - + ((ss - 1)->moveCount > 9) + (!ss->inCheck && bestValue <= ss->staticEval - 115) - + (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 81); + int bonus = (54 * (depth > 4) + 62 * (depth > 5) + 115 * (PvNode || cutNode) + + 186 * ((ss - 1)->statScore < -14144) + 121 * ((ss - 1)->moveCount > 9) + + 64 * (!ss->inCheck && bestValue <= ss->staticEval - 115) + + 137 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 81)); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, - stat_bonus(depth) * bonus); + stat_bonus(depth) * bonus / 100); thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] - << stat_bonus(depth) * bonus / 2; + << stat_bonus(depth) * bonus / 200; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) thisThread->pawnHistory[pawn_structure_index(pos)][pos.piece_on(prevSq)][prevSq] - << stat_bonus(depth) * bonus * 4; + << stat_bonus(depth) * bonus / 25; } if (PvNode) From a2f4e988aa03a1011b671af07a152682e35b4617 Mon Sep 17 00:00:00 2001 From: mstembera Date: Tue, 28 May 2024 13:32:09 -0700 Subject: [PATCH 1538/1766] Fix MSVC NUMA compile issues closes https://github.com/official-stockfish/Stockfish/pull/5298 No functional change --- src/misc.h | 4 ++-- src/numa.h | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/misc.h b/src/misc.h index 99cbecfdd2c..ec7f7b76c97 100644 --- a/src/misc.h +++ b/src/misc.h @@ -77,6 +77,8 @@ using AlignedPtr = std::unique_ptr>; template using LargePagePtr = std::unique_ptr>; +#if defined(__linux__) + struct PipeDeleter { void operator()(FILE* file) const { if (file != nullptr) @@ -86,8 +88,6 @@ struct PipeDeleter { } }; -#if defined(__linux__) - inline std::optional get_system_command_output(const std::string& command) { std::unique_ptr pipe(popen(command.c_str(), "r")); if (!pipe) diff --git a/src/numa.h b/src/numa.h index c04292daf01..03ee1fdf116 100644 --- a/src/numa.h +++ b/src/numa.h @@ -51,6 +51,9 @@ static constexpr size_t WIN_PROCESSOR_GROUP_SIZE = 64; #define NOMINMAX #endif #include + #if defined small + #undef small + #endif // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadselectedcpusetmasks using SetThreadSelectedCpuSetMasks_t = BOOL (*)(HANDLE, PGROUP_AFFINITY, USHORT); @@ -561,8 +564,8 @@ class NumaConfig { if (SetThreadSelectedCpuSetMasks_f != nullptr) { // Only available on Windows 11 and Windows Server 2022 onwards. - const USHORT numProcGroups = - ((highestCpuIndex + 1) + WIN_PROCESSOR_GROUP_SIZE - 1) / WIN_PROCESSOR_GROUP_SIZE; + const USHORT numProcGroups = USHORT( + ((highestCpuIndex + 1) + WIN_PROCESSOR_GROUP_SIZE - 1) / WIN_PROCESSOR_GROUP_SIZE); auto groupAffinities = std::make_unique(numProcGroups); std::memset(groupAffinities.get(), 0, sizeof(GROUP_AFFINITY) * numProcGroups); for (WORD i = 0; i < numProcGroups; ++i) From ae7eef51fde6d74f1a10269dec36bf6d80855a0a Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Tue, 28 May 2024 14:31:56 -0700 Subject: [PATCH 1539/1766] Simplify Fail Low Bonus Formula Tested against PR #5299 Passed Non-regression STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 76352 W: 19797 L: 19619 D: 36936 Ptnml(0-2): 236, 9017, 19509, 9161, 253 https://tests.stockfishchess.org/tests/view/66564f60a86388d5e27dd307 Passed Non-regression LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 114624 W: 28946 L: 28821 D: 56857 Ptnml(0-2): 59, 12675, 31714, 12810, 54 https://tests.stockfishchess.org/tests/view/6656543da86388d5e27dd329 closes https://github.com/official-stockfish/Stockfish/pull/5301 Bench: 1212167 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 425782ebf27..5e9f647636d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1341,7 +1341,7 @@ Value Search::Worker::search( // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (54 * (depth > 4) + 62 * (depth > 5) + 115 * (PvNode || cutNode) + int bonus = (116 * (depth > 5) + 115 * (PvNode || cutNode) + 186 * ((ss - 1)->statScore < -14144) + 121 * ((ss - 1)->moveCount > 9) + 64 * (!ss->inCheck && bestValue <= ss->staticEval - 115) + 137 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 81)); From 3c62ad7e077a5ed0ea7b55422e03e7316dcbce7e Mon Sep 17 00:00:00 2001 From: xoto10 <23479932+xoto10@users.noreply.github.com> Date: Tue, 28 May 2024 19:40:40 +0100 Subject: [PATCH 1540/1766] Add compensation factor to adjust extra time according to time control As stockfish nets and search evolve, the existing time control appears to give too little time at STC, roughly correct at LTC, and too little at VLTC+. This change adds an adjustment to the optExtra calculation. This adjustment is easy to retune and refine, so it should be easier to keep up-to-date than the more complex calculations used for optConstant and optScale. Passed STC 10+0.1: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 169568 W: 43803 L: 43295 D: 82470 Ptnml(0-2): 485, 19679, 44055, 19973, 592 https://tests.stockfishchess.org/tests/view/66531865a86388d5e27da9fa Yellow LTC 60+0.6: LLR: -2.94 (-2.94,2.94) <0.50,2.50> Total: 209970 W: 53087 L: 52914 D: 103969 Ptnml(0-2): 91, 19652, 65314, 19849, 79 https://tests.stockfishchess.org/tests/view/6653e38ba86388d5e27daaa0 Passed VLTC 180+1.8 : LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 85618 W: 21735 L: 21342 D: 42541 Ptnml(0-2): 15, 8267, 25848, 8668, 11 https://tests.stockfishchess.org/tests/view/6655131da86388d5e27db95f closes https://github.com/official-stockfish/Stockfish/pull/5297 Bench: 1212167 --- src/search.cpp | 2 +- src/search.h | 1 + src/thread.cpp | 1 + src/timeman.cpp | 14 ++++++++++++-- src/timeman.h | 8 ++++++-- 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5e9f647636d..ec4ae79d508 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -161,7 +161,7 @@ void Search::Worker::start_searching() { } main_manager()->tm.init(limits, rootPos.side_to_move(), rootPos.game_ply(), options, - main_manager()->originalPly); + main_manager()->originalPly, main_manager()->originalTimeAdjust); tt.new_search(); if (rootMoves.empty()) diff --git a/src/search.h b/src/search.h index a61f253c005..7cff10d5590 100644 --- a/src/search.h +++ b/src/search.h @@ -208,6 +208,7 @@ class SearchManager: public ISearchManager { Depth depth) const; Stockfish::TimeManagement tm; + double originalTimeAdjust; int originalPly; int callsCnt; std::atomic_bool ponder; diff --git a/src/thread.cpp b/src/thread.cpp index 5893f4b6d07..71134ead6ea 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -217,6 +217,7 @@ void ThreadPool::clear() { main_manager()->bestPreviousScore = VALUE_INFINITE; main_manager()->bestPreviousAverageScore = VALUE_INFINITE; main_manager()->originalPly = -1; + main_manager()->originalTimeAdjust = -1; main_manager()->previousTimeReduction = 1.0; main_manager()->tm.clear(); } diff --git a/src/timeman.cpp b/src/timeman.cpp index f389e082d8e..f6ca298a82f 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -44,8 +44,12 @@ void TimeManagement::advance_nodes_time(std::int64_t nodes) { // the bounds of time allowed for the current game ply. We currently support: // 1) x basetime (+ z increment) // 2) x moves in y seconds (+ z increment) -void TimeManagement::init( - Search::LimitsType& limits, Color us, int ply, const OptionsMap& options, int& originalPly) { +void TimeManagement::init(Search::LimitsType& limits, + Color us, + int ply, + const OptionsMap& options, + int& originalPly, + double& originalTimeAdjust) { TimePoint npmsec = TimePoint(options["nodestime"]); // If we have no time, we don't need to fully initialize TM. @@ -100,6 +104,10 @@ void TimeManagement::init( TimePoint timeLeft = std::max(TimePoint(1), limits.time[us] + limits.inc[us] * (mtg - 1) - moveOverhead * (2 + mtg)); + // Extra time according to timeLeft + if (originalTimeAdjust < 0) + originalTimeAdjust = 0.2078 + 0.1623 * std::log10(timeLeft); + // x basetime (+ z increment) // If there is a healthy increment, timeLeft can exceed the actual available // game time for the current move, so also cap to a percentage of available game time. @@ -109,6 +117,7 @@ void TimeManagement::init( double optExtra = scaledInc < 500 ? 1.0 : 1.13; if (ply - originalPly < 2) optExtra *= 0.95; + optExtra *= originalTimeAdjust; // Calculate time constants based on current time left. double logTimeInSec = std::log10(scaledTime / 1000.0); @@ -118,6 +127,7 @@ void TimeManagement::init( optScale = std::min(0.0122 + std::pow(ply + 2.95, 0.462) * optConstant, 0.213 * limits.time[us] / timeLeft) * optExtra; + maxScale = std::min(6.64, maxConstant + ply / 12.0); } diff --git a/src/timeman.h b/src/timeman.h index 8f1bb56397d..8b763089a70 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -36,8 +36,12 @@ struct LimitsType; // the maximum available time, the game move number, and other parameters. class TimeManagement { public: - void init( - Search::LimitsType& limits, Color us, int ply, const OptionsMap& options, int& originalPly); + void init(Search::LimitsType& limits, + Color us, + int ply, + const OptionsMap& options, + int& originalPly, + double& originalTimeAdjust); TimePoint optimum() const; TimePoint maximum() const; From 4a2291ed337730e5093af1532d36acf1f066989b Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Thu, 23 May 2024 19:22:41 -0700 Subject: [PATCH 1541/1766] Simplify Away Quadruple Extension Passed non-regression VVLTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 90792 W: 23155 L: 23018 D: 44619 Ptnml(0-2): 6, 8406, 28432, 8549, 3 https://tests.stockfishchess.org/tests/view/664ffa4ca86388d5e27d8e7a Passed non-regression VLTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 288136 W: 72608 L: 72659 D: 142869 Ptnml(0-2): 38, 30258, 83525, 30211, 36 https://tests.stockfishchess.org/tests/view/66551609a86388d5e27db9ae closes https://github.com/official-stockfish/Stockfish/pull/5293 bench 1501735 --- src/search.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ec4ae79d508..22e82be8f2b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1066,11 +1066,9 @@ Value Search::Worker::search( { int doubleMargin = 304 * PvNode - 203 * !ttCapture; int tripleMargin = 117 + 259 * PvNode - 296 * !ttCapture + 97 * ss->ttPv; - int quadMargin = 486 + 343 * PvNode - 273 * !ttCapture + 232 * ss->ttPv; extension = 1 + (value < singularBeta - doubleMargin) - + (value < singularBeta - tripleMargin) - + (value < singularBeta - quadMargin); + + (value < singularBeta - tripleMargin); depth += ((!PvNode) && (depth < 16)); } From 5ab3fe6db8ea7dff1310c792d66f2a906a5c19c5 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Tue, 28 May 2024 19:39:55 -0400 Subject: [PATCH 1542/1766] Simplify blending eval with nnue complexity Passed non-regression STC: https://tests.stockfishchess.org/tests/view/66567377a86388d5e27dd89c LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 144000 W: 37443 L: 37338 D: 69219 Ptnml(0-2): 587, 17260, 36208, 17351, 594 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/66567f29a86388d5e27dd924 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 112326 W: 28550 L: 28421 D: 55355 Ptnml(0-2): 66, 12732, 30434, 12869, 62 closes https://github.com/official-stockfish/Stockfish/pull/5305 bench 1554486 --- src/evaluate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 849b7bb683d..666697dddba 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -75,7 +75,7 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, // Blend optimism and eval with nnue complexity optimism += optimism * nnueComplexity / 470; - nnue -= nnue * (nnueComplexity * 5 / 3) / 32621; + nnue -= nnue * nnueComplexity / 20000; int material = 300 * pos.count() + 350 * pos.count() + 400 * pos.count() + 640 * pos.count() + 1200 * pos.count(); From 0ea6337ccfffa39b665e3a8371fcde668dddf4aa Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Thu, 30 May 2024 03:36:38 +0300 Subject: [PATCH 1543/1766] Remove Queen threatenedByMinor Remove Queen threatenedByMinor from movepick Passed STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 54432 W: 14053 L: 13855 D: 26524 Ptnml(0-2): 124, 6347, 14090, 6517, 138 https://tests.stockfishchess.org/tests/view/66578d036b0e318cefa8d43d Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 198168 W: 49979 L: 49940 D: 98249 Ptnml(0-2): 84, 21824, 55236, 21849, 91 https://tests.stockfishchess.org/tests/view/66579cf86b0e318cefa8d5b1 closes https://github.com/official-stockfish/Stockfish/pull/5306 bench: 1342438 --- src/movepick.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 55f9ca0e802..6c41916cd96 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -197,8 +197,7 @@ void MovePicker::score() { : 0; // malus for putting piece en prise - m.value -= (pt == QUEEN ? bool(to & threatenedByRook) * 48150 - + bool(to & threatenedByMinor) * 10650 + m.value -= (pt == QUEEN ? bool(to & threatenedByRook) * 49000 : pt == ROOK ? bool(to & threatenedByMinor) * 24335 : pt != PAWN ? bool(to & threatenedByPawn) * 14950 : 0); From 35aff79843658aef55426d5d88be412f54d936b8 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Wed, 29 May 2024 21:18:55 -0400 Subject: [PATCH 1544/1766] Update default main net to nn-ddcfb9224cdb.nnue Created by further tuning the spsa-tuned main net `nn-c721dfca8cd3.nnue` with the same methods described in https://github.com/official-stockfish/Stockfish/pull/5254 This net was reached at 61k / 120k spsa games at 70+0.7 th 7: https://tests.stockfishchess.org/tests/view/665639d0a86388d5e27dd259 Passed STC: https://tests.stockfishchess.org/tests/view/6657d44e6b0e318cefa8d771 LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 114688 W: 29775 L: 29344 D: 55569 Ptnml(0-2): 274, 13633, 29149, 13964, 324 Passed LTC: https://tests.stockfishchess.org/tests/view/6657e1e46b0e318cefa8d7a6 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 88152 W: 22412 L: 21988 D: 43752 Ptnml(0-2): 56, 9560, 24409, 10006, 45 closes https://github.com/official-stockfish/Stockfish/pull/5308 Bench: 1434678 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 4b3e91acf4c..4fab1a00137 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -33,7 +33,7 @@ namespace Eval { // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro or the location where this macro is defined, as it is used // in the Makefile/Fishtest. -#define EvalFileDefaultNameBig "nn-c721dfca8cd3.nnue" +#define EvalFileDefaultNameBig "nn-ddcfb9224cdb.nnue" #define EvalFileDefaultNameSmall "nn-baff1ede1f90.nnue" namespace NNUE { From a4ea183e7839f62665e706c13b508ccce86d5fd6 Mon Sep 17 00:00:00 2001 From: "Robert Nurnberg @ elitebook" Date: Thu, 30 May 2024 09:05:36 +0200 Subject: [PATCH 1545/1766] Tweak and update the WDL model This PR updates the internal WDL model, using data from 2.5M games played by SF-dev (3c62ad7). Note that the normalizing constant has increased from 329 to 368. Changes to the fitting procedure: * the value for --materialMin was increased from 10 to 17: including data with less material leads to less accuracy for larger material count values * the data was filtered to only include single thread LTC games at 60+0.6 * the data was filtered to only include games from master against patches that are (approximatively) within 5 nElo of master For more information and plots of the model see PR#5309 closes https://github.com/official-stockfish/Stockfish/pull/5309 No functional change --- src/uci.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/uci.cpp b/src/uci.cpp index ab0dae3946e..4b683116a9e 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -382,12 +382,12 @@ WinRateParams win_rate_params(const Position& pos) { int material = pos.count() + 3 * pos.count() + 3 * pos.count() + 5 * pos.count() + 9 * pos.count(); - // The fitted model only uses data for material counts in [10, 78], and is anchored at count 58. - double m = std::clamp(material, 10, 78) / 58.0; + // The fitted model only uses data for material counts in [17, 78], and is anchored at count 58. + double m = std::clamp(material, 17, 78) / 58.0; // Return a = p_a(material) and b = p_b(material), see github.com/official-stockfish/WDL_model - constexpr double as[] = {-150.77043883, 394.96159472, -321.73403766, 406.15850091}; - constexpr double bs[] = {62.33245393, -91.02264855, 45.88486850, 51.63461272}; + constexpr double as[] = {-41.25712052, 121.47473115, -124.46958843, 411.84490997}; + constexpr double bs[] = {84.92998051, -143.66658718, 80.09988253, 49.80869370}; double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3]; double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3]; @@ -428,8 +428,8 @@ std::string UCIEngine::format_score(const Score& s) { // without treatment of mate and similar special scores. int UCIEngine::to_cp(Value v, const Position& pos) { - // In general, the score can be defined via the the WDL as - // (log(1/L - 1) - log(1/W - 1)) / ((log(1/L - 1) + log(1/W - 1)) + // In general, the score can be defined via the WDL as + // (log(1/L - 1) - log(1/W - 1)) / (log(1/L - 1) + log(1/W - 1)). // Based on our win_rate_model, this simply yields v / a. auto [a, b] = win_rate_params(pos); From a77a895c3b7460f86b11a3ddfe3528f5be1276b9 Mon Sep 17 00:00:00 2001 From: Viren6 <94880762+Viren6@users.noreply.github.com> Date: Thu, 30 May 2024 08:18:04 +0100 Subject: [PATCH 1546/1766] Add extension condition to cutoffCnt Decrease cutoffCnt increment by 1 if extension is 2 or greater. Passed STC: https://tests.stockfishchess.org/tests/view/66577a696b0e318cefa8d34d LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 99200 W: 25703 L: 25297 D: 48200 Ptnml(0-2): 253, 11660, 25390, 12022, 275 Passed LTC: https://tests.stockfishchess.org/tests/view/665787ab6b0e318cefa8d411 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 124530 W: 31659 L: 31161 D: 61710 Ptnml(0-2): 58, 13578, 34489, 14088, 52 closes https://github.com/official-stockfish/Stockfish/pull/5310 bench 1623228 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 22e82be8f2b..d72dbfa1548 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1289,7 +1289,7 @@ Value Search::Worker::search( if (value >= beta) { - ss->cutoffCnt += 1 + !ttMove; + ss->cutoffCnt += 1 + !ttMove - (extension >= 2); assert(value >= beta); // Fail high break; } From d1a71fdaa7cc7d749495bbf5d63919a4a0b42303 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sat, 18 May 2024 17:54:13 +0300 Subject: [PATCH 1547/1766] Functional simplification in the transposition table Passed STC: LLR: 2.98 (-2.94,2.94) <-1.75,0.25> Total: 154848 W: 39838 L: 39750 D: 75260 Ptnml(0-2): 404, 16214, 44087, 16328, 391 https://tests.stockfishchess.org/tests/view/664892b088b8c6a2bbe430fc Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 68172 W: 17296 L: 17137 D: 33739 Ptnml(0-2): 23, 6349, 21185, 6504, 25 https://tests.stockfishchess.org/tests/view/6648aabfa0781149e383e526 closes https://github.com/official-stockfish/Stockfish/pull/5263 Bench: 1623228 --- src/tt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tt.cpp b/src/tt.cpp index 79274f525b9..f95170e9406 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -124,7 +124,7 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const { const uint16_t key16 = uint16_t(key); // Use the low 16 bits as key inside the cluster for (int i = 0; i < ClusterSize; ++i) - if (tte[i].key16 == key16 || !tte[i].depth8) + if (tte[i].key16 == key16) return found = bool(tte[i].depth8), &tte[i]; // Find an entry to be replaced according to the replacement strategy From b280d2f06553e8c8d98379fe547f3b995cc56d59 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Thu, 30 May 2024 19:27:12 +0300 Subject: [PATCH 1548/1766] Allow tt cutoffs for shallower depths in certain conditions Current master allows tt cutoffs only when depth from tt is strictly greater than current node depth. This patch also allows them when it's equal and if tt value is lower or equal to beta. Passed STC: https://tests.stockfishchess.org/tests/view/66578e2e6b0e318cefa8d447 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 26592 W: 6944 L: 6645 D: 13003 Ptnml(0-2): 67, 3039, 6795, 3318, 77 Passed LTC: https://tests.stockfishchess.org/tests/view/6657f46b6b0e318cefa8d7e9 LLR: 2.96 (-2.94,2.94) <0.50,2.50> Total: 142572 W: 36315 L: 35776 D: 70481 Ptnml(0-2): 70, 15666, 39288, 16179, 83 closes https://github.com/official-stockfish/Stockfish/pull/5314 Bench: 1368486 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index d72dbfa1548..638af546d09 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -618,7 +618,7 @@ Value Search::Worker::search( ss->ttPv = PvNode || (ss->ttHit && tte->is_pv()); // At non-PV nodes we check for an early TT cutoff - if (!PvNode && !excludedMove && tte->depth() > depth + if (!PvNode && !excludedMove && tte->depth() > depth - (ttValue <= beta) && ttValue != VALUE_NONE // Possible in case of TT access race or if !ttHit && (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER))) { From 02eae528330347b4c91f3d8fa4de7fc8629a5ac0 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Thu, 30 May 2024 20:44:21 +0300 Subject: [PATCH 1549/1766] Simplifying the malus for putting piece en prise formula Simplifying the malus for putting piece en prise formula by merging the minor pieces and pawns (removing the pawn exclusion from the formula). Passed STC: https://tests.stockfishchess.org/tests/view/66578d9c6b0e318cefa8d441 LLR: 2.99 (-2.94,2.94) <-1.75,0.25> Total: 314272 W: 80705 L: 80786 D: 152781 Ptnml(0-2): 873, 37577, 80366, 37398, 922 Passed LTC (before rebasing): https://tests.stockfishchess.org/tests/view/6657b5ee6b0e318cefa8d6ab LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 117000 W: 29447 L: 29324 D: 58229 Ptnml(0-2): 47, 12877, 32535, 12988, 53 Passed LTC (also after rebasing): https://tests.stockfishchess.org/tests/view/6658803d6b0e318cefa8fd99 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 244992 W: 61807 L: 61814 D: 121371 Ptnml(0-2): 125, 27420, 67414, 27411, 126 closes https://github.com/official-stockfish/Stockfish/pull/5316 Bench: 1484840 --- src/movepick.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 6c41916cd96..b6828a30b72 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -199,8 +199,7 @@ void MovePicker::score() { // malus for putting piece en prise m.value -= (pt == QUEEN ? bool(to & threatenedByRook) * 49000 : pt == ROOK ? bool(to & threatenedByMinor) * 24335 - : pt != PAWN ? bool(to & threatenedByPawn) * 14950 - : 0); + : bool(to & threatenedByPawn) * 14900); } else // Type == EVASIONS From 596fb4842bdbb872dae8023a930f1dda8b48cad1 Mon Sep 17 00:00:00 2001 From: Disservin Date: Thu, 30 May 2024 19:55:59 +0200 Subject: [PATCH 1550/1766] NUMA: Fix concurrency counting for windows systems If there is more than 1 processor group, std::thread::hardware_concurrency should not be used. fixes #5307 closes https://github.com/official-stockfish/Stockfish/pull/5311 No functional change --- src/misc.cpp | 41 ++++++++++++++++++----------------------- src/numa.h | 25 +++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 25 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index d48b75e1c28..a45becf5d99 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -34,16 +34,10 @@ // the calls at compile time), try to load them at runtime. To do this we need // first to define the corresponding function pointers. extern "C" { -using fun1_t = bool (*)(LOGICAL_PROCESSOR_RELATIONSHIP, - PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, - PDWORD); -using fun2_t = bool (*)(USHORT, PGROUP_AFFINITY); -using fun3_t = bool (*)(HANDLE, CONST GROUP_AFFINITY*, PGROUP_AFFINITY); -using fun4_t = bool (*)(USHORT, PGROUP_AFFINITY, USHORT, PUSHORT); -using fun5_t = WORD (*)(); -using fun6_t = bool (*)(HANDLE, DWORD, PHANDLE); -using fun7_t = bool (*)(LPCSTR, LPCSTR, PLUID); -using fun8_t = bool (*)(HANDLE, BOOL, PTOKEN_PRIVILEGES, DWORD, PTOKEN_PRIVILEGES, PDWORD); +using OpenProcessToken_t = bool (*)(HANDLE, DWORD, PHANDLE); +using LookupPrivilegeValueA_t = bool (*)(LPCSTR, LPCSTR, PLUID); +using AdjustTokenPrivileges_t = + bool (*)(HANDLE, BOOL, PTOKEN_PRIVILEGES, DWORD, PTOKEN_PRIVILEGES, PDWORD); } #endif @@ -488,23 +482,25 @@ static void* aligned_large_pages_alloc_windows([[maybe_unused]] size_t allocSize if (!hAdvapi32) hAdvapi32 = LoadLibrary(TEXT("advapi32.dll")); - auto fun6 = fun6_t((void (*)()) GetProcAddress(hAdvapi32, "OpenProcessToken")); - if (!fun6) + auto OpenProcessToken_f = + OpenProcessToken_t((void (*)()) GetProcAddress(hAdvapi32, "OpenProcessToken")); + if (!OpenProcessToken_f) return nullptr; - auto fun7 = fun7_t((void (*)()) GetProcAddress(hAdvapi32, "LookupPrivilegeValueA")); - if (!fun7) + auto LookupPrivilegeValueA_f = + LookupPrivilegeValueA_t((void (*)()) GetProcAddress(hAdvapi32, "LookupPrivilegeValueA")); + if (!LookupPrivilegeValueA_f) return nullptr; - auto fun8 = fun8_t((void (*)()) GetProcAddress(hAdvapi32, "AdjustTokenPrivileges")); - if (!fun8) + auto AdjustTokenPrivileges_f = + AdjustTokenPrivileges_t((void (*)()) GetProcAddress(hAdvapi32, "AdjustTokenPrivileges")); + if (!AdjustTokenPrivileges_f) return nullptr; // We need SeLockMemoryPrivilege, so try to enable it for the process - if (!fun6( // OpenProcessToken() + if (!OpenProcessToken_f( // OpenProcessToken() GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken)) return nullptr; - if (fun7( // LookupPrivilegeValue(nullptr, SE_LOCK_MEMORY_NAME, &luid) - nullptr, "SeLockMemoryPrivilege", &luid)) + if (LookupPrivilegeValueA_f(nullptr, "SeLockMemoryPrivilege", &luid)) { TOKEN_PRIVILEGES tp{}; TOKEN_PRIVILEGES prevTp{}; @@ -516,8 +512,8 @@ static void* aligned_large_pages_alloc_windows([[maybe_unused]] size_t allocSize // Try to enable SeLockMemoryPrivilege. Note that even if AdjustTokenPrivileges() succeeds, // we still need to query GetLastError() to ensure that the privileges were actually obtained. - if (fun8( // AdjustTokenPrivileges() - hProcessToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &prevTp, &prevTpLen) + if (AdjustTokenPrivileges_f(hProcessToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &prevTp, + &prevTpLen) && GetLastError() == ERROR_SUCCESS) { // Round up size to full pages and allocate @@ -526,8 +522,7 @@ static void* aligned_large_pages_alloc_windows([[maybe_unused]] size_t allocSize PAGE_READWRITE); // Privilege no longer needed, restore previous state - fun8( // AdjustTokenPrivileges () - hProcessToken, FALSE, &prevTp, 0, nullptr, nullptr); + AdjustTokenPrivileges_f(hProcessToken, FALSE, &prevTp, 0, nullptr, nullptr); } } diff --git a/src/numa.h b/src/numa.h index 03ee1fdf116..644f212ee70 100644 --- a/src/numa.h +++ b/src/numa.h @@ -61,6 +61,7 @@ using SetThreadSelectedCpuSetMasks_t = BOOL (*)(HANDLE, PGROUP_AFFINITY, USHORT) // https://learn.microsoft.com/en-us/windows/win32/api/processtopologyapi/nf-processtopologyapi-setthreadgroupaffinity using SetThreadGroupAffinity_t = BOOL (*)(HANDLE, const GROUP_AFFINITY*, PGROUP_AFFINITY); +using GetActiveProcessorCount_t = DWORD (*)(WORD); #endif #include "misc.h" @@ -70,8 +71,28 @@ namespace Stockfish { using CpuIndex = size_t; using NumaIndex = size_t; -inline const CpuIndex SYSTEM_THREADS_NB = - std::max(1, std::thread::hardware_concurrency()); +inline CpuIndex get_hardware_concurrency() { + CpuIndex concurrency = std::thread::hardware_concurrency(); + + // Get all processors across all processor groups on windows, since ::hardware_concurrency + // only returns the number of processors in the first group, because only these + // are available to std::thread. +#ifdef _WIN64 + HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll")); + auto GetActiveProcessorCount_f = + GetActiveProcessorCount_t((void (*)()) GetProcAddress(k32, "GetActiveProcessorCount")); + + if (GetActiveProcessorCount_f != nullptr) + { + concurrency = GetActiveProcessorCount_f(ALL_PROCESSOR_GROUPS); + } +#endif + + return concurrency; +} + +inline const CpuIndex SYSTEM_THREADS_NB = std::max(1, get_hardware_concurrency()); + // We want to abstract the purpose of storing the numa node index somewhat. // Whoever is using this does not need to know the specifics of the replication From f1bb4164bf481c44e707751aa8a4bb8da20d4fa1 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Thu, 30 May 2024 12:56:44 +0200 Subject: [PATCH 1551/1766] Fix process' processor affinity determination on Windows. Specialize and privatize NumaConfig::get_process_affinity. Only enable NUMA capability for 64-bit Windows. Following #5307 and some more testing it was determined that the way affinity was being determined on Windows was incorrect, based on incorrect assumptions about GetNumaProcessorNodeEx. This patch fixes the issue by attempting to retrieve the actual process' processor affinity using Windows API. However one issue persists that is not addressable due to limitations of Windows, and will have to be considered a limitation. If affinities were set using SetThreadAffinityMask instead of SetThreadSelectedCpuSetMasks and GetProcessGroupAffinity returns more than 1 group it is NOT POSSIBLE to determine the affinity programmatically on Windows. In such case the implementation assumes no affinites are set and will consider all processors available for execution. closes https://github.com/official-stockfish/Stockfish/pull/5312 No functional change --- src/numa.h | 260 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 167 insertions(+), 93 deletions(-) diff --git a/src/numa.h b/src/numa.h index 644f212ee70..3c9c823aa39 100644 --- a/src/numa.h +++ b/src/numa.h @@ -41,7 +41,7 @@ #define _GNU_SOURCE #endif #include -#elif defined(_WIN32) +#elif defined(_WIN64) // On Windows each processor group can have up to 64 processors. // https://learn.microsoft.com/en-us/windows/win32/procthread/processor-groups @@ -61,7 +61,18 @@ using SetThreadSelectedCpuSetMasks_t = BOOL (*)(HANDLE, PGROUP_AFFINITY, USHORT) // https://learn.microsoft.com/en-us/windows/win32/api/processtopologyapi/nf-processtopologyapi-setthreadgroupaffinity using SetThreadGroupAffinity_t = BOOL (*)(HANDLE, const GROUP_AFFINITY*, PGROUP_AFFINITY); +// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getthreadselectedcpusetmasks +using GetThreadSelectedCpuSetMasks_t = BOOL (*)(HANDLE, PGROUP_AFFINITY, USHORT, PUSHORT); + +// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getprocessaffinitymask +using GetProcessAffinityMask_t = BOOL (*)(HANDLE, PDWORD_PTR, PDWORD_PTR); + +// https://learn.microsoft.com/en-us/windows/win32/api/processtopologyapi/nf-processtopologyapi-getprocessgroupaffinity +using GetProcessGroupAffinity_t = BOOL (*)(HANDLE, PUSHORT, PUSHORT); + +// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getactiveprocessorcount using GetActiveProcessorCount_t = DWORD (*)(WORD); + #endif #include "misc.h" @@ -115,8 +126,6 @@ class NumaReplicatedAccessToken { // in a way that doesn't require recreating it completely, and it would be complex and expensive // to maintain class invariants. // The CPU (processor) numbers always correspond to the actual numbering used by the system. -// NOTE: the numbering is only valid within the process, as for example on Windows -// every process gets a "virtualized" set of processors that respects the current affinity // The NUMA node numbers MAY NOT correspond to the system's numbering of the NUMA nodes. // In particular, empty nodes may be removed, or the user may create custom nodes. // It is guaranteed that NUMA nodes are NOT empty, i.e. every node exposed by NumaConfig @@ -133,92 +142,21 @@ class NumaConfig { add_cpu_range_to_node(NumaIndex{0}, CpuIndex{0}, numCpus - 1); } - static std::set get_process_affinity() { - std::set cpus; - - // For unsupported systems, or in case of a soft error, we may assume all processors - // are available for use. - [[maybe_unused]] auto set_to_all_cpus = [&]() { - for (CpuIndex c = 0; c < SYSTEM_THREADS_NB; ++c) - cpus.insert(c); - }; - -#if defined(__linux__) && !defined(__ANDROID__) - - // cpu_set_t by default holds 1024 entries. This may not be enough soon, - // but there is no easy way to determine how many threads there actually is. - // In this case we just choose a reasonable upper bound. - static constexpr CpuIndex MaxNumCpus = 1024 * 64; - - cpu_set_t* mask = CPU_ALLOC(MaxNumCpus); - if (mask == nullptr) - std::exit(EXIT_FAILURE); - - const size_t masksize = CPU_ALLOC_SIZE(MaxNumCpus); - - CPU_ZERO_S(masksize, mask); - - const int status = sched_getaffinity(0, masksize, mask); - - if (status != 0) - { - CPU_FREE(mask); - std::exit(EXIT_FAILURE); - } - - for (CpuIndex c = 0; c < MaxNumCpus; ++c) - if (CPU_ISSET_S(c, masksize, mask)) - cpus.insert(c); - - CPU_FREE(mask); - -#elif defined(_WIN32) - - // Windows is problematic and weird due to multiple ways of setting affinity, processor groups, - // and behaviour changes between versions. It's unclear if we can support this feature - // on Windows in the same way we do on Linux. - // Apparently when affinity is set via either start /affinity or msys2 taskset - // the function GetNumaProcessorNodeEx completely disregards the processors that we do not - // have affinity more. Moreover, the indices are shifted to start from 0, indicating that Windows - // is providing a whole new mapping of processors to this process. This is problematic in some cases - // but it at least allows us to [probably] support this affinity restriction feature by default. - // So overall, Windows appears to "virtualize" a set of processors and processor groups for every - // process. It's unclear if this assignment can change while the process is running. - // std::thread::hardware_concurrency() returns the number of processors that's consistent - // with GetNumaProcessorNodeEx, so we can just add all of them. - - set_to_all_cpus(); - -#else - - // For other systems we assume the process is allowed to execute on all processors. - set_to_all_cpus(); - -#endif - - return cpus; - } - // This function queries the system for the mapping of processors to NUMA nodes. // On Linux we utilize `lscpu` to avoid libnuma. - // On Windows we utilize GetNumaProcessorNodeEx, which has its quirks, see - // comment for Windows implementation of get_process_affinity - static NumaConfig from_system(bool respectProcessAffinity = true) { + static NumaConfig from_system([[maybe_unused]] bool respectProcessAffinity = true) { NumaConfig cfg = empty(); +#if defined(__linux__) && !defined(__ANDROID__) + std::set allowedCpus; if (respectProcessAffinity) allowedCpus = get_process_affinity(); - else - { - for (CpuIndex c = 0; c < SYSTEM_THREADS_NB; ++c) - allowedCpus.insert(c); - } - auto is_cpu_allowed = [&](CpuIndex c) { return allowedCpus.count(c) == 1; }; - -#if defined(__linux__) && !defined(__ANDROID__) + auto is_cpu_allowed = [respectProcessAffinity, &allowedCpus](CpuIndex c) { + return !respectProcessAffinity || allowedCpus.count(c) == 1; + }; // On Linux things are straightforward, since there's no processor groups and // any thread can be scheduled on all processors. @@ -270,7 +208,19 @@ class NumaConfig { cfg.add_cpu_to_node(NumaIndex{0}, c); } -#elif defined(_WIN32) +#elif defined(_WIN64) + + std::optional> allowedCpus; + + if (respectProcessAffinity) + allowedCpus = get_process_affinity(); + + // The affinity can't be determined in all cases on Windows, but we at least guarantee + // that the number of allowed processors is >= number of processors in the affinity mask. + // In case the user is not satisfied they must set the processor numbers explicitly. + auto is_cpu_allowed = [&allowedCpus](CpuIndex c) { + return !allowedCpus.has_value() || allowedCpus->count(c) == 1; + }; // Since Windows 11 and Windows Server 2022 thread affinities can span // processor groups and can be set as such by a new WinAPI function. @@ -292,14 +242,6 @@ class NumaConfig { procnum.Reserved = 0; USHORT nodeNumber; - // When start /affinity or taskset was used to run this process with restricted affinity - // GetNumaProcessorNodeEx will NOT correspond to the system's processor setup, instead - // it appears to follow a completely new processor assignment, made specifically for this process, - // in which processors that this process has affinity for are remapped, and only those are remapped, - // to form a new set of processors. In other words, we can only get processors - // which we have affinity for this way. This means that the behaviour for - // `respectProcessAffinity == false` may be unexpected when affinity is set from outside, - // while the behaviour for `respectProcessAffinity == true` is given by default. const BOOL status = GetNumaProcessorNodeEx(&procnum, &nodeNumber); const CpuIndex c = static_cast(procGroup) * WIN_PROCESSOR_GROUP_SIZE + static_cast(number); @@ -347,8 +289,7 @@ class NumaConfig { // Fallback for unsupported systems. for (CpuIndex c = 0; c < SYSTEM_THREADS_NB; ++c) - if (is_cpu_allowed(c)) - cfg.add_cpu_to_node(NumaIndex{0}, c); + cfg.add_cpu_to_node(NumaIndex{0}, c); #endif @@ -573,7 +514,7 @@ class NumaConfig { // This is defensive, allowed because this code is not performance critical. sched_yield(); -#elif defined(_WIN32) +#elif defined(_WIN64) // Requires Windows 11. No good way to set thread affinity spanning processor groups before that. HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll")); @@ -627,9 +568,9 @@ class NumaConfig { // See https://learn.microsoft.com/en-us/windows/win32/procthread/numa-support GROUP_AFFINITY affinity; std::memset(&affinity, 0, sizeof(GROUP_AFFINITY)); - affinity.Group = static_cast(n); // We use an ordered set so we're guaranteed to get the smallest cpu number here. const size_t forcedProcGroupIndex = *(nodes[n].begin()) / WIN_PROCESSOR_GROUP_SIZE; + affinity.Group = static_cast(forcedProcGroupIndex); for (CpuIndex c : nodes[n]) { const size_t procGroupIndex = c / WIN_PROCESSOR_GROUP_SIZE; @@ -733,6 +674,139 @@ class NumaConfig { return true; } + + +#if defined(__linux__) && !defined(__ANDROID__) + + static std::set get_process_affinity() { + + std::set cpus; + + // For unsupported systems, or in case of a soft error, we may assume all processors + // are available for use. + [[maybe_unused]] auto set_to_all_cpus = [&]() { + for (CpuIndex c = 0; c < SYSTEM_THREADS_NB; ++c) + cpus.insert(c); + }; + + // cpu_set_t by default holds 1024 entries. This may not be enough soon, + // but there is no easy way to determine how many threads there actually is. + // In this case we just choose a reasonable upper bound. + static constexpr CpuIndex MaxNumCpus = 1024 * 64; + + cpu_set_t* mask = CPU_ALLOC(MaxNumCpus); + if (mask == nullptr) + std::exit(EXIT_FAILURE); + + const size_t masksize = CPU_ALLOC_SIZE(MaxNumCpus); + + CPU_ZERO_S(masksize, mask); + + const int status = sched_getaffinity(0, masksize, mask); + + if (status != 0) + { + CPU_FREE(mask); + std::exit(EXIT_FAILURE); + } + + for (CpuIndex c = 0; c < MaxNumCpus; ++c) + if (CPU_ISSET_S(c, masksize, mask)) + cpus.insert(c); + + CPU_FREE(mask); + + return cpus; + } + +#elif defined(_WIN64) + + // On Windows there are two ways to set affinity, and therefore 2 ways to get it. + // These are not consistent, so we have to check both. + // In some cases it is actually not possible to determine affinity. + // For example when two different threads have affinity on different processor groups, + // set using SetThreadAffinityMask, we can't retrieve the actual affinities. + // From documentation on GetProcessAffinityMask: + // > If the calling process contains threads in multiple groups, + // > the function returns zero for both affinity masks. + // In such cases we just give up and assume we have affinity for all processors. + // nullopt means no affinity is set, that is, all processors are allowed + static std::optional> get_process_affinity() { + HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll")); + auto GetThreadSelectedCpuSetMasks_f = GetThreadSelectedCpuSetMasks_t( + (void (*)()) GetProcAddress(k32, "GetThreadSelectedCpuSetMasks")); + auto GetProcessAffinityMask_f = + GetProcessAffinityMask_t((void (*)()) GetProcAddress(k32, "GetProcessAffinityMask")); + auto GetProcessGroupAffinity_f = + GetProcessGroupAffinity_t((void (*)()) GetProcAddress(k32, "GetProcessGroupAffinity")); + + if (GetThreadSelectedCpuSetMasks_f != nullptr) + { + std::set cpus; + + USHORT RequiredMaskCount; + GetThreadSelectedCpuSetMasks_f(GetCurrentThread(), nullptr, 0, &RequiredMaskCount); + + // If RequiredMaskCount then these affinities were never set, but it's not consistent + // so GetProcessAffinityMask may still return some affinity. + if (RequiredMaskCount > 0) + { + auto groupAffinities = std::make_unique(RequiredMaskCount); + + GetThreadSelectedCpuSetMasks_f(GetCurrentThread(), groupAffinities.get(), + RequiredMaskCount, &RequiredMaskCount); + + for (USHORT i = 0; i < RequiredMaskCount; ++i) + { + const size_t procGroupIndex = groupAffinities[i].Group; + + for (size_t j = 0; j < WIN_PROCESSOR_GROUP_SIZE; ++j) + { + if (groupAffinities[i].Mask & (KAFFINITY(1) << j)) + cpus.insert(procGroupIndex * WIN_PROCESSOR_GROUP_SIZE + j); + } + } + + return cpus; + } + } + + if (GetProcessAffinityMask_f != nullptr && GetProcessGroupAffinity_f != nullptr) + { + std::set cpus; + + DWORD_PTR proc, sys; + BOOL status = GetProcessAffinityMask_f(GetCurrentProcess(), &proc, &sys); + if (status == 0) + return std::nullopt; + + // We can't determine affinity because it spans processor groups. + if (proc == 0) + return std::nullopt; + + // We are expecting a single group. + USHORT GroupCount = 1; + USHORT GroupArray[1]; + status = GetProcessGroupAffinity_f(GetCurrentProcess(), &GroupCount, GroupArray); + if (status == 0 || GroupCount != 1) + return std::nullopt; + + const size_t procGroupIndex = GroupArray[0]; + + uint64_t mask = static_cast(proc); + for (size_t j = 0; j < WIN_PROCESSOR_GROUP_SIZE; ++j) + { + if (mask & (KAFFINITY(1) << j)) + cpus.insert(procGroupIndex * WIN_PROCESSOR_GROUP_SIZE + j); + } + + return cpus; + } + + return std::nullopt; + } + +#endif }; class NumaReplicationContext; From 86694b5914c63ee5b0f964108cbd7eacca14c93a Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Thu, 30 May 2024 18:18:51 +0200 Subject: [PATCH 1552/1766] Replace std::from_chars with std::stoull the former was not widely supported, requiring newer compiler versions. closes https://github.com/official-stockfish/Stockfish/pull/5313 No functional change --- src/misc.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index a45becf5d99..7a44732944a 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -42,16 +42,15 @@ using AdjustTokenPrivileges_t = #endif #include -#include #include #include #include #include #include +#include #include #include #include -#include #include "types.h" @@ -598,13 +597,10 @@ void aligned_large_pages_free(void* mem) { std_aligned_free(mem); } #endif size_t str_to_size_t(const std::string& s) { - size_t value; - auto result = std::from_chars(s.data(), s.data() + s.size(), value); - - if (result.ec != std::errc()) + unsigned long long value = std::stoull(s); + if (value > std::numeric_limits::max()) std::exit(EXIT_FAILURE); - - return value; + return static_cast(value); } std::string CommandLine::get_binary_directory(std::string argv0) { From c8375c2fbd398f07b8488ae2d1b12fa1251fb69f Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Thu, 30 May 2024 17:22:53 +0200 Subject: [PATCH 1553/1766] On linux use sysfs instead of lscpu Use sysfs (https://www.kernel.org/doc/Documentation/ABI/stable/sysfs-devices-node) to determine processor to NUMA node mapping. Avoids problems on some machines with high core count where lscpu was showing high cpu utilization. closes https://github.com/official-stockfish/Stockfish/pull/5315 No functional change --- src/misc.cpp | 13 +++++ src/misc.h | 24 ++++----- src/numa.h | 142 ++++++++++++++++++++++++++++----------------------- 3 files changed, 101 insertions(+), 78 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index 7a44732944a..aa22e61f23f 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -42,12 +42,14 @@ using AdjustTokenPrivileges_t = #endif #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -603,6 +605,17 @@ size_t str_to_size_t(const std::string& s) { return static_cast(value); } +std::optional read_file_to_string(const std::string& path) { + std::ifstream f(path, std::ios_base::binary); + if (!f) + return std::nullopt; + return std::string(std::istreambuf_iterator(f), std::istreambuf_iterator()); +} + +void remove_whitespace(std::string& s) { + s.erase(std::remove_if(s.begin(), s.end(), [](char c) { return std::isspace(c); }), s.end()); +} + std::string CommandLine::get_binary_directory(std::string argv0) { std::string pathSeparator; diff --git a/src/misc.h b/src/misc.h index ec7f7b76c97..5c0bde44eda 100644 --- a/src/misc.h +++ b/src/misc.h @@ -88,21 +88,12 @@ struct PipeDeleter { } }; -inline std::optional get_system_command_output(const std::string& command) { - std::unique_ptr pipe(popen(command.c_str(), "r")); - if (!pipe) - return std::nullopt; - - std::string result; - char buffer[1024]; - while (fgets(buffer, sizeof(buffer), pipe.get()) != nullptr) - result += buffer; - - return result; -} - #endif +// Reads the file as bytes. +// Returns std::nullopt if the file does not exist. +std::optional read_file_to_string(const std::string& path); + void dbg_hit_on(bool cond, int slot = 0); void dbg_mean_of(int64_t value, int slot = 0); void dbg_stdev_of(int64_t value, int slot = 0); @@ -118,9 +109,12 @@ inline TimePoint now() { } inline std::vector split(const std::string& s, const std::string& delimiter) { - size_t begin = 0; std::vector res; + if (s.empty()) + return res; + + size_t begin = 0; for (;;) { const size_t end = s.find(delimiter, begin); @@ -136,6 +130,8 @@ inline std::vector split(const std::string& s, const std::string& d return res; } +void remove_whitespace(std::string& s); + enum SyncCout { IO_LOCK, IO_UNLOCK diff --git a/src/numa.h b/src/numa.h index 3c9c823aa39..0553309afef 100644 --- a/src/numa.h +++ b/src/numa.h @@ -33,9 +33,8 @@ #include #include -// We support linux very well, but we explicitly do NOT support Android, partially because -// there are potential issues with `lscpu`, `popen` availability, and partially because -// there's no NUMA environments running Android and there probably won't be. +// We support linux very well, but we explicitly do NOT support Android, because there's +// no affected systems, not worth maintaining. #if defined(__linux__) && !defined(__ANDROID__) #if !defined(_GNU_SOURCE) #define _GNU_SOURCE @@ -143,7 +142,9 @@ class NumaConfig { } // This function queries the system for the mapping of processors to NUMA nodes. - // On Linux we utilize `lscpu` to avoid libnuma. + // On Linux we read from standardized kernel sysfs, with a fallback to single NUMA node. + // On Windows we utilize GetNumaProcessorNodeEx, which has its quirks, see + // comment for Windows implementation of get_process_affinity static NumaConfig from_system([[maybe_unused]] bool respectProcessAffinity = true) { NumaConfig cfg = empty(); @@ -160,48 +161,52 @@ class NumaConfig { // On Linux things are straightforward, since there's no processor groups and // any thread can be scheduled on all processors. - // This command produces output in the following form - // CPU NODE - // 0 0 - // 1 0 - // 2 1 - // 3 1 - // - // On some systems it may use '-' to signify no NUMA node, in which case we assume it's in node 0. - auto lscpuOpt = get_system_command_output("lscpu -e=cpu,node"); - if (lscpuOpt.has_value()) - { - std::istringstream ss(*lscpuOpt); + // We try to gather this information from the sysfs first + // https://www.kernel.org/doc/Documentation/ABI/stable/sysfs-devices-node - // skip the list header - ss.ignore(std::numeric_limits::max(), '\n'); + bool useFallback = false; + auto fallback = [&]() { + useFallback = true; + cfg = empty(); + }; - while (true) + // /sys/devices/system/node/online contains information about active NUMA nodes + auto nodeIdsStr = read_file_to_string("/sys/devices/system/node/online"); + if (!nodeIdsStr.has_value() || nodeIdsStr->empty()) + { + fallback(); + } + else + { + remove_whitespace(*nodeIdsStr); + for (size_t n : indices_from_shortened_string(*nodeIdsStr)) { - CpuIndex c; - NumaIndex n; - - ss >> c; - - if (!ss) + // /sys/devices/system/node/node.../cpulist + std::string path = + std::string("/sys/devices/system/node/node") + std::to_string(n) + "/cpulist"; + auto cpuIdsStr = read_file_to_string(path); + // Now, we only bail if the file does not exist. Some nodes may be empty, that's fine. + // An empty node still has a file that appears to have some whitespace, so we need + // to handle that. + if (!cpuIdsStr.has_value()) + { + fallback(); break; - - ss >> n; - - if (!ss) + } + else { - ss.clear(); - std::string dummy; - ss >> dummy; - n = 0; + remove_whitespace(*cpuIdsStr); + for (size_t c : indices_from_shortened_string(*cpuIdsStr)) + { + if (is_cpu_allowed(c)) + cfg.add_cpu_to_node(n, c); + } } - - if (is_cpu_allowed(c)) - cfg.add_cpu_to_node(n, c); } } - else + + if (useFallback) { for (CpuIndex c = 0; c < SYSTEM_THREADS_NB; ++c) if (is_cpu_allowed(c)) @@ -309,38 +314,17 @@ class NumaConfig { NumaIndex n = 0; for (auto&& nodeStr : split(s, ":")) { - bool addedAnyCpuInThisNode = false; - - for (const std::string& cpuStr : split(nodeStr, ",")) + auto indices = indices_from_shortened_string(nodeStr); + if (!indices.empty()) { - if (cpuStr.empty()) - continue; - - auto parts = split(cpuStr, "-"); - if (parts.size() == 1) + for (auto idx : indices) { - const CpuIndex c = CpuIndex{str_to_size_t(parts[0])}; - if (!cfg.add_cpu_to_node(n, c)) + if (!cfg.add_cpu_to_node(n, CpuIndex(idx))) std::exit(EXIT_FAILURE); } - else if (parts.size() == 2) - { - const CpuIndex cfirst = CpuIndex{str_to_size_t(parts[0])}; - const CpuIndex clast = CpuIndex{str_to_size_t(parts[1])}; - if (!cfg.add_cpu_range_to_node(n, cfirst, clast)) - std::exit(EXIT_FAILURE); - } - else - { - std::exit(EXIT_FAILURE); - } - - addedAnyCpuInThisNode = true; - } - - if (addedAnyCpuInThisNode) n += 1; + } } cfg.customAffinity = true; @@ -675,7 +659,6 @@ class NumaConfig { return true; } - #if defined(__linux__) && !defined(__ANDROID__) static std::set get_process_affinity() { @@ -807,6 +790,37 @@ class NumaConfig { } #endif + + static std::vector indices_from_shortened_string(const std::string& s) { + std::vector indices; + + if (s.empty()) + return indices; + + for (const std::string& ss : split(s, ",")) + { + if (ss.empty()) + continue; + + auto parts = split(ss, "-"); + if (parts.size() == 1) + { + const CpuIndex c = CpuIndex{str_to_size_t(parts[0])}; + indices.emplace_back(c); + } + else if (parts.size() == 2) + { + const CpuIndex cfirst = CpuIndex{str_to_size_t(parts[0])}; + const CpuIndex clast = CpuIndex{str_to_size_t(parts[1])}; + for (size_t c = cfirst; c <= clast; ++c) + { + indices.emplace_back(c); + } + } + } + + return indices; + } }; class NumaReplicationContext; From 54e74919d478def20cb103d1e9677a696073c92f Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 30 May 2024 21:42:48 +0200 Subject: [PATCH 1554/1766] Fix cross from Linux to Windows specifies Windows 7 required https://learn.microsoft.com/en-us/cpp/porting/modifying-winver-and-win32-winnt?view=msvc-170 closes https://github.com/official-stockfish/Stockfish/pull/5319 No functional change --- src/numa.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/numa.h b/src/numa.h index 0553309afef..ee84e1cf34d 100644 --- a/src/numa.h +++ b/src/numa.h @@ -42,6 +42,11 @@ #include #elif defined(_WIN64) + #if _WIN32_WINNT < 0x0601 + #undef _WIN32_WINNT + #define _WIN32_WINNT 0x0601 // Force to include needed API prototypes + #endif + // On Windows each processor group can have up to 64 processors. // https://learn.microsoft.com/en-us/windows/win32/procthread/processor-groups static constexpr size_t WIN_PROCESSOR_GROUP_SIZE = 64; From de1ae4949daf2c6d36c50e51c132cee808e2ade0 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Fri, 31 May 2024 04:01:02 +0300 Subject: [PATCH 1555/1766] Tweak first picked move (ttMove) reduction rule Tweak first picked move (ttMove) reduction rule: Instead of always resetting the reduction to 0, we now only do so if the current reduction is less than 2. If the current reduction is 2 or more, we decrease it by 2 instead. Passed STC: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 109504 W: 28340 L: 27919 D: 53245 Ptnml(0-2): 305, 12848, 28028, 13263, 308 https://tests.stockfishchess.org/tests/view/6658c2fa6b0e318cefa900c2 Passed LTC: LLR: 2.96 (-2.94,2.94) <0.50,2.50> Total: 130410 W: 33248 L: 32738 D: 64424 Ptnml(0-2): 53, 14139, 36328, 14615, 70 https://tests.stockfishchess.org/tests/view/6658dd8a6b0e318cefa90173 closes https://github.com/official-stockfish/Stockfish/pull/5321 bench: 1224588 --- src/search.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 638af546d09..4086d50f156 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1149,10 +1149,10 @@ Value Search::Worker::search( if ((ss + 1)->cutoffCnt > 3) r++; - // Set reduction to 0 for first picked move (ttMove) (~2 Elo) - // Nullifies all previous reduction adjustments to ttMove and leaves only history to do them + // For first picked move (ttMove) reduce reduction + // but never allow it to go below 0 (~3 Elo) else if (move == ttMove) - r = 0; + r = std::max(0, r - 2); ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + (*contHist[0])[movedPiece][move.to_sq()] From 0ef809ac71702ee496a88f2cf305117511b555b2 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Wed, 29 May 2024 13:56:15 -0400 Subject: [PATCH 1556/1766] Quadratic smallnet threshold with re-evaluation The threshold now decreases more quickly as pawn count decreases, using the smallnet more compared to before. Combo of two eval patches: https://tests.stockfishchess.org/tests/view/66576c5f6b0e318cefa8d26e https://tests.stockfishchess.org/tests/view/664ced40830eb9f886616a77 Passed STC: https://tests.stockfishchess.org/tests/view/66588c136b0e318cefa8ff21 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 112608 W: 29336 L: 28908 D: 54364 Ptnml(0-2): 344, 13223, 28718, 13699, 320 Passed LTC: https://tests.stockfishchess.org/tests/view/6658c8786b0e318cefa900f5 LLR: 2.96 (-2.94,2.94) <0.50,2.50> Total: 108288 W: 27493 L: 27026 D: 53769 Ptnml(0-2): 54, 11821, 29930, 12282, 57 closes https://github.com/official-stockfish/Stockfish/pull/5323 bench 1728074 --- src/evaluate.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 666697dddba..35bc9301ab1 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -46,7 +46,8 @@ int Eval::simple_eval(const Position& pos, Color c) { bool Eval::use_smallnet(const Position& pos) { int simpleEval = simple_eval(pos, pos.side_to_move()); - return std::abs(simpleEval) > 992 + 6 * pos.count(); + int pawnCount = pos.count(); + return std::abs(simpleEval) > 992 + 6 * pawnCount * pawnCount / 16; } // Evaluate is the evaluator for the outer world. It returns a static evaluation @@ -67,7 +68,7 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, : networks.big.evaluate(pos, &caches.big, true, &nnueComplexity); // Re-evaluate the position when higher eval accuracy is worth the time spent - if (smallNet && nnue * simpleEval < 0) + if (smallNet && (nnue * simpleEval < 0 || std::abs(nnue) < 250)) { nnue = networks.big.evaluate(pos, &caches.big, true, &nnueComplexity); smallNet = false; From b34a690cd4aa6d828ae0f47b427167f4e6392db7 Mon Sep 17 00:00:00 2001 From: rn5f107s2 Date: Thu, 30 May 2024 21:18:42 +0200 Subject: [PATCH 1557/1766] MCP more after a bad singular search The idea is, that if we have the information that the singular search failed low and therefore produced an upperbound score, we can use the score from singularsearch as approximate upperbound as to what bestValue our non ttMoves will produce. If this value is well below alpha, we assume that all non-ttMoves will score below alpha and therfore can skip more moves. This patch also sets up variables for future patches wanting to use teh singular search result outside of singular extensions, in singularBound and singularValue, meaning further patches using this search result to affect various pruning techniques can be tried. Passed STC: https://tests.stockfishchess.org/tests/view/6658d13e6b0e318cefa90120 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 85632 W: 22112 L: 21725 D: 41795 Ptnml(0-2): 243, 10010, 21947, 10349, 267 Passed LTC: https://tests.stockfishchess.org/tests/view/6658dd356b0e318cefa9016a LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 243978 W: 62014 L: 61272 D: 120692 Ptnml(0-2): 128, 26598, 67791, 27348, 124 closes https://github.com/official-stockfish/Stockfish/pull/5325 bench 1397172 --- src/search.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 4086d50f156..f738530ade8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -550,11 +550,12 @@ Value Search::Worker::search( Key posKey; Move ttMove, move, excludedMove, bestMove; Depth extension, newDepth; - Value bestValue, value, ttValue, eval, maxValue, probCutBeta; + Value bestValue, value, ttValue, eval, maxValue, probCutBeta, singularValue; bool givesCheck, improving, priorCapture, opponentWorsening; bool capture, moveCountPruning, ttCapture; Piece movedPiece; int moveCount, captureCount, quietCount; + Bound singularBound; // Step 1. Initialize node Worker* thisThread = this; @@ -923,6 +924,8 @@ Value Search::Worker::search( value = bestValue; moveCountPruning = false; + singularValue = VALUE_INFINITE; + singularBound = BOUND_NONE; // Step 13. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. @@ -972,7 +975,9 @@ Value Search::Worker::search( if (!rootNode && pos.non_pawn_material(us) && bestValue > VALUE_TB_LOSS_IN_MAX_PLY) { // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold (~8 Elo) - moveCountPruning = moveCount >= futility_move_count(improving, depth); + moveCountPruning = + moveCount >= futility_move_count(improving, depth) + - (singularBound == BOUND_UPPER && singularValue < alpha - 50); // Reduced depth of the next LMR search int lmrDepth = newDepth - r; @@ -1058,8 +1063,9 @@ Value Search::Worker::search( Depth singularDepth = newDepth / 2; ss->excludedMove = move; - value = + value = singularValue = search(pos, ss, singularBeta - 1, singularBeta, singularDepth, cutNode); + singularBound = singularValue >= singularBeta ? BOUND_LOWER : BOUND_UPPER; ss->excludedMove = Move::none(); if (value < singularBeta) From cb4a62311985f685ba6f5457851527a3289073e6 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Mon, 27 May 2024 10:40:25 -0400 Subject: [PATCH 1558/1766] Update default smallnet to nn-37f18f62d772.nnue Created by training L1-128 from scratch with: - skipping based on simple eval in the trainer, for compatibility with regular binpacks without requiring pre-filtering all binpacks - minimum simple eval of 950, lower than 1000 previously - usage of some hse-v1 binpacks with minimum simple eval 1000 - addition of hse-v6 binpacks with minimum simple eval 500 - permuting the FT with 10k positions from fishpack32.binpack - torch.compile to speed up smallnet training Training is significantly slower when using non-pre-filtered binpacks due to the increased skipping required. This net was reached at epoch 339. ``` experiment-name: 128--S1-hse-1k-T80-v6-unfilt-less-sf--se-gt950-no-wld-skip training-dataset: /data/: - dfrc99-16tb7p.v2.min.binpack /data/hse-v1/: - leela96-filt-v2.min.high-simple-eval-1k.min-v2.binpack - test60-novdec2021-12tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.min-v2.binpack - test77-nov2021-2tb7p.no-db.min.high-simple-eval-1k.min-v2.binpack - test77-dec2021-16tb7p.no-db.min.high-simple-eval-1k.min-v2.binpack - test77-jan2022-2tb7p.high-simple-eval-1k.min-v2.binpack - test78-jantomay2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.min-v2.binpack - test78-juntosep2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.min-v2.binpack - test79-apr2022-16tb7p.min.high-simple-eval-1k.min-v2.binpack - test79-may2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.min-v2.binpack - test80-apr2022-16tb7p.min.high-simple-eval-1k.min-v2.binpack - test80-may2022-16tb7p.high-simple-eval-1k.min-v2.binpack - test80-jun2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.min-v2.binpack - test80-jul2022-16tb7p.v6-dd.min.high-simple-eval-1k.min-v2.binpack - test80-sep2022-16tb7p-filter-v6-dd.min-mar2023.unmin.high-simple-eval-1k.min-v2.binpack - test80-nov2022-16tb7p-v6-dd.min.high-simple-eval-1k.min-v2.binpack /data/S11-mar2024/: - test80-2022-08-aug-16tb7p.v6-dd.min.binpack - test80-2022-10-oct-16tb7p.v6-dd.binpack - test80-2022-12-dec-16tb7p.min.binpack - test80-2023-01-jan-16tb7p.v6-sk20.min.binpack - test80-2023-02-feb-16tb7p.v6-sk20.min.binpack - test80-2023-03-mar-2tb7p.v6-sk16.min.binpack - test80-2023-04-apr-2tb7p.v6-sk16.min.binpack - test80-2023-05-may-2tb7p.v6.min.binpack - test80-2023-06-jun-2tb7p.binpack.min-v2.binpack - test80-2023-07-jul-2tb7p.binpack.min-v2.binpack - test80-2023-08-aug-2tb7p.v6.min.binpack - test80-2023-09-sep-2tb7p.binpack.hse-v6.binpack - test80-2023-10-oct-2tb7p.binpack.hse-v6.binpack - test80-2023-11-nov-2tb7p.binpack.hse-v6.binpack - test80-2023-12-dec-2tb7p.binpack.hse-v6.binpack - test80-2024-01-jan-2tb7p.binpack.hse-v6.binpack - test80-2024-02-feb-2tb7p.binpack.hse-v6.binpack - test80-2024-03-mar-2tb7p.binpack wld-fen-skipping: False nnue-pytorch-branch: linrock/nnue-pytorch/128-skipSimpleEval-lt950-torch-compile engine-test-branch: linrock/Stockfish/L1-128-nolazy engine-base-branch: linrock/Stockfish/L1-128 start-from-engine-test-net: False num-epochs: 500 start-lambda: 1.0 end-lambda: 1.0 ``` Training data can be found at: https://robotmoon.com/nnue-training-data/ Passed STC: https://tests.stockfishchess.org/tests/view/66549c16a86388d5e27daff5 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 196608 W: 51254 L: 50697 D: 94657 Ptnml(0-2): 722, 23244, 49796, 23839, 703 Passed LTC: https://tests.stockfishchess.org/tests/view/6658d1aa6b0e318cefa90122 LLR: 2.96 (-2.94,2.94) <0.50,2.50> Total: 122538 W: 31332 L: 30835 D: 60371 Ptnml(0-2): 69, 13407, 33811, 13922, 60 closes https://github.com/official-stockfish/Stockfish/pull/5333 bench --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 4fab1a00137..bdef9ceb620 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -34,7 +34,7 @@ namespace Eval { // name of the macro or the location where this macro is defined, as it is used // in the Makefile/Fishtest. #define EvalFileDefaultNameBig "nn-ddcfb9224cdb.nnue" -#define EvalFileDefaultNameSmall "nn-baff1ede1f90.nnue" +#define EvalFileDefaultNameSmall "nn-37f18f62d772.nnue" namespace NNUE { struct Networks; From 783dfc2eb235236ff799618436d68d0c1a3f3807 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 1 Jun 2024 20:44:06 +0300 Subject: [PATCH 1559/1766] Adjust return bonus from tt cutoffs at fail highs This is reintroduction of the recently simplified logic - if positive tt cutoff occurs return not a tt value but smth between it and beta. Difference is that instead of static linear combination there we use basically the same formula as we do in the main search - with the only difference being using tt depth instead of depth, which makes a lot of sense. Passed STC: https://tests.stockfishchess.org/tests/view/665b3a34f4a1fd0c208ea870 LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 54944 W: 14239 L: 13896 D: 26809 Ptnml(0-2): 151, 6407, 14008, 6760, 146 Passed LTC: https://tests.stockfishchess.org/tests/view/665b520011645bd3d3fac341 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 90540 W: 23070 L: 22640 D: 44830 Ptnml(0-2): 39, 9903, 24965, 10315, 48 closes https://github.com/official-stockfish/Stockfish/pull/5336 bench 1381237 --- src/search.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index f738530ade8..514b7b7d99b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -640,7 +640,12 @@ Value Search::Worker::search( // Partial workaround for the graph history interaction problem // For high rule50 counts don't produce transposition table cutoffs. if (pos.rule50_count() < 90) + { + if (ttValue >= beta && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY + && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) + ttValue = (ttValue * tte->depth() + beta) / (tte->depth() + 1); return ttValue; + } } // Step 5. Tablebases probe From b0870cf528ef90e8873719a36a448dafd73e3aee Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 1 Jun 2024 15:13:41 +0200 Subject: [PATCH 1560/1766] Avoid changing bestvalue in the case the ttValue contains mate scores, do not return them as bestValue, since they are not proven. passed STC https://tests.stockfishchess.org/tests/view/665b1ea5586058766677cfa3 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 58912 W: 15319 L: 15130 D: 28463 Ptnml(0-2): 141, 6562, 15854, 6765, 134 passed LTC: https://tests.stockfishchess.org/tests/view/665b2712586058766677cfc4 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 141666 W: 35976 L: 35879 D: 69811 Ptnml(0-2): 61, 15513, 39584, 15618, 57 closes https://github.com/official-stockfish/Stockfish/pull/5335 Bench: 1336115 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 514b7b7d99b..4dc7d3300e3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1495,7 +1495,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); // ttValue can be used as a better position evaluation (~13 Elo) - if (ttValue != VALUE_NONE + if (std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && (tte->bound() & (ttValue > bestValue ? BOUND_LOWER : BOUND_UPPER))) bestValue = ttValue; } From ec1cda1d819f534c8d0bfc4624836157bc548eb6 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Fri, 31 May 2024 22:29:29 +0300 Subject: [PATCH 1561/1766] Simplify histories movepick formula Passed STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 81440 W: 21100 L: 20929 D: 39411 Ptnml(0-2): 248, 9659, 20718, 9864, 231 https://tests.stockfishchess.org/tests/view/6659a8b7ea624d64ea5f3208 Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 85758 W: 21763 L: 21607 D: 42388 Ptnml(0-2): 34, 9606, 23463, 9722, 54 https://tests.stockfishchess.org/tests/view/6659d7bff426908fcc6b692c closes https://github.com/official-stockfish/Stockfish/pull/5326 bench: 1280472 --- src/movepick.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index b6828a30b72..d333590751c 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -178,7 +178,7 @@ void MovePicker::score() { Square to = m.to_sq(); // histories - m.value = 2 * (*mainHistory)[pos.side_to_move()][m.from_to()]; + m.value = (*mainHistory)[pos.side_to_move()][m.from_to()]; m.value += 2 * (*pawnHistory)[pawn_structure_index(pos)][pc][to]; m.value += 2 * (*continuationHistory[0])[pc][to]; m.value += (*continuationHistory[1])[pc][to]; From 180cab443896a6a37a3c39852ff124ce856987d2 Mon Sep 17 00:00:00 2001 From: MinetaS Date: Sat, 1 Jun 2024 06:11:51 +0900 Subject: [PATCH 1562/1766] Simplify 50 move rule dampening Refactor the logic of 50 move rule dampening by removing a constant. Passed non-regression STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 35232 W: 9214 L: 8992 D: 17026 Ptnml(0-2): 114, 4081, 8999, 4313, 109 https://tests.stockfishchess.org/tests/view/665a329013d08af3c1725610 Passed non-regression LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 38406 W: 9732 L: 9530 D: 19144 Ptnml(0-2): 14, 4132, 10708, 4336, 13 https://tests.stockfishchess.org/tests/view/665a370913d08af3c1725651 https://github.com/official-stockfish/Stockfish/pull/5327 Bench: 1059739 --- src/evaluate.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 35bc9301ab1..eaf7ab5f981 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -81,10 +81,10 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, int material = 300 * pos.count() + 350 * pos.count() + 400 * pos.count() + 640 * pos.count() + 1200 * pos.count(); - v = (nnue * (34300 + material) + optimism * (4400 + material)) / 35967; + v = (nnue * (34300 + material) + optimism * (4400 + material)) / 36672; // Damp down the evaluation linearly when shuffling - v = v * (204 - pos.rule50_count()) / 208; + v -= v * pos.rule50_count() / 212; // Guarantee evaluation does not hit the tablebase range v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); From b009c43254c3483dd356e28b5b66ba62a724aa1d Mon Sep 17 00:00:00 2001 From: xoto10 <23479932+xoto10@users.noreply.github.com> Date: Sat, 1 Jun 2024 17:10:06 +0100 Subject: [PATCH 1563/1766] Simplify tm, removing faster 1st move and 1.13 extraTime. Passed STC 10+0.1 : LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 349760 W: 90112 L: 90231 D: 169417 Ptnml(0-2): 784, 37970, 97496, 37841, 789 https://tests.stockfishchess.org/tests/view/665aeee00223e235f05b7d21 Passed LTC 60+0.6 : LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 140082 W: 35463 L: 35370 D: 69249 Ptnml(0-2): 59, 13492, 42851, 13575, 64 https://tests.stockfishchess.org/tests/view/665b15e78da109e362924e5a closes https://github.com/official-stockfish/Stockfish/pull/5334 No functional change --- src/search.cpp | 3 +-- src/search.h | 1 - src/thread.cpp | 7 ++++--- src/timeman.cpp | 18 ++++-------------- src/timeman.h | 1 - 5 files changed, 9 insertions(+), 21 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 4dc7d3300e3..35de756ff27 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -160,8 +160,7 @@ void Search::Worker::start_searching() { return; } - main_manager()->tm.init(limits, rootPos.side_to_move(), rootPos.game_ply(), options, - main_manager()->originalPly, main_manager()->originalTimeAdjust); + main_manager()->tm.init(limits, rootPos.side_to_move(), rootPos.game_ply(), options, main_manager()->originalTimeAdjust); tt.new_search(); if (rootMoves.empty()) diff --git a/src/search.h b/src/search.h index 7cff10d5590..01f7b8bdb00 100644 --- a/src/search.h +++ b/src/search.h @@ -209,7 +209,6 @@ class SearchManager: public ISearchManager { Stockfish::TimeManagement tm; double originalTimeAdjust; - int originalPly; int callsCnt; std::atomic_bool ponder; diff --git a/src/thread.cpp b/src/thread.cpp index 71134ead6ea..1b0fffc3572 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -213,12 +213,13 @@ void ThreadPool::clear() { for (auto&& th : threads) th->wait_for_search_finished(); + // These two affect the time taken on the first move of a game: + main_manager()->bestPreviousAverageScore = VALUE_INFINITE; + main_manager()->previousTimeReduction = 0.85; + main_manager()->callsCnt = 0; main_manager()->bestPreviousScore = VALUE_INFINITE; - main_manager()->bestPreviousAverageScore = VALUE_INFINITE; - main_manager()->originalPly = -1; main_manager()->originalTimeAdjust = -1; - main_manager()->previousTimeReduction = 1.0; main_manager()->tm.clear(); } diff --git a/src/timeman.cpp b/src/timeman.cpp index f6ca298a82f..9de70fdc613 100644 --- a/src/timeman.cpp +++ b/src/timeman.cpp @@ -48,7 +48,6 @@ void TimeManagement::init(Search::LimitsType& limits, Color us, int ply, const OptionsMap& options, - int& originalPly, double& originalTimeAdjust) { TimePoint npmsec = TimePoint(options["nodestime"]); @@ -60,9 +59,6 @@ void TimeManagement::init(Search::LimitsType& limits, if (limits.time[us] == 0) return; - if (originalPly == -1) - originalPly = ply; - TimePoint moveOverhead = TimePoint(options["Move Overhead"]); // optScale is a percentage of available time to use for the current move. @@ -104,20 +100,14 @@ void TimeManagement::init(Search::LimitsType& limits, TimePoint timeLeft = std::max(TimePoint(1), limits.time[us] + limits.inc[us] * (mtg - 1) - moveOverhead * (2 + mtg)); - // Extra time according to timeLeft - if (originalTimeAdjust < 0) - originalTimeAdjust = 0.2078 + 0.1623 * std::log10(timeLeft); - // x basetime (+ z increment) // If there is a healthy increment, timeLeft can exceed the actual available // game time for the current move, so also cap to a percentage of available game time. if (limits.movestogo == 0) { - // Use extra time with larger increments - double optExtra = scaledInc < 500 ? 1.0 : 1.13; - if (ply - originalPly < 2) - optExtra *= 0.95; - optExtra *= originalTimeAdjust; + // Extra time according to timeLeft + if (originalTimeAdjust < 0) + originalTimeAdjust = 0.3285 * std::log10(timeLeft) - 0.4830; // Calculate time constants based on current time left. double logTimeInSec = std::log10(scaledTime / 1000.0); @@ -126,7 +116,7 @@ void TimeManagement::init(Search::LimitsType& limits, optScale = std::min(0.0122 + std::pow(ply + 2.95, 0.462) * optConstant, 0.213 * limits.time[us] / timeLeft) - * optExtra; + * originalTimeAdjust; maxScale = std::min(6.64, maxConstant + ply / 12.0); } diff --git a/src/timeman.h b/src/timeman.h index 8b763089a70..10207a8a730 100644 --- a/src/timeman.h +++ b/src/timeman.h @@ -40,7 +40,6 @@ class TimeManagement { Color us, int ply, const OptionsMap& options, - int& originalPly, double& originalTimeAdjust); TimePoint optimum() const; From c17d73c554054db8cdc6eb39d667c1dca47d3818 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Sat, 1 Jun 2024 11:07:08 -0400 Subject: [PATCH 1564/1766] Simplify statScore divisor into a constant Passed non-regression STC: https://tests.stockfishchess.org/tests/view/665b392ff4a1fd0c208ea864 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 114752 W: 29628 L: 29495 D: 55629 Ptnml(0-2): 293, 13694, 29269, 13827, 293 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/665b588c11645bd3d3fac467 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 65322 W: 16549 L: 16373 D: 32400 Ptnml(0-2): 30, 7146, 18133, 7322, 30 closes https://github.com/official-stockfish/Stockfish/pull/5337 bench 1241443 --- src/numa.h | 2 +- src/search.cpp | 5 +++-- src/thread.cpp | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/numa.h b/src/numa.h index ee84e1cf34d..967e24a66e6 100644 --- a/src/numa.h +++ b/src/numa.h @@ -564,7 +564,7 @@ class NumaConfig { { const size_t procGroupIndex = c / WIN_PROCESSOR_GROUP_SIZE; const size_t idxWithinProcGroup = c % WIN_PROCESSOR_GROUP_SIZE; - // We skip processors that are not in the same proccessor group. + // We skip processors that are not in the same processor group. // If everything was set up correctly this will never be an issue, // but we have to account for bad NUMA node specification. if (procGroupIndex != forcedProcGroupIndex) diff --git a/src/search.cpp b/src/search.cpp index 35de756ff27..84ca93f8e59 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -160,7 +160,8 @@ void Search::Worker::start_searching() { return; } - main_manager()->tm.init(limits, rootPos.side_to_move(), rootPos.game_ply(), options, main_manager()->originalTimeAdjust); + main_manager()->tm.init(limits, rootPos.side_to_move(), rootPos.game_ply(), options, + main_manager()->originalTimeAdjust); tt.new_search(); if (rootMoves.empty()) @@ -1169,7 +1170,7 @@ Value Search::Worker::search( + (*contHist[1])[movedPiece][move.to_sq()] - 5169; // Decrease/increase reduction for moves with a good/bad history (~8 Elo) - r -= ss->statScore / (12219 - std::min(depth, 13) * 120); + r -= ss->statScore / 11049; // Step 17. Late moves reduction / extension (LMR, ~117 Elo) if (depth >= 2 && moveCount > 1 + rootNode) diff --git a/src/thread.cpp b/src/thread.cpp index 1b0fffc3572..a36c2efb7c1 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -217,9 +217,9 @@ void ThreadPool::clear() { main_manager()->bestPreviousAverageScore = VALUE_INFINITE; main_manager()->previousTimeReduction = 0.85; - main_manager()->callsCnt = 0; - main_manager()->bestPreviousScore = VALUE_INFINITE; - main_manager()->originalTimeAdjust = -1; + main_manager()->callsCnt = 0; + main_manager()->bestPreviousScore = VALUE_INFINITE; + main_manager()->originalTimeAdjust = -1; main_manager()->tm.clear(); } From 8aaae0367cfed7ae5da54d330b65d76d4b1b13ae Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 2 Jun 2024 09:18:19 +0200 Subject: [PATCH 1565/1766] Revert "Adjust return bonus from tt cutoffs at fail highs" This reverts commit 783dfc2eb235236ff799618436d68d0c1a3f3807. could lead to a division by zero for: ttValue = (ttValue * tte->depth() + beta) / (tte->depth() + 1) as other threads can overwrite the tte with a QS depth of -1. closes https://github.com/official-stockfish/Stockfish/pull/5338 Bench: 1280020 --- src/search.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 84ca93f8e59..a2a75af0caf 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -640,12 +640,7 @@ Value Search::Worker::search( // Partial workaround for the graph history interaction problem // For high rule50 counts don't produce transposition table cutoffs. if (pos.rule50_count() < 90) - { - if (ttValue >= beta && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY - && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) - ttValue = (ttValue * tte->depth() + beta) / (tte->depth() + 1); return ttValue; - } } // Step 5. Tablebases probe From a2a7edf4c8fa145667135bf1bc7f4f67016f7608 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Sun, 2 Jun 2024 20:39:25 +0200 Subject: [PATCH 1566/1766] Fix GetProcessGroupAffinity call `GetProcessGroupAffinity` appears to require 4 byte alignment for `GroupArray` memory. See https://stackoverflow.com/q/78567676 for further information closes https://github.com/official-stockfish/Stockfish/pull/5340 No functional change --- src/numa.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/numa.h b/src/numa.h index 967e24a66e6..5934a0cd8dd 100644 --- a/src/numa.h +++ b/src/numa.h @@ -773,8 +773,8 @@ class NumaConfig { return std::nullopt; // We are expecting a single group. - USHORT GroupCount = 1; - USHORT GroupArray[1]; + USHORT GroupCount = 1; + alignas(4) USHORT GroupArray[1]; status = GetProcessGroupAffinity_f(GetCurrentProcess(), &GroupCount, GroupArray); if (status == 0 || GroupCount != 1) return std::nullopt; From 00a28ae325688346e63a452b2050bd1491085359 Mon Sep 17 00:00:00 2001 From: Disservin Date: Fri, 31 May 2024 10:53:10 +0200 Subject: [PATCH 1567/1766] Add helpers for managing aligned memory Previously, we had two type aliases, LargePagePtr and AlignedPtr, which required manually initializing the aligned memory for the pointer. The new helpers: - make_unique_aligned - make_unique_large_page are now available for allocating aligned memory (with large pages). They behave similarly to std::make_unique, ensuring objects allocated with these functions follow RAII. The old approach had issues with initializing non-trivial types or arrays of objects. The evaluation function of the network is now a unique pointer to an array instead of an array of unique pointers. Memory related functions have been moved into memory.h Passed High Hash Pressure Test Non-Regression STC: https://tests.stockfishchess.org/tests/view/665b2b36586058766677cfd2 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 476992 W: 122426 L: 122677 D: 231889 Ptnml(0-2): 1145, 51027, 134419, 50744, 1161 Failed Normal Non-Regression STC: https://tests.stockfishchess.org/tests/view/665b2997586058766677cfc8 LLR: -2.94 (-2.94,2.94) <-1.75,0.25> Total: 877312 W: 225233 L: 226395 D: 425684 Ptnml(0-2): 2110, 94642, 246239, 93630, 2035 Probably a fluke since there shouldn't be a real slowndown and it has also passed the high hash pressure test. closes https://github.com/official-stockfish/Stockfish/pull/5332 No functional change --- src/Makefile | 8 +- src/memory.cpp | 229 +++++++++++++++++++++++++++++++++++++++++++ src/memory.h | 215 ++++++++++++++++++++++++++++++++++++++++ src/misc.cpp | 199 +------------------------------------ src/misc.h | 48 +-------- src/nnue/network.cpp | 77 +++++---------- src/nnue/network.h | 6 +- src/numa.h | 1 + src/thread.h | 4 +- src/tt.cpp | 7 +- src/tt.h | 10 +- 11 files changed, 492 insertions(+), 312 deletions(-) create mode 100644 src/memory.cpp create mode 100644 src/memory.h diff --git a/src/Makefile b/src/Makefile index 5119b615f6b..29c4f879dfb 100644 --- a/src/Makefile +++ b/src/Makefile @@ -55,7 +55,7 @@ PGOBENCH = $(WINE_PATH) ./$(EXE) bench SRCS = benchmark.cpp bitboard.cpp evaluate.cpp main.cpp \ misc.cpp movegen.cpp movepick.cpp position.cpp \ search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \ - nnue/nnue_misc.cpp nnue/features/half_ka_v2_hm.cpp nnue/network.cpp engine.cpp score.cpp + nnue/nnue_misc.cpp nnue/features/half_ka_v2_hm.cpp nnue/network.cpp engine.cpp score.cpp memory.cpp HEADERS = benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h \ nnue/nnue_misc.h nnue/features/half_ka_v2_hm.h nnue/layers/affine_transform.h \ @@ -63,7 +63,7 @@ HEADERS = benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h \ nnue/layers/sqr_clipped_relu.h nnue/nnue_accumulator.h nnue/nnue_architecture.h \ nnue/nnue_common.h nnue/nnue_feature_transformer.h position.h \ search.h syzygy/tbprobe.h thread.h thread_win32_osx.h timeman.h \ - tt.h tune.h types.h uci.h ucioption.h perft.h nnue/network.h engine.h score.h numa.h + tt.h tune.h types.h uci.h ucioption.h perft.h nnue/network.h engine.h score.h numa.h memory.h OBJS = $(notdir $(SRCS:.cpp=.o)) @@ -489,8 +489,8 @@ ifeq ($(COMP),clang) endif ifeq ($(KERNEL),Darwin) - CXXFLAGS += -mmacosx-version-min=10.14 - LDFLAGS += -mmacosx-version-min=10.14 + CXXFLAGS += -mmacosx-version-min=10.15 + LDFLAGS += -mmacosx-version-min=10.15 ifneq ($(arch),any) CXXFLAGS += -arch $(arch) LDFLAGS += -arch $(arch) diff --git a/src/memory.cpp b/src/memory.cpp new file mode 100644 index 00000000000..565b39b2061 --- /dev/null +++ b/src/memory.cpp @@ -0,0 +1,229 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "memory.h" + +#include + +#if __has_include("features.h") + #include +#endif + +#if defined(__linux__) && !defined(__ANDROID__) + #include +#endif + +#if defined(__APPLE__) || defined(__ANDROID__) || defined(__OpenBSD__) \ + || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) && !defined(_WIN32)) \ + || defined(__e2k__) + #define POSIXALIGNEDALLOC + #include +#endif + +#ifdef _WIN32 + #if _WIN32_WINNT < 0x0601 + #undef _WIN32_WINNT + #define _WIN32_WINNT 0x0601 // Force to include needed API prototypes + #endif + + #ifndef NOMINMAX + #define NOMINMAX + #endif + + #include // std::hex, std::dec + #include // std::cerr + #include // std::endl + #include +// The needed Windows API for processor groups could be missed from old Windows +// versions, so instead of calling them directly (forcing the linker to resolve +// the calls at compile time), try to load them at runtime. To do this we need +// first to define the corresponding function pointers. +extern "C" { +using OpenProcessToken_t = bool (*)(HANDLE, DWORD, PHANDLE); +using LookupPrivilegeValueA_t = bool (*)(LPCSTR, LPCSTR, PLUID); +using AdjustTokenPrivileges_t = + bool (*)(HANDLE, BOOL, PTOKEN_PRIVILEGES, DWORD, PTOKEN_PRIVILEGES, PDWORD); +} +#endif + + +namespace Stockfish { + +// Wrapper for systems where the c++17 implementation +// does not guarantee the availability of aligned_alloc(). Memory allocated with +// std_aligned_alloc() must be freed with std_aligned_free(). +void* std_aligned_alloc(size_t alignment, size_t size) { + // Apple requires 10.15, which is enforced in the makefile +#if defined(_ISOC11_SOURCE) || defined(__APPLE__) + return aligned_alloc(alignment, size); +#elif defined(POSIXALIGNEDALLOC) + void* mem; + return posix_memalign(&mem, alignment, size) ? nullptr : mem; +#elif defined(_WIN32) && !defined(_M_ARM) && !defined(_M_ARM64) + return _mm_malloc(size, alignment); +#elif defined(_WIN32) + return _aligned_malloc(size, alignment); +#else + return std::aligned_alloc(alignment, size); +#endif +} + +void std_aligned_free(void* ptr) { + +#if defined(POSIXALIGNEDALLOC) + free(ptr); +#elif defined(_WIN32) && !defined(_M_ARM) && !defined(_M_ARM64) + _mm_free(ptr); +#elif defined(_WIN32) + _aligned_free(ptr); +#else + free(ptr); +#endif +} + +// aligned_large_pages_alloc() will return suitably aligned memory, if possible using large pages. + +#if defined(_WIN32) + +static void* aligned_large_pages_alloc_windows([[maybe_unused]] size_t allocSize) { + + #if !defined(_WIN64) + return nullptr; + #else + + HANDLE hProcessToken{}; + LUID luid{}; + void* mem = nullptr; + + const size_t largePageSize = GetLargePageMinimum(); + if (!largePageSize) + return nullptr; + + // Dynamically link OpenProcessToken, LookupPrivilegeValue and AdjustTokenPrivileges + + HMODULE hAdvapi32 = GetModuleHandle(TEXT("advapi32.dll")); + + if (!hAdvapi32) + hAdvapi32 = LoadLibrary(TEXT("advapi32.dll")); + + auto OpenProcessToken_f = + OpenProcessToken_t((void (*)()) GetProcAddress(hAdvapi32, "OpenProcessToken")); + if (!OpenProcessToken_f) + return nullptr; + auto LookupPrivilegeValueA_f = + LookupPrivilegeValueA_t((void (*)()) GetProcAddress(hAdvapi32, "LookupPrivilegeValueA")); + if (!LookupPrivilegeValueA_f) + return nullptr; + auto AdjustTokenPrivileges_f = + AdjustTokenPrivileges_t((void (*)()) GetProcAddress(hAdvapi32, "AdjustTokenPrivileges")); + if (!AdjustTokenPrivileges_f) + return nullptr; + + // We need SeLockMemoryPrivilege, so try to enable it for the process + if (!OpenProcessToken_f( // OpenProcessToken() + GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken)) + return nullptr; + + if (LookupPrivilegeValueA_f(nullptr, "SeLockMemoryPrivilege", &luid)) + { + TOKEN_PRIVILEGES tp{}; + TOKEN_PRIVILEGES prevTp{}; + DWORD prevTpLen = 0; + + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = luid; + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + // Try to enable SeLockMemoryPrivilege. Note that even if AdjustTokenPrivileges() succeeds, + // we still need to query GetLastError() to ensure that the privileges were actually obtained. + if (AdjustTokenPrivileges_f(hProcessToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &prevTp, + &prevTpLen) + && GetLastError() == ERROR_SUCCESS) + { + // Round up size to full pages and allocate + allocSize = (allocSize + largePageSize - 1) & ~size_t(largePageSize - 1); + mem = VirtualAlloc(nullptr, allocSize, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, + PAGE_READWRITE); + + // Privilege no longer needed, restore previous state + AdjustTokenPrivileges_f(hProcessToken, FALSE, &prevTp, 0, nullptr, nullptr); + } + } + + CloseHandle(hProcessToken); + + return mem; + + #endif +} + +void* aligned_large_pages_alloc(size_t allocSize) { + + // Try to allocate large pages + void* mem = aligned_large_pages_alloc_windows(allocSize); + + // Fall back to regular, page-aligned, allocation if necessary + if (!mem) + mem = VirtualAlloc(nullptr, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + + return mem; +} + +#else + +void* aligned_large_pages_alloc(size_t allocSize) { + + #if defined(__linux__) + constexpr size_t alignment = 2 * 1024 * 1024; // assumed 2MB page size + #else + constexpr size_t alignment = 4096; // assumed small page size + #endif + + // Round up to multiples of alignment + size_t size = ((allocSize + alignment - 1) / alignment) * alignment; + void* mem = std_aligned_alloc(alignment, size); + #if defined(MADV_HUGEPAGE) + madvise(mem, size, MADV_HUGEPAGE); + #endif + return mem; +} + +#endif + + +// aligned_large_pages_free() will free the previously allocated ttmem + +#if defined(_WIN32) + +void aligned_large_pages_free(void* mem) { + + if (mem && !VirtualFree(mem, 0, MEM_RELEASE)) + { + DWORD err = GetLastError(); + std::cerr << "Failed to free large page memory. Error code: 0x" << std::hex << err + << std::dec << std::endl; + exit(EXIT_FAILURE); + } +} + +#else + +void aligned_large_pages_free(void* mem) { std_aligned_free(mem); } + +#endif +} // namespace Stockfish diff --git a/src/memory.h b/src/memory.h new file mode 100644 index 00000000000..ad7ca6025b3 --- /dev/null +++ b/src/memory.h @@ -0,0 +1,215 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef MEMORY_H_INCLUDED +#define MEMORY_H_INCLUDED + +#include +#include +#include +#include +#include +#include +#include + +#include "types.h" + +namespace Stockfish { + +void* std_aligned_alloc(size_t alignment, size_t size); +void std_aligned_free(void* ptr); +// memory aligned by page size, min alignment: 4096 bytes +void* aligned_large_pages_alloc(size_t size); +// nop if mem == nullptr +void aligned_large_pages_free(void* mem); + +// frees memory which was placed there with placement new. +// works for both single objects and arrays of unknown bound +template +void memory_deleter(T* ptr, FREE_FUNC free_func) { + if (!ptr) + return; + + // Explicitly needed to call the destructor + if constexpr (!std::is_trivially_destructible_v) + ptr->~T(); + + free_func(ptr); + return; +} + +// frees memory which was placed there with placement new. +// works for both single objects and arrays of unknown bound +template +void memory_deleter_array(T* ptr, FREE_FUNC free_func) { + if (!ptr) + return; + + + // Move back on the pointer to where the size is allocated. + const size_t array_offset = std::max(sizeof(size_t), alignof(T)); + char* raw_memory = reinterpret_cast(ptr) - array_offset; + + if constexpr (!std::is_trivially_destructible_v) + { + const size_t size = *reinterpret_cast(raw_memory); + + // Explicitly call the destructor for each element in reverse order + for (size_t i = size; i-- > 0;) + ptr[i].~T(); + } + + free_func(raw_memory); +} + +// Allocates memory for a single object and places it there with placement new. +template +inline std::enable_if_t, T*> memory_allocator(ALLOC_FUNC alloc_func, + Args&&... args) { + void* raw_memory = alloc_func(sizeof(T)); + ASSERT_ALIGNED(raw_memory, alignof(T)); + return new (raw_memory) T(std::forward(args)...); +} + +// Allocates memory for an array of unknown bound and places it there with placement new. +template +inline std::enable_if_t, std::remove_extent_t*> +memory_allocator(ALLOC_FUNC alloc_func, size_t num) { + using ElementType = std::remove_extent_t; + + const size_t array_offset = std::max(sizeof(size_t), alignof(ElementType)); + + // save the array size in the memory location + char* raw_memory = + reinterpret_cast(alloc_func(array_offset + num * sizeof(ElementType))); + ASSERT_ALIGNED(raw_memory, alignof(T)); + + new (raw_memory) size_t(num); + + for (size_t i = 0; i < num; ++i) + new (raw_memory + array_offset + i * sizeof(ElementType)) ElementType(); + + // Need to return the pointer at the start of the array so that the indexing in unique_ptr works + return reinterpret_cast(raw_memory + array_offset); +} + +// +// +// aligned large page unique ptr +// +// + +template +struct LargePageDeleter { + void operator()(T* ptr) const { return memory_deleter(ptr, aligned_large_pages_free); } +}; + +template +struct LargePageArrayDeleter { + void operator()(T* ptr) const { return memory_deleter_array(ptr, aligned_large_pages_free); } +}; + +template +using LargePagePtr = + std::conditional_t, + std::unique_ptr>>, + std::unique_ptr>>; + +// make_unique_large_page for single objects +template +std::enable_if_t, LargePagePtr> make_unique_large_page(Args&&... args) { + static_assert(alignof(T) <= 4096, + "aligned_large_pages_alloc() may fail for such a big alignment requirement of T"); + + T* obj = memory_allocator(aligned_large_pages_alloc, std::forward(args)...); + + return LargePagePtr(obj); +} + +// make_unique_large_page for arrays of unknown bound +template +std::enable_if_t, LargePagePtr> make_unique_large_page(size_t num) { + using ElementType = std::remove_extent_t; + + static_assert(alignof(ElementType) <= 4096, + "aligned_large_pages_alloc() may fail for such a big alignment requirement of T"); + + ElementType* memory = memory_allocator(aligned_large_pages_alloc, num); + + return LargePagePtr(memory); +} + +// +// +// aligned unique ptr +// +// + +template +struct AlignedDeleter { + void operator()(T* ptr) const { return memory_deleter(ptr, std_aligned_free); } +}; + +template +struct AlignedArrayDeleter { + void operator()(T* ptr) const { return memory_deleter_array(ptr, std_aligned_free); } +}; + +template +using AlignedPtr = + std::conditional_t, + std::unique_ptr>>, + std::unique_ptr>>; + +// make_unique_aligned for single objects +template +std::enable_if_t, AlignedPtr> make_unique_aligned(Args&&... args) { + const auto func = [](size_t size) { return std_aligned_alloc(alignof(T), size); }; + T* obj = memory_allocator(func, std::forward(args)...); + + return AlignedPtr(obj); +} + +// make_unique_aligned for arrays of unknown bound +template +std::enable_if_t, AlignedPtr> make_unique_aligned(size_t num) { + using ElementType = std::remove_extent_t; + + const auto func = [](size_t size) { return std_aligned_alloc(alignof(ElementType), size); }; + ElementType* memory = memory_allocator(func, num); + + return AlignedPtr(memory); +} + + +// Get the first aligned element of an array. +// ptr must point to an array of size at least `sizeof(T) * N + alignment` bytes, +// where N is the number of elements in the array. +template +T* align_ptr_up(T* ptr) { + static_assert(alignof(T) < Alignment); + + const uintptr_t ptrint = reinterpret_cast(reinterpret_cast(ptr)); + return reinterpret_cast( + reinterpret_cast((ptrint + (Alignment - 1)) / Alignment * Alignment)); +} + + +} // namespace Stockfish + +#endif // #ifndef MEMORY_H_INCLUDED diff --git a/src/misc.cpp b/src/misc.cpp index aa22e61f23f..a8bb46ec3c1 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -18,29 +18,6 @@ #include "misc.h" -#ifdef _WIN32 - #if _WIN32_WINNT < 0x0601 - #undef _WIN32_WINNT - #define _WIN32_WINNT 0x0601 // Force to include needed API prototypes - #endif - - #ifndef NOMINMAX - #define NOMINMAX - #endif - - #include -// The needed Windows API for processor groups could be missed from old Windows -// versions, so instead of calling them directly (forcing the linker to resolve -// the calls at compile time), try to load them at runtime. To do this we need -// first to define the corresponding function pointers. -extern "C" { -using OpenProcessToken_t = bool (*)(HANDLE, DWORD, PHANDLE); -using LookupPrivilegeValueA_t = bool (*)(LPCSTR, LPCSTR, PLUID); -using AdjustTokenPrivileges_t = - bool (*)(HANDLE, BOOL, PTOKEN_PRIVILEGES, DWORD, PTOKEN_PRIVILEGES, PDWORD); -} -#endif - #include #include #include @@ -48,25 +25,14 @@ using AdjustTokenPrivileges_t = #include #include #include -#include #include +#include #include #include #include #include "types.h" -#if defined(__linux__) && !defined(__ANDROID__) - #include -#endif - -#if defined(__APPLE__) || defined(__ANDROID__) || defined(__OpenBSD__) \ - || (defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC) && !defined(_WIN32)) \ - || defined(__e2k__) - #define POSIXALIGNEDALLOC - #include -#endif - namespace Stockfish { namespace { @@ -427,169 +393,6 @@ void prefetch(const void* addr) { #endif - -// Wrapper for systems where the c++17 implementation -// does not guarantee the availability of aligned_alloc(). Memory allocated with -// std_aligned_alloc() must be freed with std_aligned_free(). -void* std_aligned_alloc(size_t alignment, size_t size) { - -#if defined(POSIXALIGNEDALLOC) - void* mem; - return posix_memalign(&mem, alignment, size) ? nullptr : mem; -#elif defined(_WIN32) && !defined(_M_ARM) && !defined(_M_ARM64) - return _mm_malloc(size, alignment); -#elif defined(_WIN32) - return _aligned_malloc(size, alignment); -#else - return std::aligned_alloc(alignment, size); -#endif -} - -void std_aligned_free(void* ptr) { - -#if defined(POSIXALIGNEDALLOC) - free(ptr); -#elif defined(_WIN32) && !defined(_M_ARM) && !defined(_M_ARM64) - _mm_free(ptr); -#elif defined(_WIN32) - _aligned_free(ptr); -#else - free(ptr); -#endif -} - -// aligned_large_pages_alloc() will return suitably aligned memory, if possible using large pages. - -#if defined(_WIN32) - -static void* aligned_large_pages_alloc_windows([[maybe_unused]] size_t allocSize) { - - #if !defined(_WIN64) - return nullptr; - #else - - HANDLE hProcessToken{}; - LUID luid{}; - void* mem = nullptr; - - const size_t largePageSize = GetLargePageMinimum(); - if (!largePageSize) - return nullptr; - - // Dynamically link OpenProcessToken, LookupPrivilegeValue and AdjustTokenPrivileges - - HMODULE hAdvapi32 = GetModuleHandle(TEXT("advapi32.dll")); - - if (!hAdvapi32) - hAdvapi32 = LoadLibrary(TEXT("advapi32.dll")); - - auto OpenProcessToken_f = - OpenProcessToken_t((void (*)()) GetProcAddress(hAdvapi32, "OpenProcessToken")); - if (!OpenProcessToken_f) - return nullptr; - auto LookupPrivilegeValueA_f = - LookupPrivilegeValueA_t((void (*)()) GetProcAddress(hAdvapi32, "LookupPrivilegeValueA")); - if (!LookupPrivilegeValueA_f) - return nullptr; - auto AdjustTokenPrivileges_f = - AdjustTokenPrivileges_t((void (*)()) GetProcAddress(hAdvapi32, "AdjustTokenPrivileges")); - if (!AdjustTokenPrivileges_f) - return nullptr; - - // We need SeLockMemoryPrivilege, so try to enable it for the process - if (!OpenProcessToken_f( // OpenProcessToken() - GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken)) - return nullptr; - - if (LookupPrivilegeValueA_f(nullptr, "SeLockMemoryPrivilege", &luid)) - { - TOKEN_PRIVILEGES tp{}; - TOKEN_PRIVILEGES prevTp{}; - DWORD prevTpLen = 0; - - tp.PrivilegeCount = 1; - tp.Privileges[0].Luid = luid; - tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - - // Try to enable SeLockMemoryPrivilege. Note that even if AdjustTokenPrivileges() succeeds, - // we still need to query GetLastError() to ensure that the privileges were actually obtained. - if (AdjustTokenPrivileges_f(hProcessToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &prevTp, - &prevTpLen) - && GetLastError() == ERROR_SUCCESS) - { - // Round up size to full pages and allocate - allocSize = (allocSize + largePageSize - 1) & ~size_t(largePageSize - 1); - mem = VirtualAlloc(nullptr, allocSize, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, - PAGE_READWRITE); - - // Privilege no longer needed, restore previous state - AdjustTokenPrivileges_f(hProcessToken, FALSE, &prevTp, 0, nullptr, nullptr); - } - } - - CloseHandle(hProcessToken); - - return mem; - - #endif -} - -void* aligned_large_pages_alloc(size_t allocSize) { - - // Try to allocate large pages - void* mem = aligned_large_pages_alloc_windows(allocSize); - - // Fall back to regular, page-aligned, allocation if necessary - if (!mem) - mem = VirtualAlloc(nullptr, allocSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); - - return mem; -} - -#else - -void* aligned_large_pages_alloc(size_t allocSize) { - - #if defined(__linux__) - constexpr size_t alignment = 2 * 1024 * 1024; // assumed 2MB page size - #else - constexpr size_t alignment = 4096; // assumed small page size - #endif - - // Round up to multiples of alignment - size_t size = ((allocSize + alignment - 1) / alignment) * alignment; - void* mem = std_aligned_alloc(alignment, size); - #if defined(MADV_HUGEPAGE) - madvise(mem, size, MADV_HUGEPAGE); - #endif - return mem; -} - -#endif - - -// aligned_large_pages_free() will free the previously allocated ttmem - -#if defined(_WIN32) - -void aligned_large_pages_free(void* mem) { - - if (mem && !VirtualFree(mem, 0, MEM_RELEASE)) - { - DWORD err = GetLastError(); - std::cerr << "Failed to free large page memory. Error code: 0x" << std::hex << err - << std::dec << std::endl; - exit(EXIT_FAILURE); - } -} - -#else - -void aligned_large_pages_free(void* mem) { std_aligned_free(mem); } - -#endif - - #ifdef _WIN32 #include #define GETCWD _getcwd diff --git a/src/misc.h b/src/misc.h index 5c0bde44eda..557a4d8c5a4 100644 --- a/src/misc.h +++ b/src/misc.h @@ -26,10 +26,9 @@ #include #include #include -#include +#include #include #include -#include #define stringify2(x) #x #define stringify(x) stringify2(x) @@ -44,39 +43,10 @@ std::string compiler_info(); // which can be quite slow. void prefetch(const void* addr); -void start_logger(const std::string& fname); -void* std_aligned_alloc(size_t alignment, size_t size); -void std_aligned_free(void* ptr); -// memory aligned by page size, min alignment: 4096 bytes -void* aligned_large_pages_alloc(size_t size); -// nop if mem == nullptr -void aligned_large_pages_free(void* mem); +void start_logger(const std::string& fname); size_t str_to_size_t(const std::string& s); -// Deleter for automating release of memory area -template -struct AlignedDeleter { - void operator()(T* ptr) const { - ptr->~T(); - std_aligned_free(ptr); - } -}; - -template -struct LargePageDeleter { - void operator()(T* ptr) const { - ptr->~T(); - aligned_large_pages_free(ptr); - } -}; - -template -using AlignedPtr = std::unique_ptr>; - -template -using LargePagePtr = std::unique_ptr>; - #if defined(__linux__) struct PipeDeleter { @@ -141,20 +111,6 @@ std::ostream& operator<<(std::ostream&, SyncCout); #define sync_cout std::cout << IO_LOCK #define sync_endl std::endl << IO_UNLOCK - -// Get the first aligned element of an array. -// ptr must point to an array of size at least `sizeof(T) * N + alignment` bytes, -// where N is the number of elements in the array. -template -T* align_ptr_up(T* ptr) { - static_assert(alignof(T) < Alignment); - - const uintptr_t ptrint = reinterpret_cast(reinterpret_cast(ptr)); - return reinterpret_cast( - reinterpret_cast((ptrint + (Alignment - 1)) / Alignment * Alignment)); -} - - // True if and only if the binary is compiled on a little-endian machine static inline const union { uint32_t i; diff --git a/src/nnue/network.cpp b/src/nnue/network.cpp index db864fcd384..71c384ffc72 100644 --- a/src/nnue/network.cpp +++ b/src/nnue/network.cpp @@ -20,7 +20,6 @@ #include #include -#include #include #include #include @@ -30,6 +29,7 @@ #include "../evaluate.h" #include "../incbin/incbin.h" +#include "../memory.h" #include "../misc.h" #include "../position.h" #include "../types.h" @@ -86,23 +86,6 @@ namespace Stockfish::Eval::NNUE { namespace Detail { -// Initialize the evaluation function parameters -template -void initialize(AlignedPtr& pointer) { - - pointer.reset(reinterpret_cast(std_aligned_alloc(alignof(T), sizeof(T)))); - std::memset(pointer.get(), 0, sizeof(T)); -} - -template -void initialize(LargePagePtr& pointer) { - - static_assert(alignof(T) <= 4096, - "aligned_large_pages_alloc() may fail for such a big alignment requirement of T"); - pointer.reset(reinterpret_cast(aligned_large_pages_alloc(sizeof(T)))); - std::memset(pointer.get(), 0, sizeof(T)); -} - // Read evaluation function parameters template bool read_parameters(std::istream& stream, T& reference) { @@ -128,19 +111,17 @@ template Network::Network(const Network& other) : evalFile(other.evalFile), embeddedType(other.embeddedType) { + if (other.featureTransformer) - { - Detail::initialize(featureTransformer); - *featureTransformer = *other.featureTransformer; - } + featureTransformer = make_unique_large_page(*other.featureTransformer); + + network = make_unique_aligned(LayerStacks); + + if (!other.network) + return; + for (std::size_t i = 0; i < LayerStacks; ++i) - { - if (other.network[i]) - { - Detail::initialize(network[i]); - *(network[i]) = *(other.network[i]); - } - } + network[i] = other.network[i]; } template @@ -150,18 +131,15 @@ Network::operator=(const Network& other) { embeddedType = other.embeddedType; if (other.featureTransformer) - { - Detail::initialize(featureTransformer); - *featureTransformer = *other.featureTransformer; - } + featureTransformer = make_unique_large_page(*other.featureTransformer); + + network = make_unique_aligned(LayerStacks); + + if (!other.network) + return *this; + for (std::size_t i = 0; i < LayerStacks; ++i) - { - if (other.network[i]) - { - Detail::initialize(network[i]); - *(network[i]) = *(other.network[i]); - } - } + network[i] = other.network[i]; return *this; } @@ -253,7 +231,7 @@ Value Network::evaluate(const Position& const int bucket = (pos.count() - 1) / 4; const auto psqt = featureTransformer->transform(pos, cache, transformedFeatures, bucket); - const auto positional = network[bucket]->propagate(transformedFeatures); + const auto positional = network[bucket].propagate(transformedFeatures); if (complexity) *complexity = std::abs(psqt - positional) / OutputScale; @@ -292,11 +270,11 @@ void Network::verify(std::string evalfilePath) const { exit(EXIT_FAILURE); } - size_t size = sizeof(*featureTransformer) + sizeof(*network) * LayerStacks; + size_t size = sizeof(*featureTransformer) + sizeof(Arch) * LayerStacks; sync_cout << "info string NNUE evaluation using " << evalfilePath << " (" << size / (1024 * 1024) << "MiB, (" << featureTransformer->InputDimensions << ", " - << network[0]->TransformedFeatureDimensions << ", " << network[0]->FC_0_OUTPUTS - << ", " << network[0]->FC_1_OUTPUTS << ", 1))" << sync_endl; + << network[0].TransformedFeatureDimensions << ", " << network[0].FC_0_OUTPUTS << ", " + << network[0].FC_1_OUTPUTS << ", 1))" << sync_endl; } @@ -333,7 +311,7 @@ Network::trace_evaluate(const Position& { const auto materialist = featureTransformer->transform(pos, cache, transformedFeatures, bucket); - const auto positional = network[bucket]->propagate(transformedFeatures); + const auto positional = network[bucket].propagate(transformedFeatures); t.psqt[bucket] = static_cast(materialist / OutputScale); t.positional[bucket] = static_cast(positional / OutputScale); @@ -386,9 +364,8 @@ void Network::load_internal() { template void Network::initialize() { - Detail::initialize(featureTransformer); - for (std::size_t i = 0; i < LayerStacks; ++i) - Detail::initialize(network[i]); + featureTransformer = make_unique_large_page(); + network = make_unique_aligned(LayerStacks); } @@ -455,7 +432,7 @@ bool Network::read_parameters(std::istream& stream, return false; for (std::size_t i = 0; i < LayerStacks; ++i) { - if (!Detail::read_parameters(stream, *(network[i]))) + if (!Detail::read_parameters(stream, network[i])) return false; } return stream && stream.peek() == std::ios::traits_type::eof(); @@ -471,7 +448,7 @@ bool Network::write_parameters(std::ostream& stream, return false; for (std::size_t i = 0; i < LayerStacks; ++i) { - if (!Detail::write_parameters(stream, *(network[i]))) + if (!Detail::write_parameters(stream, network[i])) return false; } return bool(stream); diff --git a/src/nnue/network.h b/src/nnue/network.h index f0ccfafcb4c..6ba3cfbab8d 100644 --- a/src/nnue/network.h +++ b/src/nnue/network.h @@ -25,13 +25,13 @@ #include #include -#include "../misc.h" +#include "../memory.h" #include "../position.h" #include "../types.h" +#include "nnue_accumulator.h" #include "nnue_architecture.h" #include "nnue_feature_transformer.h" #include "nnue_misc.h" -#include "nnue_accumulator.h" namespace Stockfish::Eval::NNUE { @@ -91,7 +91,7 @@ class Network { LargePagePtr featureTransformer; // Evaluation function - AlignedPtr network[LayerStacks]; + AlignedPtr network; EvalFile evalFile; EmbeddedNNUEType embeddedType; diff --git a/src/numa.h b/src/numa.h index 5934a0cd8dd..a56d7142d9d 100644 --- a/src/numa.h +++ b/src/numa.h @@ -32,6 +32,7 @@ #include #include #include +#include // We support linux very well, but we explicitly do NOT support Android, because there's // no affected systems, not worth maintaining. diff --git a/src/thread.h b/src/thread.h index 102b229907b..7416271b4c1 100644 --- a/src/thread.h +++ b/src/thread.h @@ -23,15 +23,15 @@ #include #include #include +#include #include #include #include -#include +#include "numa.h" #include "position.h" #include "search.h" #include "thread_win32_osx.h" -#include "numa.h" namespace Stockfish { diff --git a/src/tt.cpp b/src/tt.cpp index f95170e9406..f808106a6e1 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -24,7 +24,7 @@ #include #include -#include "misc.h" +#include "memory.h" #include "syzygy/tbprobe.h" #include "thread.h" @@ -75,11 +75,10 @@ uint8_t TTEntry::relative_age(const uint8_t generation8) const { // measured in megabytes. Transposition table consists // of clusters and each cluster consists of ClusterSize number of TTEntry. void TranspositionTable::resize(size_t mbSize, ThreadPool& threads) { - aligned_large_pages_free(table); - clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster); - table = static_cast(aligned_large_pages_alloc(clusterCount * sizeof(Cluster))); + table = make_unique_large_page(clusterCount); + if (!table) { std::cerr << "Failed to allocate " << mbSize << "MB for transposition table." << std::endl; diff --git a/src/tt.h b/src/tt.h index 3b09ec4e1d9..2dcfdd44b9d 100644 --- a/src/tt.h +++ b/src/tt.h @@ -21,7 +21,9 @@ #include #include +#include +#include "memory.h" #include "misc.h" #include "types.h" @@ -94,8 +96,6 @@ class TranspositionTable { static constexpr int GENERATION_MASK = (0xFF << GENERATION_BITS) & 0xFF; public: - ~TranspositionTable() { aligned_large_pages_free(table); } - void new_search() { // increment by delta to keep lower bits as is generation8 += GENERATION_DELTA; @@ -115,9 +115,9 @@ class TranspositionTable { private: friend struct TTEntry; - size_t clusterCount; - Cluster* table = nullptr; - uint8_t generation8 = 0; // Size must be not bigger than TTEntry::genBound8 + size_t clusterCount; + LargePagePtr table; + uint8_t generation8 = 0; // Size must be not bigger than TTEntry::genBound8 }; } // namespace Stockfish From 3d6756769cd159edf1d7eaec074c880551590c32 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sun, 2 Jun 2024 18:40:32 +0300 Subject: [PATCH 1568/1766] Simplify continuation histories Functional simplification. Simplify continuation histories, therefore increasing the effect of stats updates and movepicker bonuses for continuation history 3 plies deep. Passed STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 229184 W: 59087 L: 59080 D: 111017 Ptnml(0-2): 554, 27248, 59002, 27213, 575 https://tests.stockfishchess.org/tests/view/665c7a09fd45fb0f907c223b Passed LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 44532 W: 11419 L: 11223 D: 21890 Ptnml(0-2): 18, 4787, 12457, 4989, 15 https://tests.stockfishchess.org/tests/view/665c8842fd45fb0f907c23ec closes https://github.com/official-stockfish/Stockfish/pull/5339 Bench: 1326444 --- src/movepick.cpp | 2 +- src/search.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index d333590751c..52e8c526a10 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -182,7 +182,7 @@ void MovePicker::score() { m.value += 2 * (*pawnHistory)[pawn_structure_index(pos)][pc][to]; m.value += 2 * (*continuationHistory[0])[pc][to]; m.value += (*continuationHistory[1])[pc][to]; - m.value += (*continuationHistory[2])[pc][to] / 4; + m.value += (*continuationHistory[2])[pc][to] / 3; m.value += (*continuationHistory[3])[pc][to]; m.value += (*continuationHistory[5])[pc][to]; diff --git a/src/search.cpp b/src/search.cpp index a2a75af0caf..44da868366a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1811,7 +1811,7 @@ void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { if (ss->inCheck && i > 2) break; if (((ss - i)->currentMove).is_ok()) - (*(ss - i)->continuationHistory)[pc][to] << bonus / (1 + 3 * (i == 3)); + (*(ss - i)->continuationHistory)[pc][to] << bonus / (1 + (i == 3)); } } From 924a843594743297f47edf7b0931ede8dcbb6dd8 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sun, 2 Jun 2024 23:32:58 +0300 Subject: [PATCH 1569/1766] Simplify recapture extension Simplifying the extension formula by removing the move == ttMove condition. Passed STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 47328 W: 12324 L: 12117 D: 22887 Ptnml(0-2): 134, 5532, 12097, 5795, 106 https://tests.stockfishchess.org/tests/view/665ca5e6fd45fb0f907c41be Passed LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 72126 W: 18378 L: 18209 D: 35539 Ptnml(0-2): 36, 7841, 20130, 8030, 26 https://tests.stockfishchess.org/tests/view/665cb276fd45fb0f907c41f9 closes https://github.com/official-stockfish/Stockfish/pull/5341 Bench: 1399468 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 44da868366a..4defbadb40b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1103,7 +1103,7 @@ Value Search::Worker::search( } // Extension for capturing the previous moved piece (~0 Elo on STC, ~1 Elo on LTC) - else if (PvNode && move == ttMove && move.to_sq() == prevSq + else if (PvNode && move.to_sq() == prevSq && thisThread->captureHistory[movedPiece][move.to_sq()] [type_of(pos.piece_on(move.to_sq()))] > 3988) From fe298953f89a86e7edfb0e53605d9d9c47f7ceea Mon Sep 17 00:00:00 2001 From: Gahtan Nahdi <155860115+gahtan-syarif@users.noreply.github.com> Date: Sun, 2 Jun 2024 05:26:34 +0700 Subject: [PATCH 1570/1766] Simplify smallnet threshold Turns the quadratic threshold to a linear one STC non-reg: https://tests.stockfishchess.org/tests/view/665ba0b744e8416a9cdc188d LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 330432 W: 85351 L: 85454 D: 159627 Ptnml(0-2): 888, 39643, 84283, 39488, 914 LTC non-reg: https://tests.stockfishchess.org/tests/view/665cd60ffd45fb0f907c4306 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 139146 W: 35194 L: 35093 D: 68859 Ptnml(0-2): 58, 15523, 38313, 15618, 61 closes https://github.com/official-stockfish/Stockfish/pull/5342 Bench: 1057383 --- src/evaluate.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index eaf7ab5f981..064ea027baa 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -46,8 +46,7 @@ int Eval::simple_eval(const Position& pos, Color c) { bool Eval::use_smallnet(const Position& pos) { int simpleEval = simple_eval(pos, pos.side_to_move()); - int pawnCount = pos.count(); - return std::abs(simpleEval) > 992 + 6 * pawnCount * pawnCount / 16; + return std::abs(simpleEval) > 992 + 10 * pos.count(); } // Evaluate is the evaluator for the outer world. It returns a static evaluation From 397f47a7a1b7abe490d7bcb7a526d01555aed2be Mon Sep 17 00:00:00 2001 From: Dubslow Date: Sun, 2 Jun 2024 16:27:58 -0500 Subject: [PATCH 1571/1766] Adjust lowest depth constants to the natural place Passed STC: https://tests.stockfishchess.org/tests/view/665ce3f8fd45fb0f907c537f LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 282784 W: 73032 L: 73082 D: 136670 Ptnml(0-2): 680, 31845, 76364, 31851, 652 Recently when I overhauled these comments, Disservin asked why these were so much lower: they're a relic from when we had a third QS stage at -5. Now we don't, so fix these to the obvious place. I was fairly sure it was nonfunctional but ran the nonreg to be double sure. closes https://github.com/official-stockfish/Stockfish/pull/5343 Bench: 1057383 --- src/types.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types.h b/src/types.h index aa4af012b12..10ad1fac9ef 100644 --- a/src/types.h +++ b/src/types.h @@ -196,8 +196,8 @@ enum : int { // For TT entries where no searching at all was done (whether regular or qsearch) we use // _UNSEARCHED, which should thus compare lower than any QS or regular depth. _ENTRY_OFFSET is used // only for the TT entry occupancy check (see tt.cpp), and should thus be lower than _UNSEARCHED. - DEPTH_UNSEARCHED = -6, - DEPTH_ENTRY_OFFSET = -7 + DEPTH_UNSEARCHED = -2, + DEPTH_ENTRY_OFFSET = -3 }; // clang-format off From 86b564055d753c49dede0b8549363f3ee11c572e Mon Sep 17 00:00:00 2001 From: Dubslow Date: Sun, 2 Jun 2024 16:55:10 -0500 Subject: [PATCH 1572/1766] Remove delta, adjusted, complexity from nnue code ...rather they're the consumer's concern whether to tweak the result or not. Passed STC: https://tests.stockfishchess.org/tests/view/665cea9ffd45fb0f907c53bd LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 69696 W: 18101 L: 17918 D: 33677 Ptnml(0-2): 195, 8171, 17929, 8362, 191 Passed LTC: https://tests.stockfishchess.org/tests/view/665cf761fd45fb0f907c5406 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 63720 W: 16344 L: 16165 D: 31211 Ptnml(0-2): 32, 6990, 17625, 7193, 20 Non functional except for rounding issues of OutputScale changing bench. closes https://github.com/official-stockfish/Stockfish/pull/5344 Bench: 1378596 --- src/evaluate.cpp | 23 +++++++++++++++-------- src/nnue/network.cpp | 20 ++++---------------- src/nnue/network.h | 8 ++++---- src/nnue/nnue_misc.cpp | 13 ++++++++----- 4 files changed, 31 insertions(+), 33 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 064ea027baa..248b2593333 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -24,8 +24,9 @@ #include #include #include -#include #include +#include +#include #include "nnue/network.h" #include "nnue/nnue_misc.h" @@ -60,17 +61,22 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, int simpleEval = simple_eval(pos, pos.side_to_move()); bool smallNet = use_smallnet(pos); - int nnueComplexity; int v; - Value nnue = smallNet ? networks.small.evaluate(pos, &caches.small, true, &nnueComplexity) - : networks.big.evaluate(pos, &caches.big, true, &nnueComplexity); + auto [psqt, positional] = smallNet ? networks.small.evaluate(pos, &caches.small) + : networks.big.evaluate(pos, &caches.big); + + constexpr int delta = 3; + Value nnue = ((128 - delta) * psqt + (128 + delta) * positional) / 128; + int nnueComplexity = std::abs(psqt - positional); // Re-evaluate the position when higher eval accuracy is worth the time spent if (smallNet && (nnue * simpleEval < 0 || std::abs(nnue) < 250)) { - nnue = networks.big.evaluate(pos, &caches.big, true, &nnueComplexity); - smallNet = false; + std::tie(psqt, positional) = networks.big.evaluate(pos, &caches.big); + nnue = ((128 - delta) * psqt + (128 + delta) * positional) / 128; + nnueComplexity = std::abs(psqt - positional); + smallNet = false; } // Blend optimism and eval with nnue complexity @@ -108,8 +114,9 @@ std::string Eval::trace(Position& pos, const Eval::NNUE::Networks& networks) { ss << std::showpoint << std::showpos << std::fixed << std::setprecision(2) << std::setw(15); - Value v = networks.big.evaluate(pos, &caches->big, false); - v = pos.side_to_move() == WHITE ? v : -v; + auto [psqt, positional] = networks.big.evaluate(pos, &caches->big); + Value v = psqt + positional; + v = pos.side_to_move() == WHITE ? v : -v; ss << "NNUE evaluation " << 0.01 * UCIEngine::to_cp(v, pos) << " (white side)\n"; v = evaluate(networks, pos, *caches, VALUE_ZERO); diff --git a/src/nnue/network.cpp b/src/nnue/network.cpp index 71c384ffc72..f7d2cc6ada0 100644 --- a/src/nnue/network.cpp +++ b/src/nnue/network.cpp @@ -18,7 +18,6 @@ #include "network.h" -#include #include #include #include @@ -206,15 +205,13 @@ bool Network::save(const std::optional& filename template -Value Network::evaluate(const Position& pos, - AccumulatorCaches::Cache* cache, - bool adjusted, - int* complexity) const { +NetworkOutput +Network::evaluate(const Position& pos, + AccumulatorCaches::Cache* cache) const { // We manually align the arrays on the stack because with gcc < 9.3 // overaligning stack variables with alignas() doesn't work correctly. constexpr uint64_t alignment = CacheLineSize; - constexpr int delta = 24; #if defined(ALIGNAS_ON_STACK_VARIABLES_BROKEN) TransformedFeatureType @@ -232,16 +229,7 @@ Value Network::evaluate(const Position& const int bucket = (pos.count() - 1) / 4; const auto psqt = featureTransformer->transform(pos, cache, transformedFeatures, bucket); const auto positional = network[bucket].propagate(transformedFeatures); - - if (complexity) - *complexity = std::abs(psqt - positional) / OutputScale; - - // Give more value to positional evaluation when adjusted flag is set - if (adjusted) - return static_cast(((1024 - delta) * psqt + (1024 + delta) * positional) - / (1024 * OutputScale)); - else - return static_cast((psqt + positional) / OutputScale); + return {static_cast(psqt / OutputScale), static_cast(positional / OutputScale)}; } diff --git a/src/nnue/network.h b/src/nnue/network.h index 6ba3cfbab8d..152082552c9 100644 --- a/src/nnue/network.h +++ b/src/nnue/network.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "../memory.h" @@ -40,6 +41,7 @@ enum class EmbeddedNNUEType { SMALL, }; +using NetworkOutput = std::tuple; template class Network { @@ -59,10 +61,8 @@ class Network { void load(const std::string& rootDirectory, std::string evalfilePath); bool save(const std::optional& filename) const; - Value evaluate(const Position& pos, - AccumulatorCaches::Cache* cache, - bool adjusted = false, - int* complexity = nullptr) const; + NetworkOutput evaluate(const Position& pos, + AccumulatorCaches::Cache* cache) const; void hint_common_access(const Position& pos, diff --git a/src/nnue/nnue_misc.cpp b/src/nnue/nnue_misc.cpp index a13c717c3d8..7585cce5ab6 100644 --- a/src/nnue/nnue_misc.cpp +++ b/src/nnue/nnue_misc.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include "../evaluate.h" #include "../position.h" @@ -131,8 +132,9 @@ trace(Position& pos, const Eval::NNUE::Networks& networks, Eval::NNUE::Accumulat // We estimate the value of each piece by doing a differential evaluation from // the current base eval, simulating the removal of the piece from its square. - Value base = networks.big.evaluate(pos, &caches.big); - base = pos.side_to_move() == WHITE ? base : -base; + auto [psqt, positional] = networks.big.evaluate(pos, &caches.big); + Value base = psqt + positional; + base = pos.side_to_move() == WHITE ? base : -base; for (File f = FILE_A; f <= FILE_H; ++f) for (Rank r = RANK_1; r <= RANK_8; ++r) @@ -148,9 +150,10 @@ trace(Position& pos, const Eval::NNUE::Networks& networks, Eval::NNUE::Accumulat pos.remove_piece(sq); st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] = false; - Value eval = networks.big.evaluate(pos, &caches.big); - eval = pos.side_to_move() == WHITE ? eval : -eval; - v = base - eval; + std::tie(psqt, positional) = networks.big.evaluate(pos, &caches.big); + Value eval = psqt + positional; + eval = pos.side_to_move() == WHITE ? eval : -eval; + v = base - eval; pos.put_piece(pc, sq); st->accumulatorBig.computed[WHITE] = st->accumulatorBig.computed[BLACK] = false; From ba06671aa9df5c0a3fa5f1fa2ce17ea4aa742b7a Mon Sep 17 00:00:00 2001 From: Disservin Date: Mon, 3 Jun 2024 19:47:34 +0200 Subject: [PATCH 1573/1766] Normalize some variable names and reuse existing logic closes https://github.com/official-stockfish/Stockfish/pull/5346 No functional change --- src/search.cpp | 4 ++-- src/search.h | 6 +++--- src/thread.cpp | 4 +--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 4defbadb40b..c03fe7811de 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -137,10 +137,10 @@ void update_all_stats(const Position& pos, Search::Worker::Worker(SharedState& sharedState, std::unique_ptr sm, - size_t thread_id, + size_t threadId, NumaReplicatedAccessToken token) : // Unpack the SharedState struct into member variables - thread_idx(thread_id), + threadIdx(threadId), numaAccessToken(token), manager(std::move(sm)), options(sharedState.options), diff --git a/src/search.h b/src/search.h index 01f7b8bdb00..a22d32004f5 100644 --- a/src/search.h +++ b/src/search.h @@ -244,7 +244,7 @@ class Worker { // It searches from the root position and outputs the "bestmove". void start_searching(); - bool is_mainthread() const { return thread_idx == 0; } + bool is_mainthread() const { return threadIdx == 0; } // Public because they need to be updatable by the stats CounterMoveHistory counterMoves; @@ -270,7 +270,7 @@ class Worker { // Get a pointer to the search manager, only allowed to be called by the // main thread. SearchManager* main_manager() const { - assert(thread_idx == 0); + assert(threadIdx == 0); return static_cast(manager.get()); } @@ -291,7 +291,7 @@ class Worker { Depth rootDepth, completedDepth; Value rootDelta; - size_t thread_idx; + size_t threadIdx; NumaReplicatedAccessToken numaAccessToken; // Reductions lookup table initialized at startup diff --git a/src/thread.cpp b/src/thread.cpp index a36c2efb7c1..0a33422acc9 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -127,9 +127,7 @@ void Thread::idle_loop() { } } -Search::SearchManager* ThreadPool::main_manager() { - return static_cast(main_thread()->worker.get()->manager.get()); -} +Search::SearchManager* ThreadPool::main_manager() { return main_thread()->worker->main_manager(); } uint64_t ThreadPool::nodes_searched() const { return accumulate(&Search::Worker::nodes); } uint64_t ThreadPool::tb_hits() const { return accumulate(&Search::Worker::tbHits); } From 7f09d06b834a5aaedbc78c5161ba91a8d6761421 Mon Sep 17 00:00:00 2001 From: Disservin Date: Tue, 4 Jun 2024 07:53:25 +0200 Subject: [PATCH 1574/1766] Properly initialize the TT in a multithreaded way again --- src/tt.cpp | 4 +++- src/tt.h | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/tt.cpp b/src/tt.cpp index f808106a6e1..56779b861fd 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -75,9 +75,11 @@ uint8_t TTEntry::relative_age(const uint8_t generation8) const { // measured in megabytes. Transposition table consists // of clusters and each cluster consists of ClusterSize number of TTEntry. void TranspositionTable::resize(size_t mbSize, ThreadPool& threads) { + aligned_large_pages_free(table); + clusterCount = mbSize * 1024 * 1024 / sizeof(Cluster); - table = make_unique_large_page(clusterCount); + table = static_cast(aligned_large_pages_alloc(clusterCount * sizeof(Cluster))); if (!table) { diff --git a/src/tt.h b/src/tt.h index 2dcfdd44b9d..974c7eb0c57 100644 --- a/src/tt.h +++ b/src/tt.h @@ -96,6 +96,7 @@ class TranspositionTable { static constexpr int GENERATION_MASK = (0xFF << GENERATION_BITS) & 0xFF; public: + ~TranspositionTable() { aligned_large_pages_free(table); } void new_search() { // increment by delta to keep lower bits as is generation8 += GENERATION_DELTA; @@ -115,9 +116,9 @@ class TranspositionTable { private: friend struct TTEntry; - size_t clusterCount; - LargePagePtr table; - uint8_t generation8 = 0; // Size must be not bigger than TTEntry::genBound8 + size_t clusterCount; + Cluster* table = nullptr; + uint8_t generation8 = 0; // Size must be not bigger than TTEntry::genBound8 }; } // namespace Stockfish From 4f53560d248195b172ac97d7c74e6bcfc65fe6fd Mon Sep 17 00:00:00 2001 From: Disservin Date: Tue, 4 Jun 2024 07:57:08 +0200 Subject: [PATCH 1575/1766] Accumulate nodes over all bench positions not just the last closes https://github.com/official-stockfish/Stockfish/pull/5352 No functional change --- src/tt.h | 1 - src/uci.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tt.h b/src/tt.h index 974c7eb0c57..b2e8f582b3b 100644 --- a/src/tt.h +++ b/src/tt.h @@ -21,7 +21,6 @@ #include #include -#include #include "memory.h" #include "misc.h" diff --git a/src/uci.cpp b/src/uci.cpp index 4b683116a9e..43b0e005a16 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -296,7 +296,7 @@ void UCIEngine::bench(std::istream& args) { Search::LimitsType limits = parse_limits(is); if (limits.perft) - nodes = perft(limits); + nodesSearched = perft(limits); else { engine.go(limits); From daaccd9fc9ca2dcc8ed7c72075fb1d3f504fa6ef Mon Sep 17 00:00:00 2001 From: Gahtan Nahdi <155860115+gahtan-syarif@users.noreply.github.com> Date: Tue, 4 Jun 2024 05:31:51 +0700 Subject: [PATCH 1576/1766] Simplify smallnet threshold remove pawncount Passed STC non-reg: https://tests.stockfishchess.org/tests/view/665e4548fd45fb0f907c80d5 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 60896 W: 15710 L: 15518 D: 29668 Ptnml(0-2): 149, 7145, 15660, 7353, 141 Passed LTC non-reg: https://tests.stockfishchess.org/tests/view/665e4c52fd45fb0f907c815f LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 58068 W: 14773 L: 14590 D: 28705 Ptnml(0-2): 16, 6368, 16090, 6537, 23 closes https://github.com/official-stockfish/Stockfish/pull/5349 Bench: 1343156 --- src/evaluate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 248b2593333..afba6363bc6 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -47,7 +47,7 @@ int Eval::simple_eval(const Position& pos, Color c) { bool Eval::use_smallnet(const Position& pos) { int simpleEval = simple_eval(pos, pos.side_to_move()); - return std::abs(simpleEval) > 992 + 10 * pos.count(); + return std::abs(simpleEval) > 992; } // Evaluate is the evaluator for the outer world. It returns a static evaluation From 02ff76630b358e5f958793cc93df0009d2da65a5 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Tue, 4 Jun 2024 12:48:13 +0200 Subject: [PATCH 1577/1766] Add NumaPolicy "hardware" option that bypasses current processor affinity. Can be used in case a GUI (e.g. ChessBase 17 see #5307) sets affinity to a single processor group, but the user would like to use the full capabilities of the hardware. Improves affinity handling on Windows in case of multiple available APIs and existing affinities. closes https://github.com/official-stockfish/Stockfish/pull/5353 No functional change --- src/engine.cpp | 5 + src/numa.h | 394 ++++++++++++++++++++++++++++--------------------- 2 files changed, 232 insertions(+), 167 deletions(-) diff --git a/src/engine.cpp b/src/engine.cpp index 3fc27223a09..6980dd8341c 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -133,6 +133,11 @@ void Engine::set_numa_config_from_option(const std::string& o) { { numaContext.set_numa_config(NumaConfig::from_system()); } + else if (o == "hardware") + { + // Don't respect affinity set in the system. + numaContext.set_numa_config(NumaConfig::from_system(false)); + } else if (o == "none") { numaContext.set_numa_config(NumaConfig{}); diff --git a/src/numa.h b/src/numa.h index a56d7142d9d..c170c178e60 100644 --- a/src/numa.h +++ b/src/numa.h @@ -19,6 +19,7 @@ #ifndef NUMA_H_INCLUDED #define NUMA_H_INCLUDED +#include #include #include #include @@ -63,21 +64,9 @@ static constexpr size_t WIN_PROCESSOR_GROUP_SIZE = 64; // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadselectedcpusetmasks using SetThreadSelectedCpuSetMasks_t = BOOL (*)(HANDLE, PGROUP_AFFINITY, USHORT); -// https://learn.microsoft.com/en-us/windows/win32/api/processtopologyapi/nf-processtopologyapi-setthreadgroupaffinity -using SetThreadGroupAffinity_t = BOOL (*)(HANDLE, const GROUP_AFFINITY*, PGROUP_AFFINITY); - // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getthreadselectedcpusetmasks using GetThreadSelectedCpuSetMasks_t = BOOL (*)(HANDLE, PGROUP_AFFINITY, USHORT, PUSHORT); -// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getprocessaffinitymask -using GetProcessAffinityMask_t = BOOL (*)(HANDLE, PDWORD_PTR, PDWORD_PTR); - -// https://learn.microsoft.com/en-us/windows/win32/api/processtopologyapi/nf-processtopologyapi-getprocessgroupaffinity -using GetProcessGroupAffinity_t = BOOL (*)(HANDLE, PUSHORT, PUSHORT); - -// https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getactiveprocessorcount -using GetActiveProcessorCount_t = DWORD (*)(WORD); - #endif #include "misc.h" @@ -94,14 +83,7 @@ inline CpuIndex get_hardware_concurrency() { // only returns the number of processors in the first group, because only these // are available to std::thread. #ifdef _WIN64 - HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll")); - auto GetActiveProcessorCount_f = - GetActiveProcessorCount_t((void (*)()) GetProcAddress(k32, "GetActiveProcessorCount")); - - if (GetActiveProcessorCount_f != nullptr) - { - concurrency = GetActiveProcessorCount_f(ALL_PROCESSOR_GROUPS); - } + concurrency = std::max(concurrency, GetActiveProcessorCount(ALL_PROCESSOR_GROUPS)); #endif return concurrency; @@ -109,6 +91,214 @@ inline CpuIndex get_hardware_concurrency() { inline const CpuIndex SYSTEM_THREADS_NB = std::max(1, get_hardware_concurrency()); +#if defined(_WIN64) + +struct WindowsAffinity { + std::optional> oldApi; + std::optional> newApi; + bool isDeterminate = true; + + std::optional> get_combined() const { + // When the affinity is not determinate we treat it as no affinity, + // because otherwise we would have to set affinity to fewer + // processors than we currently have affinity to. + if (!isDeterminate) + return std::nullopt; + + if (!oldApi.has_value()) + return newApi; + if (!newApi.has_value()) + return oldApi; + + std::set intersect; + std::set_intersection(oldApi->begin(), oldApi->end(), newApi->begin(), newApi->end(), + std::inserter(intersect, intersect.begin())); + return intersect; + } +}; + +inline std::pair> get_process_group_affinity() { + WORD numProcGroups = GetActiveProcessorGroupCount(); + + // GetProcessGroupAffinity requires the GroupArray argument to be + // aligned to 4 bytes instead of just 2. + static constexpr size_t GroupArrayMinimumAlignment = 4; + static_assert(GroupArrayMinimumAlignment >= alignof(USHORT)); + + auto GroupArray = std::make_unique( + numProcGroups + (GroupArrayMinimumAlignment / alignof(USHORT) - 1)); + + USHORT GroupCount = static_cast(numProcGroups); + const BOOL status = GetProcessGroupAffinity(GetCurrentProcess(), &GroupCount, GroupArray.get()); + + return std::make_pair(status, std::vector(GroupArray.get(), GroupArray.get() + GroupCount)); +} + +// Since Windows 11 and Windows Server 2022 thread affinities can span +// processor groups and can be set as such by a new WinAPI function. +// However, we may need to force using the old API if we detect +// that the process has affinity set by the old API already and we want to override that. +inline bool use_old_affinity_api() { + HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll")); + auto SetThreadSelectedCpuSetMasks_f = SetThreadSelectedCpuSetMasks_t( + (void (*)()) GetProcAddress(k32, "SetThreadSelectedCpuSetMasks")); + + if (SetThreadSelectedCpuSetMasks_f == nullptr) + return true; + + auto [status, groupAffinity] = get_process_group_affinity(); + + // If GroupCount > 1 then we know old API was never used and we can stick + // to the new API safely. + if (status != 0 && groupAffinity.size() > 1) + return false; + + return true; +}; + +// On Windows there are two ways to set affinity, and therefore 2 ways to get it. +// These are not consistent, so we have to check both. +// In some cases it is actually not possible to determine affinity. +// For example when two different threads have affinity on different processor groups, +// set using SetThreadAffinityMask, we can't retrieve the actual affinities. +// From documentation on GetProcessAffinityMask: +// > If the calling process contains threads in multiple groups, +// > the function returns zero for both affinity masks. +// In such cases we just give up and assume we have affinity for all processors. +// nullopt means no affinity is set, that is, all processors are allowed +inline WindowsAffinity get_process_affinity() { + HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll")); + auto GetThreadSelectedCpuSetMasks_f = GetThreadSelectedCpuSetMasks_t( + (void (*)()) GetProcAddress(k32, "GetThreadSelectedCpuSetMasks")); + + WindowsAffinity affinity; + + if (GetThreadSelectedCpuSetMasks_f != nullptr) + { + USHORT RequiredMaskCount; + BOOL status = + GetThreadSelectedCpuSetMasks_f(GetCurrentThread(), nullptr, 0, &RequiredMaskCount); + + // If RequiredMaskCount then these affinities were never set, but it's not consistent + // so GetProcessAffinityMask may still return some affinity. + if (status == 0) + { + affinity.isDeterminate = false; + return affinity; + } + + if (RequiredMaskCount > 0) + { + std::set cpus; + + auto groupAffinities = std::make_unique(RequiredMaskCount); + + GetThreadSelectedCpuSetMasks_f(GetCurrentThread(), groupAffinities.get(), + RequiredMaskCount, &RequiredMaskCount); + + for (USHORT i = 0; i < RequiredMaskCount; ++i) + { + const size_t procGroupIndex = groupAffinities[i].Group; + + for (size_t j = 0; j < WIN_PROCESSOR_GROUP_SIZE; ++j) + { + if (groupAffinities[i].Mask & (KAFFINITY(1) << j)) + cpus.insert(procGroupIndex * WIN_PROCESSOR_GROUP_SIZE + j); + } + } + + affinity.newApi = std::move(cpus); + } + } + + DWORD_PTR proc, sys; + BOOL status = GetProcessAffinityMask(GetCurrentProcess(), &proc, &sys); + + // If proc == 0 then we can't determine affinity because it spans processor groups. + if (status == 0 || proc == 0) + { + affinity.isDeterminate = false; + return affinity; + } + + // If SetProcessAffinityMask was never called the affinity + // must span all processor groups, but if it was called it must only span one. + auto [status2, groupAffinity] = get_process_group_affinity(); + if (status2 == 0) + { + affinity.isDeterminate = false; + return affinity; + } + + // If we have affinity for more than 1 group then at this point we + // can assume SetProcessAffinityMask has never been called and therefore + // according ot old API we do not have any affinity set. + // Otherwise we have to assume we have affinity set and gather the processor IDs. + if (groupAffinity.size() == 1) + { + std::set cpus; + + const size_t procGroupIndex = groupAffinity[0]; + + uint64_t mask = static_cast(proc); + for (size_t j = 0; j < WIN_PROCESSOR_GROUP_SIZE; ++j) + { + if (mask & (KAFFINITY(1) << j)) + cpus.insert(procGroupIndex * WIN_PROCESSOR_GROUP_SIZE + j); + } + + affinity.oldApi = std::move(cpus); + } + + return affinity; +} + +#endif + +#if defined(__linux__) && !defined(__ANDROID__) + +inline std::set get_process_affinity() { + + std::set cpus; + + // For unsupported systems, or in case of a soft error, we may assume all processors + // are available for use. + [[maybe_unused]] auto set_to_all_cpus = [&]() { + for (CpuIndex c = 0; c < SYSTEM_THREADS_NB; ++c) + cpus.insert(c); + }; + + // cpu_set_t by default holds 1024 entries. This may not be enough soon, + // but there is no easy way to determine how many threads there actually is. + // In this case we just choose a reasonable upper bound. + static constexpr CpuIndex MaxNumCpus = 1024 * 64; + + cpu_set_t* mask = CPU_ALLOC(MaxNumCpus); + if (mask == nullptr) + std::exit(EXIT_FAILURE); + + const size_t masksize = CPU_ALLOC_SIZE(MaxNumCpus); + + CPU_ZERO_S(masksize, mask); + + const int status = sched_getaffinity(0, masksize, mask); + + if (status != 0) + { + CPU_FREE(mask); + std::exit(EXIT_FAILURE); + } + + for (CpuIndex c = 0; c < MaxNumCpus; ++c) + if (CPU_ISSET_S(c, masksize, mask)) + cpus.insert(c); + + CPU_FREE(mask); + + return cpus; +} + +#endif // We want to abstract the purpose of storing the numa node index somewhat. // Whoever is using this does not need to know the specifics of the replication @@ -224,7 +414,7 @@ class NumaConfig { std::optional> allowedCpus; if (respectProcessAffinity) - allowedCpus = get_process_affinity(); + allowedCpus = get_process_affinity().get_combined(); // The affinity can't be determined in all cases on Windows, but we at least guarantee // that the number of allowed processors is >= number of processors in the affinity mask. @@ -233,15 +423,6 @@ class NumaConfig { return !allowedCpus.has_value() || allowedCpus->count(c) == 1; }; - // Since Windows 11 and Windows Server 2022 thread affinities can span - // processor groups and can be set as such by a new WinAPI function. - static const bool CanAffinitySpanProcessorGroups = []() { - HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll")); - auto SetThreadSelectedCpuSetMasks_f = SetThreadSelectedCpuSetMasks_t( - (void (*)()) GetProcAddress(k32, "SetThreadSelectedCpuSetMasks")); - return SetThreadSelectedCpuSetMasks_f != nullptr; - }(); - WORD numProcGroups = GetActiveProcessorGroupCount(); for (WORD procGroup = 0; procGroup < numProcGroups; ++procGroup) { @@ -269,7 +450,8 @@ class NumaConfig { // the new NUMA allocation behaviour was introduced while there was // still no way to set thread affinity spanning multiple processor groups. // See https://learn.microsoft.com/en-us/windows/win32/procthread/numa-support - if (!CanAffinitySpanProcessorGroups) + // We also do this is if need to force old API for some reason. + if (use_old_affinity_api()) { NumaConfig splitCfg = empty(); @@ -307,6 +489,12 @@ class NumaConfig { // We have to ensure no empty NUMA nodes persist. cfg.remove_empty_numa_nodes(); + // If the user explicitly opts out from respecting the current process affinity + // then it may be inconsistent with the current affinity (obviously), so we + // consider it custom. + if (!respectProcessAffinity) + cfg.customAffinity = true; + return cfg; } @@ -510,9 +698,11 @@ class NumaConfig { HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll")); auto SetThreadSelectedCpuSetMasks_f = SetThreadSelectedCpuSetMasks_t( (void (*)()) GetProcAddress(k32, "SetThreadSelectedCpuSetMasks")); - auto SetThreadGroupAffinity_f = - SetThreadGroupAffinity_t((void (*)()) GetProcAddress(k32, "SetThreadGroupAffinity")); + // We ALWAYS set affinity with the new API if available, + // because there's no downsides, and we forcibly keep it consistent + // with the old API should we need to use it. I.e. we always keep this as a superset + // of what we set with SetThreadGroupAffinity. if (SetThreadSelectedCpuSetMasks_f != nullptr) { // Only available on Windows 11 and Windows Server 2022 onwards. @@ -541,7 +731,9 @@ class NumaConfig { // This is defensive, allowed because this code is not performance critical. SwitchToThread(); } - else if (SetThreadGroupAffinity_f != nullptr) + + // Sometimes we need to force the old API, but do not use it unless necessary. + if (SetThreadSelectedCpuSetMasks_f == nullptr || use_old_affinity_api()) { // On earlier windows version (since windows 7) we can't run a single thread // on multiple processor groups, so we need to restrict the group. @@ -576,7 +768,7 @@ class NumaConfig { HANDLE hThread = GetCurrentThread(); - const BOOL status = SetThreadGroupAffinity_f(hThread, &affinity, nullptr); + const BOOL status = SetThreadGroupAffinity(hThread, &affinity, nullptr); if (status == 0) std::exit(EXIT_FAILURE); @@ -665,138 +857,6 @@ class NumaConfig { return true; } -#if defined(__linux__) && !defined(__ANDROID__) - - static std::set get_process_affinity() { - - std::set cpus; - - // For unsupported systems, or in case of a soft error, we may assume all processors - // are available for use. - [[maybe_unused]] auto set_to_all_cpus = [&]() { - for (CpuIndex c = 0; c < SYSTEM_THREADS_NB; ++c) - cpus.insert(c); - }; - - // cpu_set_t by default holds 1024 entries. This may not be enough soon, - // but there is no easy way to determine how many threads there actually is. - // In this case we just choose a reasonable upper bound. - static constexpr CpuIndex MaxNumCpus = 1024 * 64; - - cpu_set_t* mask = CPU_ALLOC(MaxNumCpus); - if (mask == nullptr) - std::exit(EXIT_FAILURE); - - const size_t masksize = CPU_ALLOC_SIZE(MaxNumCpus); - - CPU_ZERO_S(masksize, mask); - - const int status = sched_getaffinity(0, masksize, mask); - - if (status != 0) - { - CPU_FREE(mask); - std::exit(EXIT_FAILURE); - } - - for (CpuIndex c = 0; c < MaxNumCpus; ++c) - if (CPU_ISSET_S(c, masksize, mask)) - cpus.insert(c); - - CPU_FREE(mask); - - return cpus; - } - -#elif defined(_WIN64) - - // On Windows there are two ways to set affinity, and therefore 2 ways to get it. - // These are not consistent, so we have to check both. - // In some cases it is actually not possible to determine affinity. - // For example when two different threads have affinity on different processor groups, - // set using SetThreadAffinityMask, we can't retrieve the actual affinities. - // From documentation on GetProcessAffinityMask: - // > If the calling process contains threads in multiple groups, - // > the function returns zero for both affinity masks. - // In such cases we just give up and assume we have affinity for all processors. - // nullopt means no affinity is set, that is, all processors are allowed - static std::optional> get_process_affinity() { - HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll")); - auto GetThreadSelectedCpuSetMasks_f = GetThreadSelectedCpuSetMasks_t( - (void (*)()) GetProcAddress(k32, "GetThreadSelectedCpuSetMasks")); - auto GetProcessAffinityMask_f = - GetProcessAffinityMask_t((void (*)()) GetProcAddress(k32, "GetProcessAffinityMask")); - auto GetProcessGroupAffinity_f = - GetProcessGroupAffinity_t((void (*)()) GetProcAddress(k32, "GetProcessGroupAffinity")); - - if (GetThreadSelectedCpuSetMasks_f != nullptr) - { - std::set cpus; - - USHORT RequiredMaskCount; - GetThreadSelectedCpuSetMasks_f(GetCurrentThread(), nullptr, 0, &RequiredMaskCount); - - // If RequiredMaskCount then these affinities were never set, but it's not consistent - // so GetProcessAffinityMask may still return some affinity. - if (RequiredMaskCount > 0) - { - auto groupAffinities = std::make_unique(RequiredMaskCount); - - GetThreadSelectedCpuSetMasks_f(GetCurrentThread(), groupAffinities.get(), - RequiredMaskCount, &RequiredMaskCount); - - for (USHORT i = 0; i < RequiredMaskCount; ++i) - { - const size_t procGroupIndex = groupAffinities[i].Group; - - for (size_t j = 0; j < WIN_PROCESSOR_GROUP_SIZE; ++j) - { - if (groupAffinities[i].Mask & (KAFFINITY(1) << j)) - cpus.insert(procGroupIndex * WIN_PROCESSOR_GROUP_SIZE + j); - } - } - - return cpus; - } - } - - if (GetProcessAffinityMask_f != nullptr && GetProcessGroupAffinity_f != nullptr) - { - std::set cpus; - - DWORD_PTR proc, sys; - BOOL status = GetProcessAffinityMask_f(GetCurrentProcess(), &proc, &sys); - if (status == 0) - return std::nullopt; - - // We can't determine affinity because it spans processor groups. - if (proc == 0) - return std::nullopt; - - // We are expecting a single group. - USHORT GroupCount = 1; - alignas(4) USHORT GroupArray[1]; - status = GetProcessGroupAffinity_f(GetCurrentProcess(), &GroupCount, GroupArray); - if (status == 0 || GroupCount != 1) - return std::nullopt; - - const size_t procGroupIndex = GroupArray[0]; - - uint64_t mask = static_cast(proc); - for (size_t j = 0; j < WIN_PROCESSOR_GROUP_SIZE; ++j) - { - if (mask & (KAFFINITY(1) << j)) - cpus.insert(procGroupIndex * WIN_PROCESSOR_GROUP_SIZE + j); - } - - return cpus; - } - - return std::nullopt; - } - -#endif - static std::vector indices_from_shortened_string(const std::string& s) { std::vector indices; From 21ba32af6d34c367ef22384c0f87fe49764d8ef0 Mon Sep 17 00:00:00 2001 From: mstembera Date: Tue, 4 Jun 2024 17:59:47 -0700 Subject: [PATCH 1578/1766] Remove m512_hadd128x16_interleave() This functionality is no longer used anywhere. closes https://github.com/official-stockfish/Stockfish/pull/5357 No functional change --- src/nnue/layers/simd.h | 33 --------------------------------- src/search.cpp | 2 +- src/search.h | 2 +- 3 files changed, 2 insertions(+), 35 deletions(-) diff --git a/src/nnue/layers/simd.h b/src/nnue/layers/simd.h index cec41474bb0..55cb7df1421 100644 --- a/src/nnue/layers/simd.h +++ b/src/nnue/layers/simd.h @@ -43,39 +43,6 @@ namespace Stockfish::Simd { return _mm512_reduce_add_epi32(sum) + bias; } -/* - Parameters: - sum0 = [zmm0.i128[0], zmm0.i128[1], zmm0.i128[2], zmm0.i128[3]] - sum1 = [zmm1.i128[0], zmm1.i128[1], zmm1.i128[2], zmm1.i128[3]] - sum2 = [zmm2.i128[0], zmm2.i128[1], zmm2.i128[2], zmm2.i128[3]] - sum3 = [zmm3.i128[0], zmm3.i128[1], zmm3.i128[2], zmm3.i128[3]] - - Returns: - ret = [ - reduce_add_epi32(zmm0.i128[0]), reduce_add_epi32(zmm1.i128[0]), reduce_add_epi32(zmm2.i128[0]), reduce_add_epi32(zmm3.i128[0]), - reduce_add_epi32(zmm0.i128[1]), reduce_add_epi32(zmm1.i128[1]), reduce_add_epi32(zmm2.i128[1]), reduce_add_epi32(zmm3.i128[1]), - reduce_add_epi32(zmm0.i128[2]), reduce_add_epi32(zmm1.i128[2]), reduce_add_epi32(zmm2.i128[2]), reduce_add_epi32(zmm3.i128[2]), - reduce_add_epi32(zmm0.i128[3]), reduce_add_epi32(zmm1.i128[3]), reduce_add_epi32(zmm2.i128[3]), reduce_add_epi32(zmm3.i128[3]) - ] - */ -[[maybe_unused]] static __m512i -m512_hadd128x16_interleave(__m512i sum0, __m512i sum1, __m512i sum2, __m512i sum3) { - - __m512i sum01a = _mm512_unpacklo_epi32(sum0, sum1); - __m512i sum01b = _mm512_unpackhi_epi32(sum0, sum1); - - __m512i sum23a = _mm512_unpacklo_epi32(sum2, sum3); - __m512i sum23b = _mm512_unpackhi_epi32(sum2, sum3); - - __m512i sum01 = _mm512_add_epi32(sum01a, sum01b); - __m512i sum23 = _mm512_add_epi32(sum23a, sum23b); - - __m512i sum0123a = _mm512_unpacklo_epi64(sum01, sum23); - __m512i sum0123b = _mm512_unpackhi_epi64(sum01, sum23); - - return _mm512_add_epi32(sum0123a, sum0123b); -} - [[maybe_unused]] static void m512_add_dpbusd_epi32(__m512i& acc, __m512i a, __m512i b) { #if defined(USE_VNNI) diff --git a/src/search.cpp b/src/search.cpp index c03fe7811de..6e03b62a92c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1660,7 +1660,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, return bestValue; } -Depth Search::Worker::reduction(bool i, Depth d, int mn, int delta) { +Depth Search::Worker::reduction(bool i, Depth d, int mn, int delta) const { int reductionScale = reductions[d] * reductions[mn]; return (reductionScale + 1222 - delta * 733 / rootDelta) / 1024 + (!i && reductionScale > 1231); } diff --git a/src/search.h b/src/search.h index a22d32004f5..d5210c2e072 100644 --- a/src/search.h +++ b/src/search.h @@ -265,7 +265,7 @@ class Worker { template Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth = 0); - Depth reduction(bool i, Depth d, int mn, int delta); + Depth reduction(bool i, Depth d, int mn, int delta) const; // Get a pointer to the search manager, only allowed to be called by the // main thread. From a08fcacb2876ced0cb68d01e61f081449386f132 Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Tue, 4 Jun 2024 12:47:54 +0800 Subject: [PATCH 1579/1766] VVLTC search tune Parameters were tuned with 199k games of VVLTC: https://tests.stockfishchess.org/tests/view/665c67e73542f91ad1c54fe2 Passed VVLTC 1st sprt: https://tests.stockfishchess.org/tests/view/665e9c83fd45fb0f907c837c LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 83494 W: 21546 L: 21219 D: 40729 Ptnml(0-2): 6, 7707, 25993, 8036, 5 Passed VVLTC 2nd sprt: https://tests.stockfishchess.org/tests/view/665f650bfd45fb0f907cb360 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 151056 W: 38796 L: 38295 D: 73965 Ptnml(0-2): 5, 13742, 47536, 14237, 8 https://github.com/official-stockfish/Stockfish/pull/5359 Bench: 1154524 --- src/search.cpp | 94 +++++++++++++++++++++++++------------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 6e03b62a92c..1c0bbc4d9d7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -60,9 +60,9 @@ static constexpr double EvalLevel[10] = {0.981, 0.956, 0.895, 0.949, 0.913, // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) { - Value futilityMult = 129 - 43 * noTtCutNode; - Value improvingDeduction = 56 * improving * futilityMult / 32; - Value worseningDeduction = 336 * oppWorsening * futilityMult / 1024; + Value futilityMult = 124 - 43 * noTtCutNode; + Value improvingDeduction = 60 * improving * futilityMult / 32; + Value worseningDeduction = 344 * oppWorsening * futilityMult / 1024; return futilityMult * d - improvingDeduction - worseningDeduction; } @@ -74,15 +74,15 @@ constexpr int futility_move_count(bool improving, Depth depth) { // Add correctionHistory value to raw staticEval and guarantee evaluation does not hit the tablebase range Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { auto cv = w.correctionHistory[pos.side_to_move()][pawn_structure_index(pos)]; - v += cv * std::abs(cv) / 5435; + v += cv * std::abs(cv) / 4990; return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } // History and stats update bonus, based on depth -int stat_bonus(Depth d) { return std::clamp(205 * d - 283, 18, 1544); } +int stat_bonus(Depth d) { return std::clamp(186 * d - 285, 20, 1524); } // History and stats update malus, based on depth -int stat_malus(Depth d) { return (d < 4 ? 767 * d - 275 : 1911); } +int stat_malus(Depth d) { return (d < 4 ? 707 * d - 260 : 2073); } // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(size_t nodes) { return VALUE_DRAW - 1 + Value(nodes & 0x2); } @@ -314,12 +314,12 @@ void Search::Worker::iterative_deepening() { // Reset aspiration window starting size Value avg = rootMoves[pvIdx].averageScore; - delta = 9 + avg * avg / 10502; + delta = 9 + avg * avg / 10182; alpha = std::max(avg - delta, -VALUE_INFINITE); beta = std::min(avg + delta, VALUE_INFINITE); // Adjust optimism based on root move's averageScore (~4 Elo) - optimism[us] = 122 * avg / (std::abs(avg) + 92); + optimism[us] = 127 * avg / (std::abs(avg) + 86); optimism[~us] = -optimism[us]; // Start with a small aspiration window and, in the case of a fail @@ -500,17 +500,17 @@ void Search::Worker::clear() { counterMoves.fill(Move::none()); mainHistory.fill(0); captureHistory.fill(0); - pawnHistory.fill(-1300); + pawnHistory.fill(-1193); correctionHistory.fill(0); for (bool inCheck : {false, true}) for (StatsType c : {NoCaptures, Captures}) for (auto& to : continuationHistory[inCheck][c]) for (auto& h : to) - h->fill(-60); + h->fill(-56); for (size_t i = 1; i < reductions.size(); ++i) - reductions[i] = int((19.90 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); + reductions[i] = int((19.26 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); refreshTable.clear(networks[numaAccessToken]); } @@ -742,7 +742,7 @@ Value Search::Worker::search( // Use static evaluation difference to improve quiet move ordering (~9 Elo) if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture) { - int bonus = std::clamp(-11 * int((ss - 1)->staticEval + ss->staticEval), -1592, 1390); + int bonus = std::clamp(-10 * int((ss - 1)->staticEval + ss->staticEval), -1590, 1371); bonus = bonus > 0 ? 2 * bonus : bonus / 2; thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) @@ -764,7 +764,7 @@ Value Search::Worker::search( // Step 7. Razoring (~1 Elo) // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. - if (eval < alpha - 501 - 305 * depth * depth) + if (eval < alpha - 512 - 293 * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -773,23 +773,23 @@ Value Search::Worker::search( // Step 8. Futility pruning: child node (~40 Elo) // The depth condition is important for mate finding. - if (!ss->ttPv && depth < 12 + if (!ss->ttPv && depth < 13 && eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening) - - (ss - 1)->statScore / 248 + - (ss - 1)->statScore / 263 >= beta && eval >= beta && eval < VALUE_TB_WIN_IN_MAX_PLY && (!ttMove || ttCapture)) return beta > VALUE_TB_LOSS_IN_MAX_PLY ? beta + (eval - beta) / 3 : eval; // Step 9. Null move search with verification search (~35 Elo) - if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 13999 - && eval >= beta && ss->staticEval >= beta - 21 * depth + 390 && !excludedMove + if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 14369 + && eval >= beta && ss->staticEval >= beta - 21 * depth + 393 && !excludedMove && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly && beta > VALUE_TB_LOSS_IN_MAX_PLY) { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and eval - Depth R = std::min(int(eval - beta) / 177, 6) + depth / 3 + 5; + Depth R = std::min(int(eval - beta) / 197, 6) + depth / 3 + 5; ss->currentMove = Move::null(); ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -847,7 +847,7 @@ Value Search::Worker::search( // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search returns a value // much above beta, we can (almost) safely prune the previous move. - probCutBeta = beta + 185 - 60 * improving; + probCutBeta = beta + 177 - 57 * improving; if ( !PvNode && depth > 3 && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY @@ -903,7 +903,7 @@ Value Search::Worker::search( moves_loop: // When in check, search starts here // Step 12. A small Probcut idea, when we are in check (~4 Elo) - probCutBeta = beta + 361; + probCutBeta = beta + 388; if (ss->inCheck && !PvNode && ttCapture && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 4 && ttValue >= probCutBeta && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) @@ -991,15 +991,15 @@ Value Search::Worker::search( // Futility pruning for captures (~2 Elo) if (!givesCheck && lmrDepth < 7 && !ss->inCheck) { - Value futilityValue = ss->staticEval + 283 + 235 * lmrDepth + Value futilityValue = ss->staticEval + 287 + 248 * lmrDepth + PieceValue[capturedPiece] + captHist / 7; if (futilityValue <= alpha) continue; } // SEE based pruning for captures and checks (~11 Elo) - int seeHist = std::clamp(captHist / 32, -183 * depth, 162 * depth); - if (!pos.see_ge(move, -166 * depth - seeHist)) + int seeHist = std::clamp(captHist / 32, -180 * depth, 163 * depth); + if (!pos.see_ge(move, -160 * depth - seeHist)) continue; } else @@ -1010,18 +1010,18 @@ Value Search::Worker::search( + thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()]; // Continuation history based pruning (~2 Elo) - if (lmrDepth < 6 && history < -4427 * depth) + if (lmrDepth < 6 && history < -4151 * depth) continue; history += 2 * thisThread->mainHistory[us][move.from_to()]; - lmrDepth += history / 3670; + lmrDepth += history / 3678; Value futilityValue = - ss->staticEval + (bestValue < ss->staticEval - 51 ? 149 : 55) + 141 * lmrDepth; + ss->staticEval + (bestValue < ss->staticEval - 51 ? 138 : 54) + 140 * lmrDepth; // Futility pruning: parent node (~13 Elo) - if (!ss->inCheck && lmrDepth < 11 && futilityValue <= alpha) + if (!ss->inCheck && lmrDepth < 12 && futilityValue <= alpha) { if (bestValue <= futilityValue && std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY && futilityValue < VALUE_TB_WIN_IN_MAX_PLY) @@ -1032,7 +1032,7 @@ Value Search::Worker::search( lmrDepth = std::max(lmrDepth, 0); // Prune moves with negative SEE (~4 Elo) - if (!pos.see_ge(move, -26 * lmrDepth * lmrDepth)) + if (!pos.see_ge(move, -24 * lmrDepth * lmrDepth)) continue; } } @@ -1055,11 +1055,11 @@ Value Search::Worker::search( // margins scale well. if (!rootNode && move == ttMove && !excludedMove - && depth >= 4 - (thisThread->completedDepth > 38) + ss->ttPv + && depth >= 4 - (thisThread->completedDepth > 35) + ss->ttPv && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && (tte->bound() & BOUND_LOWER) && tte->depth() >= depth - 3) { - Value singularBeta = ttValue - (58 + 64 * (ss->ttPv && !PvNode)) * depth / 64; + Value singularBeta = ttValue - (52 + 80 * (ss->ttPv && !PvNode)) * depth / 64; Depth singularDepth = newDepth / 2; ss->excludedMove = move; @@ -1070,13 +1070,13 @@ Value Search::Worker::search( if (value < singularBeta) { - int doubleMargin = 304 * PvNode - 203 * !ttCapture; - int tripleMargin = 117 + 259 * PvNode - 296 * !ttCapture + 97 * ss->ttPv; + int doubleMargin = 290 * PvNode - 200 * !ttCapture; + int tripleMargin = 107 + 247 * PvNode - 278 * !ttCapture + 99 * ss->ttPv; extension = 1 + (value < singularBeta - doubleMargin) + (value < singularBeta - tripleMargin); - depth += ((!PvNode) && (depth < 16)); + depth += ((!PvNode) && (depth < 18)); } // Multi-cut pruning @@ -1106,7 +1106,7 @@ Value Search::Worker::search( else if (PvNode && move.to_sq() == prevSq && thisThread->captureHistory[movedPiece][move.to_sq()] [type_of(pos.piece_on(move.to_sq()))] - > 3988) + > 3922) extension = 1; } @@ -1162,10 +1162,10 @@ Value Search::Worker::search( ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + (*contHist[0])[movedPiece][move.to_sq()] - + (*contHist[1])[movedPiece][move.to_sq()] - 5169; + + (*contHist[1])[movedPiece][move.to_sq()] - 4747; // Decrease/increase reduction for moves with a good/bad history (~8 Elo) - r -= ss->statScore / 11049; + r -= ss->statScore / 11125; // Step 17. Late moves reduction / extension (LMR, ~117 Elo) if (depth >= 2 && moveCount > 1 + rootNode) @@ -1184,7 +1184,7 @@ Value Search::Worker::search( { // Adjust full-depth search based on LMR results - if the result // was good enough search deeper, if it was bad enough search shallower. - const bool doDeeperSearch = value > (bestValue + 36 + 2 * newDepth); // (~1 Elo) + const bool doDeeperSearch = value > (bestValue + 35 + 2 * newDepth); // (~1 Elo) const bool doShallowerSearch = value < bestValue + newDepth; // (~2 Elo) newDepth += doDeeperSearch - doShallowerSearch; @@ -1345,10 +1345,10 @@ Value Search::Worker::search( // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (116 * (depth > 5) + 115 * (PvNode || cutNode) - + 186 * ((ss - 1)->statScore < -14144) + 121 * ((ss - 1)->moveCount > 9) - + 64 * (!ss->inCheck && bestValue <= ss->staticEval - 115) - + 137 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 81)); + int bonus = (113 * (depth > 5) + 118 * (PvNode || cutNode) + + 191 * ((ss - 1)->statScore < -14396) + 119 * ((ss - 1)->moveCount > 8) + + 64 * (!ss->inCheck && bestValue <= ss->staticEval - 107) + + 147 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 75)); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus / 100); thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] @@ -1520,7 +1520,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, if (bestValue > alpha) alpha = bestValue; - futilityBase = ss->staticEval + 279; + futilityBase = ss->staticEval + 294; } const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, @@ -1592,11 +1592,11 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, + (*contHist[1])[pos.moved_piece(move)][move.to_sq()] + thisThread->pawnHistory[pawn_structure_index(pos)][pos.moved_piece(move)] [move.to_sq()] - <= 4181) + <= 4452) continue; // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, -67)) + if (!pos.see_ge(move, -74)) continue; } @@ -1662,7 +1662,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth Search::Worker::reduction(bool i, Depth d, int mn, int delta) const { int reductionScale = reductions[d] * reductions[mn]; - return (reductionScale + 1222 - delta * 733 / rootDelta) / 1024 + (!i && reductionScale > 1231); + return (reductionScale + 1236 - delta * 746 / rootDelta) / 1024 + (!i && reductionScale > 1326); } // elapsed() returns the time elapsed since the search started. If the @@ -1765,7 +1765,7 @@ void update_all_stats(const Position& pos, if (!pos.capture_stage(bestMove)) { - int bestMoveBonus = bestValue > beta + 176 ? quietMoveBonus // larger bonus + int bestMoveBonus = bestValue > beta + 164 ? quietMoveBonus // larger bonus : stat_bonus(depth); // smaller bonus update_quiet_stats(pos, ss, workerThread, bestMove, bestMoveBonus); @@ -1803,7 +1803,7 @@ void update_all_stats(const Position& pos, // by moves at ply -1, -2, -3, -4, and -6 with current move. void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { - bonus = bonus * 47 / 64; + bonus = bonus * 51 / 64; for (int i : {1, 2, 3, 4, 6}) { From 36eb9bc783d35842571d0d4313349b964892d9ca Mon Sep 17 00:00:00 2001 From: Viren6 <94880762+Viren6@users.noreply.github.com> Date: Wed, 5 Jun 2024 03:24:39 +0100 Subject: [PATCH 1580/1766] Use futility margin in razoring margin Uses futilityMargin * depth to set the razoring margin. This retains the quadratic depth scaling to preserve mate finding capabilities. This patch is nice because it increases the elo sensitivity of the futility margin heuristics. Passed STC: https://tests.stockfishchess.org/tests/view/665f9892fd11ae7170b4849c LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 39392 W: 10348 L: 10030 D: 19014 Ptnml(0-2): 99, 4585, 10009, 4905, 98 Passed LTC: https://tests.stockfishchess.org/tests/view/665f9d2dfd11ae7170b484a8 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 107910 W: 27521 L: 27053 D: 53336 Ptnml(0-2): 73, 11835, 29670, 12305, 72 closes https://github.com/official-stockfish/Stockfish/pull/5360 bench 1277173 --- src/search.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 1c0bbc4d9d7..15cc2d8fe76 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -60,9 +60,9 @@ static constexpr double EvalLevel[10] = {0.981, 0.956, 0.895, 0.949, 0.913, // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) { - Value futilityMult = 124 - 43 * noTtCutNode; - Value improvingDeduction = 60 * improving * futilityMult / 32; - Value worseningDeduction = 344 * oppWorsening * futilityMult / 1024; + Value futilityMult = 109 - 40 * noTtCutNode; + Value improvingDeduction = 59 * improving * futilityMult / 32; + Value worseningDeduction = 328 * oppWorsening * futilityMult / 1024; return futilityMult * d - improvingDeduction - worseningDeduction; } @@ -554,7 +554,7 @@ Value Search::Worker::search( bool givesCheck, improving, priorCapture, opponentWorsening; bool capture, moveCountPruning, ttCapture; Piece movedPiece; - int moveCount, captureCount, quietCount; + int moveCount, captureCount, quietCount, futilityMargin; Bound singularBound; // Step 1. Initialize node @@ -761,10 +761,12 @@ Value Search::Worker::search( opponentWorsening = ss->staticEval + (ss - 1)->staticEval > 2; + futilityMargin = futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening); + // Step 7. Razoring (~1 Elo) // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. - if (eval < alpha - 512 - 293 * depth * depth) + if (eval < alpha - 465 - futilityMargin * depth * 33 / 32) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -774,9 +776,7 @@ Value Search::Worker::search( // Step 8. Futility pruning: child node (~40 Elo) // The depth condition is important for mate finding. if (!ss->ttPv && depth < 13 - && eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening) - - (ss - 1)->statScore / 263 - >= beta + && eval - futilityMargin - (ss - 1)->statScore / 263 >= beta && eval >= beta && eval < VALUE_TB_WIN_IN_MAX_PLY && (!ttMove || ttCapture)) return beta > VALUE_TB_LOSS_IN_MAX_PLY ? beta + (eval - beta) / 3 : eval; From fb18caae7a7906a6c9a0579c43021221c663965a Mon Sep 17 00:00:00 2001 From: Disservin Date: Wed, 5 Jun 2024 18:31:11 +0200 Subject: [PATCH 1581/1766] Update clang-format to version 18 clang-format-18 is available in ubuntu noble(24.04), if you are on a version lower than that you can use the update script from llvm. https://apt.llvm.org/ Windows users should be able to download and use clang-format from their release builds https://github.com/llvm/llvm-project/releases or get the latest from msys2 https://packages.msys2.org/package/mingw-w64-x86_64-clang. macOS users can resort to "brew install clang-format". closes https://github.com/official-stockfish/Stockfish/pull/5365 No functional change --- .github/workflows/clang-format.yml | 6 +++--- CONTRIBUTING.md | 2 +- src/Makefile | 4 ++-- src/search.cpp | 10 +++++----- src/thread.cpp | 2 +- src/tune.cpp | 3 +-- 6 files changed, 13 insertions(+), 14 deletions(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index e20e0d5d623..630edbf93fe 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -25,7 +25,7 @@ jobs: id: clang-format continue-on-error: true with: - clang-format-version: "17" + clang-format-version: "18" exclude-regex: "incbin" - name: Comment on PR @@ -33,9 +33,9 @@ jobs: uses: thollander/actions-comment-pull-request@fabd468d3a1a0b97feee5f6b9e499eab0dd903f6 # @v2.5.0 with: message: | - clang-format 17 needs to be run on this PR. + clang-format 18 needs to be run on this PR. If you do not have clang-format installed, the maintainer will run it when merging. - For the exact version please see https://packages.ubuntu.com/mantic/clang-format-17. + For the exact version please see https://packages.ubuntu.com/noble/clang-format-18. _(execution **${{ github.run_id }}** / attempt **${{ github.run_attempt }}**)_ comment_tag: execution diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cf9cecda2b5..caffc916e60 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -59,7 +59,7 @@ discussion._ Changes to Stockfish C++ code should respect our coding style defined by [.clang-format](.clang-format). You can format your changes by running -`make format`. This requires clang-format version 17 to be installed on your system. +`make format`. This requires clang-format version 18 to be installed on your system. ## Navigate diff --git a/src/Makefile b/src/Makefile index 29c4f879dfb..742fd195839 100644 --- a/src/Makefile +++ b/src/Makefile @@ -153,8 +153,8 @@ dotprod = no arm_version = 0 STRIP = strip -ifneq ($(shell which clang-format-17 2> /dev/null),) - CLANG-FORMAT = clang-format-17 +ifneq ($(shell which clang-format-18 2> /dev/null),) + CLANG-FORMAT = clang-format-18 else CLANG-FORMAT = clang-format endif diff --git a/src/search.cpp b/src/search.cpp index 15cc2d8fe76..2cbc7677429 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -579,9 +579,10 @@ Value Search::Worker::search( // Step 2. Check for aborted search and immediate draw if (threads.stop.load(std::memory_order_relaxed) || pos.is_draw(ss->ply) || ss->ply >= MAX_PLY) - return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate( - networks[numaAccessToken], pos, refreshTable, thisThread->optimism[us]) - : value_draw(thisThread->nodes); + return (ss->ply >= MAX_PLY && !ss->inCheck) + ? evaluate(networks[numaAccessToken], pos, refreshTable, + thisThread->optimism[us]) + : value_draw(thisThread->nodes); // 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 @@ -775,8 +776,7 @@ Value Search::Worker::search( // Step 8. Futility pruning: child node (~40 Elo) // The depth condition is important for mate finding. - if (!ss->ttPv && depth < 13 - && eval - futilityMargin - (ss - 1)->statScore / 263 >= beta + if (!ss->ttPv && depth < 13 && eval - futilityMargin - (ss - 1)->statScore / 263 >= beta && eval >= beta && eval < VALUE_TB_WIN_IN_MAX_PLY && (!ttMove || ttCapture)) return beta > VALUE_TB_LOSS_IN_MAX_PLY ? beta + (eval - beta) / 3 : eval; diff --git a/src/thread.cpp b/src/thread.cpp index 0a33422acc9..4acb9854bd4 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -179,7 +179,7 @@ void ThreadPool::set(const NumaConfig& numaConfig, const size_t threadId = threads.size(); const NumaIndex numaId = doBindThreads ? boundThreadToNumaNode[threadId] : 0; auto manager = threadId == 0 ? std::unique_ptr( - std::make_unique(updateContext)) + std::make_unique(updateContext)) : std::make_unique(); // When not binding threads we want to force all access to happen diff --git a/src/tune.cpp b/src/tune.cpp index 3e5ebe5e6c3..f377e59ecf1 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -118,7 +118,6 @@ void Tune::Entry::read_option() { namespace Stockfish { -void Tune::read_results() { /* ...insert your values here... */ -} +void Tune::read_results() { /* ...insert your values here... */ } } // namespace Stockfish From 5688b188cc8560e107815c83a7084220fddebdb9 Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Fri, 31 May 2024 21:55:39 +0800 Subject: [PATCH 1582/1766] Simplify evaluation constants Passed STC (<0, 2> by accident): LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 346016 W: 89529 L: 88756 D: 167731 Ptnml(0-2): 1012, 41074, 88027, 41919, 976 https://tests.stockfishchess.org/tests/view/6659d6ecf426908fcc6b6929 Passed LTC: LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 89862 W: 22887 L: 22734 D: 44241 Ptnml(0-2): 45, 9999, 24694, 10144, 49 https://tests.stockfishchess.org/tests/view/665a6ebb062b2c3cf814fde8 Passed LTC (Rebased): LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 325500 W: 82734 L: 82826 D: 159940 Ptnml(0-2): 193, 36409, 89665, 36263, 220 https://tests.stockfishchess.org/tests/view/665bd39f44e8416a9cdc1909 closes https://github.com/official-stockfish/Stockfish/pull/5361 Bench 961982 --- src/evaluate.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index afba6363bc6..fdf35eb17d4 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -83,10 +83,8 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, optimism += optimism * nnueComplexity / 470; nnue -= nnue * nnueComplexity / 20000; - int material = 300 * pos.count() + 350 * pos.count() + 400 * pos.count() - + 640 * pos.count() + 1200 * pos.count(); - - v = (nnue * (34300 + material) + optimism * (4400 + material)) / 36672; + int material = 600 * pos.count() + pos.non_pawn_material(); + v = (nnue * (68600 + material) + optimism * (8800 + material)) / 73344; // Damp down the evaluation linearly when shuffling v -= v * pos.rule50_count() / 212; From e6c83beed12a6d3d17c69bea4bcf1a397bc60c86 Mon Sep 17 00:00:00 2001 From: R-Goc Date: Tue, 4 Jun 2024 18:06:14 +0200 Subject: [PATCH 1583/1766] Change PGO type for clang Change type of PGO in clang to IR which is recommended by LLVM/clang and could result in a speedup. https://github.com/llvm/llvm-project/issues/45668 closes https://github.com/official-stockfish/Stockfish/pull/5355 No functional change --- src/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Makefile b/src/Makefile index 742fd195839..7142b972745 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1051,14 +1051,14 @@ FORCE: clang-profile-make: $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ - EXTRACXXFLAGS='-fprofile-instr-generate ' \ - EXTRALDFLAGS=' -fprofile-instr-generate' \ + EXTRACXXFLAGS='-fprofile-generate ' \ + EXTRALDFLAGS=' -fprofile-generate' \ all clang-profile-use: $(XCRUN) llvm-profdata merge -output=stockfish.profdata *.profraw $(MAKE) ARCH=$(ARCH) COMP=$(COMP) \ - EXTRACXXFLAGS='-fprofile-instr-use=stockfish.profdata' \ + EXTRACXXFLAGS='-fprofile-use=stockfish.profdata' \ EXTRALDFLAGS='-fprofile-use ' \ all From 66ed4312f22a951aaa01bbb87b2d730656b8f2c1 Mon Sep 17 00:00:00 2001 From: Disservin Date: Fri, 7 Jun 2024 18:40:47 +0200 Subject: [PATCH 1584/1766] Workaround the clang-format inconsistencies closes https://github.com/official-stockfish/Stockfish/pull/5378 No functional change --- src/nnue/nnue_misc.cpp | 10 +++++----- src/tune.cpp | 7 +++++-- src/uci.cpp | 5 +++-- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/nnue/nnue_misc.cpp b/src/nnue/nnue_misc.cpp index 7585cce5ab6..122610a749c 100644 --- a/src/nnue/nnue_misc.cpp +++ b/src/nnue/nnue_misc.cpp @@ -178,16 +178,16 @@ trace(Position& pos, const Eval::NNUE::Networks& networks, Eval::NNUE::Accumulat for (std::size_t bucket = 0; bucket < LayerStacks; ++bucket) { - ss << "| " << bucket << " "; - ss << " | "; + ss << "| " << bucket << " " // + << " | "; format_cp_aligned_dot(t.psqt[bucket], ss, pos); - ss << " " + ss << " " // << " | "; format_cp_aligned_dot(t.positional[bucket], ss, pos); - ss << " " + ss << " " // << " | "; format_cp_aligned_dot(t.psqt[bucket] + t.positional[bucket], ss, pos); - ss << " " + ss << " " // << " |"; if (bucket == t.correctBucket) ss << " <-- this bucket is used"; diff --git a/src/tune.cpp b/src/tune.cpp index f377e59ecf1..94c9b53ecc6 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -58,8 +58,11 @@ void make_option(OptionsMap* options, const string& n, int v, const SetRange& r) LastOption = &((*options)[n]); // Print formatted parameters, ready to be copy-pasted in Fishtest - std::cout << n << "," << v << "," << r(v).first << "," << r(v).second << "," - << (r(v).second - r(v).first) / 20.0 << "," + std::cout << n << "," // + << v << "," // + << r(v).first << "," // + << r(v).second << "," // + << (r(v).second - r(v).first) / 20.0 << "," // << "0.0020" << std::endl; } } diff --git a/src/uci.cpp b/src/uci.cpp index 43b0e005a16..42c69cdeff2 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -324,8 +324,9 @@ void UCIEngine::bench(std::istream& args) { dbg_print(); - std::cerr << "\n===========================" - << "\nTotal time (ms) : " << elapsed << "\nNodes searched : " << nodes + std::cerr << "\n===========================" // + << "\nTotal time (ms) : " << elapsed // + << "\nNodes searched : " << nodes // << "\nNodes/second : " << 1000 * nodes / elapsed << std::endl; // reset callback, to not capture a dangling reference to nodesSearched From 5dda4037c73ead63b145c9a77f1dbb41422e058f Mon Sep 17 00:00:00 2001 From: rn5f107s2 Date: Wed, 5 Jun 2024 18:56:25 +0200 Subject: [PATCH 1585/1766] Simplify razor changes Remove razoring changes from https://github.com/official-stockfish/Stockfish/pull/5360 The mentioned patch introduced the usage of futility_margin into razoring alongside a tune to futility_margin. It seems the elo gained in this patch comes from the tune of futility_margin and not the introduction of futility_margin to razoring, so simplify it away here. Passed Non-regression STC: https://tests.stockfishchess.org/tests/view/66606581c340c8eed7757bc8 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 169056 W: 43922 L: 43848 D: 81286 Ptnml(0-2): 438, 20288, 43034, 20298, 470 Passed Non-regression LTC: https://tests.stockfishchess.org/tests/view/66607764c340c8eed7757c58 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 157134 W: 39805 L: 39723 D: 77606 Ptnml(0-2): 74, 17444, 43461, 17502, 86 Passed rebased Non-regression LTC: https://tests.stockfishchess.org/tests/view/6660c696c340c8eed77580c0 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 135984 W: 34427 L: 34324 D: 67233 Ptnml(0-2): 67, 15063, 37615, 15194, 53 closes https://github.com/official-stockfish/Stockfish/pull/5366 Bench: 1150518 --- src/search.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 2cbc7677429..e0a49dba860 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -554,7 +554,7 @@ Value Search::Worker::search( bool givesCheck, improving, priorCapture, opponentWorsening; bool capture, moveCountPruning, ttCapture; Piece movedPiece; - int moveCount, captureCount, quietCount, futilityMargin; + int moveCount, captureCount, quietCount; Bound singularBound; // Step 1. Initialize node @@ -762,12 +762,10 @@ Value Search::Worker::search( opponentWorsening = ss->staticEval + (ss - 1)->staticEval > 2; - futilityMargin = futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening); - // Step 7. Razoring (~1 Elo) // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. - if (eval < alpha - 465 - futilityMargin * depth * 33 / 32) + if (eval < alpha - 512 - 293 * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha) @@ -776,7 +774,10 @@ Value Search::Worker::search( // Step 8. Futility pruning: child node (~40 Elo) // The depth condition is important for mate finding. - if (!ss->ttPv && depth < 13 && eval - futilityMargin - (ss - 1)->statScore / 263 >= beta + if (!ss->ttPv && depth < 13 + && eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening) + - (ss - 1)->statScore / 263 + >= beta && eval >= beta && eval < VALUE_TB_WIN_IN_MAX_PLY && (!ttMove || ttCapture)) return beta > VALUE_TB_LOSS_IN_MAX_PLY ? beta + (eval - beta) / 3 : eval; From e2be0aaf67569788f0d1e726d0a86ce1604958da Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Thu, 6 Jun 2024 13:07:45 +0300 Subject: [PATCH 1586/1766] Tweak pruning formula Tweak pruning formula, including a constant. I started from an old yellow patch, if I'm not mistaken by Viz (Unfortunately I lost the link) where he tried something similar. I worked on it, trying different variations, until I came up with a good configuration to pass. Passed STC: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 213120 W: 55351 L: 54778 D: 102991 Ptnml(0-2): 572, 25209, 54437, 25758, 584 https://tests.stockfishchess.org/tests/view/6660c9a7c340c8eed7758195 Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 315324 W: 80176 L: 79284 D: 155864 Ptnml(0-2): 155, 34711, 87030, 35619, 147 https://tests.stockfishchess.org/tests/view/6660d7bb6489614cdad13d66 closes https://github.com/official-stockfish/Stockfish/pull/5370 Bench: 1231853 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index e0a49dba860..7417a4b6f5e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1580,7 +1580,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, // If static exchange evaluation is much worse than what is needed to not // fall below alpha we can prune this move. - if (futilityBase > alpha && !pos.see_ge(move, (alpha - futilityBase) * 4)) + if (futilityBase > alpha && !pos.see_ge(move, (alpha - futilityBase) * 2 - 30)) { bestValue = alpha; continue; From f55239b2f374a2f98717e7c361732f7c4510388b Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Thu, 6 Jun 2024 12:47:24 +0200 Subject: [PATCH 1587/1766] NumaPolicy fixes and robustness improvements 1. Fix GetProcessGroupAffinity still not getting properly aligned memory sometimes. 2. Fix a very theoretically possible heap corruption if GetActiveProcessorGroupCount changes between calls. 3. Fully determine affinity on Windows 11 and Windows Server 2022. It should only ever be indeterminate in case of an error. 4. Separate isDeterminate for old and new API, as they are &'d together we still can end up with a subset of processors even if one API is indeterminate. 5. likely_used_old_api() that is based on actual affinity that's been detected 6. IMPORTANT: Gather affinities at startup, so that we only later use the affinites set at startup. Not only does this prevent us from our own calls interfering with detection but it also means subsequent setoption NumaPolicy calls should behave as expected. 7. Fix ERROR_INSUFFICIENT_BUFFER from GetThreadSelectedCpuSetMasks being treated like an error. Should resolve https://github.com/vondele/Stockfish/commit/02ff76630b358e5f958793cc93df0009d2da65a5#commitcomment-142790025 closes https://github.com/official-stockfish/Stockfish/pull/5372 Bench: 1231853 --- src/numa.h | 272 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 197 insertions(+), 75 deletions(-) diff --git a/src/numa.h b/src/numa.h index c170c178e60..fd9abd4db7f 100644 --- a/src/numa.h +++ b/src/numa.h @@ -35,6 +35,8 @@ #include #include +#include "memory.h" + // We support linux very well, but we explicitly do NOT support Android, because there's // no affected systems, not worth maintaining. #if defined(__linux__) && !defined(__ANDROID__) @@ -96,15 +98,15 @@ inline const CpuIndex SYSTEM_THREADS_NB = std::max(1, get_hardware_con struct WindowsAffinity { std::optional> oldApi; std::optional> newApi; - bool isDeterminate = true; - std::optional> get_combined() const { - // When the affinity is not determinate we treat it as no affinity, - // because otherwise we would have to set affinity to fewer - // processors than we currently have affinity to. - if (!isDeterminate) - return std::nullopt; + // We also provide diagnostic for when the affinity is set to nullopt + // whether it was due to being indeterminate. If affinity is indeterminate + // it's best to assume it is not set at all, so consistent with the meaning + // of the nullopt affinity. + bool isNewDeterminate = true; + bool isOldDeterminate = true; + std::optional> get_combined() const { if (!oldApi.has_value()) return newApi; if (!newApi.has_value()) @@ -115,46 +117,52 @@ struct WindowsAffinity { std::inserter(intersect, intersect.begin())); return intersect; } + + // Since Windows 11 and Windows Server 2022 thread affinities can span + // processor groups and can be set as such by a new WinAPI function. + // However, we may need to force using the old API if we detect + // that the process has affinity set by the old API already and we want to override that. + // Due to the limitations of the old API we can't detect its use reliably. + // There will be cases where we detect not use but it has actually been used and vice versa. + bool likely_used_old_api() const { return oldApi.has_value() || !isOldDeterminate; } }; inline std::pair> get_process_group_affinity() { - WORD numProcGroups = GetActiveProcessorGroupCount(); - // GetProcessGroupAffinity requires the GroupArray argument to be // aligned to 4 bytes instead of just 2. static constexpr size_t GroupArrayMinimumAlignment = 4; static_assert(GroupArrayMinimumAlignment >= alignof(USHORT)); - auto GroupArray = std::make_unique( - numProcGroups + (GroupArrayMinimumAlignment / alignof(USHORT) - 1)); - - USHORT GroupCount = static_cast(numProcGroups); - const BOOL status = GetProcessGroupAffinity(GetCurrentProcess(), &GroupCount, GroupArray.get()); + // The function should succeed the second time, but it may fail if the group + // affinity has changed between GetProcessGroupAffinity calls. + // In such case we consider this a hard error, as we can't work with unstable affinities + // anyway. + static constexpr int MAX_TRIES = 2; + USHORT GroupCount = 1; + for (int i = 0; i < MAX_TRIES; ++i) + { + auto GroupArray = std::make_unique( + GroupCount + (GroupArrayMinimumAlignment / alignof(USHORT) - 1)); - return std::make_pair(status, std::vector(GroupArray.get(), GroupArray.get() + GroupCount)); -} + USHORT* GroupArrayAligned = align_ptr_up(GroupArray.get()); -// Since Windows 11 and Windows Server 2022 thread affinities can span -// processor groups and can be set as such by a new WinAPI function. -// However, we may need to force using the old API if we detect -// that the process has affinity set by the old API already and we want to override that. -inline bool use_old_affinity_api() { - HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll")); - auto SetThreadSelectedCpuSetMasks_f = SetThreadSelectedCpuSetMasks_t( - (void (*)()) GetProcAddress(k32, "SetThreadSelectedCpuSetMasks")); + const BOOL status = + GetProcessGroupAffinity(GetCurrentProcess(), &GroupCount, GroupArrayAligned); - if (SetThreadSelectedCpuSetMasks_f == nullptr) - return true; - - auto [status, groupAffinity] = get_process_group_affinity(); + if (status == 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { + break; + } - // If GroupCount > 1 then we know old API was never used and we can stick - // to the new API safely. - if (status != 0 && groupAffinity.size() > 1) - return false; + if (status != 0) + { + return std::make_pair(status, + std::vector(GroupArrayAligned, GroupArrayAligned + GroupCount)); + } + } - return true; -}; + return std::make_pair(0, std::vector()); +} // On Windows there are two ways to set affinity, and therefore 2 ways to get it. // These are not consistent, so we have to check both. @@ -171,83 +179,183 @@ inline WindowsAffinity get_process_affinity() { auto GetThreadSelectedCpuSetMasks_f = GetThreadSelectedCpuSetMasks_t( (void (*)()) GetProcAddress(k32, "GetThreadSelectedCpuSetMasks")); + BOOL status = 0; + WindowsAffinity affinity; if (GetThreadSelectedCpuSetMasks_f != nullptr) { USHORT RequiredMaskCount; - BOOL status = - GetThreadSelectedCpuSetMasks_f(GetCurrentThread(), nullptr, 0, &RequiredMaskCount); + status = GetThreadSelectedCpuSetMasks_f(GetCurrentThread(), nullptr, 0, &RequiredMaskCount); - // If RequiredMaskCount then these affinities were never set, but it's not consistent - // so GetProcessAffinityMask may still return some affinity. - if (status == 0) + // We expect ERROR_INSUFFICIENT_BUFFER from GetThreadSelectedCpuSetMasks, + // but other failure is an actual error. + if (status == 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER) { - affinity.isDeterminate = false; - return affinity; + affinity.isNewDeterminate = false; } - - if (RequiredMaskCount > 0) + else if (RequiredMaskCount > 0) { - std::set cpus; - + // If RequiredMaskCount then these affinities were never set, but it's not consistent + // so GetProcessAffinityMask may still return some affinity. auto groupAffinities = std::make_unique(RequiredMaskCount); - GetThreadSelectedCpuSetMasks_f(GetCurrentThread(), groupAffinities.get(), - RequiredMaskCount, &RequiredMaskCount); + status = GetThreadSelectedCpuSetMasks_f(GetCurrentThread(), groupAffinities.get(), + RequiredMaskCount, &RequiredMaskCount); - for (USHORT i = 0; i < RequiredMaskCount; ++i) + if (status == 0) + { + affinity.isNewDeterminate = false; + } + else { - const size_t procGroupIndex = groupAffinities[i].Group; + std::set cpus; - for (size_t j = 0; j < WIN_PROCESSOR_GROUP_SIZE; ++j) + for (USHORT i = 0; i < RequiredMaskCount; ++i) { - if (groupAffinities[i].Mask & (KAFFINITY(1) << j)) - cpus.insert(procGroupIndex * WIN_PROCESSOR_GROUP_SIZE + j); + const size_t procGroupIndex = groupAffinities[i].Group; + + for (size_t j = 0; j < WIN_PROCESSOR_GROUP_SIZE; ++j) + { + if (groupAffinities[i].Mask & (KAFFINITY(1) << j)) + cpus.insert(procGroupIndex * WIN_PROCESSOR_GROUP_SIZE + j); + } } - } - affinity.newApi = std::move(cpus); + affinity.newApi = std::move(cpus); + } } } + // NOTE: There is no way to determine full affinity using the old API if + // individual threads set affinity on different processor groups. + DWORD_PTR proc, sys; - BOOL status = GetProcessAffinityMask(GetCurrentProcess(), &proc, &sys); + status = GetProcessAffinityMask(GetCurrentProcess(), &proc, &sys); // If proc == 0 then we can't determine affinity because it spans processor groups. + // On Windows 11 and Server 2022 it will instead + // > If, however, hHandle specifies a handle to the current process, the function + // > always uses the calling thread's primary group (which by default is the same + // > as the process' primary group) in order to set the + // > lpProcessAffinityMask and lpSystemAffinityMask. + // So it will never be indeterminate here. We can only make assumptions later. if (status == 0 || proc == 0) { - affinity.isDeterminate = false; + affinity.isOldDeterminate = false; return affinity; } // If SetProcessAffinityMask was never called the affinity // must span all processor groups, but if it was called it must only span one. - auto [status2, groupAffinity] = get_process_group_affinity(); - if (status2 == 0) + std::vector groupAffinity; // We need to capture this later and capturing + // from structured bindings requires c++20. + std::tie(status, groupAffinity) = get_process_group_affinity(); + if (status == 0) { - affinity.isDeterminate = false; + affinity.isOldDeterminate = false; return affinity; } - // If we have affinity for more than 1 group then at this point we - // can assume SetProcessAffinityMask has never been called and therefore - // according ot old API we do not have any affinity set. - // Otherwise we have to assume we have affinity set and gather the processor IDs. if (groupAffinity.size() == 1) { - std::set cpus; + // We detect the case when affinity is set to all processors and correctly + // leave affinity.oldApi as nullopt. + if (GetActiveProcessorGroupCount() != 1 || proc != sys) + { + std::set cpus; - const size_t procGroupIndex = groupAffinity[0]; + const size_t procGroupIndex = groupAffinity[0]; - uint64_t mask = static_cast(proc); - for (size_t j = 0; j < WIN_PROCESSOR_GROUP_SIZE; ++j) - { - if (mask & (KAFFINITY(1) << j)) - cpus.insert(procGroupIndex * WIN_PROCESSOR_GROUP_SIZE + j); + const uint64_t mask = static_cast(proc); + for (size_t j = 0; j < WIN_PROCESSOR_GROUP_SIZE; ++j) + { + if (mask & (KAFFINITY(1) << j)) + cpus.insert(procGroupIndex * WIN_PROCESSOR_GROUP_SIZE + j); + } + + affinity.oldApi = std::move(cpus); } + } + else + { + // If we got here it means that either SetProcessAffinityMask was never set + // or we're on Windows 11/Server 2022. + + // Since Windows 11 and Windows Server 2022 the behaviour of GetProcessAffinityMask changed + // > If, however, hHandle specifies a handle to the current process, the function + // > always uses the calling thread's primary group (which by default is the same + // > as the process' primary group) in order to set the + // > lpProcessAffinityMask and lpSystemAffinityMask. + // In which case we can actually retrieve the full affinity. + + if (GetThreadSelectedCpuSetMasks_f != nullptr) + { + std::thread th([&]() { + std::set cpus; + bool isAffinityFull = true; - affinity.oldApi = std::move(cpus); + for (auto procGroupIndex : groupAffinity) + { + const int numActiveProcessors = + GetActiveProcessorCount(static_cast(procGroupIndex)); + + // We have to schedule to 2 different processors and & the affinities we get. + // Otherwise our processor choice could influence the resulting affinity. + // We assume the processor IDs within the group are filled sequentially from 0. + uint64_t procCombined = std::numeric_limits::max(); + uint64_t sysCombined = std::numeric_limits::max(); + + for (int i = 0; i < std::min(numActiveProcessors, 2); ++i) + { + GROUP_AFFINITY GroupAffinity; + std::memset(&GroupAffinity, 0, sizeof(GROUP_AFFINITY)); + GroupAffinity.Group = static_cast(procGroupIndex); + + GroupAffinity.Mask = static_cast(1) << i; + + status = + SetThreadGroupAffinity(GetCurrentThread(), &GroupAffinity, nullptr); + if (status == 0) + { + affinity.isOldDeterminate = false; + return; + } + + SwitchToThread(); + + DWORD_PTR proc2, sys2; + status = GetProcessAffinityMask(GetCurrentProcess(), &proc2, &sys2); + if (status == 0) + { + affinity.isOldDeterminate = false; + return; + } + + procCombined &= static_cast(proc2); + sysCombined &= static_cast(sys2); + } + + if (procCombined != sysCombined) + isAffinityFull = false; + + for (size_t j = 0; j < WIN_PROCESSOR_GROUP_SIZE; ++j) + { + if (procCombined & (KAFFINITY(1) << j)) + cpus.insert(procGroupIndex * WIN_PROCESSOR_GROUP_SIZE + j); + } + } + + // We have to detect the case where the affinity was not set, or is set to all processors + // so that we correctly produce as std::nullopt result. + if (!isAffinityFull) + { + affinity.oldApi = std::move(cpus); + } + }); + + th.join(); + } } return affinity; @@ -300,6 +408,18 @@ inline std::set get_process_affinity() { #endif +#if defined(__linux__) && !defined(__ANDROID__) + +inline static const auto STARTUP_PROCESSOR_AFFINITY = get_process_affinity(); + +#elif defined(_WIN64) + +inline static const auto STARTUP_PROCESSOR_AFFINITY = get_process_affinity(); +inline static const auto STARTUP_USE_OLD_AFFINITY_API = + STARTUP_PROCESSOR_AFFINITY.likely_used_old_api(); + +#endif + // We want to abstract the purpose of storing the numa node index somewhat. // Whoever is using this does not need to know the specifics of the replication // machinery to be able to access NUMA replicated memory. @@ -326,6 +446,8 @@ class NumaReplicatedAccessToken { // It is guaranteed that NUMA nodes are NOT empty, i.e. every node exposed by NumaConfig // has at least one processor assigned. // +// We use startup affinities so as not to modify its own behaviour in time. +// // Until Stockfish doesn't support exceptions all places where an exception should be thrown // are replaced by std::exit. class NumaConfig { @@ -349,7 +471,7 @@ class NumaConfig { std::set allowedCpus; if (respectProcessAffinity) - allowedCpus = get_process_affinity(); + allowedCpus = STARTUP_PROCESSOR_AFFINITY; auto is_cpu_allowed = [respectProcessAffinity, &allowedCpus](CpuIndex c) { return !respectProcessAffinity || allowedCpus.count(c) == 1; @@ -414,7 +536,7 @@ class NumaConfig { std::optional> allowedCpus; if (respectProcessAffinity) - allowedCpus = get_process_affinity().get_combined(); + allowedCpus = STARTUP_PROCESSOR_AFFINITY.get_combined(); // The affinity can't be determined in all cases on Windows, but we at least guarantee // that the number of allowed processors is >= number of processors in the affinity mask. @@ -451,7 +573,7 @@ class NumaConfig { // still no way to set thread affinity spanning multiple processor groups. // See https://learn.microsoft.com/en-us/windows/win32/procthread/numa-support // We also do this is if need to force old API for some reason. - if (use_old_affinity_api()) + if (STARTUP_USE_OLD_AFFINITY_API) { NumaConfig splitCfg = empty(); @@ -733,7 +855,7 @@ class NumaConfig { } // Sometimes we need to force the old API, but do not use it unless necessary. - if (SetThreadSelectedCpuSetMasks_f == nullptr || use_old_affinity_api()) + if (SetThreadSelectedCpuSetMasks_f == nullptr || STARTUP_USE_OLD_AFFINITY_API) { // On earlier windows version (since windows 7) we can't run a single thread // on multiple processor groups, so we need to restrict the group. From 7d4ffa175c52a425c6ebc19737586baf93f5b6ff Mon Sep 17 00:00:00 2001 From: Dubslow Date: Mon, 3 Jun 2024 17:47:03 -0500 Subject: [PATCH 1588/1766] Remove delta from evaluation Passed STC: https://tests.stockfishchess.org/tests/view/6660e49c6489614cdad14e29 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 188768 W: 48907 L: 48854 D: 91007 Ptnml(0-2): 584, 22571, 48005, 22656, 568 Passed LTC: https://tests.stockfishchess.org/tests/view/6660ff9791e372763104b38c LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 310680 W: 78651 L: 78727 D: 153302 Ptnml(0-2): 180, 34818, 85433, 34716, 193 closes https://github.com/official-stockfish/Stockfish/pull/5373 Bench: 1214575 --- src/evaluate.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index fdf35eb17d4..1317a01ecbd 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -47,7 +47,7 @@ int Eval::simple_eval(const Position& pos, Color c) { bool Eval::use_smallnet(const Position& pos) { int simpleEval = simple_eval(pos, pos.side_to_move()); - return std::abs(simpleEval) > 992; + return std::abs(simpleEval) > 962; } // Evaluate is the evaluator for the outer world. It returns a static evaluation @@ -66,25 +66,24 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, auto [psqt, positional] = smallNet ? networks.small.evaluate(pos, &caches.small) : networks.big.evaluate(pos, &caches.big); - constexpr int delta = 3; - Value nnue = ((128 - delta) * psqt + (128 + delta) * positional) / 128; - int nnueComplexity = std::abs(psqt - positional); + Value nnue = psqt + positional; + int nnueComplexity = std::abs(psqt - positional); // Re-evaluate the position when higher eval accuracy is worth the time spent - if (smallNet && (nnue * simpleEval < 0 || std::abs(nnue) < 250)) + if (smallNet && (nnue * simpleEval < 0 || std::abs(nnue) < 227)) { std::tie(psqt, positional) = networks.big.evaluate(pos, &caches.big); - nnue = ((128 - delta) * psqt + (128 + delta) * positional) / 128; + nnue = psqt + positional; nnueComplexity = std::abs(psqt - positional); smallNet = false; } // Blend optimism and eval with nnue complexity - optimism += optimism * nnueComplexity / 470; - nnue -= nnue * nnueComplexity / 20000; + optimism += optimism * nnueComplexity / 457; + nnue -= nnue * nnueComplexity / 19157; - int material = 600 * pos.count() + pos.non_pawn_material(); - v = (nnue * (68600 + material) + optimism * (8800 + material)) / 73344; + int material = 554 * pos.count() + pos.non_pawn_material(); + v = (nnue * (73921 + material) + optimism * (8112 + material)) / 73260; // Damp down the evaluation linearly when shuffling v -= v * pos.rule50_count() / 212; From 1c67b46caf91a0e6277967ea9a7e4b2f6afbc971 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Thu, 6 Jun 2024 13:10:30 -0500 Subject: [PATCH 1589/1766] Linearize corrHist Passed STC: https://tests.stockfishchess.org/tests/view/6661fff88dd8f31ed3c5d819 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 17504 W: 4651 L: 4406 D: 8447 Ptnml(0-2): 71, 1975, 4384, 2282, 40 Passed LTC: https://tests.stockfishchess.org/tests/view/666205b48dd8f31ed3c61296 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 24522 W: 6313 L: 6094 D: 12115 Ptnml(0-2): 14, 2643, 6726, 2866, 12 closes https://github.com/official-stockfish/Stockfish/pull/5374 Bench: 1237729 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 7417a4b6f5e..06adc92a3e6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -74,7 +74,7 @@ constexpr int futility_move_count(bool improving, Depth depth) { // Add correctionHistory value to raw staticEval and guarantee evaluation does not hit the tablebase range Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { auto cv = w.correctionHistory[pos.side_to_move()][pawn_structure_index(pos)]; - v += cv * std::abs(cv) / 4990; + v += cv / 10; return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } From 4151c06b744a3145617200ca8f76285aae193dc2 Mon Sep 17 00:00:00 2001 From: evqsx <149484438+evqsx@users.noreply.github.com> Date: Thu, 6 Jun 2024 15:43:55 +0800 Subject: [PATCH 1590/1766] Remove the correction history bonus in null move search Passed STC: https://tests.stockfishchess.org/tests/view/666168e191e372763104c664 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 94848 W: 24708 L: 24550 D: 45590 Ptnml(0-2): 289, 11355, 24033, 11403, 344 Passed LTC: https://tests.stockfishchess.org/tests/view/6661e73591e372763104c751 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 70452 W: 17849 L: 17679 D: 34924 Ptnml(0-2): 27, 7707, 19596, 7861, 35 closes https://github.com/official-stockfish/Stockfish/pull/5375 Bench: 1174094 --- AUTHORS | 1 + src/search.cpp | 9 --------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/AUTHORS b/AUTHORS index a232e115f7a..6eefb56dd5f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -72,6 +72,7 @@ Ehsan Rashid (erashid) Elvin Liu (solarlight2) erbsenzaehler Ernesto Gatti +evqsx Fabian Beuke (madnight) Fabian Fichter (ianfab) Fanael Linithien (Fanael) diff --git a/src/search.cpp b/src/search.cpp index 06adc92a3e6..8ae12e68bf4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -805,16 +805,7 @@ Value Search::Worker::search( if (nullValue >= beta && nullValue < VALUE_TB_WIN_IN_MAX_PLY) { if (thisThread->nmpMinPly || depth < 16) - { - if (nullValue >= ss->staticEval) - { - auto bonus = std::min(int(nullValue - ss->staticEval) * depth / 32, - CORRECTION_HISTORY_LIMIT / 16); - thisThread->correctionHistory[us][pawn_structure_index(pos)] - << bonus; - } return nullValue; - } assert(!thisThread->nmpMinPly); // Recursive verification is not allowed From e271059e08c6258420af12897367ea2149220171 Mon Sep 17 00:00:00 2001 From: cj5716 <125858804+cj5716@users.noreply.github.com> Date: Fri, 7 Jun 2024 18:30:33 +0800 Subject: [PATCH 1591/1766] Make repeated bench runs identical fixes https://github.com/official-stockfish/Stockfish/issues/5376 closes https://github.com/official-stockfish/Stockfish/pull/5377 No functional changes --- src/tt.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tt.cpp b/src/tt.cpp index 56779b861fd..5a44759e4bf 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -94,6 +94,7 @@ void TranspositionTable::resize(size_t mbSize, ThreadPool& threads) { // Initializes the entire transposition table to zero, // in a multi-threaded way. void TranspositionTable::clear(ThreadPool& threads) { + generation8 = 0; const size_t threadCount = threads.num_threads(); for (size_t i = 0; i < threadCount; ++i) From 7e890fd048e22bfd213d46ec8eb88f7931f0315d Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 7 Jun 2024 23:53:33 +0200 Subject: [PATCH 1592/1766] Keep mate PVs intact. do not return a cutoff value in razoring if that value is in the mate/tb range. passed STC: https://tests.stockfishchess.org/tests/view/666381880ff7cb4868d1fe58 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 130848 W: 34046 L: 33931 D: 62871 Ptnml(0-2): 429, 14968, 34524, 15065, 438 passed LTC: https://tests.stockfishchess.org/tests/view/66643f120612cd151f9e7788 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 39702 W: 10157 L: 9959 D: 19586 Ptnml(0-2): 20, 4108, 11402, 4296, 25 closes https://github.com/official-stockfish/Stockfish/pull/5379 Bench: 1174094 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 8ae12e68bf4..3dbdfd47c27 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -768,7 +768,7 @@ Value Search::Worker::search( if (eval < alpha - 512 - 293 * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); - if (value < alpha) + if (value < alpha && std::abs(value) < VALUE_TB_WIN_IN_MAX_PLY) return value; } From c8213ba0d047569141ed58f5eb86579d976b5614 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Mon, 10 Jun 2024 18:03:36 -0500 Subject: [PATCH 1593/1766] Simplify TT interface and avoid changing TT info This commit builds on the work and ideas of #5345, #5348, and #5364. Place as much as possible of the TT implementation in tt.cpp, rather than in the header. Some commentary is added to better document the public interface. Fix the search read-TT races, or at least contain them to within TT methods only. Passed SMP STC: https://tests.stockfishchess.org/tests/view/666134ab91e372763104b443 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 512552 W: 132387 L: 132676 D: 247489 Ptnml(0-2): 469, 58429, 138771, 58136, 471 The unmerged version has bench identical to the other PR (see also #5348) and therefore those same-functionality tests: SMP LTC: https://tests.stockfishchess.org/tests/view/665c7021fd45fb0f907c214a SMP LTC: https://tests.stockfishchess.org/tests/view/665d28a7fd45fb0f907c5495 closes https://github.com/official-stockfish/Stockfish/pull/5369 bench 1205675 --- src/search.cpp | 199 +++++++++++++++++++++--------------------- src/tt.cpp | 126 ++++++++++++++++++++++---- src/tt.h | 119 ++++++++++--------------- tests/instrumented.sh | 7 +- 4 files changed, 254 insertions(+), 197 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 3dbdfd47c27..9c3f915db34 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -546,16 +546,15 @@ Value Search::Worker::search( StateInfo st; ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize); - TTEntry* tte; - Key posKey; - Move ttMove, move, excludedMove, bestMove; - Depth extension, newDepth; - Value bestValue, value, ttValue, eval, maxValue, probCutBeta, singularValue; - bool givesCheck, improving, priorCapture, opponentWorsening; - bool capture, moveCountPruning, ttCapture; - Piece movedPiece; - int moveCount, captureCount, quietCount; - Bound singularBound; + Key posKey; + Move move, excludedMove, bestMove; + Depth extension, newDepth; + Value bestValue, value, eval, maxValue, probCutBeta, singularValue; + bool givesCheck, improving, priorCapture, opponentWorsening; + bool capture, moveCountPruning, ttCapture; + Piece movedPiece; + int moveCount, captureCount, quietCount; + Bound singularBound; // Step 1. Initialize node Worker* thisThread = this; @@ -605,31 +604,32 @@ Value Search::Worker::search( ss->statScore = 0; // Step 4. Transposition table lookup. - excludedMove = ss->excludedMove; - posKey = pos.key(); - tte = tt.probe(posKey, ss->ttHit); - ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; - ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] - : ss->ttHit ? tte->move() - : Move::none(); - ttCapture = ttMove && pos.capture_stage(ttMove); + excludedMove = ss->excludedMove; + posKey = pos.key(); + auto [ttHit, ttData, ttWriter] = tt.probe(posKey); + // Need further processing of the saved data + ss->ttHit = ttHit; + ttData.move = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0] + : ttHit ? ttData.move + : Move::none(); + ttData.value = ttHit ? value_from_tt(ttData.value, ss->ply, pos.rule50_count()) : VALUE_NONE; + ss->ttPv = excludedMove ? ss->ttPv : PvNode || (ttHit && ttData.is_pv); + ttCapture = ttData.move && pos.capture_stage(ttData.move); // At this point, if excluded, skip straight to step 6, static eval. However, // to save indentation, we list the condition in all code between here and there. - if (!excludedMove) - ss->ttPv = PvNode || (ss->ttHit && tte->is_pv()); // At non-PV nodes we check for an early TT cutoff - if (!PvNode && !excludedMove && tte->depth() > depth - (ttValue <= beta) - && ttValue != VALUE_NONE // Possible in case of TT access race or if !ttHit - && (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER))) + if (!PvNode && !excludedMove && ttData.depth > depth - (ttData.value <= beta) + && ttData.value != VALUE_NONE // Can happen when !ttHit or when access race in probe() + && (ttData.bound & (ttData.value >= beta ? BOUND_LOWER : BOUND_UPPER))) { // If ttMove is quiet, update move sorting heuristics on TT hit (~2 Elo) - if (ttMove && ttValue >= beta) + if (ttData.move && ttData.value >= beta) { // Bonus for a quiet ttMove that fails high (~2 Elo) if (!ttCapture) - update_quiet_stats(pos, ss, *this, ttMove, stat_bonus(depth)); + update_quiet_stats(pos, ss, *this, ttData.move, stat_bonus(depth)); // Extra penalty for early quiet moves of // the previous ply (~1 Elo on STC, ~2 Elo on LTC) @@ -641,7 +641,7 @@ Value Search::Worker::search( // Partial workaround for the graph history interaction problem // For high rule50 counts don't produce transposition table cutoffs. if (pos.rule50_count() < 90) - return ttValue; + return ttData.value; } // Step 5. Tablebases probe @@ -679,9 +679,9 @@ Value Search::Worker::search( if (b == BOUND_EXACT || (b == BOUND_LOWER ? value >= beta : value <= alpha)) { - tte->save(posKey, value_to_tt(value, ss->ply), ss->ttPv, b, - std::min(MAX_PLY - 1, depth + 6), Move::none(), VALUE_NONE, - tt.generation()); + ttWriter.write(posKey, value_to_tt(value, ss->ply), ss->ttPv, b, + std::min(MAX_PLY - 1, depth + 6), Move::none(), VALUE_NONE, + tt.generation()); return value; } @@ -716,7 +716,7 @@ Value Search::Worker::search( else if (ss->ttHit) { // Never assume anything about values stored in TT - unadjustedStaticEval = tte->eval(); + unadjustedStaticEval = ttData.eval; if (unadjustedStaticEval == VALUE_NONE) unadjustedStaticEval = evaluate(networks[numaAccessToken], pos, refreshTable, thisThread->optimism[us]); @@ -726,8 +726,9 @@ Value Search::Worker::search( ss->staticEval = eval = to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); // ttValue can be used as a better position evaluation (~7 Elo) - if (ttValue != VALUE_NONE && (tte->bound() & (ttValue > eval ? BOUND_LOWER : BOUND_UPPER))) - eval = ttValue; + if (ttData.value != VALUE_NONE + && (ttData.bound & (ttData.value > eval ? BOUND_LOWER : BOUND_UPPER))) + eval = ttData.value; } else { @@ -736,8 +737,8 @@ Value Search::Worker::search( ss->staticEval = eval = to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); // Static evaluation is saved as it was before adjustment by correction history - tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_UNSEARCHED, Move::none(), - unadjustedStaticEval, tt.generation()); + ttWriter.write(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_UNSEARCHED, Move::none(), + unadjustedStaticEval, tt.generation()); } // Use static evaluation difference to improve quiet move ordering (~9 Elo) @@ -778,7 +779,7 @@ Value Search::Worker::search( && eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening) - (ss - 1)->statScore / 263 >= beta - && eval >= beta && eval < VALUE_TB_WIN_IN_MAX_PLY && (!ttMove || ttCapture)) + && eval >= beta && eval < VALUE_TB_WIN_IN_MAX_PLY && (!ttData.move || ttCapture)) return beta > VALUE_TB_LOSS_IN_MAX_PLY ? beta + (eval - beta) / 3 : eval; // Step 9. Null move search with verification search (~35 Elo) @@ -824,7 +825,7 @@ Value Search::Worker::search( // Step 10. Internal iterative reductions (~9 Elo) // For PV nodes without a ttMove, we decrease depth by 3. - if (PvNode && !ttMove) + if (PvNode && !ttData.move) depth -= 3; // Use qsearch if depth <= 0. @@ -833,8 +834,8 @@ Value Search::Worker::search( // For cutNodes, if depth is high enough, decrease depth by 2 if there is no ttMove, or // by 1 if there is a ttMove with an upper bound. - if (cutNode && depth >= 8 && (!ttMove || tte->bound() == BOUND_UPPER)) - depth -= 1 + !ttMove; + if (cutNode && depth >= 8 && (!ttData.move || ttData.bound == BOUND_UPPER)) + depth -= 1 + !ttData.move; // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search returns a value @@ -847,11 +848,11 @@ Value Search::Worker::search( // there and in further interactions with transposition table cutoff depth is set to depth - 3 // because probCut search has depth set to depth - 4 but we also do a move before it // So effective depth is equal to depth - 3 - && !(tte->depth() >= depth - 3 && ttValue != VALUE_NONE && ttValue < probCutBeta)) + && !(ttData.depth >= depth - 3 && ttData.value != VALUE_NONE && ttData.value < probCutBeta)) { assert(probCutBeta < VALUE_INFINITE && probCutBeta > beta); - MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, &thisThread->captureHistory); + MovePicker mp(pos, ttData.move, probCutBeta - ss->staticEval, &thisThread->captureHistory); while ((move = mp.next_move()) != Move::none()) if (move != excludedMove && pos.legal(move)) @@ -882,8 +883,8 @@ Value Search::Worker::search( if (value >= probCutBeta) { // Save ProbCut data into transposition table - tte->save(posKey, value_to_tt(value, ss->ply), ss->ttPv, BOUND_LOWER, depth - 3, - move, unadjustedStaticEval, tt.generation()); + ttWriter.write(posKey, value_to_tt(value, ss->ply), ss->ttPv, BOUND_LOWER, + depth - 3, move, unadjustedStaticEval, tt.generation()); return std::abs(value) < VALUE_TB_WIN_IN_MAX_PLY ? value - (probCutBeta - beta) : value; } @@ -896,9 +897,10 @@ Value Search::Worker::search( // Step 12. A small Probcut idea, when we are in check (~4 Elo) probCutBeta = beta + 388; - if (ss->inCheck && !PvNode && ttCapture && (tte->bound() & BOUND_LOWER) - && tte->depth() >= depth - 4 && ttValue >= probCutBeta - && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) + if (ss->inCheck && !PvNode && ttCapture && (ttData.bound & BOUND_LOWER) + && ttData.depth >= depth - 4 && ttData.value >= probCutBeta + && std::abs(ttData.value) < VALUE_TB_WIN_IN_MAX_PLY + && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) return probCutBeta; const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, @@ -911,7 +913,7 @@ Value Search::Worker::search( Move countermove = prevSq != SQ_NONE ? thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] : Move::none(); - MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory, + MovePicker mp(pos, ttData.move, depth, &thisThread->mainHistory, &thisThread->captureHistory, contHist, &thisThread->pawnHistory, countermove, ss->killers); value = bestValue; @@ -1046,12 +1048,12 @@ Value Search::Worker::search( // Generally, higher singularBeta (i.e closer to ttValue) and lower extension // margins scale well. - if (!rootNode && move == ttMove && !excludedMove + if (!rootNode && move == ttData.move && !excludedMove && depth >= 4 - (thisThread->completedDepth > 35) + ss->ttPv - && std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY && (tte->bound() & BOUND_LOWER) - && tte->depth() >= depth - 3) + && std::abs(ttData.value) < VALUE_TB_WIN_IN_MAX_PLY && (ttData.bound & BOUND_LOWER) + && ttData.depth >= depth - 3) { - Value singularBeta = ttValue - (52 + 80 * (ss->ttPv && !PvNode)) * depth / 64; + Value singularBeta = ttData.value - (52 + 80 * (ss->ttPv && !PvNode)) * depth / 64; Depth singularDepth = newDepth / 2; ss->excludedMove = move; @@ -1086,7 +1088,7 @@ Value Search::Worker::search( // so we reduce the ttMove in favor of other moves based on some conditions: // If the ttMove is assumed to fail high over current beta (~7 Elo) - else if (ttValue >= beta) + else if (ttData.value >= beta) extension = -3; // If we are on a cutNode but the ttMove is not assumed to fail high over current beta (~1 Elo) @@ -1126,7 +1128,7 @@ Value Search::Worker::search( // Decrease reduction if position is or has been on the PV (~7 Elo) if (ss->ttPv) - r -= 1 + (ttValue > alpha) + (tte->depth() >= depth); + r -= 1 + (ttData.value > alpha) + (ttData.depth >= depth); // Decrease reduction for PvNodes (~0 Elo on STC, ~2 Elo on LTC) if (PvNode) @@ -1136,8 +1138,8 @@ Value Search::Worker::search( // Increase reduction for cut nodes (~4 Elo) if (cutNode) - r += 2 - (tte->depth() >= depth && ss->ttPv) - + (!ss->ttPv && move != ttMove && move != ss->killers[0]); + r += 2 - (ttData.depth >= depth && ss->ttPv) + + (!ss->ttPv && move != ttData.move && move != ss->killers[0]); // Increase reduction if ttMove is a capture (~3 Elo) if (ttCapture) @@ -1149,7 +1151,7 @@ Value Search::Worker::search( // For first picked move (ttMove) reduce reduction // but never allow it to go below 0 (~3 Elo) - else if (move == ttMove) + else if (move == ttData.move) r = std::max(0, r - 2); ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] @@ -1197,7 +1199,7 @@ Value Search::Worker::search( else if (!PvNode || moveCount > 1) { // Increase reduction if ttMove is not present (~6 Elo) - if (!ttMove) + if (!ttData.move) r += 2; // Note that if expected reduction is high, we reduce search depth by 1 here (~9 Elo) @@ -1287,7 +1289,7 @@ Value Search::Worker::search( if (value >= beta) { - ss->cutoffCnt += 1 + !ttMove - (extension >= 2); + ss->cutoffCnt += 1 + !ttData.move - (extension >= 2); assert(value >= beta); // Fail high break; } @@ -1363,11 +1365,11 @@ Value Search::Worker::search( // Write gathered information in transposition table // Static evaluation is saved as it was before correction history if (!excludedMove && !(rootNode && thisThread->pvIdx)) - tte->save(posKey, value_to_tt(bestValue, ss->ply), ss->ttPv, - bestValue >= beta ? BOUND_LOWER - : PvNode && bestMove ? BOUND_EXACT - : BOUND_UPPER, - depth, bestMove, unadjustedStaticEval, tt.generation()); + ttWriter.write(posKey, value_to_tt(bestValue, ss->ply), ss->ttPv, + bestValue >= beta ? BOUND_LOWER + : PvNode && bestMove ? BOUND_EXACT + : BOUND_UPPER, + depth, bestMove, unadjustedStaticEval, tt.generation()); // Adjust correction history if (!ss->inCheck && (!bestMove || !pos.capture(bestMove)) @@ -1414,14 +1416,12 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, StateInfo st; ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize); - TTEntry* tte; - Key posKey; - Move ttMove, move, bestMove; - Depth ttDepth; - Value bestValue, value, ttValue, futilityBase; - bool pvHit, givesCheck, capture; - int moveCount; - Color us = pos.side_to_move(); + Key posKey; + Move move, bestMove; + Value bestValue, value, futilityBase; + bool pvHit, givesCheck, capture; + int moveCount; + Color us = pos.side_to_move(); // Step 1. Initialize node if (PvNode) @@ -1447,23 +1447,25 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, assert(0 <= ss->ply && ss->ply < MAX_PLY); - // Note that unlike regular search, which stores literal depth, in QS we only store the - // current movegen stage. If in check, we search all evasions and thus store - // DEPTH_QS_CHECKS. (Evasions may be quiet, and _CHECKS includes quiets.) - ttDepth = ss->inCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS : DEPTH_QS_NORMAL; + // Note that unlike regular search, which stores the literal depth into the TT, from QS we + // only store the current movegen stage as "depth". If in check, we search all evasions and + // thus store DEPTH_QS_CHECKS. (Evasions may be quiet, and _CHECKS includes quiets.) + Depth qsTtDepth = ss->inCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS : DEPTH_QS_NORMAL; // Step 3. Transposition table lookup - posKey = pos.key(); - tte = tt.probe(posKey, ss->ttHit); - ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE; - ttMove = ss->ttHit ? tte->move() : Move::none(); - pvHit = ss->ttHit && tte->is_pv(); + posKey = pos.key(); + auto [ttHit, ttData, ttWriter] = tt.probe(posKey); + // Need further processing of the saved data + ss->ttHit = ttHit; + ttData.move = ttHit ? ttData.move : Move::none(); + ttData.value = ttHit ? value_from_tt(ttData.value, ss->ply, pos.rule50_count()) : VALUE_NONE; + pvHit = ttHit && ttData.is_pv; // At non-PV nodes we check for an early TT cutoff - if (!PvNode && tte->depth() >= ttDepth - && ttValue != VALUE_NONE // Only in case of TT access race or if !ttHit - && (tte->bound() & (ttValue >= beta ? BOUND_LOWER : BOUND_UPPER))) - return ttValue; + if (!PvNode && ttData.depth >= qsTtDepth + && ttData.value != VALUE_NONE // Can happen when !ttHit or when access race in probe() + && (ttData.bound & (ttData.value >= beta ? BOUND_LOWER : BOUND_UPPER))) + return ttData.value; // Step 4. Static evaluation of the position Value unadjustedStaticEval = VALUE_NONE; @@ -1474,7 +1476,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, if (ss->ttHit) { // Never assume anything about values stored in TT - unadjustedStaticEval = tte->eval(); + unadjustedStaticEval = ttData.eval; if (unadjustedStaticEval == VALUE_NONE) unadjustedStaticEval = evaluate(networks[numaAccessToken], pos, refreshTable, thisThread->optimism[us]); @@ -1482,9 +1484,9 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); // ttValue can be used as a better position evaluation (~13 Elo) - if (std::abs(ttValue) < VALUE_TB_WIN_IN_MAX_PLY - && (tte->bound() & (ttValue > bestValue ? BOUND_LOWER : BOUND_UPPER))) - bestValue = ttValue; + if (std::abs(ttData.value) < VALUE_TB_WIN_IN_MAX_PLY + && (ttData.bound & (ttData.value > bestValue ? BOUND_LOWER : BOUND_UPPER))) + bestValue = ttData.value; } else { @@ -1503,9 +1505,9 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, if (std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY && !PvNode) bestValue = (3 * bestValue + beta) / 4; if (!ss->ttHit) - tte->save(posKey, value_to_tt(bestValue, ss->ply), false, BOUND_LOWER, - DEPTH_UNSEARCHED, Move::none(), unadjustedStaticEval, tt.generation()); - + ttWriter.write(posKey, value_to_tt(bestValue, ss->ply), false, BOUND_LOWER, + DEPTH_UNSEARCHED, Move::none(), unadjustedStaticEval, + tt.generation()); return bestValue; } @@ -1524,7 +1526,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, // (Presently, having the checks stage is worth only 1 Elo, and may be removable in the near future, // which would result in only a single stage of QS movegen.) Square prevSq = ((ss - 1)->currentMove).is_ok() ? ((ss - 1)->currentMove).to_sq() : SQ_NONE; - MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory, + MovePicker mp(pos, ttData.move, depth, &thisThread->mainHistory, &thisThread->captureHistory, contHist, &thisThread->pawnHistory); // Step 5. Loop through all pseudo-legal moves until no moves remain or a beta cutoff occurs. @@ -1643,9 +1645,9 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, // Save gathered info in transposition table // Static evaluation is saved as it was before adjustment by correction history - tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit, - bestValue >= beta ? BOUND_LOWER : BOUND_UPPER, ttDepth, bestMove, - unadjustedStaticEval, tt.generation()); + ttWriter.write(posKey, value_to_tt(bestValue, ss->ply), pvHit, + bestValue >= beta ? BOUND_LOWER : BOUND_UPPER, qsTtDepth, bestMove, + unadjustedStaticEval, tt.generation()); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); @@ -1986,20 +1988,17 @@ bool RootMove::extract_ponder_from_tt(const TranspositionTable& tt, Position& po StateInfo st; ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize); - bool ttHit; - assert(pv.size() == 1); if (pv[0] == Move::none()) return false; pos.do_move(pv[0], st); - TTEntry* tte = tt.probe(pos.key(), ttHit); + auto [ttHit, ttData, ttWriter] = tt.probe(pos.key()); if (ttHit) { - Move m = tte->move(); // Local copy to be SMP safe - if (MoveList(pos).contains(m)) - pv.push_back(m); + if (MoveList(pos).contains(ttData.move)) + pv.push_back(ttData.move); } pos.undo_move(pv[0]); diff --git a/src/tt.cpp b/src/tt.cpp index 5a44759e4bf..763e2c9b349 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -25,11 +25,63 @@ #include #include "memory.h" +#include "misc.h" #include "syzygy/tbprobe.h" #include "thread.h" namespace Stockfish { + +// TTEntry struct is the 10 bytes transposition table entry, defined as below: +// +// key 16 bit +// depth 8 bit +// generation 5 bit +// pv node 1 bit +// bound type 2 bit +// move 16 bit +// value 16 bit +// evaluation 16 bit +// +// These fields are in the same order as accessed by TT::probe(), since memory is fastest sequentially. +// Equally, the store order in save() matches this order. + +struct TTEntry { + + // Convert internal bitfields to external types + TTData read() const { + return TTData{Move(move16), Value(value16), + Value(eval16), Depth(depth8 + DEPTH_ENTRY_OFFSET), + Bound(genBound8 & 0x3), bool(genBound8 & 0x4)}; + } + + void save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev, uint8_t generation8); + // The returned age is a multiple of TranspositionTable::GENERATION_DELTA + uint8_t relative_age(const uint8_t generation8) const; + + private: + friend class TranspositionTable; + + uint16_t key16; + uint8_t depth8; + uint8_t genBound8; + Move move16; + int16_t value16; + int16_t eval16; +}; + +// `genBound8` is where most of the details are. We use the following constants to manipulate 5 leading generation bits +// and 3 trailing miscellaneous bits. + +// These bits are reserved for other things. +static constexpr unsigned GENERATION_BITS = 3; +// increment for generation field +static constexpr int GENERATION_DELTA = (1 << GENERATION_BITS); +// cycle length +static constexpr int GENERATION_CYCLE = 255 + GENERATION_DELTA; +// mask to pull out generation number +static constexpr int GENERATION_MASK = (0xFF << GENERATION_BITS) & 0xFF; + // DEPTH_ENTRY_OFFSET exists because 1) we use `bool(depth8)` as the occupancy check, but // 2) we need to store negative depths for QS. (`depth8` is the only field with "spare bits": // we sacrifice the ability to store depths greater than 1<<8 less the offset, as asserted below.) @@ -65,12 +117,34 @@ uint8_t TTEntry::relative_age(const uint8_t generation8) const { // is needed to keep the unrelated lowest n bits from affecting // the result) to calculate the entry age correctly even after // generation8 overflows into the next cycle. + return (GENERATION_CYCLE + generation8 - genBound8) & GENERATION_MASK; +} + - return (TranspositionTable::GENERATION_CYCLE + generation8 - genBound8) - & TranspositionTable::GENERATION_MASK; +// TTWriter is but a very thin wrapper around the pointer +TTWriter::TTWriter(TTEntry* tte) : + entry(tte) {} + +void TTWriter::write( + Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev, uint8_t generation8) { + entry->save(k, v, pv, b, d, m, ev, generation8); } +// A TranspositionTable is an array of Cluster, of size clusterCount. Each cluster consists of ClusterSize number +// of TTEntry. Each non-empty TTEntry contains information on exactly one position. The size of a Cluster should +// divide the size of a cache line for best performance, as the cacheline is prefetched when possible. + +static constexpr int ClusterSize = 3; + +struct Cluster { + TTEntry entry[ClusterSize]; + char padding[2]; // Pad to 32 bytes +}; + +static_assert(sizeof(Cluster) == 32, "Suboptimal Cluster size"); + + // Sets the size of the transposition table, // measured in megabytes. Transposition table consists // of clusters and each cluster consists of ClusterSize number of TTEntry. @@ -114,20 +188,46 @@ void TranspositionTable::clear(ThreadPool& threads) { } +// Returns an approximation of the hashtable +// occupation during a search. The hash is x permill full, as per UCI protocol. +// Only counts entries which match the current generation. +int TranspositionTable::hashfull() const { + + int cnt = 0; + for (int i = 0; i < 1000; ++i) + for (int j = 0; j < ClusterSize; ++j) + cnt += table[i].entry[j].depth8 + && (table[i].entry[j].genBound8 & GENERATION_MASK) == generation8; + + return cnt / ClusterSize; +} + + +void TranspositionTable::new_search() { + // increment by delta to keep lower bits as is + generation8 += GENERATION_DELTA; +} + + +uint8_t TranspositionTable::generation() const { return generation8; } + + // Looks up the current position in the transposition -// table. It returns true and a pointer to the TTEntry if the position is found. +// table. It returns true if the position is found. // Otherwise, it returns false and a pointer to an empty or least valuable TTEntry // to be replaced later. The replace value of an entry is calculated as its depth // minus 8 times its relative age. TTEntry t1 is considered more valuable than // TTEntry t2 if its replace value is greater than that of t2. -TTEntry* TranspositionTable::probe(const Key key, bool& found) const { +std::tuple TranspositionTable::probe(const Key key) const { TTEntry* const tte = first_entry(key); const uint16_t key16 = uint16_t(key); // Use the low 16 bits as key inside the cluster for (int i = 0; i < ClusterSize; ++i) if (tte[i].key16 == key16) - return found = bool(tte[i].depth8), &tte[i]; + // This gap is the main place for read races. + // After `read()` completes that copy is final, but may be self-inconsistent. + return {bool(tte[i].depth8), tte[i].read(), TTWriter(&tte[i])}; // Find an entry to be replaced according to the replacement strategy TTEntry* replace = tte; @@ -136,22 +236,12 @@ TTEntry* TranspositionTable::probe(const Key key, bool& found) const { > tte[i].depth8 - tte[i].relative_age(generation8) * 2) replace = &tte[i]; - return found = false, replace; + return {false, replace->read(), TTWriter(replace)}; } -// Returns an approximation of the hashtable -// occupation during a search. The hash is x permill full, as per UCI protocol. -// Only counts entries which match the current generation. -int TranspositionTable::hashfull() const { - - int cnt = 0; - for (int i = 0; i < 1000; ++i) - for (int j = 0; j < ClusterSize; ++j) - cnt += table[i].entry[j].depth8 - && (table[i].entry[j].genBound8 & GENERATION_MASK) == generation8; - - return cnt / ClusterSize; +TTEntry* TranspositionTable::first_entry(const Key key) const { + return &table[mul_hi64(key, clusterCount)].entry[0]; } } // namespace Stockfish diff --git a/src/tt.h b/src/tt.h index b2e8f582b3b..1bece002c7b 100644 --- a/src/tt.h +++ b/src/tt.h @@ -21,103 +21,76 @@ #include #include +#include #include "memory.h" -#include "misc.h" #include "types.h" namespace Stockfish { -// TTEntry struct is the 10 bytes transposition table entry, defined as below: -// -// key 16 bit -// depth 8 bit -// generation 5 bit -// pv node 1 bit -// bound type 2 bit -// move 16 bit -// value 16 bit -// eval value 16 bit +class ThreadPool; +struct TTEntry; +struct Cluster; + +// There is only one global hash table for the engine and all its threads. For chess in particular, we even allow racy +// updates between threads to and from the TT, as taking the time to synchronize access would cost thinking time and +// thus elo. As a hash table, collisions are possible and may cause chess playing issues (bizarre blunders, faulty mate +// reports, etc). Fixing these also loses elo; however such risk decreases quickly with larger TT size. // -// These fields are in the same order as accessed by TT::probe(), since memory is fastest sequentially. -// Equally, the store order in save() matches this order. -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 + DEPTH_ENTRY_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, uint8_t generation8); - // The returned age is a multiple of TranspositionTable::GENERATION_DELTA - uint8_t relative_age(const uint8_t generation8) const; +// `probe` is the primary method: given a board position, we lookup its entry in the table, and return a tuple of: +// 1) whether the entry already has this position +// 2) a copy of the prior data (if any) (may be inconsistent due to read races) +// 3) a writer object to this entry +// The copied data and the writer are separated to maintain clear boundaries between local vs global objects. + + +// A copy of the data already in the entry (possibly collided). `probe` may be racy, resulting in inconsistent data. +struct TTData { + Move move; + Value value, eval; + Depth depth; + Bound bound; + bool is_pv; +}; + + +// This is used to make racy writes to the global TT. +struct TTWriter { + public: + void write(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev, uint8_t generation8); private: friend class TranspositionTable; - - uint16_t key16; - uint8_t depth8; - uint8_t genBound8; - Move move16; - int16_t value16; - int16_t eval16; + TTEntry* entry; + TTWriter(TTEntry* tte); }; -class ThreadPool; -// A TranspositionTable is an array of Cluster, of size clusterCount. Each -// cluster consists of ClusterSize number of TTEntry. Each non-empty TTEntry -// contains information on exactly one position. The size of a Cluster should -// divide the size of a cache line for best performance, as the cacheline is -// prefetched when possible. class TranspositionTable { - static constexpr int ClusterSize = 3; - - struct Cluster { - TTEntry entry[ClusterSize]; - char padding[2]; // Pad to 32 bytes - }; - - static_assert(sizeof(Cluster) == 32, "Unexpected Cluster size"); - - // Constants used to refresh the hash table periodically - - // We have 8 bits available where the lowest 3 bits are - // reserved for other things. - static constexpr unsigned GENERATION_BITS = 3; - // increment for generation field - static constexpr int GENERATION_DELTA = (1 << GENERATION_BITS); - // cycle length - static constexpr int GENERATION_CYCLE = 255 + GENERATION_DELTA; - // mask to pull out generation number - static constexpr int GENERATION_MASK = (0xFF << GENERATION_BITS) & 0xFF; - public: ~TranspositionTable() { aligned_large_pages_free(table); } - void new_search() { - // increment by delta to keep lower bits as is - generation8 += GENERATION_DELTA; - } - TTEntry* probe(const Key key, bool& found) const; - int hashfull() const; - void resize(size_t mbSize, ThreadPool& threads); - void clear(ThreadPool& threads); + void resize(size_t mbSize, ThreadPool& threads); // Set TT size + void clear(ThreadPool& threads); // Re-initialize memory, multithreaded + int hashfull() + const; // Approximate what fraction of entries (permille) have been written to during this root search - TTEntry* first_entry(const Key key) const { - return &table[mul_hi64(key, clusterCount)].entry[0]; - } - - uint8_t generation() const { return generation8; } + void + new_search(); // This must be called at the beginning of each root search to track entry aging + uint8_t generation() const; // The current age, used when writing new data to the TT + std::tuple + probe(const Key key) const; // The main method, whose retvals separate local vs global objects + TTEntry* first_entry(const Key key) + const; // This is the hash function; its only external use is memory prefetching. private: friend struct TTEntry; size_t clusterCount; - Cluster* table = nullptr; - uint8_t generation8 = 0; // Size must be not bigger than TTEntry::genBound8 + Cluster* table = nullptr; + + uint8_t generation8 = 0; // Size must be not bigger than TTEntry::genBound8 }; } // namespace Stockfish diff --git a/tests/instrumented.sh b/tests/instrumented.sh index 4c63fc5714e..e77ee0dd2be 100755 --- a/tests/instrumented.sh +++ b/tests/instrumented.sh @@ -39,13 +39,8 @@ case $1 in threads="2" cat << EOF > tsan.supp -race:Stockfish::TTEntry::move -race:Stockfish::TTEntry::depth -race:Stockfish::TTEntry::bound +race:Stockfish::TTEntry::read race:Stockfish::TTEntry::save -race:Stockfish::TTEntry::value -race:Stockfish::TTEntry::eval -race:Stockfish::TTEntry::is_pv race:Stockfish::TranspositionTable::probe race:Stockfish::TranspositionTable::hashfull From 7013a22b741b9fa937e0e027c4992c52b999283c Mon Sep 17 00:00:00 2001 From: Disservin Date: Tue, 4 Jun 2024 22:29:27 +0200 Subject: [PATCH 1594/1766] Move options into the engine Move the engine options into the engine class, also avoid duplicated initializations after startup. UCIEngine needs to register an add_listener to listen to all option changes and print these. Also avoid a double initialization of the TT, which was the case with the old state. closes https://github.com/official-stockfish/Stockfish/pull/5356 No functional change --- src/engine.cpp | 84 +++++++++++++++++++++++++++++++++++++++++++++-- src/engine.h | 11 +++++-- src/tune.cpp | 16 ++++----- src/tune.h | 2 ++ src/uci.cpp | 81 ++++++++------------------------------------- src/uci.h | 5 +-- src/ucioption.cpp | 28 ++++++++++++---- src/ucioption.h | 72 ++++++++++++++++++++++++++-------------- 8 files changed, 183 insertions(+), 116 deletions(-) diff --git a/src/engine.cpp b/src/engine.cpp index 6980dd8341c..233f6270110 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -44,7 +44,8 @@ namespace Stockfish { namespace NN = Eval::NNUE; -constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; +constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; +constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048; Engine::Engine(std::string path) : binaryDirectory(CommandLine::get_binary_directory(path)), @@ -58,6 +59,58 @@ Engine::Engine(std::string path) : NN::NetworkSmall({EvalFileDefaultNameSmall, "None", ""}, NN::EmbeddedNNUEType::SMALL))) { pos.set(StartFEN, false, &states->back()); capSq = SQ_NONE; + + options["Debug Log File"] << Option("", [](const Option& o) { + start_logger(o); + return std::nullopt; + }); + + options["NumaPolicy"] << Option("auto", [this](const Option& o) { + set_numa_config_from_option(o); + return numa_config_information_as_string() + "\n" + thread_binding_information_as_string(); + }); + + options["Threads"] << Option(1, 1, 1024, [this](const Option&) { + resize_threads(); + return thread_binding_information_as_string(); + }); + + options["Hash"] << Option(16, 1, MaxHashMB, [this](const Option& o) { + set_tt_size(o); + return std::nullopt; + }); + + options["Clear Hash"] << Option([this](const Option&) { + search_clear(); + return std::nullopt; + }); + options["Ponder"] << Option(false); + options["MultiPV"] << Option(1, 1, MAX_MOVES); + options["Skill Level"] << Option(20, 0, 20); + options["Move Overhead"] << Option(10, 0, 5000); + options["nodestime"] << Option(0, 0, 10000); + options["UCI_Chess960"] << Option(false); + options["UCI_LimitStrength"] << Option(false); + options["UCI_Elo"] << Option(1320, 1320, 3190); + options["UCI_ShowWDL"] << Option(false); + options["SyzygyPath"] << Option("", [](const Option& o) { + Tablebases::init(o); + return std::nullopt; + }); + options["SyzygyProbeDepth"] << Option(1, 1, 100); + options["Syzygy50MoveRule"] << Option(true); + options["SyzygyProbeLimit"] << Option(7, 0, 7); + options["EvalFile"] << Option(EvalFileDefaultNameBig, [this](const Option& o) { + load_big_network(o); + return std::nullopt; + }); + options["EvalFileSmall"] << Option(EvalFileDefaultNameSmall, [this](const Option& o) { + load_small_network(o); + return std::nullopt; + }); + + load_networks(); + resize_threads(); } std::uint64_t Engine::perft(const std::string& fen, Depth depth, bool isChess960) { @@ -212,7 +265,8 @@ void Engine::trace_eval() const { sync_cout << "\n" << Eval::trace(p, *networks) << sync_endl; } -OptionsMap& Engine::get_options() { return options; } +const OptionsMap& Engine::get_options() const { return options; } +OptionsMap& Engine::get_options() { return options; } std::string Engine::fen() const { return pos.fen(); } @@ -241,4 +295,30 @@ std::string Engine::get_numa_config_as_string() const { return numaContext.get_numa_config().to_string(); } +std::string Engine::numa_config_information_as_string() const { + auto cfgStr = get_numa_config_as_string(); + return "Available Processors: " + cfgStr; +} + +std::string Engine::thread_binding_information_as_string() const { + auto boundThreadsByNode = get_bound_thread_count_by_numa_node(); + if (boundThreadsByNode.empty()) + return ""; + + std::stringstream ss; + ss << "NUMA Node Thread Binding: "; + + bool isFirst = true; + + for (auto&& [current, total] : boundThreadsByNode) + { + if (!isFirst) + ss << ":"; + ss << current << "/" << total; + isFirst = false; + } + + return ss.str(); +} + } diff --git a/src/engine.h b/src/engine.h index 91a8a96b0dc..0d6f0f2b826 100644 --- a/src/engine.h +++ b/src/engine.h @@ -29,13 +29,13 @@ #include #include "nnue/network.h" +#include "numa.h" #include "position.h" #include "search.h" #include "syzygy/tbprobe.h" // for Stockfish::Depth #include "thread.h" #include "tt.h" #include "ucioption.h" -#include "numa.h" namespace Stockfish { @@ -92,13 +92,18 @@ class Engine { // utility functions - void trace_eval() const; - OptionsMap& get_options(); + void trace_eval() const; + + const OptionsMap& get_options() const; + OptionsMap& get_options(); + std::string fen() const; void flip(); std::string visualize() const; std::vector> get_bound_thread_count_by_numa_node() const; std::string get_numa_config_as_string() const; + std::string numa_config_information_as_string() const; + std::string thread_binding_information_as_string() const; private: const std::string binaryDirectory; diff --git a/src/tune.cpp b/src/tune.cpp index 94c9b53ecc6..dfcd34689d4 100644 --- a/src/tune.cpp +++ b/src/tune.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -33,19 +34,19 @@ namespace Stockfish { bool Tune::update_on_last; const Option* LastOption = nullptr; OptionsMap* Tune::options; - - namespace { std::map TuneResults; -void on_tune(const Option& o) { +std::optional on_tune(const Option& o) { if (!Tune::update_on_last || LastOption == &o) Tune::read_options(); -} + return std::nullopt; +} +} -void make_option(OptionsMap* options, const string& n, int v, const SetRange& r) { +void Tune::make_option(OptionsMap* opts, const string& n, int v, const SetRange& r) { // Do not generate option when there is nothing to tune (ie. min = max) if (r(v).first == r(v).second) @@ -54,8 +55,8 @@ void make_option(OptionsMap* options, const string& n, int v, const SetRange& r) if (TuneResults.count(n)) v = TuneResults[n]; - (*options)[n] << Option(v, r(v).first, r(v).second, on_tune); - LastOption = &((*options)[n]); + (*opts)[n] << Option(v, r(v).first, r(v).second, on_tune); + LastOption = &((*opts)[n]); // Print formatted parameters, ready to be copy-pasted in Fishtest std::cout << n << "," // @@ -65,7 +66,6 @@ void make_option(OptionsMap* options, const string& n, int v, const SetRange& r) << (r(v).second - r(v).first) / 20.0 << "," // << "0.0020" << std::endl; } -} string Tune::next(string& names, bool pop) { diff --git a/src/tune.h b/src/tune.h index 079614db28a..ed4738cdc47 100644 --- a/src/tune.h +++ b/src/tune.h @@ -145,6 +145,8 @@ class Tune { return add(value, (next(names), std::move(names)), args...); } + static void make_option(OptionsMap* options, const std::string& n, int v, const SetRange& r); + std::vector> list; public: diff --git a/src/uci.cpp b/src/uci.cpp index 42c69cdeff2..75b7dfc77a4 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -30,20 +30,16 @@ #include "benchmark.h" #include "engine.h" -#include "evaluate.h" #include "movegen.h" #include "position.h" #include "score.h" #include "search.h" -#include "syzygy/tbprobe.h" #include "types.h" #include "ucioption.h" namespace Stockfish { -constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; -constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048; - +constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; template struct overload: Ts... { using Ts::operator()...; @@ -56,55 +52,25 @@ UCIEngine::UCIEngine(int argc, char** argv) : engine(argv[0]), cli(argc, argv) { - auto& options = engine.get_options(); - - options["Debug Log File"] << Option("", [](const Option& o) { start_logger(o); }); + engine.get_options().add_info_listener([](const std::optional& str) { + if (!str || (*str).empty()) + return; - options["NumaPolicy"] << Option("auto", [this](const Option& o) { - engine.set_numa_config_from_option(o); - print_numa_config_information(); - print_thread_binding_information(); - }); + // split all lines + auto ss = std::istringstream{*str}; - options["Threads"] << Option(1, 1, 1024, [this](const Option&) { - engine.resize_threads(); - print_thread_binding_information(); + for (std::string line; std::getline(ss, line, '\n');) + sync_cout << "info string " << line << sync_endl; }); - options["Hash"] << Option(16, 1, MaxHashMB, [this](const Option& o) { engine.set_tt_size(o); }); - - options["Clear Hash"] << Option([this](const Option&) { engine.search_clear(); }); - options["Ponder"] << Option(false); - options["MultiPV"] << Option(1, 1, MAX_MOVES); - options["Skill Level"] << Option(20, 0, 20); - options["Move Overhead"] << Option(10, 0, 5000); - options["nodestime"] << Option(0, 0, 10000); - options["UCI_Chess960"] << Option(false); - options["UCI_LimitStrength"] << Option(false); - options["UCI_Elo"] << Option(1320, 1320, 3190); - options["UCI_ShowWDL"] << Option(false); - options["SyzygyPath"] << Option("", [](const Option& o) { Tablebases::init(o); }); - options["SyzygyProbeDepth"] << Option(1, 1, 100); - options["Syzygy50MoveRule"] << Option(true); - options["SyzygyProbeLimit"] << Option(7, 0, 7); - options["EvalFile"] << Option(EvalFileDefaultNameBig, - [this](const Option& o) { engine.load_big_network(o); }); - options["EvalFileSmall"] << Option(EvalFileDefaultNameSmall, - [this](const Option& o) { engine.load_small_network(o); }); - - engine.set_on_iter([](const auto& i) { on_iter(i); }); engine.set_on_update_no_moves([](const auto& i) { on_update_no_moves(i); }); - engine.set_on_update_full([&](const auto& i) { on_update_full(i, options["UCI_ShowWDL"]); }); + engine.set_on_update_full( + [this](const auto& i) { on_update_full(i, engine.get_options()["UCI_ShowWDL"]); }); engine.set_on_bestmove([](const auto& bm, const auto& p) { on_bestmove(bm, p); }); - - engine.load_networks(); - engine.resize_threads(); - engine.search_clear(); // After threads are up } void UCIEngine::loop() { - std::string token, cmd; for (int i = 1; i < cli.argc; ++i) @@ -136,8 +102,9 @@ void UCIEngine::loop() { sync_cout << "id name " << engine_info(true) << "\n" << engine.get_options() << sync_endl; - print_numa_config_information(); - print_thread_binding_information(); + sync_cout << "info string " << engine.numa_config_information_as_string() << sync_endl; + sync_cout << "info string " << engine.thread_binding_information_as_string() + << sync_endl; sync_cout << "uciok" << sync_endl; } @@ -193,28 +160,6 @@ void UCIEngine::loop() { } while (token != "quit" && cli.argc == 1); // The command-line arguments are one-shot } -void UCIEngine::print_numa_config_information() const { - auto cfgStr = engine.get_numa_config_as_string(); - sync_cout << "info string Available Processors: " << cfgStr << sync_endl; -} - -void UCIEngine::print_thread_binding_information() const { - auto boundThreadsByNode = engine.get_bound_thread_count_by_numa_node(); - if (!boundThreadsByNode.empty()) - { - sync_cout << "info string NUMA Node Thread Binding: "; - bool isFirst = true; - for (auto&& [current, total] : boundThreadsByNode) - { - if (!isFirst) - std::cout << ":"; - std::cout << current << "/" << total; - isFirst = false; - } - std::cout << sync_endl; - } -} - Search::LimitsType UCIEngine::parse_limits(std::istream& is) { Search::LimitsType limits; std::string token; diff --git a/src/uci.h b/src/uci.h index bac62bb90c0..122bcc40cf4 100644 --- a/src/uci.h +++ b/src/uci.h @@ -19,10 +19,10 @@ #ifndef UCI_H_INCLUDED #define UCI_H_INCLUDED +#include #include #include #include -#include #include "engine.h" #include "misc.h" @@ -42,9 +42,6 @@ class UCIEngine { void loop(); - void print_numa_config_information() const; - void print_thread_binding_information() const; - static int to_cp(Value v, const Position& pos); static std::string format_score(const Score& s); static std::string square(Square s); diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 4819a68db73..1cd028c9932 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -36,6 +36,8 @@ bool CaseInsensitiveLess::operator()(const std::string& s1, const std::string& s [](char c1, char c2) { return std::tolower(c1) < std::tolower(c2); }); } +void OptionsMap::add_info_listener(InfoListener&& message_func) { info = std::move(message_func); } + void OptionsMap::setoption(std::istringstream& is) { std::string token, name, value; @@ -57,13 +59,20 @@ void OptionsMap::setoption(std::istringstream& is) { Option OptionsMap::operator[](const std::string& name) const { auto it = options_map.find(name); - return it != options_map.end() ? it->second : Option(); + return it != options_map.end() ? it->second : Option(this); } -Option& OptionsMap::operator[](const std::string& name) { return options_map[name]; } +Option& OptionsMap::operator[](const std::string& name) { + if (!options_map.count(name)) + options_map[name] = Option(this); + return options_map[name]; +} std::size_t OptionsMap::count(const std::string& name) const { return options_map.count(name); } +Option::Option(const OptionsMap* map) : + parent(map) {} + Option::Option(const char* v, OnChange f) : type("string"), min(0), @@ -127,10 +136,12 @@ void Option::operator<<(const Option& o) { static size_t insert_order = 0; - *this = o; - idx = insert_order++; -} + auto p = this->parent; + *this = o; + this->parent = p; + idx = insert_order++; +} // Updates currentValue and triggers on_change() action. It's up to // the GUI to check for option's limits, but we could receive the new value @@ -159,7 +170,12 @@ Option& Option::operator=(const std::string& v) { currentValue = v; if (on_change) - on_change(*this); + { + const auto ret = on_change(*this); + + if (ret && parent != nullptr && parent->info != nullptr) + parent->info(ret); + } return *this; } diff --git a/src/ucioption.h b/src/ucioption.h index 16d46696145..a47cc98de53 100644 --- a/src/ucioption.h +++ b/src/ucioption.h @@ -23,6 +23,7 @@ #include #include #include +#include #include namespace Stockfish { @@ -31,31 +32,14 @@ struct CaseInsensitiveLess { bool operator()(const std::string&, const std::string&) const; }; -class Option; - -class OptionsMap { - public: - void setoption(std::istringstream&); - - friend std::ostream& operator<<(std::ostream&, const OptionsMap&); - - Option operator[](const std::string&) const; - Option& operator[](const std::string&); - - std::size_t count(const std::string&) const; - - private: - // The options container is defined as a std::map - using OptionsStore = std::map; - - OptionsStore options_map; -}; +class OptionsMap; // The Option class implements each option as specified by the UCI protocol class Option { public: - using OnChange = std::function; + using OnChange = std::function(const Option&)>; + Option(const OptionsMap*); Option(OnChange = nullptr); Option(bool v, OnChange = nullptr); Option(const char* v, OnChange = nullptr); @@ -63,7 +47,6 @@ class Option { Option(const char* v, const char* cur, OnChange = nullptr); Option& operator=(const std::string&); - void operator<<(const Option&); operator int() const; operator std::string() const; bool operator==(const char*) const; @@ -72,10 +55,49 @@ class Option { friend std::ostream& operator<<(std::ostream&, const OptionsMap&); private: - std::string defaultValue, currentValue, type; - int min, max; - size_t idx; - OnChange on_change; + friend class OptionsMap; + friend class Engine; + friend class Tune; + + void operator<<(const Option&); + + std::string defaultValue, currentValue, type; + int min, max; + size_t idx; + OnChange on_change; + const OptionsMap* parent = nullptr; +}; + +class OptionsMap { + public: + using InfoListener = std::function)>; + + OptionsMap() = default; + OptionsMap(const OptionsMap&) = delete; + OptionsMap(OptionsMap&&) = delete; + OptionsMap& operator=(const OptionsMap&) = delete; + OptionsMap& operator=(OptionsMap&&) = delete; + + void add_info_listener(InfoListener&&); + + void setoption(std::istringstream&); + + Option operator[](const std::string&) const; + Option& operator[](const std::string&); + + std::size_t count(const std::string&) const; + + private: + friend class Engine; + friend class Option; + + friend std::ostream& operator<<(std::ostream&, const OptionsMap&); + + // The options container is defined as a std::map + using OptionsStore = std::map; + + OptionsStore options_map; + InfoListener info; }; } From 025da6a0d1f96c1743c0ea6b182487bc2f78082c Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Sat, 8 Jun 2024 14:57:09 -0700 Subject: [PATCH 1595/1766] Give positional output more weight in nnue eval This effectively reverts the removal of delta in: https://github.com/official-stockfish/Stockfish/pull/5373 Passed STC: https://tests.stockfishchess.org/tests/view/6664d41922234461cef58e6b LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 56448 W: 14849 L: 14500 D: 27099 Ptnml(0-2): 227, 6481, 14457, 6834, 225 Passed LTC: https://tests.stockfishchess.org/tests/view/666587a1996b40829f4ee007 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 91686 W: 23402 L: 22963 D: 45321 Ptnml(0-2): 78, 10205, 24840, 10640, 80 closes https://github.com/official-stockfish/Stockfish/pull/5382 bench 1160467 --- src/evaluate.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 1317a01ecbd..4e895fd366f 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -66,14 +66,14 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, auto [psqt, positional] = smallNet ? networks.small.evaluate(pos, &caches.small) : networks.big.evaluate(pos, &caches.big); - Value nnue = psqt + positional; + Value nnue = (125 * psqt + 131 * positional) / 128; int nnueComplexity = std::abs(psqt - positional); // Re-evaluate the position when higher eval accuracy is worth the time spent if (smallNet && (nnue * simpleEval < 0 || std::abs(nnue) < 227)) { std::tie(psqt, positional) = networks.big.evaluate(pos, &caches.big); - nnue = psqt + positional; + nnue = (125 * psqt + 131 * positional) / 128; nnueComplexity = std::abs(psqt - positional); smallNet = false; } From 3d92950859e1d45dad60d276dd7a78fbeb097bcb Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Tue, 11 Jun 2024 21:28:11 +0200 Subject: [PATCH 1596/1766] Limit depth after extensions to avoid asserts. currently extensions can cause depth to exceed MAX_PLY. This triggers the assert near line 542 in search when running a binary compiled with `debug=yes` on a testcase like: ``` position fen 7K/P1p1p1p1/2P1P1Pk/6pP/3p2P1/1P6/3P4/8 w - - 0 1 go nodes 1000000 ``` passed STC https://tests.stockfishchess.org/tests/view/6668a56a602682471b064c8d LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 143936 W: 37338 L: 37238 D: 69360 Ptnml(0-2): 514, 16335, 38149, 16477, 493 closes https://github.com/official-stockfish/Stockfish/pull/5383 Bench: 1160467 --- src/search.cpp | 3 +++ tests/instrumented.sh | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 9c3f915db34..91b3c7894fb 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -528,6 +528,9 @@ Value Search::Worker::search( if (depth <= 0) return qsearch < PvNode ? PV : NonPV > (pos, ss, alpha, beta); + // Limit the depth if extensions made it too large + depth = std::min(depth, MAX_PLY - 1); + // Check if we have an upcoming move that draws by repetition, or // if the opponent had an alternative move earlier to this position. if (!rootNode && alpha < VALUE_DRAW && pos.has_game_cycle(ss->ply)) diff --git a/tests/instrumented.sh b/tests/instrumented.sh index e77ee0dd2be..cb5a3a9f245 100755 --- a/tests/instrumented.sh +++ b/tests/instrumented.sh @@ -170,6 +170,11 @@ cat << EOF > game.exp expect "score mate -1" expect "bestmove" + send "ucinewgame\n" + send "position fen 7K/P1p1p1p1/2P1P1Pk/6pP/3p2P1/1P6/3P4/8 w - - 0 1\n" + send "go nodes 500000\n" + expect "bestmove" + send "ucinewgame\n" send "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - -\n" send "go depth 18\n" From 7c0607d2d36afd7b34e686e85711aca3d77c7ecf Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Wed, 12 Jun 2024 16:54:15 +0200 Subject: [PATCH 1597/1766] Fix printing of empty info strings. Handle printing of `info string` in a single place. Fixes #5386 closes https://github.com/official-stockfish/Stockfish/pull/5387 No functional change --- src/misc.cpp | 6 ++++++ src/misc.h | 4 ++++ src/uci.cpp | 27 ++++++++++++++++----------- src/uci.h | 2 ++ 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index a8bb46ec3c1..e97d58b939f 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -371,6 +371,8 @@ std::ostream& operator<<(std::ostream& os, SyncCout sc) { return os; } +void sync_cout_start() { std::cout << IO_LOCK; } +void sync_cout_end() { std::cout << IO_UNLOCK; } // Trampoline helper to avoid moving Logger to misc.h void start_logger(const std::string& fname) { Logger::start(fname); } @@ -419,6 +421,10 @@ void remove_whitespace(std::string& s) { s.erase(std::remove_if(s.begin(), s.end(), [](char c) { return std::isspace(c); }), s.end()); } +bool is_whitespace(const std::string& s) { + return std::all_of(s.begin(), s.end(), [](char c) { return std::isspace(c); }); +} + std::string CommandLine::get_binary_directory(std::string argv0) { std::string pathSeparator; diff --git a/src/misc.h b/src/misc.h index 557a4d8c5a4..bdc7c864d3f 100644 --- a/src/misc.h +++ b/src/misc.h @@ -101,6 +101,7 @@ inline std::vector split(const std::string& s, const std::string& d } void remove_whitespace(std::string& s); +bool is_whitespace(const std::string& s); enum SyncCout { IO_LOCK, @@ -111,6 +112,9 @@ std::ostream& operator<<(std::ostream&, SyncCout); #define sync_cout std::cout << IO_LOCK #define sync_endl std::endl << IO_UNLOCK +void sync_cout_start(); +void sync_cout_end(); + // True if and only if the binary is compiled on a little-endian machine static inline const union { uint32_t i; diff --git a/src/uci.cpp b/src/uci.cpp index 75b7dfc77a4..4bc358d8acb 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -48,19 +48,25 @@ struct overload: Ts... { template overload(Ts...) -> overload; +void UCIEngine::print_info_string(const std::string& str) { + sync_cout_start(); + for (auto& line : split(str, "\n")) + { + if (!is_whitespace(line)) + { + std::cout << "info string " << line << '\n'; + } + } + sync_cout_end(); +} + UCIEngine::UCIEngine(int argc, char** argv) : engine(argv[0]), cli(argc, argv) { engine.get_options().add_info_listener([](const std::optional& str) { - if (!str || (*str).empty()) - return; - - // split all lines - auto ss = std::istringstream{*str}; - - for (std::string line; std::getline(ss, line, '\n');) - sync_cout << "info string " << line << sync_endl; + if (str.has_value()) + print_info_string(*str); }); engine.set_on_iter([](const auto& i) { on_iter(i); }); @@ -102,9 +108,8 @@ void UCIEngine::loop() { sync_cout << "id name " << engine_info(true) << "\n" << engine.get_options() << sync_endl; - sync_cout << "info string " << engine.numa_config_information_as_string() << sync_endl; - sync_cout << "info string " << engine.thread_binding_information_as_string() - << sync_endl; + print_info_string(engine.numa_config_information_as_string()); + print_info_string(engine.thread_binding_information_as_string()); sync_cout << "uciok" << sync_endl; } diff --git a/src/uci.h b/src/uci.h index 122bcc40cf4..23745f96a96 100644 --- a/src/uci.h +++ b/src/uci.h @@ -58,6 +58,8 @@ class UCIEngine { Engine engine; CommandLine cli; + static void print_info_string(const std::string& str); + void go(std::istringstream& is); void bench(std::istream& args); void position(std::istringstream& is); From 44cddbd962c738678f407a7414efa5b93f0710d9 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Thu, 13 Jun 2024 18:43:19 +0200 Subject: [PATCH 1598/1766] Add matetrack to CI verifies that all mate PVs printed for finished iterations (i.e. no lower or upper bounds), are complete, i.e. of the expected length and ending in mate, and do not contain drawing or illegal moves. based on a set of 2000 positions and the code in https://github.com/vondele/matetrack closes https://github.com/official-stockfish/Stockfish/pull/5390 No functional change --- .github/workflows/matetrack.yml | 36 +++++++++++++++++++++++++++++++++ .github/workflows/stockfish.yml | 2 ++ 2 files changed, 38 insertions(+) create mode 100644 .github/workflows/matetrack.yml diff --git a/.github/workflows/matetrack.yml b/.github/workflows/matetrack.yml new file mode 100644 index 00000000000..dd81f334d05 --- /dev/null +++ b/.github/workflows/matetrack.yml @@ -0,0 +1,36 @@ +# This workflow will run matetrack on the PR + +name: Matetrack +on: + workflow_call: +jobs: + Matetrack: + name: Matetrack + runs-on: ubuntu-22.04 + steps: + - name: Checkout SF repo + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + path: Stockfish + + - name: build SF + working-directory: Stockfish/src + run: make -j profile-build + + - name: Checkout matetrack repo + uses: actions/checkout@v4 + with: + repository: vondele/matetrack + path: matetrack + ref: 20287a1a145f30a166b7ef251eddb611e4e44fbf + + - name: matetrack install deps + working-directory: matetrack + run: pip install -r requirements.txt + + - name: Run matetrack + working-directory: matetrack + run: | + python matecheck.py --engine /home/runner/work/Stockfish/Stockfish/Stockfish/src/stockfish --epdFile mates2000.epd --nodes 100000 | tee matecheckout.out + ! grep "issues were detected" matecheckout.out > /dev/null diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index 13d57f9ed9b..fcaa3f6b800 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -90,6 +90,8 @@ jobs: uses: ./.github/workflows/sanitizers.yml Tests: uses: ./.github/workflows/tests.yml + Matetrack: + uses: ./.github/workflows/matetrack.yml Binaries: if: github.repository == 'official-stockfish/Stockfish' needs: [Matrix, Prerelease, Compilation] From b01fdb596a196f966549f7132c042ab67962fbbd Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 12 Jun 2024 13:23:26 +0200 Subject: [PATCH 1599/1766] Fix upperbound/lowerbound output in multithreaded case In case a stop is received during multithreaded searches, the PV of the best thread might be printed without the correct upperbound/lowerbound indicators. This was due to the pvIdx variable being incremented after receiving the stop. passed STC: https://tests.stockfishchess.org/tests/view/666985da602682471b064d08 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 196576 W: 51039 L: 50996 D: 94541 Ptnml(0-2): 760, 22545, 51603, 22652, 728 closes https://github.com/official-stockfish/Stockfish/pull/5391 Bench: 1160467 --- src/search.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 91b3c7894fb..af0ab400fa1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -299,7 +299,7 @@ void Search::Worker::iterative_deepening() { searchAgainCounter++; // MultiPV loop. We perform a full root search for each PV line - for (pvIdx = 0; pvIdx < multiPV && !threads.stop; ++pvIdx) + for (pvIdx = 0; pvIdx < multiPV; ++pvIdx) { if (pvIdx == pvLast) { @@ -390,6 +390,9 @@ void Search::Worker::iterative_deepening() { // below pick a proven score/PV for this thread (from the previous iteration). && !(threads.abortedSearch && rootMoves[0].uciScore <= VALUE_TB_LOSS_IN_MAX_PLY)) main_manager()->pv(*this, threads, tt, rootDepth); + + if (threads.stop) + break; } if (!threads.stop) From ff10f4ac6516d691b5a48788bc7b21d0ecd83b03 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Wed, 12 Jun 2024 03:14:55 -0500 Subject: [PATCH 1600/1766] Fix readability of TTEntry occupancy check Passed STC: https://tests.stockfishchess.org/tests/view/66695b6a602682471b064cfc LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 107520 W: 28138 L: 27998 D: 51384 Ptnml(0-2): 373, 12257, 28358, 12401, 371 closes https://github.com/official-stockfish/Stockfish/pull/5394 No functional change --- src/tt.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/tt.cpp b/src/tt.cpp index 763e2c9b349..30104ab7d4d 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -55,6 +55,7 @@ struct TTEntry { Bound(genBound8 & 0x3), bool(genBound8 & 0x4)}; } + bool is_occupied() const; void save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev, uint8_t generation8); // The returned age is a multiple of TranspositionTable::GENERATION_DELTA uint8_t relative_age(const uint8_t generation8) const; @@ -84,7 +85,8 @@ static constexpr int GENERATION_MASK = (0xFF << GENERATION_BITS) & 0xFF; // DEPTH_ENTRY_OFFSET exists because 1) we use `bool(depth8)` as the occupancy check, but // 2) we need to store negative depths for QS. (`depth8` is the only field with "spare bits": -// we sacrifice the ability to store depths greater than 1<<8 less the offset, as asserted below.) +// we sacrifice the ability to store depths greater than 1<<8 less the offset, as asserted in `save`.) +bool TTEntry::is_occupied() const { return bool(depth8); } // Populates the TTEntry with a new node's data, possibly // overwriting an old position. The update is not atomic and can be racy. @@ -196,7 +198,7 @@ int TranspositionTable::hashfull() const { int cnt = 0; for (int i = 0; i < 1000; ++i) for (int j = 0; j < ClusterSize; ++j) - cnt += table[i].entry[j].depth8 + cnt += table[i].entry[j].is_occupied() && (table[i].entry[j].genBound8 & GENERATION_MASK) == generation8; return cnt / ClusterSize; @@ -227,7 +229,7 @@ std::tuple TranspositionTable::probe(const Key key) cons if (tte[i].key16 == key16) // This gap is the main place for read races. // After `read()` completes that copy is final, but may be self-inconsistent. - return {bool(tte[i].depth8), tte[i].read(), TTWriter(&tte[i])}; + return {tte[i].is_occupied(), tte[i].read(), TTWriter(&tte[i])}; // Find an entry to be replaced according to the replacement strategy TTEntry* replace = tte; From 2046c92ad461f5e852ba62a144b53c3d3fea04b0 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Wed, 12 Jun 2024 14:04:43 +0300 Subject: [PATCH 1601/1766] Tweak the reduction formula Tweak the reduction formula if position is or has been on the PV Taking inspiration from an old Viren test. Passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 78528 W: 20607 L: 20225 D: 37696 Ptnml(0-2): 262, 9297, 19785, 9637, 283 https://tests.stockfishchess.org/tests/view/666339c70ff7cb4868d1fe24 Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 138630 W: 35666 L: 35132 D: 67832 Ptnml(0-2): 118, 15345, 37835, 15919, 98 https://tests.stockfishchess.org/tests/view/66645dec0612cd151f9e77b0 closes https://github.com/official-stockfish/Stockfish/pull/5385 Bench: 1134281 --- src/search.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index af0ab400fa1..8fb65fe766c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1134,7 +1134,8 @@ Value Search::Worker::search( // Decrease reduction if position is or has been on the PV (~7 Elo) if (ss->ttPv) - r -= 1 + (ttData.value > alpha) + (ttData.depth >= depth); + r -= 1 + (ttData.value > alpha) + (ttData.depth >= depth) + - (PvNode && ttData.value < alpha && ttData.depth >= depth); // Decrease reduction for PvNodes (~0 Elo on STC, ~2 Elo on LTC) if (PvNode) From 2678606e8dbeac8332909f0b3e43638936570835 Mon Sep 17 00:00:00 2001 From: xoto10 <23479932+xoto10@users.noreply.github.com> Date: Fri, 14 Jun 2024 17:27:09 +0100 Subject: [PATCH 1602/1766] Consider wider range of moves near leaves. try to avoid missing good moves for opponent or engine, by updating bestMove also when value == bestValue (i.e. value == alpha) under certain conditions. In particular require this is at higher depth in the tree, leaving the logic near the root unchanged, and only apply randomly. Avoid doing this near mate scores, leaving mate PVs intact. Passed SMP STC 6+0.06 th7 : LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 42040 W: 10930 L: 10624 D: 20486 Ptnml(0-2): 28, 4682, 11289, 4998, 23 https://tests.stockfishchess.org/tests/view/66608b00c340c8eed7757d1d Passed SMP LTC 24+0.24 th7 : LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 73692 W: 18978 L: 18600 D: 36114 Ptnml(0-2): 9, 7421, 21614, 7787, 15 https://tests.stockfishchess.org/tests/view/666095e8c340c8eed7757d49 closes https://github.com/official-stockfish/Stockfish/pull/5367 Bench 1205168 --- src/search.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 8fb65fe766c..75eea2fd932 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1283,11 +1283,17 @@ Value Search::Worker::search( rm.score = -VALUE_INFINITE; } - if (value > bestValue) + // In case we have an alternative move equal in eval to the current bestmove, + // promote it to bestmove by pretending it just exceeds alpha (but not beta). + int inc = (value == bestValue && (int(nodes) & 15) == 0 + && ss->ply + 2 + ss->ply / 32 >= thisThread->rootDepth + && std::abs(value) + 1 < VALUE_TB_WIN_IN_MAX_PLY); + + if (value + inc > bestValue) { bestValue = value; - if (value > alpha) + if (value + inc > alpha) { bestMove = move; From 5514690f8e19631054271a6ca7e1cbfaf1b443f2 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 22 Jun 2024 09:17:45 +0200 Subject: [PATCH 1603/1766] CI/CD: play games this action plays games under fast-chess with a `debug=yes` compiled binary. It checks for triggered asserts in the code, or generally for engine disconnects. closes https://github.com/official-stockfish/Stockfish/pull/5403 No functional change --- .github/workflows/games.yml | 41 +++++++++++++++++++++++++++++++++ .github/workflows/stockfish.yml | 2 ++ 2 files changed, 43 insertions(+) create mode 100644 .github/workflows/games.yml diff --git a/.github/workflows/games.yml b/.github/workflows/games.yml new file mode 100644 index 00000000000..088695e57fc --- /dev/null +++ b/.github/workflows/games.yml @@ -0,0 +1,41 @@ +# This workflow will play games with a debug enabled SF using the PR + +name: Games +on: + workflow_call: +jobs: + Matetrack: + name: Games + runs-on: ubuntu-22.04 + steps: + - name: Checkout SF repo + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + path: Stockfish + + - name: build debug enabled version of SF + working-directory: Stockfish/src + run: make -j build debug=yes + + - name: Checkout fast-chess repo + uses: actions/checkout@v4 + with: + repository: Disservin/fast-chess + path: fast-chess + ref: d54af1910d5479c669dc731f1f54f9108a251951 + + - name: fast-chess build + working-directory: fast-chess + run: make -j + + - name: Run games + working-directory: fast-chess + run: | + ./fast-chess -rounds 4 -games 2 -repeat -concurrency 4 -openings file=app/tests/data/openings.epd format=epd order=random -srand $RANDOM\ + -engine name=sf1 cmd=/home/runner/work/Stockfish/Stockfish/Stockfish/src/stockfish\ + -engine name=sf2 cmd=/home/runner/work/Stockfish/Stockfish/Stockfish/src/stockfish\ + -ratinginterval 1 -report penta=true -each proto=uci tc=4+0.04 -log file=fast.log | tee fast.out + cat fast.log + ! grep "Assertion" fast.log > /dev/null + ! grep "disconnect" fast.out > /dev/null diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index fcaa3f6b800..8a1094fbdbd 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -92,6 +92,8 @@ jobs: uses: ./.github/workflows/tests.yml Matetrack: uses: ./.github/workflows/matetrack.yml + Games: + uses: ./.github/workflows/games.yml Binaries: if: github.repository == 'official-stockfish/Stockfish' needs: [Matrix, Prerelease, Compilation] From 8806a58ebf5ade73696fd1f89ac4ea12cd1eedd3 Mon Sep 17 00:00:00 2001 From: evqsx <149484438+evqsx@users.noreply.github.com> Date: Sun, 16 Jun 2024 12:34:24 +0800 Subject: [PATCH 1604/1766] Simplify static exchange evaluation pruning formula Passed STC: https://tests.stockfishchess.org/tests/view/666bda31602682471b064e1f LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 141696 W: 36932 L: 36826 D: 67938 Ptnml(0-2): 510, 16880, 35989, 16932, 537 Passed LTC: https://tests.stockfishchess.org/tests/view/666e6b67602682471b064f4b LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 159504 W: 40552 L: 40471 D: 78481 Ptnml(0-2): 130, 18160, 43103, 18217, 142 closes https://github.com/official-stockfish/Stockfish/pull/5400 bench: 1084115 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 75eea2fd932..9b296e7fee5 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1586,7 +1586,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, // If static exchange evaluation is much worse than what is needed to not // fall below alpha we can prune this move. - if (futilityBase > alpha && !pos.see_ge(move, (alpha - futilityBase) * 2 - 30)) + if (futilityBase > alpha && !pos.see_ge(move, (alpha - futilityBase) * 4)) { bestValue = alpha; continue; From d5c130569b364899fc151101d069291a8934789a Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sun, 16 Jun 2024 16:14:22 -0700 Subject: [PATCH 1605/1766] Simplify Bonus Formula In History Adjustment Inspired by a discord message [1] from Vizvezdenec, this patch simplifies the bonus adjustment bonus = bonus > 0 ? 2 * bonus : bonus / 2 to a constant addition, maintaining bonus average at around 0 in regular bench. As cj5716 pointed in discord [2], the constant bonus can also be considered as factoring tempo when calculating bonus, yielding a better value of the move. [1] https://discord.com/channels/435943710472011776/882956631514689597/1243877089443188776 [2] https://discord.com/channels/435943710472011776/813919248455827515/1252277437249622077 Passed Non-regression STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 29984 W: 7908 L: 7677 D: 14399 Ptnml(0-2): 95, 3502, 7594, 3679, 122 https://tests.stockfishchess.org/tests/view/666f7210602682471b064fa2 Passed Non-regression LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 170136 W: 43214 L: 43145 D: 83777 Ptnml(0-2): 158, 19185, 46311, 19258, 156 https://tests.stockfishchess.org/tests/view/666fb32e602682471b064fb5 closes https://github.com/official-stockfish/Stockfish/pull/5401 bench 1438375 --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 9b296e7fee5..562bdbf9c0d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -750,8 +750,7 @@ Value Search::Worker::search( // Use static evaluation difference to improve quiet move ordering (~9 Elo) if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture) { - int bonus = std::clamp(-10 * int((ss - 1)->staticEval + ss->staticEval), -1590, 1371); - bonus = bonus > 0 ? 2 * bonus : bonus / 2; + int bonus = std::clamp(-10 * int((ss - 1)->staticEval + ss->staticEval), -1590, 1371) + 800; thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) thisThread->pawnHistory[pawn_structure_index(pos)][pos.piece_on(prevSq)][prevSq] From cc992e5e4a7110b21f85168bdedad7978edad140 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Mon, 17 Jun 2024 00:03:15 +0300 Subject: [PATCH 1606/1766] Internal iterative reductions: decrease depth more For PV nodes without a ttMove, we decrease depth. But in this patch, additionally, if the current position is found in the TT, and the stored depth in the TT is greater than or equal to the current search depth, we decrease the search depth even further. Passed STC: LLR: 2.96 (-2.94,2.94) <0.00,2.00> Total: 84384 W: 22154 L: 21761 D: 40469 Ptnml(0-2): 292, 9972, 21315, 10277, 336 https://tests.stockfishchess.org/tests/view/666b0a4d602682471b064db6 Passed LTC: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 92106 W: 23471 L: 23032 D: 45603 Ptnml(0-2): 79, 10155, 25154, 10578, 87 https://tests.stockfishchess.org/tests/view/666c423d602682471b064e56 closes https://github.com/official-stockfish/Stockfish/pull/5397 bench: 1038234 --- src/search.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 562bdbf9c0d..e63595c1b73 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -829,9 +829,12 @@ Value Search::Worker::search( } // Step 10. Internal iterative reductions (~9 Elo) - // For PV nodes without a ttMove, we decrease depth by 3. + // For PV nodes without a ttMove, we decrease depth. + // Additionally, if the current position is found in the TT + // and the stored depth in the TT is greater than or equal to + // current search depth, we decrease search depth even further. if (PvNode && !ttData.move) - depth -= 3; + depth -= 3 + (ss->ttHit && ttData.depth >= depth); // Use qsearch if depth <= 0. if (depth <= 0) From 5fbfd06171cadf97e6e8173216046b099ebfa43b Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 23 Jun 2024 21:53:25 +0200 Subject: [PATCH 1607/1766] Move info output afer uciok fixes #5393 : an incompatibility with an older GUI (Chesspartner) fixes #5396 : an incompatibility with an older GUI (Fritz9) closes https://github.com/official-stockfish/Stockfish/pull/5404 No functional change --- src/uci.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/uci.cpp b/src/uci.cpp index 4bc358d8acb..3c9177ee3b3 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -108,10 +108,11 @@ void UCIEngine::loop() { sync_cout << "id name " << engine_info(true) << "\n" << engine.get_options() << sync_endl; + sync_cout << "uciok" << sync_endl; + + // keep info strings after uciok for old GUIs print_info_string(engine.numa_config_information_as_string()); print_info_string(engine.thread_binding_information_as_string()); - - sync_cout << "uciok" << sync_endl; } else if (token == "setoption") From b2a12917e2125fcd1e1c344165e840b0756201a8 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Mon, 24 Jun 2024 17:12:07 +0300 Subject: [PATCH 1608/1766] Remove redundant inline constexpr implies inline anyway closes https://github.com/official-stockfish/Stockfish/pull/5406 No functional change --- src/misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index e97d58b939f..26dd3a2898c 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -281,7 +281,7 @@ template struct DebugInfo { std::atomic data[N] = {0}; - constexpr inline std::atomic& operator[](int index) { return data[index]; } + constexpr std::atomic& operator[](int index) { return data[index]; } }; DebugInfo<2> hit[MaxDebugSlots]; From 66e6274d32e9a59b6d0d8c347a0f1ee8175ffcdc Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 1 Jul 2024 19:44:00 +0200 Subject: [PATCH 1609/1766] Fix typos in comments closes https://github.com/official-stockfish/Stockfish/pull/5409 No functional change --- src/thread.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/thread.h b/src/thread.h index 7416271b4c1..81ca39bbcb6 100644 --- a/src/thread.h +++ b/src/thread.h @@ -39,10 +39,10 @@ namespace Stockfish { class OptionsMap; using Value = int; -// Sometimes we don't want to actually bind the threads, but the recipent still +// Sometimes we don't want to actually bind the threads, but the recipient still // needs to think it runs on *some* NUMA node, such that it can access structures // that rely on NUMA node knowledge. This class encapsulates this optional process -// such that the recipent does not need to know whether the binding happened or not. +// such that the recipient does not need to know whether the binding happened or not. class OptionalThreadToNumaNodeBinder { public: OptionalThreadToNumaNodeBinder(NumaIndex n) : @@ -87,7 +87,7 @@ class Thread { // this name is no longer correct. However, this class (and ThreadPool) // require further work to make them properly generic while maintaining // appropriate specificity regarding search, from the point of view of an - // outside user, so renaming of this function in left for whenever that happens. + // outside user, so renaming of this function is left for whenever that happens. void wait_for_search_finished(); size_t id() const { return idx; } From 22a502ac7486576f52d7ba6cf884702162e92400 Mon Sep 17 00:00:00 2001 From: Taras Vuk <117687515+TarasVuk@users.noreply.github.com> Date: Tue, 25 Jun 2024 10:48:50 +0200 Subject: [PATCH 1610/1766] Skip futility pruning if beta is below TB loss value Passed STC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 77024 W: 20122 L: 19946 D: 36956 Ptnml(0-2): 278, 8754, 20277, 8920, 283 https://tests.stockfishchess.org/tests/view/66752d59602682471b0652f3 Passed LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 93114 W: 23623 L: 23477 D: 46014 Ptnml(0-2): 77, 9839, 26566, 10011, 64 https://tests.stockfishchess.org/tests/view/6676b3e1602682471b065395 closes https://github.com/official-stockfish/Stockfish/pull/5413 bench: 1003441 --- src/search.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index e63595c1b73..d04ba194d7e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -784,8 +784,9 @@ Value Search::Worker::search( && eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening) - (ss - 1)->statScore / 263 >= beta - && eval >= beta && eval < VALUE_TB_WIN_IN_MAX_PLY && (!ttData.move || ttCapture)) - return beta > VALUE_TB_LOSS_IN_MAX_PLY ? beta + (eval - beta) / 3 : eval; + && eval >= beta && (!ttData.move || ttCapture) && beta > VALUE_TB_LOSS_IN_MAX_PLY + && eval < VALUE_TB_WIN_IN_MAX_PLY) + return beta + (eval - beta) / 3; // Step 9. Null move search with verification search (~35 Elo) if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 14369 From 90eca83e7f40ca719cd49e487893f32598ae6f19 Mon Sep 17 00:00:00 2001 From: mstembera Date: Sat, 29 Jun 2024 17:18:39 -0700 Subject: [PATCH 1611/1766] Simplify away a useless TTEntry::read() Not needed when we don hit an entry. closes https://github.com/official-stockfish/Stockfish/pull/5416 No functional change --- src/tt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tt.cpp b/src/tt.cpp index 30104ab7d4d..4b55e53fdfc 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -238,7 +238,7 @@ std::tuple TranspositionTable::probe(const Key key) cons > tte[i].depth8 - tte[i].relative_age(generation8) * 2) replace = &tte[i]; - return {false, replace->read(), TTWriter(replace)}; + return {false, TTData(), TTWriter(replace)}; } From 91ec31dac430e1d587f8239f2377ffb796008f8a Mon Sep 17 00:00:00 2001 From: Daniel Monroe <39802758+Ergodice@users.noreply.github.com> Date: Sat, 29 Jun 2024 21:23:41 -0400 Subject: [PATCH 1612/1766] Grade countermove bonus for low statscores Passed STC: LLR: 2.96 (-2.94,2.94) <0.00,2.00> Total: 338592 W: 88396 L: 87627 D: 162569 Ptnml(0-2): 1161, 40201, 85788, 41000, 1146 https://tests.stockfishchess.org/tests/view/6679d40c0c2db3fa2dcecbcc Passed LTC: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 83526 W: 21429 L: 21010 D: 41087 Ptnml(0-2): 54, 9173, 22913, 9546, 77 https://tests.stockfishchess.org/tests/view/667c5f2980450dba965911fc closes https://github.com/official-stockfish/Stockfish/pull/5418 bench: 1489815 --- src/search.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index d04ba194d7e..81bb9a06cf7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1355,10 +1355,16 @@ Value Search::Worker::search( // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (113 * (depth > 5) + 118 * (PvNode || cutNode) - + 191 * ((ss - 1)->statScore < -14396) + 119 * ((ss - 1)->moveCount > 8) + int bonus = (113 * (depth > 5) + 118 * (PvNode || cutNode) + 119 * ((ss - 1)->moveCount > 8) + 64 * (!ss->inCheck && bestValue <= ss->staticEval - 107) + 147 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 75)); + + + // proportional to "how much damage we have to undo" + if ((ss - 1)->statScore < -8000) + bonus += std::clamp(-(ss - 1)->statScore / 100, 0, 250); + + update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus / 100); thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] From 7b49f9dd7091ce1d075ebdd16fff85ff1dba31fa Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 30 Jun 2024 12:47:04 +0300 Subject: [PATCH 1613/1766] Tweak multicut This patch is an original patch by author of Altair (https://github.com/Alex2262/AltairChessEngine) chess engine. It allows to produce more aggressive multicut compared to master by changing condition it needs to fulfil and also returns bigger value. Also has applied matetrack fix on top. Passed STC: https://tests.stockfishchess.org/tests/view/667223ab602682471b0650e2 LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 50048 W: 13200 L: 12860 D: 23988 Ptnml(0-2): 181, 5822, 12679, 6160, 182 Passed LTC: https://tests.stockfishchess.org/tests/view/6672f777602682471b06515d LLR: 2.97 (-2.94,2.94) <0.50,2.50> Total: 706380 W: 179707 L: 177981 D: 348692 Ptnml(0-2): 656, 79250, 191665, 80950, 669 closes https://github.com/official-stockfish/Stockfish/pull/5421 bench 1148966 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 81bb9a06cf7..da01f82f54c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1087,8 +1087,8 @@ Value Search::Worker::search( // and if after excluding the ttMove with a reduced search we fail high over the original beta, // we assume this expected cut-node is not singular (multiple moves fail high), // and we can prune the whole subtree by returning a softbound. - else if (singularBeta >= beta) - return singularBeta; + else if (value >= beta && std::abs(value) < VALUE_TB_WIN_IN_MAX_PLY) + return value; // Negative extensions // If other moves failed high over (ttValue - margin) without the ttMove on a reduced search, From 38c5fc33e493f210dc199dab7c105e84e7601b99 Mon Sep 17 00:00:00 2001 From: "Shahin M. Shahin" Date: Sun, 30 Jun 2024 16:32:20 +0300 Subject: [PATCH 1614/1766] Increase reduction based on correct expectation If the current node is not a cutNode then it means that the child is one in LMR and the cutoff count is expected, so more reduction when the cutoffs are expected Passed STC: https://tests.stockfishchess.org/tests/view/66815e791c5b344a34ca7090 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 64416 W: 16876 L: 16519 D: 31021 Ptnml(0-2): 150, 7670, 16264, 7921, 203 Passed LTC: https://tests.stockfishchess.org/tests/view/668162f61c5b344a34ca725c LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 78186 W: 19905 L: 19499 D: 38782 Ptnml(0-2): 55, 8561, 21437, 9003, 37 closes https://github.com/official-stockfish/Stockfish/pull/5422 bench: 1161531 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index da01f82f54c..b68b30268bb 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1157,7 +1157,7 @@ Value Search::Worker::search( // Increase reduction if next ply has a lot of fail high (~5 Elo) if ((ss + 1)->cutoffCnt > 3) - r++; + r += 1 + !(PvNode || cutNode); // For first picked move (ttMove) reduce reduction // but never allow it to go below 0 (~3 Elo) From 5deb26239340a6a1a91d1c2050f90b7a36f9f5d1 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sun, 30 Jun 2024 22:24:28 +0300 Subject: [PATCH 1615/1766] Simplify rm.averageScore calculation Passed STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 485056 W: 125222 L: 125497 D: 234337 Ptnml(0-2): 1384, 58197, 123614, 57976, 1357 https://tests.stockfishchess.org/tests/view/6681816d442423e54714133f Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 56622 W: 14301 L: 14115 D: 28206 Ptnml(0-2): 31, 6259, 15538, 6459, 24 https://tests.stockfishchess.org/tests/view/6681a9a5596d543edc677490 closes https://github.com/official-stockfish/Stockfish/pull/5423 bench: 1171203 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index b68b30268bb..f561b183239 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1246,7 +1246,7 @@ Value Search::Worker::search( rm.effort += nodes - nodeCount; rm.averageScore = - rm.averageScore != -VALUE_INFINITE ? (2 * value + rm.averageScore) / 3 : value; + rm.averageScore != -VALUE_INFINITE ? (value + rm.averageScore) / 2 : value; // PV move or new best move? if (moveCount == 1 || value > alpha) From f6842a145cf59176abc229928b94404543daa250 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Sun, 30 Jun 2024 11:43:36 -0400 Subject: [PATCH 1616/1766] Simplify worsening deduction in futility margin Passed non-regression STC: https://tests.stockfishchess.org/tests/view/66817d46442423e547141226 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 345408 W: 89146 L: 89266 D: 166996 Ptnml(0-2): 954, 41317, 88286, 41189, 958 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/66818dbe1e90a146232d1f62 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 173214 W: 43821 L: 43755 D: 85638 Ptnml(0-2): 108, 19407, 47492, 19511, 89 closes https://github.com/official-stockfish/Stockfish/pull/5424 bench 981017 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index f561b183239..52eefdc94ec 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -62,7 +62,7 @@ static constexpr double EvalLevel[10] = {0.981, 0.956, 0.895, 0.949, 0.913, Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) { Value futilityMult = 109 - 40 * noTtCutNode; Value improvingDeduction = 59 * improving * futilityMult / 32; - Value worseningDeduction = 328 * oppWorsening * futilityMult / 1024; + Value worseningDeduction = oppWorsening * futilityMult / 3; return futilityMult * d - improvingDeduction - worseningDeduction; } From 843b6f7c9873d86742cf9b6ce3523f2c5dc69d2a Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Sun, 30 Jun 2024 17:00:49 -0400 Subject: [PATCH 1617/1766] Update some params for pruning at shallow depth Values found around 82k / 120k spsa games at 60+0.6: https://tests.stockfishchess.org/tests/view/6681aca4481148df247298bd Passed STC: https://tests.stockfishchess.org/tests/view/6681c795c1657e386d2948fa LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 145216 W: 37595 L: 37122 D: 70499 Ptnml(0-2): 375, 17122, 37185, 17507, 419 Passed LTC: https://tests.stockfishchess.org/tests/view/6681d4eec1657e386d2949e0 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 154062 W: 39117 L: 38557 D: 76388 Ptnml(0-2): 67, 16874, 42608, 17396, 86 closes https://github.com/official-stockfish/Stockfish/pull/5425 bench 996419 --- src/search.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 52eefdc94ec..2e8d47cf952 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -994,7 +994,7 @@ Value Search::Worker::search( // Futility pruning for captures (~2 Elo) if (!givesCheck && lmrDepth < 7 && !ss->inCheck) { - Value futilityValue = ss->staticEval + 287 + 248 * lmrDepth + Value futilityValue = ss->staticEval + 294 + 246 * lmrDepth + PieceValue[capturedPiece] + captHist / 7; if (futilityValue <= alpha) continue; @@ -1002,7 +1002,7 @@ Value Search::Worker::search( // SEE based pruning for captures and checks (~11 Elo) int seeHist = std::clamp(captHist / 32, -180 * depth, 163 * depth); - if (!pos.see_ge(move, -160 * depth - seeHist)) + if (!pos.see_ge(move, -163 * depth - seeHist)) continue; } else @@ -1013,15 +1013,15 @@ Value Search::Worker::search( + thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()]; // Continuation history based pruning (~2 Elo) - if (lmrDepth < 6 && history < -4151 * depth) + if (lmrDepth < 6 && history < -3899 * depth) continue; history += 2 * thisThread->mainHistory[us][move.from_to()]; - lmrDepth += history / 3678; + lmrDepth += history / 4040; Value futilityValue = - ss->staticEval + (bestValue < ss->staticEval - 51 ? 138 : 54) + 140 * lmrDepth; + ss->staticEval + (bestValue < ss->staticEval - 51 ? 135 : 56) + 140 * lmrDepth; // Futility pruning: parent node (~13 Elo) if (!ss->inCheck && lmrDepth < 12 && futilityValue <= alpha) From 6138a0fd0e43753a86e4a170a5f6e2b7b6752677 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Sun, 30 Jun 2024 19:22:04 -0400 Subject: [PATCH 1618/1766] Probcut in check no matter if pv or capture Passed STC: https://tests.stockfishchess.org/tests/view/6681e9c8c1657e386d294cef LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 217824 W: 56149 L: 56129 D: 105546 Ptnml(0-2): 587, 25926, 55848, 25982, 569 Passed LTC: https://tests.stockfishchess.org/tests/view/6681fcb8c1657e386d294db1 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 357552 W: 90546 L: 90671 D: 176335 Ptnml(0-2): 207, 40064, 98362, 39933, 210 Each half of this also passed STC+LTC separately closes https://github.com/official-stockfish/Stockfish/pull/5427 bench 1227870 --- src/search.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 2e8d47cf952..31278241c79 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -906,9 +906,8 @@ Value Search::Worker::search( // Step 12. A small Probcut idea, when we are in check (~4 Elo) probCutBeta = beta + 388; - if (ss->inCheck && !PvNode && ttCapture && (ttData.bound & BOUND_LOWER) - && ttData.depth >= depth - 4 && ttData.value >= probCutBeta - && std::abs(ttData.value) < VALUE_TB_WIN_IN_MAX_PLY + if (ss->inCheck && (ttData.bound & BOUND_LOWER) && ttData.depth >= depth - 4 + && ttData.value >= probCutBeta && std::abs(ttData.value) < VALUE_TB_WIN_IN_MAX_PLY && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) return probCutBeta; From 69ad4667fb40cc0d7195f9fa20652903813d698c Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sun, 30 Jun 2024 22:04:51 -0700 Subject: [PATCH 1619/1766] Do Capture History Updates In Probcut This patch introduces history updates to probcut. Standard depth - 3 bonus and maluses are given to the capture that caused fail high and previously searched captures, respectively. Similar to #5243, a negative history fill is applied to compensate for an increase in capture history average, thus improving the scaling of this patch. Passed STC: LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 84832 W: 21941 L: 21556 D: 41335 Ptnml(0-2): 226, 9927, 21688, 10386, 189 https://tests.stockfishchess.org/tests/view/6682fab9389b9ee542b1d029 Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 104298 W: 26469 L: 26011 D: 51818 Ptnml(0-2): 43, 11458, 28677, 11940, 31 https://tests.stockfishchess.org/tests/view/6682ff06389b9ee542b1d0a0 closes https://github.com/official-stockfish/Stockfish/pull/5428 bench 1281351 --- src/search.cpp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 31278241c79..188e81f4f84 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -502,7 +502,7 @@ void Search::Worker::iterative_deepening() { void Search::Worker::clear() { counterMoves.fill(Move::none()); mainHistory.fill(0); - captureHistory.fill(0); + captureHistory.fill(-700); pawnHistory.fill(-1193); correctionHistory.fill(0); @@ -862,12 +862,19 @@ Value Search::Worker::search( assert(probCutBeta < VALUE_INFINITE && probCutBeta > beta); MovePicker mp(pos, ttData.move, probCutBeta - ss->staticEval, &thisThread->captureHistory); + Move probcutCapturesSearched[32]; + int probcutCaptureCount = 0; + Piece captured; while ((move = mp.next_move()) != Move::none()) if (move != excludedMove && pos.legal(move)) { assert(pos.capture_stage(move)); + movedPiece = pos.moved_piece(move); + captured = pos.piece_on(move.to_sq()); + + // Prefetch the TT entry for the resulting position prefetch(tt.first_entry(pos.key_after(move))); @@ -891,12 +898,28 @@ Value Search::Worker::search( if (value >= probCutBeta) { + thisThread->captureHistory[movedPiece][move.to_sq()][type_of(captured)] + << stat_bonus(depth - 2); + + for (int i = 0; i < probcutCaptureCount; i++) + { + movedPiece = pos.moved_piece(probcutCapturesSearched[i]); + captured = pos.piece_on(probcutCapturesSearched[i].to_sq()); + + thisThread->captureHistory[movedPiece][probcutCapturesSearched[i].to_sq()] + [type_of(captured)] + << -stat_malus(depth - 3); + } + // Save ProbCut data into transposition table ttWriter.write(posKey, value_to_tt(value, ss->ply), ss->ttPv, BOUND_LOWER, depth - 3, move, unadjustedStaticEval, tt.generation()); return std::abs(value) < VALUE_TB_WIN_IN_MAX_PLY ? value - (probCutBeta - beta) : value; } + + if (probcutCaptureCount < 32) + probcutCapturesSearched[probcutCaptureCount++] = move; } Eval::NNUE::hint_common_parent_position(pos, networks[numaAccessToken], refreshTable); From 6b7822119feffd0a27ae5b2a95d3570c9e046090 Mon Sep 17 00:00:00 2001 From: "Shahin M. Shahin" Date: Tue, 25 Jun 2024 01:57:35 +0300 Subject: [PATCH 1620/1766] Limit has_game_cycle() to only upcoming repetition use the original algorithm according to the paper http://web.archive.org/web/20201107002606/https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf, which detects accurately if a position has an upcoming repetition. The 'no progress' part of has_game_cycle has been removed, the function has been renamed to upcoming_repetition to reflect this. As a result of this fix, to the best of our knowledge, all PVs for completed iterations that yield a mate or decisive table base score now end in mate or contain a TB position, respectively. passed non-regression STC: https://tests.stockfishchess.org/tests/view/6679fa1d0c2db3fa2dcecbf2 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 63584 W: 16666 L: 16472 D: 30446 Ptnml(0-2): 186, 7552, 16146, 7698, 210 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/667ac965e439ed1c7a9ca042 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 464574 W: 117493 L: 117729 D: 229352 Ptnml(0-2): 311, 52468, 126974, 52214, 320 closes https://github.com/official-stockfish/Stockfish/pull/5432 bench: 1209805 --- src/position.cpp | 20 ++++++++++---------- src/position.h | 2 +- src/search.cpp | 10 ++++------ 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index b46ba029985..d374b1c070a 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1156,9 +1156,9 @@ bool Position::has_repeated() const { } -// Tests if the position has a move which draws by repetition, -// or an earlier position has a move that directly reaches the current position. -bool Position::has_game_cycle(int ply) const { +// Tests if the position has a move which draws by repetition. +// This function accurately matches the outcome of is_draw() over all legal moves. +bool Position::upcoming_repetition(int ply) const { int j; @@ -1169,10 +1169,16 @@ bool Position::has_game_cycle(int ply) const { Key originalKey = st->key; StateInfo* stp = st->previous; + Key other = originalKey ^ stp->key ^ Zobrist::side; for (int i = 3; i <= end; i += 2) { - stp = stp->previous->previous; + stp = stp->previous; + other ^= stp->key ^ stp->previous->key ^ Zobrist::side; + stp = stp->previous; + + if (other != 0) + continue; Key moveKey = originalKey ^ stp->key; if ((j = H1(moveKey), cuckoo[j] == moveKey) || (j = H2(moveKey), cuckoo[j] == moveKey)) @@ -1188,12 +1194,6 @@ bool Position::has_game_cycle(int ply) const { // For nodes before or at the root, check that the move is a // repetition rather than a move to the current position. - // In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in - // the same location, so we have to select which square to check. - if (color_of(piece_on(empty(s1) ? s2 : s1)) != side_to_move()) - continue; - - // For repetitions before or at the root, require one more if (stp->repetition) return true; } diff --git a/src/position.h b/src/position.h index 154ed652942..3cfb87d065f 100644 --- a/src/position.h +++ b/src/position.h @@ -156,7 +156,7 @@ class Position { int game_ply() const; bool is_chess960() const; bool is_draw(int ply) const; - bool has_game_cycle(int ply) const; + bool upcoming_repetition(int ply) const; bool has_repeated() const; int rule50_count() const; Value non_pawn_material(Color c) const; diff --git a/src/search.cpp b/src/search.cpp index 188e81f4f84..6368acc6b97 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -534,9 +534,8 @@ Value Search::Worker::search( // Limit the depth if extensions made it too large depth = std::min(depth, MAX_PLY - 1); - // Check if we have an upcoming move that draws by repetition, or - // if the opponent had an alternative move earlier to this position. - if (!rootNode && alpha < VALUE_DRAW && pos.has_game_cycle(ss->ply)) + // Check if we have an upcoming move that draws by repetition. + if (!rootNode && alpha < VALUE_DRAW && pos.upcoming_repetition(ss->ply)) { alpha = value_draw(this->nodes); if (alpha >= beta) @@ -1447,9 +1446,8 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, assert(PvNode || (alpha == beta - 1)); assert(depth <= 0); - // Check if we have an upcoming move that draws by repetition, or if - // the opponent had an alternative move earlier to this position. (~1 Elo) - if (alpha < VALUE_DRAW && pos.has_game_cycle(ss->ply)) + // Check if we have an upcoming move that draws by repetition. (~1 Elo) + if (alpha < VALUE_DRAW && pos.upcoming_repetition(ss->ply)) { alpha = value_draw(this->nodes); if (alpha >= beta) From ad0f1fecda6987b16e34807a5ebc3947ced9a866 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Tue, 2 Jul 2024 14:18:04 +0200 Subject: [PATCH 1621/1766] Move info strings once more Follow up from #5404 ... current location leads to troubles with Aquarium GUI Fixes #5430 Now prints the information on threads and available processors at the beginning of search, where info about the networks is already printed (and is known to work) closes https://github.com/official-stockfish/Stockfish/pull/5433 No functional change. --- src/engine.cpp | 14 +++++++++----- src/uci.cpp | 9 +++++---- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/engine.cpp b/src/engine.cpp index 233f6270110..2bc0db6affb 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -297,16 +297,20 @@ std::string Engine::get_numa_config_as_string() const { std::string Engine::numa_config_information_as_string() const { auto cfgStr = get_numa_config_as_string(); - return "Available Processors: " + cfgStr; + return "Available processors: " + cfgStr; } std::string Engine::thread_binding_information_as_string() const { - auto boundThreadsByNode = get_bound_thread_count_by_numa_node(); + auto boundThreadsByNode = get_bound_thread_count_by_numa_node(); + std::stringstream ss; + + size_t threadsSize = threads.size(); + ss << "Using " << threadsSize << (threadsSize > 1 ? " threads" : " thread"); + if (boundThreadsByNode.empty()) - return ""; + return ss.str(); - std::stringstream ss; - ss << "NUMA Node Thread Binding: "; + ss << " with NUMA node thread binding: "; bool isFirst = true; diff --git a/src/uci.cpp b/src/uci.cpp index 3c9177ee3b3..9b60680d8c5 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -109,16 +109,17 @@ void UCIEngine::loop() { << engine.get_options() << sync_endl; sync_cout << "uciok" << sync_endl; - - // keep info strings after uciok for old GUIs - print_info_string(engine.numa_config_information_as_string()); - print_info_string(engine.thread_binding_information_as_string()); } else if (token == "setoption") setoption(is); else if (token == "go") + { + // send info strings after the go command is sent for old GUIs and python-chess + print_info_string(engine.numa_config_information_as_string()); + print_info_string(engine.thread_binding_information_as_string()); go(is); + } else if (token == "position") position(is); else if (token == "ucinewgame") From b9ff5bb93be410b418d6812d6753e64cf216057a Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Tue, 2 Jul 2024 15:06:37 -0700 Subject: [PATCH 1622/1766] Implement dbg_extremes_of An alternative to #5431, implements one function `dbg_extremes_of` to keep track of min and max. closes https://github.com/official-stockfish/Stockfish/pull/5434 No functional change --- src/misc.cpp | 35 +++++++++++++++++++++++++++++++---- src/misc.h | 2 ++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index 26dd3a2898c..b68c12b97f6 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -284,10 +284,18 @@ struct DebugInfo { constexpr std::atomic& operator[](int index) { return data[index]; } }; -DebugInfo<2> hit[MaxDebugSlots]; -DebugInfo<2> mean[MaxDebugSlots]; -DebugInfo<3> stdev[MaxDebugSlots]; -DebugInfo<6> correl[MaxDebugSlots]; +struct DebugExtremes: public DebugInfo<3> { + DebugExtremes() { + data[1] = std::numeric_limits::min(); + data[2] = std::numeric_limits::max(); + } +}; + +DebugInfo<2> hit[MaxDebugSlots]; +DebugInfo<2> mean[MaxDebugSlots]; +DebugInfo<3> stdev[MaxDebugSlots]; +DebugInfo<6> correl[MaxDebugSlots]; +DebugExtremes extremes[MaxDebugSlots]; } // namespace @@ -311,6 +319,18 @@ void dbg_stdev_of(int64_t value, int slot) { stdev[slot][2] += value * value; } +void dbg_extremes_of(int64_t value, int slot) { + ++extremes[slot][0]; + + int64_t current_max = extremes[slot][1].load(); + while (current_max < value && !extremes[slot][1].compare_exchange_weak(current_max, value)) + {} + + int64_t current_min = extremes[slot][2].load(); + while (current_min > value && !extremes[slot][2].compare_exchange_weak(current_min, value)) + {} +} + void dbg_correl_of(int64_t value1, int64_t value2, int slot) { ++correl[slot][0]; @@ -345,6 +365,13 @@ void dbg_print() { std::cerr << "Stdev #" << i << ": Total " << n << " Stdev " << r << std::endl; } + for (int i = 0; i < MaxDebugSlots; ++i) + if ((n = extremes[i][0])) + { + std::cerr << "Extremity #" << i << ": Total " << n << " Min " << extremes[i][2] + << " Max " << extremes[i][1] << std::endl; + } + for (int i = 0; i < MaxDebugSlots; ++i) if ((n = correl[i][0])) { diff --git a/src/misc.h b/src/misc.h index bdc7c864d3f..0184ab88c00 100644 --- a/src/misc.h +++ b/src/misc.h @@ -67,6 +67,8 @@ std::optional read_file_to_string(const std::string& path); void dbg_hit_on(bool cond, int slot = 0); void dbg_mean_of(int64_t value, int slot = 0); void dbg_stdev_of(int64_t value, int slot = 0); +void dbg_extremes_of(int64_t value, int slot); + void dbg_correl_of(int64_t value1, int64_t value2, int slot = 0); void dbg_print(); From ee6fc7e38b4aeef44862159215a56d97122f59a0 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 3 Jul 2024 11:14:41 +0200 Subject: [PATCH 1623/1766] CI: limit artifact uploads do not upload some unneeded intermediate directories, disable running authenticated git commands with the checkout action. Thanks to Yaron A for the report. closes https://github.com/official-stockfish/Stockfish/pull/5435 No functional change --- .github/workflows/arm_compilation.yml | 6 +++++- .github/workflows/clang-format.yml | 1 + .github/workflows/codeql.yml | 2 ++ .github/workflows/compilation.yml | 7 ++++++- .github/workflows/games.yml | 2 ++ .github/workflows/iwyu.yml | 2 ++ .github/workflows/matetrack.yml | 2 ++ .github/workflows/sanitizers.yml | 2 ++ .github/workflows/stockfish.yml | 4 ++++ .github/workflows/tests.yml | 1 + .github/workflows/upload_binaries.yml | 2 ++ 11 files changed, 29 insertions(+), 2 deletions(-) diff --git a/.github/workflows/arm_compilation.yml b/.github/workflows/arm_compilation.yml index 3934ac2d636..5bf2a93e552 100644 --- a/.github/workflows/arm_compilation.yml +++ b/.github/workflows/arm_compilation.yml @@ -26,6 +26,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 + persist-credentials: false - name: Download required linux packages if: runner.os == 'Linux' @@ -91,4 +92,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: ${{ matrix.config.simple_name }} ${{ matrix.binaries }} - path: . + path: | + . + !.git + !.output diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 630edbf93fe..637cfc0d826 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -19,6 +19,7 @@ jobs: - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} + persist-credentials: false - name: Run clang-format style check uses: jidicula/clang-format-action@f62da5e3d3a2d88ff364771d9d938773a618ab5e # @v4.11.0 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index d949a5a7649..d01ed41fea6 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -30,6 +30,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + persist-credentials: false # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/compilation.yml b/.github/workflows/compilation.yml index 3524d5e9f2e..5878adecb5c 100644 --- a/.github/workflows/compilation.yml +++ b/.github/workflows/compilation.yml @@ -25,6 +25,8 @@ jobs: shell: ${{ matrix.config.shell }} steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Install fixed GCC on Linux if: runner.os == 'Linux' @@ -86,4 +88,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: ${{ matrix.config.simple_name }} ${{ matrix.binaries }} - path: . + path: | + . + !.git + !.output diff --git a/.github/workflows/games.yml b/.github/workflows/games.yml index 088695e57fc..f0bca442fdc 100644 --- a/.github/workflows/games.yml +++ b/.github/workflows/games.yml @@ -13,6 +13,7 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha }} path: Stockfish + persist-credentials: false - name: build debug enabled version of SF working-directory: Stockfish/src @@ -24,6 +25,7 @@ jobs: repository: Disservin/fast-chess path: fast-chess ref: d54af1910d5479c669dc731f1f54f9108a251951 + persist-credentials: false - name: fast-chess build working-directory: fast-chess diff --git a/.github/workflows/iwyu.yml b/.github/workflows/iwyu.yml index 0552a598c8f..f8898b1c90e 100644 --- a/.github/workflows/iwyu.yml +++ b/.github/workflows/iwyu.yml @@ -14,6 +14,7 @@ jobs: uses: actions/checkout@v4 with: path: Stockfish + persist-credentials: false - name: Checkout include-what-you-use uses: actions/checkout@v4 @@ -21,6 +22,7 @@ jobs: repository: include-what-you-use/include-what-you-use ref: f25caa280dc3277c4086ec345ad279a2463fea0f path: include-what-you-use + persist-credentials: false - name: Download required linux packages run: | diff --git a/.github/workflows/matetrack.yml b/.github/workflows/matetrack.yml index dd81f334d05..de65209fb29 100644 --- a/.github/workflows/matetrack.yml +++ b/.github/workflows/matetrack.yml @@ -13,6 +13,7 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha }} path: Stockfish + persist-credentials: false - name: build SF working-directory: Stockfish/src @@ -24,6 +25,7 @@ jobs: repository: vondele/matetrack path: matetrack ref: 20287a1a145f30a166b7ef251eddb611e4e44fbf + persist-credentials: false - name: matetrack install deps working-directory: matetrack diff --git a/.github/workflows/sanitizers.yml b/.github/workflows/sanitizers.yml index b75c06cfbbe..55459292107 100644 --- a/.github/workflows/sanitizers.yml +++ b/.github/workflows/sanitizers.yml @@ -40,6 +40,8 @@ jobs: shell: ${{ matrix.config.shell }} steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Download required linux packages run: | diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index 8a1094fbdbd..5589c762489 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -17,6 +17,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + persist-credentials: false # returns null if no pre-release exists - name: Get Commit SHA of Latest Pre-release @@ -66,6 +68,8 @@ jobs: arm_matrix: ${{ steps.set-arm-matrix.outputs.arm_matrix }} steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - id: set-matrix run: | TASKS=$(echo $(cat .github/ci/matrix.json) ) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 328c9cf94b1..836555e6127 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -106,6 +106,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 + persist-credentials: false - name: Download required linux packages if: runner.os == 'Linux' diff --git a/.github/workflows/upload_binaries.yml b/.github/workflows/upload_binaries.yml index acf91a8f331..c91824a2556 100644 --- a/.github/workflows/upload_binaries.yml +++ b/.github/workflows/upload_binaries.yml @@ -25,6 +25,8 @@ jobs: shell: ${{ matrix.config.shell }} steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Download artifact from compilation uses: actions/download-artifact@v4 From 74a8fc060465a822f0c047f908d5fb07ebc6ad96 Mon Sep 17 00:00:00 2001 From: Disservin Date: Wed, 3 Jul 2024 14:07:48 +0200 Subject: [PATCH 1624/1766] Use explicit action permissions in CI Necessary modifications according to changes in the GitHub Action settings. closes https://github.com/official-stockfish/Stockfish/pull/5437 Follow up from the report by Yaron Avital (yaronav) earlier. No functional change --- .github/workflows/stockfish.yml | 10 ++++++++++ .github/workflows/upload_binaries.yml | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/.github/workflows/stockfish.yml b/.github/workflows/stockfish.yml index 5589c762489..1f87e061be9 100644 --- a/.github/workflows/stockfish.yml +++ b/.github/workflows/stockfish.yml @@ -15,6 +15,8 @@ jobs: Prerelease: if: github.repository == 'official-stockfish/Stockfish' && (github.ref == 'refs/heads/master' || (startsWith(github.ref_name, 'sf_') && github.ref_type == 'tag')) runs-on: ubuntu-latest + permissions: + contents: write # For deleting/creating a prerelease steps: - uses: actions/checkout@v4 with: @@ -104,9 +106,17 @@ jobs: uses: ./.github/workflows/upload_binaries.yml with: matrix: ${{ needs.Matrix.outputs.matrix }} + permissions: + contents: write # For deleting/creating a (pre)release + secrets: + token: ${{ secrets.GITHUB_TOKEN }} ARM_Binaries: if: github.repository == 'official-stockfish/Stockfish' needs: [Matrix, Prerelease, ARMCompilation] uses: ./.github/workflows/upload_binaries.yml with: matrix: ${{ needs.Matrix.outputs.arm_matrix }} + permissions: + contents: write # For deleting/creating a (pre)release + secrets: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/upload_binaries.yml b/.github/workflows/upload_binaries.yml index c91824a2556..c5a2cd105c6 100644 --- a/.github/workflows/upload_binaries.yml +++ b/.github/workflows/upload_binaries.yml @@ -5,6 +5,9 @@ on: matrix: type: string required: true + secrets: + token: + required: true jobs: Artifacts: @@ -80,6 +83,7 @@ jobs: uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981 with: files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.${{ matrix.config.archive_ext }} + token: ${{ secrets.token }} - name: Get last commit sha id: last_commit @@ -106,3 +110,4 @@ jobs: tag_name: stockfish-dev-${{ env.COMMIT_DATE }}-${{ env.COMMIT_SHA }} prerelease: true files: stockfish-${{ matrix.config.simple_name }}-${{ matrix.binaries }}.${{ matrix.config.archive_ext }} + token: ${{ secrets.token }} From 25361e514bffb81284d4311601a9f7a4a7ced79b Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 3 Jul 2024 17:39:55 +0200 Subject: [PATCH 1625/1766] Output from a fix depth onward, instead of 3s. To avoid output that depends on timing, output currmove and similar only from depth > 30 onward. Current choice of 3s makes the output of the same search depending on the system load, and doesn't always start at move 1. Depth 30 is nowadays reached in a few seconds on most systems. closes https://github.com/official-stockfish/Stockfish/pull/5436 No functional change --- src/search.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 6368acc6b97..5eda1217b74 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -349,10 +349,10 @@ void Search::Worker::iterative_deepening() { if (threads.stop) break; - // When failing high/low give some update (without cluttering - // the UI) before a re-search. + // When failing high/low give some update before a re-search. + // To avoid excessive output, only start at rootDepth > 30. if (mainThread && multiPV == 1 && (bestValue <= alpha || bestValue >= beta) - && elapsed_time() > 3000) + && rootDepth > 30) main_manager()->pv(*this, threads, tt, rootDepth); // In case of failing low/high increase aspiration window and @@ -383,7 +383,7 @@ void Search::Worker::iterative_deepening() { std::stable_sort(rootMoves.begin() + pvFirst, rootMoves.begin() + pvIdx + 1); if (mainThread - && (threads.stop || pvIdx + 1 == multiPV || elapsed_time() > 3000) + && (threads.stop || pvIdx + 1 == multiPV || rootDepth > 30) // A thread that aborted search can have mated-in/TB-loss PV and score // that cannot be trusted, i.e. it can be delayed or refuted if we would have // had time to fully search other root-moves. Thus we suppress this output and @@ -974,7 +974,7 @@ Value Search::Worker::search( ss->moveCount = ++moveCount; - if (rootNode && is_mainthread() && elapsed_time() > 3000) + if (rootNode && is_mainthread() && rootDepth > 30) { main_manager()->updates.onIter( {depth, UCIEngine::move(move, pos.is_chess960()), moveCount + thisThread->pvIdx}); From 3c379e55d9d92a5704632c6255e72892a4db9a2f Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Mon, 1 Jul 2024 16:26:44 -0400 Subject: [PATCH 1626/1766] Update 7 stat bonus/malus params Values found around 119k / 120k spsa games at 60+0.6: https://tests.stockfishchess.org/tests/view/6683112a192114e61f92f87a Passed STC: https://tests.stockfishchess.org/tests/view/66838148c4f539faa0326897 LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 40928 W: 10835 L: 10508 D: 19585 Ptnml(0-2): 139, 4802, 10254, 5131, 138 Passed LTC: https://tests.stockfishchess.org/tests/view/668448a87a1863935cee42c6 LLR: 2.96 (-2.94,2.94) <0.50,2.50> Total: 29208 W: 7559 L: 7253 D: 14396 Ptnml(0-2): 17, 3118, 8019, 3442, 8 closes https://github.com/official-stockfish/Stockfish/pull/5439 bench 1138753 --- src/search.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5eda1217b74..f74d4f87c5f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -79,10 +79,10 @@ Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { } // History and stats update bonus, based on depth -int stat_bonus(Depth d) { return std::clamp(186 * d - 285, 20, 1524); } +int stat_bonus(Depth d) { return std::clamp(191 * d - 285, 20, 1412); } // History and stats update malus, based on depth -int stat_malus(Depth d) { return (d < 4 ? 707 * d - 260 : 2073); } +int stat_malus(Depth d) { return (d < 4 ? 727 * d - 260 : 1908); } // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(size_t nodes) { return VALUE_DRAW - 1 + Value(nodes & 0x2); } @@ -1380,11 +1380,9 @@ Value Search::Worker::search( + 64 * (!ss->inCheck && bestValue <= ss->staticEval - 107) + 147 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 75)); - - // proportional to "how much damage we have to undo" - if ((ss - 1)->statScore < -8000) - bonus += std::clamp(-(ss - 1)->statScore / 100, 0, 250); - + // Proportional to "how much damage we have to undo" + if ((ss - 1)->statScore < -7850) + bonus += std::clamp(-(ss - 1)->statScore / 100, 0, 224); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus / 100); @@ -1801,7 +1799,7 @@ void update_all_stats(const Position& pos, if (!pos.capture_stage(bestMove)) { - int bestMoveBonus = bestValue > beta + 164 ? quietMoveBonus // larger bonus + int bestMoveBonus = bestValue > beta + 166 ? quietMoveBonus // larger bonus : stat_bonus(depth); // smaller bonus update_quiet_stats(pos, ss, workerThread, bestMove, bestMoveBonus); From 2cbc20e846e46da8bfc8e254a7703a0bfad3b850 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Tue, 25 Jun 2024 16:54:25 +0200 Subject: [PATCH 1627/1766] Correct and extend PV lines with decisive TB score Currently (after #5407), SF has the property that any PV line with a decisive TB score contains the corresponding TB position, with a score that correctly identifies the depth at which TB are entered. The PV line that follows might not preserve the game outcome, but can easily be verified and extended based on TB information. This patch provides this functionality, simply extending the PV lines on output, this doesn't affect search. Indeed, if DTZ tables are available, search based PV lines that correspond to decisive TB scores are verified to preserve game outcome, truncating the line as needed. Subsequently, such PV lines are extended with a game outcome preserving line until mate, as a possible continuation. These lines are not optimal mating lines, but are similar to what a user could produce on a website like https://syzygy-tables.info/ clicking always the top ranked move, i.e. minimizing or maximizing DTZ (with a simple tie-breaker for moves that have identical DTZ), and are thus an just an illustration of how to game can be won. A similar approach is already in established in https://github.com/joergoster/Stockfish/tree/matefish2 This also contributes to addressing #5175 where SF can give an incorrect TB win/loss for positions in TB with a movecounter that doesn't reflect optimal play. While the full solution requires either TB generated differently, or a search when ranking rootmoves, current SF will eventually find a draw in these cases, in practice quite quickly, e.g. `1kq5/q2r4/5K2/8/8/8/8/7Q w - - 96 1` `8/8/6k1/3B4/3K4/4N3/8/8 w - - 54 106` Gives the same results as master on an extended set of test positions from https://github.com/mcostalba/Stockfish/commit/9173d29c414ddb8f4bec74e4db3ccbe664c66bf9 with the exception of the above mentioned fen where this commit improves. With https://github.com/vondele/matetrack using 6men TB, all generated PVs verify: ``` Using ../Stockfish/src/stockfish.syzygyExtend on matetrack.epd with --nodes 1000000 --syzygyPath /chess/syzygy/3-4-5-6/WDL:/chess/syzygy/3-4-5-6/DTZ Engine ID: Stockfish dev-20240704-ff227954 Total FENs: 6555 Found mates: 3299 Best mates: 2582 Found TB wins: 568 ``` As repeated DTZ probing could be slow a procedure (100ms+ on HDD, a few ms on SSD), the extension is only done as long as the time taken is less than half the `Move Overhead` parameter. For tournaments where these lines might be of interest to the user, a suitable `Move Overhead` might be needed (e.g. TCEC has 1000ms already). closes https://github.com/official-stockfish/Stockfish/pull/5414 No functional change --- src/search.cpp | 177 +++++++++++++++++++++++++++++++++++++---- src/search.h | 4 +- src/syzygy/tbprobe.cpp | 29 ++++--- src/syzygy/tbprobe.h | 7 +- 4 files changed, 186 insertions(+), 31 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index f74d4f87c5f..023e5113460 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -28,6 +29,9 @@ #include #include #include +#include +#include +#include #include "evaluate.h" #include "misc.h" @@ -50,6 +54,12 @@ namespace Stockfish { namespace TB = Tablebases; +void syzygy_extend_pv(const OptionsMap& options, + const Search::LimitsType& limits, + Stockfish::Position& pos, + Stockfish::Search::RootMove& rootMove, + Value& v); + using Eval::evaluate; using namespace Search; @@ -1955,18 +1965,145 @@ void SearchManager::check_time(Search::Worker& worker) { worker.threads.stop = worker.threads.abortedSearch = true; } -void SearchManager::pv(const Search::Worker& worker, +// Used to correct and extend PVs for moves that have a TB (but not a mate) score. +// Keeps the search based PV for as long as it is verified to maintain the game outcome, truncates afterwards. +// Finally, extends to mate the PV, providing a possible continuation (but not a proven mating line). +void syzygy_extend_pv(const OptionsMap& options, + const Search::LimitsType& limits, + Position& pos, + RootMove& rootMove, + Value& v) { + + auto t_start = std::chrono::steady_clock::now(); + int moveOverhead = int(options["Move Overhead"]); + + // Do not use more than moveOverhead / 2 time, if time management is active. + auto time_abort = [&t_start, &moveOverhead, &limits]() -> bool { + auto t_end = std::chrono::steady_clock::now(); + return limits.use_time_management() + && 2 * std::chrono::duration(t_end - t_start).count() + > moveOverhead; + }; + + std::list sts; + + // Step 1, walk the PV to the last position in TB with correct decisive score + int ply = 0; + while (size_t(ply) < rootMove.pv.size()) + { + Move& pvMove = rootMove.pv[ply]; + + RootMoves legalMoves; + for (const auto& m : MoveList(pos)) + legalMoves.emplace_back(m); + + Tablebases::Config config = Tablebases::rank_root_moves(options, pos, legalMoves); + RootMove& rm = *std::find(legalMoves.begin(), legalMoves.end(), pvMove); + + if (legalMoves[0].tbRank != rm.tbRank) + break; + + ply++; + + auto& st = sts.emplace_back(); + pos.do_move(pvMove, st); + + // don't allow for repetitions or drawing moves along the PV in TB regime. + if (config.rootInTB && pos.is_draw(ply)) + { + pos.undo_move(pvMove); + ply--; + break; + } + + // Full PV shown will thus be validated and end TB. + // If we can't validate the full PV in time, we don't show it. + if (config.rootInTB && time_abort()) + break; + } + + // resize the PV to the correct part + rootMove.pv.resize(ply); + + // Step 2, now extend the PV to mate, as if the user explores syzygy-tables.info using + // top ranked moves (minimal DTZ), which gives optimal mates only for simple endgames e.g. KRvK + while (!pos.is_draw(0)) + { + if (time_abort()) + break; + + RootMoves legalMoves; + for (const auto& m : MoveList(pos)) + { + auto& rm = legalMoves.emplace_back(m); + StateInfo tmpSI; + pos.do_move(m, tmpSI); + // Give a score of each move to break DTZ ties + // restricting opponent mobility, but not giving the opponent a capture. + for (const auto& mOpp : MoveList(pos)) + rm.tbRank -= pos.capture(mOpp) ? 100 : 1; + pos.undo_move(m); + } + + // Mate found + if (legalMoves.size() == 0) + break; + + // sort moves according to their above assigned rank, + // This will break ties for moves with equal DTZ in rank_root_moves. + std::stable_sort( + legalMoves.begin(), legalMoves.end(), + [](const Search::RootMove& a, const Search::RootMove& b) { return a.tbRank > b.tbRank; }); + + // The winning side tries to minimize DTZ, the losing side maximizes it. + Tablebases::Config config = Tablebases::rank_root_moves(options, pos, legalMoves, true); + + // If DTZ is not available we might not find a mate, so we bail out. + if (!config.rootInTB || config.cardinality > 0) + break; + + ply++; + + Move& pvMove = legalMoves[0].pv[0]; + rootMove.pv.push_back(pvMove); + auto& st = sts.emplace_back(); + pos.do_move(pvMove, st); + } + + // Finding a draw in this function is an exceptional case, that cannot happen during engine game play, + // since we have a winning score, and play correctly with TB support. + // However, it can be that a position is draw due to the 50 move rule if it has been been reached + // on the board with a non-optimal 50 move counter e.g. 8/8/6k1/3B4/3K4/4N3/8/8 w - - 54 106 + // which TB with dtz counter rounding cannot always correctly rank. See also + // https://github.com/official-stockfish/Stockfish/issues/5175#issuecomment-2058893495 + // We adjust the score to match the found PV. Note that a TB loss score can be displayed + // if the engine did not find a drawing move yet, but eventually search will figure it out. + // E.g. 1kq5/q2r4/5K2/8/8/8/8/7Q w - - 96 1 + if (pos.is_draw(0)) + v = VALUE_DRAW; + + // Undo the PV moves. + for (auto it = rootMove.pv.rbegin(); it != rootMove.pv.rend(); ++it) + pos.undo_move(*it); + + // Inform if we couldn't get a full extension in time. + if (time_abort()) + sync_cout + << "info string Syzygy based PV extension requires more time, increase Move Overhead as needed." + << sync_endl; +} + +void SearchManager::pv(Search::Worker& worker, const ThreadPool& threads, const TranspositionTable& tt, - Depth depth) const { + Depth depth) { - const auto nodes = threads.nodes_searched(); - const auto& rootMoves = worker.rootMoves; - const auto& pos = worker.rootPos; - size_t pvIdx = worker.pvIdx; - TimePoint time = tm.elapsed_time() + 1; - size_t multiPV = std::min(size_t(worker.options["MultiPV"]), rootMoves.size()); - uint64_t tbHits = threads.tb_hits() + (worker.tbConfig.rootInTB ? rootMoves.size() : 0); + const auto nodes = threads.nodes_searched(); + auto& rootMoves = worker.rootMoves; + auto& pos = worker.rootPos; + size_t pvIdx = worker.pvIdx; + size_t multiPV = std::min(size_t(worker.options["MultiPV"]), rootMoves.size()); + uint64_t tbHits = threads.tb_hits() + (worker.tbConfig.rootInTB ? rootMoves.size() : 0); for (size_t i = 0; i < multiPV; ++i) { @@ -1984,6 +2121,13 @@ void SearchManager::pv(const Search::Worker& worker, bool tb = worker.tbConfig.rootInTB && std::abs(v) <= VALUE_TB; v = tb ? rootMoves[i].tbScore : v; + bool isExact = i != pvIdx || tb || !updated; // tablebase- and previous-scores are exact + + // Potentially correct and extend the PV, and in exceptional cases v + if (std::abs(v) >= VALUE_TB_WIN_IN_MAX_PLY && std::abs(v) < VALUE_MATE_IN_MAX_PLY + && ((!rootMoves[i].scoreLowerbound && !rootMoves[i].scoreUpperbound) || isExact)) + syzygy_extend_pv(worker.options, worker.limits, pos, rootMoves[i], v); + std::string pv; for (Move m : rootMoves[i].pv) pv += UCIEngine::move(m, pos.is_chess960()) + " "; @@ -2005,15 +2149,16 @@ void SearchManager::pv(const Search::Worker& worker, info.score = {v, pos}; info.wdl = wdl; - if (i == pvIdx && !tb && updated) // tablebase- and previous-scores are exact + if (!isExact) info.bound = bound; - info.timeMs = time; - info.nodes = nodes; - info.nps = nodes * 1000 / time; - info.tbHits = tbHits; - info.pv = pv; - info.hashfull = tt.hashfull(); + TimePoint time = tm.elapsed_time() + 1; + info.timeMs = time; + info.nodes = nodes; + info.nps = nodes * 1000 / time; + info.tbHits = tbHits; + info.pv = pv; + info.hashfull = tt.hashfull(); updates.onUpdateFull(info); } diff --git a/src/search.h b/src/search.h index d5210c2e072..e8e33b1a819 100644 --- a/src/search.h +++ b/src/search.h @@ -202,10 +202,10 @@ class SearchManager: public ISearchManager { void check_time(Search::Worker& worker) override; - void pv(const Search::Worker& worker, + void pv(Search::Worker& worker, const ThreadPool& threads, const TranspositionTable& tt, - Depth depth) const; + Depth depth); Stockfish::TimeManagement tm; double originalTimeAdjust; diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index 722dc9d3e8e..fc2a092aa54 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -66,7 +66,7 @@ namespace { constexpr int TBPIECES = 7; // Max number of supported pieces constexpr int MAX_DTZ = - 1 << 18; // Max DTZ supported, large enough to deal with the syzygy TB limit. + 1 << 18; // Max DTZ supported times 2, large enough to deal with the syzygy TB limit. enum { BigEndian, @@ -1574,7 +1574,10 @@ int Tablebases::probe_dtz(Position& pos, ProbeState* result) { // Use the DTZ tables to rank root moves. // // A return value false indicates that not all probes were successful. -bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves, bool rule50) { +bool Tablebases::root_probe(Position& pos, + Search::RootMoves& rootMoves, + bool rule50, + bool rankDTZ) { ProbeState result = OK; StateInfo st; @@ -1585,7 +1588,7 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves, bool ru // Check whether a position was repeated since the last zeroing move. bool rep = pos.has_repeated(); - int dtz, bound = rule50 ? (MAX_DTZ - 100) : 1; + int dtz, bound = rule50 ? (MAX_DTZ / 2 - 100) : 1; // Probe and rank each move for (auto& m : rootMoves) @@ -1624,8 +1627,10 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves, bool ru // Better moves are ranked higher. Certain wins are ranked equally. // Losing moves are ranked equally unless a 50-move draw is in sight. - int r = dtz > 0 ? (dtz + cnt50 <= 99 && !rep ? MAX_DTZ : MAX_DTZ - (dtz + cnt50)) - : dtz < 0 ? (-dtz * 2 + cnt50 < 100 ? -MAX_DTZ : -MAX_DTZ + (-dtz + cnt50)) + int r = dtz > 0 ? (dtz + cnt50 <= 99 && !rep ? MAX_DTZ - (rankDTZ ? dtz : 0) + : MAX_DTZ / 2 - (dtz + cnt50)) + : dtz < 0 ? (-dtz * 2 + cnt50 < 100 ? -MAX_DTZ - (rankDTZ ? dtz : 0) + : -MAX_DTZ / 2 + (-dtz + cnt50)) : 0; m.tbRank = r; @@ -1633,10 +1638,11 @@ bool Tablebases::root_probe(Position& pos, Search::RootMoves& rootMoves, bool ru // 1 cp to cursed wins and let it grow to 49 cp as the positions gets // closer to a real win. m.tbScore = r >= bound ? VALUE_MATE - MAX_PLY - 1 - : r > 0 ? Value((std::max(3, r - (MAX_DTZ - 200)) * int(PawnValue)) / 200) - : r == 0 ? VALUE_DRAW - : r > -bound ? Value((std::min(-3, r + (MAX_DTZ - 200)) * int(PawnValue)) / 200) - : -VALUE_MATE + MAX_PLY + 1; + : r > 0 ? Value((std::max(3, r - (MAX_DTZ / 2 - 200)) * int(PawnValue)) / 200) + : r == 0 ? VALUE_DRAW + : r > -bound + ? Value((std::min(-3, r + (MAX_DTZ / 2 - 200)) * int(PawnValue)) / 200) + : -VALUE_MATE + MAX_PLY + 1; } return true; @@ -1683,7 +1689,8 @@ bool Tablebases::root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, boo Config Tablebases::rank_root_moves(const OptionsMap& options, Position& pos, - Search::RootMoves& rootMoves) { + Search::RootMoves& rootMoves, + bool rankDTZ) { Config config; if (rootMoves.empty()) @@ -1707,7 +1714,7 @@ Config Tablebases::rank_root_moves(const OptionsMap& options, if (config.cardinality >= popcount(pos.pieces()) && !pos.can_castle(ANY_CASTLING)) { // Rank moves using DTZ tables - config.rootInTB = root_probe(pos, rootMoves, options["Syzygy50MoveRule"]); + config.rootInTB = root_probe(pos, rootMoves, options["Syzygy50MoveRule"], rankDTZ); if (!config.rootInTB) { diff --git a/src/syzygy/tbprobe.h b/src/syzygy/tbprobe.h index e10950f4e6b..75a1858576b 100644 --- a/src/syzygy/tbprobe.h +++ b/src/syzygy/tbprobe.h @@ -66,9 +66,12 @@ extern int MaxCardinality; void init(const std::string& paths); WDLScore probe_wdl(Position& pos, ProbeState* result); int probe_dtz(Position& pos, ProbeState* result); -bool root_probe(Position& pos, Search::RootMoves& rootMoves, bool rule50); +bool root_probe(Position& pos, Search::RootMoves& rootMoves, bool rule50, bool rankDTZ); bool root_probe_wdl(Position& pos, Search::RootMoves& rootMoves, bool rule50); -Config rank_root_moves(const OptionsMap& options, Position& pos, Search::RootMoves& rootMoves); +Config rank_root_moves(const OptionsMap& options, + Position& pos, + Search::RootMoves& rootMoves, + bool rankDTZ = false); } // namespace Stockfish::Tablebases From c40dd26cbce89cf15055acac75800da6a9721307 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 6 Jul 2024 17:31:54 +0200 Subject: [PATCH 1628/1766] CI give creditials for the clang-format action following up from earlier changes closes https://github.com/official-stockfish/Stockfish/pull/5450 No functional change --- .github/workflows/clang-format.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 637cfc0d826..630edbf93fe 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -19,7 +19,6 @@ jobs: - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} - persist-credentials: false - name: Run clang-format style check uses: jidicula/clang-format-action@f62da5e3d3a2d88ff364771d9d938773a618ab5e # @v4.11.0 From d212e663bb00226f861f3046b36a5d8a3a127865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Sat, 6 Jul 2024 12:16:38 +0200 Subject: [PATCH 1629/1766] Introduction evaluation grain of 16 (and randomize) This patch uses an evaluation grain of 16 in order to get more cutoffs in the alpha-beta algorithm. For a discussion of the efficiency of alpha-beta related to changes in the number of discrete values of terminal nodes, see for instance section 9.1.2 of Judea Pearl's classical book "Heuristics" : https://mat.uab.cat/~alseda/MasterOpt/Judea_Pearl-Heuristics_Intelligent_Search_Strategies_for_Computer_Problem_Solving.pdf Moreover, we add a small (-1, +1) random component after the quantification to help the search exploration a little bit. This is similar in spirit to the (-1, +1) random component already present in the function draw_value() to make Stockfish more robust in draw evaluations. passed STC: LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 220960 W: 57249 L: 56668 D: 107043 Ptnml(0-2): 499, 26017, 56882, 26568, 514 https://tests.stockfishchess.org/tests/view/668907fb7edfb6f233f999f8 passed LTC : LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 48966 W: 12574 L: 12233 D: 24159 Ptnml(0-2): 14, 5233, 13654, 5562, 20 https://tests.stockfishchess.org/tests/view/6689105659cb3228a47598bf closes https://github.com/official-stockfish/Stockfish/pull/5449 bench: 1336007 --- src/evaluate.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 4e895fd366f..44890a361af 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -85,6 +85,9 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, int material = 554 * pos.count() + pos.non_pawn_material(); v = (nnue * (73921 + material) + optimism * (8112 + material)) / 73260; + // Evaluation grain (to get more alpha-beta cuts) with randomization (for robustness) + v = (v / 16) * 16 - 1 + (pos.key() & 0x2); + // Damp down the evaluation linearly when shuffling v -= v * pos.rule50_count() / 212; From daa9e217ab59a090a6344738505edbdfcd09a700 Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Sat, 6 Jul 2024 10:43:35 +0800 Subject: [PATCH 1630/1766] VVLTC search tune Passed VVLTC 1st sprt: https://tests.stockfishchess.org/tests/view/6688af640c9d7c1ab33ed327 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 16050 W: 4200 L: 3959 D: 7891 Ptnml(0-2): 0, 1383, 5018, 1624, 0 Passed VVLTC 2nd sprt: https://tests.stockfishchess.org/tests/view/6688e8900c9d7c1ab33efd60 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 44044 W: 11303 L: 10999 D: 21742 Ptnml(0-2): 1, 3973, 13772, 4273, 3 closes https://github.com/official-stockfish/Stockfish/pull/5444 Bench: 992058 --- src/search.cpp | 92 +++++++++++++++++++++++++------------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 023e5113460..0863013e870 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -70,8 +70,8 @@ static constexpr double EvalLevel[10] = {0.981, 0.956, 0.895, 0.949, 0.913, // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) { - Value futilityMult = 109 - 40 * noTtCutNode; - Value improvingDeduction = 59 * improving * futilityMult / 32; + Value futilityMult = 122 - 37 * noTtCutNode; + Value improvingDeduction = 58 * improving * futilityMult / 32; Value worseningDeduction = oppWorsening * futilityMult / 3; return futilityMult * d - improvingDeduction - worseningDeduction; @@ -84,15 +84,15 @@ constexpr int futility_move_count(bool improving, Depth depth) { // Add correctionHistory value to raw staticEval and guarantee evaluation does not hit the tablebase range Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { auto cv = w.correctionHistory[pos.side_to_move()][pawn_structure_index(pos)]; - v += cv / 10; + v += cv * std::abs(cv) / 5073; return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } // History and stats update bonus, based on depth -int stat_bonus(Depth d) { return std::clamp(191 * d - 285, 20, 1412); } +int stat_bonus(Depth d) { return std::clamp(190 * d - 298, 20, 1596); } // History and stats update malus, based on depth -int stat_malus(Depth d) { return (d < 4 ? 727 * d - 260 : 1908); } +int stat_malus(Depth d) { return (d < 4 ? 736 * d - 268 : 2044); } // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(size_t nodes) { return VALUE_DRAW - 1 + Value(nodes & 0x2); } @@ -324,12 +324,12 @@ void Search::Worker::iterative_deepening() { // Reset aspiration window starting size Value avg = rootMoves[pvIdx].averageScore; - delta = 9 + avg * avg / 10182; + delta = 9 + avg * avg / 10424; alpha = std::max(avg - delta, -VALUE_INFINITE); beta = std::min(avg + delta, VALUE_INFINITE); // Adjust optimism based on root move's averageScore (~4 Elo) - optimism[us] = 127 * avg / (std::abs(avg) + 86); + optimism[us] = 125 * avg / (std::abs(avg) + 89); optimism[~us] = -optimism[us]; // Start with a small aspiration window and, in the case of a fail @@ -513,17 +513,17 @@ void Search::Worker::clear() { counterMoves.fill(Move::none()); mainHistory.fill(0); captureHistory.fill(-700); - pawnHistory.fill(-1193); + pawnHistory.fill(-1188); correctionHistory.fill(0); for (bool inCheck : {false, true}) for (StatsType c : {NoCaptures, Captures}) for (auto& to : continuationHistory[inCheck][c]) for (auto& h : to) - h->fill(-56); + h->fill(-58); for (size_t i = 1; i < reductions.size(); ++i) - reductions[i] = int((19.26 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); + reductions[i] = int((18.62 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); refreshTable.clear(networks[numaAccessToken]); } @@ -759,7 +759,7 @@ Value Search::Worker::search( // Use static evaluation difference to improve quiet move ordering (~9 Elo) if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture) { - int bonus = std::clamp(-10 * int((ss - 1)->staticEval + ss->staticEval), -1590, 1371) + 800; + int bonus = std::clamp(-10 * int((ss - 1)->staticEval + ss->staticEval), -1664, 1471) + 752; thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) thisThread->pawnHistory[pawn_structure_index(pos)][pos.piece_on(prevSq)][prevSq] @@ -780,7 +780,7 @@ Value Search::Worker::search( // Step 7. Razoring (~1 Elo) // If eval is really low check with qsearch if it can exceed alpha, if it can't, // return a fail low. - if (eval < alpha - 512 - 293 * depth * depth) + if (eval < alpha - 494 - 290 * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha && std::abs(value) < VALUE_TB_WIN_IN_MAX_PLY) @@ -791,22 +791,22 @@ Value Search::Worker::search( // The depth condition is important for mate finding. if (!ss->ttPv && depth < 13 && eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening) - - (ss - 1)->statScore / 263 + - (ss - 1)->statScore / 260 >= beta && eval >= beta && (!ttData.move || ttCapture) && beta > VALUE_TB_LOSS_IN_MAX_PLY && eval < VALUE_TB_WIN_IN_MAX_PLY) return beta + (eval - beta) / 3; // Step 9. Null move search with verification search (~35 Elo) - if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 14369 - && eval >= beta && ss->staticEval >= beta - 21 * depth + 393 && !excludedMove + if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 14389 + && eval >= beta && ss->staticEval >= beta - 21 * depth + 390 && !excludedMove && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly && beta > VALUE_TB_LOSS_IN_MAX_PLY) { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and eval - Depth R = std::min(int(eval - beta) / 197, 6) + depth / 3 + 5; + Depth R = std::min(int(eval - beta) / 202, 6) + depth / 3 + 5; ss->currentMove = Move::null(); ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -852,13 +852,13 @@ Value Search::Worker::search( // For cutNodes, if depth is high enough, decrease depth by 2 if there is no ttMove, or // by 1 if there is a ttMove with an upper bound. - if (cutNode && depth >= 8 && (!ttData.move || ttData.bound == BOUND_UPPER)) + if (cutNode && depth >= 7 && (!ttData.move || ttData.bound == BOUND_UPPER)) depth -= 1 + !ttData.move; // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search returns a value // much above beta, we can (almost) safely prune the previous move. - probCutBeta = beta + 177 - 57 * improving; + probCutBeta = beta + 184 - 53 * improving; if ( !PvNode && depth > 3 && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY @@ -937,7 +937,7 @@ Value Search::Worker::search( moves_loop: // When in check, search starts here // Step 12. A small Probcut idea, when we are in check (~4 Elo) - probCutBeta = beta + 388; + probCutBeta = beta + 390; if (ss->inCheck && (ttData.bound & BOUND_LOWER) && ttData.depth >= depth - 4 && ttData.value >= probCutBeta && std::abs(ttData.value) < VALUE_TB_WIN_IN_MAX_PLY && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) @@ -1011,7 +1011,7 @@ Value Search::Worker::search( // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold (~8 Elo) moveCountPruning = moveCount >= futility_move_count(improving, depth) - - (singularBound == BOUND_UPPER && singularValue < alpha - 50); + - (singularBound == BOUND_UPPER && singularValue < alpha - 51); // Reduced depth of the next LMR search int lmrDepth = newDepth - r; @@ -1025,15 +1025,15 @@ Value Search::Worker::search( // Futility pruning for captures (~2 Elo) if (!givesCheck && lmrDepth < 7 && !ss->inCheck) { - Value futilityValue = ss->staticEval + 294 + 246 * lmrDepth + Value futilityValue = ss->staticEval + 285 + 251 * lmrDepth + PieceValue[capturedPiece] + captHist / 7; if (futilityValue <= alpha) continue; } // SEE based pruning for captures and checks (~11 Elo) - int seeHist = std::clamp(captHist / 32, -180 * depth, 163 * depth); - if (!pos.see_ge(move, -163 * depth - seeHist)) + int seeHist = std::clamp(captHist / 32, -182 * depth, 166 * depth); + if (!pos.see_ge(move, -168 * depth - seeHist)) continue; } else @@ -1044,15 +1044,15 @@ Value Search::Worker::search( + thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()]; // Continuation history based pruning (~2 Elo) - if (lmrDepth < 6 && history < -3899 * depth) + if (lmrDepth < 6 && history < -4165 * depth) continue; history += 2 * thisThread->mainHistory[us][move.from_to()]; - lmrDepth += history / 4040; + lmrDepth += history / 3853; Value futilityValue = - ss->staticEval + (bestValue < ss->staticEval - 51 ? 135 : 56) + 140 * lmrDepth; + ss->staticEval + (bestValue < ss->staticEval - 51 ? 143 : 52) + 135 * lmrDepth; // Futility pruning: parent node (~13 Elo) if (!ss->inCheck && lmrDepth < 12 && futilityValue <= alpha) @@ -1089,11 +1089,11 @@ Value Search::Worker::search( // margins scale well. if (!rootNode && move == ttData.move && !excludedMove - && depth >= 4 - (thisThread->completedDepth > 35) + ss->ttPv + && depth >= 4 - (thisThread->completedDepth > 36) + ss->ttPv && std::abs(ttData.value) < VALUE_TB_WIN_IN_MAX_PLY && (ttData.bound & BOUND_LOWER) && ttData.depth >= depth - 3) { - Value singularBeta = ttData.value - (52 + 80 * (ss->ttPv && !PvNode)) * depth / 64; + Value singularBeta = ttData.value - (54 + 76 * (ss->ttPv && !PvNode)) * depth / 64; Depth singularDepth = newDepth / 2; ss->excludedMove = move; @@ -1104,13 +1104,13 @@ Value Search::Worker::search( if (value < singularBeta) { - int doubleMargin = 290 * PvNode - 200 * !ttCapture; - int tripleMargin = 107 + 247 * PvNode - 278 * !ttCapture + 99 * ss->ttPv; + int doubleMargin = 293 * PvNode - 195 * !ttCapture; + int tripleMargin = 107 + 259 * PvNode - 260 * !ttCapture + 98 * ss->ttPv; extension = 1 + (value < singularBeta - doubleMargin) + (value < singularBeta - tripleMargin); - depth += ((!PvNode) && (depth < 18)); + depth += ((!PvNode) && (depth < 16)); } // Multi-cut pruning @@ -1140,7 +1140,7 @@ Value Search::Worker::search( else if (PvNode && move.to_sq() == prevSq && thisThread->captureHistory[movedPiece][move.to_sq()] [type_of(pos.piece_on(move.to_sq()))] - > 3922) + > 3994) extension = 1; } @@ -1197,10 +1197,10 @@ Value Search::Worker::search( ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + (*contHist[0])[movedPiece][move.to_sq()] - + (*contHist[1])[movedPiece][move.to_sq()] - 4747; + + (*contHist[1])[movedPiece][move.to_sq()] - 4664; // Decrease/increase reduction for moves with a good/bad history (~8 Elo) - r -= ss->statScore / 11125; + r -= ss->statScore / 10898; // Step 17. Late moves reduction / extension (LMR, ~117 Elo) if (depth >= 2 && moveCount > 1 + rootNode) @@ -1343,7 +1343,7 @@ Value Search::Worker::search( else { // Reduce other moves if we have found at least one score improvement (~2 Elo) - if (depth > 2 && depth < 13 && std::abs(value) < VALUE_TB_WIN_IN_MAX_PLY) + if (depth > 2 && depth < 14 && std::abs(value) < VALUE_TB_WIN_IN_MAX_PLY) depth -= 2; assert(depth > 0); @@ -1386,13 +1386,13 @@ Value Search::Worker::search( // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (113 * (depth > 5) + 118 * (PvNode || cutNode) + 119 * ((ss - 1)->moveCount > 8) - + 64 * (!ss->inCheck && bestValue <= ss->staticEval - 107) - + 147 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 75)); + int bonus = (114 * (depth > 5) + 116 * (PvNode || cutNode) + 123 * ((ss - 1)->moveCount > 8) + + 64 * (!ss->inCheck && bestValue <= ss->staticEval - 108) + + 153 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 76)); // Proportional to "how much damage we have to undo" - if ((ss - 1)->statScore < -7850) - bonus += std::clamp(-(ss - 1)->statScore / 100, 0, 224); + if ((ss - 1)->statScore < -7865) + bonus += std::clamp(-(ss - 1)->statScore / 103, 0, 258); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus / 100); @@ -1564,7 +1564,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, if (bestValue > alpha) alpha = bestValue; - futilityBase = ss->staticEval + 294; + futilityBase = ss->staticEval + 299; } const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, @@ -1636,11 +1636,11 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, + (*contHist[1])[pos.moved_piece(move)][move.to_sq()] + thisThread->pawnHistory[pawn_structure_index(pos)][pos.moved_piece(move)] [move.to_sq()] - <= 4452) + <= 4643) continue; // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, -74)) + if (!pos.see_ge(move, -83)) continue; } @@ -1706,7 +1706,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth Search::Worker::reduction(bool i, Depth d, int mn, int delta) const { int reductionScale = reductions[d] * reductions[mn]; - return (reductionScale + 1236 - delta * 746 / rootDelta) / 1024 + (!i && reductionScale > 1326); + return (reductionScale + 1274 - delta * 746 / rootDelta) / 1024 + (!i && reductionScale > 1293); } // elapsed() returns the time elapsed since the search started. If the @@ -1809,7 +1809,7 @@ void update_all_stats(const Position& pos, if (!pos.capture_stage(bestMove)) { - int bestMoveBonus = bestValue > beta + 166 ? quietMoveBonus // larger bonus + int bestMoveBonus = bestValue > beta + 172 ? quietMoveBonus // larger bonus : stat_bonus(depth); // smaller bonus update_quiet_stats(pos, ss, workerThread, bestMove, bestMoveBonus); @@ -1847,7 +1847,7 @@ void update_all_stats(const Position& pos, // by moves at ply -1, -2, -3, -4, and -6 with current move. void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { - bonus = bonus * 51 / 64; + bonus = bonus * 52 / 64; for (int i : {1, 2, 3, 4, 6}) { From a45c2bc34ae03dd35402e6cf26d515bae1425517 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Mon, 1 Jul 2024 17:08:22 -0700 Subject: [PATCH 1631/1766] Simplify Away Countermove Heuristic Passed Non-regression STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 977824 W: 252072 L: 252888 D: 472864 Ptnml(0-2): 2518, 117120, 250560, 116088, 2626 https://tests.stockfishchess.org/tests/view/6683452d95b0d1e881e81541 Passed Non-regression LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 81048 W: 20630 L: 20470 D: 39948 Ptnml(0-2): 36, 8915, 22464, 9071, 38 https://tests.stockfishchess.org/tests/view/66886b7b0c9d7c1ab33ed281 closes https://github.com/official-stockfish/Stockfish/pull/5441 bench 1276784 --- src/movepick.cpp | 7 +------ src/movepick.h | 5 ----- src/search.cpp | 18 ++++-------------- src/search.h | 1 - 4 files changed, 5 insertions(+), 26 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 52e8c526a10..05f57ae79b6 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -91,7 +91,6 @@ MovePicker::MovePicker(const Position& p, const CapturePieceToHistory* cph, const PieceToHistory** ch, const PawnHistory* ph, - Move cm, const Move* killers) : pos(p), mainHistory(mh), @@ -99,7 +98,7 @@ MovePicker::MovePicker(const Position& p, continuationHistory(ch), pawnHistory(ph), ttMove(ttm), - refutations{{killers[0], 0}, {killers[1], 0}, {cm, 0}}, + refutations{{killers[0], 0}, {killers[1], 0}}, depth(d) { assert(d > 0); @@ -273,10 +272,6 @@ Move MovePicker::next_move(bool skipQuiets) { cur = std::begin(refutations); endMoves = std::end(refutations); - // If the countermove is the same as a killer, skip it - if (refutations[0] == refutations[2] || refutations[1] == refutations[2]) - --endMoves; - ++stage; [[fallthrough]]; diff --git a/src/movepick.h b/src/movepick.h index b81f76e18f2..8a2e0145d0d 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -118,10 +118,6 @@ enum StatsType { // see www.chessprogramming.org/Butterfly_Boards (~11 elo) using ButterflyHistory = Stats; -// CounterMoveHistory stores counter moves indexed by [piece][to] of the previous -// move, see www.chessprogramming.org/Countermove_Heuristic -using CounterMoveHistory = Stats; - // CapturePieceToHistory is addressed by a move's [piece][to][captured piece type] using CapturePieceToHistory = Stats; @@ -164,7 +160,6 @@ class MovePicker { const CapturePieceToHistory*, const PieceToHistory**, const PawnHistory*, - Move, const Move*); MovePicker(const Position&, Move, diff --git a/src/search.cpp b/src/search.cpp index 0863013e870..cd29176eb2c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -125,7 +125,7 @@ Value value_to_tt(Value v, int ply); Value value_from_tt(Value v, int ply, int r50c); void update_pv(Move* pv, Move move, const Move* childPv); void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus); -void update_refutations(const Position& pos, Stack* ss, Search::Worker& workerThread, Move move); +void update_refutations(Stack* ss, Move move); void update_quiet_histories( const Position& pos, Stack* ss, Search::Worker& workerThread, Move move, int bonus); void update_quiet_stats( @@ -510,7 +510,6 @@ void Search::Worker::iterative_deepening() { } void Search::Worker::clear() { - counterMoves.fill(Move::none()); mainHistory.fill(0); captureHistory.fill(-700); pawnHistory.fill(-1188); @@ -950,11 +949,9 @@ Value Search::Worker::search( nullptr, (ss - 6)->continuationHistory}; - Move countermove = - prevSq != SQ_NONE ? thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] : Move::none(); MovePicker mp(pos, ttData.move, depth, &thisThread->mainHistory, &thisThread->captureHistory, - contHist, &thisThread->pawnHistory, countermove, ss->killers); + contHist, &thisThread->pawnHistory, ss->killers); value = bestValue; moveCountPruning = false; @@ -1860,7 +1857,7 @@ void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { } // Updates move sorting heuristics -void update_refutations(const Position& pos, Stack* ss, Search::Worker& workerThread, Move move) { +void update_refutations(Stack* ss, Move move) { // Update killers if (ss->killers[0] != move) @@ -1868,13 +1865,6 @@ void update_refutations(const Position& pos, Stack* ss, Search::Worker& workerTh ss->killers[1] = ss->killers[0]; ss->killers[0] = move; } - - // Update countermove history - if (((ss - 1)->currentMove).is_ok()) - { - Square prevSq = ((ss - 1)->currentMove).to_sq(); - workerThread.counterMoves[pos.piece_on(prevSq)][prevSq] = move; - } } void update_quiet_histories( @@ -1893,7 +1883,7 @@ void update_quiet_histories( void update_quiet_stats( const Position& pos, Stack* ss, Search::Worker& workerThread, Move move, int bonus) { - update_refutations(pos, ss, workerThread, move); + update_refutations(ss, move); update_quiet_histories(pos, ss, workerThread, move, bonus); } diff --git a/src/search.h b/src/search.h index e8e33b1a819..122cd549e3c 100644 --- a/src/search.h +++ b/src/search.h @@ -247,7 +247,6 @@ class Worker { bool is_mainthread() const { return threadIdx == 0; } // Public because they need to be updatable by the stats - CounterMoveHistory counterMoves; ButterflyHistory mainHistory; CapturePieceToHistory captureHistory; ContinuationHistory continuationHistory[2][2]; From ec8288fe0d81be31084cf4609767466b04458ec7 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sat, 6 Jul 2024 14:26:31 +0300 Subject: [PATCH 1632/1766] Simplify away eval in TM Passed STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 40160 W: 10523 L: 10309 D: 19328 Ptnml(0-2): 129, 4543, 10524, 4753, 131 https://tests.stockfishchess.org/tests/view/6685ab8b99271ae49479dbe9 Passed LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 195672 W: 49681 L: 49639 D: 96352 Ptnml(0-2): 112, 20976, 55597, 21060, 91 https://tests.stockfishchess.org/tests/view/6686f27a7092ade1206f7889 closes https://github.com/official-stockfish/Stockfish/pull/5445 No functional change --- src/search.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index cd29176eb2c..2fd38e50a5c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -65,9 +65,6 @@ using namespace Search; namespace { -static constexpr double EvalLevel[10] = {0.981, 0.956, 0.895, 0.949, 0.913, - 0.942, 0.933, 0.890, 0.984, 0.941}; - // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) { Value futilityMult = 122 - 37 * noTtCutNode; @@ -463,11 +460,10 @@ void Search::Worker::iterative_deepening() { timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.495 : 0.687; double reduction = (1.48 + mainThread->previousTimeReduction) / (2.17 * timeReduction); double bestMoveInstability = 1 + 1.88 * totBestMoveChanges / threads.size(); - int el = std::clamp((bestValue + 750) / 150, 0, 9); double recapture = limits.capSq == rootMoves[0].pv[0].to_sq() ? 0.955 : 1.005; double totalTime = mainThread->tm.optimum() * fallingEval * reduction - * bestMoveInstability * EvalLevel[el] * recapture; + * bestMoveInstability * recapture; // Cap used time in case of a single legal move for a better viewer experience if (rootMoves.size() == 1) From 24ab46c5110d6f5c587f4929e23a13983babb759 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sat, 6 Jul 2024 04:45:37 -0700 Subject: [PATCH 1633/1766] Non-functional Fixes & Updates Fixes a missing default slot for dbg_extremes of, removes a extra newline, and updates SE elo estimate from https://tests.stockfishchess.org/tests/view/664ebd1e928b1fb18de4e4b7 while we are at it. closes https://github.com/official-stockfish/Stockfish/pull/5446 No functional change --- src/misc.h | 3 +-- src/search.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/misc.h b/src/misc.h index 0184ab88c00..ce49a1f6553 100644 --- a/src/misc.h +++ b/src/misc.h @@ -67,8 +67,7 @@ std::optional read_file_to_string(const std::string& path); void dbg_hit_on(bool cond, int slot = 0); void dbg_mean_of(int64_t value, int slot = 0); void dbg_stdev_of(int64_t value, int slot = 0); -void dbg_extremes_of(int64_t value, int slot); - +void dbg_extremes_of(int64_t value, int slot = 0); void dbg_correl_of(int64_t value1, int64_t value2, int slot = 0); void dbg_print(); diff --git a/src/search.cpp b/src/search.cpp index 2fd38e50a5c..2bdcd25a3f7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -462,8 +462,8 @@ void Search::Worker::iterative_deepening() { double bestMoveInstability = 1 + 1.88 * totBestMoveChanges / threads.size(); double recapture = limits.capSq == rootMoves[0].pv[0].to_sq() ? 0.955 : 1.005; - double totalTime = mainThread->tm.optimum() * fallingEval * reduction - * bestMoveInstability * recapture; + double totalTime = + mainThread->tm.optimum() * fallingEval * reduction * bestMoveInstability * recapture; // Cap used time in case of a single legal move for a better viewer experience if (rootMoves.size() == 1) @@ -1068,8 +1068,8 @@ Value Search::Worker::search( // We take care to not overdo to avoid search getting stuck. if (ss->ply < thisThread->rootDepth * 2) { - // Singular extension search (~94 Elo). If all moves but one fail low on a - // search of (alpha-s, beta-s), and just one fails high on (alpha, beta), + // Singular extension search (~76 Elo, ~170 nElo). If all moves but one fail + // low on a search of (alpha-s, beta-s), and just one fails high on (alpha, beta), // then that move is singular and should be extended. To verify this we do // a reduced search on the position excluding the ttMove and if the result // is lower than ttValue minus a margin, then we will extend the ttMove. From 55cb235d47afe8422cc781970ef69790387f42bc Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Sat, 6 Jul 2024 19:07:42 +0800 Subject: [PATCH 1634/1766] Simplify internal iterative reductions This is a revert of cc992e5. This patch is based on consistent observations that decreasing depth more in IIR generally has a bad scaling behaviour (good at STC, bad at longer time controls). Simplification STC: https://tests.stockfishchess.org/tests/view/6689266659cb3228a4759b26 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 96992 W: 24977 L: 24824 D: 47191 Ptnml(0-2): 251, 11497, 24851, 11642, 255 Simplification LTC: https://tests.stockfishchess.org/tests/view/668930ffe59d990b103f6ab1 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 35808 W: 9185 L: 8980 D: 17643 Ptnml(0-2): 25, 3776, 10101, 3973, 29 closes https://github.com/official-stockfish/Stockfish/pull/5447 Bench: 1097766 --- src/search.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 2bdcd25a3f7..576e1f9048d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -835,11 +835,8 @@ Value Search::Worker::search( // Step 10. Internal iterative reductions (~9 Elo) // For PV nodes without a ttMove, we decrease depth. - // Additionally, if the current position is found in the TT - // and the stored depth in the TT is greater than or equal to - // current search depth, we decrease search depth even further. if (PvNode && !ttData.move) - depth -= 3 + (ss->ttHit && ttData.depth >= depth); + depth -= 3; // Use qsearch if depth <= 0. if (depth <= 0) From 4d6e1225bd409c72a9b966c3008cf99a804c5026 Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Sat, 6 Jul 2024 19:08:28 +0800 Subject: [PATCH 1635/1766] Simplify ttPv reduction formula This is a revert of 2046c92. This patch is based on the fact that the ttPv reduction has proven non-linear scaling (as documented in the code, along with testing guidelines); however, the original patch had (erroneously) not been tested at VLTC or longer. Simplification STC: https://tests.stockfishchess.org/tests/view/6689266e59cb3228a4759b28 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 100320 W: 25913 L: 25763 D: 48644 Ptnml(0-2): 270, 11842, 25750, 12064, 234 Simplification LTC: https://tests.stockfishchess.org/tests/view/66893103e59d990b103f6ab3 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 57078 W: 14466 L: 14282 D: 28330 Ptnml(0-2): 34, 6214, 15851, 6414, 26 closes https://github.com/official-stockfish/Stockfish/pull/5448 Bench: 1124658 --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 576e1f9048d..cb0340ece84 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1158,8 +1158,7 @@ Value Search::Worker::search( // Decrease reduction if position is or has been on the PV (~7 Elo) if (ss->ttPv) - r -= 1 + (ttData.value > alpha) + (ttData.depth >= depth) - - (PvNode && ttData.value < alpha && ttData.depth >= depth); + r -= 1 + (ttData.value > alpha) + (ttData.depth >= depth); // Decrease reduction for PvNodes (~0 Elo on STC, ~2 Elo on LTC) if (PvNode) From b1f522930d58118a6035870fe7d02b3d82681ec8 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Thu, 4 Jul 2024 23:39:10 -0700 Subject: [PATCH 1636/1766] Simplify Away Move Count Pruning Adjustment Using Singular Search Result Passed Non-regression STC: LLR: 3.01 (-2.94,2.94) <-1.75,0.25> Total: 62688 W: 16319 L: 16121 D: 30248 Ptnml(0-2): 196, 7317, 16104, 7547, 180 https://tests.stockfishchess.org/tests/view/66879bf51b527f04dd477ff9 Passed Non-regression LTC: LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 116502 W: 29504 L: 29379 D: 57619 Ptnml(0-2): 66, 12881, 32226, 13018, 60 https://tests.stockfishchess.org/tests/view/6688629e0c9d7c1ab33ed030 closes https://github.com/official-stockfish/Stockfish/pull/5442 bench 1207930 --- src/search.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index cb0340ece84..ffe6e04b1ae 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -559,12 +559,11 @@ Value Search::Worker::search( Key posKey; Move move, excludedMove, bestMove; Depth extension, newDepth; - Value bestValue, value, eval, maxValue, probCutBeta, singularValue; + Value bestValue, value, eval, maxValue, probCutBeta; bool givesCheck, improving, priorCapture, opponentWorsening; bool capture, moveCountPruning, ttCapture; Piece movedPiece; int moveCount, captureCount, quietCount; - Bound singularBound; // Step 1. Initialize node Worker* thisThread = this; @@ -948,8 +947,6 @@ Value Search::Worker::search( value = bestValue; moveCountPruning = false; - singularValue = VALUE_INFINITE; - singularBound = BOUND_NONE; // Step 13. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. @@ -999,9 +996,7 @@ Value Search::Worker::search( if (!rootNode && pos.non_pawn_material(us) && bestValue > VALUE_TB_LOSS_IN_MAX_PLY) { // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold (~8 Elo) - moveCountPruning = - moveCount >= futility_move_count(improving, depth) - - (singularBound == BOUND_UPPER && singularValue < alpha - 51); + moveCountPruning = moveCount >= futility_move_count(improving, depth); // Reduced depth of the next LMR search int lmrDepth = newDepth - r; @@ -1087,9 +1082,8 @@ Value Search::Worker::search( Depth singularDepth = newDepth / 2; ss->excludedMove = move; - value = singularValue = + value = search(pos, ss, singularBeta - 1, singularBeta, singularDepth, cutNode); - singularBound = singularValue >= singularBeta ? BOUND_LOWER : BOUND_UPPER; ss->excludedMove = Move::none(); if (value < singularBeta) From b79ac764ff1662b40d5480595bafb599b72512eb Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Mon, 1 Jul 2024 21:57:53 -0700 Subject: [PATCH 1637/1766] Simplify in-check condition for Probcut-in-check dont let your memes be dreams !? Passed Non-regression STC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 512000 W: 132193 L: 132497 D: 247310 Ptnml(0-2): 1600, 61170, 130704, 60986, 1540 https://tests.stockfishchess.org/tests/view/66838911c4f539faa03268a2 Passed Non-regression LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 380268 W: 95894 L: 96042 D: 188332 Ptnml(0-2): 193, 42861, 104180, 42701, 199 https://tests.stockfishchess.org/tests/view/6688d0550c9d7c1ab33ed5a8 closes https://github.com/official-stockfish/Stockfish/pull/5443 Bench: 1130282 --- src/search.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ffe6e04b1ae..1bce9daad2b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -927,10 +927,9 @@ Value Search::Worker::search( moves_loop: // When in check, search starts here - // Step 12. A small Probcut idea, when we are in check (~4 Elo) + // Step 12. A small Probcut idea (~4 Elo) probCutBeta = beta + 390; - if (ss->inCheck && (ttData.bound & BOUND_LOWER) && ttData.depth >= depth - 4 - && ttData.value >= probCutBeta && std::abs(ttData.value) < VALUE_TB_WIN_IN_MAX_PLY + if ((ttData.bound & BOUND_LOWER) && ttData.depth >= depth - 4 && ttData.value >= probCutBeta && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) return probCutBeta; From 2d3ef434b4009fcc9e198b508f00957c2d05eb1e Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sat, 6 Jul 2024 03:44:28 -0700 Subject: [PATCH 1638/1766] Tweak LMR at Root Passed STC: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 328192 W: 84751 L: 84014 D: 159427 Ptnml(0-2): 758, 38802, 84253, 39511, 772 https://tests.stockfishchess.org/tests/view/6689203959cb3228a4759a49 Passed LTC: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 56748 W: 14494 L: 14136 D: 28118 Ptnml(0-2): 19, 6089, 15803, 6441, 22 https://tests.stockfishchess.org/tests/view/66892d76e59d990b103f6626 closes https://github.com/official-stockfish/Stockfish/pull/5452 Bench 1253593 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 1bce9daad2b..9d0b5627cf4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1185,7 +1185,7 @@ Value Search::Worker::search( r -= ss->statScore / 10898; // Step 17. Late moves reduction / extension (LMR, ~117 Elo) - if (depth >= 2 && moveCount > 1 + rootNode) + if (depth >= 2 && moveCount > 1 + (rootNode && depth < 10)) { // In general we want to cap the LMR depth search at newDepth, but when // reduction is negative, we allow this move a limited search extension From bb9b65408ffe0f71eb60760e05c5d599300173da Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Sat, 6 Jul 2024 13:41:11 -0400 Subject: [PATCH 1639/1766] Simplify improving deduction in futility margin Passed non-regression STC: https://tests.stockfishchess.org/tests/view/668981d4df142e108ffc9bb4 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 312672 W: 80280 L: 80363 D: 152029 Ptnml(0-2): 729, 37198, 80529, 37187, 693 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/668988c6df142e108ffca042 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 126042 W: 31971 L: 31857 D: 62214 Ptnml(0-2): 50, 13988, 34832, 14100, 51 closes https://github.com/official-stockfish/Stockfish/pull/5454 bench 1100483 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 9d0b5627cf4..153eba188c8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -68,7 +68,7 @@ namespace { // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) { Value futilityMult = 122 - 37 * noTtCutNode; - Value improvingDeduction = 58 * improving * futilityMult / 32; + Value improvingDeduction = improving * futilityMult * 2; Value worseningDeduction = oppWorsening * futilityMult / 3; return futilityMult * d - improvingDeduction - worseningDeduction; From 75c8cb2c2f7d687d3ba02eac2088860b625acd47 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 6 Jul 2024 22:21:33 +0300 Subject: [PATCH 1640/1766] Adjust usage of previous statscore in bonus assignments This patch adjusts usage of previous statscore for bonus assginments - allowing it for any statscores and clamping it to wider range. Passed STC: https://tests.stockfishchess.org/tests/view/66892e76e59d990b103f6a91 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 431520 W: 111767 L: 110872 D: 208881 Ptnml(0-2): 1180, 51165, 110133, 52144, 1138 Passed LTC: https://tests.stockfishchess.org/tests/view/66897176e59d990b103f9605 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 143184 W: 36463 L: 35929 D: 70792 Ptnml(0-2): 55, 15540, 39863, 16084, 50 closes https://github.com/official-stockfish/Stockfish/pull/5455 bench 1330556 --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 153eba188c8..9b0ea9df860 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1373,8 +1373,7 @@ Value Search::Worker::search( + 153 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 76)); // Proportional to "how much damage we have to undo" - if ((ss - 1)->statScore < -7865) - bonus += std::clamp(-(ss - 1)->statScore / 103, 0, 258); + bonus += std::clamp(-(ss - 1)->statScore / 100, -50, 274); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus / 100); From 4e9fded5a63a2a72964f6d3518e2f66186662d05 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Sat, 6 Jul 2024 16:04:07 -0400 Subject: [PATCH 1641/1766] Larger bonus when updating quiet stats Also removes unused arguments to update_all_stats to fix compiler warnings about unused parameters. Passed non-regression STC: https://tests.stockfishchess.org/tests/view/6689a79a0fdd852d63cf52e9 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 26496 W: 6901 L: 6669 D: 12926 Ptnml(0-2): 62, 3094, 6715, 3304, 73 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/6689a9960fdd852d63cf532d LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 41214 W: 10373 L: 10173 D: 20668 Ptnml(0-2): 11, 4491, 11412, 4673, 20 closes https://github.com/official-stockfish/Stockfish/pull/5456 bench 1169958 --- src/search.cpp | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 9b0ea9df860..3f1600474d4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -131,8 +131,6 @@ void update_all_stats(const Position& pos, Stack* ss, Search::Worker& workerThread, Move bestMove, - Value bestValue, - Value beta, Square prevSq, Move* quietsSearched, int quietCount, @@ -1362,8 +1360,8 @@ Value Search::Worker::search( // If there is a move that produces search value greater than alpha we update the stats of searched moves else if (bestMove) - update_all_stats(pos, ss, *this, bestMove, bestValue, beta, prevSq, quietsSearched, - quietCount, capturesSearched, captureCount, depth); + update_all_stats(pos, ss, *this, bestMove, prevSq, quietsSearched, quietCount, + capturesSearched, captureCount, depth); // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) @@ -1772,8 +1770,6 @@ void update_all_stats(const Position& pos, Stack* ss, Search::Worker& workerThread, Move bestMove, - Value bestValue, - Value beta, Square prevSq, Move* quietsSearched, int quietCount, @@ -1790,10 +1786,7 @@ void update_all_stats(const Position& pos, if (!pos.capture_stage(bestMove)) { - int bestMoveBonus = bestValue > beta + 172 ? quietMoveBonus // larger bonus - : stat_bonus(depth); // smaller bonus - - update_quiet_stats(pos, ss, workerThread, bestMove, bestMoveBonus); + update_quiet_stats(pos, ss, workerThread, bestMove, quietMoveBonus); // Decrease stats for all non-best quiet moves for (int i = 0; i < quietCount; ++i) From cdb0b96e0725bb9aafc0ca9aecfebdae32eede8f Mon Sep 17 00:00:00 2001 From: MinetaS Date: Sun, 7 Jul 2024 08:27:43 +0900 Subject: [PATCH 1642/1766] Clean up refutations array in MovePicker This is a follow-up cleanup to a45c2bc34ae03dd35402e6cf26d515bae1425517. closes https://github.com/official-stockfish/Stockfish/pull/5458 No functional change --- src/movepick.cpp | 9 +++------ src/movepick.h | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 05f57ae79b6..f6f9f0dc895 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -297,9 +297,8 @@ Move MovePicker::next_move(bool skipQuiets) { [[fallthrough]]; case GOOD_QUIET : - if (!skipQuiets && select([&]() { - return *cur != refutations[0] && *cur != refutations[1] && *cur != refutations[2]; - })) + if (!skipQuiets + && select([&]() { return *cur != refutations[0] && *cur != refutations[1]; })) { if ((cur - 1)->value > -7998 || (cur - 1)->value <= quiet_threshold(depth)) return *(cur - 1); @@ -328,9 +327,7 @@ Move MovePicker::next_move(bool skipQuiets) { case BAD_QUIET : if (!skipQuiets) - return select([&]() { - return *cur != refutations[0] && *cur != refutations[1] && *cur != refutations[2]; - }); + return select([&]() { return *cur != refutations[0] && *cur != refutations[1]; }); return Move::none(); diff --git a/src/movepick.h b/src/movepick.h index 8a2e0145d0d..2564f730190 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -185,7 +185,7 @@ class MovePicker { const PieceToHistory** continuationHistory; const PawnHistory* pawnHistory; Move ttMove; - ExtMove refutations[3], *cur, *endMoves, *endBadCaptures, *beginBadQuiets, *endBadQuiets; + ExtMove refutations[2], *cur, *endMoves, *endBadCaptures, *beginBadQuiets, *endBadQuiets; int stage; int threshold; Depth depth; From 5752529cabb3270e055147709ff0847e4d59ec22 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Sat, 6 Jul 2024 09:31:35 -0400 Subject: [PATCH 1643/1766] Update default main net to nn-74f1d263ae9a.nnue Created by setting output weights (256) and biases (8) of the previous main net nn-ddcfb9224cdb.nnue to values found around 12k / 120k spsa games at 120+1.2 This used modified fishtest dev workers to construct .nnue files from spsa params, then load them with EvalFile when running tests: https://github.com/linrock/fishtest/tree/spsa-file-modified-nnue/worker Inspired by researching loading spsa params from files: https://github.com/official-stockfish/fishtest/pull/1926 Scripts for modifying nnue files and preparing params: https://github.com/linrock/nnue-pytorch/tree/no-gpu-modify-nnue spsa params: weights: [-127, 127], c_end = 6 biases: [-8192, 8192], c_end = 64 Example of reading output weights and biases from the previous main net using nnue-pytorch and printing spsa params in a format compatible with fishtest: ``` import features from serialize import NNUEReader feature_set = features.get_feature_set_from_name("HalfKAv2_hm") with open("nn-ddcfb9224cdb.nnue", "rb") as f: model = NNUEReader(f, feature_set).model c_end_weights = 6 c_end_biases = 64 for i in range(8): for j in range(32): value = round(int(model.layer_stacks.output.weight[i, j] * 600 * 16) / 127) print(f"oW[{i}][{j}],{value},-127,127,{c_end_weights},0.0020") for i in range(8): value = int(model.layer_stacks.output.bias[i] * 600 * 16) print(f"oB[{i}],{value},-8192,8192,{c_end_biases},0.0020") ``` For more info on spsa tuning params in nets: https://github.com/official-stockfish/Stockfish/pull/5149 https://github.com/official-stockfish/Stockfish/pull/5254 Passed STC: https://tests.stockfishchess.org/tests/view/66894d64e59d990b103f8a37 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 32000 W: 8443 L: 8137 D: 15420 Ptnml(0-2): 80, 3627, 8309, 3875, 109 Passed LTC: https://tests.stockfishchess.org/tests/view/6689668ce59d990b103f8b8b LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 172176 W: 43822 L: 43225 D: 85129 Ptnml(0-2): 97, 18821, 47633, 19462, 75 closes https://github.com/official-stockfish/Stockfish/pull/5459 bench 1120091 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index bdef9ceb620..047c4a56bc3 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -33,7 +33,7 @@ namespace Eval { // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro or the location where this macro is defined, as it is used // in the Makefile/Fishtest. -#define EvalFileDefaultNameBig "nn-ddcfb9224cdb.nnue" +#define EvalFileDefaultNameBig "nn-74f1d263ae9a.nnue" #define EvalFileDefaultNameSmall "nn-37f18f62d772.nnue" namespace NNUE { From 5d3517c601c64d026824251784dd44f0cbf14873 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 7 Jul 2024 11:23:50 +0200 Subject: [PATCH 1644/1766] Fix output for GUI Fritz 19 can hang with the current way to provide output, i.e. too much output in a short time for a mate / depth 245 found quickly. fallout from 25361e514bffb81284d4311601a9f7a4a7ced79b closes https://github.com/official-stockfish/Stockfish/pull/5460 No functional change --- src/search.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 3f1600474d4..d22761e3ef0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -355,9 +355,10 @@ void Search::Worker::iterative_deepening() { break; // When failing high/low give some update before a re-search. - // To avoid excessive output, only start at rootDepth > 30. + // To avoid excessive output that could hang GUIs like Fritz 19, only start + // at nodes > 10M (rather than depth N, which can be reached quickly) if (mainThread && multiPV == 1 && (bestValue <= alpha || bestValue >= beta) - && rootDepth > 30) + && nodes > 10000000) main_manager()->pv(*this, threads, tt, rootDepth); // In case of failing low/high increase aspiration window and @@ -388,7 +389,7 @@ void Search::Worker::iterative_deepening() { std::stable_sort(rootMoves.begin() + pvFirst, rootMoves.begin() + pvIdx + 1); if (mainThread - && (threads.stop || pvIdx + 1 == multiPV || rootDepth > 30) + && (threads.stop || pvIdx + 1 == multiPV || nodes > 10000000) // A thread that aborted search can have mated-in/TB-loss PV and score // that cannot be trusted, i.e. it can be delayed or refuted if we would have // had time to fully search other root-moves. Thus we suppress this output and @@ -968,7 +969,7 @@ Value Search::Worker::search( ss->moveCount = ++moveCount; - if (rootNode && is_mainthread() && rootDepth > 30) + if (rootNode && is_mainthread() && nodes > 10000000) { main_manager()->updates.onIter( {depth, UCIEngine::move(move, pos.is_chess960()), moveCount + thisThread->pvIdx}); From eac2d080a3279358a79e35bfcecf016d01db97e4 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sun, 7 Jul 2024 22:25:10 +0300 Subject: [PATCH 1645/1766] Further simplify stat bonuses Based on recent simplification by linrock Since it completely removed any special bonuses based on values difference and made it flat stat_bonus(depth + 1) I got an idea that we might as well remove all (depth + 1) bonuses and make them usual depth bonuses. Also removes weird negative bonus for depth 1 as a side effect. Passed STC: https://tests.stockfishchess.org/tests/view/6689d817eca84f4d25863746 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 18080 W: 4789 L: 4552 D: 8739 Ptnml(0-2): 46, 1987, 4727, 2244, 36 Passed LTC: https://tests.stockfishchess.org/tests/view/6689daa4eca84f4d258639d7 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 109062 W: 27548 L: 27417 D: 54097 Ptnml(0-2): 58, 11983, 30293, 12164, 33 Passed direct LTC vs linrock patch: https://tests.stockfishchess.org/tests/view/668a46f8eca84f4d25866fe9 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 100002 W: 25351 L: 25209 D: 49442 Ptnml(0-2): 54, 11119, 27529, 11229, 70 closes https://github.com/official-stockfish/Stockfish/pull/5461 Bench 1175744 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index d22761e3ef0..ac0e59b5e7a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -86,7 +86,7 @@ Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { } // History and stats update bonus, based on depth -int stat_bonus(Depth d) { return std::clamp(190 * d - 298, 20, 1596); } +int stat_bonus(Depth d) { return std::min(190 * d - 108, 1596); } // History and stats update malus, based on depth int stat_malus(Depth d) { return (d < 4 ? 736 * d - 268 : 2044); } @@ -1782,7 +1782,7 @@ void update_all_stats(const Position& pos, Piece moved_piece = pos.moved_piece(bestMove); PieceType captured; - int quietMoveBonus = stat_bonus(depth + 1); + int quietMoveBonus = stat_bonus(depth); int quietMoveMalus = stat_malus(depth); if (!pos.capture_stage(bestMove)) From acd0a933ad5beacaafeb89025b2e60802e993e43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Tue, 9 Jul 2024 00:48:40 +0200 Subject: [PATCH 1646/1766] Fix compilation on Apple MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Always use the posix function posix_memalign() as aligned memory allocator on Apple computers. This should allow to compile Stockfish out of the box on all versions of Mac OS X. Patch tested on the following systems (apart from the CI) : • Mac OS 10.9.6 (arch x86-64-sse41-popcnt) with gcc-10 • Mac OS 10.13.6 (arch x86-64-bmi2) with gcc-10, gcc-14 and clang-11 • Mac OS 14.1.1 (arch apple-silicon) with clang-15 closes https://github.com/official-stockfish/Stockfish/pull/5462 No functional change --- src/memory.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/memory.cpp b/src/memory.cpp index 565b39b2061..1769a6617b9 100644 --- a/src/memory.cpp +++ b/src/memory.cpp @@ -68,12 +68,12 @@ namespace Stockfish { // does not guarantee the availability of aligned_alloc(). Memory allocated with // std_aligned_alloc() must be freed with std_aligned_free(). void* std_aligned_alloc(size_t alignment, size_t size) { - // Apple requires 10.15, which is enforced in the makefile -#if defined(_ISOC11_SOURCE) || defined(__APPLE__) +#if defined(_ISOC11_SOURCE) return aligned_alloc(alignment, size); #elif defined(POSIXALIGNEDALLOC) - void* mem; - return posix_memalign(&mem, alignment, size) ? nullptr : mem; + void* mem = nullptr; + posix_memalign(&mem, alignment, size); + return mem; #elif defined(_WIN32) && !defined(_M_ARM) && !defined(_M_ARM64) return _mm_malloc(size, alignment); #elif defined(_WIN32) From 4880ed4ad177f2cec50cfc784a6cbb65d31ff4ef Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sat, 6 Jul 2024 20:07:01 -0700 Subject: [PATCH 1647/1766] Simplify Probcut Malus Passed Non-regression STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 74880 W: 19261 L: 19083 D: 36536 Ptnml(0-2): 202, 8861, 19120, 9071, 186 https://tests.stockfishchess.org/tests/view/668a0966eca84f4d25864cba Passed Non-regression LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 263916 W: 66690 L: 66718 D: 130508 Ptnml(0-2): 125, 29348, 73040, 29320, 125 https://tests.stockfishchess.org/tests/view/668a17e3eca84f4d25864e91 closes https://github.com/official-stockfish/Stockfish/pull/5464 bench 1331408 --- src/search.cpp | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ac0e59b5e7a..3e6c56a696d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -861,8 +861,6 @@ Value Search::Worker::search( assert(probCutBeta < VALUE_INFINITE && probCutBeta > beta); MovePicker mp(pos, ttData.move, probCutBeta - ss->staticEval, &thisThread->captureHistory); - Move probcutCapturesSearched[32]; - int probcutCaptureCount = 0; Piece captured; while ((move = mp.next_move()) != Move::none()) @@ -900,25 +898,12 @@ Value Search::Worker::search( thisThread->captureHistory[movedPiece][move.to_sq()][type_of(captured)] << stat_bonus(depth - 2); - for (int i = 0; i < probcutCaptureCount; i++) - { - movedPiece = pos.moved_piece(probcutCapturesSearched[i]); - captured = pos.piece_on(probcutCapturesSearched[i].to_sq()); - - thisThread->captureHistory[movedPiece][probcutCapturesSearched[i].to_sq()] - [type_of(captured)] - << -stat_malus(depth - 3); - } - // Save ProbCut data into transposition table ttWriter.write(posKey, value_to_tt(value, ss->ply), ss->ttPv, BOUND_LOWER, depth - 3, move, unadjustedStaticEval, tt.generation()); return std::abs(value) < VALUE_TB_WIN_IN_MAX_PLY ? value - (probCutBeta - beta) : value; } - - if (probcutCaptureCount < 32) - probcutCapturesSearched[probcutCaptureCount++] = move; } Eval::NNUE::hint_common_parent_position(pos, networks[numaAccessToken], refreshTable); From b209f14b1ee0cda8cbd7fa3a8349e65701d1869f Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Sun, 7 Jul 2024 15:13:40 -0400 Subject: [PATCH 1648/1766] Update default main net to nn-e8bac1c07a5a.nnue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Created by modifying L2 weights from the previous main net (nn-74f1d263ae9a.nnue) with params found by spsa around 9k / 120k games at 120+1.2. 370 spsa params - L2 weights in nn-74f1d263ae9a.nnue where |val| >= 50 A: 6000, alpha: 0.602, gamma: 0.101 weights: [-127, 127], c_end = 6 To print the spsa params with nnue-pytorch: ``` import features from serialize import NNUEReader feature_set = features.get_feature_set_from_name("HalfKAv2_hm") with open("nn-74f1d263ae9a.nnue", "rb") as f: model = NNUEReader(f, feature_set).model c_end = 6 for i in range(8): for j in range(32): for k in range(30): value = int(model.layer_stacks.l2.weight[32 * i + j, k] * 64) if abs(value) >= 50: print(f"twoW[{i}][{j}][{k}],{value},-127,127,{c_end},0.0020") ``` Among the 370 params, 229 weights were changed. avg change: 0.0961 ± 1.67 range: [-4, 3] The number of weights changed, grouped by layer stack index, shows more weights were modified in the lower piece count buckets: [54, 52, 29, 23, 22, 18, 14, 17] Found with the same method described in: https://github.com/official-stockfish/Stockfish/pull/5459 Passed STC: https://tests.stockfishchess.org/tests/view/668aec9a58083e5fd88239e7 LLR: 3.00 (-2.94,2.94) <0.00,2.00> Total: 52384 W: 13569 L: 13226 D: 25589 Ptnml(0-2): 127, 6141, 13335, 6440, 149 Passed LTC: https://tests.stockfishchess.org/tests/view/668af50658083e5fd8823a0b LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 46974 W: 12006 L: 11668 D: 23300 Ptnml(0-2): 25, 4992, 13121, 5318, 31 closes https://github.com/official-stockfish/Stockfish/pull/5466 bench 1300471 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 047c4a56bc3..4b5f447ee32 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -33,7 +33,7 @@ namespace Eval { // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro or the location where this macro is defined, as it is used // in the Makefile/Fishtest. -#define EvalFileDefaultNameBig "nn-74f1d263ae9a.nnue" +#define EvalFileDefaultNameBig "nn-e8bac1c07a5a.nnue" #define EvalFileDefaultNameSmall "nn-37f18f62d772.nnue" namespace NNUE { From 362a77a3450335e1940020c080bf3b7b361c594a Mon Sep 17 00:00:00 2001 From: yl25946 Date: Tue, 9 Jul 2024 00:53:04 -0500 Subject: [PATCH 1649/1766] Move Loop Consistency in Probcut In probcut move loop, everything is enclosed within a large if statement. I've changed it to guard clauses to stay consistent with other move loops. closes https://github.com/official-stockfish/Stockfish/pull/5463 No functional change --- AUTHORS | 1 + src/search.cpp | 69 +++++++++++++++++++++++++++----------------------- 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/AUTHORS b/AUTHORS index 6eefb56dd5f..6957682f494 100644 --- a/AUTHORS +++ b/AUTHORS @@ -129,6 +129,7 @@ Kojirion Krystian Kuzniarek (kuzkry) Leonardo Ljubičić (ICCF World Champion) Leonid Pechenik (lp--) +Li Ying (yl25946) Liam Keegan (lkeegan) Linmiao Xu (linrock) Linus Arver (listx) diff --git a/src/search.cpp b/src/search.cpp index 3e6c56a696d..47c5dc88242 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -864,47 +864,54 @@ Value Search::Worker::search( Piece captured; while ((move = mp.next_move()) != Move::none()) - if (move != excludedMove && pos.legal(move)) - { - assert(pos.capture_stage(move)); + { + assert(move.is_ok()); - movedPiece = pos.moved_piece(move); - captured = pos.piece_on(move.to_sq()); + if (move == excludedMove) + continue; + // Check for legality + if (!pos.legal(move)) + continue; - // Prefetch the TT entry for the resulting position - prefetch(tt.first_entry(pos.key_after(move))); + assert(pos.capture_stage(move)); - ss->currentMove = move; - ss->continuationHistory = - &this - ->continuationHistory[ss->inCheck][true][pos.moved_piece(move)][move.to_sq()]; + movedPiece = pos.moved_piece(move); + captured = pos.piece_on(move.to_sq()); - thisThread->nodes.fetch_add(1, std::memory_order_relaxed); - pos.do_move(move, st); - // Perform a preliminary qsearch to verify that the move holds - value = -qsearch(pos, ss + 1, -probCutBeta, -probCutBeta + 1); + // Prefetch the TT entry for the resulting position + prefetch(tt.first_entry(pos.key_after(move))); - // If the qsearch held, perform the regular search - if (value >= probCutBeta) - value = -search(pos, ss + 1, -probCutBeta, -probCutBeta + 1, depth - 4, - !cutNode); + ss->currentMove = move; + ss->continuationHistory = + &this->continuationHistory[ss->inCheck][true][pos.moved_piece(move)][move.to_sq()]; - pos.undo_move(move); + thisThread->nodes.fetch_add(1, std::memory_order_relaxed); + pos.do_move(move, st); - if (value >= probCutBeta) - { - thisThread->captureHistory[movedPiece][move.to_sq()][type_of(captured)] - << stat_bonus(depth - 2); - - // Save ProbCut data into transposition table - ttWriter.write(posKey, value_to_tt(value, ss->ply), ss->ttPv, BOUND_LOWER, - depth - 3, move, unadjustedStaticEval, tt.generation()); - return std::abs(value) < VALUE_TB_WIN_IN_MAX_PLY ? value - (probCutBeta - beta) - : value; - } + // Perform a preliminary qsearch to verify that the move holds + value = -qsearch(pos, ss + 1, -probCutBeta, -probCutBeta + 1); + + // If the qsearch held, perform the regular search + if (value >= probCutBeta) + value = + -search(pos, ss + 1, -probCutBeta, -probCutBeta + 1, depth - 4, !cutNode); + + pos.undo_move(move); + + if (value >= probCutBeta) + { + thisThread->captureHistory[movedPiece][move.to_sq()][type_of(captured)] + << stat_bonus(depth - 2); + + // Save ProbCut data into transposition table + ttWriter.write(posKey, value_to_tt(value, ss->ply), ss->ttPv, BOUND_LOWER, + depth - 3, move, unadjustedStaticEval, tt.generation()); + return std::abs(value) < VALUE_TB_WIN_IN_MAX_PLY ? value - (probCutBeta - beta) + : value; } + } Eval::NNUE::hint_common_parent_position(pos, networks[numaAccessToken], refreshTable); } From 98a7bb4436f04505a17f37befab0207252e97897 Mon Sep 17 00:00:00 2001 From: Disservin Date: Wed, 10 Jul 2024 17:56:43 +0200 Subject: [PATCH 1650/1766] CI give correct permissions for the clang-format action closes https://github.com/official-stockfish/Stockfish/pull/5470 No functional change --- .github/workflows/clang-format.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 630edbf93fe..452c2f2a30f 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -11,6 +11,10 @@ on: paths: - "**.cpp" - "**.h" + +permissions: + pull-requests: write + jobs: Clang-Format: name: Clang-Format @@ -39,6 +43,7 @@ jobs: _(execution **${{ github.run_id }}** / attempt **${{ github.run_attempt }}**)_ comment_tag: execution + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Comment on PR if: steps.clang-format.outcome != 'failure' @@ -49,3 +54,4 @@ jobs: create_if_not_exists: false comment_tag: execution mode: delete + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 7e72b37e4ce3351399bb0ac08686bd84cdc4fba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Wed, 10 Jul 2024 14:51:48 +0200 Subject: [PATCH 1651/1766] Clean up comments in code - Capitalize comments - Reformat multi-lines comments to equalize the widths of the lines - Try to keep the width of comments around 85 characters - Remove periods at the end of single-line comments closes https://github.com/official-stockfish/Stockfish/pull/5469 No functional change --- src/engine.h | 2 +- src/memory.cpp | 26 ++- src/memory.h | 25 +-- src/misc.cpp | 19 +- src/movepick.cpp | 8 +- src/nnue/nnue_feature_transformer.h | 40 +++-- src/numa.h | 203 +++++++++++---------- src/position.h | 4 +- src/search.cpp | 270 ++++++++++++++-------------- src/search.h | 9 +- src/thread.cpp | 31 ++-- src/types.h | 30 ++-- 12 files changed, 356 insertions(+), 311 deletions(-) diff --git a/src/engine.h b/src/engine.h index 0d6f0f2b826..127f7d7c84d 100644 --- a/src/engine.h +++ b/src/engine.h @@ -49,7 +49,7 @@ class Engine { Engine(std::string path = ""); - // Can't be movable due to components holding backreferences to fields + // Cannot be movable due to components holding backreferences to fields Engine(const Engine&) = delete; Engine(Engine&&) = delete; Engine& operator=(const Engine&) = delete; diff --git a/src/memory.cpp b/src/memory.cpp index 1769a6617b9..ae303c5377a 100644 --- a/src/memory.cpp +++ b/src/memory.cpp @@ -49,10 +49,12 @@ #include // std::cerr #include // std::endl #include + // The needed Windows API for processor groups could be missed from old Windows // versions, so instead of calling them directly (forcing the linker to resolve // the calls at compile time), try to load them at runtime. To do this we need // first to define the corresponding function pointers. + extern "C" { using OpenProcessToken_t = bool (*)(HANDLE, DWORD, PHANDLE); using LookupPrivilegeValueA_t = bool (*)(LPCSTR, LPCSTR, PLUID); @@ -64,9 +66,10 @@ using AdjustTokenPrivileges_t = namespace Stockfish { -// Wrapper for systems where the c++17 implementation -// does not guarantee the availability of aligned_alloc(). Memory allocated with -// std_aligned_alloc() must be freed with std_aligned_free(). +// Wrappers for systems where the c++17 implementation does not guarantee the +// availability of aligned_alloc(). Memory allocated with std_aligned_alloc() +// must be freed with std_aligned_free(). + void* std_aligned_alloc(size_t alignment, size_t size) { #if defined(_ISOC11_SOURCE) return aligned_alloc(alignment, size); @@ -96,7 +99,8 @@ void std_aligned_free(void* ptr) { #endif } -// aligned_large_pages_alloc() will return suitably aligned memory, if possible using large pages. +// aligned_large_pages_alloc() will return suitably aligned memory, +// if possible using large pages. #if defined(_WIN32) @@ -135,6 +139,7 @@ static void* aligned_large_pages_alloc_windows([[maybe_unused]] size_t allocSize return nullptr; // We need SeLockMemoryPrivilege, so try to enable it for the process + if (!OpenProcessToken_f( // OpenProcessToken() GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken)) return nullptr; @@ -149,8 +154,10 @@ static void* aligned_large_pages_alloc_windows([[maybe_unused]] size_t allocSize tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - // Try to enable SeLockMemoryPrivilege. Note that even if AdjustTokenPrivileges() succeeds, - // we still need to query GetLastError() to ensure that the privileges were actually obtained. + // Try to enable SeLockMemoryPrivilege. Note that even if AdjustTokenPrivileges() + // succeeds, we still need to query GetLastError() to ensure that the privileges + // were actually obtained. + if (AdjustTokenPrivileges_f(hProcessToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &prevTp, &prevTpLen) && GetLastError() == ERROR_SUCCESS) @@ -189,9 +196,9 @@ void* aligned_large_pages_alloc(size_t allocSize) { void* aligned_large_pages_alloc(size_t allocSize) { #if defined(__linux__) - constexpr size_t alignment = 2 * 1024 * 1024; // assumed 2MB page size + constexpr size_t alignment = 2 * 1024 * 1024; // 2MB page size assumed #else - constexpr size_t alignment = 4096; // assumed small page size + constexpr size_t alignment = 4096; // small page size assumed #endif // Round up to multiples of alignment @@ -206,7 +213,8 @@ void* aligned_large_pages_alloc(size_t allocSize) { #endif -// aligned_large_pages_free() will free the previously allocated ttmem +// aligned_large_pages_free() will free the previously memory allocated +// by aligned_large_pages_alloc(). The effect is a nop if mem == nullptr. #if defined(_WIN32) diff --git a/src/memory.h b/src/memory.h index ad7ca6025b3..3155a5aab12 100644 --- a/src/memory.h +++ b/src/memory.h @@ -33,13 +33,13 @@ namespace Stockfish { void* std_aligned_alloc(size_t alignment, size_t size); void std_aligned_free(void* ptr); -// memory aligned by page size, min alignment: 4096 bytes + +// Memory aligned by page size, min alignment: 4096 bytes void* aligned_large_pages_alloc(size_t size); -// nop if mem == nullptr -void aligned_large_pages_free(void* mem); +void aligned_large_pages_free(void* mem); -// frees memory which was placed there with placement new. -// works for both single objects and arrays of unknown bound +// Frees memory which was placed there with placement new. +// Works for both single objects and arrays of unknown bound. template void memory_deleter(T* ptr, FREE_FUNC free_func) { if (!ptr) @@ -53,15 +53,15 @@ void memory_deleter(T* ptr, FREE_FUNC free_func) { return; } -// frees memory which was placed there with placement new. -// works for both single objects and arrays of unknown bound +// Frees memory which was placed there with placement new. +// Works for both single objects and arrays of unknown bound. template void memory_deleter_array(T* ptr, FREE_FUNC free_func) { if (!ptr) return; - // Move back on the pointer to where the size is allocated. + // Move back on the pointer to where the size is allocated const size_t array_offset = std::max(sizeof(size_t), alignof(T)); char* raw_memory = reinterpret_cast(ptr) - array_offset; @@ -77,7 +77,7 @@ void memory_deleter_array(T* ptr, FREE_FUNC free_func) { free_func(raw_memory); } -// Allocates memory for a single object and places it there with placement new. +// Allocates memory for a single object and places it there with placement new template inline std::enable_if_t, T*> memory_allocator(ALLOC_FUNC alloc_func, Args&&... args) { @@ -86,7 +86,7 @@ inline std::enable_if_t, T*> memory_allocator(ALLOC_FUNC all return new (raw_memory) T(std::forward(args)...); } -// Allocates memory for an array of unknown bound and places it there with placement new. +// Allocates memory for an array of unknown bound and places it there with placement new template inline std::enable_if_t, std::remove_extent_t*> memory_allocator(ALLOC_FUNC alloc_func, size_t num) { @@ -94,7 +94,7 @@ memory_allocator(ALLOC_FUNC alloc_func, size_t num) { const size_t array_offset = std::max(sizeof(size_t), alignof(ElementType)); - // save the array size in the memory location + // Save the array size in the memory location char* raw_memory = reinterpret_cast(alloc_func(array_offset + num * sizeof(ElementType))); ASSERT_ALIGNED(raw_memory, alignof(T)); @@ -104,7 +104,8 @@ memory_allocator(ALLOC_FUNC alloc_func, size_t num) { for (size_t i = 0; i < num; ++i) new (raw_memory + array_offset + i * sizeof(ElementType)) ElementType(); - // Need to return the pointer at the start of the array so that the indexing in unique_ptr works + // Need to return the pointer at the start of the array so that + // the indexing in unique_ptr works. return reinterpret_cast(raw_memory + array_offset); } diff --git a/src/misc.cpp b/src/misc.cpp index b68c12b97f6..664ab4b89ff 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -112,14 +112,16 @@ class Logger { // Returns the full name of the current Stockfish version. -// For local dev compiles we try to append the commit sha and commit date -// from git if that fails only the local compilation date is set and "nogit" is specified: -// Stockfish dev-YYYYMMDD-SHA -// or -// Stockfish dev-YYYYMMDD-nogit +// +// For local dev compiles we try to append the commit SHA and +// commit date from git. If that fails only the local compilation +// date is set and "nogit" is specified: +// Stockfish dev-YYYYMMDD-SHA +// or +// Stockfish dev-YYYYMMDD-nogit // // For releases (non-dev builds) we only include the version number: -// Stockfish version +// Stockfish version std::string engine_info(bool to_uci) { std::stringstream ss; ss << "Stockfish " << version << std::setfill('0'); @@ -131,8 +133,9 @@ std::string engine_info(bool to_uci) { ss << stringify(GIT_DATE); #else constexpr std::string_view months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); - std::string month, day, year; - std::stringstream date(__DATE__); // From compiler, format is "Sep 21 2008" + + std::string month, day, year; + std::stringstream date(__DATE__); // From compiler, format is "Sep 21 2008" date >> month >> day >> year; ss << year << std::setw(2) << std::setfill('0') << (1 + months.find(month) / 4) diff --git a/src/movepick.cpp b/src/movepick.cpp index f6f9f0dc895..c21b14a9089 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -59,8 +59,8 @@ enum Stages { QCHECK }; -// Sort moves in descending order up to and including -// a given limit. The order of moves smaller than the limit is left unspecified. +// Sort moves in descending order up to and including a given limit. +// The order of moves smaller than the limit is left unspecified. void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) { for (ExtMove *sortedEnd = begin, *p = begin + 1; p < end; ++p) @@ -125,8 +125,8 @@ MovePicker::MovePicker(const Position& p, stage = (pos.checkers() ? EVASION_TT : QSEARCH_TT) + !(ttm && pos.pseudo_legal(ttm)); } -// Constructor for ProbCut: we generate captures with SEE greater -// than or equal to the given threshold. +// Constructor for ProbCut: we generate captures with SEE greater than or equal +// to the given threshold. MovePicker::MovePicker(const Position& p, Move ttm, int th, const CapturePieceToHistory* cph) : pos(p), captureHistory(cph), diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 483b84a8704..ad0fb1b4d93 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -354,13 +354,18 @@ class FeatureTransformer { for (IndexType j = 0; j < NumOutputChunks; ++j) { - // What we want to do is multiply inputs in a pairwise manner (after clipping), and then shift right by 9. - // Instead, we shift left by 7, and use mulhi, stripping the bottom 16 bits, effectively shifting right by 16, - // resulting in a net shift of 9 bits. We use mulhi because it maintains the sign of the multiplication (unlike mullo), - // allowing us to make use of packus to clip 2 of the inputs, resulting in a save of 2 "vec_max_16" calls. - // A special case is when we use NEON, where we shift left by 6 instead, because the instruction "vqdmulhq_s16" - // also doubles the return value after the multiplication, adding an extra shift to the left by 1, so we - // compensate by shifting less before the multiplication. + // What we want to do is multiply inputs in a pairwise manner + // (after clipping), and then shift right by 9. Instead, we + // shift left by 7, and use mulhi, stripping the bottom 16 bits, + // effectively shifting right by 16, resulting in a net shift + // of 9 bits. We use mulhi because it maintains the sign of + // the multiplication (unlike mullo), allowing us to make use + // of packus to clip 2 of the inputs, resulting in a save of 2 + // "vec_max_16" calls. A special case is when we use NEON, + // where we shift left by 6 instead, because the instruction + // "vqdmulhq_s16" also doubles the return value after the + // multiplication, adding an extra shift to the left by 1, so + // we compensate by shifting less before the multiplication. #if defined(USE_SSE2) constexpr int shift = 7; @@ -426,10 +431,10 @@ class FeatureTransformer { } // NOTE: The parameter states_to_update is an array of position states. - // All states must be sequential, that is states_to_update[i] must either be reachable - // by repeatedly applying ->previous from states_to_update[i+1]. - // computed_st must be reachable by repeatedly applying ->previous on - // states_to_update[0]. + // All states must be sequential, that is states_to_update[i] must + // either be reachable by repeatedly applying ->previous from + // states_to_update[i+1], and computed_st must be reachable by + // repeatedly applying ->previous on states_to_update[0]. template void update_accumulator_incremental(const Position& pos, StateInfo* computed_st, @@ -446,7 +451,7 @@ class FeatureTransformer { #ifdef VECTOR // Gcc-10.2 unnecessarily spills AVX2 registers if this array - // is defined in the VECTOR code below, once in each branch + // is defined in the VECTOR code below, once in each branch. vec_t acc[NumRegs]; psqt_vec_t psqt[NumPsqtRegs]; #endif @@ -474,7 +479,8 @@ class FeatureTransformer { StateInfo* st = computed_st; - // Now update the accumulators listed in states_to_update[], where the last element is a sentinel. + // Now update the accumulators listed in states_to_update[], + // where the last element is a sentinel. #ifdef VECTOR if (N == 1 && (removed[0].size() == 1 || removed[0].size() == 2) && added[0].size() == 1) @@ -794,7 +800,7 @@ class FeatureTransformer { } // The accumulator of the refresh entry has been updated. - // Now copy its content to the actual accumulator we were refreshing + // Now copy its content to the actual accumulator we were refreshing. std::memcpy(accumulator.accumulation[Perspective], entry.accumulation, sizeof(BiasType) * HalfDimensions); @@ -827,7 +833,7 @@ class FeatureTransformer { if ((oldest_st->*accPtr).computed[Perspective]) { - // Only update current position accumulator to minimize work. + // Only update current position accumulator to minimize work StateInfo* states_to_update[1] = {pos.state()}; update_accumulator_incremental(pos, oldest_st, states_to_update); } @@ -846,8 +852,8 @@ class FeatureTransformer { if (next == nullptr) return; - // Now update the accumulators listed in states_to_update[], where the last element is a sentinel. - // Currently we update 2 accumulators. + // Now update the accumulators listed in states_to_update[], where + // the last element is a sentinel. Currently we update two accumulators: // 1. for the current position // 2. the next accumulator after the computed one // The heuristic may change in the future. diff --git a/src/numa.h b/src/numa.h index fd9abd4db7f..3de8281d152 100644 --- a/src/numa.h +++ b/src/numa.h @@ -37,8 +37,8 @@ #include "memory.h" -// We support linux very well, but we explicitly do NOT support Android, because there's -// no affected systems, not worth maintaining. +// We support linux very well, but we explicitly do NOT support Android, +// because there is no affected systems, not worth maintaining. #if defined(__linux__) && !defined(__ANDROID__) #if !defined(_GNU_SOURCE) #define _GNU_SOURCE @@ -81,9 +81,9 @@ using NumaIndex = size_t; inline CpuIndex get_hardware_concurrency() { CpuIndex concurrency = std::thread::hardware_concurrency(); - // Get all processors across all processor groups on windows, since ::hardware_concurrency - // only returns the number of processors in the first group, because only these - // are available to std::thread. + // Get all processors across all processor groups on windows, since + // hardware_concurrency() only returns the number of processors in + // the first group, because only these are available to std::thread. #ifdef _WIN64 concurrency = std::max(concurrency, GetActiveProcessorCount(ALL_PROCESSOR_GROUPS)); #endif @@ -101,7 +101,7 @@ struct WindowsAffinity { // We also provide diagnostic for when the affinity is set to nullopt // whether it was due to being indeterminate. If affinity is indeterminate - // it's best to assume it is not set at all, so consistent with the meaning + // it is best to assume it is not set at all, so consistent with the meaning // of the nullopt affinity. bool isNewDeterminate = true; bool isOldDeterminate = true; @@ -119,23 +119,25 @@ struct WindowsAffinity { } // Since Windows 11 and Windows Server 2022 thread affinities can span - // processor groups and can be set as such by a new WinAPI function. - // However, we may need to force using the old API if we detect - // that the process has affinity set by the old API already and we want to override that. - // Due to the limitations of the old API we can't detect its use reliably. - // There will be cases where we detect not use but it has actually been used and vice versa. + // processor groups and can be set as such by a new WinAPI function. However, + // we may need to force using the old API if we detect that the process has + // affinity set by the old API already and we want to override that. Due to the + // limitations of the old API we cannot detect its use reliably. There will be + // cases where we detect not use but it has actually been used and vice versa. + bool likely_used_old_api() const { return oldApi.has_value() || !isOldDeterminate; } }; inline std::pair> get_process_group_affinity() { + // GetProcessGroupAffinity requires the GroupArray argument to be // aligned to 4 bytes instead of just 2. static constexpr size_t GroupArrayMinimumAlignment = 4; static_assert(GroupArrayMinimumAlignment >= alignof(USHORT)); // The function should succeed the second time, but it may fail if the group - // affinity has changed between GetProcessGroupAffinity calls. - // In such case we consider this a hard error, as we can't work with unstable affinities + // affinity has changed between GetProcessGroupAffinity calls. In such case + // we consider this a hard error, as we Cannot work with unstable affinities // anyway. static constexpr int MAX_TRIES = 2; USHORT GroupCount = 1; @@ -165,10 +167,10 @@ inline std::pair> get_process_group_affinity() { } // On Windows there are two ways to set affinity, and therefore 2 ways to get it. -// These are not consistent, so we have to check both. -// In some cases it is actually not possible to determine affinity. -// For example when two different threads have affinity on different processor groups, -// set using SetThreadAffinityMask, we can't retrieve the actual affinities. +// These are not consistent, so we have to check both. In some cases it is actually +// not possible to determine affinity. For example when two different threads have +// affinity on different processor groups, set using SetThreadAffinityMask, we cannot +// retrieve the actual affinities. // From documentation on GetProcessAffinityMask: // > If the calling process contains threads in multiple groups, // > the function returns zero for both affinity masks. @@ -196,8 +198,8 @@ inline WindowsAffinity get_process_affinity() { } else if (RequiredMaskCount > 0) { - // If RequiredMaskCount then these affinities were never set, but it's not consistent - // so GetProcessAffinityMask may still return some affinity. + // If RequiredMaskCount then these affinities were never set, but it's + // not consistent so GetProcessAffinityMask may still return some affinity. auto groupAffinities = std::make_unique(RequiredMaskCount); status = GetThreadSelectedCpuSetMasks_f(GetCurrentThread(), groupAffinities.get(), @@ -233,7 +235,7 @@ inline WindowsAffinity get_process_affinity() { DWORD_PTR proc, sys; status = GetProcessAffinityMask(GetCurrentProcess(), &proc, &sys); - // If proc == 0 then we can't determine affinity because it spans processor groups. + // If proc == 0 then we cannot determine affinity because it spans processor groups. // On Windows 11 and Server 2022 it will instead // > If, however, hHandle specifies a handle to the current process, the function // > always uses the calling thread's primary group (which by default is the same @@ -246,10 +248,12 @@ inline WindowsAffinity get_process_affinity() { return affinity; } - // If SetProcessAffinityMask was never called the affinity - // must span all processor groups, but if it was called it must only span one. + // If SetProcessAffinityMask was never called the affinity must span + // all processor groups, but if it was called it must only span one. + std::vector groupAffinity; // We need to capture this later and capturing // from structured bindings requires c++20. + std::tie(status, groupAffinity) = get_process_group_affinity(); if (status == 0) { @@ -282,11 +286,12 @@ inline WindowsAffinity get_process_affinity() { // If we got here it means that either SetProcessAffinityMask was never set // or we're on Windows 11/Server 2022. - // Since Windows 11 and Windows Server 2022 the behaviour of GetProcessAffinityMask changed - // > If, however, hHandle specifies a handle to the current process, the function - // > always uses the calling thread's primary group (which by default is the same - // > as the process' primary group) in order to set the - // > lpProcessAffinityMask and lpSystemAffinityMask. + // Since Windows 11 and Windows Server 2022 the behaviour of + // GetProcessAffinityMask changed: + // > If, however, hHandle specifies a handle to the current process, + // > the function always uses the calling thread's primary group + // > (which by default is the same as the process' primary group) + // > in order to set the lpProcessAffinityMask and lpSystemAffinityMask. // In which case we can actually retrieve the full affinity. if (GetThreadSelectedCpuSetMasks_f != nullptr) @@ -300,9 +305,11 @@ inline WindowsAffinity get_process_affinity() { const int numActiveProcessors = GetActiveProcessorCount(static_cast(procGroupIndex)); - // We have to schedule to 2 different processors and & the affinities we get. - // Otherwise our processor choice could influence the resulting affinity. - // We assume the processor IDs within the group are filled sequentially from 0. + // We have to schedule to two different processors + // and & the affinities we get. Otherwise our processor + // choice could influence the resulting affinity. + // We assume the processor IDs within the group are + // filled sequentially from 0. uint64_t procCombined = std::numeric_limits::max(); uint64_t sysCombined = std::numeric_limits::max(); @@ -346,8 +353,9 @@ inline WindowsAffinity get_process_affinity() { } } - // We have to detect the case where the affinity was not set, or is set to all processors - // so that we correctly produce as std::nullopt result. + // We have to detect the case where the affinity was not set, + // or is set to all processors so that we correctly produce as + // std::nullopt result. if (!isAffinityFull) { affinity.oldApi = std::move(cpus); @@ -369,16 +377,16 @@ inline std::set get_process_affinity() { std::set cpus; - // For unsupported systems, or in case of a soft error, we may assume all processors - // are available for use. + // For unsupported systems, or in case of a soft error, we may assume + // all processors are available for use. [[maybe_unused]] auto set_to_all_cpus = [&]() { for (CpuIndex c = 0; c < SYSTEM_THREADS_NB; ++c) cpus.insert(c); }; // cpu_set_t by default holds 1024 entries. This may not be enough soon, - // but there is no easy way to determine how many threads there actually is. - // In this case we just choose a reasonable upper bound. + // but there is no easy way to determine how many threads there actually + // is. In this case we just choose a reasonable upper bound. static constexpr CpuIndex MaxNumCpus = 1024 * 64; cpu_set_t* mask = CPU_ALLOC(MaxNumCpus); @@ -437,19 +445,19 @@ class NumaReplicatedAccessToken { NumaIndex n; }; -// Designed as immutable, because there is no good reason to alter an already existing config -// in a way that doesn't require recreating it completely, and it would be complex and expensive -// to maintain class invariants. -// The CPU (processor) numbers always correspond to the actual numbering used by the system. -// The NUMA node numbers MAY NOT correspond to the system's numbering of the NUMA nodes. -// In particular, empty nodes may be removed, or the user may create custom nodes. -// It is guaranteed that NUMA nodes are NOT empty, i.e. every node exposed by NumaConfig -// has at least one processor assigned. +// Designed as immutable, because there is no good reason to alter an already +// existing config in a way that doesn't require recreating it completely, and +// it would be complex and expensive to maintain class invariants. +// The CPU (processor) numbers always correspond to the actual numbering used +// by the system. The NUMA node numbers MAY NOT correspond to the system's +// numbering of the NUMA nodes. In particular, empty nodes may be removed, or +// the user may create custom nodes. It is guaranteed that NUMA nodes are NOT +// empty: every node exposed by NumaConfig has at least one processor assigned. // // We use startup affinities so as not to modify its own behaviour in time. // -// Until Stockfish doesn't support exceptions all places where an exception should be thrown -// are replaced by std::exit. +// Since Stockfish doesn't support exceptions all places where an exception +// should be thrown are replaced by std::exit. class NumaConfig { public: NumaConfig() : @@ -460,9 +468,9 @@ class NumaConfig { } // This function queries the system for the mapping of processors to NUMA nodes. - // On Linux we read from standardized kernel sysfs, with a fallback to single NUMA node. - // On Windows we utilize GetNumaProcessorNodeEx, which has its quirks, see - // comment for Windows implementation of get_process_affinity + // On Linux we read from standardized kernel sysfs, with a fallback to single NUMA + // node. On Windows we utilize GetNumaProcessorNodeEx, which has its quirks, see + // comment for Windows implementation of get_process_affinity. static NumaConfig from_system([[maybe_unused]] bool respectProcessAffinity = true) { NumaConfig cfg = empty(); @@ -479,7 +487,6 @@ class NumaConfig { // On Linux things are straightforward, since there's no processor groups and // any thread can be scheduled on all processors. - // We try to gather this information from the sysfs first // https://www.kernel.org/doc/Documentation/ABI/stable/sysfs-devices-node @@ -504,9 +511,9 @@ class NumaConfig { std::string path = std::string("/sys/devices/system/node/node") + std::to_string(n) + "/cpulist"; auto cpuIdsStr = read_file_to_string(path); - // Now, we only bail if the file does not exist. Some nodes may be empty, that's fine. - // An empty node still has a file that appears to have some whitespace, so we need - // to handle that. + // Now, we only bail if the file does not exist. Some nodes may be + // empty, that's fine. An empty node still has a file that appears + // to have some whitespace, so we need to handle that. if (!cpuIdsStr.has_value()) { fallback(); @@ -538,9 +545,10 @@ class NumaConfig { if (respectProcessAffinity) allowedCpus = STARTUP_PROCESSOR_AFFINITY.get_combined(); - // The affinity can't be determined in all cases on Windows, but we at least guarantee - // that the number of allowed processors is >= number of processors in the affinity mask. - // In case the user is not satisfied they must set the processor numbers explicitly. + // The affinity cannot be determined in all cases on Windows, + // but we at least guarantee that the number of allowed processors + // is >= number of processors in the affinity mask. In case the user + // is not satisfied they must set the processor numbers explicitly. auto is_cpu_allowed = [&allowedCpus](CpuIndex c) { return !allowedCpus.has_value() || allowedCpus->count(c) == 1; }; @@ -711,21 +719,22 @@ class NumaConfig { } bool suggests_binding_threads(CpuIndex numThreads) const { - // If we can reasonably determine that the threads can't be contained + // If we can reasonably determine that the threads cannot be contained // by the OS within the first NUMA node then we advise distributing // and binding threads. When the threads are not bound we can only use // NUMA memory replicated objects from the first node, so when the OS - // has to schedule on other nodes we lose performance. - // We also suggest binding if there's enough threads to distribute among nodes - // with minimal disparity. - // We try to ignore small nodes, in particular the empty ones. + // has to schedule on other nodes we lose performance. We also suggest + // binding if there's enough threads to distribute among nodes with minimal + // disparity. We try to ignore small nodes, in particular the empty ones. - // If the affinity set by the user does not match the affinity given by the OS - // then binding is necessary to ensure the threads are running on correct processors. + // If the affinity set by the user does not match the affinity given by + // the OS then binding is necessary to ensure the threads are running on + // correct processors. if (customAffinity) return true; - // We obviously can't distribute a single thread, so a single thread should never be bound. + // We obviously cannot distribute a single thread, so a single thread + // should never be bound. if (numThreads <= 1) return false; @@ -754,8 +763,8 @@ class NumaConfig { if (nodes.size() == 1) { - // special case for when there's no NUMA nodes - // doesn't buy us much, but let's keep the default path simple + // Special case for when there's no NUMA nodes. This doesn't buy us + // much, but let's keep the default path simple. ns.resize(numThreads, NumaIndex{0}); } else @@ -769,9 +778,11 @@ class NumaConfig { { float fill = static_cast(occupation[n] + 1) / static_cast(nodes[n].size()); - // NOTE: Do we want to perhaps fill the first available node up to 50% first before considering other nodes? - // Probably not, because it would interfere with running multiple instances. We basically shouldn't - // favor any particular node. + // NOTE: Do we want to perhaps fill the first available node + // up to 50% first before considering other nodes? + // Probably not, because it would interfere with running + // multiple instances. We basically shouldn't favor any + // particular node. if (fill < bestNodeFill) { bestNode = n; @@ -816,18 +827,19 @@ class NumaConfig { #elif defined(_WIN64) - // Requires Windows 11. No good way to set thread affinity spanning processor groups before that. + // Requires Windows 11. No good way to set thread affinity spanning + // processor groups before that. HMODULE k32 = GetModuleHandle(TEXT("Kernel32.dll")); auto SetThreadSelectedCpuSetMasks_f = SetThreadSelectedCpuSetMasks_t( (void (*)()) GetProcAddress(k32, "SetThreadSelectedCpuSetMasks")); - // We ALWAYS set affinity with the new API if available, - // because there's no downsides, and we forcibly keep it consistent - // with the old API should we need to use it. I.e. we always keep this as a superset - // of what we set with SetThreadGroupAffinity. + // We ALWAYS set affinity with the new API if available, because + // there's no downsides, and we forcibly keep it consistent with + // the old API should we need to use it. I.e. we always keep this + // as a superset of what we set with SetThreadGroupAffinity. if (SetThreadSelectedCpuSetMasks_f != nullptr) { - // Only available on Windows 11 and Windows Server 2022 onwards. + // Only available on Windows 11 and Windows Server 2022 onwards const USHORT numProcGroups = USHORT( ((highestCpuIndex + 1) + WIN_PROCESSOR_GROUP_SIZE - 1) / WIN_PROCESSOR_GROUP_SIZE); auto groupAffinities = std::make_unique(numProcGroups); @@ -857,22 +869,25 @@ class NumaConfig { // Sometimes we need to force the old API, but do not use it unless necessary. if (SetThreadSelectedCpuSetMasks_f == nullptr || STARTUP_USE_OLD_AFFINITY_API) { - // On earlier windows version (since windows 7) we can't run a single thread + // On earlier windows version (since windows 7) we cannot run a single thread // on multiple processor groups, so we need to restrict the group. // We assume the group of the first processor listed for this node. // Processors from outside this group will not be assigned for this thread. // Normally this won't be an issue because windows used to assign NUMA nodes - // such that they can't span processor groups. However, since Windows 10 Build 20348 - // the behaviour changed, so there's a small window of versions between this and Windows 11 - // that might exhibit problems with not all processors being utilized. - // We handle this in NumaConfig::from_system by manually splitting the nodes when - // we detect that there's no function to set affinity spanning processor nodes. - // This is required because otherwise our thread distribution code may produce - // suboptimal results. + // such that they cannot span processor groups. However, since Windows 10 + // Build 20348 the behaviour changed, so there's a small window of versions + // between this and Windows 11 that might exhibit problems with not all + // processors being utilized. + // + // We handle this in NumaConfig::from_system by manually splitting the + // nodes when we detect that there is no function to set affinity spanning + // processor nodes. This is required because otherwise our thread distribution + // code may produce suboptimal results. + // // See https://learn.microsoft.com/en-us/windows/win32/procthread/numa-support GROUP_AFFINITY affinity; std::memset(&affinity, 0, sizeof(GROUP_AFFINITY)); - // We use an ordered set so we're guaranteed to get the smallest cpu number here. + // We use an ordered set to be sure to get the smallest cpu number here. const size_t forcedProcGroupIndex = *(nodes[n].begin()) / WIN_PROCESSOR_GROUP_SIZE; affinity.Group = static_cast(forcedProcGroupIndex); for (CpuIndex c : nodes[n]) @@ -894,8 +909,8 @@ class NumaConfig { if (status == 0) std::exit(EXIT_FAILURE); - // We yield this thread just to be sure it gets rescheduled. - // This is defensive, allowed because this code is not performance critical. + // We yield this thread just to be sure it gets rescheduled. This is + // defensive, allowed because this code is not performance critical. SwitchToThread(); } @@ -1013,8 +1028,8 @@ class NumaConfig { class NumaReplicationContext; -// Instances of this class are tracked by the NumaReplicationContext instance -// NumaReplicationContext informs all tracked instances whenever NUMA configuration changes. +// Instances of this class are tracked by the NumaReplicationContext instance. +// NumaReplicationContext informs all tracked instances when NUMA configuration changes. class NumaReplicatedBase { public: NumaReplicatedBase(NumaReplicationContext& ctx); @@ -1034,9 +1049,9 @@ class NumaReplicatedBase { NumaReplicationContext* context; }; -// We force boxing with a unique_ptr. If this becomes an issue due to added indirection we -// may need to add an option for a custom boxing type. -// When the NUMA config changes the value stored at the index 0 is replicated to other nodes. +// We force boxing with a unique_ptr. If this becomes an issue due to added +// indirection we may need to add an option for a custom boxing type. When the +// NUMA config changes the value stored at the index 0 is replicated to other nodes. template class NumaReplicated: public NumaReplicatedBase { public: @@ -1090,8 +1105,8 @@ class NumaReplicated: public NumaReplicatedBase { } void on_numa_config_changed() override { - // Use the first one as the source. It doesn't matter which one we use, because they all must - // be identical, but the first one is guaranteed to exist. + // Use the first one as the source. It doesn't matter which one we use, + // because they all must be identical, but the first one is guaranteed to exist. auto source = std::move(instances[0]); replicate_from(std::move(*source)); } @@ -1167,7 +1182,7 @@ class NumaReplicationContext { private: NumaConfig config; - // std::set uses std::less by default, which is required for pointer comparison to be defined. + // std::set uses std::less by default, which is required for pointer comparison std::set trackedReplicatedObjects; }; diff --git a/src/position.h b/src/position.h index 3cfb87d065f..064dd5fa918 100644 --- a/src/position.h +++ b/src/position.h @@ -315,8 +315,8 @@ inline bool Position::capture(Move m) const { } // Returns true if a move is generated from the capture stage, having also -// queen promotions covered, i.e. consistency with the capture stage move generation -// is needed to avoid the generation of duplicate moves. +// queen promotions covered, i.e. consistency with the capture stage move +// generation is needed to avoid the generation of duplicate moves. inline bool Position::capture_stage(Move m) const { assert(m.is_ok()); return capture(m) || m.promotion_type() == QUEEN; diff --git a/src/search.cpp b/src/search.cpp index 47c5dc88242..d3d95eda8f3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -78,7 +78,8 @@ constexpr int futility_move_count(bool improving, Depth depth) { return improving ? (3 + depth * depth) : (3 + depth * depth) / 2; } -// Add correctionHistory value to raw staticEval and guarantee evaluation does not hit the tablebase range +// Add correctionHistory value to raw staticEval and guarantee evaluation +// does not hit the tablebase range. Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { auto cv = w.correctionHistory[pos.side_to_move()][pawn_structure_index(pos)]; v += cv * std::abs(cv) / 5073; @@ -333,8 +334,8 @@ void Search::Worker::iterative_deepening() { int failedHighCnt = 0; while (true) { - // Adjust the effective depth searched, but ensure at least one effective increment - // for every four searchAgain steps (see issue #2717). + // Adjust the effective depth searched, but ensure at least one + // effective increment for every four searchAgain steps (see issue #2717). Depth adjustedDepth = std::max(1, rootDepth - failedHighCnt - 3 * (searchAgainCounter + 1) / 4); rootDelta = beta - alpha; @@ -354,15 +355,15 @@ void Search::Worker::iterative_deepening() { if (threads.stop) break; - // When failing high/low give some update before a re-search. - // To avoid excessive output that could hang GUIs like Fritz 19, only start + // When failing high/low give some update before a re-search. To avoid + // excessive output that could hang GUIs like Fritz 19, only start // at nodes > 10M (rather than depth N, which can be reached quickly) if (mainThread && multiPV == 1 && (bestValue <= alpha || bestValue >= beta) && nodes > 10000000) main_manager()->pv(*this, threads, tt, rootDepth); - // In case of failing low/high increase aspiration window and - // re-search, otherwise exit the loop. + // In case of failing low/high increase aspiration window and re-search, + // otherwise exit the loop. if (bestValue <= alpha) { beta = (alpha + beta) / 2; @@ -390,10 +391,11 @@ void Search::Worker::iterative_deepening() { if (mainThread && (threads.stop || pvIdx + 1 == multiPV || nodes > 10000000) - // A thread that aborted search can have mated-in/TB-loss PV and score - // that cannot be trusted, i.e. it can be delayed or refuted if we would have - // had time to fully search other root-moves. Thus we suppress this output and - // below pick a proven score/PV for this thread (from the previous iteration). + // A thread that aborted search can have mated-in/TB-loss PV and + // score that cannot be trusted, i.e. it can be delayed or refuted + // if we would have had time to fully search other root-moves. Thus + // we suppress this output and below pick a proven score/PV for this + // thread (from the previous iteration). && !(threads.abortedSearch && rootMoves[0].uciScore <= VALUE_TB_LOSS_IN_MAX_PLY)) main_manager()->pv(*this, threads, tt, rootDepth); @@ -504,6 +506,7 @@ void Search::Worker::iterative_deepening() { skill.best ? skill.best : skill.pick_best(rootMoves, multiPV))); } +// Reset histories, usually before a new game void Search::Worker::clear() { mainHistory.fill(0); captureHistory.fill(-700); @@ -523,7 +526,7 @@ void Search::Worker::clear() { } -// Main search function for both PV and non-PV nodes. +// Main search function for both PV and non-PV nodes template Value Search::Worker::search( Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode) { @@ -538,7 +541,7 @@ Value Search::Worker::search( // Limit the depth if extensions made it too large depth = std::min(depth, MAX_PLY - 1); - // Check if we have an upcoming move that draws by repetition. + // Check if we have an upcoming move that draws by repetition if (!rootNode && alpha < VALUE_DRAW && pos.upcoming_repetition(ss->ply)) { alpha = value_draw(this->nodes); @@ -611,7 +614,7 @@ Value Search::Worker::search( Square prevSq = ((ss - 1)->currentMove).is_ok() ? ((ss - 1)->currentMove).to_sq() : SQ_NONE; ss->statScore = 0; - // Step 4. Transposition table lookup. + // Step 4. Transposition table lookup excludedMove = ss->excludedMove; posKey = pos.key(); auto [ttHit, ttData, ttWriter] = tt.probe(posKey); @@ -676,7 +679,7 @@ Value Search::Worker::search( Value tbValue = VALUE_TB - ss->ply; - // use the range VALUE_TB to VALUE_TB_WIN_IN_MAX_PLY to score + // Use the range VALUE_TB to VALUE_TB_WIN_IN_MAX_PLY to score value = wdl < -drawScore ? -tbValue : wdl > drawScore ? tbValue : VALUE_DRAW + 2 * wdl * drawScore; @@ -771,8 +774,8 @@ Value Search::Worker::search( opponentWorsening = ss->staticEval + (ss - 1)->staticEval > 2; // Step 7. Razoring (~1 Elo) - // If eval is really low check with qsearch if it can exceed alpha, if it can't, - // return a fail low. + // If eval is really low, check with qsearch if we can exceed alpha. If the + // search suggests we cannot exceed alpha, return a speculative fail low. if (eval < alpha - 494 - 290 * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); @@ -836,27 +839,26 @@ Value Search::Worker::search( if (PvNode && !ttData.move) depth -= 3; - // Use qsearch if depth <= 0. + // Use qsearch if depth <= 0 if (depth <= 0) return qsearch(pos, ss, alpha, beta); - // For cutNodes, if depth is high enough, decrease depth by 2 if there is no ttMove, or - // by 1 if there is a ttMove with an upper bound. + // For cutNodes, if depth is high enough, decrease depth by 2 if there is no ttMove, + // or by 1 if there is a ttMove with an upper bound. if (cutNode && depth >= 7 && (!ttData.move || ttData.bound == BOUND_UPPER)) depth -= 1 + !ttData.move; // Step 11. ProbCut (~10 Elo) - // If we have a good enough capture (or queen promotion) and a reduced search returns a value - // much above beta, we can (almost) safely prune the previous move. + // If we have a good enough capture (or queen promotion) and a reduced search + // returns a value much above beta, we can (almost) safely prune the previous move. probCutBeta = beta + 184 - 53 * improving; - if ( - !PvNode && depth > 3 - && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY - // If value from transposition table is lower than probCutBeta, don't attempt probCut - // there and in further interactions with transposition table cutoff depth is set to depth - 3 - // because probCut search has depth set to depth - 4 but we also do a move before it - // So effective depth is equal to depth - 3 - && !(ttData.depth >= depth - 3 && ttData.value != VALUE_NONE && ttData.value < probCutBeta)) + if (!PvNode && depth > 3 + && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY + // If value from transposition table is lower than probCutBeta, don't attempt + // probCut there and in further interactions with transposition table cutoff + // depth is set to depth - 3 because probCut search has depth set to depth - 4 + // but we also do a move before it. So effective depth is equal to depth - 3. + && !(ttData.depth >= depth - 3 && ttData.value != VALUE_NONE && ttData.value < probCutBeta)) { assert(probCutBeta < VALUE_INFINITE && probCutBeta > beta); @@ -870,7 +872,6 @@ Value Search::Worker::search( if (move == excludedMove) continue; - // Check for legality if (!pos.legal(move)) continue; @@ -1050,18 +1051,18 @@ Value Search::Worker::search( // We take care to not overdo to avoid search getting stuck. if (ss->ply < thisThread->rootDepth * 2) { - // Singular extension search (~76 Elo, ~170 nElo). If all moves but one fail - // low on a search of (alpha-s, beta-s), and just one fails high on (alpha, beta), - // then that move is singular and should be extended. To verify this we do - // a reduced search on the position excluding the ttMove and if the result - // is lower than ttValue minus a margin, then we will extend the ttMove. - // Recursive singular search is avoided. - - // Note: the depth margin and singularBeta margin are known for having non-linear - // scaling. Their values are optimized to time controls of 180+1.8 and longer - // so changing them requires tests at these types of time controls. - // Generally, higher singularBeta (i.e closer to ttValue) and lower extension - // margins scale well. + // Singular extension search (~76 Elo, ~170 nElo). If all moves but one + // fail low on a search of (alpha-s, beta-s), and just one fails high on + // (alpha, beta), then that move is singular and should be extended. To + // verify this we do a reduced search on the position excluding the ttMove + // and if the result is lower than ttValue minus a margin, then we will + // extend the ttMove. Recursive singular search is avoided. + + // Note: the depth margin and singularBeta margin are known for having + // non-linear scaling. Their values are optimized to time controls of + // 180+1.8 and longer so changing them requires tests at these types of + // time controls. Generally, higher singularBeta (i.e closer to ttValue) + // and lower extension margins scale well. if (!rootNode && move == ttData.move && !excludedMove && depth >= 4 - (thisThread->completedDepth > 36) + ss->ttPv @@ -1089,28 +1090,31 @@ Value Search::Worker::search( // Multi-cut pruning // Our ttMove is assumed to fail high based on the bound of the TT entry, - // and if after excluding the ttMove with a reduced search we fail high over the original beta, - // we assume this expected cut-node is not singular (multiple moves fail high), - // and we can prune the whole subtree by returning a softbound. + // and if after excluding the ttMove with a reduced search we fail high + // over the original beta, we assume this expected cut-node is not + // singular (multiple moves fail high), and we can prune the whole + // subtree by returning a softbound. else if (value >= beta && std::abs(value) < VALUE_TB_WIN_IN_MAX_PLY) return value; // Negative extensions - // If other moves failed high over (ttValue - margin) without the ttMove on a reduced search, - // but we cannot do multi-cut because (ttValue - margin) is lower than the original beta, - // we do not know if the ttMove is singular or can do a multi-cut, - // so we reduce the ttMove in favor of other moves based on some conditions: + // If other moves failed high over (ttValue - margin) without the + // ttMove on a reduced search, but we cannot do multi-cut because + // (ttValue - margin) is lower than the original beta, we do not know + // if the ttMove is singular or can do a multi-cut, so we reduce the + // ttMove in favor of other moves based on some conditions: // If the ttMove is assumed to fail high over current beta (~7 Elo) else if (ttData.value >= beta) extension = -3; - // If we are on a cutNode but the ttMove is not assumed to fail high over current beta (~1 Elo) + // If we are on a cutNode but the ttMove is not assumed to fail high + // over current beta (~1 Elo) else if (cutNode) extension = -2; } - // Extension for capturing the previous moved piece (~0 Elo on STC, ~1 Elo on LTC) + // Extension for capturing the previous moved piece (~1 Elo at LTC) else if (PvNode && move.to_sq() == prevSq && thisThread->captureHistory[movedPiece][move.to_sq()] [type_of(pos.piece_on(move.to_sq()))] @@ -1136,9 +1140,9 @@ Value Search::Worker::search( pos.do_move(move, st, givesCheck); // These reduction adjustments have proven non-linear scaling. - // They are optimized to time controls of 180 + 1.8 and longer so - // changing them or adding conditions that are similar - // requires tests at these types of time controls. + // They are optimized to time controls of 180 + 1.8 and longer, + // so changing them or adding conditions that are similar requires + // tests at these types of time controls. // Decrease reduction if position is or has been on the PV (~7 Elo) if (ss->ttPv) @@ -1148,7 +1152,7 @@ Value Search::Worker::search( if (PvNode) r--; - // These reduction adjustments have no proven non-linear scaling. + // These reduction adjustments have no proven non-linear scaling // Increase reduction for cut nodes (~4 Elo) if (cutNode) @@ -1163,8 +1167,8 @@ Value Search::Worker::search( if ((ss + 1)->cutoffCnt > 3) r += 1 + !(PvNode || cutNode); - // For first picked move (ttMove) reduce reduction - // but never allow it to go below 0 (~3 Elo) + // For first picked move (ttMove) reduce reduction, but never allow + // reduction to go below 0 (~3 Elo) else if (move == ttData.move) r = std::max(0, r - 2); @@ -1190,8 +1194,8 @@ Value Search::Worker::search( // Do a full-depth search when reduced LMR search fails high if (value > alpha && d < newDepth) { - // Adjust full-depth search based on LMR results - if the result - // was good enough search deeper, if it was bad enough search shallower. + // Adjust full-depth search based on LMR results - if the result was + // good enough search deeper, if it was bad enough search shallower. const bool doDeeperSearch = value > (bestValue + 35 + 2 * newDepth); // (~1 Elo) const bool doShallowerSearch = value < bestValue + newDepth; // (~2 Elo) @@ -1237,8 +1241,8 @@ Value Search::Worker::search( // Step 20. Check for a new best move // Finished searching the move. If a stop occurred, the return value of - // the search cannot be trusted, and we return immediately without - // updating best move, PV and TT. + // the search cannot be trusted, and we return immediately without updating + // best move, principal variation nor transposition table. if (threads.stop.load(std::memory_order_relaxed)) return VALUE_ZERO; @@ -1351,7 +1355,8 @@ Value Search::Worker::search( if (!moveCount) bestValue = excludedMove ? alpha : ss->inCheck ? mated_in(ss->ply) : VALUE_DRAW; - // If there is a move that produces search value greater than alpha we update the stats of searched moves + // If there is a move that produces search value greater than alpha, + // we update the stats of searched moves. else if (bestMove) update_all_stats(pos, ss, *this, bestMove, prevSq, quietsSearched, quietCount, capturesSearched, captureCount, depth); @@ -1385,8 +1390,8 @@ Value Search::Worker::search( if (bestValue <= alpha) ss->ttPv = ss->ttPv || ((ss - 1)->ttPv && depth > 3); - // Write gathered information in transposition table - // Static evaluation is saved as it was before correction history + // Write gathered information in transposition table. Note that the + // static evaluation is saved as it was before correction history. if (!excludedMove && !(rootNode && thisThread->pvIdx)) ttWriter.write(posKey, value_to_tt(bestValue, ss->ply), ss->ttPv, bestValue >= beta ? BOUND_LOWER @@ -1410,12 +1415,12 @@ Value Search::Worker::search( } -// Quiescence search function, which is called by the main search function with zero depth, or -// recursively with further decreasing depth per call. With depth <= 0, we "should" be using -// static eval only, but tactical moves may confuse the static eval. To fight this horizon effect, -// we implement this qsearch of tactical moves only. -// See https://www.chessprogramming.org/Horizon_Effect and https://www.chessprogramming.org/Quiescence_Search -// (~155 Elo) +// Quiescence search function, which is called by the main search function with +// depth zero, or recursively with further decreasing depth. With depth <= 0, we +// "should" be using static eval only, but tactical moves may confuse the static eval. +// To fight this horizon effect, we implement this qsearch of tactical moves (~155 Elo). +// See https://www.chessprogramming.org/Horizon_Effect +// and https://www.chessprogramming.org/Quiescence_Search template Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { @@ -1426,7 +1431,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, assert(PvNode || (alpha == beta - 1)); assert(depth <= 0); - // Check if we have an upcoming move that draws by repetition. (~1 Elo) + // Check if we have an upcoming move that draws by repetition (~1 Elo) if (alpha < VALUE_DRAW && pos.upcoming_repetition(ss->ply)) { alpha = value_draw(this->nodes); @@ -1469,9 +1474,10 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, assert(0 <= ss->ply && ss->ply < MAX_PLY); - // Note that unlike regular search, which stores the literal depth into the TT, from QS we - // only store the current movegen stage as "depth". If in check, we search all evasions and - // thus store DEPTH_QS_CHECKS. (Evasions may be quiet, and _CHECKS includes quiets.) + // Note that unlike regular search, which stores the literal depth into the + // transposition table, from qsearch we only store the current movegen stage + // as "depth". If in check, we search all evasions and thus store DEPTH_QS_CHECKS. + // Evasions may be quiet, and _CHECKS includes quiets. Depth qsTtDepth = ss->inCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS : DEPTH_QS_NORMAL; // Step 3. Transposition table lookup @@ -1512,7 +1518,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, } else { - // In case of null move search, use previous static eval with a different sign + // In case of null move search, use previous static eval with opposite sign unadjustedStaticEval = (ss - 1)->currentMove != Move::null() ? evaluate(networks[numaAccessToken], pos, refreshTable, thisThread->optimism[us]) @@ -1542,21 +1548,20 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, (ss - 2)->continuationHistory}; - // Initialize a MovePicker object for the current position, and prepare to search the moves. - // We presently use two stages of qs movegen, first captures+checks, then captures only. - // (When in check, we simply search all evasions.) - // (Presently, having the checks stage is worth only 1 Elo, and may be removable in the near future, - // which would result in only a single stage of QS movegen.) + // Initialize a MovePicker object for the current position, and prepare to search + // the moves. We presently use two stages of move generator in quiescence search: + // first captures+checks, then captures only (but when in check, we simply search + // all evasions). Square prevSq = ((ss - 1)->currentMove).is_ok() ? ((ss - 1)->currentMove).to_sq() : SQ_NONE; MovePicker mp(pos, ttData.move, depth, &thisThread->mainHistory, &thisThread->captureHistory, contHist, &thisThread->pawnHistory); - // Step 5. Loop through all pseudo-legal moves until no moves remain or a beta cutoff occurs. + // Step 5. Loop through all pseudo-legal moves until no moves remain or a beta + // cutoff occurs. while ((move = mp.next_move()) != Move::none()) { assert(move.is_ok()); - // Check for legality if (!pos.legal(move)) continue; @@ -1577,24 +1582,24 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Value futilityValue = futilityBase + PieceValue[pos.piece_on(move.to_sq())]; - // If static eval + value of piece we are going to capture is much lower - // than alpha we can prune this move. (~2 Elo) + // If static eval + value of piece we are going to capture is + // much lower than alpha, we can prune this move. (~2 Elo) if (futilityValue <= alpha) { bestValue = std::max(bestValue, futilityValue); continue; } - // If static eval is much lower than alpha and move is not winning material - // we can prune this move. (~2 Elo) + // If static eval is much lower than alpha and move is + // not winning material, we can prune this move. (~2 Elo) if (futilityBase <= alpha && !pos.see_ge(move, 1)) { bestValue = std::max(bestValue, futilityBase); continue; } - // If static exchange evaluation is much worse than what is needed to not - // fall below alpha we can prune this move. + // If static exchange evaluation is much worse than what + // is needed to not fall below alpha, we can prune this move. if (futilityBase > alpha && !pos.see_ge(move, (alpha - futilityBase) * 4)) { bestValue = alpha; @@ -1654,8 +1659,8 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, } // Step 9. Check for mate - // All legal moves have been searched. A special case: if we're in check - // and no legal moves were found, it is checkmate. + // All legal moves have been searched. A special case: if we are + // in check and no legal moves were found, it is checkmate. if (ss->inCheck && bestValue == -VALUE_INFINITE) { assert(!MoveList(pos).size()); @@ -1665,8 +1670,8 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, if (std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY && bestValue >= beta) bestValue = (3 * bestValue + beta) / 4; - // Save gathered info in transposition table - // Static evaluation is saved as it was before adjustment by correction history + // Save gathered info in transposition table. The static evaluation + // is saved as it was before adjustment by correction history. ttWriter.write(posKey, value_to_tt(bestValue, ss->ply), pvHit, bestValue >= beta ? BOUND_LOWER : BOUND_UPPER, qsTtDepth, bestMove, unadjustedStaticEval, tt.generation()); @@ -1697,8 +1702,8 @@ TimePoint Search::Worker::elapsed_time() const { return main_manager()->tm.elaps namespace { -// Adjusts a mate or TB score from "plies to mate from the root" -// to "plies to mate from the current position". Standard scores are unchanged. +// Adjusts a mate or TB score from "plies to mate from the root" to +// "plies to mate from the current position". Standard scores are unchanged. // The function is called before storing a value in the transposition table. Value value_to_tt(Value v, int ply) { @@ -1707,11 +1712,11 @@ Value value_to_tt(Value v, int ply) { } -// Inverse of value_to_tt(): it adjusts a mate or TB score -// from the transposition table (which refers to the plies to mate/be mated from -// current position) to "plies to mate/be mated (TB win/loss) from the root". -// However, to avoid potentially false mate or TB scores related to the 50 moves rule -// and the graph history interaction, we return the highest non-TB score instead. +// Inverse of value_to_tt(): it adjusts a mate or TB score from the transposition +// table (which refers to the plies to mate/be mated from current position) to +// "plies to mate/be mated (TB win/loss) from the root". However, to avoid +// potentially false mate or TB scores related to the 50 moves rule and the +// graph history interaction, we return the highest non-TB score instead. Value value_from_tt(Value v, int ply, int r50c) { if (v == VALUE_NONE) @@ -1810,8 +1815,8 @@ void update_all_stats(const Position& pos, } -// Updates histories of the move pairs formed -// by moves at ply -1, -2, -3, -4, and -6 with current move. +// Updates histories of the move pairs formed by moves +// at ply -1, -2, -3, -4, and -6 with current move. void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { bonus = bonus * 52 / 64; @@ -1859,8 +1864,8 @@ void update_quiet_stats( } -// When playing with strength handicap, choose the best move among a set of RootMoves -// using a statistical rule dependent on 'level'. Idea by Heinz van Saanen. +// When playing with strength handicap, choose the best move among a set of +// RootMoves using a statistical rule dependent on 'level'. Idea by Heinz van Saanen. Move Skill::pick_best(const RootMoves& rootMoves, size_t multiPV) { static PRNG rng(now()); // PRNG sequence should be non-deterministic @@ -1891,8 +1896,8 @@ Move Skill::pick_best(const RootMoves& rootMoves, size_t multiPV) { } -// Used to print debug info and, more importantly, -// to detect when we are out of available time and thus stop the search. +// Used to print debug info and, more importantly, to detect +// when we are out of available time and thus stop the search. void SearchManager::check_time(Search::Worker& worker) { if (--callsCnt > 0) return; @@ -1926,8 +1931,9 @@ void SearchManager::check_time(Search::Worker& worker) { } // Used to correct and extend PVs for moves that have a TB (but not a mate) score. -// Keeps the search based PV for as long as it is verified to maintain the game outcome, truncates afterwards. -// Finally, extends to mate the PV, providing a possible continuation (but not a proven mating line). +// Keeps the search based PV for as long as it is verified to maintain the game +// outcome, truncates afterwards. Finally, extends to mate the PV, providing a +// possible continuation (but not a proven mating line). void syzygy_extend_pv(const OptionsMap& options, const Search::LimitsType& limits, Position& pos, @@ -1937,7 +1943,7 @@ void syzygy_extend_pv(const OptionsMap& options, auto t_start = std::chrono::steady_clock::now(); int moveOverhead = int(options["Move Overhead"]); - // Do not use more than moveOverhead / 2 time, if time management is active. + // Do not use more than moveOverhead / 2 time, if time management is active auto time_abort = [&t_start, &moveOverhead, &limits]() -> bool { auto t_end = std::chrono::steady_clock::now(); return limits.use_time_management() @@ -1968,7 +1974,7 @@ void syzygy_extend_pv(const OptionsMap& options, auto& st = sts.emplace_back(); pos.do_move(pvMove, st); - // don't allow for repetitions or drawing moves along the PV in TB regime. + // Do not allow for repetitions or drawing moves along the PV in TB regime if (config.rootInTB && pos.is_draw(ply)) { pos.undo_move(pvMove); @@ -1976,17 +1982,18 @@ void syzygy_extend_pv(const OptionsMap& options, break; } - // Full PV shown will thus be validated and end TB. - // If we can't validate the full PV in time, we don't show it. + // Full PV shown will thus be validated and end in TB. + // If we cannot validate the full PV in time, we do not show it. if (config.rootInTB && time_abort()) break; } - // resize the PV to the correct part + // Resize the PV to the correct part rootMove.pv.resize(ply); - // Step 2, now extend the PV to mate, as if the user explores syzygy-tables.info using - // top ranked moves (minimal DTZ), which gives optimal mates only for simple endgames e.g. KRvK + // Step 2, now extend the PV to mate, as if the user explored syzygy-tables.info + // using top ranked moves (minimal DTZ), which gives optimal mates only for simple + // endgames e.g. KRvK. while (!pos.is_draw(0)) { if (time_abort()) @@ -1998,8 +2005,8 @@ void syzygy_extend_pv(const OptionsMap& options, auto& rm = legalMoves.emplace_back(m); StateInfo tmpSI; pos.do_move(m, tmpSI); - // Give a score of each move to break DTZ ties - // restricting opponent mobility, but not giving the opponent a capture. + // Give a score of each move to break DTZ ties restricting opponent mobility, + // but not giving the opponent a capture. for (const auto& mOpp : MoveList(pos)) rm.tbRank -= pos.capture(mOpp) ? 100 : 1; pos.undo_move(m); @@ -2009,16 +2016,16 @@ void syzygy_extend_pv(const OptionsMap& options, if (legalMoves.size() == 0) break; - // sort moves according to their above assigned rank, + // Sort moves according to their above assigned rank. // This will break ties for moves with equal DTZ in rank_root_moves. std::stable_sort( legalMoves.begin(), legalMoves.end(), [](const Search::RootMove& a, const Search::RootMove& b) { return a.tbRank > b.tbRank; }); - // The winning side tries to minimize DTZ, the losing side maximizes it. + // The winning side tries to minimize DTZ, the losing side maximizes it Tablebases::Config config = Tablebases::rank_root_moves(options, pos, legalMoves, true); - // If DTZ is not available we might not find a mate, so we bail out. + // If DTZ is not available we might not find a mate, so we bail out if (!config.rootInTB || config.cardinality > 0) break; @@ -2030,23 +2037,24 @@ void syzygy_extend_pv(const OptionsMap& options, pos.do_move(pvMove, st); } - // Finding a draw in this function is an exceptional case, that cannot happen during engine game play, - // since we have a winning score, and play correctly with TB support. - // However, it can be that a position is draw due to the 50 move rule if it has been been reached - // on the board with a non-optimal 50 move counter e.g. 8/8/6k1/3B4/3K4/4N3/8/8 w - - 54 106 - // which TB with dtz counter rounding cannot always correctly rank. See also + // Finding a draw in this function is an exceptional case, that cannot happen + // during engine game play, since we have a winning score, and play correctly + // with TB support. However, it can be that a position is draw due to the 50 move + // rule if it has been been reached on the board with a non-optimal 50 move counter + // (e.g. 8/8/6k1/3B4/3K4/4N3/8/8 w - - 54 106 ) which TB with dtz counter rounding + // cannot always correctly rank. See also // https://github.com/official-stockfish/Stockfish/issues/5175#issuecomment-2058893495 - // We adjust the score to match the found PV. Note that a TB loss score can be displayed - // if the engine did not find a drawing move yet, but eventually search will figure it out. - // E.g. 1kq5/q2r4/5K2/8/8/8/8/7Q w - - 96 1 + // We adjust the score to match the found PV. Note that a TB loss score can be + // displayed if the engine did not find a drawing move yet, but eventually search + // will figure it out (e.g. 1kq5/q2r4/5K2/8/8/8/8/7Q w - - 96 1 ) if (pos.is_draw(0)) v = VALUE_DRAW; - // Undo the PV moves. + // Undo the PV moves for (auto it = rootMove.pv.rbegin(); it != rootMove.pv.rend(); ++it) pos.undo_move(*it); - // Inform if we couldn't get a full extension in time. + // Inform if we couldn't get a full extension in time if (time_abort()) sync_cout << "info string Syzygy based PV extension requires more time, increase Move Overhead as needed." @@ -2092,7 +2100,7 @@ void SearchManager::pv(Search::Worker& worker, for (Move m : rootMoves[i].pv) pv += UCIEngine::move(m, pos.is_chess960()) + " "; - // remove last whitespace + // Remove last whitespace if (!pv.empty()) pv.pop_back(); diff --git a/src/search.h b/src/search.h index 122cd549e3c..575967540fb 100644 --- a/src/search.h +++ b/src/search.h @@ -236,8 +236,8 @@ class Worker { public: Worker(SharedState&, std::unique_ptr, size_t, NumaReplicatedAccessToken); - // Called at instantiation to initialize Reductions tables - // Reset histories, usually before a new game + // Called at instantiation to initialize reductions tables. + // Reset histories, usually before a new game. void clear(); // Called when the program receives the UCI 'go' command. @@ -256,7 +256,7 @@ class Worker { private: void iterative_deepening(); - // Main search function for both PV and non-PV nodes + // This is the main search function, for both PV and non-PV nodes template Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, bool cutNode); @@ -266,8 +266,7 @@ class Worker { Depth reduction(bool i, Depth d, int mn, int delta) const; - // Get a pointer to the search manager, only allowed to be called by the - // main thread. + // Pointer to the search manager, only allowed to be called by the main thread SearchManager* main_manager() const { assert(threadIdx == 0); return static_cast(manager.get()); diff --git a/src/thread.cpp b/src/thread.cpp index 4acb9854bd4..f17fc4a5366 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -50,8 +50,8 @@ Thread::Thread(Search::SharedState& sharedState, run_custom_job([this, &binder, &sharedState, &sm, n]() { // Use the binder to [maybe] bind the threads to a NUMA node before doing - // the Worker allocation. - // Ideally we would also allocate the SearchManager here, but that's minor. + // the Worker allocation. Ideally we would also allocate the SearchManager + // here, but that's minor. this->numaAccessToken = binder(); this->worker = std::make_unique(sharedState, std::move(sm), n, this->numaAccessToken); @@ -72,27 +72,26 @@ Thread::~Thread() { stdThread.join(); } - // Wakes up the thread that will start the search void Thread::start_searching() { assert(worker != nullptr); run_custom_job([this]() { worker->start_searching(); }); } -// Wakes up the thread that will start the search +// Clears the histories for the thread worker (usually before a new game) void Thread::clear_worker() { assert(worker != nullptr); run_custom_job([this]() { worker->clear(); }); } -// Blocks on the condition variable -// until the thread has finished searching. +// Blocks on the condition variable until the thread has finished searching void Thread::wait_for_search_finished() { std::unique_lock lk(mutex); cv.wait(lk, [&] { return !searching; }); } +// Launching a function in the thread void Thread::run_custom_job(std::function f) { { std::unique_lock lk(mutex); @@ -103,8 +102,8 @@ void Thread::run_custom_job(std::function f) { cv.notify_one(); } -// Thread gets parked here, blocked on the -// condition variable, when it has no work to do. +// Thread gets parked here, blocked on the condition variable +// when the thread has no work to do. void Thread::idle_loop() { while (true) @@ -233,8 +232,9 @@ void ThreadPool::wait_on_thread(size_t threadId) { size_t ThreadPool::num_threads() const { return threads.size(); } -// Wakes up main thread waiting in idle_loop() and -// returns immediately. Main thread will wake up other threads and start the search. + +// Wakes up main thread waiting in idle_loop() and returns immediately. +// Main thread will wake up other threads and start the search. void ThreadPool::start_thinking(const OptionsMap& options, Position& pos, StateListPtr& states, @@ -274,8 +274,8 @@ void ThreadPool::start_thinking(const OptionsMap& options, // We use Position::set() to set root position across threads. But there are // some StateInfo fields (previous, pliesFromNull, capturedPiece) that cannot // be deduced from a fen string, so set() clears them and they are set from - // setupStates->back() later. The rootState is per thread, earlier states are shared - // since they are read-only. + // setupStates->back() later. The rootState is per thread, earlier states are + // shared since they are read-only. for (auto&& th : threads) { th->run_custom_job([&]() { @@ -335,7 +335,7 @@ Thread* ThreadPool::get_best_thread() const { const bool newThreadInProvenLoss = newThreadScore != -VALUE_INFINITE && newThreadScore <= VALUE_TB_LOSS_IN_MAX_PLY; - // Note that we make sure not to pick a thread with truncated-PV for better viewer experience. + // We make sure not to pick a thread with truncated principal variation const bool betterVotingValue = thread_voting_value(th.get()) * int(newThreadPV.size() > 2) > thread_voting_value(bestThread) * int(bestThreadPV.size() > 2); @@ -363,8 +363,8 @@ Thread* ThreadPool::get_best_thread() const { } -// Start non-main threads -// Will be invoked by main thread after it has started searching +// Start non-main threads. +// Will be invoked by main thread after it has started searching. void ThreadPool::start_searching() { for (auto&& th : threads) @@ -374,7 +374,6 @@ void ThreadPool::start_searching() { // Wait for non-main threads - void ThreadPool::wait_for_search_finished() const { for (auto&& th : threads) diff --git a/src/types.h b/src/types.h index 10ad1fac9ef..8a9400bb8f8 100644 --- a/src/types.h +++ b/src/types.h @@ -137,9 +137,9 @@ enum Bound { BOUND_EXACT = BOUND_UPPER | BOUND_LOWER }; -// Value is used as an alias for int16_t, this is done to differentiate between -// a search value and any other integer value. The values used in search are always -// supposed to be in the range (-VALUE_NONE, VALUE_NONE] and should not exceed this range. +// Value is used as an alias for int, this is done to differentiate between a search +// value and any other integer value. The values used in search are always supposed +// to be in the range (-VALUE_NONE, VALUE_NONE] and should not exceed this range. using Value = int; constexpr Value VALUE_ZERO = 0; @@ -187,15 +187,20 @@ constexpr Value PieceValue[PIECE_NB] = { using Depth = int; enum : int { - // The following DEPTH_ constants are used for TT entries and QS movegen stages. In regular search, - // TT depth is literal: the search depth (effort) used to make the corresponding TT value. - // In qsearch, however, TT entries only store the current QS movegen stage (which should thus compare + // The following DEPTH_ constants are used for transposition table entries + // and quiescence search move generation stages. In regular search, the + // depth stored in the transposition table is literal: the search depth + // (effort) used to make the corresponding transposition table value. In + // quiescence search, however, the transposition table entries only store + // the current quiescence move generation stage (which should thus compare // lower than any regular search depth). DEPTH_QS_CHECKS = 0, DEPTH_QS_NORMAL = -1, - // For TT entries where no searching at all was done (whether regular or qsearch) we use - // _UNSEARCHED, which should thus compare lower than any QS or regular depth. _ENTRY_OFFSET is used - // only for the TT entry occupancy check (see tt.cpp), and should thus be lower than _UNSEARCHED. + // For transposition table entries where no searching at all was done + // (whether regular or qsearch) we use DEPTH_UNSEARCHED, which should thus + // compare lower than any quiescence or regular depth. DEPTH_ENTRY_OFFSET + // is used only for the transposition table entry occupancy check (see tt.cpp), + // and should thus be lower than DEPTH_UNSEARCHED. DEPTH_UNSEARCHED = -2, DEPTH_ENTRY_OFFSET = -3 }; @@ -356,9 +361,10 @@ enum MoveType { // bit 14-15: special move flag: promotion (1), en passant (2), castling (3) // NOTE: en passant bit is set only when a pawn can be captured // -// Special cases are Move::none() and Move::null(). We can sneak these in because in -// any normal move destination square is always different from origin square -// while Move::none() and Move::null() have the same origin and destination square. +// Special cases are Move::none() and Move::null(). We can sneak these in because +// in any normal move the destination square and origin square are always different, +// but Move::none() and Move::null() have the same origin and destination square. + class Move { public: Move() = default; From 6135a0e2f830a587d2ac7a332bb62188fa924aad Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 10 Jul 2024 21:13:59 +0200 Subject: [PATCH 1652/1766] Provide more info on found TB files now uses the following format: `info string Found 510 WDL and 510 DTZ tablebase files (up to 6-man).` this clarifies exactly what has been found, as the difference matters, e.g. for the PV extension of TB scores. closes https://github.com/official-stockfish/Stockfish/pull/5471 No functional change --- src/syzygy/tbprobe.cpp | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index fc2a092aa54..e2344fdab2e 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -443,6 +443,8 @@ class TBTables { std::deque> wdlTable; std::deque> dtzTable; + size_t foundDTZFiles = 0; + size_t foundWDLFiles = 0; void insert(Key key, TBTable* wdl, TBTable* dtz) { uint32_t homeBucket = uint32_t(key) & (Size - 1); @@ -486,9 +488,16 @@ class TBTables { memset(hashTable, 0, sizeof(hashTable)); wdlTable.clear(); dtzTable.clear(); + foundDTZFiles = 0; + foundWDLFiles = 0; } - size_t size() const { return wdlTable.size(); } - void add(const std::vector& pieces); + + void info() const { + sync_cout << "info string Found " << foundWDLFiles << " WDL and " << foundDTZFiles + << " DTZ tablebase files (up to " << MaxCardinality << "-man)." << sync_endl; + } + + void add(const std::vector& pieces); }; TBTables TBTables; @@ -501,13 +510,22 @@ void TBTables::add(const std::vector& pieces) { for (PieceType pt : pieces) code += PieceToChar[pt]; + code.insert(code.find('K', 1), "v"); + + TBFile file_dtz(code + ".rtbz"); // KRK -> KRvK + if (file_dtz.is_open()) + { + file_dtz.close(); + foundDTZFiles++; + } - TBFile file(code.insert(code.find('K', 1), "v") + ".rtbw"); // KRK -> KRvK + TBFile file(code + ".rtbw"); // KRK -> KRvK if (!file.is_open()) // Only WDL file is checked return; file.close(); + foundWDLFiles++; MaxCardinality = std::max(int(pieces.size()), MaxCardinality); @@ -1466,7 +1484,7 @@ void Tablebases::init(const std::string& paths) { } } - sync_cout << "info string Found " << TBTables.size() << " tablebases" << sync_endl; + TBTables.info(); } // Probe the WDL table for a particular position. From 8d1e41458e1fd12aaf42a13fcc0676ae487531f0 Mon Sep 17 00:00:00 2001 From: yl25946 Date: Wed, 10 Jul 2024 23:49:16 -0500 Subject: [PATCH 1653/1766] removed second killer move STC with movepicker rewrite: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 46656 W: 12208 L: 11995 D: 22453 Ptnml(0-2): 203, 5461, 11777, 5694, 193 https://tests.stockfishchess.org/tests/view/668d98a15034141ae5999e68 Earlier version passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 468896 W: 120999 L: 120054 D: 227843 Ptnml(0-2): 1207, 55209, 120639, 56218, 1175 https://tests.stockfishchess.org/tests/view/668b17d2cf91c430fca58630 Earlier version passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 550524 W: 139553 L: 139877 D: 271094 Ptnml(0-2): 333, 61646, 151616, 61346, 321 https://tests.stockfishchess.org/tests/view/668b2e04cf91c430fca586b1 closes https://github.com/official-stockfish/Stockfish/pull/5472 bench 1234309 Co-authored-by: rn5f107s2 --- src/movepick.cpp | 28 ++++++++++++---------------- src/movepick.h | 4 ++-- src/search.cpp | 24 ++++++++++-------------- src/search.h | 2 +- 4 files changed, 25 insertions(+), 33 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index c21b14a9089..d54bcbc74d1 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -20,7 +20,6 @@ #include #include -#include #include #include "bitboard.h" @@ -35,7 +34,7 @@ enum Stages { MAIN_TT, CAPTURE_INIT, GOOD_CAPTURE, - REFUTATION, + KILLER, QUIET_INIT, GOOD_QUIET, BAD_CAPTURE, @@ -91,14 +90,14 @@ MovePicker::MovePicker(const Position& p, const CapturePieceToHistory* cph, const PieceToHistory** ch, const PawnHistory* ph, - const Move* killers) : + Move km) : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), pawnHistory(ph), ttMove(ttm), - refutations{{killers[0], 0}, {killers[1], 0}}, + killer{km, 0}, depth(d) { assert(d > 0); @@ -268,19 +267,17 @@ Move MovePicker::next_move(bool skipQuiets) { })) return *(cur - 1); - // Prepare the pointers to loop over the refutations array - cur = std::begin(refutations); - endMoves = std::end(refutations); - ++stage; [[fallthrough]]; - case REFUTATION : - if (select([&]() { - return *cur != Move::none() && !pos.capture_stage(*cur) && pos.pseudo_legal(*cur); - })) - return *(cur - 1); + case KILLER : + // increment it before so if we aren't stuck here indefinitely ++stage; + + if (killer != ttMove && killer != Move::none() && !pos.capture_stage(killer) + && pos.pseudo_legal(killer)) + return killer; + [[fallthrough]]; case QUIET_INIT : @@ -297,8 +294,7 @@ Move MovePicker::next_move(bool skipQuiets) { [[fallthrough]]; case GOOD_QUIET : - if (!skipQuiets - && select([&]() { return *cur != refutations[0] && *cur != refutations[1]; })) + if (!skipQuiets && select([&]() { return *cur != killer; })) { if ((cur - 1)->value > -7998 || (cur - 1)->value <= quiet_threshold(depth)) return *(cur - 1); @@ -327,7 +323,7 @@ Move MovePicker::next_move(bool skipQuiets) { case BAD_QUIET : if (!skipQuiets) - return select([&]() { return *cur != refutations[0] && *cur != refutations[1]; }); + return select([&]() { return *cur != killer; }); return Move::none(); diff --git a/src/movepick.h b/src/movepick.h index 2564f730190..86a2a5834d6 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -160,7 +160,7 @@ class MovePicker { const CapturePieceToHistory*, const PieceToHistory**, const PawnHistory*, - const Move*); + Move); MovePicker(const Position&, Move, Depth, @@ -185,7 +185,7 @@ class MovePicker { const PieceToHistory** continuationHistory; const PawnHistory* pawnHistory; Move ttMove; - ExtMove refutations[2], *cur, *endMoves, *endBadCaptures, *beginBadQuiets, *endBadQuiets; + ExtMove killer, *cur, *endMoves, *endBadCaptures, *beginBadQuiets, *endBadQuiets; int stage; int threshold; Depth depth; diff --git a/src/search.cpp b/src/search.cpp index d3d95eda8f3..a4da8cb002b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -123,7 +123,7 @@ Value value_to_tt(Value v, int ply); Value value_from_tt(Value v, int ply, int r50c); void update_pv(Move* pv, Move move, const Move* childPv); void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus); -void update_refutations(Stack* ss, Move move); +void update_killer(Stack* ss, Move move); void update_quiet_histories( const Position& pos, Stack* ss, Search::Worker& workerThread, Move move, int bonus); void update_quiet_stats( @@ -608,9 +608,9 @@ Value Search::Worker::search( assert(0 <= ss->ply && ss->ply < MAX_PLY); - bestMove = Move::none(); - (ss + 2)->killers[0] = (ss + 2)->killers[1] = Move::none(); - (ss + 2)->cutoffCnt = 0; + bestMove = Move::none(); + (ss + 1)->killer = Move::none(); + (ss + 2)->cutoffCnt = 0; Square prevSq = ((ss - 1)->currentMove).is_ok() ? ((ss - 1)->currentMove).to_sq() : SQ_NONE; ss->statScore = 0; @@ -934,7 +934,7 @@ Value Search::Worker::search( MovePicker mp(pos, ttData.move, depth, &thisThread->mainHistory, &thisThread->captureHistory, - contHist, &thisThread->pawnHistory, ss->killers); + contHist, &thisThread->pawnHistory, ss->killer); value = bestValue; moveCountPruning = false; @@ -1157,7 +1157,7 @@ Value Search::Worker::search( // Increase reduction for cut nodes (~4 Elo) if (cutNode) r += 2 - (ttData.depth >= depth && ss->ttPv) - + (!ss->ttPv && move != ttData.move && move != ss->killers[0]); + + (!ss->ttPv && move != ttData.move && move != ss->killer); // Increase reduction if ttMove is a capture (~3 Elo) if (ttCapture) @@ -1801,7 +1801,7 @@ void update_all_stats(const Position& pos, // main killer move in previous ply when it gets refuted. if (prevSq != SQ_NONE && ((ss - 1)->moveCount == 1 + (ss - 1)->ttHit - || ((ss - 1)->currentMove == (ss - 1)->killers[0])) + || ((ss - 1)->currentMove == (ss - 1)->killer)) && !pos.captured_piece()) update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, -quietMoveMalus); @@ -1832,14 +1832,10 @@ void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { } // Updates move sorting heuristics -void update_refutations(Stack* ss, Move move) { +void update_killer(Stack* ss, Move move) { // Update killers - if (ss->killers[0] != move) - { - ss->killers[1] = ss->killers[0]; - ss->killers[0] = move; - } + ss->killer = move; } void update_quiet_histories( @@ -1858,7 +1854,7 @@ void update_quiet_histories( void update_quiet_stats( const Position& pos, Stack* ss, Search::Worker& workerThread, Move move, int bonus) { - update_refutations(ss, move); + update_killer(ss, move); update_quiet_histories(pos, ss, workerThread, move, bonus); } diff --git a/src/search.h b/src/search.h index 575967540fb..65394bc0763 100644 --- a/src/search.h +++ b/src/search.h @@ -65,7 +65,7 @@ struct Stack { int ply; Move currentMove; Move excludedMove; - Move killers[2]; + Move killer; Value staticEval; int statScore; int moveCount; From 42aae5fe8b3f41dac7b0e080ea2e55fa3816d802 Mon Sep 17 00:00:00 2001 From: Andyson007 Date: Thu, 11 Jul 2024 10:09:57 +0200 Subject: [PATCH 1654/1766] Fixed non UCI compliance print `` and accept `` for UCI string options, accepting empty strings as well. Internally use empty strings (`""`). closes https://github.com/official-stockfish/Stockfish/pull/5474 No functional change --- AUTHORS | 1 + src/engine.cpp | 2 +- src/syzygy/tbprobe.cpp | 2 +- src/ucioption.cpp | 14 +++++++++++--- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/AUTHORS b/AUTHORS index 6957682f494..1ac40d879df 100644 --- a/AUTHORS +++ b/AUTHORS @@ -20,6 +20,7 @@ Alexander Kure Alexander Pagel (Lolligerhans) Alfredo Menezes (lonfom169) Ali AlZhrani (Cooffe) +Andreas Jan van der Meulen (Andyson007) Andreas Matthies (Matthies) Andrei Vetrov (proukornew) Andrew Grant (AndyGrant) diff --git a/src/engine.cpp b/src/engine.cpp index 2bc0db6affb..41b19ac6859 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -93,7 +93,7 @@ Engine::Engine(std::string path) : options["UCI_LimitStrength"] << Option(false); options["UCI_Elo"] << Option(1320, 1320, 3190); options["UCI_ShowWDL"] << Option(false); - options["SyzygyPath"] << Option("", [](const Option& o) { + options["SyzygyPath"] << Option("", [](const Option& o) { Tablebases::init(o); return std::nullopt; }); diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index e2344fdab2e..9b24e700b18 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -1344,7 +1344,7 @@ void Tablebases::init(const std::string& paths) { MaxCardinality = 0; TBFile::Paths = paths; - if (paths.empty() || paths == "") + if (paths.empty()) return; // MapB1H1H7[] encodes a square below a1-h8 diagonal to 0..27 diff --git a/src/ucioption.cpp b/src/ucioption.cpp index 1cd028c9932..455803cfe1b 100644 --- a/src/ucioption.cpp +++ b/src/ucioption.cpp @@ -166,7 +166,9 @@ Option& Option::operator=(const std::string& v) { return *this; } - if (type != "button") + if (type == "string") + currentValue = v == "" ? "" : v; + else if (type != "button") currentValue = v; if (on_change) @@ -188,10 +190,16 @@ std::ostream& operator<<(std::ostream& os, const OptionsMap& om) { const Option& o = it.second; os << "\noption name " << it.first << " type " << o.type; - if (o.type == "string" || o.type == "check" || o.type == "combo") + if (o.type == "check" || o.type == "combo") os << " default " << o.defaultValue; - if (o.type == "spin") + else if (o.type == "string") + { + std::string defaultValue = o.defaultValue.empty() ? "" : o.defaultValue; + os << " default " << defaultValue; + } + + else if (o.type == "spin") os << " default " << int(stof(o.defaultValue)) << " min " << o.min << " max " << o.max; From 3df09c04d7081d341cb0c5bcc3adc498ba877f9a Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Tue, 9 Jul 2024 13:04:47 -0500 Subject: [PATCH 1655/1766] Simplify Away Refutation Stage Simplify away killer stage to a constant bonus given to the killer move during quiet move scoring. Passed Non-regression STC (Against then-pending PR #5472): LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 106176 W: 27685 L: 27539 D: 50952 Ptnml(0-2): 410, 12765, 26637, 12821, 455 https://tests.stockfishchess.org/tests/view/668dd0835034141ae5999e8f Passed Non-regression LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 92472 W: 23426 L: 23276 D: 45770 Ptnml(0-2): 55, 10376, 25215, 10544, 46 https://tests.stockfishchess.org/tests/view/669019e45034141ae5999fd2 closes https://github.com/official-stockfish/Stockfish/pull/5476 Bench 1459677 --- src/movepick.cpp | 19 +++++-------------- src/movepick.h | 12 ++++++------ 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index d54bcbc74d1..7619471f15b 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -34,7 +34,6 @@ enum Stages { MAIN_TT, CAPTURE_INIT, GOOD_CAPTURE, - KILLER, QUIET_INIT, GOOD_QUIET, BAD_CAPTURE, @@ -97,7 +96,7 @@ MovePicker::MovePicker(const Position& p, continuationHistory(ch), pawnHistory(ph), ttMove(ttm), - killer{km, 0}, + killer(km), depth(d) { assert(d > 0); @@ -184,6 +183,8 @@ void MovePicker::score() { m.value += (*continuationHistory[3])[pc][to]; m.value += (*continuationHistory[5])[pc][to]; + m.value += (m == killer) * 65536; + // bonus for checks m.value += bool(pos.check_squares(pt) & to) * 16384; @@ -270,16 +271,6 @@ Move MovePicker::next_move(bool skipQuiets) { ++stage; [[fallthrough]]; - case KILLER : - // increment it before so if we aren't stuck here indefinitely - ++stage; - - if (killer != ttMove && killer != Move::none() && !pos.capture_stage(killer) - && pos.pseudo_legal(killer)) - return killer; - - [[fallthrough]]; - case QUIET_INIT : if (!skipQuiets) { @@ -294,7 +285,7 @@ Move MovePicker::next_move(bool skipQuiets) { [[fallthrough]]; case GOOD_QUIET : - if (!skipQuiets && select([&]() { return *cur != killer; })) + if (!skipQuiets && select([]() { return true; })) { if ((cur - 1)->value > -7998 || (cur - 1)->value <= quiet_threshold(depth)) return *(cur - 1); @@ -323,7 +314,7 @@ Move MovePicker::next_move(bool skipQuiets) { case BAD_QUIET : if (!skipQuiets) - return select([&]() { return *cur != killer; }); + return select([]() { return true; }); return Move::none(); diff --git a/src/movepick.h b/src/movepick.h index 86a2a5834d6..c6a5d25aecc 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -184,12 +184,12 @@ class MovePicker { const CapturePieceToHistory* captureHistory; const PieceToHistory** continuationHistory; const PawnHistory* pawnHistory; - Move ttMove; - ExtMove killer, *cur, *endMoves, *endBadCaptures, *beginBadQuiets, *endBadQuiets; - int stage; - int threshold; - Depth depth; - ExtMove moves[MAX_MOVES]; + Move ttMove, killer; + ExtMove * cur, *endMoves, *endBadCaptures, *beginBadQuiets, *endBadQuiets; + int stage; + int threshold; + Depth depth; + ExtMove moves[MAX_MOVES]; }; } // namespace Stockfish From 024eb6f453e06e37ceca81d5f759b8fe6006b03b Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Thu, 11 Jul 2024 14:07:38 -0700 Subject: [PATCH 1656/1766] Unify Movepick Initializer Passed Non-regression STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 168704 W: 43524 L: 43455 D: 81725 Ptnml(0-2): 414, 17173, 49076, 17308, 381 https://tests.stockfishchess.org/tests/view/66904b7b5034141ae599a197 Passed Non-regression LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 120294 W: 30473 L: 30364 D: 59457 Ptnml(0-2): 40, 10974, 38032, 11039, 62 https://tests.stockfishchess.org/tests/view/66905b235034141ae599a223 closes https://github.com/official-stockfish/Stockfish/pull/5477 bench 1459677 --- src/movepick.cpp | 25 ++++--------------------- src/movepick.h | 9 +-------- 2 files changed, 5 insertions(+), 29 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 7619471f15b..55bacf6e747 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -98,29 +98,12 @@ MovePicker::MovePicker(const Position& p, ttMove(ttm), killer(km), depth(d) { - assert(d > 0); - stage = (pos.checkers() ? EVASION_TT : MAIN_TT) + !(ttm && pos.pseudo_legal(ttm)); -} - -// Constructor for quiescence search -MovePicker::MovePicker(const Position& p, - Move ttm, - Depth d, - const ButterflyHistory* mh, - const CapturePieceToHistory* cph, - const PieceToHistory** ch, - const PawnHistory* ph) : - pos(p), - mainHistory(mh), - captureHistory(cph), - continuationHistory(ch), - pawnHistory(ph), - ttMove(ttm), - depth(d) { - assert(d <= 0); + if (pos.checkers()) + stage = EVASION_TT + !(ttm && pos.pseudo_legal(ttm)); - stage = (pos.checkers() ? EVASION_TT : QSEARCH_TT) + !(ttm && pos.pseudo_legal(ttm)); + else + stage = (depth > 0 ? MAIN_TT : QSEARCH_TT) + !(ttm && pos.pseudo_legal(ttm)); } // Constructor for ProbCut: we generate captures with SEE greater than or equal diff --git a/src/movepick.h b/src/movepick.h index c6a5d25aecc..92e11de2bf3 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -160,14 +160,7 @@ class MovePicker { const CapturePieceToHistory*, const PieceToHistory**, const PawnHistory*, - Move); - MovePicker(const Position&, - Move, - Depth, - const ButterflyHistory*, - const CapturePieceToHistory*, - const PieceToHistory**, - const PawnHistory*); + Move killer = Move::none()); MovePicker(const Position&, Move, int, const CapturePieceToHistory*); Move next_move(bool skipQuiets = false); From 563d268519885a411e9a3b784875e457aeb26929 Mon Sep 17 00:00:00 2001 From: MinetaS Date: Sat, 13 Jul 2024 00:53:34 +0900 Subject: [PATCH 1657/1766] Simplify futility_move_count This patch reverts changes from #4032 which was introduced as a speedup. Modern compilers no longer use DIV/IDIV instructions, potentially making the explicit branch perform worse. Since evaluations spend significantly more time now, the impact of the speedup in search diminishes with old compilers as well. GCC 14.1.0 profile-build, x86-64-vnni512 ``` .text:000000014010FEA9 mov ecx, [rsp+3FB8h+var_3F5C] ... .text:000000014010FEBD mov r10d, ecx .text:000000014010FEC0 imul r10d, ecx .text:000000014010FEC4 mov ecx, dword ptr [rsp+3FB8h+var_3F44+4] .text:000000014010FEC8 add r10d, 3 .text:000000014010FECC mov r11d, r10d .text:000000014010FECF sar r11d, 1 .text:000000014010FED2 cmp [rsp+3FB8h+var_3EE7], 0 .text:000000014010FEDA cmovnz r11d, r10d ``` LLVM 18.1.18 profile-build, x86-64-vnni512 ``` .text:0000000140001EDC mov [rsp+40h+arg_E0], r13 .text:0000000140001EE4 movsxd rcx, r13d .text:0000000140001EE7 mov rax, rcx .text:0000000140001EEA mov [rsp+40h+arg_B8], rcx .text:0000000140001EF2 imul eax, eax .text:0000000140001EF5 add eax, 3 .text:0000000140001EF8 mov ecx, [rsp+40h+arg_8C] .text:0000000140001EFF shrx eax, eax, ecx .text:0000000140001F04 mov [rsp+40h+arg_190], rax ``` Passed non-regression STC: LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 109504 W: 28420 L: 28280 D: 52804 Ptnml(0-2): 355, 12326, 29273, 12420, 378 https://tests.stockfishchess.org/tests/view/6690dc095034141ae599c5fe closes https://github.com/official-stockfish/Stockfish/pull/5478 No functional change --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index a4da8cb002b..26bee2c134e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -75,7 +75,7 @@ Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorseni } constexpr int futility_move_count(bool improving, Depth depth) { - return improving ? (3 + depth * depth) : (3 + depth * depth) / 2; + return (3 + depth * depth) / (2 - improving); } // Add correctionHistory value to raw staticEval and guarantee evaluation From 930915de901b89c7f7d4bf1495c7e949c0d5e546 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 13 Jul 2024 05:34:09 +0300 Subject: [PATCH 1658/1766] Decrease delta Decrease delta in aspiration windows - both initial value and quadratic function of previous best value. Passed STC: https://tests.stockfishchess.org/tests/view/6691a52ec6827afcdcee1569 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 55456 W: 14449 L: 14107 D: 26900 Ptnml(0-2): 174, 6416, 14193, 6784, 161 Passed LTC: https://tests.stockfishchess.org/tests/view/6691aac1c6827afcdcee1625 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 107940 W: 27530 L: 27065 D: 53345 Ptnml(0-2): 52, 11787, 29840, 12226, 65 closes https://github.com/official-stockfish/Stockfish/pull/5479 bench 1547707 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 26bee2c134e..d1e0b32109e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -320,7 +320,7 @@ void Search::Worker::iterative_deepening() { // Reset aspiration window starting size Value avg = rootMoves[pvIdx].averageScore; - delta = 9 + avg * avg / 10424; + delta = 5 + avg * avg / 13424; alpha = std::max(avg - delta, -VALUE_INFINITE); beta = std::min(avg + delta, VALUE_INFINITE); From 558abdbe8a1262b7f15f20ccf961b335c4713364 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Fri, 12 Jul 2024 10:10:00 -0400 Subject: [PATCH 1659/1766] Set best value to futility value after pruned quiet move Passed non-regression STC: https://tests.stockfishchess.org/tests/view/6691592f5034141ae599c68d LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 278496 W: 71818 L: 71865 D: 134813 Ptnml(0-2): 865, 33311, 70978, 33194, 900 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/66918fca5034141ae599e761 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 202986 W: 51048 L: 51013 D: 100925 Ptnml(0-2): 107, 22552, 56133, 22601, 100 closes https://github.com/official-stockfish/Stockfish/pull/5480 bench 1715206 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index d1e0b32109e..ebae94ef18c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1035,7 +1035,7 @@ Value Search::Worker::search( { if (bestValue <= futilityValue && std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY && futilityValue < VALUE_TB_WIN_IN_MAX_PLY) - bestValue = (bestValue + futilityValue * 3) / 4; + bestValue = futilityValue; continue; } From 7395d568329f404cd4dc3f4c2fe093059ac2b391 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sat, 13 Jul 2024 14:44:23 +0300 Subject: [PATCH 1660/1766] bonus calculation for prior countermoves Introduce a new term to the bonus calculation for prior countermoves Passed STC: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 140896 W: 36545 L: 36079 D: 68272 Ptnml(0-2): 383, 16505, 36217, 16949, 394 https://tests.stockfishchess.org/tests/view/6691c73cc6827afcdcee1816 Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 126660 W: 32089 L: 31587 D: 62984 Ptnml(0-2): 63, 13774, 35154, 14276, 63 https://tests.stockfishchess.org/tests/view/6691cdc4c6827afcdcee1930 closes https://github.com/official-stockfish/Stockfish/pull/5483 bench: 1250388 --- src/search.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ebae94ef18c..87310301ff6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1364,12 +1364,13 @@ Value Search::Worker::search( // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (114 * (depth > 5) + 116 * (PvNode || cutNode) + 123 * ((ss - 1)->moveCount > 8) - + 64 * (!ss->inCheck && bestValue <= ss->staticEval - 108) - + 153 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 76)); + int bonus = (138 * (depth > 5) + 58 * (PvNode || cutNode) + 160 * ((ss - 1)->moveCount > 8) + + 84 * (!ss->inCheck && bestValue <= ss->staticEval - 108) + + 153 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 76) + + 32 * (!(ss - 1)->inCheck && bestValue > -(ss - 1)->staticEval + 76)); // Proportional to "how much damage we have to undo" - bonus += std::clamp(-(ss - 1)->statScore / 100, -50, 274); + bonus += std::clamp(-(ss - 1)->statScore / 100, -64, 300); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus / 100); From 2b37b151dd8c4374353d9e185bddbea1cfe300b0 Mon Sep 17 00:00:00 2001 From: MinetaS Date: Sun, 14 Jul 2024 01:03:49 +0900 Subject: [PATCH 1661/1766] Use ValueList to represent searched moves array This PR replaces a pair of array and size with existing ValueList class. Removes two local variables in search and two parameters of update_all_stats. Passed non-regression STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 227040 W: 58472 L: 58463 D: 110105 Ptnml(0-2): 495, 23572, 65427, 23481, 545 https://tests.stockfishchess.org/tests/view/669299204ff211be9d4e98dc closes https://github.com/official-stockfish/Stockfish/pull/5484 No functional change --- src/search.cpp | 75 +++++++++++++++++++++++++------------------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 87310301ff6..1d709749de7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -128,16 +128,14 @@ void update_quiet_histories( const Position& pos, Stack* ss, Search::Worker& workerThread, Move move, int bonus); void update_quiet_stats( const Position& pos, Stack* ss, Search::Worker& workerThread, Move move, int bonus); -void update_all_stats(const Position& pos, - Stack* ss, - Search::Worker& workerThread, - Move bestMove, - Square prevSq, - Move* quietsSearched, - int quietCount, - Move* capturesSearched, - int captureCount, - Depth depth); +void update_all_stats(const Position& pos, + Stack* ss, + Search::Worker& workerThread, + Move bestMove, + Square prevSq, + ValueList& quietsSearched, + ValueList& capturesSearched, + Depth depth); } // namespace @@ -554,7 +552,7 @@ Value Search::Worker::search( assert(0 < depth && depth < MAX_PLY); assert(!(PvNode && cutNode)); - Move pv[MAX_PLY + 1], capturesSearched[32], quietsSearched[32]; + Move pv[MAX_PLY + 1]; StateInfo st; ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize); @@ -563,18 +561,20 @@ Value Search::Worker::search( Depth extension, newDepth; Value bestValue, value, eval, maxValue, probCutBeta; bool givesCheck, improving, priorCapture, opponentWorsening; - bool capture, moveCountPruning, ttCapture; + bool capture, ttCapture; Piece movedPiece; - int moveCount, captureCount, quietCount; + + ValueList capturesSearched; + ValueList quietsSearched; // Step 1. Initialize node Worker* thisThread = this; ss->inCheck = pos.checkers(); priorCapture = pos.captured_piece(); Color us = pos.side_to_move(); - moveCount = captureCount = quietCount = ss->moveCount = 0; - bestValue = -VALUE_INFINITE; - maxValue = VALUE_INFINITE; + ss->moveCount = 0; + bestValue = -VALUE_INFINITE; + maxValue = VALUE_INFINITE; // Check for the available remaining time if (is_mainthread()) @@ -936,8 +936,10 @@ Value Search::Worker::search( MovePicker mp(pos, ttData.move, depth, &thisThread->mainHistory, &thisThread->captureHistory, contHist, &thisThread->pawnHistory, ss->killer); - value = bestValue; - moveCountPruning = false; + value = bestValue; + + int moveCount = 0; + bool moveCountPruning = false; // Step 13. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. @@ -1334,9 +1336,9 @@ Value Search::Worker::search( if (move != bestMove && moveCount <= 32) { if (capture) - capturesSearched[captureCount++] = move; + capturesSearched.push_back(move); else - quietsSearched[quietCount++] = move; + quietsSearched.push_back(move); } } @@ -1358,8 +1360,7 @@ Value Search::Worker::search( // If there is a move that produces search value greater than alpha, // we update the stats of searched moves. else if (bestMove) - update_all_stats(pos, ss, *this, bestMove, prevSq, quietsSearched, quietCount, - capturesSearched, captureCount, depth); + update_all_stats(pos, ss, *this, bestMove, prevSq, quietsSearched, capturesSearched, depth); // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) @@ -1765,16 +1766,14 @@ void update_pv(Move* pv, Move move, const Move* childPv) { // Updates stats at the end of search() when a bestMove is found -void update_all_stats(const Position& pos, - Stack* ss, - Search::Worker& workerThread, - Move bestMove, - Square prevSq, - Move* quietsSearched, - int quietCount, - Move* capturesSearched, - int captureCount, - Depth depth) { +void update_all_stats(const Position& pos, + Stack* ss, + Search::Worker& workerThread, + Move bestMove, + Square prevSq, + ValueList& quietsSearched, + ValueList& capturesSearched, + Depth depth) { CapturePieceToHistory& captureHistory = workerThread.captureHistory; Piece moved_piece = pos.moved_piece(bestMove); @@ -1788,8 +1787,8 @@ void update_all_stats(const Position& pos, update_quiet_stats(pos, ss, workerThread, bestMove, quietMoveBonus); // Decrease stats for all non-best quiet moves - for (int i = 0; i < quietCount; ++i) - update_quiet_histories(pos, ss, workerThread, quietsSearched[i], -quietMoveMalus); + for (Move move : quietsSearched) + update_quiet_histories(pos, ss, workerThread, move, -quietMoveMalus); } else { @@ -1807,11 +1806,11 @@ void update_all_stats(const Position& pos, update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, -quietMoveMalus); // Decrease stats for all non-best capture moves - for (int i = 0; i < captureCount; ++i) + for (Move move : capturesSearched) { - moved_piece = pos.moved_piece(capturesSearched[i]); - captured = type_of(pos.piece_on(capturesSearched[i].to_sq())); - captureHistory[moved_piece][capturesSearched[i].to_sq()][captured] << -quietMoveMalus; + moved_piece = pos.moved_piece(move); + captured = type_of(pos.piece_on(move.to_sq())); + captureHistory[moved_piece][move.to_sq()][captured] << -quietMoveMalus; } } From de2bf1a186ef036a7df06b448f41b00ff62f9322 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 13 Jul 2024 22:18:38 +0300 Subject: [PATCH 1662/1766] Remove quiet history pruning depth limit This patch removes lmrDepth limit for quiet moves history based pruning. Previously removal of this type of depth limits was considered bad because it was performing bad for matetrack - but with this pruning heuristic this shouldn't be that bad because it's "naturally" depth limited by history threshold and should be completely disabled at depth >= 15 or so. Also this heuristic in previous years was known to scale non-linearly - bigger lmrDepth thresholds were better at longer time controls and removing it completely probably should scale pretty well. Passed STC: https://tests.stockfishchess.org/tests/view/6692b89b4ff211be9d4eab21 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 114464 W: 29675 L: 29545 D: 55244 Ptnml(0-2): 372, 12516, 31329, 12640, 375 Passed LTC: https://tests.stockfishchess.org/tests/view/6692c4554ff211be9d4eab3d LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 67746 W: 17182 L: 17014 D: 33550 Ptnml(0-2): 28, 6993, 19652, 7183, 17 closes https://github.com/official-stockfish/Stockfish/pull/5485 Bench: 1250388 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 1d709749de7..3c6617ebdf2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1022,7 +1022,7 @@ Value Search::Worker::search( + thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()]; // Continuation history based pruning (~2 Elo) - if (lmrDepth < 6 && history < -4165 * depth) + if (history < -4165 * depth) continue; history += 2 * thisThread->mainHistory[us][move.from_to()]; From e443b2459e973c47dbf7e46104bf3bb02ffbb6f7 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Sat, 13 Jul 2024 00:40:07 -0400 Subject: [PATCH 1663/1766] Separate eval params for smallnet and main net Values found with spsa around 80% of 120k games at 60+0.6: https://tests.stockfishchess.org/tests/view/669205dac6827afcdcee3ea4 Passed STC: https://tests.stockfishchess.org/tests/view/6692928b4ff211be9d4e98a9 LLR: 2.96 (-2.94,2.94) <0.00,2.00> Total: 313696 W: 81107 L: 80382 D: 152207 Ptnml(0-2): 934, 36942, 80363, 37683, 926 Passed LTC: https://tests.stockfishchess.org/tests/view/6692aab54ff211be9d4e9915 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 228420 W: 57903 L: 57190 D: 113327 Ptnml(0-2): 131, 25003, 63243, 25688, 145 closes https://github.com/official-stockfish/Stockfish/pull/5486 bench 1319322 --- src/evaluate.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 44890a361af..1cff6478fb9 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -79,11 +79,11 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, } // Blend optimism and eval with nnue complexity - optimism += optimism * nnueComplexity / 457; - nnue -= nnue * nnueComplexity / 19157; + optimism += optimism * nnueComplexity / (smallNet ? 433 : 453); + nnue -= nnue * nnueComplexity / (smallNet ? 18815 : 17864); - int material = 554 * pos.count() + pos.non_pawn_material(); - v = (nnue * (73921 + material) + optimism * (8112 + material)) / 73260; + int material = (smallNet ? 553 : 532) * pos.count() + pos.non_pawn_material(); + v = (nnue * (73921 + material) + optimism * (8112 + material)) / (smallNet ? 68104 : 74715); // Evaluation grain (to get more alpha-beta cuts) with randomization (for robustness) v = (v / 16) * 16 - 1 + (pos.key() & 0x2); From c755bc1a73bb10ec0357ca3c98b6de2eb3d9ad63 Mon Sep 17 00:00:00 2001 From: Guenther Demetz Date: Thu, 18 Jul 2024 09:38:17 +0200 Subject: [PATCH 1664/1766] Simplify improving condition if we were in check at our previous move we look back until we weren't in check and take the staticEval of that position as reference. Passed STC: https://tests.stockfishchess.org/tests/view/668ba7b65034141ae5996665 LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 74784 W: 19454 L: 19274 D: 36056 Ptnml(0-2): 260, 8874, 18952, 9038, 268 Passted LTC: https://tests.stockfishchess.org/tests/view/668cb2db5034141ae599678b LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 241488 W: 61166 L: 61171 D: 119151 Ptnml(0-2): 190, 27154, 66062, 27147, 191 closes https://github.com/official-stockfish/Stockfish/pull/5492 bench: 1368313 --- src/search.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 3c6617ebdf2..09918bfdce4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -713,7 +713,7 @@ Value Search::Worker::search( if (ss->inCheck) { // Skip early pruning when in check - ss->staticEval = eval = VALUE_NONE; + ss->staticEval = eval = (ss - 2)->staticEval; improving = false; goto moves_loop; } @@ -764,12 +764,9 @@ Value Search::Worker::search( // Set up the improving flag, which is true if current static evaluation is // bigger than the previous static evaluation at our turn (if we were in - // check at our previous move we look at static evaluation at move prior to it - // and if we were in check at move prior to it flag is set to true) and is + // check at our previous move we go back until we weren't in check) and is // false otherwise. The improving flag is used in various pruning heuristics. - improving = (ss - 2)->staticEval != VALUE_NONE - ? ss->staticEval > (ss - 2)->staticEval - : (ss - 4)->staticEval != VALUE_NONE && ss->staticEval > (ss - 4)->staticEval; + improving = ss->staticEval > (ss - 2)->staticEval; opponentWorsening = ss->staticEval + (ss - 1)->staticEval > 2; From 7bb45d05faa62463f4c791749907f4c50ceee990 Mon Sep 17 00:00:00 2001 From: yl25946 Date: Mon, 15 Jul 2024 14:55:38 -0500 Subject: [PATCH 1665/1766] Replace ternary with std::min equivalent and more readable. closes https://github.com/official-stockfish/Stockfish/pull/5488 No functional change --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 09918bfdce4..e34aabba199 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -90,7 +90,7 @@ Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { int stat_bonus(Depth d) { return std::min(190 * d - 108, 1596); } // History and stats update malus, based on depth -int stat_malus(Depth d) { return (d < 4 ? 736 * d - 268 : 2044); } +int stat_malus(Depth d) { return std::min(736 * d - 268, 2044); } // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(size_t nodes) { return VALUE_DRAW - 1 + Value(nodes & 0x2); } From 27042fe9497f721abbfccab50ebb6a0641e63b21 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Sat, 6 Jul 2024 22:31:45 -0400 Subject: [PATCH 1666/1766] Linearize corrHist Passed STC: https://tests.stockfishchess.org/tests/view/66919cdec6827afcdcee146f LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 130656 W: 33579 L: 33461 D: 63616 Ptnml(0-2): 394, 15548, 33318, 15682, 386 Passed VVLTC: https://tests.stockfishchess.org/tests/view/6691acb2c6827afcdcee1645 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 160314 W: 40925 L: 40854 D: 78535 Ptnml(0-2): 12, 14754, 50551, 14831, 9 closes https://github.com/official-stockfish/Stockfish/pull/5489 bench 1380295 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index e34aabba199..218d1ce4bb4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -82,7 +82,7 @@ constexpr int futility_move_count(bool improving, Depth depth) { // does not hit the tablebase range. Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { auto cv = w.correctionHistory[pos.side_to_move()][pawn_structure_index(pos)]; - v += cv * std::abs(cv) / 5073; + v += 66 * cv / 512; return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } From c8d8e362fcf58238da07aeb31f6fa029cf9828c6 Mon Sep 17 00:00:00 2001 From: "Shahin M. Shahin" Date: Sun, 14 Jul 2024 21:36:19 +0300 Subject: [PATCH 1667/1766] Try nullmoves only on cutnodes since master only tries nullmoves on cutNodes already with 99.0224% of the cases running bench, We can try null moves at 100% of cutNodes and achieve such simplification, by making passing false already equivalent to passing !cutNode This is a more correct form of PR #5482 Passed non-regression STC: https://tests.stockfishchess.org/tests/view/66941c044ff211be9d4ebf5f LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 153216 W: 39856 L: 39764 D: 73596 Ptnml(0-2): 590, 18174, 38979, 18284, 581 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/6694e5cd4ff211be9d4ebfdf LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 67842 W: 17178 L: 17004 D: 33660 Ptnml(0-2): 52, 7437, 18759, 7631, 42 closes https://github.com/official-stockfish/Stockfish/pull/5490 bench: 1345400 Co-Authored-By: FauziAkram <11150271+fauziakram@users.noreply.github.com> --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 218d1ce4bb4..c03a30f5675 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -791,7 +791,7 @@ Value Search::Worker::search( return beta + (eval - beta) / 3; // Step 9. Null move search with verification search (~35 Elo) - if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 14389 + if (cutNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 14389 && eval >= beta && ss->staticEval >= beta - 21 * depth + 390 && !excludedMove && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly && beta > VALUE_TB_LOSS_IN_MAX_PLY) @@ -806,7 +806,7 @@ Value Search::Worker::search( pos.do_null_move(st, tt); - Value nullValue = -search(pos, ss + 1, -beta, -beta + 1, depth - R, !cutNode); + Value nullValue = -search(pos, ss + 1, -beta, -beta + 1, depth - R, false); pos.undo_null_move(); From c2837769e0d43f1195081c2aa97b7028b27dee73 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Wed, 17 Jul 2024 00:15:44 -0400 Subject: [PATCH 1668/1766] Avoid calculating nnue complexity twice Passed non-regression STC: https://tests.stockfishchess.org/tests/view/6697459d4ff211be9d4ec236 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 146848 W: 38289 L: 38189 D: 70370 Ptnml(0-2): 503, 16665, 39046, 16649, 561 closes https://github.com/official-stockfish/Stockfish/pull/5493 No functional change --- src/evaluate.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 1cff6478fb9..d0c553ffc8a 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -66,19 +66,18 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, auto [psqt, positional] = smallNet ? networks.small.evaluate(pos, &caches.small) : networks.big.evaluate(pos, &caches.big); - Value nnue = (125 * psqt + 131 * positional) / 128; - int nnueComplexity = std::abs(psqt - positional); + Value nnue = (125 * psqt + 131 * positional) / 128; // Re-evaluate the position when higher eval accuracy is worth the time spent if (smallNet && (nnue * simpleEval < 0 || std::abs(nnue) < 227)) { std::tie(psqt, positional) = networks.big.evaluate(pos, &caches.big); nnue = (125 * psqt + 131 * positional) / 128; - nnueComplexity = std::abs(psqt - positional); smallNet = false; } // Blend optimism and eval with nnue complexity + int nnueComplexity = std::abs(psqt - positional); optimism += optimism * nnueComplexity / (smallNet ? 433 : 453); nnue -= nnue * nnueComplexity / (smallNet ? 18815 : 17864); From a8401e803d37ec7dbf0650f4d79475214655477e Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Thu, 18 Jul 2024 16:30:42 +0300 Subject: [PATCH 1669/1766] Adjust bonus to move that caused a fail low This is an elo gainer and simultaneously a minor logical fix to bonuses that caused a fail low. It increases maximum of statscore based subtraction - but disallows negative bonuses. Passed STC: https://tests.stockfishchess.org/tests/view/66955e6f4ff211be9d4ec063 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 44640 W: 11805 L: 11472 D: 21363 Ptnml(0-2): 166, 5178, 11335, 5439, 202 Passed LTC: https://tests.stockfishchess.org/tests/view/66963fde4ff211be9d4ec190 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 72288 W: 18478 L: 18082 D: 35728 Ptnml(0-2): 50, 7919, 19825, 8285, 65 closes https://github.com/official-stockfish/Stockfish/pull/5494 Bench: 1477054 --- src/search.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index c03a30f5675..945f8b408e3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1368,7 +1368,9 @@ Value Search::Worker::search( + 32 * (!(ss - 1)->inCheck && bestValue > -(ss - 1)->staticEval + 76)); // Proportional to "how much damage we have to undo" - bonus += std::clamp(-(ss - 1)->statScore / 100, -64, 300); + bonus += std::clamp(-(ss - 1)->statScore / 100, -94, 300); + + bonus = std::max(bonus, 0); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, stat_bonus(depth) * bonus / 100); From 1fb4dc2e0f0dbeddff889bcd75466e4be4fe1ad6 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Fri, 19 Jul 2024 21:26:51 +0200 Subject: [PATCH 1670/1766] Enable syzygy in the matetrack action now checks correctness of PV lines with TB score. uses 3-4-5 man table bases, downloaded from lichess, which are cached with the appropriate action. closes https://github.com/official-stockfish/Stockfish/pull/5500 No functional change --- .github/workflows/matetrack.yml | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/.github/workflows/matetrack.yml b/.github/workflows/matetrack.yml index de65209fb29..dc8dff8d57f 100644 --- a/.github/workflows/matetrack.yml +++ b/.github/workflows/matetrack.yml @@ -24,15 +24,31 @@ jobs: with: repository: vondele/matetrack path: matetrack - ref: 20287a1a145f30a166b7ef251eddb611e4e44fbf + ref: 814160f82e6428ed2f6522dc06c2a6fa539cd413 persist-credentials: false - name: matetrack install deps working-directory: matetrack run: pip install -r requirements.txt + - name: cache syzygy + id: cache-syzygy + uses: actions/cache@v4 + with: + path: | + matetrack/3-4-5-wdl/ + matetrack/3-4-5-dtz/ + key: key-syzygy + + - name: download syzygy 3-4-5 if needed + working-directory: matetrack + if: steps.cache-syzygy.outputs.cache-hit != 'true' + run: | + wget --no-verbose -r -nH --cut-dirs=2 --no-parent --reject="index.html*" -e robots=off https://tablebase.lichess.ovh/tables/standard/3-4-5-wdl/ + wget --no-verbose -r -nH --cut-dirs=2 --no-parent --reject="index.html*" -e robots=off https://tablebase.lichess.ovh/tables/standard/3-4-5-dtz/ + - name: Run matetrack working-directory: matetrack run: | - python matecheck.py --engine /home/runner/work/Stockfish/Stockfish/Stockfish/src/stockfish --epdFile mates2000.epd --nodes 100000 | tee matecheckout.out + python matecheck.py --syzygyPath 3-4-5-wdl/:3-4-5-dtz/ --engine /home/runner/work/Stockfish/Stockfish/Stockfish/src/stockfish --epdFile mates2000.epd --nodes 100000 | tee matecheckout.out ! grep "issues were detected" matecheckout.out > /dev/null From e57fba7fc9be461cbb97c063b269a1e231cdd284 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sun, 21 Jul 2024 13:15:12 +0200 Subject: [PATCH 1671/1766] Fix TB PV extension and MultiPV in the case of MultiPV, the first move of the Nth multiPV could actually turn a winning position in a losing one, so don't attempt to correct it. Instead, always perform the first move without correction. Fixes #5505 Closes https://github.com/official-stockfish/Stockfish/pull/5506 No functional change --- src/search.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 945f8b408e3..435af4b244b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1948,8 +1948,12 @@ void syzygy_extend_pv(const OptionsMap& options, std::list sts; + // Step 0, do the rootMove, no correction allowed, as needed for MultiPV in TB. + auto& stRoot = sts.emplace_back(); + pos.do_move(rootMove.pv[0], stRoot); + int ply = 1; + // Step 1, walk the PV to the last position in TB with correct decisive score - int ply = 0; while (size_t(ply) < rootMove.pv.size()) { Move& pvMove = rootMove.pv[ply]; From 703f17975bd9c29172a27f795ca6b5a7d0a32b25 Mon Sep 17 00:00:00 2001 From: Dubslow Date: Thu, 2 May 2024 05:35:15 -0500 Subject: [PATCH 1672/1766] Remove QS_CHECKS movepick stage Passed STC: https://tests.stockfishchess.org/tests/view/669597cf4ff211be9d4ec147 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 199072 W: 52100 L: 52058 D: 94914 Ptnml(0-2): 829, 23679, 50406, 23865, 757 Passed LTC: https://tests.stockfishchess.org/tests/view/66988f5f4ff211be9d4ec33e LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 119778 W: 30420 L: 30299 D: 59059 Ptnml(0-2): 106, 13293, 32957, 13440, 93 closes https://github.com/official-stockfish/Stockfish/pull/5498 Bench 1499842 --- src/movegen.cpp | 54 ++++++++++++++---------------------------------- src/movegen.h | 1 - src/movepick.cpp | 22 +------------------- src/search.cpp | 10 ++------- src/types.h | 3 +-- 5 files changed, 19 insertions(+), 71 deletions(-) diff --git a/src/movegen.cpp b/src/movegen.cpp index e6923067f88..69b8fe6ae2b 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -75,17 +75,6 @@ ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard ta b2 &= target; } - if constexpr (Type == QUIET_CHECKS) - { - // To make a quiet check, you either make a direct check by pushing a pawn - // or push a blocker pawn that is not on the same file as the enemy king. - // Discovered check promotion has been already generated amongst the captures. - Square ksq = pos.square(Them); - Bitboard dcCandidatePawns = pos.blockers_for_king(Them) & ~file_bb(ksq); - b1 &= pawn_attacks_bb(Them, ksq) | shift(dcCandidatePawns); - b2 &= pawn_attacks_bb(Them, ksq) | shift(dcCandidatePawns); - } - while (b1) { Square to = pop_lsb(b1); @@ -158,7 +147,7 @@ ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard ta } -template +template ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) { static_assert(Pt != KING && Pt != PAWN, "Unsupported piece type in generate_moves()"); @@ -170,10 +159,6 @@ ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target) Square from = pop_lsb(bb); Bitboard b = attacks_bb(from, pos.pieces()) & target; - // To check, you either move freely a blocker or make a direct check. - if (Checks && (Pt == QUEEN || !(pos.blockers_for_king(~Us) & from))) - b &= pos.check_squares(Pt); - while (b) *moveList++ = Move(from, pop_lsb(b)); } @@ -187,9 +172,8 @@ ExtMove* generate_all(const Position& pos, ExtMove* moveList) { static_assert(Type != LEGAL, "Unsupported type in generate_all()"); - constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantiations - const Square ksq = pos.square(Us); - Bitboard target; + const Square ksq = pos.square(Us); + Bitboard target; // Skip generating non-king moves when in double check if (Type != EVASIONS || !more_than_one(pos.checkers())) @@ -197,29 +181,24 @@ ExtMove* generate_all(const Position& pos, ExtMove* moveList) { target = Type == EVASIONS ? between_bb(ksq, lsb(pos.checkers())) : Type == NON_EVASIONS ? ~pos.pieces(Us) : Type == CAPTURES ? pos.pieces(~Us) - : ~pos.pieces(); // QUIETS || QUIET_CHECKS + : ~pos.pieces(); // QUIETS moveList = generate_pawn_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, target); - moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); + moveList = generate_moves(pos, moveList, target); } - if (!Checks || pos.blockers_for_king(~Us) & ksq) - { - Bitboard b = attacks_bb(ksq) & (Type == EVASIONS ? ~pos.pieces(Us) : target); - if (Checks) - b &= ~attacks_bb(pos.square(~Us)); + Bitboard b = attacks_bb(ksq) & (Type == EVASIONS ? ~pos.pieces(Us) : target); - while (b) - *moveList++ = Move(ksq, pop_lsb(b)); + while (b) + *moveList++ = Move(ksq, pop_lsb(b)); - if ((Type == QUIETS || Type == NON_EVASIONS) && pos.can_castle(Us & ANY_CASTLING)) - for (CastlingRights cr : {Us & KING_SIDE, Us & QUEEN_SIDE}) - if (!pos.castling_impeded(cr) && pos.can_castle(cr)) - *moveList++ = Move::make(ksq, pos.castling_rook_square(cr)); - } + if ((Type == QUIETS || Type == NON_EVASIONS) && pos.can_castle(Us & ANY_CASTLING)) + for (CastlingRights cr : {Us & KING_SIDE, Us & QUEEN_SIDE}) + if (!pos.castling_impeded(cr) && pos.can_castle(cr)) + *moveList++ = Move::make(ksq, pos.castling_rook_square(cr)); return moveList; } @@ -231,8 +210,6 @@ ExtMove* generate_all(const Position& pos, ExtMove* moveList) { // Generates all pseudo-legal non-captures and underpromotions // Generates all pseudo-legal check evasions // Generates all pseudo-legal captures and non-captures -// Generates all pseudo-legal non-captures giving check, -// except castling and promotions // // Returns a pointer to the end of the move list. template @@ -251,7 +228,6 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { template ExtMove* generate(const Position&, ExtMove*); template ExtMove* generate(const Position&, ExtMove*); template ExtMove* generate(const Position&, ExtMove*); -template ExtMove* generate(const Position&, ExtMove*); template ExtMove* generate(const Position&, ExtMove*); diff --git a/src/movegen.h b/src/movegen.h index 5f650d2e36d..f067f8808b6 100644 --- a/src/movegen.h +++ b/src/movegen.h @@ -31,7 +31,6 @@ class Position; enum GenType { CAPTURES, QUIETS, - QUIET_CHECKS, EVASIONS, NON_EVASIONS, LEGAL diff --git a/src/movepick.cpp b/src/movepick.cpp index 55bacf6e747..81384328729 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -52,9 +52,7 @@ enum Stages { // generate qsearch moves QSEARCH_TT, QCAPTURE_INIT, - QCAPTURE, - QCHECK_INIT, - QCHECK + QCAPTURE }; // Sort moves in descending order up to and including a given limit. @@ -316,24 +314,6 @@ Move MovePicker::next_move(bool skipQuiets) { return select([&]() { return pos.see_ge(*cur, threshold); }); case QCAPTURE : - if (select([]() { return true; })) - return *(cur - 1); - - // If we found no move and the depth is too low to try checks, then we have finished - if (depth <= DEPTH_QS_NORMAL) - return Move::none(); - - ++stage; - [[fallthrough]]; - - case QCHECK_INIT : - cur = moves; - endMoves = generate(pos, cur); - - ++stage; - [[fallthrough]]; - - case QCHECK : return select([]() { return true; }); } diff --git a/src/search.cpp b/src/search.cpp index 435af4b244b..fd9fa6da094 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1475,12 +1475,6 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, assert(0 <= ss->ply && ss->ply < MAX_PLY); - // Note that unlike regular search, which stores the literal depth into the - // transposition table, from qsearch we only store the current movegen stage - // as "depth". If in check, we search all evasions and thus store DEPTH_QS_CHECKS. - // Evasions may be quiet, and _CHECKS includes quiets. - Depth qsTtDepth = ss->inCheck || depth >= DEPTH_QS_CHECKS ? DEPTH_QS_CHECKS : DEPTH_QS_NORMAL; - // Step 3. Transposition table lookup posKey = pos.key(); auto [ttHit, ttData, ttWriter] = tt.probe(posKey); @@ -1491,7 +1485,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, pvHit = ttHit && ttData.is_pv; // At non-PV nodes we check for an early TT cutoff - if (!PvNode && ttData.depth >= qsTtDepth + if (!PvNode && ttData.depth >= DEPTH_QS && ttData.value != VALUE_NONE // Can happen when !ttHit or when access race in probe() && (ttData.bound & (ttData.value >= beta ? BOUND_LOWER : BOUND_UPPER))) return ttData.value; @@ -1674,7 +1668,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, // Save gathered info in transposition table. The static evaluation // is saved as it was before adjustment by correction history. ttWriter.write(posKey, value_to_tt(bestValue, ss->ply), pvHit, - bestValue >= beta ? BOUND_LOWER : BOUND_UPPER, qsTtDepth, bestMove, + bestValue >= beta ? BOUND_LOWER : BOUND_UPPER, DEPTH_QS, bestMove, unadjustedStaticEval, tt.generation()); assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); diff --git a/src/types.h b/src/types.h index 8a9400bb8f8..b12491d6cdd 100644 --- a/src/types.h +++ b/src/types.h @@ -194,8 +194,7 @@ enum : int { // quiescence search, however, the transposition table entries only store // the current quiescence move generation stage (which should thus compare // lower than any regular search depth). - DEPTH_QS_CHECKS = 0, - DEPTH_QS_NORMAL = -1, + DEPTH_QS = 0, // For transposition table entries where no searching at all was done // (whether regular or qsearch) we use DEPTH_UNSEARCHED, which should thus // compare lower than any quiescence or regular depth. DEPTH_ENTRY_OFFSET From a2ba3e33628bed0930f50c54a5ae4f30b853b3b8 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sat, 20 Jul 2024 13:34:27 +0300 Subject: [PATCH 1673/1766] Bonus Simplification This tune removes completely a recently added term. Passed STC: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 61376 W: 16046 L: 15693 D: 29637 Ptnml(0-2): 207, 7132, 15665, 7469, 215 https://tests.stockfishchess.org/tests/view/669512b94ff211be9d4ebffb Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 100662 W: 25474 L: 25020 D: 50168 Ptnml(0-2): 64, 11092, 27581, 11514, 80 https://tests.stockfishchess.org/tests/view/66955f194ff211be9d4ec06a Passed LTC#2: LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 28056 W: 7128 L: 6909 D: 14019 Ptnml(0-2): 18, 3084, 7620, 3273, 33 https://tests.stockfishchess.org/tests/view/669a541a4ff211be9d4ec52b closes https://github.com/official-stockfish/Stockfish/pull/5502 bench: 1619438 --- src/search.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index fd9fa6da094..f51a749995b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -189,7 +189,7 @@ void Search::Worker::start_searching() { {} // Busy wait for a stop or a ponder reset // Stop the threads if not already stopped (also raise the stop if - // "ponderhit" just reset threads.ponder). + // "ponderhit" just reset threads.ponder) threads.stop = true; // Wait until all threads have finished @@ -1362,20 +1362,19 @@ Value Search::Worker::search( // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (138 * (depth > 5) + 58 * (PvNode || cutNode) + 160 * ((ss - 1)->moveCount > 8) - + 84 * (!ss->inCheck && bestValue <= ss->staticEval - 108) - + 153 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 76) - + 32 * (!(ss - 1)->inCheck && bestValue > -(ss - 1)->staticEval + 76)); + int bonus = (122 * (depth > 5) + 39 * (PvNode || cutNode) + 165 * ((ss - 1)->moveCount > 8) + + 107 * (!ss->inCheck && bestValue <= ss->staticEval - 98) + + 134 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 91)); // Proportional to "how much damage we have to undo" - bonus += std::clamp(-(ss - 1)->statScore / 100, -94, 300); + bonus += std::clamp(-(ss - 1)->statScore / 100, -94, 304); bonus = std::max(bonus, 0); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, - stat_bonus(depth) * bonus / 100); + stat_bonus(depth) * bonus / 116); thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] - << stat_bonus(depth) * bonus / 200; + << stat_bonus(depth) * bonus / 180; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) From 986173264f4c03e3750bd68f904bfdf1152437d4 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sun, 21 Jul 2024 18:52:26 +0300 Subject: [PATCH 1674/1766] Adding LowestElo and HighestElo constants These values represent the lowest Elo rating in the skill level calculation, and the highest one, but it's not clear from the code where these values come from other than the comment. This should improve code readability and maintainability. It makes the purpose of the values clear and allows for easy modification if the Elo range for skill level calculation changes in the future. Moved the Skill struct definition from search.cpp to search.h header file to define the Search::Skill struct, making it accessible from other files. closes https://github.com/official-stockfish/Stockfish/pull/5508 No functional change --- src/engine.cpp | 4 +++- src/search.cpp | 25 ------------------------- src/search.h | 29 +++++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/src/engine.cpp b/src/engine.cpp index 41b19ac6859..498b7c3e741 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -91,7 +91,9 @@ Engine::Engine(std::string path) : options["nodestime"] << Option(0, 0, 10000); options["UCI_Chess960"] << Option(false); options["UCI_LimitStrength"] << Option(false); - options["UCI_Elo"] << Option(1320, 1320, 3190); + options["UCI_Elo"] << Option(Stockfish::Search::Skill::LowestElo, + Stockfish::Search::Skill::LowestElo, + Stockfish::Search::Skill::HighestElo); options["UCI_ShowWDL"] << Option(false); options["SyzygyPath"] << Option("", [](const Option& o) { Tablebases::init(o); diff --git a/src/search.cpp b/src/search.cpp index f51a749995b..0d9824b7701 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -94,31 +94,6 @@ int stat_malus(Depth d) { return std::min(736 * d - 268, 2044); } // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(size_t nodes) { return VALUE_DRAW - 1 + Value(nodes & 0x2); } - -// Skill structure is used to implement strength limit. If we have a UCI_Elo, -// we convert it to an appropriate skill level, anchored to the Stash engine. -// This method is based on a fit of the Elo results for games played between -// Stockfish at various skill levels and various versions of the Stash engine. -// Skill 0 .. 19 now covers CCRL Blitz Elo from 1320 to 3190, approximately -// Reference: https://github.com/vondele/Stockfish/commit/a08b8d4e9711c2 -struct Skill { - Skill(int skill_level, int uci_elo) { - if (uci_elo) - { - double e = double(uci_elo - 1320) / (3190 - 1320); - level = std::clamp((((37.2473 * e - 40.8525) * e + 22.2943) * e - 0.311438), 0.0, 19.0); - } - else - level = double(skill_level); - } - bool enabled() const { return level < 20.0; } - bool time_to_pick(Depth depth) const { return depth == 1 + int(level); } - Move pick_best(const RootMoves&, size_t multiPV); - - double level; - Move best = Move::none(); -}; - Value value_to_tt(Value v, int ply); Value value_from_tt(Value v, int ply, int r50c); void update_pv(Move* pv, Move move, const Move* childPv); diff --git a/src/search.h b/src/search.h index 65394bc0763..d42b5fba9b7 100644 --- a/src/search.h +++ b/src/search.h @@ -19,6 +19,7 @@ #ifndef SEARCH_H_INCLUDED #define SEARCH_H_INCLUDED +#include #include #include #include @@ -180,6 +181,34 @@ struct InfoIteration { size_t currmovenumber; }; +// Skill structure is used to implement strength limit. If we have a UCI_Elo, +// we convert it to an appropriate skill level, anchored to the Stash engine. +// This method is based on a fit of the Elo results for games played between +// Stockfish at various skill levels and various versions of the Stash engine. +// Skill 0 .. 19 now covers CCRL Blitz Elo from 1320 to 3190, approximately +// Reference: https://github.com/vondele/Stockfish/commit/a08b8d4e9711c2 +struct Skill { + // Lowest and highest Elo ratings used in the skill level calculation + constexpr static int LowestElo = 1320; + constexpr static int HighestElo = 3190; + + Skill(int skill_level, int uci_elo) { + if (uci_elo) + { + double e = double(uci_elo - LowestElo) / (HighestElo - LowestElo); + level = std::clamp((((37.2473 * e - 40.8525) * e + 22.2943) * e - 0.311438), 0.0, 19.0); + } + else + level = double(skill_level); + } + bool enabled() const { return level < 20.0; } + bool time_to_pick(Depth depth) const { return depth == 1 + int(level); } + Move pick_best(const RootMoves&, size_t multiPV); + + double level; + Move best = Move::none(); +}; + // SearchManager manages the search from the main thread. It is responsible for // keeping track of the time, and storing data strictly related to the main thread. class SearchManager: public ISearchManager { From bb4b01e3063d5ad19679d51140d8e9f0599ac538 Mon Sep 17 00:00:00 2001 From: "Shahin M. Shahin" Date: Fri, 19 Jul 2024 13:27:30 +0300 Subject: [PATCH 1675/1766] Fix TB guard even if beta is below TB range, once we return probcutBeta with beta + 390 we can return wrong TB value, and guard against ttData.value being `VALUE_NONE` closes https://github.com/official-stockfish/Stockfish/pull/5499 bench: 1440277 --- src/search.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 0d9824b7701..ca3465029b1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -894,7 +894,8 @@ Value Search::Worker::search( // Step 12. A small Probcut idea (~4 Elo) probCutBeta = beta + 390; if ((ttData.bound & BOUND_LOWER) && ttData.depth >= depth - 4 && ttData.value >= probCutBeta - && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY) + && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY + && std::abs(ttData.value) < VALUE_TB_WIN_IN_MAX_PLY) return probCutBeta; const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, From 1e2f0511033945d07e1c8856980ed72cdbe42822 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Wed, 17 Jul 2024 00:03:10 -0400 Subject: [PATCH 1676/1766] Replace simple eval with psqt in re-eval condition As a result, re-eval depends only on smallnet outputs so an extra call to simple eval can be removed. Passed non-regression STC: https://tests.stockfishchess.org/tests/view/669743054ff211be9d4ec232 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 214912 W: 55801 L: 55777 D: 103334 Ptnml(0-2): 746, 24597, 56760, 24593, 760 https://github.com/official-stockfish/Stockfish/pull/5501 Bench: 1440277 --- src/evaluate.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d0c553ffc8a..221ccde8b8b 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -59,8 +59,7 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, assert(!pos.checkers()); - int simpleEval = simple_eval(pos, pos.side_to_move()); - bool smallNet = use_smallnet(pos); + bool smallNet = use_smallnet(pos); int v; auto [psqt, positional] = smallNet ? networks.small.evaluate(pos, &caches.small) @@ -69,7 +68,7 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, Value nnue = (125 * psqt + 131 * positional) / 128; // Re-evaluate the position when higher eval accuracy is worth the time spent - if (smallNet && (nnue * simpleEval < 0 || std::abs(nnue) < 227)) + if (smallNet && (nnue * psqt < 0 || std::abs(nnue) < 227)) { std::tie(psqt, positional) = networks.big.evaluate(pos, &caches.big); nnue = (125 * psqt + 131 * positional) / 128; From 985b9fd7b05d1d81be7a1ac90862a5790ee56176 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sat, 20 Jul 2024 12:40:16 -0700 Subject: [PATCH 1677/1766] Remove Killer Heuristic In Move Ordering Passed Non-regression STC: LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 80480 W: 20979 L: 20802 D: 38699 Ptnml(0-2): 279, 9610, 20337, 9683, 331 https://tests.stockfishchess.org/tests/view/669c12c14ff211be9d4ec69b Passed Non-regression LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 77988 W: 19788 L: 19624 D: 38576 Ptnml(0-2): 66, 8605, 21481, 8783, 59 https://tests.stockfishchess.org/tests/view/669d628a4ff211be9d4ec7a8 closes https://github.com/official-stockfish/Stockfish/pull/5511 bench 1367740 --- src/movepick.cpp | 6 +----- src/movepick.h | 5 ++--- src/search.cpp | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 81384328729..7bd0252c819 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -86,15 +86,13 @@ MovePicker::MovePicker(const Position& p, const ButterflyHistory* mh, const CapturePieceToHistory* cph, const PieceToHistory** ch, - const PawnHistory* ph, - Move km) : + const PawnHistory* ph) : pos(p), mainHistory(mh), captureHistory(cph), continuationHistory(ch), pawnHistory(ph), ttMove(ttm), - killer(km), depth(d) { if (pos.checkers()) @@ -164,8 +162,6 @@ void MovePicker::score() { m.value += (*continuationHistory[3])[pc][to]; m.value += (*continuationHistory[5])[pc][to]; - m.value += (m == killer) * 65536; - // bonus for checks m.value += bool(pos.check_squares(pt) & to) * 16384; diff --git a/src/movepick.h b/src/movepick.h index 92e11de2bf3..671cbb9cead 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -159,8 +159,7 @@ class MovePicker { const ButterflyHistory*, const CapturePieceToHistory*, const PieceToHistory**, - const PawnHistory*, - Move killer = Move::none()); + const PawnHistory*); MovePicker(const Position&, Move, int, const CapturePieceToHistory*); Move next_move(bool skipQuiets = false); @@ -177,7 +176,7 @@ class MovePicker { const CapturePieceToHistory* captureHistory; const PieceToHistory** continuationHistory; const PawnHistory* pawnHistory; - Move ttMove, killer; + Move ttMove; ExtMove * cur, *endMoves, *endBadCaptures, *beginBadQuiets, *endBadQuiets; int stage; int threshold; diff --git a/src/search.cpp b/src/search.cpp index ca3465029b1..233dc4f71ca 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -907,7 +907,7 @@ Value Search::Worker::search( MovePicker mp(pos, ttData.move, depth, &thisThread->mainHistory, &thisThread->captureHistory, - contHist, &thisThread->pawnHistory, ss->killer); + contHist, &thisThread->pawnHistory); value = bestValue; From 836154acb5ba447a46196a64d6bbab5a5b31ea1b Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Tue, 23 Jul 2024 16:36:49 +0300 Subject: [PATCH 1678/1766] Introduce pre-qsearch ttmove extensions at pv nodes The idea is that we are about to dive into qsearch (next search depth is <= 0) but since we have the move in transposition table we should extend that move and evaluate it with more precise search - because branch seems important. Passed STC: https://tests.stockfishchess.org/tests/view/6699d2564ff211be9d4ec488 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 83104 W: 21789 L: 21401 D: 39914 Ptnml(0-2): 293, 9748, 21128, 10044, 339 Passed LTC: https://tests.stockfishchess.org/tests/view/669b3f1a4ff211be9d4ec602 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 136098 W: 34636 L: 34111 D: 67351 Ptnml(0-2): 105, 14882, 37550, 15407, 105 closes https://github.com/official-stockfish/Stockfish/pull/5512 bench 1526129 --- src/search.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index 233dc4f71ca..5e260247ca9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1206,6 +1206,10 @@ Value Search::Worker::search( (ss + 1)->pv = pv; (ss + 1)->pv[0] = Move::none(); + // Extend move from transposition table if we are about to dive into qsearch. + if (move == ttData.move && ss->ply <= thisThread->rootDepth * 2) + newDepth = std::max(newDepth, 1); + value = -search(pos, ss + 1, -beta, -alpha, newDepth, false); } From b55217fd02d8e5bc0754e5f27bc84df7b01479a6 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Mon, 15 Jul 2024 11:39:02 -0400 Subject: [PATCH 1679/1766] Update default main net to nn-31337bea577c.nnue Created by updating output weights (256) and biases (8) of the previous main net with values found with spsa around 101k / 120k games at 140+1.4. 264 spsa params: output weights and biases in nn-e8bac1c07a5a.nnue A: 6000, alpha: 0.602, gamma: 0.101 weights: [-127, 127], c_end = 6 biases: [-8192, 8192], c_end = 64 Among the 264 params, 189 weights and all 8 biases were changed. Changes in the weights: - mean: -0.111 +/- 3.57 - range: [-8, 8] Found with the same method as: https://github.com/official-stockfish/Stockfish/pull/5459 Due to the original name (nn-ea8c9128c325.nnue) being too similar to the previous main net (nn-e8bac1c07a5a.nnue) and creating confusion, it was renamed by making non-functional changes to the .nnue file the same way as past nets with: https://github.com/linrock/nnue-namer To verify that bench is the same and view the modified non-functional bytes: ``` echo -e "setoption name EvalFile value nn-ea8c9128c325.nnue\nbench" | ./stockfish echo -e "setoption name EvalFile value nn-31337bea577c.nnue\nbench" | ./stockfish cmp -l nn-ea8c9128c325.nnue nn-31337bea577c.nnue diff <(xxd nn-ea8c9128c325.nnue) <(xxd nn-31337bea577c.nnue) ``` Passed STC: https://tests.stockfishchess.org/tests/view/669564154ff211be9d4ec080 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 57280 W: 15139 L: 14789 D: 27352 Ptnml(0-2): 209, 6685, 14522, 6995, 229 Passed LTC: https://tests.stockfishchess.org/tests/view/669694204ff211be9d4ec1b4 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 63030 W: 16093 L: 15720 D: 31217 Ptnml(0-2): 47, 6766, 17516, 7139, 47 closes https://github.com/official-stockfish/Stockfish/pull/5509 bench 1371485 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 4b5f447ee32..55838243342 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -33,7 +33,7 @@ namespace Eval { // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro or the location where this macro is defined, as it is used // in the Makefile/Fishtest. -#define EvalFileDefaultNameBig "nn-e8bac1c07a5a.nnue" +#define EvalFileDefaultNameBig "nn-31337bea577c.nnue" #define EvalFileDefaultNameSmall "nn-37f18f62d772.nnue" namespace NNUE { From 85893ac1cd1933f9d24700026972b278f5a37b9c Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sat, 20 Jul 2024 12:41:56 -0700 Subject: [PATCH 1680/1766] Simplify Away Killer Condition in Cutnode LMR Passed Non-regression STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 42944 W: 11240 L: 11024 D: 20680 Ptnml(0-2): 159, 5056, 10825, 5274, 158 https://tests.stockfishchess.org/tests/view/669c13384ff211be9d4ec69f Passed Non-regression LTC: LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 163548 W: 41366 L: 41289 D: 80893 Ptnml(0-2): 109, 18246, 45007, 18283, 129 https://tests.stockfishchess.org/tests/view/669cb1254ff211be9d4ec73a closes https://github.com/official-stockfish/Stockfish/pull/5513 Bench: 1178570 --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5e260247ca9..e2df475e9e1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1131,8 +1131,7 @@ Value Search::Worker::search( // Increase reduction for cut nodes (~4 Elo) if (cutNode) - r += 2 - (ttData.depth >= depth && ss->ttPv) - + (!ss->ttPv && move != ttData.move && move != ss->killer); + r += 2 - (ttData.depth >= depth && ss->ttPv) + (!ss->ttPv && move != ttData.move); // Increase reduction if ttMove is a capture (~3 Elo) if (ttCapture) From 607c3e404fc706d09bd3b276ddd563d636823533 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Wed, 24 Jul 2024 18:25:08 +0300 Subject: [PATCH 1681/1766] Remove unneeded depth tracking in qsearch Since simplification of quiet checks in qsearch this depth isn't used by any function at all apart movepicker, which also doesn't use passed qsearch depth in any way, so can be removed. No functional change. closes https://github.com/official-stockfish/Stockfish/pull/5514 No functional change --- src/search.cpp | 7 +++---- src/search.h | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index e2df475e9e1..09004ba6e7b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1401,14 +1401,13 @@ Value Search::Worker::search( // See https://www.chessprogramming.org/Horizon_Effect // and https://www.chessprogramming.org/Quiescence_Search template -Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) { +Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) { static_assert(nodeType != Root); constexpr bool PvNode = nodeType == PV; assert(alpha >= -VALUE_INFINITE && alpha < beta && beta <= VALUE_INFINITE); assert(PvNode || (alpha == beta - 1)); - assert(depth <= 0); // Check if we have an upcoming move that draws by repetition (~1 Elo) if (alpha < VALUE_DRAW && pos.upcoming_repetition(ss->ply)) @@ -1526,7 +1525,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, // first captures+checks, then captures only (but when in check, we simply search // all evasions). Square prevSq = ((ss - 1)->currentMove).is_ok() ? ((ss - 1)->currentMove).to_sq() : SQ_NONE; - MovePicker mp(pos, ttData.move, depth, &thisThread->mainHistory, &thisThread->captureHistory, + MovePicker mp(pos, ttData.move, DEPTH_QS, &thisThread->mainHistory, &thisThread->captureHistory, contHist, &thisThread->pawnHistory); // Step 5. Loop through all pseudo-legal moves until no moves remain or a beta @@ -1606,7 +1605,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta, // Step 7. Make and search the move thisThread->nodes.fetch_add(1, std::memory_order_relaxed); pos.do_move(move, st, givesCheck); - value = -qsearch(pos, ss + 1, -beta, -alpha, depth - 1); + value = -qsearch(pos, ss + 1, -beta, -alpha); pos.undo_move(move); assert(value > -VALUE_INFINITE && value < VALUE_INFINITE); diff --git a/src/search.h b/src/search.h index d42b5fba9b7..4872a58af14 100644 --- a/src/search.h +++ b/src/search.h @@ -291,7 +291,7 @@ class Worker { // Quiescence search function, which is called by the main search template - Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth = 0); + Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta); Depth reduction(bool i, Depth d, int mn, int delta) const; From af802da65b595f67046e97d580479ef1f7b18cab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Nicolet?= Date: Fri, 26 Jul 2024 11:13:37 +0200 Subject: [PATCH 1682/1766] Clean up comments for movepicker Remove references to checks in MovePicker comments. Follow-up for https://github.com/official-stockfish/Stockfish/pull/5498 closes https://github.com/official-stockfish/Stockfish/pull/5516 No functional change --- src/movepick.cpp | 26 ++++++++++++-------------- src/movepick.h | 12 ++++++------ src/search.cpp | 14 +++++++------- 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 7bd0252c819..bdc0e4affdb 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -74,12 +74,10 @@ void partial_insertion_sort(ExtMove* begin, ExtMove* end, int limit) { // Constructors of the MovePicker class. As arguments, we pass information -// to help it return the (presumably) good moves first, to decide which -// moves to return (in the quiescence search, for instance, we only want to -// search captures, promotions, and some checks) and how important a good -// move ordering is at the current node. +// to decide which class of moves to emit, to help sorting the (presumably) +// good moves first, and how important move ordering is at the current node. -// MovePicker constructor for the main search +// MovePicker constructor for the main search and for the quiescence search MovePicker::MovePicker(const Position& p, Move ttm, Depth d, @@ -102,8 +100,8 @@ MovePicker::MovePicker(const Position& p, stage = (depth > 0 ? MAIN_TT : QSEARCH_TT) + !(ttm && pos.pseudo_legal(ttm)); } -// Constructor for ProbCut: we generate captures with SEE greater than or equal -// to the given threshold. +// MovePicker constructor for ProbCut: we generate captures with Static Exchange +// Evaluation (SEE) greater than or equal to the given threshold. MovePicker::MovePicker(const Position& p, Move ttm, int th, const CapturePieceToHistory* cph) : pos(p), captureHistory(cph), @@ -115,9 +113,9 @@ MovePicker::MovePicker(const Position& p, Move ttm, int th, const CapturePieceTo + !(ttm && pos.capture_stage(ttm) && pos.pseudo_legal(ttm) && pos.see_ge(ttm, threshold)); } -// Assigns a numerical value to each move in a list, used -// for sorting. Captures are ordered by Most Valuable Victim (MVV), preferring -// captures with a good history. Quiets moves are ordered using the history tables. +// Assigns a numerical value to each move in a list, used for sorting. +// Captures are ordered by Most Valuable Victim (MVV), preferring captures +// with a good history. Quiets moves are ordered using the history tables. template void MovePicker::score() { @@ -191,7 +189,7 @@ void MovePicker::score() { } // Returns the next move satisfying a predicate function. -// It never returns the TT move. +// This never returns the TT move, as it was emitted before. template Move MovePicker::select(Pred filter) { @@ -208,9 +206,9 @@ Move MovePicker::select(Pred filter) { return Move::none(); } -// Most important method of the MovePicker class. It -// returns a new pseudo-legal move every time it is called until there are no more -// moves left, picking the move with the highest score from a list of generated moves. +// This is the most important method of the MovePicker class. We emit one +// new pseudo-legal move on every call until there are no more moves left, +// picking the move with the highest score from a list of generated moves. Move MovePicker::next_move(bool skipQuiets) { auto quiet_threshold = [](Depth d) { return -3560 * d; }; diff --git a/src/movepick.h b/src/movepick.h index 671cbb9cead..61f6368e6d9 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -137,12 +137,12 @@ using PawnHistory = Stats using CorrectionHistory = Stats; -// MovePicker class is used to pick one pseudo-legal move at a time from the -// current position. The most important method is next_move(), which returns a -// new pseudo-legal move each time it is called, until there are no moves left, -// when Move::none() is returned. In order to improve the efficiency of the -// alpha-beta algorithm, MovePicker attempts to return the moves which are most -// likely to get a cut-off first. +// The MovePicker class is used to pick one pseudo-legal move at a time from the +// current position. The most important method is next_move(), which emits one +// new pseudo-legal move on every call, until there are no moves left, when +// Move::none() is returned. In order to improve the efficiency of the alpha-beta +// algorithm, MovePicker attempts to return the moves which are most likely to get +// a cut-off first. class MovePicker { enum PickType { diff --git a/src/search.cpp b/src/search.cpp index 09004ba6e7b..e83034561c0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -20,18 +20,18 @@ #include #include -#include #include #include +#include #include #include #include #include -#include -#include -#include #include +#include #include +#include +#include #include "evaluate.h" #include "misc.h" @@ -1520,11 +1520,11 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, (ss - 2)->continuationHistory}; + Square prevSq = ((ss - 1)->currentMove).is_ok() ? ((ss - 1)->currentMove).to_sq() : SQ_NONE; + // Initialize a MovePicker object for the current position, and prepare to search // the moves. We presently use two stages of move generator in quiescence search: - // first captures+checks, then captures only (but when in check, we simply search - // all evasions). - Square prevSq = ((ss - 1)->currentMove).is_ok() ? ((ss - 1)->currentMove).to_sq() : SQ_NONE; + // captures, or evasions only when in check. MovePicker mp(pos, ttData.move, DEPTH_QS, &thisThread->mainHistory, &thisThread->captureHistory, contHist, &thisThread->pawnHistory); From 2343f71f3ff524e937f81b2922705081f8907980 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sat, 20 Jul 2024 12:41:56 -0700 Subject: [PATCH 1683/1766] Remove Killers The removal of killers on line 1774 resulted in a substantial decrease in pre-LMR history average, so a negative history fill is applied to counter it. Passed Non-regression STC (vs #5513): LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 21984 W: 5886 L: 5645 D: 10453 Ptnml(0-2): 80, 2492, 5628, 2691, 101 https://tests.stockfishchess.org/tests/view/66a095894ff211be9d4ecb9d Passed Non-regression LTC (vs #5513): LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 95430 W: 24141 L: 23995 D: 47294 Ptnml(0-2): 97, 10537, 26298, 10689, 94 https://tests.stockfishchess.org/tests/view/66a11c8d4ff211be9d4ecbf8 closes https://github.com/official-stockfish/Stockfish/pull/5517 Bench: 1660869 --- src/search.cpp | 34 +++++++--------------------------- src/search.h | 1 - 2 files changed, 7 insertions(+), 28 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index e83034561c0..f20bd4c9379 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -98,11 +98,8 @@ Value value_to_tt(Value v, int ply); Value value_from_tt(Value v, int ply, int r50c); void update_pv(Move* pv, Move move, const Move* childPv); void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus); -void update_killer(Stack* ss, Move move); void update_quiet_histories( const Position& pos, Stack* ss, Search::Worker& workerThread, Move move, int bonus); -void update_quiet_stats( - const Position& pos, Stack* ss, Search::Worker& workerThread, Move move, int bonus); void update_all_stats(const Position& pos, Stack* ss, Search::Worker& workerThread, @@ -222,7 +219,7 @@ void Search::Worker::iterative_deepening() { // Allocate stack with extra size to allow access from (ss - 7) to (ss + 2): // (ss - 7) is needed for update_continuation_histories(ss - 1) which accesses (ss - 6), - // (ss + 2) is needed for initialization of cutOffCnt and killers. + // (ss + 2) is needed for initialization of cutOffCnt. Stack stack[MAX_PLY + 10] = {}; Stack* ss = stack + 7; @@ -490,7 +487,7 @@ void Search::Worker::clear() { for (StatsType c : {NoCaptures, Captures}) for (auto& to : continuationHistory[inCheck][c]) for (auto& h : to) - h->fill(-58); + h->fill(-658); for (size_t i = 1; i < reductions.size(); ++i) reductions[i] = int((18.62 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); @@ -584,7 +581,6 @@ Value Search::Worker::search( assert(0 <= ss->ply && ss->ply < MAX_PLY); bestMove = Move::none(); - (ss + 1)->killer = Move::none(); (ss + 2)->cutoffCnt = 0; Square prevSq = ((ss - 1)->currentMove).is_ok() ? ((ss - 1)->currentMove).to_sq() : SQ_NONE; ss->statScore = 0; @@ -615,7 +611,7 @@ Value Search::Worker::search( { // Bonus for a quiet ttMove that fails high (~2 Elo) if (!ttCapture) - update_quiet_stats(pos, ss, *this, ttData.move, stat_bonus(depth)); + update_quiet_histories(pos, ss, *this, ttData.move, stat_bonus(depth)); // Extra penalty for early quiet moves of // the previous ply (~1 Elo on STC, ~2 Elo on LTC) @@ -1754,7 +1750,7 @@ void update_all_stats(const Position& pos, if (!pos.capture_stage(bestMove)) { - update_quiet_stats(pos, ss, workerThread, bestMove, quietMoveBonus); + update_quiet_histories(pos, ss, workerThread, bestMove, quietMoveBonus); // Decrease stats for all non-best quiet moves for (Move move : quietsSearched) @@ -1767,12 +1763,9 @@ void update_all_stats(const Position& pos, captureHistory[moved_piece][bestMove.to_sq()][captured] << quietMoveBonus; } - // Extra penalty for a quiet early move that was not a TT move or - // main killer move in previous ply when it gets refuted. - if (prevSq != SQ_NONE - && ((ss - 1)->moveCount == 1 + (ss - 1)->ttHit - || ((ss - 1)->currentMove == (ss - 1)->killer)) - && !pos.captured_piece()) + // Extra penalty for a quiet early move that was not a TT move in + // previous ply when it gets refuted. + if (prevSq != SQ_NONE && ((ss - 1)->moveCount == 1 + (ss - 1)->ttHit) && !pos.captured_piece()) update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, -quietMoveMalus); // Decrease stats for all non-best capture moves @@ -1802,11 +1795,6 @@ void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { } // Updates move sorting heuristics -void update_killer(Stack* ss, Move move) { - - // Update killers - ss->killer = move; -} void update_quiet_histories( const Position& pos, Stack* ss, Search::Worker& workerThread, Move move, int bonus) { @@ -1820,14 +1808,6 @@ void update_quiet_histories( workerThread.pawnHistory[pIndex][pos.moved_piece(move)][move.to_sq()] << bonus / 2; } -// Updates move sorting heuristics -void update_quiet_stats( - const Position& pos, Stack* ss, Search::Worker& workerThread, Move move, int bonus) { - - update_killer(ss, move); - update_quiet_histories(pos, ss, workerThread, move, bonus); -} - } // When playing with strength handicap, choose the best move among a set of diff --git a/src/search.h b/src/search.h index 4872a58af14..bdb63ffd0d8 100644 --- a/src/search.h +++ b/src/search.h @@ -66,7 +66,6 @@ struct Stack { int ply; Move currentMove; Move excludedMove; - Move killer; Value staticEval; int statScore; int moveCount; From 8e560c4fd347514a699bde1931912834047cc835 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Thu, 25 Jul 2024 14:37:08 +0200 Subject: [PATCH 1684/1766] Replicate network weights only to used NUMA nodes On a system with multiple NUMA nodes, this patch avoids unneeded replicated (e.g. 8x for a single threaded run), reducting memory use in that case. Lazy initialization forced before search. Passed STC: https://tests.stockfishchess.org/tests/view/66a28c524ff211be9d4ecdd4 LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 691776 W: 179429 L: 179927 D: 332420 Ptnml(0-2): 2573, 79370, 182547, 78778, 2620 closes https://github.com/official-stockfish/Stockfish/pull/5515 No functional change --- src/engine.cpp | 5 +++ src/engine.h | 8 ++-- src/numa.h | 112 +++++++++++++++++++++++++++++++++++++++++++++++++ src/search.cpp | 6 +++ src/search.h | 26 ++++++------ src/thread.cpp | 7 ++++ src/thread.h | 4 ++ 7 files changed, 152 insertions(+), 16 deletions(-) diff --git a/src/engine.cpp b/src/engine.cpp index 498b7c3e741..81bb260bd88 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -204,6 +204,7 @@ void Engine::set_numa_config_from_option(const std::string& o) { // Force reallocation of threads in case affinities need to change. resize_threads(); + threads.ensure_network_replicated(); } void Engine::resize_threads() { @@ -212,6 +213,7 @@ void Engine::resize_threads() { // Reallocate the hash with the new threadpool size set_tt_size(options["Hash"]); + threads.ensure_network_replicated(); } void Engine::set_tt_size(size_t mb) { @@ -234,18 +236,21 @@ void Engine::load_networks() { networks_.small.load(binaryDirectory, options["EvalFileSmall"]); }); threads.clear(); + threads.ensure_network_replicated(); } void Engine::load_big_network(const std::string& file) { networks.modify_and_replicate( [this, &file](NN::Networks& networks_) { networks_.big.load(binaryDirectory, file); }); threads.clear(); + threads.ensure_network_replicated(); } void Engine::load_small_network(const std::string& file) { networks.modify_and_replicate( [this, &file](NN::Networks& networks_) { networks_.small.load(binaryDirectory, file); }); threads.clear(); + threads.ensure_network_replicated(); } void Engine::save_network(const std::pair, std::string> files[2]) { diff --git a/src/engine.h b/src/engine.h index 127f7d7c84d..f3c78398669 100644 --- a/src/engine.h +++ b/src/engine.h @@ -114,10 +114,10 @@ class Engine { StateListPtr states; Square capSq; - OptionsMap options; - ThreadPool threads; - TranspositionTable tt; - NumaReplicated networks; + OptionsMap options; + ThreadPool threads; + TranspositionTable tt; + LazyNumaReplicated networks; Search::SearchManager::UpdateContext updateContext; }; diff --git a/src/numa.h b/src/numa.h index 3de8281d152..20d352c91c2 100644 --- a/src/numa.h +++ b/src/numa.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -1136,6 +1137,117 @@ class NumaReplicated: public NumaReplicatedBase { } }; +// We force boxing with a unique_ptr. If this becomes an issue due to added +// indirection we may need to add an option for a custom boxing type. +template +class LazyNumaReplicated: public NumaReplicatedBase { + public: + using ReplicatorFuncType = std::function; + + LazyNumaReplicated(NumaReplicationContext& ctx) : + NumaReplicatedBase(ctx) { + prepare_replicate_from(T{}); + } + + LazyNumaReplicated(NumaReplicationContext& ctx, T&& source) : + NumaReplicatedBase(ctx) { + prepare_replicate_from(std::move(source)); + } + + LazyNumaReplicated(const LazyNumaReplicated&) = delete; + LazyNumaReplicated(LazyNumaReplicated&& other) noexcept : + NumaReplicatedBase(std::move(other)), + instances(std::exchange(other.instances, {})) {} + + LazyNumaReplicated& operator=(const LazyNumaReplicated&) = delete; + LazyNumaReplicated& operator=(LazyNumaReplicated&& other) noexcept { + NumaReplicatedBase::operator=(*this, std::move(other)); + instances = std::exchange(other.instances, {}); + + return *this; + } + + LazyNumaReplicated& operator=(T&& source) { + prepare_replicate_from(std::move(source)); + + return *this; + } + + ~LazyNumaReplicated() override = default; + + const T& operator[](NumaReplicatedAccessToken token) const { + assert(token.get_numa_index() < instances.size()); + ensure_present(token.get_numa_index()); + return *(instances[token.get_numa_index()]); + } + + const T& operator*() const { return *(instances[0]); } + + const T* operator->() const { return instances[0].get(); } + + template + void modify_and_replicate(FuncT&& f) { + auto source = std::move(instances[0]); + std::forward(f)(*source); + prepare_replicate_from(std::move(*source)); + } + + void on_numa_config_changed() override { + // Use the first one as the source. It doesn't matter which one we use, + // because they all must be identical, but the first one is guaranteed to exist. + auto source = std::move(instances[0]); + prepare_replicate_from(std::move(*source)); + } + + private: + mutable std::vector> instances; + mutable std::mutex mutex; + + void ensure_present(NumaIndex idx) const { + assert(idx < instances.size()); + + if (instances[idx] != nullptr) + return; + + assert(idx != 0); + + std::unique_lock lock(mutex); + // Check again for races. + if (instances[idx] != nullptr) + return; + + const NumaConfig& cfg = get_numa_config(); + cfg.execute_on_numa_node( + idx, [this, idx]() { instances[idx] = std::make_unique(*instances[0]); }); + } + + void prepare_replicate_from(T&& source) { + instances.clear(); + + const NumaConfig& cfg = get_numa_config(); + if (cfg.requires_memory_replication()) + { + assert(cfg.num_numa_nodes() > 0); + + // We just need to make sure the first instance is there. + // Note that we cannot move here as we need to reallocate the data + // on the correct NUMA node. + cfg.execute_on_numa_node( + 0, [this, &source]() { instances.emplace_back(std::make_unique(source)); }); + + // Prepare others for lazy init. + instances.resize(cfg.num_numa_nodes()); + } + else + { + assert(cfg.num_numa_nodes() == 1); + // We take advantage of the fact that replication is not required + // and reuse the source value, avoiding one copy operation. + instances.emplace_back(std::make_unique(std::move(source))); + } + } +}; + class NumaReplicationContext { public: NumaReplicationContext(NumaConfig&& cfg) : diff --git a/src/search.cpp b/src/search.cpp index f20bd4c9379..beafd87dd5c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -127,6 +127,12 @@ Search::Worker::Worker(SharedState& sharedState, clear(); } +void Search::Worker::ensure_network_replicated() { + // Access once to force lazy initialization. + // We do this because we want to avoid initialization during search. + (void) (networks[numaAccessToken]); +} + void Search::Worker::start_searching() { // Non-main threads go directly to iterative_deepening() diff --git a/src/search.h b/src/search.h index bdb63ffd0d8..0f635186a3b 100644 --- a/src/search.h +++ b/src/search.h @@ -131,19 +131,19 @@ struct LimitsType { // The UCI stores the uci options, thread pool, and transposition table. // This struct is used to easily forward data to the Search::Worker class. struct SharedState { - SharedState(const OptionsMap& optionsMap, - ThreadPool& threadPool, - TranspositionTable& transpositionTable, - const NumaReplicated& nets) : + SharedState(const OptionsMap& optionsMap, + ThreadPool& threadPool, + TranspositionTable& transpositionTable, + const LazyNumaReplicated& nets) : options(optionsMap), threads(threadPool), tt(transpositionTable), networks(nets) {} - const OptionsMap& options; - ThreadPool& threads; - TranspositionTable& tt; - const NumaReplicated& networks; + const OptionsMap& options; + ThreadPool& threads; + TranspositionTable& tt; + const LazyNumaReplicated& networks; }; class Worker; @@ -274,6 +274,8 @@ class Worker { bool is_mainthread() const { return threadIdx == 0; } + void ensure_network_replicated(); + // Public because they need to be updatable by the stats ButterflyHistory mainHistory; CapturePieceToHistory captureHistory; @@ -328,10 +330,10 @@ class Worker { Tablebases::Config tbConfig; - const OptionsMap& options; - ThreadPool& threads; - TranspositionTable& tt; - const NumaReplicated& networks; + const OptionsMap& options; + ThreadPool& threads; + TranspositionTable& tt; + const LazyNumaReplicated& networks; // Used by NNUE Eval::NNUE::AccumulatorCaches refreshTable; diff --git a/src/thread.cpp b/src/thread.cpp index f17fc4a5366..b5d51594c54 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -102,6 +102,8 @@ void Thread::run_custom_job(std::function f) { cv.notify_one(); } +void Thread::ensure_network_replicated() { worker->ensure_network_replicated(); } + // Thread gets parked here, blocked on the condition variable // when the thread has no work to do. @@ -400,4 +402,9 @@ std::vector ThreadPool::get_bound_thread_count_by_numa_node() const { return counts; } +void ThreadPool::ensure_network_replicated() { + for (auto&& th : threads) + th->ensure_network_replicated(); +} + } // namespace Stockfish diff --git a/src/thread.h b/src/thread.h index 81ca39bbcb6..43e2e1423ce 100644 --- a/src/thread.h +++ b/src/thread.h @@ -83,6 +83,8 @@ class Thread { void clear_worker(); void run_custom_job(std::function f); + void ensure_network_replicated(); + // Thread has been slightly altered to allow running custom jobs, so // this name is no longer correct. However, this class (and ThreadPool) // require further work to make them properly generic while maintaining @@ -146,6 +148,8 @@ class ThreadPool { std::vector get_bound_thread_count_by_numa_node() const; + void ensure_network_replicated(); + std::atomic_bool stop, abortedSearch, increaseDepth; auto cbegin() const noexcept { return threads.cbegin(); } From b976f0a101f80d8b80aa212e92d1cc04b12c6136 Mon Sep 17 00:00:00 2001 From: MinetaS Date: Mon, 29 Jul 2024 12:10:34 +0900 Subject: [PATCH 1685/1766] Move DotProd code into optimized affine layer This patch moves the DotProd code into the propagation function which has sequential access optimization. To prove the speedup, the comparison is done without the sparse layer. With the sparse layer the effect is marginal (GCC 0.3%, LLVM/Clang 0.1%). For both tests, binary is compiled with GCC 14.1. Each test had 50 runs. Sparse layer included: ``` speedup = +0.0030 P(speedup > 0) = 1.0000 ``` Sparse layer excluded: ``` speedup = +0.0561 P(speedup > 0) = 1.0000 ``` closes https://github.com/official-stockfish/Stockfish/pull/5520 No functional change --- src/nnue/layers/affine_transform.h | 58 +++++++++++++++--------------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index ad9167c05c4..59a6149f0c4 100644 --- a/src/nnue/layers/affine_transform.h +++ b/src/nnue/layers/affine_transform.h @@ -39,25 +39,26 @@ namespace Stockfish::Eval::NNUE::Layers { +#if defined(USE_SSSE3) || defined(USE_NEON_DOTPROD) + #define ENABLE_SEQ_OPT +#endif + // Fallback implementation for older/other architectures. // Requires the input to be padded to at least 16 values. -#if !defined(USE_SSSE3) +#ifndef ENABLE_SEQ_OPT + template static void affine_transform_non_ssse3(std::int32_t* output, const std::int8_t* weights, const std::int32_t* biases, const std::uint8_t* input) { - #if defined(USE_SSE2) || defined(USE_NEON_DOTPROD) || defined(USE_NEON) + #if defined(USE_SSE2) || defined(USE_NEON) #if defined(USE_SSE2) // At least a multiple of 16, with SSE2. constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 16) / 16; const __m128i Zeros = _mm_setzero_si128(); const auto inputVector = reinterpret_cast(input); - #elif defined(USE_NEON_DOTPROD) - constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 16) / 16; - const auto inputVector = reinterpret_cast(input); - #elif defined(USE_NEON) constexpr IndexType NumChunks = ceil_to_multiple(InputDimensions, 16) / 16; const auto inputVector = reinterpret_cast(input); @@ -91,16 +92,8 @@ static void affine_transform_non_ssse3(std::int32_t* output, sum = _mm_add_epi32(sum, sum_second_32); output[i] = _mm_cvtsi128_si32(sum); - #elif defined(USE_NEON_DOTPROD) - int32x4_t sum = {biases[i]}; - const auto row = reinterpret_cast(&weights[offset]); - for (IndexType j = 0; j < NumChunks; ++j) - { - sum = vdotq_s32(sum, inputVector[j], row[j]); - } - output[i] = vaddvq_s32(sum); - #elif defined(USE_NEON) + int32x4_t sum = {biases[i]}; const auto row = reinterpret_cast(&weights[offset]); for (IndexType j = 0; j < NumChunks; ++j) @@ -127,7 +120,8 @@ static void affine_transform_non_ssse3(std::int32_t* output, } #endif } -#endif + +#endif // !ENABLE_SEQ_OPT template class AffineTransform { @@ -162,7 +156,7 @@ class AffineTransform { } static constexpr IndexType get_weight_index(IndexType i) { -#if defined(USE_SSSE3) +#ifdef ENABLE_SEQ_OPT return get_weight_index_scrambled(i); #else return i; @@ -190,29 +184,28 @@ class AffineTransform { // Forward propagation void propagate(const InputType* input, OutputType* output) const { -#if defined(USE_SSSE3) +#ifdef ENABLE_SEQ_OPT if constexpr (OutputDimensions > 1) { - #if defined(USE_AVX512) using vec_t = __m512i; - #define vec_setzero _mm512_setzero_si512 #define vec_set_32 _mm512_set1_epi32 #define vec_add_dpbusd_32 Simd::m512_add_dpbusd_epi32 - #define vec_hadd Simd::m512_hadd #elif defined(USE_AVX2) using vec_t = __m256i; - #define vec_setzero _mm256_setzero_si256 #define vec_set_32 _mm256_set1_epi32 #define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32 - #define vec_hadd Simd::m256_hadd #elif defined(USE_SSSE3) using vec_t = __m128i; - #define vec_setzero _mm_setzero_si128 #define vec_set_32 _mm_set1_epi32 #define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32 - #define vec_hadd Simd::m128_hadd + #elif defined(USE_NEON_DOTPROD) + using vec_t = int32x4_t; + #define vec_set_32 vdupq_n_s32 + #define vec_add_dpbusd_32(acc, a, b) \ + Simd::dotprod_m128_add_dpbusd_epi32(acc, vreinterpretq_s8_s32(a), \ + vreinterpretq_s8_s32(b)) #endif static constexpr IndexType OutputSimdWidth = sizeof(vec_t) / sizeof(OutputType); @@ -242,28 +235,33 @@ class AffineTransform { for (IndexType k = 0; k < NumRegs; ++k) outptr[k] = acc[k]; - #undef vec_setzero #undef vec_set_32 #undef vec_add_dpbusd_32 - #undef vec_hadd } else if constexpr (OutputDimensions == 1) { - // We cannot use AVX512 for the last layer because there are only 32 inputs // and the buffer is not padded to 64 elements. #if defined(USE_AVX2) using vec_t = __m256i; - #define vec_setzero _mm256_setzero_si256 + #define vec_setzero() _mm256_setzero_si256() #define vec_set_32 _mm256_set1_epi32 #define vec_add_dpbusd_32 Simd::m256_add_dpbusd_epi32 #define vec_hadd Simd::m256_hadd #elif defined(USE_SSSE3) using vec_t = __m128i; - #define vec_setzero _mm_setzero_si128 + #define vec_setzero() _mm_setzero_si128() #define vec_set_32 _mm_set1_epi32 #define vec_add_dpbusd_32 Simd::m128_add_dpbusd_epi32 #define vec_hadd Simd::m128_hadd + #elif defined(USE_NEON_DOTPROD) + using vec_t = int32x4_t; + #define vec_setzero() vdupq_n_s32(0) + #define vec_set_32 vdupq_n_s32 + #define vec_add_dpbusd_32(acc, a, b) \ + Simd::dotprod_m128_add_dpbusd_epi32(acc, vreinterpretq_s8_s32(a), \ + vreinterpretq_s8_s32(b)) + #define vec_hadd Simd::neon_m128_hadd #endif const auto inputVector = reinterpret_cast(input); From ae9e55cf530081afea34216b86b6eb5d9b2b5661 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Mon, 29 Jul 2024 00:04:03 -0700 Subject: [PATCH 1686/1766] Simplify Cutnode Reduction Passed Non-regression STC: LR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 143968 W: 37439 L: 37333 D: 69196 Ptnml(0-2): 521, 17228, 36456, 17182, 597 https://tests.stockfishchess.org/tests/view/66a73f9f4ff211be9d4ed27f Passed Non-regression LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 198954 W: 50384 L: 50345 D: 98225 Ptnml(0-2): 201, 22360, 54347, 22337, 232 https://tests.stockfishchess.org/tests/view/66a906e94ff211be9d4ed423 closes https://github.com/official-stockfish/Stockfish/pull/5526 bench 1277466 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index beafd87dd5c..5f87f28fded 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1133,7 +1133,7 @@ Value Search::Worker::search( // Increase reduction for cut nodes (~4 Elo) if (cutNode) - r += 2 - (ttData.depth >= depth && ss->ttPv) + (!ss->ttPv && move != ttData.move); + r += 2 - (ttData.depth >= depth && ss->ttPv) + !ss->ttPv; // Increase reduction if ttMove is a capture (~3 Elo) if (ttCapture) From d626af5c3a3781a8f63e1b9b7104ec69aaa4c726 Mon Sep 17 00:00:00 2001 From: Disservin Date: Sat, 17 Aug 2024 22:07:42 +0200 Subject: [PATCH 1687/1766] Fix failing CI for MacOS 13 GCC 11 closes https://github.com/official-stockfish/Stockfish/pull/5540 No functional change --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 836555e6127..8d209a4f974 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -148,7 +148,7 @@ jobs: - name: Download required macOS packages if: runner.os == 'macOS' - run: brew install coreutils + run: brew install coreutils gcc@11 - name: Setup msys and install required packages if: runner.os == 'Windows' From bc80ece6c78cafb3a89d3abcec6c71a517c29f2d Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Tue, 30 Jul 2024 01:33:56 -0700 Subject: [PATCH 1688/1766] Improve Comments for Pairwise Multiplication Optimization closes https://github.com/official-stockfish/Stockfish/pull/5524 no functional change --- src/nnue/nnue_feature_transformer.h | 76 ++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 17 deletions(-) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index ad0fb1b4d93..2f74dcae2ed 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -352,26 +352,68 @@ class FeatureTransformer { reinterpret_cast(&(accumulation[perspectives[p]][HalfDimensions / 2])); vec_t* out = reinterpret_cast(output + offset); - for (IndexType j = 0; j < NumOutputChunks; ++j) - { - // What we want to do is multiply inputs in a pairwise manner - // (after clipping), and then shift right by 9. Instead, we - // shift left by 7, and use mulhi, stripping the bottom 16 bits, - // effectively shifting right by 16, resulting in a net shift - // of 9 bits. We use mulhi because it maintains the sign of - // the multiplication (unlike mullo), allowing us to make use - // of packus to clip 2 of the inputs, resulting in a save of 2 - // "vec_max_16" calls. A special case is when we use NEON, - // where we shift left by 6 instead, because the instruction - // "vqdmulhq_s16" also doubles the return value after the - // multiplication, adding an extra shift to the left by 1, so - // we compensate by shifting less before the multiplication. - + // Per the NNUE architecture, here we want to multiply pairs of + // clipped elements and divide the product by 128. To do this, + // we can naively perform min/max operation to clip each of the + // four int16 vectors, mullo pairs together, then pack them into + // one int8 vector. However, there exists a faster way. + + // The idea here is to use the implicit clipping from packus to + // save us two vec_max_16 instructions. This clipping works due + // to the fact that any int16 integer below zero will be zeroed + // on packus. + + // Consider the case where the second element is negative. + // If we do standard clipping, that element will be zero, which + // means our pairwise product is zero. If we perform packus and + // remove the lower-side clip for the second element, then our + // product before packus will be negative, and is zeroed on pack. + // The two operation produce equivalent results, but the second + // one (using packus) saves one max operation per pair. + + // But here we run into a problem: mullo does not preserve the + // sign of the multiplication. We can get around this by doing + // mulhi, which keeps the sign. But that requires an additional + // tweak. + + // mulhi cuts off the last 16 bits of the resulting product, + // which is the same as performing a rightward shift of 16 bits. + // We can use this to our advantage. Recall that we want to + // divide the final product by 128, which is equivalent to a + // 7-bit right shift. Intuitively, if we shift the clipped + // value left by 9, and perform mulhi, which shifts the product + // right by 16 bits, then we will net a right shift of 7 bits. + // However, this won't work as intended. Since we clip the + // values to have a maximum value of 127, shifting it by 9 bits + // might occupy the signed bit, resulting in some positive + // values being interpreted as negative after the shift. + + // There is a way, however, to get around this limitation. When + // loading the network, scale accumulator weights and biases by + // 2. To get the same pairwise multiplication result as before, + // we need to divide the product by 128 * 2 * 2 = 512, which + // amounts to a right shift of 9 bits. So now we only have to + // shift left by 7 bits, perform mulhi (shifts right by 16 bits) + // and net a 9 bit right shift. Since we scaled everything by + // two, the values are clipped at 127 * 2 = 254, which occupies + // 8 bits. Shifting it by 7 bits left will no longer occupy the + // signed bit, so we are safe. + + // Note that on NEON processors, we shift left by 6 instead + // because the instruction "vqdmulhq_s16" also doubles the + // return value after the multiplication, adding an extra shift + // to the left by 1, so we compensate by shifting less before + // the multiplication. + + constexpr int shift = #if defined(USE_SSE2) - constexpr int shift = 7; + 7; #else - constexpr int shift = 6; + 6; #endif + + for (IndexType j = 0; j < NumOutputChunks; ++j) + { const vec_t sum0a = vec_slli_16(vec_max_16(vec_min_16(in0[j * 2 + 0], One), Zero), shift); const vec_t sum0b = From a75717ede14df4526a0990466e7b10d00e89c9ff Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Fri, 2 Aug 2024 13:23:44 -0700 Subject: [PATCH 1689/1766] Simplify Post-LMR Continuation History Updates Passed Non-regression STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 55520 W: 14625 L: 14420 D: 26475 Ptnml(0-2): 247, 6522, 14007, 6747, 237 https://tests.stockfishchess.org/tests/view/66ad40874ff211be9d4ed8f7 Passed Non-regression LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 216168 W: 54561 L: 54540 D: 107067 Ptnml(0-2): 196, 24212, 59244, 24239, 193 https://tests.stockfishchess.org/tests/view/66aeac954ff211be9d4eda03 closes https://github.com/official-stockfish/Stockfish/pull/5530 bench 1418263 --- src/search.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5f87f28fded..3c7fc2532dc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1181,9 +1181,7 @@ Value Search::Worker::search( value = -search(pos, ss + 1, -(alpha + 1), -alpha, newDepth, !cutNode); // Post LMR continuation history updates (~1 Elo) - int bonus = value <= alpha ? -stat_malus(newDepth) - : value >= beta ? stat_bonus(newDepth) - : 0; + int bonus = value >= beta ? stat_bonus(newDepth) : -stat_malus(newDepth); update_continuation_histories(ss, movedPiece, move.to_sq(), bonus); } From 4995792a6c1dfca13e3fafc8e55577854b4de1dd Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sat, 10 Aug 2024 13:06:28 +0300 Subject: [PATCH 1690/1766] Simplify cutnode reduction formula Passed STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 137994 W: 34705 L: 34603 D: 68686 Ptnml(0-2): 124, 15371, 37903, 15477, 122 https://tests.stockfishchess.org/tests/view/66aeb74b4ff211be9d4eda10 Passed LTC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 131456 W: 34148 L: 34031 D: 63277 Ptnml(0-2): 506, 15571, 33465, 15672, 514 https://tests.stockfishchess.org/tests/view/66ae258b4ff211be9d4ed95d closes https://github.com/official-stockfish/Stockfish/pull/5531 Bench: 1261995 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 3c7fc2532dc..35f203b9342 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1133,7 +1133,7 @@ Value Search::Worker::search( // Increase reduction for cut nodes (~4 Elo) if (cutNode) - r += 2 - (ttData.depth >= depth && ss->ttPv) + !ss->ttPv; + r += 2 - (ttData.depth >= depth && ss->ttPv); // Increase reduction if ttMove is a capture (~3 Elo) if (ttCapture) From 5d81071953bd304e57613140b694b03a8241eac9 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Thu, 8 Aug 2024 01:33:42 -0400 Subject: [PATCH 1691/1766] Update default main net to nn-1111cefa1111.nnue Created from 2 distinct spsa tunes of the latest main net (nn-31337bea577c.nnue) and applying the params to the prior main net (nn-e8bac1c07a5a.nnue). This effectively reverts the modifications to output weights and biases in https://github.com/official-stockfish/Stockfish/pull/5509 SPSA: A: 6000, alpha: 0.602, gamma: 0.101 1st - 437 feature transformer biases where values are < 25 54k / 120k games at 180+1.8 https://tests.stockfishchess.org/tests/view/66af98ac4ff211be9d4edad0 nn-808259761cca.nnue 2nd - 208 L2 weights where values are zero 112k / 120k games at 180+1.8 https://tests.stockfishchess.org/tests/view/66b0c3074ff211be9d4edbe5 nn-a56cb8c3d477.nnue When creating the above 2 nets (nn-808259761cca.nnue, nn-a56cb8c3d477.nnue), spsa params were unintentionally applied to nn-e8bac1c07a5a.nnue rather than nn-31337bea577c.nnue due to an issue in a script that creates nets by applying spsa results to base nets. Since they both passed STC and were neutral or slightly positive at LTC, they were combined to see if the elo from each set of params was additive. The 2 nets can be merged on top of nn-e8bac1c07a5a.nnue with: https://github.com/linrock/nnue-tools/blob/90942d3/spsa/combine_nnue.py ``` python3 combine_nnue.py \ nn-e8bac1c07a5a.nnue \ nn-808259761cca.nnue \ nn-a56cb8c3d477.nnue ``` Merging yields nn-87caa003fc6a.nnue which was renamed to nn-1111cefa1111.nnue with an updated nnue-namer around 10x faster than before by: - using a prefix trie for efficient prefix matches - modifying 4 non-functional bytes near the end of the file instead of 2 https://github.com/linrock/nnue-namer Thanks to @MinetaS for pointing out in #nnue-dev what the non-functional bytes are: L3 is 32, 4 bytes for biases, 32 bytes for weights. (fc_2) So -38 and -37 are technically -2 and -1 of fc_1 (type AffineTransform<30, 32>) And since InputDimension is padded to 32 there are total 32 of 2 adjacent bytes padding. So yes, it's non-functional whatever values are there. It's possible to tweak bytes at -38 - 32 * N and -37 - 32 * N given N = 0 ... 31 The net renamed with the new method passed non-regression STC vs. the original net: https://tests.stockfishchess.org/tests/view/66c0f0a821503a509c13b332 To print the spsa params with nnue-pytorch: ``` import features from serialize import NNUEReader feature_set = features.get_feature_set_from_name("HalfKAv2_hm") with open("nn-31337bea577c.nnue", "rb") as f: model = NNUEReader(f, feature_set).model c_end = 16 for i,ft_bias in enumerate(model.input.bias.data[:3072]): value = int(ft_bias * 254) if abs(value) < 25: print(f"ftB[{i}],{value},-1024,1024,{c_end},0.0020") c_end = 6 for i in range(8): for j in range(32): for k in range(30): value = int(model.layer_stacks.l2.weight.data[32 * i + j, k] * 64) if value == 0: print(f"twoW[{i}][{j}][{k}],{value},-127,127,{c_end},0.0020") ``` New params found with the same method as: https://github.com/official-stockfish/Stockfish/pull/5459 Passed STC: https://tests.stockfishchess.org/tests/view/66b4d4464ff211be9d4edf6e LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 136416 W: 35753 L: 35283 D: 65380 Ptnml(0-2): 510, 16159, 34416, 16597, 526 Passed LTC: https://tests.stockfishchess.org/tests/view/66b76e814ff211be9d4ee1cc LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 159336 W: 40753 L: 40178 D: 78405 Ptnml(0-2): 126, 17497, 43864, 18038, 143 closes https://github.com/official-stockfish/Stockfish/pull/5534 bench 1613043 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 55838243342..c9041efbf84 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -33,7 +33,7 @@ namespace Eval { // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro or the location where this macro is defined, as it is used // in the Makefile/Fishtest. -#define EvalFileDefaultNameBig "nn-31337bea577c.nnue" +#define EvalFileDefaultNameBig "nn-1111cefa1111.nnue" #define EvalFileDefaultNameSmall "nn-37f18f62d772.nnue" namespace NNUE { From 175021721c6042896f2b35beb251edcf107d9dc2 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Tue, 13 Aug 2024 19:02:02 -0700 Subject: [PATCH 1692/1766] Simplify bestMove promotion Passed Non-regression STC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 216768 W: 56240 L: 56217 D: 104311 Ptnml(0-2): 794, 24900, 56956, 24957, 777 https://tests.stockfishchess.org/tests/view/66bc11324ff211be9d4ee78b Passed Non-regression LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 44970 W: 11391 L: 11199 D: 22380 Ptnml(0-2): 44, 4596, 13002, 4810, 33 https://tests.stockfishchess.org/tests/view/66bdbb1b4ff211be9d4eec5a closes https://github.com/official-stockfish/Stockfish/pull/5535 bench: 1613043 --- src/search.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 35f203b9342..ec8a9dd2d98 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1274,9 +1274,9 @@ Value Search::Worker::search( // In case we have an alternative move equal in eval to the current bestmove, // promote it to bestmove by pretending it just exceeds alpha (but not beta). - int inc = (value == bestValue && (int(nodes) & 15) == 0 - && ss->ply + 2 + ss->ply / 32 >= thisThread->rootDepth - && std::abs(value) + 1 < VALUE_TB_WIN_IN_MAX_PLY); + int inc = + (value == bestValue && (int(nodes) & 15) == 0 && ss->ply + 2 >= thisThread->rootDepth + && std::abs(value) + 1 < VALUE_TB_WIN_IN_MAX_PLY); if (value + inc > bestValue) { From 87814d2fb869166f1bdbcb23893aca57729602fe Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Tue, 13 Aug 2024 19:07:08 -0700 Subject: [PATCH 1693/1766] Simplify doShallowerSearch Passed Non-regression STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 242336 W: 62657 L: 62663 D: 117016 Ptnml(0-2): 941, 28949, 61418, 28895, 965 https://tests.stockfishchess.org/tests/view/66bc13c34ff211be9d4ee794 Passed Non-regression LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 128100 W: 32503 L: 32390 D: 63207 Ptnml(0-2): 106, 14319, 35113, 14380, 132 https://tests.stockfishchess.org/tests/view/66bdbb304ff211be9d4eec5d closes https://github.com/official-stockfish/Stockfish/pull/5537 bench 1586246 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index ec8a9dd2d98..34190596bfa 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1173,7 +1173,7 @@ Value Search::Worker::search( // Adjust full-depth search based on LMR results - if the result was // good enough search deeper, if it was bad enough search shallower. const bool doDeeperSearch = value > (bestValue + 35 + 2 * newDepth); // (~1 Elo) - const bool doShallowerSearch = value < bestValue + newDepth; // (~2 Elo) + const bool doShallowerSearch = value < bestValue + 8; // (~2 Elo) newDepth += doDeeperSearch - doShallowerSearch; From 6cf7f300acc88df277da64b754c436462f48dadf Mon Sep 17 00:00:00 2001 From: Nonlinear2 <131959792+Nonlinear2@users.noreply.github.com> Date: Fri, 16 Aug 2024 22:54:05 +0200 Subject: [PATCH 1694/1766] Simplify stand pat adjustement Remove && !PvNode condition for stand pat adjustement in quiescence search. Passed non-regression STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 108544 W: 28228 L: 28085 D: 52231 Ptnml(0-2): 389, 12902, 27554, 13031, 396 https://tests.stockfishchess.org/tests/view/66bb402e4ff211be9d4ee688 Passed non-regression LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 193014 W: 48796 L: 48751 D: 95467 Ptnml(0-2): 188, 21481, 53116, 21542, 180 https://tests.stockfishchess.org/tests/view/66bc78774ff211be9d4ee88f closes https://github.com/official-stockfish/Stockfish/pull/5538 Bench 1787360 --- AUTHORS | 1 + src/search.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 1ac40d879df..3201e7a8afe 100644 --- a/AUTHORS +++ b/AUTHORS @@ -171,6 +171,7 @@ Niklas Fiekas (niklasf) Nikolay Kostov (NikolayIT) Norman Schmidt (FireFather) notruck +Nour Berakdar (Nonlinear) Ofek Shochat (OfekShochat, ghostway) Ondrej Mosnáček (WOnder93) Ondřej Mišina (AndrovT) diff --git a/src/search.cpp b/src/search.cpp index 34190596bfa..b062aa46df0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1502,7 +1502,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) // Stand pat. Return immediately if static value is at least beta if (bestValue >= beta) { - if (std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY && !PvNode) + if (std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY) bestValue = (3 * bestValue + beta) / 4; if (!ss->ttHit) ttWriter.write(posKey, value_to_tt(bestValue, ss->ply), false, BOUND_LOWER, From d275bf9643768cbf6472977f4262220e6c1c1bb5 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Thu, 15 Aug 2024 16:24:56 -0600 Subject: [PATCH 1695/1766] Introduce Fail Low History Bonus When a node fails low, give TT move a small bonus 1/4 of normal value. Passed STC: LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 92384 W: 24094 L: 23691 D: 44599 Ptnml(0-2): 323, 10852, 23465, 11203, 349 https://tests.stockfishchess.org/tests/view/66be80794ff211be9d4eed68 Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 114660 W: 29260 L: 28778 D: 56622 Ptnml(0-2): 97, 12506, 31653, 12966, 108 https://tests.stockfishchess.org/tests/view/66bf63ee4ff211be9d4eeef0 closes https://github.com/official-stockfish/Stockfish/pull/5539 bench 1463003 --- src/search.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index b062aa46df0..c94d3c42222 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1361,6 +1361,10 @@ Value Search::Worker::search( << stat_bonus(depth) * bonus / 25; } + // Bonus when search fails low and there is a TT move + else if (moveCount > 1 && ttData.move && (cutNode || PvNode)) + thisThread->mainHistory[us][ttData.move.from_to()] << stat_bonus(depth) / 4; + if (PvNode) bestValue = std::min(bestValue, maxValue); From 9fb58328e363d84e3cf720b018e639b139ba95c2 Mon Sep 17 00:00:00 2001 From: Taras Vuk <117687515+TarasVuk@users.noreply.github.com> Date: Sun, 18 Aug 2024 16:11:06 +0200 Subject: [PATCH 1696/1766] Tweak late move extensions Allow late move extensions only for PV and cut nodes. Passed STC: LLR: 2.95 (-2.94,2.94) <0.00,2.00> Total: 44512 W: 11688 L: 11355 D: 21469 Ptnml(0-2): 167, 5180, 11229, 5513, 167 https://tests.stockfishchess.org/tests/view/66c0509d4ff211be9d4ef10e Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 152970 W: 39026 L: 38466 D: 75478 Ptnml(0-2): 102, 16792, 42164, 17298, 129 https://tests.stockfishchess.org/tests/view/66c0994d21503a509c13b2b6 closes https://github.com/official-stockfish/Stockfish/pull/5541 bench: 1484730 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index c94d3c42222..531fc42fe35 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1163,7 +1163,7 @@ Value Search::Worker::search( // beyond the first move depth. // To prevent problems when the max value is less than the min value, // std::clamp has been replaced by a more robust implementation. - Depth d = std::max(1, std::min(newDepth - r, newDepth + 1)); + Depth d = std::max(1, std::min(newDepth - r, newDepth + (PvNode || cutNode))); value = -search(pos, ss + 1, -(alpha + 1), -alpha, d, true); From a0597b1281f22dc90dbcc2f52f4a1a0e2bc09f96 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Mon, 26 Aug 2024 15:22:22 +0200 Subject: [PATCH 1697/1766] Forcibly split NUMA nodes on Windows split by processor groups due to Window's thread scheduler issues. fixes #5551 closes https://github.com/official-stockfish/Stockfish/pull/5552 No functional change --- src/numa.h | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/numa.h b/src/numa.h index 20d352c91c2..db8359222cd 100644 --- a/src/numa.h +++ b/src/numa.h @@ -582,7 +582,21 @@ class NumaConfig { // still no way to set thread affinity spanning multiple processor groups. // See https://learn.microsoft.com/en-us/windows/win32/procthread/numa-support // We also do this is if need to force old API for some reason. - if (STARTUP_USE_OLD_AFFINITY_API) + // + // 2024-08-26: It appears that we need to actually always force this behaviour. + // While Windows allows this to work now, such assignments have bad interaction + // with the scheduler - in particular it still prefers scheduling on the thread's + // "primary" node, even if it means scheduling SMT processors first. + // See https://github.com/official-stockfish/Stockfish/issues/5551 + // See https://learn.microsoft.com/en-us/windows/win32/procthread/processor-groups + // + // Each process is assigned a primary group at creation, and by default all + // of its threads' primary group is the same. Each thread's ideal processor + // is in the thread's primary group, so threads will preferentially be + // scheduled to processors on their primary group, but they are able to + // be scheduled to processors on any other group. + // + // used to be guarded by if (STARTUP_USE_OLD_AFFINITY_API) { NumaConfig splitCfg = empty(); From 54def6f7eb7c411cba9c1e31ff4074757f64e826 Mon Sep 17 00:00:00 2001 From: Taras Vuk <117687515+TarasVuk@users.noreply.github.com> Date: Fri, 23 Aug 2024 08:37:52 +0200 Subject: [PATCH 1698/1766] rename !(PvNode || cutNode) to allNode Passed STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 108992 W: 28178 L: 28039 D: 52775 Ptnml(0-2): 356, 12428, 28762, 12621, 329 https://tests.stockfishchess.org/tests/view/66c73a51bf8c9d8780fda532 closes https://github.com/official-stockfish/Stockfish/pull/5549 No functional change --- src/search.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 531fc42fe35..0b6756a7e21 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -509,6 +509,7 @@ Value Search::Worker::search( constexpr bool PvNode = nodeType != NonPV; constexpr bool rootNode = nodeType == Root; + const bool allNode = !(PvNode || cutNode); // Dive into quiescence search when the depth reaches zero if (depth <= 0) @@ -1141,7 +1142,7 @@ Value Search::Worker::search( // Increase reduction if next ply has a lot of fail high (~5 Elo) if ((ss + 1)->cutoffCnt > 3) - r += 1 + !(PvNode || cutNode); + r += 1 + allNode; // For first picked move (ttMove) reduce reduction, but never allow // reduction to go below 0 (~3 Elo) @@ -1163,7 +1164,7 @@ Value Search::Worker::search( // beyond the first move depth. // To prevent problems when the max value is less than the min value, // std::clamp has been replaced by a more robust implementation. - Depth d = std::max(1, std::min(newDepth - r, newDepth + (PvNode || cutNode))); + Depth d = std::max(1, std::min(newDepth - r, newDepth + !allNode)); value = -search(pos, ss + 1, -(alpha + 1), -alpha, d, true); @@ -1341,7 +1342,7 @@ Value Search::Worker::search( // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (122 * (depth > 5) + 39 * (PvNode || cutNode) + 165 * ((ss - 1)->moveCount > 8) + int bonus = (122 * (depth > 5) + 39 * !allNode + 165 * ((ss - 1)->moveCount > 8) + 107 * (!ss->inCheck && bestValue <= ss->staticEval - 98) + 134 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 91)); @@ -1362,7 +1363,7 @@ Value Search::Worker::search( } // Bonus when search fails low and there is a TT move - else if (moveCount > 1 && ttData.move && (cutNode || PvNode)) + else if (moveCount > 1 && ttData.move && !allNode) thisThread->mainHistory[us][ttData.move.from_to()] << stat_bonus(depth) / 4; if (PvNode) From 451044202a49fbbbe908b49fab323d70fab333e7 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Thu, 22 Aug 2024 18:27:16 +0300 Subject: [PATCH 1699/1766] Simpler formula for ss->cutoffCnt update closes https://github.com/official-stockfish/Stockfish/pull/5548 No functional change --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 0b6756a7e21..ad2c35e7708 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1292,7 +1292,7 @@ Value Search::Worker::search( if (value >= beta) { - ss->cutoffCnt += 1 + !ttData.move - (extension >= 2); + ss->cutoffCnt += !ttData.move + (extension < 2); assert(value >= beta); // Fail high break; } From ab00c24c7e547a06e3277fc5ae7f66980532224c Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 31 Aug 2024 15:42:29 +0200 Subject: [PATCH 1700/1766] Fix some of the tests due to https://github.com/official-stockfish/Stockfish/issues/5185 some CI tests are skipped. This patch fixes a few tests that need updating. closes https://github.com/official-stockfish/Stockfish/pull/5560 No functional change --- tests/instrumented.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/instrumented.sh b/tests/instrumented.sh index cb5a3a9f245..5fc6ca9a974 100755 --- a/tests/instrumented.sh +++ b/tests/instrumented.sh @@ -177,25 +177,25 @@ cat << EOF > game.exp send "ucinewgame\n" send "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - -\n" - send "go depth 18\n" + send "go depth 18 searchmoves c6d7\n" expect "score mate 2 * pv c6d7 * f7f5" expect "bestmove c6d7" send "ucinewgame\n" send "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - -\n" - send "go mate 2\n" + send "go mate 2 searchmoves c6d7\n" expect "score mate 2 * pv c6d7" expect "bestmove c6d7" send "ucinewgame\n" send "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - -\n" - send "go nodes 10000\n" + send "go nodes 500000 searchmoves c6d7\n" expect "score mate 2 * pv c6d7 * f7f5" expect "bestmove c6d7" send "ucinewgame\n" send "position fen 1NR2B2/5p2/5p2/1p1kpp2/1P2rp2/2P1pB2/2P1P1K1/8 b - - \n" - send "go depth 18\n" + send "go depth 27\n" expect "score mate -2" expect "pv d5e6 c8d8" expect "bestmove d5e6" @@ -257,7 +257,7 @@ cat << EOF > syzygy.exp expect "Stockfish" send "uci\n" send "setoption name SyzygyPath value ../tests/syzygy/\n" - expect "info string Found 35 tablebases" + expect "info string Found 35 WDL and 35 DTZ tablebase files (up to 4-man)." send "bench 128 1 8 default depth\n" expect "Nodes searched :" send "ucinewgame\n" From 2054add23cf234f302c67709efc0d265c5a98eae Mon Sep 17 00:00:00 2001 From: "Robert Nurnberg @ elitebook" Date: Tue, 3 Sep 2024 08:20:06 +0200 Subject: [PATCH 1701/1766] Update the WDL model updates the internal WDL model, using data from 2.6M games played by the revisions since 9fb5832. https://github.com/official-stockfish/Stockfish/pull/5565 No functional change --- src/uci.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uci.cpp b/src/uci.cpp index 9b60680d8c5..c94f8b914cd 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -339,8 +339,8 @@ WinRateParams win_rate_params(const Position& pos) { double m = std::clamp(material, 17, 78) / 58.0; // Return a = p_a(material) and b = p_b(material), see github.com/official-stockfish/WDL_model - constexpr double as[] = {-41.25712052, 121.47473115, -124.46958843, 411.84490997}; - constexpr double bs[] = {84.92998051, -143.66658718, 80.09988253, 49.80869370}; + constexpr double as[] = {-37.45051876, 121.19101539, -132.78783573, 420.70576692}; + constexpr double bs[] = {90.26261072, -137.26549898, 71.10130540, 51.35259597}; double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3]; double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3]; From 38e0cc7b909796c1a71d9c07b636698b69420975 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Sat, 31 Aug 2024 16:00:59 +0200 Subject: [PATCH 1702/1766] Update Top CPU Contributors to the status as of Aug 31st 2024. closes https://github.com/official-stockfish/Stockfish/pull/5561 No functional change --- Top CPU Contributors.txt | 193 +++++++++++++++++++++------------------ 1 file changed, 104 insertions(+), 89 deletions(-) diff --git a/Top CPU Contributors.txt b/Top CPU Contributors.txt index 11636e840b5..3d8c52361dd 100644 --- a/Top CPU Contributors.txt +++ b/Top CPU Contributors.txt @@ -1,106 +1,109 @@ -Contributors to Fishtest with >10,000 CPU hours, as of 2024-02-24. +Contributors to Fishtest with >10,000 CPU hours, as of 2024-08-31. Thank you! Username CPU Hours Games played ------------------------------------------------------------------ -noobpwnftw 39302472 3055513453 -technologov 20845762 994893444 -linrock 8616428 560281417 +noobpwnftw 40428649 3164740143 +technologov 23581394 1076895482 +vdv 19425375 718302718 +linrock 10034115 643194527 mlang 3026000 200065824 -okrout 2332151 222639518 -pemo 1800019 60274069 +okrout 2572676 237511408 +pemo 1836785 62226157 dew 1689162 100033738 -TueRens 1474943 75121774 -grandphish2 1463002 91616949 -JojoM 1109702 72927902 -olafm 978631 71037944 -sebastronomy 939955 44920556 +TueRens 1648780 77891164 +sebastronomy 1468328 60859092 +grandphish2 1466110 91776075 +JojoM 1130625 73666098 +olafm 1067009 74807270 tvijlbrief 796125 51897690 -gvreuls 711320 49142318 +oz 781847 53910686 +rpngn 768460 49812975 +gvreuls 751085 52177668 mibere 703840 46867607 -oz 646268 46293638 -rpngn 572571 38928563 -leszek 531858 39316505 -cw 518116 34894291 +leszek 566598 42024615 +cw 519601 34988161 fastgm 503862 30260818 CSU_Dynasty 468784 31385034 -ctoks 434591 28520597 -maximmasiutin 429983 27066286 +maximmasiutin 439192 27893522 +ctoks 435148 28541909 crunchy 427414 27371625 bcross 415724 29061187 +robal 371112 24642270 +mgrabiak 367963 26464704 velislav 342588 22140902 -mgrabiak 338763 23999170 +ncfish1 329039 20624527 Fisherman 327231 21829379 -robal 299836 20213182 Dantist 296386 18031762 -ncfish1 267604 17881149 +tolkki963 262050 22049676 +Sylvain27 255595 8864404 nordlandia 249322 16420192 +Fifis 237657 13065577 marrco 234581 17714473 -tolkki963 233490 19773930 +Calis007 217537 14450582 glinscott 208125 13277240 drabel 204167 13930674 mhoram 202894 12601997 bking_US 198894 11876016 -Calis007 188631 12795784 Thanar 179852 12365359 -Fifis 176209 10638245 -vdv 175544 9904472 +javran 169679 13481966 +armo9494 162863 10937118 spams 157128 10319326 -DesolatedDodo 156659 10210328 -armo9494 155355 10566898 +DesolatedDodo 156683 10211206 +Wencey 152308 8375444 sqrt2 147963 9724586 +vdbergh 140311 9225125 jcAEie 140086 10603658 -vdbergh 139746 9172061 CoffeeOne 137100 5024116 malala 136182 8002293 xoto 133759 9159372 +Dubslow 129614 8519312 davar 129023 8376525 DMBK 122960 8980062 dsmith 122059 7570238 -javran 121564 10144656 +CypressChess 120784 8672620 +sschnee 120526 7547722 +maposora 119734 10749710 amicic 119661 7938029 -sschnee 118107 7389266 -Wolfgang 114616 8070494 +Wolfgang 115713 8159062 Data 113305 8220352 BrunoBanani 112960 7436849 -Wencey 111502 5991676 -cuistot 108503 7006992 -CypressChess 108331 7759788 +markkulix 112897 9133168 +cuistot 109802 7121030 skiminki 107583 7218170 +sterni1971 104431 5938282 MaZePallas 102823 6633619 -sterni1971 100532 5880772 sunu 100167 7040199 zeryl 99331 6221261 thirdlife 99156 2245320 ElbertoOne 99028 7023771 -Dubslow 98600 6903242 -markkulix 97010 7643900 -bigpen0r 94809 6529203 +megaman7de 98456 6675076 +Goatminola 96765 8257832 +bigpen0r 94825 6529241 brabos 92118 6186135 Maxim 90818 3283364 psk 89957 5984901 -megaman7de 88822 6052132 racerschmacer 85805 6122790 -maposora 85710 7778146 Vizvezdenec 83761 5344740 0x3C33 82614 5271253 +szupaw 82495 7151686 BRAVONE 81239 5054681 nssy 76497 5259388 +cody 76126 4492126 jromang 76106 5236025 +MarcusTullius 76103 5061991 +woutboat 76072 6022922 +Spprtr 75977 5252287 teddybaer 75125 5407666 Pking_cda 73776 5293873 -yurikvelo 73516 5036928 -MarcusTullius 71053 4803477 +yurikvelo 73611 5046822 +Mineta 71130 4711422 Bobo1239 70579 4794999 solarlight 70517 5028306 dv8silencer 70287 3883992 -Spprtr 69646 4806763 -Mineta 66325 4537742 manap 66273 4121774 -szupaw 65468 5669742 tinker 64333 4268790 qurashee 61208 3429862 -woutboat 59496 4906352 AGI 58195 4329580 robnjr 57262 4053117 Freja 56938 3733019 @@ -108,39 +111,45 @@ MaxKlaxxMiner 56879 3423958 ttruscott 56010 3680085 rkl 55132 4164467 jmdana 54697 4012593 +notchris 53936 4184018 renouve 53811 3501516 -notchris 52433 4044590 finfish 51360 3370515 eva42 51272 3599691 eastorwest 51117 3454811 -Goatminola 51004 4432492 rap 49985 3219146 pb00067 49733 3298934 GPUex 48686 3684998 OuaisBla 48626 3445134 ronaldjerum 47654 3240695 biffhero 46564 3111352 -oryx 45533 3539290 +oryx 45639 3546530 VoyagerOne 45476 3452465 speedycpu 43842 3003273 jbwiebe 43305 2805433 Antihistamine 41788 2761312 mhunt 41735 2691355 +jibarbosa 41640 4145702 homyur 39893 2850481 gri 39871 2515779 +DeepnessFulled 39020 3323102 Garf 37741 2999686 SC 37299 2731694 -Sylvain27 36520 1467082 +Gaster319 37118 3279678 +naclosagc 36562 1279618 csnodgrass 36207 2688994 -Gaster319 35655 3149442 strelock 34716 2074055 +gopeto 33717 2245606 EthanOConnor 33370 2090311 slakovv 32915 2021889 -gopeto 31884 2076712 +jojo2357 32890 2826662 +shawnxu 32019 2802552 Gelma 31771 1551204 +vidar808 31560 1351810 kdave 31157 2198362 manapbk 30987 1810399 -ZacHFX 30551 2238078 +ZacHFX 30966 2272416 +TataneSan 30713 1513402 +votoanthuan 30691 2460856 Prcuvu 30377 2170122 anst 30301 2190091 jkiiski 30136 1904470 @@ -149,14 +158,15 @@ hyperbolic.tom 29840 2017394 chuckstablers 29659 2093438 Pyafue 29650 1902349 belzedar94 28846 1811530 -votoanthuan 27978 2285818 -shawnxu 27438 2465810 +mecevdimitar 27610 1721382 chriswk 26902 1868317 xwziegtm 26897 2124586 achambord 26582 1767323 +somethingintheshadows 26496 2186404 Patrick_G 26276 1801617 yorkman 26193 1992080 -Ulysses 25397 1701264 +srowen 25743 1490684 +Ulysses 25413 1702830 Jopo12321 25227 1652482 SFTUser 25182 1675689 nabildanial 25068 1531665 @@ -164,66 +174,69 @@ Sharaf_DG 24765 1786697 rodneyc 24376 1416402 jsys14 24297 1721230 agg177 23890 1395014 -srowen 23842 1342508 +AndreasKrug 23754 1890115 Ente 23752 1678188 -jojo2357 23479 2061238 JanErik 23408 1703875 Isidor 23388 1680691 Norabor 23371 1603244 +WoodMan777 23253 2023048 +Nullvalue 23155 2022752 cisco2015 22920 1763301 Zirie 22542 1472937 -Nullvalue 22490 1970374 -AndreasKrug 22485 1769491 team-oh 22272 1636708 Roady 22220 1465606 MazeOfGalious 21978 1629593 -sg4032 21947 1643353 +sg4032 21950 1643373 +tsim67 21747 1330880 ianh2105 21725 1632562 +Skiff84 21711 1014212 xor12 21628 1680365 dex 21612 1467203 nesoneg 21494 1463031 user213718 21454 1404128 +Serpensin 21452 1790510 sphinx 21211 1384728 -qoo_charly_cai 21135 1514907 +qoo_charly_cai 21136 1514927 +IslandLambda 21062 1220838 jjoshua2 21001 1423089 Zake9298 20938 1565848 horst.prack 20878 1465656 +fishtester 20729 1348888 0xB00B1ES 20590 1208666 -Serpensin 20487 1729674 -Dinde 20440 1292390 +ols 20477 1195945 +Dinde 20459 1292774 j3corre 20405 941444 Adrian.Schmidt123 20316 1281436 wei 19973 1745989 -fishtester 19617 1257388 +teenychess 19819 1762006 rstoesser 19569 1293588 eudhan 19274 1283717 vulcan 18871 1729392 +wizardassassin 18795 1376884 Karpovbot 18766 1053178 -WoodMan777 18556 1628264 jundery 18445 1115855 +mkstockfishtester 18350 1690676 ville 17883 1384026 chris 17698 1487385 purplefishies 17595 1092533 dju 17414 981289 -ols 17291 1042003 iisiraider 17275 1049015 -Skiff84 17111 950248 DragonLord 17014 1162790 +Karby 17008 1013160 +pirt 16965 1271519 redstone59 16842 1461780 -Karby 16839 1010124 Alb11747 16787 1213990 -pirt 16493 1237199 Naven94 16414 951718 -wizardassassin 16392 1148672 +scuzzi 16115 994341 IgorLeMasson 16064 1147232 -scuzzi 15757 968735 ako027ako 15671 1173203 +infinigon 15285 965966 Nikolay.IT 15154 1068349 Andrew Grant 15114 895539 OssumOpossum 14857 1007129 LunaticBFF57 14525 1190310 enedene 14476 905279 -IslandLambda 14393 958196 +Hjax 14394 1005013 bpfliegel 14233 882523 YELNAMRON 14230 1128094 mpx86 14019 759568 @@ -233,54 +246,56 @@ Nesa92 13806 1116101 crocogoat 13803 1117422 joster 13710 946160 mbeier 13650 1044928 -Hjax 13535 915487 +Pablohn26 13552 1088532 +wxt9861 13550 1312306 Dark_wizzie 13422 1007152 Rudolphous 13244 883140 Machariel 13010 863104 -infinigon 12991 943216 +nalanzeyu 12996 232590 mabichito 12903 749391 +Jackfish 12895 868928 thijsk 12886 722107 AdrianSA 12860 804972 Flopzee 12698 894821 +whelanh 12682 266404 mschmidt 12644 863193 korposzczur 12606 838168 -tsim67 12570 890180 -Jackfish 12553 836958 fatmurphy 12547 853210 -Oakwen 12503 853105 +Oakwen 12532 855759 +icewulf 12447 854878 SapphireBrand 12416 969604 deflectooor 12386 579392 modolief 12386 896470 -TataneSan 12358 609332 Farseer 12249 694108 +Hongildong 12201 648712 pgontarz 12151 848794 dbernier 12103 860824 -FormazChar 11989 907809 +szczur90 12035 942376 +FormazChar 12019 910409 +rensonthemove 11999 971993 stocky 11954 699440 -somethingintheshadows 11940 989472 -MooTheCow 11892 776126 +MooTheCow 11923 779432 3cho 11842 1036786 -whelanh 11557 245188 +ckaz 11792 732276 infinity 11470 727027 aga 11412 695127 torbjo 11395 729145 Thomas A. Anderson 11372 732094 savage84 11358 670860 +Def9Infinity 11345 696552 d64 11263 789184 ali-al-zhrani 11245 779246 -ckaz 11170 680866 +ImperiumAeternum 11155 952000 snicolet 11106 869170 dapper 11032 771402 Ethnikoi 10993 945906 Snuuka 10938 435504 -Karmatron 10859 678058 +Karmatron 10871 678306 basepi 10637 744851 -jibarbosa 10628 857100 Cubox 10621 826448 -mecevdimitar 10609 787318 +gerbil 10519 971688 michaelrpg 10509 739239 -Def9Infinity 10427 686978 OIVAS7572 10420 995586 -wxt9861 10412 1013864 Garruk 10365 706465 dzjp 10343 732529 +RickGroszkiewicz 10263 990798 From e0bfc4b69bbe928d6f474a46560bcc3b3f6709aa Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Tue, 3 Sep 2024 18:07:22 +0200 Subject: [PATCH 1703/1766] Stockfish 17 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Official release version of Stockfish 17 Bench: 1484730 --- Stockfish 17 Today we have the pleasure to announce a new major release of Stockfish. As always, you can freely download it at https://stockfishchess.org/download and use it in the GUI of your choice. Don’t forget to join our Discord server[1] to get in touch with the community of developers and users of the project! *Quality of chess play* In tests against Stockfish 16, this release brings an Elo gain of up to 46 points[2] and wins up to 4.5 times more game pairs[3] than it loses. In practice, high-quality moves are now found in less time, with a user upgrading from Stockfish 14 being able to analyze games at least 6 times[4] faster with Stockfish 17 while maintaining roughly the same quality. During this development period, Stockfish won its 9th consecutive first place in the main league of the Top Chess Engine Championship (TCEC)[5], and the 24th consecutive first place in the main events (bullet, blitz, and rapid) of the Computer Chess Championship (CCC)[6]. *Update highlights* *Improved engine lines* This release introduces principal variations (PVs) that are more informative for mate and decisive table base (TB) scores. In both cases, the PV will contain all moves up to checkmate. For mate scores, the PV shown is the best variation known to the engine at that point, while for table base wins, it follows, based on the TB, a sequence of moves that preserves the game outcome to checkmate. *NUMA performance optimization* For high-end computers with multiple CPUs (typically a dual-socket architecture with 100+ cores), this release automatically improves performance with a `NumaPolicy` setting that optimizes non-uniform memory access (NUMA). Although typical consumer hardware will not benefit, speedups of up to 2.8x[7] have been measured. *Shoutouts* *ChessDB* During the past 1.5 years, hundreds of cores have been continuously running Stockfish to grow a database of analyzed positions. This chess cloud database[8] now contains well over 45 billion positions, providing excellent coverage of all openings and commonly played lines. This database is already integrated into GUIs such as En Croissant[9] and Nibbler[10], which access it through the public API. *Leela Chess Zero* Generally considered to be the strongest GPU engine, it continues to provide open data which is essential for training our NNUE networks. They released version 0.31.1[11] of their engine a few weeks ago, check it out! *Website redesign* Our website has undergone a redesign in recent months, most notably in our home page[12], now featuring a darker color scheme and a more modern aesthetic, while still maintaining its core identity. We hope you'll like it as much as we do! *Thank you* The Stockfish project builds on a thriving community of enthusiasts (thanks everybody!) who contribute their expertise, time, and resources to build a free and open-source chess engine that is robust, widely available, and very strong. We would like to express our gratitude for the 11k stars[13] that light up our GitHub project! Thank you for your support and encouragement – your recognition means a lot to us. We invite our chess fans to join the Fishtest testing framework[14] to contribute compute resources needed for development. Programmers can contribute to the project either directly to Stockfish[15] (C++), to Fishtest[16] (HTML, CSS, JavaScript, and Python), to our trainer nnue-pytorch[17] (C++ and Python), or to our website[18] (HTML, CSS/SCSS, and JavaScript). The Stockfish team [1] https://discord.gg/GWDRS3kU6R [2] https://tests.stockfishchess.org/tests/view/66d738ba9de3e7f9b33d159a [3] https://tests.stockfishchess.org/tests/view/66d738f39de3e7f9b33d15a0 [4] https://github.com/official-stockfish/Stockfish/wiki/Useful-data#equivalent-time-odds-and-normalized-game-pair-elo [5] https://en.wikipedia.org/wiki/Stockfish_(chess)#Top_Chess_Engine_Championship [6] https://en.wikipedia.org/wiki/Stockfish_(chess)#Chess.com_Computer_Chess_Championship [7] https://github.com/official-stockfish/Stockfish/pull/5285 [8] https://chessdb.cn/queryc_en/ [9] https://encroissant.org/ [10] https://github.com/rooklift/nibbler [11] https://github.com/LeelaChessZero/lc0/releases/tag/v0.31.1 [12] https://stockfishchess.org/ [13] https://github.com/official-stockfish/Stockfish/stargazers [14] https://github.com/official-stockfish/fishtest/wiki/Running-the-worker [15] https://github.com/official-stockfish/Stockfish [16] https://github.com/official-stockfish/fishtest [17] https://github.com/official-stockfish/nnue-pytorch [18] https://github.com/official-stockfish/stockfish-web --- src/misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index 664ab4b89ff..91cdbc4dccc 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -38,7 +38,7 @@ namespace Stockfish { namespace { // Version number or dev. -constexpr std::string_view version = "dev"; +constexpr std::string_view version = "17"; // Our fancy logging facility. The trick here is to replace cin.rdbuf() and // cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We From f4ba7ce67a0a4b77d9d641b4010dfc73f3b534b2 Mon Sep 17 00:00:00 2001 From: Disservin Date: Mon, 9 Sep 2024 17:07:36 +0200 Subject: [PATCH 1704/1766] Restore development closes https://github.com/official-stockfish/Stockfish/pull/5580 No functional change --- src/misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/misc.cpp b/src/misc.cpp index 91cdbc4dccc..664ab4b89ff 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -38,7 +38,7 @@ namespace Stockfish { namespace { // Version number or dev. -constexpr std::string_view version = "17"; +constexpr std::string_view version = "dev"; // Our fancy logging facility. The trick here is to replace cin.rdbuf() and // cout.rdbuf() with two Tie objects that tie cin and cout to a file stream. We From 4fb04eb3df9279cd7b8c3d43dbf1916b3c74bea3 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Fri, 30 Aug 2024 14:09:24 +0300 Subject: [PATCH 1705/1766] Simplify history bonus After we recently added the disallowance for negative bonuses, it is no longer necessary to keep the max comparison in the previous step. Passed STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 72000 W: 18820 L: 18637 D: 34543 Ptnml(0-2): 267, 8489, 18276, 8730, 238 https://tests.stockfishchess.org/tests/view/66ce132cbf8c9d8780fdabe7 Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 67452 W: 17136 L: 16961 D: 33355 Ptnml(0-2): 35, 7489, 18519, 7632, 51 https://tests.stockfishchess.org/tests/view/66cf6ad49de3e7f9b33d1010 closes https://github.com/official-stockfish/Stockfish/pull/5554 Bench: 1147012 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index ad2c35e7708..9d950c0e755 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1347,7 +1347,7 @@ Value Search::Worker::search( + 134 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 91)); // Proportional to "how much damage we have to undo" - bonus += std::clamp(-(ss - 1)->statScore / 100, -94, 304); + bonus += std::min(-(ss - 1)->statScore / 100, 304); bonus = std::max(bonus, 0); From ddc9f48bc3e0b1d22b2d1259d5d45d4640e0374d Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Fri, 23 Aug 2024 17:13:49 -0700 Subject: [PATCH 1706/1766] Introduce Material Correction History Idea from Caissa (https://github.com/Witek902/Caissa) chess engine. Add a secondary correction history indexed by the material key of a position. The material key is the zobrist hash representing the number of pieces left in a position. Passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 189472 W: 49360 L: 48813 D: 91299 Ptnml(0-2): 666, 22453, 47953, 22996, 668 https://tests.stockfishchess.org/tests/view/66cbddafbf8c9d8780fda9f1 Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 224190 W: 57022 L: 56312 D: 110856 Ptnml(0-2): 197, 24723, 61540, 25443, 192 https://tests.stockfishchess.org/tests/view/66cd529bbf8c9d8780fdab4c closes https://github.com/official-stockfish/Stockfish/pull/5556 Bench: 1462697 --- src/movepick.h | 30 ++++++++++++++++++++++-------- src/search.cpp | 11 ++++++++--- src/search.h | 11 ++++++----- 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/src/movepick.h b/src/movepick.h index 61f6368e6d9..651091b0829 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -34,14 +34,15 @@ namespace Stockfish { -constexpr int PAWN_HISTORY_SIZE = 512; // has to be a power of 2 -constexpr int CORRECTION_HISTORY_SIZE = 16384; // has to be a power of 2 -constexpr int CORRECTION_HISTORY_LIMIT = 1024; +constexpr int PAWN_HISTORY_SIZE = 512; // has to be a power of 2 +constexpr int PAWN_CORRECTION_HISTORY_SIZE = 16384; // has to be a power of 2 +constexpr int MATERIAL_CORRECTION_HISTORY_SIZE = 32768; // has to be a power of 2 +constexpr int CORRECTION_HISTORY_LIMIT = 1024; static_assert((PAWN_HISTORY_SIZE & (PAWN_HISTORY_SIZE - 1)) == 0, "PAWN_HISTORY_SIZE has to be a power of 2"); -static_assert((CORRECTION_HISTORY_SIZE & (CORRECTION_HISTORY_SIZE - 1)) == 0, +static_assert((PAWN_CORRECTION_HISTORY_SIZE & (PAWN_CORRECTION_HISTORY_SIZE - 1)) == 0, "CORRECTION_HISTORY_SIZE has to be a power of 2"); enum PawnHistoryType { @@ -51,7 +52,11 @@ enum PawnHistoryType { template inline int pawn_structure_index(const Position& pos) { - return pos.pawn_key() & ((T == Normal ? PAWN_HISTORY_SIZE : CORRECTION_HISTORY_SIZE) - 1); + return pos.pawn_key() & ((T == Normal ? PAWN_HISTORY_SIZE : PAWN_CORRECTION_HISTORY_SIZE) - 1); +} + +inline int material_index(const Position& pos) { + return pos.material_key() & (MATERIAL_CORRECTION_HISTORY_SIZE - 1); } // StatsEntry stores the stat table value. It is usually a number but could @@ -133,9 +138,18 @@ using ContinuationHistory = Stats // PawnHistory is addressed by the pawn structure and a move's [piece][to] using PawnHistory = Stats; -// CorrectionHistory is addressed by color and pawn structure -using CorrectionHistory = - Stats; + +// Correction histories record differences between the static evaluation of +// positions and their search score. It is used to improve the static evaluation +// used by some search heuristics. + +// PawnCorrectionHistory is addressed by color and pawn structure +using PawnCorrectionHistory = + Stats; + +// MaterialCorrectionHistory is addressed by color and material configuration +using MaterialCorrectionHistory = + Stats; // The MovePicker class is used to pick one pseudo-legal move at a time from the // current position. The most important method is next_move(), which emits one diff --git a/src/search.cpp b/src/search.cpp index 9d950c0e755..bc85a5c3e96 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -81,7 +81,10 @@ constexpr int futility_move_count(bool improving, Depth depth) { // Add correctionHistory value to raw staticEval and guarantee evaluation // does not hit the tablebase range. Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { - auto cv = w.correctionHistory[pos.side_to_move()][pawn_structure_index(pos)]; + const auto pcv = + w.pawnCorrectionHistory[pos.side_to_move()][pawn_structure_index(pos)]; + const auto mcv = w.materialCorrectionHistory[pos.side_to_move()][material_index(pos)]; + const auto cv = (2 * pcv + mcv) / 3; v += 66 * cv / 512; return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } @@ -487,7 +490,8 @@ void Search::Worker::clear() { mainHistory.fill(0); captureHistory.fill(-700); pawnHistory.fill(-1188); - correctionHistory.fill(0); + pawnCorrectionHistory.fill(0); + materialCorrectionHistory.fill(0); for (bool inCheck : {false, true}) for (StatsType c : {NoCaptures, Captures}) @@ -1390,7 +1394,8 @@ Value Search::Worker::search( { auto bonus = std::clamp(int(bestValue - ss->staticEval) * depth / 8, -CORRECTION_HISTORY_LIMIT / 4, CORRECTION_HISTORY_LIMIT / 4); - thisThread->correctionHistory[us][pawn_structure_index(pos)] << bonus; + thisThread->pawnCorrectionHistory[us][pawn_structure_index(pos)] << bonus; + thisThread->materialCorrectionHistory[us][material_index(pos)] << bonus; } assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); diff --git a/src/search.h b/src/search.h index 0f635186a3b..c9fe9e184ac 100644 --- a/src/search.h +++ b/src/search.h @@ -277,11 +277,12 @@ class Worker { void ensure_network_replicated(); // Public because they need to be updatable by the stats - ButterflyHistory mainHistory; - CapturePieceToHistory captureHistory; - ContinuationHistory continuationHistory[2][2]; - PawnHistory pawnHistory; - CorrectionHistory correctionHistory; + ButterflyHistory mainHistory; + CapturePieceToHistory captureHistory; + ContinuationHistory continuationHistory[2][2]; + PawnHistory pawnHistory; + PawnCorrectionHistory pawnCorrectionHistory; + MaterialCorrectionHistory materialCorrectionHistory; private: void iterative_deepening(); From e74452ae44df35aeda21e81bb2eec883a7a45c38 Mon Sep 17 00:00:00 2001 From: Daniel Monroe Date: Sun, 1 Sep 2024 20:56:09 -0400 Subject: [PATCH 1707/1766] Reduce on ttcaptures if not capture Tweak ttcapture reduction. Reduce on ttcaptures only if the current move is a capture Passed STC: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 94912 W: 24896 L: 24492 D: 45524 Ptnml(0-2): 301, 11197, 24087, 11539, 332 https://tests.stockfishchess.org/tests/view/66cd2264bf8c9d8780fdab34 Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 60738 W: 15465 L: 15096 D: 30177 Ptnml(0-2): 42, 6573, 16775, 6932, 47 https://tests.stockfishchess.org/tests/view/66cf356d9de3e7f9b33d0fde closes https://github.com/official-stockfish/Stockfish/pull/5562 Bench: 1268700 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index bc85a5c3e96..aab5c743cc5 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1140,8 +1140,8 @@ Value Search::Worker::search( if (cutNode) r += 2 - (ttData.depth >= depth && ss->ttPv); - // Increase reduction if ttMove is a capture (~3 Elo) - if (ttCapture) + // Increase reduction if ttMove is a capture but the current move is not a capture (~3 Elo) + if (ttCapture && !capture) r++; // Increase reduction if next ply has a lot of fail high (~5 Elo) From 66a7965b0fab4d1ae59203039b0b2262dbf2bcc0 Mon Sep 17 00:00:00 2001 From: xu-shawn <50402888+xu-shawn@users.noreply.github.com> Date: Fri, 6 Sep 2024 14:31:40 -0700 Subject: [PATCH 1708/1766] Copy scripts directory in distributed packages closes https://github.com/official-stockfish/Stockfish/pull/5571 No functional change --- .github/workflows/upload_binaries.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/upload_binaries.yml b/.github/workflows/upload_binaries.yml index c5a2cd105c6..1067f6e7615 100644 --- a/.github/workflows/upload_binaries.yml +++ b/.github/workflows/upload_binaries.yml @@ -59,6 +59,7 @@ jobs: mv "${{ matrix.config.simple_name }} ${{ matrix.binaries }}" stockfish-workflow cd stockfish-workflow cp -r src ../stockfish/ + cp -r scripts ../stockfish/ cp stockfish-$NAME-$BINARY$EXT ../stockfish/ cp "Top CPU Contributors.txt" ../stockfish/ cp Copying.txt ../stockfish/ From 1b310cc87e22840621284f27f1f5873c9b9c0384 Mon Sep 17 00:00:00 2001 From: MinetaS Date: Mon, 2 Sep 2024 19:22:18 +0900 Subject: [PATCH 1709/1766] Export and clean up net downloading script Fixes https://github.com/official-stockfish/Stockfish/issues/5564 This patch extracts the net downloading script in Makefile into an external script file. Also the script is moderately rewritten for improved readability and speed. * Use wget preferentially over curl, as curl is known to have slight overhead. * Use command instead of hash to check if command exists. Reportedly, hash always returns zero in some POSIX shells even when the command fails. * Command existence checks (wget/curl, sha256sum) are performed only once at the beginning. * Each of common patterns is encapsulated in a function (get_nnue_filename, validate_network). * Print out error/warning messages to stderr. closes https://github.com/official-stockfish/Stockfish/pull/5563 No functional change Co-authored-by: Disservin --- .github/workflows/tests.yml | 12 +++--- scripts/net.sh | 75 +++++++++++++++++++++++++++++++++++++ src/Makefile | 52 +------------------------ 3 files changed, 82 insertions(+), 57 deletions(-) create mode 100755 scripts/net.sh diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8d209a4f974..a826e6f063e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -143,7 +143,7 @@ jobs: FROM ${{ matrix.config.base_image }} WORKDIR /app RUN apk update && apk add make g++ - CMD ["sh", "script.sh"] + CMD ["sh", "src/script.sh"] EOF - name: Download required macOS packages @@ -176,7 +176,7 @@ jobs: $COMPCXX -v else echo "$COMPCXX -v" > script.sh - docker run --rm --platform ${{ matrix.config.platform }} -v ${{ github.workspace }}/src:/app sf_builder + docker run --rm --platform ${{ matrix.config.platform }} -v ${{ github.workspace }}:/app sf_builder fi - name: Test help target @@ -342,8 +342,8 @@ jobs: - name: Test riscv64 build if: matrix.config.run_riscv64_tests run: | - echo "export LDFLAGS='-static' && make clean && make -j4 ARCH=riscv64 build" > script.sh - docker run --rm --platform ${{ matrix.config.platform }} -v ${{ github.workspace }}/src:/app sf_builder + echo "cd src && export LDFLAGS='-static' && make clean && make -j4 ARCH=riscv64 build" > script.sh + docker run --rm --platform ${{ matrix.config.platform }} -v ${{ github.workspace }}:/app sf_builder ../tests/signature.sh $benchref # ppc64 tests @@ -351,8 +351,8 @@ jobs: - name: Test ppc64 build if: matrix.config.run_ppc64_tests run: | - echo "export LDFLAGS='-static' && make clean && make -j4 ARCH=ppc-64 build" > script.sh - docker run --rm --platform ${{ matrix.config.platform }} -v ${{ github.workspace }}/src:/app sf_builder + echo "cd src && export LDFLAGS='-static' && make clean && make -j4 ARCH=ppc-64 build" > script.sh + docker run --rm --platform ${{ matrix.config.platform }} -v ${{ github.workspace }}:/app sf_builder ../tests/signature.sh $benchref # Other tests diff --git a/scripts/net.sh b/scripts/net.sh new file mode 100755 index 00000000000..168fbad66b0 --- /dev/null +++ b/scripts/net.sh @@ -0,0 +1,75 @@ +#!/bin/sh + +wget_or_curl=$( (command -v wget > /dev/null 2>&1 && echo "wget -q") || \ + (command -v curl > /dev/null 2>&1 && echo "curl -L -s -k")) + +if [ -z "$wget_or_curl" ]; then + >&2 printf "%s\n" "Neither wget or curl is installed." \ + "Install one of these tools to download NNUE files automatically." + exit 1 +fi + +sha256sum=$( (command -v shasum > /dev/null 2>&1 && echo "shasum -a 256") || \ + (command -v sha256sum > /dev/null 2>&1 && echo "sha256sum")) + +if [ -z "$sha256sum" ]; then + >&2 echo "sha256sum not found, NNUE files will be assumed valid." +fi + +get_nnue_filename() { + grep "$1" evaluate.h | grep "#define" | sed "s/.*\(nn-[a-z0-9]\{12\}.nnue\).*/\1/" +} + +validate_network() { + # If no sha256sum command is available, assume the file is always valid. + if [ -n "$sha256sum" ] && [ -f "$1" ]; then + if [ "$1" != "nn-$($sha256sum "$1" | cut -c 1-12).nnue" ]; then + rm -f "$1" + return 1 + fi + fi +} + +fetch_network() { + _filename="$(get_nnue_filename "$1")" + + if [ -z "$_filename" ]; then + >&2 echo "NNUE file name not found for: $1" + return 1 + fi + + if [ -f "$_filename" ]; then + if validate_network "$_filename"; then + echo "Existing $_filename validated, skipping download" + return + else + echo "Removing invalid NNUE file: $_filename" + fi + fi + + for url in \ + "https://tests.stockfishchess.org/api/nn/$_filename" \ + "https://github.com/official-stockfish/networks/raw/master/$_filename"; do + echo "Downloading from $url ..." + if $wget_or_curl "$url"; then + if validate_network "$_filename"; then + echo "Successfully validated $_filename" + else + echo "Downloaded $_filename is invalid" + continue + fi + else + echo "Failed to download from $url" + fi + if [ -f "$_filename" ]; then + return + fi + done + + # Download was not successful in the loop, return false. + >&2 echo "Failed to download $_filename" + return 1 +} + +fetch_network EvalFileDefaultNameBig && \ +fetch_network EvalFileDefaultNameSmall diff --git a/src/Makefile b/src/Makefile index 7142b972745..042d9479cc8 100644 --- a/src/Makefile +++ b/src/Makefile @@ -917,59 +917,9 @@ profileclean: @rm -f stockfish.res @rm -f ./-lstdc++.res -define fetch_network - @echo "Default net: $(nnuenet)" - @if [ "x$(curl_or_wget)" = "x" ]; then \ - echo "Neither curl nor wget is installed. Install one of these tools unless the net has been downloaded manually"; \ - fi - @if [ "x$(shasum_command)" = "x" ]; then \ - echo "shasum / sha256sum not found, skipping net validation"; \ - elif test -f "$(nnuenet)"; then \ - if [ "$(nnuenet)" != "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then \ - echo "Removing invalid network"; rm -f $(nnuenet); \ - fi; \ - fi; - @for nnuedownloadurl in "$(nnuedownloadurl1)" "$(nnuedownloadurl2)"; do \ - if test -f "$(nnuenet)"; then \ - echo "$(nnuenet) available : OK"; break; \ - else \ - if [ "x$(curl_or_wget)" != "x" ]; then \ - echo "Downloading $${nnuedownloadurl}"; $(curl_or_wget) $${nnuedownloadurl} > $(nnuenet);\ - else \ - echo "No net found and download not possible"; exit 1;\ - fi; \ - fi; \ - if [ "x$(shasum_command)" != "x" ]; then \ - if [ "$(nnuenet)" != "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then \ - echo "Removing failed download"; rm -f $(nnuenet); \ - fi; \ - fi; \ - done - @if ! test -f "$(nnuenet)"; then \ - echo "Failed to download $(nnuenet)."; \ - fi; - @if [ "x$(shasum_command)" != "x" ]; then \ - if [ "$(nnuenet)" = "nn-"`$(shasum_command) $(nnuenet) | cut -c1-12`".nnue" ]; then \ - echo "Network validated"; break; \ - fi; \ - fi; -endef - -# set up shell variables for the net stuff -define netvariables -$(eval nnuenet := $(shell grep $(1) evaluate.h | grep define | sed 's/.*\(nn-[a-z0-9]\{12\}.nnue\).*/\1/')) -$(eval nnuedownloadurl1 := https://tests.stockfishchess.org/api/nn/$(nnuenet)) -$(eval nnuedownloadurl2 := https://github.com/official-stockfish/networks/raw/master/$(nnuenet)) -$(eval curl_or_wget := $(shell if hash curl 2>/dev/null; then echo "curl -skL"; elif hash wget 2>/dev/null; then echo "wget -qO-"; fi)) -$(eval shasum_command := $(shell if hash shasum 2>/dev/null; then echo "shasum -a 256 "; elif hash sha256sum 2>/dev/null; then echo "sha256sum "; fi)) -endef - # evaluation network (nnue) net: - $(call netvariables, EvalFileDefaultNameBig) - $(call fetch_network) - $(call netvariables, EvalFileDefaultNameSmall) - $(call fetch_network) + @$(SHELL) ../scripts/net.sh format: $(CLANG-FORMAT) -i $(SRCS) $(HEADERS) -style=file From d7e3a708d456ff2793c2392c13d8d9cbea61aaba Mon Sep 17 00:00:00 2001 From: xu-shawn <50402888+xu-shawn@users.noreply.github.com> Date: Fri, 6 Sep 2024 14:20:40 -0700 Subject: [PATCH 1710/1766] Remove ARCH=... from README.md closes https://github.com/official-stockfish/Stockfish/pull/5570 No functional change --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 52b123cbd25..25da319d5a2 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ descriptions. An example suitable for most Intel and AMD chips: ``` cd src -make -j profile-build ARCH=x86-64-avx2 +make -j profile-build ``` Detailed compilation instructions for all platforms can be found in our From a8cb002038bf314764a737077864a961c0e1d145 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 7 Sep 2024 18:46:09 +0300 Subject: [PATCH 1711/1766] Simplify ttmove reduction Remove condition that clamps reductions for tt move. Passed STC: https://tests.stockfishchess.org/tests/view/66d5f1239de3e7f9b33d14b0 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 91136 W: 23805 L: 23646 D: 43685 Ptnml(0-2): 334, 10328, 24066, 10525, 315 Passed LTC: https://tests.stockfishchess.org/tests/view/66d7c5889de3e7f9b33d1721 LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 139242 W: 35130 L: 35030 D: 69082 Ptnml(0-2): 78, 15200, 38986, 15258, 99 closes https://github.com/official-stockfish/Stockfish/pull/5574 Bench: 1268715 --- src/search.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index aab5c743cc5..1ed849f2a34 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1148,10 +1148,9 @@ Value Search::Worker::search( if ((ss + 1)->cutoffCnt > 3) r += 1 + allNode; - // For first picked move (ttMove) reduce reduction, but never allow - // reduction to go below 0 (~3 Elo) + // For first picked move (ttMove) reduce reduction (~3 Elo) else if (move == ttData.move) - r = std::max(0, r - 2); + r -= 2; ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + (*contHist[0])[movedPiece][move.to_sq()] From effa2460710aef54465967796099916a5f0d13d3 Mon Sep 17 00:00:00 2001 From: Disservin Date: Sat, 7 Sep 2024 20:34:10 +0200 Subject: [PATCH 1712/1766] Use optional for the engine path - A small quality of file change is to change the type of engine path from a string to an optional string, skips the binary directory lookup, which is commonly disabled by people who create wasm builds or include stockfish as a library. closes https://github.com/official-stockfish/Stockfish/pull/5575 No functional change --- src/engine.cpp | 4 ++-- src/engine.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/engine.cpp b/src/engine.cpp index 81bb260bd88..b5cc3f832f5 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -47,8 +47,8 @@ namespace NN = Eval::NNUE; constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; constexpr int MaxHashMB = Is64Bit ? 33554432 : 2048; -Engine::Engine(std::string path) : - binaryDirectory(CommandLine::get_binary_directory(path)), +Engine::Engine(std::optional path) : + binaryDirectory(path ? CommandLine::get_binary_directory(*path) : ""), numaContext(NumaConfig::from_system()), states(new std::deque(1)), threads(), diff --git a/src/engine.h b/src/engine.h index f3c78398669..efab1c6af83 100644 --- a/src/engine.h +++ b/src/engine.h @@ -47,7 +47,7 @@ class Engine { using InfoFull = Search::InfoFull; using InfoIter = Search::InfoIteration; - Engine(std::string path = ""); + Engine(std::optional path = std::nullopt); // Cannot be movable due to components holding backreferences to fields Engine(const Engine&) = delete; From 2680c9c7992f6565e9a2f0acc52260af55e56b5a Mon Sep 17 00:00:00 2001 From: MinetaS Date: Fri, 6 Sep 2024 22:14:47 +0900 Subject: [PATCH 1713/1766] Small speedup in incremental accumulator updates Instead of updating at most two accumulators, update all accumluators during incremental updates. Tests have shown that this change yields a small speedup of at least 0.5%, and up to 1% with shorter TC. Passed STC: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 54368 W: 14179 L: 13842 D: 26347 Ptnml(0-2): 173, 6122, 14262, 6449, 178 https://tests.stockfishchess.org/tests/view/66db038a9de3e7f9b33d1ad9 Passed 5+0.05: LLR: 2.98 (-2.94,2.94) <0.00,2.00> Total: 55040 W: 14682 L: 14322 D: 26036 Ptnml(0-2): 303, 6364, 13856, 6664, 333 https://tests.stockfishchess.org/tests/view/66dbc325dc53972b68218ba7 Passed non-regression LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 57390 W: 14555 L: 14376 D: 28459 Ptnml(0-2): 37, 5876, 16683, 6069, 30 https://tests.stockfishchess.org/tests/view/66dbc30adc53972b68218ba5 closes https://github.com/official-stockfish/Stockfish/pull/5576 No functional change --- src/nnue/nnue_feature_transformer.h | 330 ++++++++++++---------------- src/position.cpp | 2 + src/position.h | 1 + 3 files changed, 140 insertions(+), 193 deletions(-) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 2f74dcae2ed..fa180678d89 100644 --- a/src/nnue/nnue_feature_transformer.h +++ b/src/nnue/nnue_feature_transformer.h @@ -453,11 +453,10 @@ class FeatureTransformer { private: template - [[nodiscard]] std::pair - try_find_computed_accumulator(const Position& pos) const { + StateInfo* try_find_computed_accumulator(const Position& pos) const { // Look for a usable accumulator of an earlier position. We keep track // of the estimated gain in terms of features to be added/subtracted. - StateInfo *st = pos.state(), *next = nullptr; + StateInfo* st = pos.state(); int gain = FeatureSet::refresh_cost(pos); while (st->previous && !(st->*accPtr).computed[Perspective]) { @@ -466,30 +465,17 @@ class FeatureTransformer { if (FeatureSet::requires_refresh(st, Perspective) || (gain -= FeatureSet::update_cost(st) + 1) < 0) break; - next = st; - st = st->previous; + st = st->previous; } - return {st, next}; + return st; } - // NOTE: The parameter states_to_update is an array of position states. - // All states must be sequential, that is states_to_update[i] must - // either be reachable by repeatedly applying ->previous from - // states_to_update[i+1], and computed_st must be reachable by - // repeatedly applying ->previous on states_to_update[0]. - template - void update_accumulator_incremental(const Position& pos, - StateInfo* computed_st, - StateInfo* states_to_update[N]) const { - static_assert(N > 0); - assert([&]() { - for (size_t i = 0; i < N; ++i) - { - if (states_to_update[i] == nullptr) - return false; - } - return true; - }()); + // It computes the accumulator of the next position, or updates the + // current position's accumulator if CurrentOnly is true. + template + void update_accumulator_incremental(const Position& pos, StateInfo* computed) const { + assert((computed->*accPtr).computed[Perspective]); + assert(computed->next != nullptr); #ifdef VECTOR // Gcc-10.2 unnecessarily spills AVX2 registers if this array @@ -498,205 +484,186 @@ class FeatureTransformer { psqt_vec_t psqt[NumPsqtRegs]; #endif - // Update incrementally going back through states_to_update. - // Gather all features to be updated. const Square ksq = pos.square(Perspective); // The size must be enough to contain the largest possible update. // That might depend on the feature set and generally relies on the // feature set's update cost calculation to be correct and never allow // updates with more added/removed features than MaxActiveDimensions. - FeatureSet::IndexList removed[N], added[N]; - - for (int i = N - 1; i >= 0; --i) - { - (states_to_update[i]->*accPtr).computed[Perspective] = true; - - const StateInfo* end_state = i == 0 ? computed_st : states_to_update[i - 1]; + FeatureSet::IndexList removed, added; - for (StateInfo* st2 = states_to_update[i]; st2 != end_state; st2 = st2->previous) - FeatureSet::append_changed_indices(ksq, st2->dirtyPiece, removed[i], - added[i]); - } + if constexpr (CurrentOnly) + for (StateInfo* st = pos.state(); st != computed; st = st->previous) + FeatureSet::append_changed_indices(ksq, st->dirtyPiece, removed, + added); + else + FeatureSet::append_changed_indices(ksq, computed->next->dirtyPiece, + removed, added); - StateInfo* st = computed_st; + StateInfo* next = CurrentOnly ? pos.state() : computed->next; + assert(!(next->*accPtr).computed[Perspective]); - // Now update the accumulators listed in states_to_update[], - // where the last element is a sentinel. #ifdef VECTOR - - if (N == 1 && (removed[0].size() == 1 || removed[0].size() == 2) && added[0].size() == 1) + if ((removed.size() == 1 || removed.size() == 2) && added.size() == 1) { - assert(states_to_update[0]); - auto accIn = - reinterpret_cast(&(st->*accPtr).accumulation[Perspective][0]); - auto accOut = reinterpret_cast( - &(states_to_update[0]->*accPtr).accumulation[Perspective][0]); + reinterpret_cast(&(computed->*accPtr).accumulation[Perspective][0]); + auto accOut = reinterpret_cast(&(next->*accPtr).accumulation[Perspective][0]); - const IndexType offsetR0 = HalfDimensions * removed[0][0]; + const IndexType offsetR0 = HalfDimensions * removed[0]; auto columnR0 = reinterpret_cast(&weights[offsetR0]); - const IndexType offsetA = HalfDimensions * added[0][0]; + const IndexType offsetA = HalfDimensions * added[0]; auto columnA = reinterpret_cast(&weights[offsetA]); - if (removed[0].size() == 1) + if (removed.size() == 1) { - for (IndexType k = 0; k < HalfDimensions * sizeof(std::int16_t) / sizeof(vec_t); - ++k) - accOut[k] = vec_add_16(vec_sub_16(accIn[k], columnR0[k]), columnA[k]); + for (IndexType i = 0; i < HalfDimensions * sizeof(WeightType) / sizeof(vec_t); ++i) + accOut[i] = vec_add_16(vec_sub_16(accIn[i], columnR0[i]), columnA[i]); } else { - const IndexType offsetR1 = HalfDimensions * removed[0][1]; + const IndexType offsetR1 = HalfDimensions * removed[1]; auto columnR1 = reinterpret_cast(&weights[offsetR1]); - for (IndexType k = 0; k < HalfDimensions * sizeof(std::int16_t) / sizeof(vec_t); - ++k) - accOut[k] = vec_sub_16(vec_add_16(accIn[k], columnA[k]), - vec_add_16(columnR0[k], columnR1[k])); + for (IndexType i = 0; i < HalfDimensions * sizeof(WeightType) / sizeof(vec_t); ++i) + accOut[i] = vec_sub_16(vec_add_16(accIn[i], columnA[i]), + vec_add_16(columnR0[i], columnR1[i])); } - auto accPsqtIn = - reinterpret_cast(&(st->*accPtr).psqtAccumulation[Perspective][0]); - auto accPsqtOut = reinterpret_cast( - &(states_to_update[0]->*accPtr).psqtAccumulation[Perspective][0]); + auto accPsqtIn = reinterpret_cast( + &(computed->*accPtr).psqtAccumulation[Perspective][0]); + auto accPsqtOut = + reinterpret_cast(&(next->*accPtr).psqtAccumulation[Perspective][0]); - const IndexType offsetPsqtR0 = PSQTBuckets * removed[0][0]; + const IndexType offsetPsqtR0 = PSQTBuckets * removed[0]; auto columnPsqtR0 = reinterpret_cast(&psqtWeights[offsetPsqtR0]); - const IndexType offsetPsqtA = PSQTBuckets * added[0][0]; + const IndexType offsetPsqtA = PSQTBuckets * added[0]; auto columnPsqtA = reinterpret_cast(&psqtWeights[offsetPsqtA]); - if (removed[0].size() == 1) + if (removed.size() == 1) { - for (std::size_t k = 0; k < PSQTBuckets * sizeof(std::int32_t) / sizeof(psqt_vec_t); - ++k) - accPsqtOut[k] = vec_add_psqt_32(vec_sub_psqt_32(accPsqtIn[k], columnPsqtR0[k]), - columnPsqtA[k]); + for (std::size_t i = 0; + i < PSQTBuckets * sizeof(PSQTWeightType) / sizeof(psqt_vec_t); ++i) + accPsqtOut[i] = vec_add_psqt_32(vec_sub_psqt_32(accPsqtIn[i], columnPsqtR0[i]), + columnPsqtA[i]); } else { - const IndexType offsetPsqtR1 = PSQTBuckets * removed[0][1]; + const IndexType offsetPsqtR1 = PSQTBuckets * removed[1]; auto columnPsqtR1 = reinterpret_cast(&psqtWeights[offsetPsqtR1]); - for (std::size_t k = 0; k < PSQTBuckets * sizeof(std::int32_t) / sizeof(psqt_vec_t); - ++k) - accPsqtOut[k] = - vec_sub_psqt_32(vec_add_psqt_32(accPsqtIn[k], columnPsqtA[k]), - vec_add_psqt_32(columnPsqtR0[k], columnPsqtR1[k])); + for (std::size_t i = 0; + i < PSQTBuckets * sizeof(PSQTWeightType) / sizeof(psqt_vec_t); ++i) + accPsqtOut[i] = + vec_sub_psqt_32(vec_add_psqt_32(accPsqtIn[i], columnPsqtA[i]), + vec_add_psqt_32(columnPsqtR0[i], columnPsqtR1[i])); } } else { - for (IndexType j = 0; j < HalfDimensions / TileHeight; ++j) + for (IndexType i = 0; i < HalfDimensions / TileHeight; ++i) { // Load accumulator auto accTileIn = reinterpret_cast( - &(st->*accPtr).accumulation[Perspective][j * TileHeight]); - for (IndexType k = 0; k < NumRegs; ++k) - acc[k] = vec_load(&accTileIn[k]); + &(computed->*accPtr).accumulation[Perspective][i * TileHeight]); + for (IndexType j = 0; j < NumRegs; ++j) + acc[j] = vec_load(&accTileIn[j]); + + // Difference calculation for the deactivated features + for (const auto index : removed) + { + const IndexType offset = HalfDimensions * index + i * TileHeight; + auto column = reinterpret_cast(&weights[offset]); + for (IndexType j = 0; j < NumRegs; ++j) + acc[j] = vec_sub_16(acc[j], column[j]); + } - for (IndexType i = 0; i < N; ++i) + // Difference calculation for the activated features + for (const auto index : added) { - // Difference calculation for the deactivated features - for (const auto index : removed[i]) - { - const IndexType offset = HalfDimensions * index + j * TileHeight; - auto column = reinterpret_cast(&weights[offset]); - for (IndexType k = 0; k < NumRegs; ++k) - acc[k] = vec_sub_16(acc[k], column[k]); - } - - // Difference calculation for the activated features - for (const auto index : added[i]) - { - const IndexType offset = HalfDimensions * index + j * TileHeight; - auto column = reinterpret_cast(&weights[offset]); - for (IndexType k = 0; k < NumRegs; ++k) - acc[k] = vec_add_16(acc[k], column[k]); - } - - // Store accumulator - auto accTileOut = reinterpret_cast( - &(states_to_update[i]->*accPtr).accumulation[Perspective][j * TileHeight]); - for (IndexType k = 0; k < NumRegs; ++k) - vec_store(&accTileOut[k], acc[k]); + const IndexType offset = HalfDimensions * index + i * TileHeight; + auto column = reinterpret_cast(&weights[offset]); + for (IndexType j = 0; j < NumRegs; ++j) + acc[j] = vec_add_16(acc[j], column[j]); } + + // Store accumulator + auto accTileOut = reinterpret_cast( + &(next->*accPtr).accumulation[Perspective][i * TileHeight]); + for (IndexType j = 0; j < NumRegs; ++j) + vec_store(&accTileOut[j], acc[j]); } - for (IndexType j = 0; j < PSQTBuckets / PsqtTileHeight; ++j) + for (IndexType i = 0; i < PSQTBuckets / PsqtTileHeight; ++i) { // Load accumulator auto accTilePsqtIn = reinterpret_cast( - &(st->*accPtr).psqtAccumulation[Perspective][j * PsqtTileHeight]); - for (std::size_t k = 0; k < NumPsqtRegs; ++k) - psqt[k] = vec_load_psqt(&accTilePsqtIn[k]); + &(computed->*accPtr).psqtAccumulation[Perspective][i * PsqtTileHeight]); + for (std::size_t j = 0; j < NumPsqtRegs; ++j) + psqt[j] = vec_load_psqt(&accTilePsqtIn[j]); + + // Difference calculation for the deactivated features + for (const auto index : removed) + { + const IndexType offset = PSQTBuckets * index + i * PsqtTileHeight; + auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); + for (std::size_t j = 0; j < NumPsqtRegs; ++j) + psqt[j] = vec_sub_psqt_32(psqt[j], columnPsqt[j]); + } - for (IndexType i = 0; i < N; ++i) + // Difference calculation for the activated features + for (const auto index : added) { - // Difference calculation for the deactivated features - for (const auto index : removed[i]) - { - const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; - auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); - for (std::size_t k = 0; k < NumPsqtRegs; ++k) - psqt[k] = vec_sub_psqt_32(psqt[k], columnPsqt[k]); - } - - // Difference calculation for the activated features - for (const auto index : added[i]) - { - const IndexType offset = PSQTBuckets * index + j * PsqtTileHeight; - auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); - for (std::size_t k = 0; k < NumPsqtRegs; ++k) - psqt[k] = vec_add_psqt_32(psqt[k], columnPsqt[k]); - } - - // Store accumulator - auto accTilePsqtOut = reinterpret_cast( - &(states_to_update[i]->*accPtr) - .psqtAccumulation[Perspective][j * PsqtTileHeight]); - for (std::size_t k = 0; k < NumPsqtRegs; ++k) - vec_store_psqt(&accTilePsqtOut[k], psqt[k]); + const IndexType offset = PSQTBuckets * index + i * PsqtTileHeight; + auto columnPsqt = reinterpret_cast(&psqtWeights[offset]); + for (std::size_t j = 0; j < NumPsqtRegs; ++j) + psqt[j] = vec_add_psqt_32(psqt[j], columnPsqt[j]); } + + // Store accumulator + auto accTilePsqtOut = reinterpret_cast( + &(next->*accPtr).psqtAccumulation[Perspective][i * PsqtTileHeight]); + for (std::size_t j = 0; j < NumPsqtRegs; ++j) + vec_store_psqt(&accTilePsqtOut[j], psqt[j]); } } #else - for (IndexType i = 0; i < N; ++i) + std::memcpy((next->*accPtr).accumulation[Perspective], + (computed->*accPtr).accumulation[Perspective], + HalfDimensions * sizeof(BiasType)); + std::memcpy((next->*accPtr).psqtAccumulation[Perspective], + (computed->*accPtr).psqtAccumulation[Perspective], + PSQTBuckets * sizeof(PSQTWeightType)); + + // Difference calculation for the deactivated features + for (const auto index : removed) { - std::memcpy((states_to_update[i]->*accPtr).accumulation[Perspective], - (st->*accPtr).accumulation[Perspective], HalfDimensions * sizeof(BiasType)); - - for (std::size_t k = 0; k < PSQTBuckets; ++k) - (states_to_update[i]->*accPtr).psqtAccumulation[Perspective][k] = - (st->*accPtr).psqtAccumulation[Perspective][k]; - - st = states_to_update[i]; - - // Difference calculation for the deactivated features - for (const auto index : removed[i]) - { - const IndexType offset = HalfDimensions * index; - for (IndexType j = 0; j < HalfDimensions; ++j) - (st->*accPtr).accumulation[Perspective][j] -= weights[offset + j]; + const IndexType offset = HalfDimensions * index; + for (IndexType i = 0; i < HalfDimensions; ++i) + (next->*accPtr).accumulation[Perspective][i] -= weights[offset + i]; - for (std::size_t k = 0; k < PSQTBuckets; ++k) - (st->*accPtr).psqtAccumulation[Perspective][k] -= - psqtWeights[index * PSQTBuckets + k]; - } + for (std::size_t i = 0; i < PSQTBuckets; ++i) + (next->*accPtr).psqtAccumulation[Perspective][i] -= + psqtWeights[index * PSQTBuckets + i]; + } - // Difference calculation for the activated features - for (const auto index : added[i]) - { - const IndexType offset = HalfDimensions * index; - for (IndexType j = 0; j < HalfDimensions; ++j) - (st->*accPtr).accumulation[Perspective][j] += weights[offset + j]; + // Difference calculation for the activated features + for (const auto index : added) + { + const IndexType offset = HalfDimensions * index; + for (IndexType i = 0; i < HalfDimensions; ++i) + (next->*accPtr).accumulation[Perspective][i] += weights[offset + i]; - for (std::size_t k = 0; k < PSQTBuckets; ++k) - (st->*accPtr).psqtAccumulation[Perspective][k] += - psqtWeights[index * PSQTBuckets + k]; - } + for (std::size_t i = 0; i < PSQTBuckets; ++i) + (next->*accPtr).psqtAccumulation[Perspective][i] += + psqtWeights[index * PSQTBuckets + i]; } #endif + + (next->*accPtr).computed[Perspective] = true; + + if (!CurrentOnly && next != pos.state()) + update_accumulator_incremental(pos, next); } template @@ -871,14 +838,10 @@ class FeatureTransformer { if ((pos.state()->*accPtr).computed[Perspective]) return; - auto [oldest_st, _] = try_find_computed_accumulator(pos); + StateInfo* oldest = try_find_computed_accumulator(pos); - if ((oldest_st->*accPtr).computed[Perspective]) - { - // Only update current position accumulator to minimize work - StateInfo* states_to_update[1] = {pos.state()}; - update_accumulator_incremental(pos, oldest_st, states_to_update); - } + if ((oldest->*accPtr).computed[Perspective] && oldest != pos.state()) + update_accumulator_incremental(pos, oldest); else update_accumulator_refresh_cache(pos, cache); } @@ -887,31 +850,12 @@ class FeatureTransformer { void update_accumulator(const Position& pos, AccumulatorCaches::Cache* cache) const { - auto [oldest_st, next] = try_find_computed_accumulator(pos); - - if ((oldest_st->*accPtr).computed[Perspective]) - { - if (next == nullptr) - return; - - // Now update the accumulators listed in states_to_update[], where - // the last element is a sentinel. Currently we update two accumulators: - // 1. for the current position - // 2. the next accumulator after the computed one - // The heuristic may change in the future. - if (next == pos.state()) - { - StateInfo* states_to_update[1] = {next}; - - update_accumulator_incremental(pos, oldest_st, states_to_update); - } - else - { - StateInfo* states_to_update[2] = {next, pos.state()}; + StateInfo* oldest = try_find_computed_accumulator(pos); - update_accumulator_incremental(pos, oldest_st, states_to_update); - } - } + if ((oldest->*accPtr).computed[Perspective] && oldest != pos.state()) + // Start from the oldest computed accumulator, update all the + // accumulators up to the current position. + update_accumulator_incremental(pos, oldest); else update_accumulator_refresh_cache(pos, cache); } diff --git a/src/position.cpp b/src/position.cpp index d374b1c070a..df95ffef380 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -671,6 +671,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { // our state pointer to point to the new (ready to be updated) state. std::memcpy(&newSt, st, offsetof(StateInfo, key)); newSt.previous = st; + st->next = &newSt; st = &newSt; // Increment ply counters. In particular, rule50 will be reset to zero later on @@ -963,6 +964,7 @@ void Position::do_null_move(StateInfo& newSt, TranspositionTable& tt) { std::memcpy(&newSt, st, offsetof(StateInfo, accumulatorBig)); newSt.previous = st; + st->next = &newSt; st = &newSt; st->dirtyPiece.dirty_num = 0; diff --git a/src/position.h b/src/position.h index 064dd5fa918..6cac1731951 100644 --- a/src/position.h +++ b/src/position.h @@ -53,6 +53,7 @@ struct StateInfo { Key key; Bitboard checkersBB; StateInfo* previous; + StateInfo* next; Bitboard blockersForKing[COLOR_NB]; Bitboard pinners[COLOR_NB]; Bitboard checkSquares[PIECE_TYPE_NB]; From 6de25872361de9515bdb25bf1d0391311d074012 Mon Sep 17 00:00:00 2001 From: MinetaS Date: Sat, 31 Aug 2024 16:35:17 +0900 Subject: [PATCH 1714/1766] Remove statScore condition in NMP Eliminate the condition that is nearly 100% likelihood of being true. Passed non-regression STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 208832 W: 54053 L: 54022 D: 100757 Ptnml(0-2): 753, 24987, 52901, 25026, 749 https://tests.stockfishchess.org/tests/view/66cddb50bf8c9d8780fdabaf Passed non-regression LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 154344 W: 39132 L: 39047 D: 76165 Ptnml(0-2): 115, 17231, 42403, 17300, 123 https://tests.stockfishchess.org/tests/view/66cfafe39de3e7f9b33d1050 closes https://github.com/official-stockfish/Stockfish/pull/5558 Bench: 1393697 --- src/search.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 1ed849f2a34..d26f43dbc44 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -773,10 +773,9 @@ Value Search::Worker::search( return beta + (eval - beta) / 3; // Step 9. Null move search with verification search (~35 Elo) - if (cutNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 14389 - && eval >= beta && ss->staticEval >= beta - 21 * depth + 390 && !excludedMove - && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly - && beta > VALUE_TB_LOSS_IN_MAX_PLY) + if (cutNode && (ss - 1)->currentMove != Move::null() && eval >= beta + && ss->staticEval >= beta - 21 * depth + 390 && !excludedMove && pos.non_pawn_material(us) + && ss->ply >= thisThread->nmpMinPly && beta > VALUE_TB_LOSS_IN_MAX_PLY) { assert(eval - beta >= 0); From d8e49cdbdd8076d85b137510ee5637e36db1074f Mon Sep 17 00:00:00 2001 From: Nonlinear2 <131959792+Nonlinear2@users.noreply.github.com> Date: Mon, 9 Sep 2024 22:32:00 +0200 Subject: [PATCH 1715/1766] Remove the `moveCount` increase in the LMR condition. Passed non-regression STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 87104 W: 22630 L: 22464 D: 42010 Ptnml(0-2): 316, 10295, 22132, 10525, 284 https://tests.stockfishchess.org/tests/view/66dccd00dc53972b68218c60 Passed non-regression LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 94050 W: 23869 L: 23722 D: 46459 Ptnml(0-2): 49, 10400, 25985, 10537, 54 https://tests.stockfishchess.org/tests/view/66dd69c7dc53972b68218ca5 closes https://github.com/official-stockfish/Stockfish/pull/5582 Bench: 1281840 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index d26f43dbc44..ac0b9c6d9dc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1159,7 +1159,7 @@ Value Search::Worker::search( r -= ss->statScore / 10898; // Step 17. Late moves reduction / extension (LMR, ~117 Elo) - if (depth >= 2 && moveCount > 1 + (rootNode && depth < 10)) + if (depth >= 2 && moveCount > 1) { // In general we want to cap the LMR depth search at newDepth, but when // reduction is negative, we allow this move a limited search extension From f677aee28baedcab4d3110d0a5c414621ed805c4 Mon Sep 17 00:00:00 2001 From: MinetaS Date: Wed, 11 Sep 2024 05:14:01 +0900 Subject: [PATCH 1716/1766] Fix net downloading script The recent commit introduced a bug in the net downloading script that the file is not downloaded correctly and the content is redirected to stdout. closes https://github.com/official-stockfish/Stockfish/pull/5585 No functional change --- scripts/net.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/net.sh b/scripts/net.sh index 168fbad66b0..0bc57a19e30 100755 --- a/scripts/net.sh +++ b/scripts/net.sh @@ -1,7 +1,7 @@ #!/bin/sh -wget_or_curl=$( (command -v wget > /dev/null 2>&1 && echo "wget -q") || \ - (command -v curl > /dev/null 2>&1 && echo "curl -L -s -k")) +wget_or_curl=$( (command -v wget > /dev/null 2>&1 && echo "wget -qO-") || \ + (command -v curl > /dev/null 2>&1 && echo "curl -skL")) if [ -z "$wget_or_curl" ]; then >&2 printf "%s\n" "Neither wget or curl is installed." \ @@ -51,7 +51,7 @@ fetch_network() { "https://tests.stockfishchess.org/api/nn/$_filename" \ "https://github.com/official-stockfish/networks/raw/master/$_filename"; do echo "Downloading from $url ..." - if $wget_or_curl "$url"; then + if $wget_or_curl "$url" > "$_filename"; then if validate_network "$_filename"; then echo "Successfully validated $_filename" else From a06e7004c1a01fb56f5db90295884eaf3b7cd0f6 Mon Sep 17 00:00:00 2001 From: Disservin Date: Tue, 10 Sep 2024 18:36:54 +0200 Subject: [PATCH 1717/1766] Port instrumented testing to python Since an unknown amount of time the instrumented CI has been a bit flawed, explained here https://github.com/official-stockfish/Stockfish/issues/5185. It also experiences random timeout issues where restarting the workflow fixes it or very long run times (more than other workflows) and is not very portable. The intention of this commit is to port the instrumented.sh to python which also works on other operating systems. It should also be relatively easy for beginners to add new tests to assert stockfish's output and to run it. From the source directory the following command can be run. `python3 ../tests/instrumented.py --none ./stockfish` A test runner will go over the test suites and run the test cases. All instrumented tests should have been ported over. The required python version for this is should be 3.7 (untested) + the requests package, testing.py includes some infrastructure code which setups the testing. fixes https://github.com/official-stockfish/Stockfish/issues/5185 closes https://github.com/official-stockfish/Stockfish/pull/5583 No functional change --- .github/workflows/sanitizers.yml | 2 +- .gitignore | 5 + tests/instrumented.py | 520 +++++++++++++++++++++++++++++++ tests/instrumented.sh | 301 ------------------ tests/testing.py | 378 ++++++++++++++++++++++ 5 files changed, 904 insertions(+), 302 deletions(-) create mode 100644 tests/instrumented.py delete mode 100755 tests/instrumented.sh create mode 100644 tests/testing.py diff --git a/.github/workflows/sanitizers.yml b/.github/workflows/sanitizers.yml index 55459292107..946a81cec4a 100644 --- a/.github/workflows/sanitizers.yml +++ b/.github/workflows/sanitizers.yml @@ -75,4 +75,4 @@ jobs: export CXXFLAGS="-O1 -fno-inline" make clean make -j4 ARCH=x86-64-sse41-popcnt ${{ matrix.sanitizers.make_option }} debug=yes optimize=no build > /dev/null - ../tests/instrumented.sh --${{ matrix.sanitizers.instrumented_option }} + python3 ../tests/instrumented.py --${{ matrix.sanitizers.instrumented_option }} ./stockfish diff --git a/.gitignore b/.gitignore index 8981efcaf13..2fc80d48731 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,8 @@ src/-lstdc++.res # Neural network for the NNUE evaluation **/*.nnue +# Files generated by the instrumented tests +tsan.supp +__pycache__/ +tests/syzygy +tests/bench_tmp.epd \ No newline at end of file diff --git a/tests/instrumented.py b/tests/instrumented.py new file mode 100644 index 00000000000..a3747d4e97a --- /dev/null +++ b/tests/instrumented.py @@ -0,0 +1,520 @@ +import argparse +import re +import sys +import subprocess +import pathlib +import os + +from testing import ( + EPD, + TSAN, + Stockfish as Engine, + MiniTestFramework, + OrderedClassMembers, + Valgrind, + Syzygy, +) + +PATH = pathlib.Path(__file__).parent.resolve() +CWD = os.getcwd() + + +def get_prefix(): + if args.valgrind: + return Valgrind.get_valgrind_command() + if args.valgrind_thread: + return Valgrind.get_valgrind_thread_command() + + return [] + + +def get_threads(): + if args.valgrind_thread or args.sanitizer_thread: + return 2 + return 1 + + +def get_path(): + return os.path.abspath(os.path.join(CWD, args.stockfish_path)) + + +def postfix_check(output): + if args.sanitizer_undefined: + for idx, line in enumerate(output): + if "runtime error:" in line: + # print next possible 50 lines + for i in range(50): + debug_idx = idx + i + if debug_idx < len(output): + print(output[debug_idx]) + return False + + if args.sanitizer_thread: + for idx, line in enumerate(output): + if "WARNING: ThreadSanitizer:" in line: + # print next possible 50 lines + for i in range(50): + debug_idx = idx + i + if debug_idx < len(output): + print(output[debug_idx]) + return False + + return True + + +def Stockfish(*args, **kwargs): + return Engine(get_prefix(), get_path(), *args, **kwargs) + + +class TestCLI(metaclass=OrderedClassMembers): + + def beforeAll(self): + pass + + def afterAll(self): + pass + + def beforeEach(self): + self.stockfish = None + + def afterEach(self): + assert postfix_check(self.stockfish.get_output()) == True + self.stockfish.clear_output() + + def test_eval(self): + self.stockfish = Stockfish("eval".split(" "), True) + assert self.stockfish.process.returncode == 0 + + def test_go_nodes_1000(self): + self.stockfish = Stockfish("go nodes 1000".split(" "), True) + assert self.stockfish.process.returncode == 0 + + def test_go_depth_10(self): + self.stockfish = Stockfish("go depth 10".split(" "), True) + assert self.stockfish.process.returncode == 0 + + def test_go_perft_4(self): + self.stockfish = Stockfish("go perft 4".split(" "), True) + assert self.stockfish.process.returncode == 0 + + def test_go_movetime_1000(self): + self.stockfish = Stockfish("go movetime 1000".split(" "), True) + assert self.stockfish.process.returncode == 0 + + def test_go_wtime_8000_btime_8000_winc_500_binc_500(self): + self.stockfish = Stockfish( + "go wtime 8000 btime 8000 winc 500 binc 500".split(" "), + True, + ) + assert self.stockfish.process.returncode == 0 + + def test_go_wtime_1000_btime_1000_winc_0_binc_0(self): + self.stockfish = Stockfish( + "go wtime 1000 btime 1000 winc 0 binc 0".split(" "), + True, + ) + assert self.stockfish.process.returncode == 0 + + def test_go_wtime_1000_btime_1000_winc_0_binc_0_movestogo_5(self): + self.stockfish = Stockfish( + "go wtime 1000 btime 1000 winc 0 binc 0 movestogo 5".split(" "), + True, + ) + assert self.stockfish.process.returncode == 0 + + def test_go_movetime_200(self): + self.stockfish = Stockfish("go movetime 200".split(" "), True) + assert self.stockfish.process.returncode == 0 + + def test_go_nodes_20000_searchmoves_e2e4_d2d4(self): + self.stockfish = Stockfish( + "go nodes 20000 searchmoves e2e4 d2d4".split(" "), True + ) + assert self.stockfish.process.returncode == 0 + + def test_bench_128_threads_8_default_depth(self): + self.stockfish = Stockfish( + f"bench 128 {get_threads()} 8 default depth".split(" "), + True, + ) + assert self.stockfish.process.returncode == 0 + + def test_bench_128_threads_3_bench_tmp_epd_depth(self): + self.stockfish = Stockfish( + f"bench 128 {get_threads()} 3 {os.path.join(PATH,'bench_tmp.epd')} depth".split( + " " + ), + True, + ) + assert self.stockfish.process.returncode == 0 + + def test_d(self): + self.stockfish = Stockfish("d".split(" "), True) + assert self.stockfish.process.returncode == 0 + + def test_compiler(self): + self.stockfish = Stockfish("compiler".split(" "), True) + assert self.stockfish.process.returncode == 0 + + def test_license(self): + self.stockfish = Stockfish("license".split(" "), True) + assert self.stockfish.process.returncode == 0 + + def test_uci(self): + self.stockfish = Stockfish("uci".split(" "), True) + assert self.stockfish.process.returncode == 0 + + def test_export_net_verify_nnue(self): + current_path = os.path.abspath(os.getcwd()) + self.stockfish = Stockfish( + f"export_net {os.path.join(current_path , 'verify.nnue')}".split(" "), True + ) + assert self.stockfish.process.returncode == 0 + + # verify the generated net equals the base net + + def test_network_equals_base(self): + self.stockfish = Stockfish( + ["uci"], + True, + ) + + output = self.stockfish.process.stdout + + # find line + for line in output.split("\n"): + if "option name EvalFile type string default" in line: + network = line.split(" ")[-1] + break + + # find network file in src dir + network = os.path.join(PATH.parent.resolve(), "src", network) + + if not os.path.exists(network): + print( + f"Network file {network} not found, please download the network file over the make command." + ) + assert False + + diff = subprocess.run(["diff", network, f"verify.nnue"]) + + assert diff.returncode == 0 + + +class TestInteractive(metaclass=OrderedClassMembers): + def beforeAll(self): + self.stockfish = Stockfish() + + def afterAll(self): + self.stockfish.quit() + assert self.stockfish.close() == 0 + + def afterEach(self): + assert postfix_check(self.stockfish.get_output()) == True + self.stockfish.clear_output() + + def test_startup_output(self): + self.stockfish.starts_with("Stockfish") + + def test_uci_command(self): + self.stockfish.send_command("uci") + self.stockfish.equals("uciok") + + def test_set_threads_option(self): + self.stockfish.send_command(f"setoption name Threads value {get_threads()}") + + def test_ucinewgame_and_startpos_nodes_1000(self): + self.stockfish.send_command("ucinewgame") + self.stockfish.send_command("position startpos") + self.stockfish.send_command("go nodes 1000") + self.stockfish.starts_with("bestmove") + + def test_ucinewgame_and_startpos_moves(self): + self.stockfish.send_command("ucinewgame") + self.stockfish.send_command("position startpos moves e2e4 e7e6") + self.stockfish.send_command("go nodes 1000") + self.stockfish.starts_with("bestmove") + + def test_fen_position_1(self): + self.stockfish.send_command("ucinewgame") + self.stockfish.send_command("position fen 5rk1/1K4p1/8/8/3B4/8/8/8 b - - 0 1") + self.stockfish.send_command("go nodes 1000") + self.stockfish.starts_with("bestmove") + + def test_fen_position_2_flip(self): + self.stockfish.send_command("ucinewgame") + self.stockfish.send_command("position fen 5rk1/1K4p1/8/8/3B4/8/8/8 b - - 0 1") + self.stockfish.send_command("flip") + self.stockfish.send_command("go nodes 1000") + self.stockfish.starts_with("bestmove") + + def test_depth_5_with_callback(self): + self.stockfish.send_command("ucinewgame") + self.stockfish.send_command("position startpos") + self.stockfish.send_command("go depth 5") + + def callback(output): + regex = r"info depth \d+ seldepth \d+ multipv \d+ score cp \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv" + if output.startswith("info depth") and not re.match(regex, output): + assert False + if output.startswith("bestmove"): + return True + return False + + self.stockfish.check_output(callback) + + def test_ucinewgame_and_go_depth_9(self): + self.stockfish.send_command("ucinewgame") + self.stockfish.send_command("setoption name UCI_ShowWDL value true") + self.stockfish.send_command("position startpos") + self.stockfish.send_command("go depth 9") + + depth = 1 + + def callback(output): + nonlocal depth + + regex = rf"info depth {depth} seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv" + + if output.startswith("info depth"): + if not re.match(regex, output): + assert False + depth += 1 + + if output.startswith("bestmove"): + assert depth == 10 + return True + + return False + + self.stockfish.check_output(callback) + + def test_clear_hash(self): + self.stockfish.send_command("setoption name Clear Hash") + + def test_fen_position_mate_1(self): + self.stockfish.send_command("ucinewgame") + self.stockfish.send_command( + "position fen 5K2/8/2qk4/2nPp3/3r4/6B1/B7/3R4 w - e6" + ) + self.stockfish.send_command("go depth 18") + + self.stockfish.expect("* score mate 1 * pv d5e6") + self.stockfish.equals("bestmove d5e6") + + def test_fen_position_mate_minus_1(self): + self.stockfish.send_command("ucinewgame") + self.stockfish.send_command( + "position fen 2brrb2/8/p7/Q7/1p1kpPp1/1P1pN1K1/3P4/8 b - -" + ) + self.stockfish.send_command("go depth 18") + self.stockfish.expect("* score mate -1 *") + self.stockfish.starts_with("bestmove") + + def test_fen_position_fixed_node(self): + self.stockfish.send_command("ucinewgame") + self.stockfish.send_command( + "position fen 5K2/8/2P1P1Pk/6pP/3p2P1/1P6/3P4/8 w - - 0 1" + ) + self.stockfish.send_command("go nodes 500000") + self.stockfish.starts_with("bestmove") + + def test_fen_position_with_mate_go_depth(self): + self.stockfish.send_command("ucinewgame") + self.stockfish.send_command( + "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - -" + ) + self.stockfish.send_command("go depth 18 searchmoves c6d7") + self.stockfish.expect("* score mate 2 * pv c6d7 * f7f5") + + self.stockfish.starts_with("bestmove") + + def test_fen_position_with_mate_go_mate(self): + self.stockfish.send_command("ucinewgame") + self.stockfish.send_command( + "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - -" + ) + self.stockfish.send_command("go mate 2 searchmoves c6d7") + self.stockfish.expect("* score mate 2 * pv c6d7 *") + + self.stockfish.starts_with("bestmove") + + def test_fen_position_with_mate_go_nodes(self): + self.stockfish.send_command("ucinewgame") + self.stockfish.send_command( + "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - -" + ) + self.stockfish.send_command("go nodes 500000 searchmoves c6d7") + self.stockfish.expect("* score mate 2 * pv c6d7 * f7f5") + + self.stockfish.starts_with("bestmove") + + def test_fen_position_depth_27(self): + self.stockfish.send_command("ucinewgame") + self.stockfish.send_command( + "position fen 1NR2B2/5p2/5p2/1p1kpp2/1P2rp2/2P1pB2/2P1P1K1/8 b - -" + ) + self.stockfish.send_command("go depth 27") + self.stockfish.contains("score mate -2") + + self.stockfish.starts_with("bestmove") + + def test_fen_position_with_mate_go_depth_and_promotion(self): + self.stockfish.send_command("ucinewgame") + self.stockfish.send_command( + "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - - moves c6d7 f2f1q" + ) + self.stockfish.send_command("go depth 18") + self.stockfish.expect("* score mate 1 * pv f7f5") + self.stockfish.starts_with("bestmove f7f5") + + def test_fen_position_with_mate_go_depth_and_searchmoves(self): + self.stockfish.send_command("ucinewgame") + self.stockfish.send_command( + "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - -" + ) + self.stockfish.send_command("go depth 18 searchmoves c6d7") + self.stockfish.expect("* score mate 2 * pv c6d7 * f7f5") + + self.stockfish.starts_with("bestmove c6d7") + + def test_fen_position_with_moves_with_mate_go_depth_and_searchmoves(self): + self.stockfish.send_command("ucinewgame") + self.stockfish.send_command( + "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - - moves c6d7" + ) + self.stockfish.send_command("go depth 18 searchmoves e3e2") + self.stockfish.expect("* score mate -1 * pv e3e2 f7f5") + self.stockfish.starts_with("bestmove e3e2") + + def test_verify_nnue_network(self): + current_path = os.path.abspath(os.getcwd()) + Stockfish( + f"export_net {os.path.join(current_path , 'verify.nnue')}".split(" "), True + ) + + self.stockfish.send_command("setoption name EvalFile value verify.nnue") + self.stockfish.send_command("position startpos") + self.stockfish.send_command("go depth 5") + self.stockfish.starts_with("bestmove") + + def test_multipv_setting(self): + self.stockfish.send_command("setoption name MultiPV value 4") + self.stockfish.send_command("position startpos") + self.stockfish.send_command("go depth 5") + self.stockfish.starts_with("bestmove") + + def test_fen_position_with_skill_level(self): + self.stockfish.send_command("setoption name Skill Level value 10") + self.stockfish.send_command("position startpos") + self.stockfish.send_command("go depth 5") + self.stockfish.starts_with("bestmove") + + self.stockfish.send_command("setoption name Skill Level value 20") + + +class TestSyzygy(metaclass=OrderedClassMembers): + def beforeAll(self): + self.stockfish = Stockfish() + + def afterAll(self): + self.stockfish.quit() + assert self.stockfish.close() == 0 + + def afterEach(self): + assert postfix_check(self.stockfish.get_output()) == True + self.stockfish.clear_output() + + def test_syzygy_setup(self): + self.stockfish.starts_with("Stockfish") + self.stockfish.send_command("uci") + self.stockfish.send_command( + f"setoption name SyzygyPath value {os.path.join(PATH, 'syzygy')}" + ) + self.stockfish.expect( + "info string Found 35 WDL and 35 DTZ tablebase files (up to 4-man)." + ) + + def test_syzygy_bench(self): + self.stockfish.send_command("bench 128 1 8 default depth") + self.stockfish.expect("Nodes searched :*") + + def test_syzygy_position(self): + self.stockfish.send_command("ucinewgame") + self.stockfish.send_command("position fen 4k3/PP6/8/8/8/8/8/4K3 w - - 0 1") + self.stockfish.send_command("go depth 5") + + def check_output(output): + if "score cp 20000" in output or "score mate" in output: + return True + + self.stockfish.check_output(check_output) + self.stockfish.expect("bestmove *") + + def test_syzygy_position_2(self): + self.stockfish.send_command("ucinewgame") + self.stockfish.send_command("position fen 8/1P6/2B5/8/4K3/8/6k1/8 w - - 0 1") + self.stockfish.send_command("go depth 5") + + def check_output(output): + if "score cp 20000" in output or "score mate" in output: + return True + + self.stockfish.check_output(check_output) + self.stockfish.expect("bestmove *") + + def test_syzygy_position_3(self): + self.stockfish.send_command("ucinewgame") + self.stockfish.send_command("position fen 8/1P6/2B5/8/4K3/8/6k1/8 b - - 0 1") + self.stockfish.send_command("go depth 5") + + def check_output(output): + if "score cp -20000" in output or "score mate" in output: + return True + + self.stockfish.check_output(check_output) + self.stockfish.expect("bestmove *") + + +def parse_args(): + parser = argparse.ArgumentParser(description="Run Stockfish with testing options") + parser.add_argument("--valgrind", action="store_true", help="Run valgrind testing") + parser.add_argument( + "--valgrind-thread", action="store_true", help="Run valgrind-thread testing" + ) + parser.add_argument( + "--sanitizer-undefined", + action="store_true", + help="Run sanitizer-undefined testing", + ) + parser.add_argument( + "--sanitizer-thread", action="store_true", help="Run sanitizer-thread testing" + ) + + parser.add_argument( + "--none", action="store_true", help="Run without any testing options" + ) + parser.add_argument("stockfish_path", type=str, help="Path to Stockfish binary") + + return parser.parse_args() + + +if __name__ == "__main__": + args = parse_args() + + EPD.create_bench_epd() + TSAN.set_tsan_option() + Syzygy.download_syzygy() + + framework = MiniTestFramework() + + # Each test suite will be ran inside a temporary directory + framework.run([TestCLI, TestInteractive, TestSyzygy]) + + EPD.delete_bench_epd() + TSAN.unset_tsan_option() + + if framework.has_failed(): + sys.exit(1) + + sys.exit(0) diff --git a/tests/instrumented.sh b/tests/instrumented.sh deleted file mode 100755 index 5fc6ca9a974..00000000000 --- a/tests/instrumented.sh +++ /dev/null @@ -1,301 +0,0 @@ -#!/bin/bash -# check for errors under Valgrind or sanitizers. - -error() -{ - echo "instrumented testing failed on line $1" - exit 1 -} -trap 'error ${LINENO}' ERR - -# define suitable post and prefixes for testing options -case $1 in - --valgrind) - echo "valgrind testing started" - prefix='' - exeprefix='valgrind --error-exitcode=42 --errors-for-leak-kinds=all --leak-check=full' - postfix='' - threads="1" - ;; - --valgrind-thread) - echo "valgrind-thread testing started" - prefix='' - exeprefix='valgrind --fair-sched=try --error-exitcode=42' - postfix='' - threads="2" - ;; - --sanitizer-undefined) - echo "sanitizer-undefined testing started" - prefix='!' - exeprefix='' - postfix='2>&1 | grep -A50 "runtime error:"' - threads="1" - ;; - --sanitizer-thread) - echo "sanitizer-thread testing started" - prefix='!' - exeprefix='' - postfix='2>&1 | grep -A50 "WARNING: ThreadSanitizer:"' - threads="2" - -cat << EOF > tsan.supp -race:Stockfish::TTEntry::read -race:Stockfish::TTEntry::save - -race:Stockfish::TranspositionTable::probe -race:Stockfish::TranspositionTable::hashfull - -EOF - - export TSAN_OPTIONS="suppressions=./tsan.supp" - - ;; - *) - echo "unknown testing started" - prefix='' - exeprefix='' - postfix='' - threads="1" - ;; -esac - -cat << EOF > bench_tmp.epd -Rn6/1rbq1bk1/2p2n1p/2Bp1p2/3Pp1pP/1N2P1P1/2Q1NPB1/6K1 w - - 2 26 -rnbqkb1r/ppp1pp2/5n1p/3p2p1/P2PP3/5P2/1PP3PP/RNBQKBNR w KQkq - 0 3 -3qnrk1/4bp1p/1p2p1pP/p2bN3/1P1P1B2/P2BQ3/5PP1/4R1K1 w - - 9 28 -r4rk1/1b2ppbp/pq4pn/2pp1PB1/1p2P3/1P1P1NN1/1PP3PP/R2Q1RK1 w - - 0 13 -EOF - -# simple command line testing -for args in "eval" \ - "go nodes 1000" \ - "go depth 10" \ - "go perft 4" \ - "go movetime 1000" \ - "go wtime 8000 btime 8000 winc 500 binc 500" \ - "go wtime 1000 btime 1000 winc 0 binc 0" \ - "go wtime 1000 btime 1000 winc 0 binc 0" \ - "go wtime 1000 btime 1000 winc 0 binc 0 movestogo 5" \ - "go movetime 200" \ - "go nodes 20000 searchmoves e2e4 d2d4" \ - "bench 128 $threads 8 default depth" \ - "bench 128 $threads 3 bench_tmp.epd depth" \ - "export_net verify.nnue" \ - "d" \ - "compiler" \ - "license" \ - "uci" -do - - echo "$prefix $exeprefix ./stockfish $args $postfix" - eval "$prefix $exeprefix ./stockfish $args $postfix" - -done - -# verify the generated net equals the base net -network=`./stockfish uci | grep 'option name EvalFile type string default' | awk '{print $NF}'` -echo "Comparing $network to the written verify.nnue" -diff $network verify.nnue - -# more general testing, following an uci protocol exchange -cat << EOF > game.exp - set timeout 240 - # to correctly catch eof we need the following line - # expect_before timeout { exit 2 } eof { exit 3 } - expect_before timeout { exit 2 } - - spawn $exeprefix ./stockfish - expect "Stockfish" - - send "uci\n" - expect "uciok" - - # send "setoption name Debug Log File value debug.log\n" - send "setoption name Threads value $threads\n" - - send "ucinewgame\n" - send "position startpos\n" - send "go nodes 1000\n" - expect "bestmove" - - send "ucinewgame\n" - send "position startpos moves e2e4 e7e6\n" - send "go nodes 1000\n" - expect "bestmove" - - send "ucinewgame\n" - send "position fen 5rk1/1K4p1/8/8/3B4/8/8/8 b - - 0 1\n" - send "go depth 10\n" - expect "bestmove" - - send "ucinewgame\n" - send "position fen 5rk1/1K4p1/8/8/3B4/8/8/8 b - - 0 1\n" - send "flip\n" - send "go depth 10\n" - expect "bestmove" - - send "ucinewgame\n" - send "position startpos\n" - send "go depth 5\n" - expect -re {info depth \d+ seldepth \d+ multipv \d+ score cp \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv} - expect "bestmove" - - send "ucinewgame\n" - send "setoption name UCI_ShowWDL value true\n" - send "position startpos\n" - send "go depth 9\n" - expect -re {info depth 1 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv} - expect -re {info depth 2 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv} - expect -re {info depth 3 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv} - expect -re {info depth 4 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv} - expect -re {info depth 5 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv} - expect -re {info depth 6 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv} - expect -re {info depth 7 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv} - expect -re {info depth 8 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv} - expect -re {info depth 9 seldepth \d+ multipv \d+ score cp \d+ wdl \d+ \d+ \d+ nodes \d+ nps \d+ hashfull \d+ tbhits \d+ time \d+ pv} - expect "bestmove" - - send "setoption name Clear Hash\n" - - send "ucinewgame\n" - send "position fen 5K2/8/2qk4/2nPp3/3r4/6B1/B7/3R4 w - e6\n" - send "go depth 18\n" - expect "score mate 1" - expect "pv d5e6" - expect "bestmove d5e6" - - send "ucinewgame\n" - send "position fen 2brrb2/8/p7/Q7/1p1kpPp1/1P1pN1K1/3P4/8 b - -\n" - send "go depth 18\n" - expect "score mate -1" - expect "bestmove" - - send "ucinewgame\n" - send "position fen 7K/P1p1p1p1/2P1P1Pk/6pP/3p2P1/1P6/3P4/8 w - - 0 1\n" - send "go nodes 500000\n" - expect "bestmove" - - send "ucinewgame\n" - send "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - -\n" - send "go depth 18 searchmoves c6d7\n" - expect "score mate 2 * pv c6d7 * f7f5" - expect "bestmove c6d7" - - send "ucinewgame\n" - send "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - -\n" - send "go mate 2 searchmoves c6d7\n" - expect "score mate 2 * pv c6d7" - expect "bestmove c6d7" - - send "ucinewgame\n" - send "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - -\n" - send "go nodes 500000 searchmoves c6d7\n" - expect "score mate 2 * pv c6d7 * f7f5" - expect "bestmove c6d7" - - send "ucinewgame\n" - send "position fen 1NR2B2/5p2/5p2/1p1kpp2/1P2rp2/2P1pB2/2P1P1K1/8 b - - \n" - send "go depth 27\n" - expect "score mate -2" - expect "pv d5e6 c8d8" - expect "bestmove d5e6" - - send "ucinewgame\n" - send "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - - moves c6d7 f2f1q\n" - send "go depth 18\n" - expect "score mate 1 * pv f7f5" - expect "bestmove f7f5" - - send "ucinewgame\n" - send "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - -\n" - send "go depth 18 searchmoves c6d7\n" - expect "score mate 2 * pv c6d7 * f7f5" - expect "bestmove c6d7" - - send "ucinewgame\n" - send "position fen 8/5R2/2K1P3/4k3/8/b1PPpp1B/5p2/8 w - - moves c6d7\n" - send "go depth 18 searchmoves e3e2\n" - expect "score mate -1 * pv e3e2 f7f5" - expect "bestmove e3e2" - - send "setoption name EvalFile value verify.nnue\n" - send "position startpos\n" - send "go depth 5\n" - expect "bestmove" - - send "setoption name MultiPV value 4\n" - send "position startpos\n" - send "go depth 5\n" - expect "bestmove" - - send "setoption name Skill Level value 10\n" - send "position startpos\n" - send "go depth 5\n" - expect "bestmove" - send "setoption name Skill Level value 20\n" - - send "quit\n" - expect eof - - # return error code of the spawned program, useful for Valgrind - lassign [wait] pid spawnid os_error_flag value - exit \$value -EOF - -#download TB as needed -if [ ! -d ../tests/syzygy ]; then - curl -sL https://api.github.com/repos/niklasf/python-chess/tarball/9b9aa13f9f36d08aadfabff872882f4ab1494e95 | tar -xzf - - mv niklasf-python-chess-9b9aa13 ../tests/syzygy -fi - -cat << EOF > syzygy.exp - set timeout 240 - # to correctly catch eof we need the following line - # expect_before timeout { exit 2 } eof { exit 3 } - expect_before timeout { exit 2 } - spawn $exeprefix ./stockfish - expect "Stockfish" - send "uci\n" - send "setoption name SyzygyPath value ../tests/syzygy/\n" - expect "info string Found 35 WDL and 35 DTZ tablebase files (up to 4-man)." - send "bench 128 1 8 default depth\n" - expect "Nodes searched :" - send "ucinewgame\n" - send "position fen 4k3/PP6/8/8/8/8/8/4K3 w - - 0 1\n" - send "go depth 5\n" - expect -re {score cp 20000|score mate} - expect "bestmove" - send "ucinewgame\n" - send "position fen 8/1P6/2B5/8/4K3/8/6k1/8 w - - 0 1\n" - send "go depth 5\n" - expect -re {score cp 20000|score mate} - expect "bestmove" - send "ucinewgame\n" - send "position fen 8/1P6/2B5/8/4K3/8/6k1/8 b - - 0 1\n" - send "go depth 5\n" - expect -re {score cp -20000|score mate} - expect "bestmove" - send "quit\n" - expect eof - - # return error code of the spawned program, useful for Valgrind - lassign [wait] pid spawnid os_error_flag value - exit \$value -EOF - -for exp in game.exp syzygy.exp -do - - echo "======== $exp ==============" - cat $exp - echo "============================" - echo "$prefix expect $exp $postfix" - eval "$prefix expect $exp $postfix" - - rm $exp - -done - -rm -f tsan.supp bench_tmp.epd - -echo "instrumented testing OK" diff --git a/tests/testing.py b/tests/testing.py new file mode 100644 index 00000000000..d51ca89ac92 --- /dev/null +++ b/tests/testing.py @@ -0,0 +1,378 @@ +import subprocess +from typing import List +import os +import collections +import time +import sys +import traceback +import fnmatch +from functools import wraps +from contextlib import redirect_stdout +import io +import tarfile +import pathlib +import concurrent.futures +import tempfile +import shutil +import requests + +CYAN_COLOR = "\033[36m" +GRAY_COLOR = "\033[2m" +RED_COLOR = "\033[31m" +GREEN_COLOR = "\033[32m" +RESET_COLOR = "\033[0m" +WHITE_BOLD = "\033[1m" + +MAX_TIMEOUT = 60 * 5 + +PATH = pathlib.Path(__file__).parent.resolve() + + +class Valgrind: + @staticmethod + def get_valgrind_command(): + return [ + "valgrind", + "--error-exitcode=42", + "--errors-for-leak-kinds=all", + "--leak-check=full", + ] + + @staticmethod + def get_valgrind_thread_command(): + return ["valgrind", "--error-exitcode=42", "--fair-sched=try"] + + +class TSAN: + @staticmethod + def set_tsan_option(): + with open(f"tsan.supp", "w") as f: + f.write( + """ +race:Stockfish::TTEntry::read +race:Stockfish::TTEntry::save +race:Stockfish::TranspositionTable::probe +race:Stockfish::TranspositionTable::hashfull +""" + ) + + os.environ["TSAN_OPTIONS"] = "suppressions=./tsan.supp" + + @staticmethod + def unset_tsan_option(): + os.environ.pop("TSAN_OPTIONS", None) + os.remove(f"tsan.supp") + + +class EPD: + @staticmethod + def create_bench_epd(): + with open(f"{os.path.join(PATH,'bench_tmp.epd')}", "w") as f: + f.write( + """ +Rn6/1rbq1bk1/2p2n1p/2Bp1p2/3Pp1pP/1N2P1P1/2Q1NPB1/6K1 w - - 2 26 +rnbqkb1r/ppp1pp2/5n1p/3p2p1/P2PP3/5P2/1PP3PP/RNBQKBNR w KQkq - 0 3 +3qnrk1/4bp1p/1p2p1pP/p2bN3/1P1P1B2/P2BQ3/5PP1/4R1K1 w - - 9 28 +r4rk1/1b2ppbp/pq4pn/2pp1PB1/1p2P3/1P1P1NN1/1PP3PP/R2Q1RK1 w - - 0 13 +""" + ) + + @staticmethod + def delete_bench_epd(): + os.remove(f"{os.path.join(PATH,'bench_tmp.epd')}") + + +class Syzygy: + @staticmethod + def get_syzygy_path(): + return os.path.abspath("syzygy") + + @staticmethod + def download_syzygy(): + if not os.path.isdir(os.path.join(PATH, "syzygy")): + url = "https://api.github.com/repos/niklasf/python-chess/tarball/9b9aa13f9f36d08aadfabff872882f4ab1494e95" + file = "niklasf-python-chess-9b9aa13" + + with tempfile.TemporaryDirectory() as tmpdirname: + tarball_path = os.path.join(tmpdirname, f"{file}.tar.gz") + + response = requests.get(url, stream=True) + with open(tarball_path, 'wb') as f: + for chunk in response.iter_content(chunk_size=8192): + f.write(chunk) + + with tarfile.open(tarball_path, "r:gz") as tar: + tar.extractall(tmpdirname) + + shutil.move(os.path.join(tmpdirname, file), os.path.join(PATH, "syzygy")) + +class OrderedClassMembers(type): + @classmethod + def __prepare__(self, name, bases): + return collections.OrderedDict() + + def __new__(self, name, bases, classdict): + classdict["__ordered__"] = [ + key for key in classdict.keys() if key not in ("__module__", "__qualname__") + ] + return type.__new__(self, name, bases, classdict) + + +class TimeoutException(Exception): + def __init__(self, message: str, timeout: int): + self.message = message + self.timeout = timeout + + +def timeout_decorator(timeout: float): + def decorator(func): + @wraps(func) + def wrapper(*args, **kwargs): + with concurrent.futures.ThreadPoolExecutor() as executor: + future = executor.submit(func, *args, **kwargs) + try: + result = future.result(timeout=timeout) + except concurrent.futures.TimeoutError: + raise TimeoutException( + f"Function {func.__name__} timed out after {timeout} seconds", + timeout, + ) + return result + + return wrapper + + return decorator + + +class MiniTestFramework: + def __init__(self): + self.passed_test_suites = 0 + self.failed_test_suites = 0 + self.passed_tests = 0 + self.failed_tests = 0 + + def has_failed(self) -> bool: + return self.failed_test_suites > 0 + + def run(self, classes: List[type]) -> bool: + self.start_time = time.time() + + for test_class in classes: + with tempfile.TemporaryDirectory() as tmpdirname: + original_cwd = os.getcwd() + os.chdir(tmpdirname) + + try: + if self.__run(test_class): + self.failed_test_suites += 1 + else: + self.passed_test_suites += 1 + finally: + os.chdir(original_cwd) + + self.__print_summary(round(time.time() - self.start_time, 2)) + return self.has_failed() + + def __run(self, test_class) -> bool: + test_instance = test_class() + test_name = test_instance.__class__.__name__ + test_methods = [m for m in test_instance.__ordered__ if m.startswith("test_")] + + print(f"\nTest Suite: {test_name}") + + if hasattr(test_instance, "beforeAll"): + test_instance.beforeAll() + + fails = 0 + + for method in test_methods: + fails += self.__run_test_method(test_instance, method) + + if hasattr(test_instance, "afterAll"): + test_instance.afterAll() + + self.failed_tests += fails + + return fails > 0 + + def __run_test_method(self, test_instance, method: str) -> int: + print(f" Running {method}... \r", end="", flush=True) + + buffer = io.StringIO() + fails = 0 + + try: + t0 = time.time() + + with redirect_stdout(buffer): + if hasattr(test_instance, "beforeEach"): + test_instance.beforeEach() + + getattr(test_instance, method)() + + if hasattr(test_instance, "afterEach"): + test_instance.afterEach() + + duration = time.time() - t0 + + self.print_success(f" {method} ({duration * 1000:.2f}ms)") + self.passed_tests += 1 + except Exception as e: + if isinstance(e, TimeoutException): + self.print_failure( + f" {method} (hit execution limit of {e.timeout} seconds)" + ) + + if isinstance(e, AssertionError): + self.__handle_assertion_error(t0, method) + + fails += 1 + finally: + self.__print_buffer_output(buffer) + + return fails + + def __handle_assertion_error(self, start_time, method: str): + duration = time.time() - start_time + self.print_failure(f" {method} ({duration * 1000:.2f}ms)") + traceback_output = "".join(traceback.format_tb(sys.exc_info()[2])) + + colored_traceback = "\n".join( + f" {CYAN_COLOR}{line}{RESET_COLOR}" + for line in traceback_output.splitlines() + ) + + print(colored_traceback) + + def __print_buffer_output(self, buffer: io.StringIO): + output = buffer.getvalue() + if output: + indented_output = "\n".join(f" {line}" for line in output.splitlines()) + print(f" {RED_COLOR}⎯⎯⎯⎯⎯OUTPUT⎯⎯⎯⎯⎯{RESET_COLOR}") + print(f"{GRAY_COLOR}{indented_output}{RESET_COLOR}") + print(f" {RED_COLOR}⎯⎯⎯⎯⎯OUTPUT⎯⎯⎯⎯⎯{RESET_COLOR}") + + def __print_summary(self, duration: float): + print(f"\n{WHITE_BOLD}Test Summary{RESET_COLOR}\n") + print( + f" Test Suites: {GREEN_COLOR}{self.passed_test_suites} passed{RESET_COLOR}, {RED_COLOR}{self.failed_test_suites} failed{RESET_COLOR}, {self.passed_test_suites + self.failed_test_suites} total" + ) + print( + f" Tests: {GREEN_COLOR}{self.passed_tests} passed{RESET_COLOR}, {RED_COLOR}{self.failed_tests} failed{RESET_COLOR}, {self.passed_tests + self.failed_tests} total" + ) + print(f" Time: {duration}s\n") + + def print_failure(self, add: str): + print(f" {RED_COLOR}✗{RESET_COLOR}{add}", flush=True) + + def print_success(self, add: str): + print(f" {GREEN_COLOR}✓{RESET_COLOR}{add}", flush=True) + + +class Stockfish: + def __init__( + self, + prefix: List[str], + path: str, + args: List[str] = [], + cli: bool = False, + ): + self.path = path + self.process = None + self.args = args + self.cli = cli + self.prefix = prefix + self.output = [] + + self.start() + + def start(self): + if self.cli: + self.process = subprocess.run( + self.prefix + [self.path] + self.args, + capture_output=True, + text=True, + ) + + self.process.stdout + + return + + self.process = subprocess.Popen( + self.prefix + [self.path] + self.args, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True, + bufsize=1, + ) + + def setoption(self, name: str, value: str): + self.send_command(f"setoption name {name} value {value}") + + def send_command(self, command: str): + if not self.process: + raise RuntimeError("Stockfish process is not started") + + self.process.stdin.write(command + "\n") + self.process.stdin.flush() + + @timeout_decorator(MAX_TIMEOUT) + def equals(self, expected_output: str): + for line in self.readline(): + if line == expected_output: + return + + @timeout_decorator(MAX_TIMEOUT) + def expect(self, expected_output: str): + for line in self.readline(): + if fnmatch.fnmatch(line, expected_output): + return + + @timeout_decorator(MAX_TIMEOUT) + def contains(self, expected_output: str): + for line in self.readline(): + if expected_output in line: + return + + @timeout_decorator(MAX_TIMEOUT) + def starts_with(self, expected_output: str): + for line in self.readline(): + if line.startswith(expected_output): + return + + @timeout_decorator(MAX_TIMEOUT) + def check_output(self, callback): + if not callback: + raise ValueError("Callback function is required") + + for line in self.readline(): + if callback(line) == True: + return + + def readline(self): + if not self.process: + raise RuntimeError("Stockfish process is not started") + + while True: + line = self.process.stdout.readline().strip() + self.output.append(line) + + yield line + + def clear_output(self): + self.output = [] + + def get_output(self) -> List[str]: + return self.output + + def quit(self): + self.send_command("quit") + + def close(self): + if self.process: + self.process.stdin.close() + self.process.stdout.close() + return self.process.wait() + + return 0 From 224c147bd6211d2481afd25605b07c3fc98d837c Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Tue, 17 Sep 2024 20:44:57 +0200 Subject: [PATCH 1718/1766] VVLTC Search Tune Tuned with 115k games at VVLTC: https://tests.stockfishchess.org/tests/view/66c80e09bf8c9d8780fda62a Passed VVLTC 1st sprt: https://tests.stockfishchess.org/tests/view/66d69ade9de3e7f9b33d14f9 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 54270 W: 13935 L: 13647 D: 26688 Ptnml(0-2): 2, 4907, 17032, 5189, 5 Passed VVLTC 2nd sprt: https://tests.stockfishchess.org/tests/view/66dcf9c1dc53972b68218c84 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 136696 W: 34941 L: 34462 D: 67293 Ptnml(0-2): 8, 12659, 42535, 13138, 8 closes https://github.com/official-stockfish/Stockfish/pull/5592 Bench: 1644273 --- src/search.cpp | 84 +++++++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ac0b9c6d9dc..4f6e75111d1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -67,7 +67,7 @@ namespace { // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) { - Value futilityMult = 122 - 37 * noTtCutNode; + Value futilityMult = 118 - 33 * noTtCutNode; Value improvingDeduction = improving * futilityMult * 2; Value worseningDeduction = oppWorsening * futilityMult / 3; @@ -85,15 +85,15 @@ Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { w.pawnCorrectionHistory[pos.side_to_move()][pawn_structure_index(pos)]; const auto mcv = w.materialCorrectionHistory[pos.side_to_move()][material_index(pos)]; const auto cv = (2 * pcv + mcv) / 3; - v += 66 * cv / 512; + v += 74 * cv / 512; return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } // History and stats update bonus, based on depth -int stat_bonus(Depth d) { return std::min(190 * d - 108, 1596); } +int stat_bonus(Depth d) { return std::min(179 * d - 108, 1598); } // History and stats update malus, based on depth -int stat_malus(Depth d) { return std::min(736 * d - 268, 2044); } +int stat_malus(Depth d) { return std::min(820 * d - 261, 2246); } // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(size_t nodes) { return VALUE_DRAW - 1 + Value(nodes & 0x2); } @@ -299,12 +299,12 @@ void Search::Worker::iterative_deepening() { // Reset aspiration window starting size Value avg = rootMoves[pvIdx].averageScore; - delta = 5 + avg * avg / 13424; + delta = 5 + avg * avg / 11797; alpha = std::max(avg - delta, -VALUE_INFINITE); beta = std::min(avg + delta, VALUE_INFINITE); // Adjust optimism based on root move's averageScore (~4 Elo) - optimism[us] = 125 * avg / (std::abs(avg) + 89); + optimism[us] = 132 * avg / (std::abs(avg) + 89); optimism[~us] = -optimism[us]; // Start with a small aspiration window and, in the case of a fail @@ -488,8 +488,8 @@ void Search::Worker::iterative_deepening() { // Reset histories, usually before a new game void Search::Worker::clear() { mainHistory.fill(0); - captureHistory.fill(-700); - pawnHistory.fill(-1188); + captureHistory.fill(-753); + pawnHistory.fill(-1152); pawnCorrectionHistory.fill(0); materialCorrectionHistory.fill(0); @@ -497,10 +497,10 @@ void Search::Worker::clear() { for (StatsType c : {NoCaptures, Captures}) for (auto& to : continuationHistory[inCheck][c]) for (auto& h : to) - h->fill(-658); + h->fill(-678); for (size_t i = 1; i < reductions.size(); ++i) - reductions[i] = int((18.62 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); + reductions[i] = int((18.43 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); refreshTable.clear(networks[numaAccessToken]); } @@ -737,7 +737,7 @@ Value Search::Worker::search( // Use static evaluation difference to improve quiet move ordering (~9 Elo) if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture) { - int bonus = std::clamp(-10 * int((ss - 1)->staticEval + ss->staticEval), -1664, 1471) + 752; + int bonus = std::clamp(-10 * int((ss - 1)->staticEval + ss->staticEval), -1641, 1423) + 760; thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) thisThread->pawnHistory[pawn_structure_index(pos)][pos.piece_on(prevSq)][prevSq] @@ -755,7 +755,7 @@ Value Search::Worker::search( // Step 7. Razoring (~1 Elo) // If eval is really low, check with qsearch if we can exceed alpha. If the // search suggests we cannot exceed alpha, return a speculative fail low. - if (eval < alpha - 494 - 290 * depth * depth) + if (eval < alpha - 501 - 272 * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha && std::abs(value) < VALUE_TB_WIN_IN_MAX_PLY) @@ -766,7 +766,7 @@ Value Search::Worker::search( // The depth condition is important for mate finding. if (!ss->ttPv && depth < 13 && eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening) - - (ss - 1)->statScore / 260 + - (ss - 1)->statScore / 272 >= beta && eval >= beta && (!ttData.move || ttCapture) && beta > VALUE_TB_LOSS_IN_MAX_PLY && eval < VALUE_TB_WIN_IN_MAX_PLY) @@ -774,13 +774,13 @@ Value Search::Worker::search( // Step 9. Null move search with verification search (~35 Elo) if (cutNode && (ss - 1)->currentMove != Move::null() && eval >= beta - && ss->staticEval >= beta - 21 * depth + 390 && !excludedMove && pos.non_pawn_material(us) + && ss->staticEval >= beta - 23 * depth + 400 && !excludedMove && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly && beta > VALUE_TB_LOSS_IN_MAX_PLY) { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and eval - Depth R = std::min(int(eval - beta) / 202, 6) + depth / 3 + 5; + Depth R = std::min(int(eval - beta) / 209, 6) + depth / 3 + 5; ss->currentMove = Move::null(); ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -829,7 +829,7 @@ Value Search::Worker::search( // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search // returns a value much above beta, we can (almost) safely prune the previous move. - probCutBeta = beta + 184 - 53 * improving; + probCutBeta = beta + 189 - 53 * improving; if (!PvNode && depth > 3 && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY // If value from transposition table is lower than probCutBeta, don't attempt @@ -898,7 +898,7 @@ Value Search::Worker::search( moves_loop: // When in check, search starts here // Step 12. A small Probcut idea (~4 Elo) - probCutBeta = beta + 390; + probCutBeta = beta + 379; if ((ttData.bound & BOUND_LOWER) && ttData.depth >= depth - 4 && ttData.value >= probCutBeta && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY && std::abs(ttData.value) < VALUE_TB_WIN_IN_MAX_PLY) @@ -982,15 +982,15 @@ Value Search::Worker::search( // Futility pruning for captures (~2 Elo) if (!givesCheck && lmrDepth < 7 && !ss->inCheck) { - Value futilityValue = ss->staticEval + 285 + 251 * lmrDepth + Value futilityValue = ss->staticEval + 300 + 238 * lmrDepth + PieceValue[capturedPiece] + captHist / 7; if (futilityValue <= alpha) continue; } // SEE based pruning for captures and checks (~11 Elo) - int seeHist = std::clamp(captHist / 32, -182 * depth, 166 * depth); - if (!pos.see_ge(move, -168 * depth - seeHist)) + int seeHist = std::clamp(captHist / 32, -159 * depth, 160 * depth); + if (!pos.see_ge(move, -167 * depth - seeHist)) continue; } else @@ -1001,15 +1001,15 @@ Value Search::Worker::search( + thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()]; // Continuation history based pruning (~2 Elo) - if (history < -4165 * depth) + if (history < -4071 * depth) continue; history += 2 * thisThread->mainHistory[us][move.from_to()]; - lmrDepth += history / 3853; + lmrDepth += history / 3653; Value futilityValue = - ss->staticEval + (bestValue < ss->staticEval - 51 ? 143 : 52) + 135 * lmrDepth; + ss->staticEval + (bestValue < ss->staticEval - 51 ? 145 : 49) + 144 * lmrDepth; // Futility pruning: parent node (~13 Elo) if (!ss->inCheck && lmrDepth < 12 && futilityValue <= alpha) @@ -1050,7 +1050,7 @@ Value Search::Worker::search( && std::abs(ttData.value) < VALUE_TB_WIN_IN_MAX_PLY && (ttData.bound & BOUND_LOWER) && ttData.depth >= depth - 3) { - Value singularBeta = ttData.value - (54 + 76 * (ss->ttPv && !PvNode)) * depth / 64; + Value singularBeta = ttData.value - (54 + 77 * (ss->ttPv && !PvNode)) * depth / 64; Depth singularDepth = newDepth / 2; ss->excludedMove = move; @@ -1060,13 +1060,13 @@ Value Search::Worker::search( if (value < singularBeta) { - int doubleMargin = 293 * PvNode - 195 * !ttCapture; - int tripleMargin = 107 + 259 * PvNode - 260 * !ttCapture + 98 * ss->ttPv; + int doubleMargin = 262 * PvNode - 204 * !ttCapture; + int tripleMargin = 97 + 266 * PvNode - 255 * !ttCapture + 94 * ss->ttPv; extension = 1 + (value < singularBeta - doubleMargin) + (value < singularBeta - tripleMargin); - depth += ((!PvNode) && (depth < 16)); + depth += ((!PvNode) && (depth < 14)); } // Multi-cut pruning @@ -1099,7 +1099,7 @@ Value Search::Worker::search( else if (PvNode && move.to_sq() == prevSq && thisThread->captureHistory[movedPiece][move.to_sq()] [type_of(pos.piece_on(move.to_sq()))] - > 3994) + > 4299) extension = 1; } @@ -1153,10 +1153,10 @@ Value Search::Worker::search( ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + (*contHist[0])[movedPiece][move.to_sq()] - + (*contHist[1])[movedPiece][move.to_sq()] - 4664; + + (*contHist[1])[movedPiece][move.to_sq()] - 4410; // Decrease/increase reduction for moves with a good/bad history (~8 Elo) - r -= ss->statScore / 10898; + r -= ss->statScore / 11016; // Step 17. Late moves reduction / extension (LMR, ~117 Elo) if (depth >= 2 && moveCount > 1) @@ -1175,7 +1175,7 @@ Value Search::Worker::search( { // Adjust full-depth search based on LMR results - if the result was // good enough search deeper, if it was bad enough search shallower. - const bool doDeeperSearch = value > (bestValue + 35 + 2 * newDepth); // (~1 Elo) + const bool doDeeperSearch = value > (bestValue + 38 + 2 * newDepth); // (~1 Elo) const bool doShallowerSearch = value < bestValue + 8; // (~2 Elo) newDepth += doDeeperSearch - doShallowerSearch; @@ -1344,19 +1344,19 @@ Value Search::Worker::search( // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (122 * (depth > 5) + 39 * !allNode + 165 * ((ss - 1)->moveCount > 8) - + 107 * (!ss->inCheck && bestValue <= ss->staticEval - 98) - + 134 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 91)); + int bonus = (118 * (depth > 5) + 38 * !allNode + 169 * ((ss - 1)->moveCount > 8) + + 116 * (!ss->inCheck && bestValue <= ss->staticEval - 101) + + 133 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 92)); // Proportional to "how much damage we have to undo" - bonus += std::min(-(ss - 1)->statScore / 100, 304); + bonus += std::min(-(ss - 1)->statScore / 102, 305); bonus = std::max(bonus, 0); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, - stat_bonus(depth) * bonus / 116); + stat_bonus(depth) * bonus / 107); thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] - << stat_bonus(depth) * bonus / 180; + << stat_bonus(depth) * bonus / 174; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) @@ -1522,7 +1522,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) if (bestValue > alpha) alpha = bestValue; - futilityBase = ss->staticEval + 299; + futilityBase = ss->staticEval + 280; } const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, @@ -1593,11 +1593,11 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) + (*contHist[1])[pos.moved_piece(move)][move.to_sq()] + thisThread->pawnHistory[pawn_structure_index(pos)][pos.moved_piece(move)] [move.to_sq()] - <= 4643) + <= 5036) continue; // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, -83)) + if (!pos.see_ge(move, -82)) continue; } @@ -1663,7 +1663,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) Depth Search::Worker::reduction(bool i, Depth d, int mn, int delta) const { int reductionScale = reductions[d] * reductions[mn]; - return (reductionScale + 1274 - delta * 746 / rootDelta) / 1024 + (!i && reductionScale > 1293); + return (reductionScale + 1239 - delta * 795 / rootDelta) / 1024 + (!i && reductionScale > 1341); } // elapsed() returns the time elapsed since the search started. If the @@ -1794,7 +1794,7 @@ void update_all_stats(const Position& pos, // at ply -1, -2, -3, -4, and -6 with current move. void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { - bonus = bonus * 52 / 64; + bonus = bonus * 53 / 64; for (int i : {1, 2, 3, 4, 6}) { From 5ce7f866a57264c38cf308152208deadc65508c8 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sat, 7 Sep 2024 15:04:28 -0700 Subject: [PATCH 1719/1766] Simplify Fail Low Bonus Passed Non-regression STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 302528 W: 78190 L: 78264 D: 146074 Ptnml(0-2): 1029, 35797, 77551, 35993, 894 https://tests.stockfishchess.org/tests/view/66dcebdedc53972b68218c7e Passed Non-regression LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 122754 W: 31025 L: 30907 D: 60822 Ptnml(0-2): 74, 13597, 33908, 13733, 65 https://tests.stockfishchess.org/tests/view/66e0c38686d5ee47d953a481 closes https://github.com/official-stockfish/Stockfish/pull/5594 Bench: 1646373 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 4f6e75111d1..135db0cee67 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1365,7 +1365,7 @@ Value Search::Worker::search( } // Bonus when search fails low and there is a TT move - else if (moveCount > 1 && ttData.move && !allNode) + else if (ttData.move && !allNode) thisThread->mainHistory[us][ttData.move.from_to()] << stat_bonus(depth) / 4; if (PvNode) From 240a5b1c72af0c9fa7b2dd13d17cdef61415b4e6 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 14 Sep 2024 08:22:32 +0300 Subject: [PATCH 1720/1766] Introduce separate butterfly history table for sorting root moves Idea of this patch comes from the fact that current history heuristics are mostly populated by low depth entries since our stat bonus reaches maximum value at depth 5-6 and number of low depth nodes is much bigger than number of high depth nodes. But it doesn't make a whole lost of sense to use this low-depth centered histories to sort moves at root. Current patch introduces special history table that is used exclusively at root, it remembers which quiet moves were good and which quiet moves were not good there and uses this information for move ordering. Passed STC: https://tests.stockfishchess.org/tests/view/66dda74adc53972b68218cc9 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 127680 W: 33579 L: 33126 D: 60975 Ptnml(0-2): 422, 15098, 32391, 15463, 466 Passed LTC: https://tests.stockfishchess.org/tests/view/66dead2adc53972b68218d34 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 381978 W: 96958 L: 95923 D: 189097 Ptnml(0-2): 277, 42165, 105089, 43162, 296 closes https://github.com/official-stockfish/Stockfish/pull/5595 Bench: 1611283 --- src/movepick.cpp | 11 +++++++-- src/movepick.h | 6 ++++- src/search.cpp | 59 +++++++++++++++++++++++++++++++----------------- src/search.h | 1 + 4 files changed, 53 insertions(+), 24 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index bdc0e4affdb..63d9e8b1ace 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -82,16 +82,20 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, + const ButterflyHistory* rh, const CapturePieceToHistory* cph, const PieceToHistory** ch, - const PawnHistory* ph) : + const PawnHistory* ph, + bool rn) : pos(p), mainHistory(mh), + rootHistory(rh), captureHistory(cph), continuationHistory(ch), pawnHistory(ph), ttMove(ttm), - depth(d) { + depth(d), + rootNode(rn) { if (pos.checkers()) stage = EVASION_TT + !(ttm && pos.pseudo_legal(ttm)); @@ -174,6 +178,9 @@ void MovePicker::score() { m.value -= (pt == QUEEN ? bool(to & threatenedByRook) * 49000 : pt == ROOK ? bool(to & threatenedByMinor) * 24335 : bool(to & threatenedByPawn) * 14900); + + if (rootNode) + m.value += 4 * (*rootHistory)[pos.side_to_move()][m.from_to()]; } else // Type == EVASIONS diff --git a/src/movepick.h b/src/movepick.h index 651091b0829..f66cdadf5bb 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -171,9 +171,11 @@ class MovePicker { Move, Depth, const ButterflyHistory*, + const ButterflyHistory*, const CapturePieceToHistory*, const PieceToHistory**, - const PawnHistory*); + const PawnHistory*, + bool); MovePicker(const Position&, Move, int, const CapturePieceToHistory*); Move next_move(bool skipQuiets = false); @@ -187,6 +189,7 @@ class MovePicker { const Position& pos; const ButterflyHistory* mainHistory; + const ButterflyHistory* rootHistory; const CapturePieceToHistory* captureHistory; const PieceToHistory** continuationHistory; const PawnHistory* pawnHistory; @@ -195,6 +198,7 @@ class MovePicker { int stage; int threshold; Depth depth; + bool rootNode; ExtMove moves[MAX_MOVES]; }; diff --git a/src/search.cpp b/src/search.cpp index 135db0cee67..3c6da163b89 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -101,16 +101,21 @@ Value value_to_tt(Value v, int ply); Value value_from_tt(Value v, int ply, int r50c); void update_pv(Move* pv, Move move, const Move* childPv); void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus); -void update_quiet_histories( - const Position& pos, Stack* ss, Search::Worker& workerThread, Move move, int bonus); -void update_all_stats(const Position& pos, - Stack* ss, - Search::Worker& workerThread, - Move bestMove, - Square prevSq, - ValueList& quietsSearched, - ValueList& capturesSearched, - Depth depth); +void update_quiet_histories(const Position& pos, + Stack* ss, + Search::Worker& workerThread, + Move move, + int bonus, + bool rootNode); +void update_all_stats(const Position& pos, + Stack* ss, + Search::Worker& workerThread, + Move bestMove, + Square prevSq, + ValueList& quietsSearched, + ValueList& capturesSearched, + Depth depth, + bool rootNode); } // namespace @@ -264,6 +269,8 @@ void Search::Worker::iterative_deepening() { int searchAgainCounter = 0; + rootHistory.fill(0); + // Iterative deepening loop until requested to stop or the target depth is reached while (++rootDepth < MAX_PLY && !threads.stop && !(limits.depth && mainThread && rootDepth > limits.depth)) @@ -488,6 +495,7 @@ void Search::Worker::iterative_deepening() { // Reset histories, usually before a new game void Search::Worker::clear() { mainHistory.fill(0); + rootHistory.fill(0); captureHistory.fill(-753); pawnHistory.fill(-1152); pawnCorrectionHistory.fill(0); @@ -622,7 +630,7 @@ Value Search::Worker::search( { // Bonus for a quiet ttMove that fails high (~2 Elo) if (!ttCapture) - update_quiet_histories(pos, ss, *this, ttData.move, stat_bonus(depth)); + update_quiet_histories(pos, ss, *this, ttData.move, stat_bonus(depth), rootNode); // Extra penalty for early quiet moves of // the previous ply (~1 Elo on STC, ~2 Elo on LTC) @@ -912,8 +920,8 @@ Value Search::Worker::search( (ss - 6)->continuationHistory}; - MovePicker mp(pos, ttData.move, depth, &thisThread->mainHistory, &thisThread->captureHistory, - contHist, &thisThread->pawnHistory); + MovePicker mp(pos, ttData.move, depth, &thisThread->mainHistory, &thisThread->rootHistory, + &thisThread->captureHistory, contHist, &thisThread->pawnHistory, rootNode); value = bestValue; @@ -1339,7 +1347,8 @@ Value Search::Worker::search( // If there is a move that produces search value greater than alpha, // we update the stats of searched moves. else if (bestMove) - update_all_stats(pos, ss, *this, bestMove, prevSq, quietsSearched, capturesSearched, depth); + update_all_stats(pos, ss, *this, bestMove, prevSq, quietsSearched, capturesSearched, depth, + rootNode); // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) @@ -1533,8 +1542,9 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) // Initialize a MovePicker object for the current position, and prepare to search // the moves. We presently use two stages of move generator in quiescence search: // captures, or evasions only when in check. - MovePicker mp(pos, ttData.move, DEPTH_QS, &thisThread->mainHistory, &thisThread->captureHistory, - contHist, &thisThread->pawnHistory); + MovePicker mp(pos, ttData.move, DEPTH_QS, &thisThread->mainHistory, &thisThread->rootHistory, + &thisThread->captureHistory, contHist, &thisThread->pawnHistory, + nodeType == Root); // Step 5. Loop through all pseudo-legal moves until no moves remain or a beta // cutoff occurs. @@ -1751,7 +1761,8 @@ void update_all_stats(const Position& pos, Square prevSq, ValueList& quietsSearched, ValueList& capturesSearched, - Depth depth) { + Depth depth, + bool rootNode) { CapturePieceToHistory& captureHistory = workerThread.captureHistory; Piece moved_piece = pos.moved_piece(bestMove); @@ -1762,11 +1773,11 @@ void update_all_stats(const Position& pos, if (!pos.capture_stage(bestMove)) { - update_quiet_histories(pos, ss, workerThread, bestMove, quietMoveBonus); + update_quiet_histories(pos, ss, workerThread, bestMove, quietMoveBonus, rootNode); // Decrease stats for all non-best quiet moves for (Move move : quietsSearched) - update_quiet_histories(pos, ss, workerThread, move, -quietMoveMalus); + update_quiet_histories(pos, ss, workerThread, move, -quietMoveMalus, rootNode); } else { @@ -1808,11 +1819,17 @@ void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { // Updates move sorting heuristics -void update_quiet_histories( - const Position& pos, Stack* ss, Search::Worker& workerThread, Move move, int bonus) { +void update_quiet_histories(const Position& pos, + Stack* ss, + Search::Worker& workerThread, + Move move, + int bonus, + bool rootNode) { Color us = pos.side_to_move(); workerThread.mainHistory[us][move.from_to()] << bonus; + if (rootNode) + workerThread.rootHistory[us][move.from_to()] << bonus; update_continuation_histories(ss, pos.moved_piece(move), move.to_sq(), bonus); diff --git a/src/search.h b/src/search.h index c9fe9e184ac..b06c7c9484b 100644 --- a/src/search.h +++ b/src/search.h @@ -278,6 +278,7 @@ class Worker { // Public because they need to be updatable by the stats ButterflyHistory mainHistory; + ButterflyHistory rootHistory; CapturePieceToHistory captureHistory; ContinuationHistory continuationHistory[2][2]; PawnHistory pawnHistory; From 60351b9df901ff5278f208a9cf3a40059ff54832 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Thu, 12 Sep 2024 15:53:15 -0700 Subject: [PATCH 1721/1766] Introduce Various Correction histories This patch introduces three additional correction histories, namely, Major Piece Correction History, Minor Piece Correction History, and Non-Pawn Correction History. Introduced by @mcthouacbb in Sirius (https://github.com/mcthouacbb/Sirius) chess engine. The Major Piece Correction History is indexed by side-to-move and the Zobrist key representing the position of the King, Rook, and Queen of both sides. Likewise, the Minor Piece Correction History is indexed by side-to-move and the Zobrist key representing the position of the King, Knight, and Bishop of both sides. Also See: https://github.com/mcthouacbb/Sirius/commit/97b85bbaac88ff5a0f63e28776027dd3de77164e https://github.com/mcthouacbb/Sirius/commit/3099cdef2f13e29805654b5f8153e6ecd5853195 Introduced by @zzzzz151 in Starzix (https://github.com/zzzzz151/Starzix) chess engine. Non-Pawn correction history consists of side-to-move, side of Zobrist key, and a Zobrist key representing of the position of all non-pawn pieces of **one side**. The non-pawn correction values for both key sides are then summed. Also See: https://github.com/zzzzz151/Starzix/commit/34911772f178c27b3a239dda0acb79c397c3a2f0 https://github.com/zzzzz151/Starzix/commit/33e0df8dd2db1d4775974ab12e3390154697f47a The weights on the final correction value of the above correction histories, as well as existing correction histories, are then tuned in two separate SPSA sessions, totaling 75k games. SPSA1: https://tests.stockfishchess.org/tests/view/66e5243886d5ee47d953a86b (Stopped early due to some weights reaching the maximum value) SPSA2: https://tests.stockfishchess.org/tests/view/66e6a26f86d5ee47d953a965 Also thanks to @martinnovaak, (Motor https://github.com/martinnovaak/motor author) for insights and suggestions. Passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 23328 W: 6197 L: 5901 D: 11230 Ptnml(0-2): 82, 2582, 6041, 2876, 83 https://tests.stockfishchess.org/tests/view/66e8787b86d5ee47d953ab6f Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 10626 W: 2826 L: 2560 D: 5240 Ptnml(0-2): 4, 1054, 2941, 1300, 14 https://tests.stockfishchess.org/tests/view/66e8ab2386d5ee47d953aba8 closes https://github.com/official-stockfish/Stockfish/pull/5598 Bench: 1011161 --- src/bitboard.cpp | 4 +-- src/movepick.h | 40 ++++++++++++++++++++++++----- src/position.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++++---- src/position.h | 12 +++++++++ src/search.cpp | 24 +++++++++++++----- src/search.h | 19 +++++++++----- tests/perft.sh | 2 +- 7 files changed, 140 insertions(+), 27 deletions(-) diff --git a/src/bitboard.cpp b/src/bitboard.cpp index c842ca1271e..a8b4e5f4464 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -140,8 +140,8 @@ Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) { // Computes all rook and bishop attacks at startup. Magic // bitboards are used to look up attacks of sliding pieces. As a reference see -// www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so -// called "fancy" approach. +// https://www.chessprogramming.org/Magic_Bitboards. In particular, here we use +// the so called "fancy" approach. void init_magics(PieceType pt, Bitboard table[], Magic magics[]) { // Optimal PRNG seeds to pick the correct magics in the shortest time diff --git a/src/movepick.h b/src/movepick.h index f66cdadf5bb..13b9635bb2a 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -34,10 +34,13 @@ namespace Stockfish { -constexpr int PAWN_HISTORY_SIZE = 512; // has to be a power of 2 -constexpr int PAWN_CORRECTION_HISTORY_SIZE = 16384; // has to be a power of 2 -constexpr int MATERIAL_CORRECTION_HISTORY_SIZE = 32768; // has to be a power of 2 -constexpr int CORRECTION_HISTORY_LIMIT = 1024; +constexpr int PAWN_HISTORY_SIZE = 512; // has to be a power of 2 +constexpr int PAWN_CORRECTION_HISTORY_SIZE = 16384; // has to be a power of 2 +constexpr int MATERIAL_CORRECTION_HISTORY_SIZE = 32768; // has to be a power of 2 +constexpr int MAJOR_PIECE_CORRECTION_HISTORY_SIZE = 32768; // has to be a power of 2 +constexpr int MINOR_PIECE_CORRECTION_HISTORY_SIZE = 32768; // has to be a power of 2 +constexpr int NON_PAWN_CORRECTION_HISTORY_SIZE = 32768; // has to be a power of 2 +constexpr int CORRECTION_HISTORY_LIMIT = 1024; static_assert((PAWN_HISTORY_SIZE & (PAWN_HISTORY_SIZE - 1)) == 0, "PAWN_HISTORY_SIZE has to be a power of 2"); @@ -59,6 +62,19 @@ inline int material_index(const Position& pos) { return pos.material_key() & (MATERIAL_CORRECTION_HISTORY_SIZE - 1); } +inline int major_piece_index(const Position& pos) { + return pos.major_piece_key() & (MAJOR_PIECE_CORRECTION_HISTORY_SIZE - 1); +} + +inline int minor_piece_index(const Position& pos) { + return pos.minor_piece_key() & (MINOR_PIECE_CORRECTION_HISTORY_SIZE - 1); +} + +template +inline int non_pawn_index(const Position& pos) { + return pos.non_pawn_key(c) & (NON_PAWN_CORRECTION_HISTORY_SIZE - 1); +} + // StatsEntry stores the stat table value. It is usually a number but could // be a move or even a nested history. We use a class instead of a naked value // to directly call history update operator<<() on the entry so to use stats @@ -120,7 +136,7 @@ enum StatsType { // ButterflyHistory records how often quiet moves have been successful or unsuccessful // during the current search, and is used for reduction and move ordering decisions. // It uses 2 tables (one for each color) indexed by the move's from and to squares, -// see www.chessprogramming.org/Butterfly_Boards (~11 elo) +// see https://www.chessprogramming.org/Butterfly_Boards (~11 elo) using ButterflyHistory = Stats; // CapturePieceToHistory is addressed by a move's [piece][to][captured piece type] @@ -138,10 +154,10 @@ using ContinuationHistory = Stats // PawnHistory is addressed by the pawn structure and a move's [piece][to] using PawnHistory = Stats; - // Correction histories record differences between the static evaluation of // positions and their search score. It is used to improve the static evaluation // used by some search heuristics. +// see https://www.chessprogramming.org/Static_Evaluation_Correction_History // PawnCorrectionHistory is addressed by color and pawn structure using PawnCorrectionHistory = @@ -151,6 +167,18 @@ using PawnCorrectionHistory = using MaterialCorrectionHistory = Stats; +// MajorPieceCorrectionHistory is addressed by color and king/major piece (Queen, Rook) positions +using MajorPieceCorrectionHistory = + Stats; + +// MinorPieceCorrectionHistory is addressed by color and king/minor piece (Knight, Bishop) positions +using MinorPieceCorrectionHistory = + Stats; + +// NonPawnCorrectionHistory is addressed by color and non-pawn material positions +using NonPawnCorrectionHistory = + Stats; + // The MovePicker class is used to pick one pseudo-legal move at a time from the // current position. The most important method is next_move(), which emits one // new pseudo-legal move on every call, until there are no moves left, when diff --git a/src/position.cpp b/src/position.cpp index df95ffef380..f596b015355 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -334,8 +334,10 @@ void Position::set_check_info() const { // The function is only used when a new position is set up void Position::set_state() const { - st->key = st->materialKey = 0; - st->pawnKey = Zobrist::noPawns; + st->key = st->materialKey = 0; + st->majorPieceKey = st->minorPieceKey = 0; + st->nonPawnKey[WHITE] = st->nonPawnKey[BLACK] = 0; + st->pawnKey = Zobrist::noPawns; st->nonPawnMaterial[WHITE] = st->nonPawnMaterial[BLACK] = VALUE_ZERO; st->checkersBB = attackers_to(square(sideToMove)) & pieces(~sideToMove); @@ -350,8 +352,27 @@ void Position::set_state() const { if (type_of(pc) == PAWN) st->pawnKey ^= Zobrist::psq[pc][s]; - else if (type_of(pc) != KING) - st->nonPawnMaterial[color_of(pc)] += PieceValue[pc]; + else + { + st->nonPawnKey[color_of(pc)] ^= Zobrist::psq[pc][s]; + + if (type_of(pc) != KING) + { + st->nonPawnMaterial[color_of(pc)] += PieceValue[pc]; + + if (type_of(pc) == QUEEN || type_of(pc) == ROOK) + st->majorPieceKey ^= Zobrist::psq[pc][s]; + + else + st->minorPieceKey ^= Zobrist::psq[pc][s]; + } + + else + { + st->majorPieceKey ^= Zobrist::psq[pc][s]; + st->minorPieceKey ^= Zobrist::psq[pc][s]; + } + } } if (st->epSquare != SQ_NONE) @@ -707,6 +728,8 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { do_castling(us, from, to, rfrom, rto); k ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto]; + st->majorPieceKey ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto]; + st->nonPawnKey[us] ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto]; captured = NO_PIECE; } @@ -732,7 +755,16 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { st->pawnKey ^= Zobrist::psq[captured][capsq]; } else + { st->nonPawnMaterial[them] -= PieceValue[captured]; + st->nonPawnKey[them] ^= Zobrist::psq[captured][capsq]; + + if (type_of(pc) == QUEEN || type_of(pc) == ROOK) + st->majorPieceKey ^= Zobrist::psq[captured][capsq]; + + else + st->minorPieceKey ^= Zobrist::psq[captured][capsq]; + } dp.dirty_num = 2; // 1 piece moved, 1 piece captured dp.piece[1] = captured; @@ -790,7 +822,8 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { else if (m.type_of() == PROMOTION) { - Piece promotion = make_piece(us, m.promotion_type()); + Piece promotion = make_piece(us, m.promotion_type()); + PieceType promotionType = type_of(promotion); assert(relative_rank(us, to) == RANK_8); assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN); @@ -811,6 +844,12 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { st->materialKey ^= Zobrist::psq[promotion][pieceCount[promotion] - 1] ^ Zobrist::psq[pc][pieceCount[pc]]; + if (promotionType == QUEEN || promotionType == ROOK) + st->majorPieceKey ^= Zobrist::psq[promotion][to]; + + else + st->minorPieceKey ^= Zobrist::psq[promotion][to]; + // Update material st->nonPawnMaterial[us] += PieceValue[promotion]; } @@ -822,6 +861,23 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { st->rule50 = 0; } + else + { + st->nonPawnKey[us] ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; + + if (type_of(pc) == KING) + { + st->majorPieceKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; + st->minorPieceKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; + } + + else if (type_of(pc) == QUEEN || type_of(pc) == ROOK) + st->majorPieceKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; + + else + st->minorPieceKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to]; + } + // Set capture piece st->capturedPiece = captured; diff --git a/src/position.h b/src/position.h index 6cac1731951..888612da78d 100644 --- a/src/position.h +++ b/src/position.h @@ -43,6 +43,9 @@ struct StateInfo { // Copied when making a move Key materialKey; Key pawnKey; + Key majorPieceKey; + Key minorPieceKey; + Key nonPawnKey[COLOR_NB]; Value nonPawnMaterial[COLOR_NB]; int castlingRights; int rule50; @@ -151,6 +154,9 @@ class Position { Key key_after(Move m) const; Key material_key() const; Key pawn_key() const; + Key major_piece_key() const; + Key minor_piece_key() const; + Key non_pawn_key(Color c) const; // Other properties of the position Color side_to_move() const; @@ -298,6 +304,12 @@ inline Key Position::pawn_key() const { return st->pawnKey; } inline Key Position::material_key() const { return st->materialKey; } +inline Key Position::major_piece_key() const { return st->majorPieceKey; } + +inline Key Position::minor_piece_key() const { return st->minorPieceKey; } + +inline Key Position::non_pawn_key(Color c) const { return st->nonPawnKey[c]; } + inline Value Position::non_pawn_material(Color c) const { return st->nonPawnMaterial[c]; } inline Value Position::non_pawn_material() const { diff --git a/src/search.cpp b/src/search.cpp index 3c6da163b89..199b9355403 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -46,7 +46,6 @@ #include "thread.h" #include "timeman.h" #include "tt.h" -#include "types.h" #include "uci.h" #include "ucioption.h" @@ -81,11 +80,16 @@ constexpr int futility_move_count(bool improving, Depth depth) { // Add correctionHistory value to raw staticEval and guarantee evaluation // does not hit the tablebase range. Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { - const auto pcv = - w.pawnCorrectionHistory[pos.side_to_move()][pawn_structure_index(pos)]; - const auto mcv = w.materialCorrectionHistory[pos.side_to_move()][material_index(pos)]; - const auto cv = (2 * pcv + mcv) / 3; - v += 74 * cv / 512; + const Color us = pos.side_to_move(); + const auto pcv = w.pawnCorrectionHistory[us][pawn_structure_index(pos)]; + const auto mcv = w.materialCorrectionHistory[us][material_index(pos)]; + const auto macv = w.majorPieceCorrectionHistory[us][major_piece_index(pos)]; + const auto micv = w.minorPieceCorrectionHistory[us][minor_piece_index(pos)]; + const auto wnpcv = w.nonPawnCorrectionHistory[WHITE][us][non_pawn_index(pos)]; + const auto bnpcv = w.nonPawnCorrectionHistory[BLACK][us][non_pawn_index(pos)]; + const auto cv = + (98198 * pcv + 68968 * mcv + 54353 * macv + 85174 * micv + 85581 * (wnpcv + bnpcv)) / 2097152; + v += cv; return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } @@ -500,6 +504,10 @@ void Search::Worker::clear() { pawnHistory.fill(-1152); pawnCorrectionHistory.fill(0); materialCorrectionHistory.fill(0); + majorPieceCorrectionHistory.fill(0); + minorPieceCorrectionHistory.fill(0); + nonPawnCorrectionHistory[WHITE].fill(0); + nonPawnCorrectionHistory[BLACK].fill(0); for (bool inCheck : {false, true}) for (StatsType c : {NoCaptures, Captures}) @@ -1403,6 +1411,10 @@ Value Search::Worker::search( -CORRECTION_HISTORY_LIMIT / 4, CORRECTION_HISTORY_LIMIT / 4); thisThread->pawnCorrectionHistory[us][pawn_structure_index(pos)] << bonus; thisThread->materialCorrectionHistory[us][material_index(pos)] << bonus; + thisThread->majorPieceCorrectionHistory[us][major_piece_index(pos)] << bonus; + thisThread->minorPieceCorrectionHistory[us][minor_piece_index(pos)] << bonus; + thisThread->nonPawnCorrectionHistory[WHITE][us][non_pawn_index(pos)] << bonus; + thisThread->nonPawnCorrectionHistory[BLACK][us][non_pawn_index(pos)] << bonus; } assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); diff --git a/src/search.h b/src/search.h index b06c7c9484b..d7a909a82a8 100644 --- a/src/search.h +++ b/src/search.h @@ -277,13 +277,18 @@ class Worker { void ensure_network_replicated(); // Public because they need to be updatable by the stats - ButterflyHistory mainHistory; - ButterflyHistory rootHistory; - CapturePieceToHistory captureHistory; - ContinuationHistory continuationHistory[2][2]; - PawnHistory pawnHistory; - PawnCorrectionHistory pawnCorrectionHistory; - MaterialCorrectionHistory materialCorrectionHistory; + ButterflyHistory mainHistory; + ButterflyHistory rootHistory; + + CapturePieceToHistory captureHistory; + ContinuationHistory continuationHistory[2][2]; + PawnHistory pawnHistory; + + PawnCorrectionHistory pawnCorrectionHistory; + MaterialCorrectionHistory materialCorrectionHistory; + MajorPieceCorrectionHistory majorPieceCorrectionHistory; + MinorPieceCorrectionHistory minorPieceCorrectionHistory; + NonPawnCorrectionHistory nonPawnCorrectionHistory[COLOR_NB]; private: void iterative_deepening(); diff --git a/tests/perft.sh b/tests/perft.sh index 545e750fec0..c1532c20c19 100755 --- a/tests/perft.sh +++ b/tests/perft.sh @@ -1,5 +1,5 @@ #!/bin/bash -# verify perft numbers (positions from www.chessprogramming.org/Perft_Results) +# verify perft numbers (positions from https://www.chessprogramming.org/Perft_Results) error() { From 93869d5d0aab2f7121bdf227def3a942c9fcde17 Mon Sep 17 00:00:00 2001 From: Wencey Wang Date: Thu, 19 Sep 2024 16:30:28 +0800 Subject: [PATCH 1722/1766] Fix native arch builds on loongarch64 Adds support for LSX and LASX closes https://github.com/official-stockfish/Stockfish/pull/5600 No functional change --- AUTHORS | 1 + scripts/get_native_properties.sh | 15 ++++++++++++ src/Makefile | 42 +++++++++++++++++++++++++++++--- 3 files changed, 54 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index 3201e7a8afe..c0a8beebc45 100644 --- a/AUTHORS +++ b/AUTHORS @@ -237,6 +237,7 @@ Unai Corzo (unaiic) Uri Blass (uriblass) Vince Negri (cuddlestmonkey) Viren +Wencey Wang windfishballad xefoci7612 Xiang Wang (KatyushaScarlet) diff --git a/scripts/get_native_properties.sh b/scripts/get_native_properties.sh index fb124021a31..dfbfac0eab6 100755 --- a/scripts/get_native_properties.sh +++ b/scripts/get_native_properties.sh @@ -26,6 +26,17 @@ check_znver_1_2() { [ "$vendor_id" = "AuthenticAMD" ] && [ "$cpu_family" = "23" ] && znver_1_2=true } +# Set the file CPU loongarch64 architecture +set_arch_loongarch64() { + if check_flags 'lasx'; then + true_arch='loongarch64-lasx' + elif check_flags 'lsx'; then + true_arch='lonngarch64-lsx' + else + true_arch='loongarch64' + fi +} + # Set the file CPU x86_64 architecture set_arch_x86_64() { if check_flags 'avx512vnni' 'avx512dq' 'avx512f' 'avx512bw' 'avx512vl'; then @@ -90,6 +101,10 @@ case $uname_s in true_arch="$true_arch-neon" fi ;; + 'loongarch64'*) + file_os='linux' + set_arch_loongarch64 + ;; *) # Unsupported machine type, exit with error printf 'Unsupported machine type: %s\n' "$uname_m" exit 1 diff --git a/src/Makefile b/src/Makefile index 042d9479cc8..6cb778a6822 100644 --- a/src/Makefile +++ b/src/Makefile @@ -100,6 +100,8 @@ VPATH = syzygy:nnue:nnue/features # vnni512 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 512 # neon = yes/no --- -DUSE_NEON --- Use ARM SIMD architecture # dotprod = yes/no --- -DUSE_NEON_DOTPROD --- Use ARM advanced SIMD Int8 dot product instructions +# lsx = yes/no --- -mlsx --- Use Loongson SIMD eXtension +# lasx = yes/no --- -mlasx --- use Loongson Advanced SIMD eXtension # # Note that Makefile is space sensitive, so when adding new architectures # or modifying existing flags, you have to make sure there are no extra spaces @@ -125,7 +127,8 @@ ifeq ($(ARCH), $(filter $(ARCH), \ x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-avxvnni x86-64-bmi2 \ x86-64-avx2 x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \ x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-32 e2k \ - armv7 armv7-neon armv8 armv8-dotprod apple-silicon general-64 general-32 riscv64 loongarch64)) + armv7 armv7-neon armv8 armv8-dotprod apple-silicon general-64 general-32 riscv64 \ + loongarch64 loongarch64-lsx loongarch64-lasx)) SUPPORTED_ARCH=true else SUPPORTED_ARCH=false @@ -151,6 +154,8 @@ vnni512 = no neon = no dotprod = no arm_version = 0 +lsx = no +lasx = no STRIP = strip ifneq ($(shell which clang-format-18 2> /dev/null),) @@ -370,8 +375,19 @@ ifeq ($(ARCH),riscv64) arch = riscv64 endif -ifeq ($(ARCH),loongarch64) +ifeq ($(findstring loongarch64,$(ARCH)),loongarch64) arch = loongarch64 + prefetch = yes + +ifeq ($(findstring -lasx,$(ARCH)),-lasx) + lsx = yes + lasx = yes +endif + +ifeq ($(findstring -lsx,$(ARCH)),-lsx) + lsx = yes +endif + endif endif @@ -408,7 +424,7 @@ ifeq ($(COMP),gcc) ifeq ($(ARCH),riscv64) CXXFLAGS += -latomic endif - else ifeq ($(ARCH),loongarch64) + else ifeq ($(arch),loongarch64) CXXFLAGS += -latomic else CXXFLAGS += -m$(bits) @@ -480,7 +496,7 @@ ifeq ($(COMP),clang) ifeq ($(ARCH),riscv64) CXXFLAGS += -latomic endif - else ifeq ($(ARCH),loongarch64) + else ifeq ($(arch),loongarch64) CXXFLAGS += -latomic else CXXFLAGS += -m$(bits) @@ -719,6 +735,18 @@ ifeq ($(dotprod),yes) CXXFLAGS += -march=armv8.2-a+dotprod -DUSE_NEON_DOTPROD endif +ifeq ($(lasx),yes) + ifeq ($(comp),$(filter $(comp),gcc clang mingw icx)) + CXXFLAGS += -mlasx + endif +endif + +ifeq ($(lsx),yes) + ifeq ($(comp),$(filter $(comp),gcc clang mingw icx)) + CXXFLAGS += -mlsx + endif +endif + ### 3.7 pext ifeq ($(pext),yes) CXXFLAGS += -DUSE_PEXT @@ -835,6 +863,8 @@ help: @echo "general-32 > unspecified 32-bit" @echo "riscv64 > RISC-V 64-bit" @echo "loongarch64 > LoongArch 64-bit" + @echo "loongarch64-lsx > LoongArch 64-bit with SIMD eXtension" + @echo "loongarch64-lasx > LoongArch 64-bit with Advanced SIMD eXtension" @echo "" @echo "Supported compilers:" @echo "" @@ -960,6 +990,8 @@ config-sanity: net @echo "neon: '$(neon)'" @echo "dotprod: '$(dotprod)'" @echo "arm_version: '$(arm_version)'" + @echo "lsx: '$(lsx)'" + @echo "lasx: '$(lasx)'" @echo "target_windows: '$(target_windows)'" @echo "" @echo "Flags:" @@ -989,6 +1021,8 @@ config-sanity: net @test "$(vnni256)" = "yes" || test "$(vnni256)" = "no" @test "$(vnni512)" = "yes" || test "$(vnni512)" = "no" @test "$(neon)" = "yes" || test "$(neon)" = "no" + @test "$(lsx)" = "yes" || test "$(lsx)" = "no" + @test "$(lasx)" = "yes" || test "$(lasx)" = "no" @test "$(comp)" = "gcc" || test "$(comp)" = "icx" || test "$(comp)" = "mingw" || test "$(comp)" = "clang" \ || test "$(comp)" = "armv7a-linux-androideabi16-clang" || test "$(comp)" = "aarch64-linux-android21-clang" From 5d0bb5976ef2da06a6386d0f5cad2f755e9b0927 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Thu, 19 Sep 2024 15:03:07 +0300 Subject: [PATCH 1723/1766] Removed ROOK threatenedByPawn Passed STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 56608 W: 14788 L: 14588 D: 27232 Ptnml(0-2): 162, 6763, 14313, 6845, 221 https://tests.stockfishchess.org/tests/view/66e83f9c86d5ee47d953ab1d Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 175758 W: 44501 L: 44438 D: 86819 Ptnml(0-2): 125, 19489, 48601, 19526, 138 https://tests.stockfishchess.org/tests/view/66e882d486d5ee47d953ab8a closes https://github.com/official-stockfish/Stockfish/pull/5601 bench: 1241271 --- src/movepick.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 63d9e8b1ace..f4ef0e5499b 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -175,9 +175,9 @@ void MovePicker::score() { : 0; // malus for putting piece en prise - m.value -= (pt == QUEEN ? bool(to & threatenedByRook) * 49000 - : pt == ROOK ? bool(to & threatenedByMinor) * 24335 - : bool(to & threatenedByPawn) * 14900); + m.value -= (pt == QUEEN ? bool(to & threatenedByRook) * 49000 + : pt == ROOK && bool(to & threatenedByMinor) ? 24335 + : 0); if (rootNode) m.value += 4 * (*rootHistory)[pos.side_to_move()][m.from_to()]; From ae420e735f378bbb675dcf47598a5204f008cdd5 Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Wed, 18 Sep 2024 06:26:20 +0200 Subject: [PATCH 1724/1766] Tweak Correction histories tune parameters some more, adjust scores updated for each history passed STC: https://tests.stockfishchess.org/tests/view/66ea569186d5ee47d953ae48 LLR: 2.92 (-2.94,2.94) <0.00,2.00> Total: 36288 W: 9660 L: 9344 D: 17284 Ptnml(0-2): 110, 4207, 9220, 4471, 136 passed LTC: https://tests.stockfishchess.org/tests/view/66ea9b4e86d5ee47d953ae6f LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 241446 W: 61748 L: 61010 D: 118688 Ptnml(0-2): 173, 26211, 67202, 26979, 158 closes https://github.com/official-stockfish/Stockfish/pull/5606 Bench: 1677953 --- src/search.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 199b9355403..229aef9b218 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -88,7 +88,8 @@ Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { const auto wnpcv = w.nonPawnCorrectionHistory[WHITE][us][non_pawn_index(pos)]; const auto bnpcv = w.nonPawnCorrectionHistory[BLACK][us][non_pawn_index(pos)]; const auto cv = - (98198 * pcv + 68968 * mcv + 54353 * macv + 85174 * micv + 85581 * (wnpcv + bnpcv)) / 2097152; + (99916 * pcv + 55067 * mcv + 55530 * macv + 95324 * micv + 105056 * (wnpcv + bnpcv)) + / 2097152; v += cv; return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } @@ -1409,12 +1410,15 @@ Value Search::Worker::search( { auto bonus = std::clamp(int(bestValue - ss->staticEval) * depth / 8, -CORRECTION_HISTORY_LIMIT / 4, CORRECTION_HISTORY_LIMIT / 4); - thisThread->pawnCorrectionHistory[us][pawn_structure_index(pos)] << bonus; - thisThread->materialCorrectionHistory[us][material_index(pos)] << bonus; - thisThread->majorPieceCorrectionHistory[us][major_piece_index(pos)] << bonus; - thisThread->minorPieceCorrectionHistory[us][minor_piece_index(pos)] << bonus; - thisThread->nonPawnCorrectionHistory[WHITE][us][non_pawn_index(pos)] << bonus; - thisThread->nonPawnCorrectionHistory[BLACK][us][non_pawn_index(pos)] << bonus; + thisThread->pawnCorrectionHistory[us][pawn_structure_index(pos)] + << bonus * 101 / 128; + thisThread->materialCorrectionHistory[us][material_index(pos)] << bonus * 99 / 128; + thisThread->majorPieceCorrectionHistory[us][major_piece_index(pos)] << bonus * 157 / 128; + thisThread->minorPieceCorrectionHistory[us][minor_piece_index(pos)] << bonus * 153 / 128; + thisThread->nonPawnCorrectionHistory[WHITE][us][non_pawn_index(pos)] + << bonus * 123 / 128; + thisThread->nonPawnCorrectionHistory[BLACK][us][non_pawn_index(pos)] + << bonus * 165 / 128; } assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); From aff1f67997cd2584ea7c82d967ac7bfd4cc77861 Mon Sep 17 00:00:00 2001 From: Nonlinear2 <131959792+Nonlinear2@users.noreply.github.com> Date: Tue, 24 Sep 2024 13:17:24 +0200 Subject: [PATCH 1725/1766] simplify see pruning in qsearch passed non-regression STC: LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 34880 W: 9193 L: 8968 D: 16719 Ptnml(0-2): 103, 4047, 8935, 4232, 123 https://tests.stockfishchess.org/tests/view/66ee83bd86d5ee47d953b15b passed non-regression LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 69126 W: 17529 L: 17357 D: 34240 Ptnml(0-2): 41, 7507, 19285, 7699, 31 https://tests.stockfishchess.org/tests/view/66ef3e0386d5ee47d953b1d3 closes https://github.com/official-stockfish/Stockfish/pull/5607 Bench: 1339840 --- src/search.cpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 229aef9b218..d87a6b9a041 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1596,19 +1596,11 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) continue; } - // If static eval is much lower than alpha and move is - // not winning material, we can prune this move. (~2 Elo) - if (futilityBase <= alpha && !pos.see_ge(move, 1)) + // if static exchange evaluation is low enough + // we can prune this move. (~2 Elo) + if (!pos.see_ge(move, alpha - futilityBase)) { - bestValue = std::max(bestValue, futilityBase); - continue; - } - - // If static exchange evaluation is much worse than what - // is needed to not fall below alpha, we can prune this move. - if (futilityBase > alpha && !pos.see_ge(move, (alpha - futilityBase) * 4)) - { - bestValue = alpha; + bestValue = (futilityBase > alpha) ? alpha : std::max(bestValue, futilityBase); continue; } } From 3ac75cd27d914da29280163c9d391bbca414d766 Mon Sep 17 00:00:00 2001 From: Tomasz Sobczyk Date: Tue, 4 Jun 2024 17:23:56 +0200 Subject: [PATCH 1726/1766] Add a standardized benchmark command `speedtest`. `speedtest [threads] [hash_MiB] [time_s]`. `threads` default to system concurrency. `hash_MiB` defaults to `threads*128`. `time_s` defaults to 150. Intended to be used with default parameters, as a stable hardware benchmark. Example: ``` C:\dev\stockfish-master\src>stockfish.exe speedtest Stockfish dev-20240928-nogit by the Stockfish developers (see AUTHORS file) info string Using 16 threads Warmup position 3/3 Position 258/258 =========================== Version : Stockfish dev-20240928-nogit Compiled by : g++ (GNUC) 13.2.0 on MinGW64 Compilation architecture : x86-64-vnni256 Compilation settings : 64bit VNNI BMI2 AVX2 SSE41 SSSE3 SSE2 POPCNT Compiler __VERSION__ macro : 13.2.0 Large pages : yes User invocation : speedtest Filled invocation : speedtest 16 2048 150 Available processors : 0-15 Thread count : 16 Thread binding : none TT size [MiB] : 2048 Hash max, avg [per mille] : single search : 40, 21 single game : 631, 428 Total nodes searched : 2099917842 Total search time [s] : 153.937 Nodes/second : 13641410 ``` ------------------------------- Small unrelated tweaks: - Network verification output is now handled as a callback. - TT hashfull queries allow specifying maximum entry age. closes https://github.com/official-stockfish/Stockfish/pull/5354 No functional change --- src/benchmark.cpp | 349 +++++++++++++++++++++++++++++++++++++++++++ src/benchmark.h | 10 ++ src/engine.cpp | 37 +++-- src/engine.h | 7 +- src/memory.cpp | 31 ++++ src/memory.h | 2 + src/misc.cpp | 11 +- src/misc.h | 8 +- src/nnue/network.cpp | 51 ++++--- src/nnue/network.h | 4 +- src/numa.h | 11 +- src/tt.cpp | 15 +- src/tt.h | 2 +- src/uci.cpp | 174 ++++++++++++++++++++- src/uci.h | 5 +- 15 files changed, 664 insertions(+), 53 deletions(-) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 3622ac8afc8..35ad3c18014 100644 --- a/src/benchmark.cpp +++ b/src/benchmark.cpp @@ -17,6 +17,7 @@ */ #include "benchmark.h" +#include "numa.h" #include #include @@ -91,6 +92,282 @@ const std::vector Defaults = { }; // clang-format on +// clang-format off +// human-randomly picked 5 games with <60 moves from +// https://tests.stockfishchess.org/tests/view/665c71f9fd45fb0f907c21e0 +// only moves for one side +const std::vector> BenchmarkPositions = { + { + "rnbq1k1r/ppp1bppp/4pn2/8/2B5/2NP1N2/PPP2PPP/R1BQR1K1 b - - 2 8", + "rnbq1k1r/pp2bppp/4pn2/2p5/2B2B2/2NP1N2/PPP2PPP/R2QR1K1 b - - 1 9", + "r1bq1k1r/pp2bppp/2n1pn2/2p5/2B1NB2/3P1N2/PPP2PPP/R2QR1K1 b - - 3 10", + "r1bq1k1r/pp2bppp/2n1p3/2p5/2B1PB2/5N2/PPP2PPP/R2QR1K1 b - - 0 11", + "r1b2k1r/pp2bppp/2n1p3/2p5/2B1PB2/5N2/PPP2PPP/3RR1K1 b - - 0 12", + "r1b1k2r/pp2bppp/2n1p3/2p5/2B1PB2/2P2N2/PP3PPP/3RR1K1 b - - 0 13", + "r1b1k2r/1p2bppp/p1n1p3/2p5/4PB2/2P2N2/PP2BPPP/3RR1K1 b - - 1 14", + "r1b1k2r/4bppp/p1n1p3/1pp5/P3PB2/2P2N2/1P2BPPP/3RR1K1 b - - 0 15", + "r1b1k2r/4bppp/p1n1p3/1P6/2p1PB2/2P2N2/1P2BPPP/3RR1K1 b - - 0 16", + "r1b1k2r/4bppp/2n1p3/1p6/2p1PB2/1PP2N2/4BPPP/3RR1K1 b - - 0 17", + "r3k2r/3bbppp/2n1p3/1p6/2P1PB2/2P2N2/4BPPP/3RR1K1 b - - 0 18", + "r3k2r/3bbppp/2n1p3/8/1pP1P3/2P2N2/3BBPPP/3RR1K1 b - - 1 19", + "1r2k2r/3bbppp/2n1p3/8/1pPNP3/2P5/3BBPPP/3RR1K1 b - - 3 20", + "1r2k2r/3bbppp/2n1p3/8/2PNP3/2B5/4BPPP/3RR1K1 b - - 0 21", + "1r2k2r/3bb1pp/2n1pp2/1N6/2P1P3/2B5/4BPPP/3RR1K1 b - - 1 22", + "1r2k2r/3b2pp/2n1pp2/1N6/1BP1P3/8/4BPPP/3RR1K1 b - - 0 23", + "1r2k2r/3b2pp/4pp2/1N6/1nP1P3/8/3RBPPP/4R1K1 b - - 1 24", + "1r5r/3bk1pp/4pp2/1N6/1nP1PP2/8/3RB1PP/4R1K1 b - - 0 25", + "1r5r/3bk1pp/2n1pp2/1N6/2P1PP2/8/3RBKPP/4R3 b - - 2 26", + "1r5r/3bk1pp/2n2p2/1N2p3/2P1PP2/6P1/3RBK1P/4R3 b - - 0 27", + "1r1r4/3bk1pp/2n2p2/1N2p3/2P1PP2/6P1/3RBK1P/R7 b - - 2 28", + "1r1r4/N3k1pp/2n1bp2/4p3/2P1PP2/6P1/3RBK1P/R7 b - - 4 29", + "1r1r4/3bk1pp/2N2p2/4p3/2P1PP2/6P1/3RBK1P/R7 b - - 0 30", + "1r1R4/4k1pp/2b2p2/4p3/2P1PP2/6P1/4BK1P/R7 b - - 0 31", + "3r4/4k1pp/2b2p2/4P3/2P1P3/6P1/4BK1P/R7 b - - 0 32", + "3r4/R3k1pp/2b5/4p3/2P1P3/6P1/4BK1P/8 b - - 1 33", + "8/3rk1pp/2b5/R3p3/2P1P3/6P1/4BK1P/8 b - - 3 34", + "8/3r2pp/2bk4/R1P1p3/4P3/6P1/4BK1P/8 b - - 0 35", + "8/2kr2pp/2b5/R1P1p3/4P3/4K1P1/4B2P/8 b - - 2 36", + "1k6/3r2pp/2b5/RBP1p3/4P3/4K1P1/7P/8 b - - 4 37", + "8/1k1r2pp/2b5/R1P1p3/4P3/3BK1P1/7P/8 b - - 6 38", + "1k6/3r2pp/2b5/2P1p3/4P3/3BK1P1/7P/R7 b - - 8 39", + "1k6/r5pp/2b5/2P1p3/4P3/3BK1P1/7P/5R2 b - - 10 40", + "1k3R2/6pp/2b5/2P1p3/4P3/r2BK1P1/7P/8 b - - 12 41", + "5R2/2k3pp/2b5/2P1p3/4P3/r2B2P1/3K3P/8 b - - 14 42", + "5R2/2k3pp/2b5/2P1p3/4P3/3BK1P1/r6P/8 b - - 16 43", + "5R2/2k3pp/2b5/2P1p3/4P3/r2B2P1/4K2P/8 b - - 18 44", + "5R2/2k3pp/2b5/2P1p3/4P3/3B1KP1/r6P/8 b - - 20 45", + "8/2k2Rpp/2b5/2P1p3/4P3/r2B1KP1/7P/8 b - - 22 46", + "3k4/5Rpp/2b5/2P1p3/4P3/r2B2P1/4K2P/8 b - - 24 47", + "3k4/5Rpp/2b5/2P1p3/4P3/3B1KP1/r6P/8 b - - 26 48", + "3k4/5Rpp/2b5/2P1p3/4P3/r2B2P1/4K2P/8 b - - 28 49", + "3k4/5Rpp/2b5/2P1p3/4P3/3BK1P1/r6P/8 b - - 30 50", + "3k4/5Rpp/2b5/2P1p3/4P3/r2B2P1/3K3P/8 b - - 32 51", + "3k4/5Rpp/2b5/2P1p3/4P3/2KB2P1/r6P/8 b - - 34 52", + "3k4/5Rpp/2b5/2P1p3/4P3/r2B2P1/2K4P/8 b - - 36 53", + "3k4/5Rpp/2b5/2P1p3/4P3/1K1B2P1/r6P/8 b - - 38 54", + "3k4/6Rp/2b5/2P1p3/4P3/1K1B2P1/7r/8 b - - 0 55", + "3k4/8/2b3Rp/2P1p3/4P3/1K1B2P1/7r/8 b - - 1 56", + "8/2k3R1/2b4p/2P1p3/4P3/1K1B2P1/7r/8 b - - 3 57", + "3k4/8/2b3Rp/2P1p3/4P3/1K1B2P1/7r/8 b - - 5 58", + "8/2k5/2b3Rp/2P1p3/1K2P3/3B2P1/7r/8 b - - 7 59", + "8/2k5/2b3Rp/2P1p3/4P3/2KB2P1/3r4/8 b - - 9 60", + "8/2k5/2b3Rp/2P1p3/1K2P3/3B2P1/6r1/8 b - - 11 61", + "8/2k5/2b3Rp/2P1p3/4P3/2KB2P1/3r4/8 b - - 13 62", + "8/2k5/2b3Rp/2P1p3/2K1P3/3B2P1/6r1/8 b - - 15 63", + "4b3/2k3R1/7p/2P1p3/2K1P3/3B2P1/6r1/8 b - - 17 64", + }, + { + "r1bqkbnr/npp1pppp/p7/3P4/4pB2/2N5/PPP2PPP/R2QKBNR w KQkq - 1 6", + "r1bqkb1r/npp1pppp/p4n2/3P4/4pB2/2N5/PPP1QPPP/R3KBNR w KQkq - 3 7", + "r2qkb1r/npp1pppp/p4n2/3P1b2/4pB2/2N5/PPP1QPPP/2KR1BNR w kq - 5 8", + "r2qkb1r/1pp1pppp/p4n2/1n1P1b2/4pB2/2N4P/PPP1QPP1/2KR1BNR w kq - 1 9", + "r2qkb1r/1pp1pppp/5n2/1p1P1b2/4pB2/7P/PPP1QPP1/2KR1BNR w kq - 0 10", + "r2qkb1r/1ppbpppp/5n2/1Q1P4/4pB2/7P/PPP2PP1/2KR1BNR w kq - 1 11", + "3qkb1r/1Qpbpppp/5n2/3P4/4pB2/7P/rPP2PP1/2KR1BNR w k - 0 12", + "q3kb1r/1Qpbpppp/5n2/3P4/4pB2/7P/rPP2PP1/1K1R1BNR w k - 2 13", + "r3kb1r/2pbpppp/5n2/3P4/4pB2/7P/1PP2PP1/1K1R1BNR w k - 0 14", + "r3kb1r/2Bb1ppp/4pn2/3P4/4p3/7P/1PP2PP1/1K1R1BNR w k - 0 15", + "r3kb1r/2Bb2pp/4pn2/8/4p3/7P/1PP2PP1/1K1R1BNR w k - 0 16", + "r3k2r/2Bb2pp/4pn2/2b5/4p3/7P/1PP1NPP1/1K1R1B1R w k - 2 17", + "r6r/2Bbk1pp/4pn2/2b5/3Np3/7P/1PP2PP1/1K1R1B1R w - - 4 18", + "r6r/b2bk1pp/4pn2/4B3/3Np3/7P/1PP2PP1/1K1R1B1R w - - 6 19", + "r1r5/b2bk1pp/4pn2/4B3/2BNp3/7P/1PP2PP1/1K1R3R w - - 8 20", + "r7/b2bk1pp/4pn2/2r1B3/2BNp3/1P5P/2P2PP1/1K1R3R w - - 1 21", + "rb6/3bk1pp/4pn2/2r1B3/2BNpP2/1P5P/2P3P1/1K1R3R w - - 1 22", + "1r6/3bk1pp/4pn2/2r5/2BNpP2/1P5P/2P3P1/1K1R3R w - - 0 23", + "1r6/3bk1p1/4pn1p/2r5/2BNpP2/1P5P/2P3P1/2KR3R w - - 0 24", + "8/3bk1p1/1r2pn1p/2r5/2BNpP1P/1P6/2P3P1/2KR3R w - - 1 25", + "8/3bk3/1r2pnpp/2r5/2BNpP1P/1P6/2P3P1/2K1R2R w - - 0 26", + "2b5/4k3/1r2pnpp/2r5/2BNpP1P/1P4P1/2P5/2K1R2R w - - 1 27", + "8/1b2k3/1r2pnpp/2r5/2BNpP1P/1P4P1/2P5/2K1R1R1 w - - 3 28", + "8/1b1nk3/1r2p1pp/2r5/2BNpPPP/1P6/2P5/2K1R1R1 w - - 1 29", + "8/1b2k3/1r2p1pp/2r1nP2/2BNp1PP/1P6/2P5/2K1R1R1 w - - 1 30", + "8/1b2k3/1r2p1p1/2r1nPp1/2BNp2P/1P6/2P5/2K1R1R1 w - - 0 31", + "8/1b2k3/1r2p1n1/2r3p1/2BNp2P/1P6/2P5/2K1R1R1 w - - 0 32", + "8/1b2k3/1r2p1n1/6r1/2BNp2P/1P6/2P5/2K1R3 w - - 0 33", + "8/1b2k3/1r2p3/4n1P1/2BNp3/1P6/2P5/2K1R3 w - - 1 34", + "8/1b2k3/1r2p3/4n1P1/2BN4/1P2p3/2P5/2K4R w - - 0 35", + "8/1b2k3/1r2p2R/6P1/2nN4/1P2p3/2P5/2K5 w - - 0 36", + "8/1b2k3/3rp2R/6P1/2PN4/4p3/2P5/2K5 w - - 1 37", + "8/4k3/3rp2R/6P1/2PN4/2P1p3/6b1/2K5 w - - 1 38", + "8/4k3/r3p2R/2P3P1/3N4/2P1p3/6b1/2K5 w - - 1 39", + "8/3k4/r3p2R/2P2NP1/8/2P1p3/6b1/2K5 w - - 3 40", + "8/3k4/4p2R/2P3P1/8/2P1N3/6b1/r1K5 w - - 1 41", + "8/3k4/4p2R/2P3P1/8/2P1N3/3K2b1/6r1 w - - 3 42", + "8/3k4/4p2R/2P3P1/8/2PKNb2/8/6r1 w - - 5 43", + "8/4k3/4p1R1/2P3P1/8/2PKNb2/8/6r1 w - - 7 44", + "8/4k3/4p1R1/2P3P1/3K4/2P1N3/8/6rb w - - 9 45", + "8/3k4/4p1R1/2P1K1P1/8/2P1N3/8/6rb w - - 11 46", + "8/3k4/4p1R1/2P3P1/5K2/2P1N3/8/4r2b w - - 13 47", + "8/3k4/2b1p2R/2P3P1/5K2/2P1N3/8/4r3 w - - 15 48", + "8/3k4/2b1p3/2P3P1/5K2/2P1N2R/8/6r1 w - - 17 49", + "2k5/7R/2b1p3/2P3P1/5K2/2P1N3/8/6r1 w - - 19 50", + "2k5/7R/4p3/2P3P1/b1P2K2/4N3/8/6r1 w - - 1 51", + "2k5/3bR3/4p3/2P3P1/2P2K2/4N3/8/6r1 w - - 3 52", + "3k4/3b2R1/4p3/2P3P1/2P2K2/4N3/8/6r1 w - - 5 53", + "3kb3/6R1/4p1P1/2P5/2P2K2/4N3/8/6r1 w - - 1 54", + "3kb3/6R1/4p1P1/2P5/2P2KN1/8/8/2r5 w - - 3 55", + "3kb3/6R1/4p1P1/2P1N3/2P2K2/8/8/5r2 w - - 5 56", + "3kb3/6R1/4p1P1/2P1N3/2P5/4K3/8/4r3 w - - 7 57", + }, + { + "rnbq1rk1/ppp1npb1/4p1p1/3P3p/3PP3/2N2N2/PP2BPPP/R1BQ1RK1 b - - 0 8", + "rnbq1rk1/ppp1npb1/6p1/3pP2p/3P4/2N2N2/PP2BPPP/R1BQ1RK1 b - - 0 9", + "rn1q1rk1/ppp1npb1/6p1/3pP2p/3P2b1/2N2N2/PP2BPPP/R1BQR1K1 b - - 2 10", + "r2q1rk1/ppp1npb1/2n3p1/3pP2p/3P2bN/2N5/PP2BPPP/R1BQR1K1 b - - 4 11", + "r4rk1/pppqnpb1/2n3p1/3pP2p/3P2bN/2N4P/PP2BPP1/R1BQR1K1 b - - 0 12", + "r4rk1/pppqnpb1/2n3p1/3pP2p/3P3N/7P/PP2NPP1/R1BQR1K1 b - - 0 13", + "r4rk1/pppq1pb1/2n3p1/3pPN1p/3P4/7P/PP2NPP1/R1BQR1K1 b - - 0 14", + "r4rk1/ppp2pb1/2n3p1/3pPq1p/3P1N2/7P/PP3PP1/R1BQR1K1 b - - 1 15", + "r4rk1/pppq1pb1/2n3p1/3pP2p/P2P1N2/7P/1P3PP1/R1BQR1K1 b - - 0 16", + "r2n1rk1/pppq1pb1/6p1/3pP2p/P2P1N2/R6P/1P3PP1/2BQR1K1 b - - 2 17", + "r4rk1/pppq1pb1/4N1p1/3pP2p/P2P4/R6P/1P3PP1/2BQR1K1 b - - 0 18", + "r4rk1/ppp2pb1/4q1p1/3pP1Bp/P2P4/R6P/1P3PP1/3QR1K1 b - - 1 19", + "r3r1k1/ppp2pb1/4q1p1/3pP1Bp/P2P1P2/R6P/1P4P1/3QR1K1 b - - 0 20", + "r3r1k1/ppp3b1/4qpp1/3pP2p/P2P1P1B/R6P/1P4P1/3QR1K1 b - - 1 21", + "r3r1k1/ppp3b1/4q1p1/3pP2p/P4P1B/R6P/1P4P1/3QR1K1 b - - 0 22", + "r4rk1/ppp3b1/4q1p1/3pP1Bp/P4P2/R6P/1P4P1/3QR1K1 b - - 2 23", + "r4rk1/pp4b1/4q1p1/2ppP1Bp/P4P2/3R3P/1P4P1/3QR1K1 b - - 1 24", + "r4rk1/pp4b1/4q1p1/2p1P1Bp/P2p1PP1/3R3P/1P6/3QR1K1 b - - 0 25", + "r4rk1/pp4b1/4q1p1/2p1P1B1/P2p1PP1/3R4/1P6/3QR1K1 b - - 0 26", + "r5k1/pp3rb1/4q1p1/2p1P1B1/P2p1PP1/6R1/1P6/3QR1K1 b - - 2 27", + "5rk1/pp3rb1/4q1p1/2p1P1B1/P2pRPP1/6R1/1P6/3Q2K1 b - - 4 28", + "5rk1/1p3rb1/p3q1p1/P1p1P1B1/3pRPP1/6R1/1P6/3Q2K1 b - - 0 29", + "4r1k1/1p3rb1/p3q1p1/P1p1P1B1/3pRPP1/1P4R1/8/3Q2K1 b - - 0 30", + "4r1k1/5rb1/pP2q1p1/2p1P1B1/3pRPP1/1P4R1/8/3Q2K1 b - - 0 31", + "4r1k1/5rb1/pq4p1/2p1P1B1/3pRPP1/1P4R1/4Q3/6K1 b - - 1 32", + "4r1k1/1r4b1/pq4p1/2p1P1B1/3pRPP1/1P4R1/2Q5/6K1 b - - 3 33", + "4r1k1/1r4b1/1q4p1/p1p1P1B1/3p1PP1/1P4R1/2Q5/4R1K1 b - - 1 34", + "4r1k1/3r2b1/1q4p1/p1p1P1B1/2Qp1PP1/1P4R1/8/4R1K1 b - - 3 35", + "4r1k1/3r2b1/4q1p1/p1p1P1B1/2Qp1PP1/1P4R1/5K2/4R3 b - - 5 36", + "4r1k1/3r2b1/6p1/p1p1P1B1/2Pp1PP1/6R1/5K2/4R3 b - - 0 37", + "4r1k1/3r2b1/6p1/p1p1P1B1/2P2PP1/3p2R1/5K2/3R4 b - - 1 38", + "5rk1/3r2b1/6p1/p1p1P1B1/2P2PP1/3p2R1/8/3RK3 b - - 3 39", + "5rk1/6b1/6p1/p1p1P1B1/2Pr1PP1/3R4/8/3RK3 b - - 0 40", + "5rk1/3R2b1/6p1/p1p1P1B1/2r2PP1/8/8/3RK3 b - - 1 41", + "5rk1/3R2b1/6p1/p1p1P1B1/4rPP1/8/3K4/3R4 b - - 3 42", + "1r4k1/3R2b1/6p1/p1p1P1B1/4rPP1/2K5/8/3R4 b - - 5 43", + "1r4k1/3R2b1/6p1/p1p1P1B1/2K2PP1/4r3/8/3R4 b - - 7 44", + "1r3bk1/8/3R2p1/p1p1P1B1/2K2PP1/4r3/8/3R4 b - - 9 45", + "1r3bk1/8/6R1/2p1P1B1/p1K2PP1/4r3/8/3R4 b - - 0 46", + "1r3b2/5k2/R7/2p1P1B1/p1K2PP1/4r3/8/3R4 b - - 2 47", + "5b2/1r3k2/R7/2p1P1B1/p1K2PP1/4r3/8/7R b - - 4 48", + "5b2/5k2/R7/2pKP1B1/pr3PP1/4r3/8/7R b - - 6 49", + "5b2/5k2/R1K5/2p1P1B1/p2r1PP1/4r3/8/7R b - - 8 50", + "8/R4kb1/2K5/2p1P1B1/p2r1PP1/4r3/8/7R b - - 10 51", + "8/R5b1/2K3k1/2p1PPB1/p2r2P1/4r3/8/7R b - - 0 52", + "8/6R1/2K5/2p1PPk1/p2r2P1/4r3/8/7R b - - 0 53", + "8/6R1/2K5/2p1PP2/p2r1kP1/4r3/8/5R2 b - - 2 54", + "8/6R1/2K2P2/2p1P3/p2r2P1/4r1k1/8/5R2 b - - 0 55", + "8/5PR1/2K5/2p1P3/p2r2P1/4r3/6k1/5R2 b - - 0 56", + }, + { + "rn1qkb1r/p1pbpppp/5n2/8/2pP4/2N5/1PQ1PPPP/R1B1KBNR w KQkq - 0 7", + "r2qkb1r/p1pbpppp/2n2n2/8/2pP4/2N2N2/1PQ1PPPP/R1B1KB1R w KQkq - 2 8", + "r2qkb1r/p1pbpppp/5n2/8/1npPP3/2N2N2/1PQ2PPP/R1B1KB1R w KQkq - 1 9", + "r2qkb1r/p1pb1ppp/4pn2/8/1npPP3/2N2N2/1P3PPP/R1BQKB1R w KQkq - 0 10", + "r2qk2r/p1pbbppp/4pn2/8/1nBPP3/2N2N2/1P3PPP/R1BQK2R w KQkq - 1 11", + "r2q1rk1/p1pbbppp/4pn2/8/1nBPP3/2N2N2/1P3PPP/R1BQ1RK1 w - - 3 12", + "r2q1rk1/2pbbppp/p3pn2/8/1nBPPB2/2N2N2/1P3PPP/R2Q1RK1 w - - 0 13", + "r2q1rk1/2p1bppp/p3pn2/1b6/1nBPPB2/2N2N2/1P3PPP/R2QR1K1 w - - 2 14", + "r2q1rk1/4bppp/p1p1pn2/1b6/1nBPPB2/1PN2N2/5PPP/R2QR1K1 w - - 0 15", + "r4rk1/3qbppp/p1p1pn2/1b6/1nBPPB2/1PN2N2/3Q1PPP/R3R1K1 w - - 2 16", + "r4rk1/1q2bppp/p1p1pn2/1b6/1nBPPB2/1PN2N1P/3Q1PP1/R3R1K1 w - - 1 17", + "r3r1k1/1q2bppp/p1p1pn2/1b6/1nBPPB2/1PN2N1P/4QPP1/R3R1K1 w - - 3 18", + "r3r1k1/1q1nbppp/p1p1p3/1b6/1nBPPB2/1PN2N1P/4QPP1/3RR1K1 w - - 5 19", + "r3rbk1/1q1n1ppp/p1p1p3/1b6/1nBPPB2/1PN2N1P/3RQPP1/4R1K1 w - - 7 20", + "r3rbk1/1q3ppp/pnp1p3/1b6/1nBPPB2/1PN2N1P/3RQPP1/4R2K w - - 9 21", + "2r1rbk1/1q3ppp/pnp1p3/1b6/1nBPPB2/1PN2N1P/3RQPP1/1R5K w - - 11 22", + "2r1rbk1/1q4pp/pnp1pp2/1b6/1nBPPB2/1PN2N1P/4QPP1/1R1R3K w - - 0 23", + "2r1rbk1/5qpp/pnp1pp2/1b6/1nBPP3/1PN1BN1P/4QPP1/1R1R3K w - - 2 24", + "2r1rbk1/5qp1/pnp1pp1p/1b6/1nBPP3/1PN1BN1P/4QPP1/1R1R2K1 w - - 0 25", + "2r1rbk1/5qp1/pnp1pp1p/1b6/2BPP3/1P2BN1P/n3QPP1/1R1R2K1 w - - 0 26", + "r3rbk1/5qp1/pnp1pp1p/1b6/2BPP3/1P2BN1P/Q4PP1/1R1R2K1 w - - 1 27", + "rr3bk1/5qp1/pnp1pp1p/1b6/2BPP3/1P2BN1P/Q4PP1/R2R2K1 w - - 3 28", + "rr2qbk1/6p1/pnp1pp1p/1b6/2BPP3/1P2BN1P/4QPP1/R2R2K1 w - - 5 29", + "rr2qbk1/6p1/1np1pp1p/pb6/2BPP3/1P1QBN1P/5PP1/R2R2K1 w - - 0 30", + "rr2qbk1/6p1/1n2pp1p/pp6/3PP3/1P1QBN1P/5PP1/R2R2K1 w - - 0 31", + "rr2qbk1/6p1/1n2pp1p/1p1P4/p3P3/1P1QBN1P/5PP1/R2R2K1 w - - 0 32", + "rr2qbk1/3n2p1/3Ppp1p/1p6/p3P3/1P1QBN1P/5PP1/R2R2K1 w - - 1 33", + "rr3bk1/3n2p1/3Ppp1p/1p5q/pP2P3/3QBN1P/5PP1/R2R2K1 w - - 1 34", + "rr3bk1/3n2p1/3Ppp1p/1p5q/1P2P3/p2QBN1P/5PP1/2RR2K1 w - - 0 35", + "1r3bk1/3n2p1/r2Ppp1p/1p5q/1P2P3/pQ2BN1P/5PP1/2RR2K1 w - - 2 36", + "1r2qbk1/2Rn2p1/r2Ppp1p/1p6/1P2P3/pQ2BN1P/5PP1/3R2K1 w - - 4 37", + "1r2qbk1/2Rn2p1/r2Ppp1p/1pB5/1P2P3/1Q3N1P/p4PP1/3R2K1 w - - 0 38", + "1r2q1k1/2Rn2p1/r2bpp1p/1pB5/1P2P3/1Q3N1P/p4PP1/R5K1 w - - 0 39", + "1r2q1k1/2Rn2p1/3rpp1p/1p6/1P2P3/1Q3N1P/p4PP1/R5K1 w - - 0 40", + "2r1q1k1/2Rn2p1/3rpp1p/1p6/1P2P3/5N1P/Q4PP1/R5K1 w - - 1 41", + "1r2q1k1/1R1n2p1/3rpp1p/1p6/1P2P3/5N1P/Q4PP1/R5K1 w - - 3 42", + "2r1q1k1/2Rn2p1/3rpp1p/1p6/1P2P3/5N1P/Q4PP1/R5K1 w - - 5 43", + "1r2q1k1/1R1n2p1/3rpp1p/1p6/1P2P3/5N1P/Q4PP1/R5K1 w - - 7 44", + "1rq3k1/R2n2p1/3rpp1p/1p6/1P2P3/5N1P/Q4PP1/R5K1 w - - 9 45", + "2q3k1/Rr1n2p1/3rpp1p/1p6/1P2P3/5N1P/4QPP1/R5K1 w - - 11 46", + "Rrq3k1/3n2p1/3rpp1p/1p6/1P2P3/5N1P/4QPP1/R5K1 w - - 13 47", + }, + { + "rn1qkb1r/1pp2ppp/p4p2/3p1b2/5P2/1P2PN2/P1PP2PP/RN1QKB1R b KQkq - 1 6", + "r2qkb1r/1pp2ppp/p1n2p2/3p1b2/3P1P2/1P2PN2/P1P3PP/RN1QKB1R b KQkq - 0 7", + "r2qkb1r/1pp2ppp/p4p2/3p1b2/1n1P1P2/1P1BPN2/P1P3PP/RN1QK2R b KQkq - 2 8", + "r2qkb1r/1pp2ppp/p4p2/3p1b2/3P1P2/1P1PPN2/P5PP/RN1QK2R b KQkq - 0 9", + "r2qk2r/1pp2ppp/p2b1p2/3p1b2/3P1P2/1PNPPN2/P5PP/R2QK2R b KQkq - 2 10", + "r2qk2r/1p3ppp/p1pb1p2/3p1b2/3P1P2/1PNPPN2/P5PP/R2Q1RK1 b kq - 1 11", + "r2q1rk1/1p3ppp/p1pb1p2/3p1b2/3P1P2/1PNPPN2/P2Q2PP/R4RK1 b - - 3 12", + "r2qr1k1/1p3ppp/p1pb1p2/3p1b2/3P1P2/1P1PPN2/P2QN1PP/R4RK1 b - - 5 13", + "r3r1k1/1p3ppp/pqpb1p2/3p1b2/3P1P2/1P1PPNN1/P2Q2PP/R4RK1 b - - 7 14", + "r3r1k1/1p3ppp/pqp2p2/3p1b2/1b1P1P2/1P1PPNN1/P1Q3PP/R4RK1 b - - 9 15", + "r3r1k1/1p1b1ppp/pqp2p2/3p4/1b1P1P2/1P1PPNN1/P4QPP/R4RK1 b - - 11 16", + "2r1r1k1/1p1b1ppp/pqp2p2/3p4/1b1PPP2/1P1P1NN1/P4QPP/R4RK1 b - - 0 17", + "2r1r1k1/1p1b1ppp/pq3p2/2pp4/1b1PPP2/PP1P1NN1/5QPP/R4RK1 b - - 0 18", + "2r1r1k1/1p1b1ppp/pq3p2/2Pp4/4PP2/PPbP1NN1/5QPP/R4RK1 b - - 0 19", + "2r1r1k1/1p1b1ppp/p4p2/2Pp4/4PP2/PqbP1NN1/5QPP/RR4K1 b - - 1 20", + "2r1r1k1/1p1b1ppp/p4p2/2Pp4/q3PP2/P1bP1NN1/R4QPP/1R4K1 b - - 3 21", + "2r1r1k1/1p3ppp/p4p2/1bPP4/q4P2/P1bP1NN1/R4QPP/1R4K1 b - - 0 22", + "2r1r1k1/1p3ppp/p4p2/2PP4/q4P2/P1bb1NN1/R4QPP/2R3K1 b - - 1 23", + "2r1r1k1/1p3ppp/p2P1p2/2P5/2q2P2/P1bb1NN1/R4QPP/2R3K1 b - - 0 24", + "2rr2k1/1p3ppp/p2P1p2/2P5/2q2P2/P1bb1NN1/R4QPP/2R4K b - - 2 25", + "2rr2k1/1p3ppp/p2P1p2/2Q5/5P2/P1bb1NN1/R5PP/2R4K b - - 0 26", + "3r2k1/1p3ppp/p2P1p2/2r5/5P2/P1bb1N2/R3N1PP/2R4K b - - 1 27", + "3r2k1/1p3ppp/p2P1p2/2r5/5P2/P1b2N2/4R1PP/2R4K b - - 0 28", + "3r2k1/1p3ppp/p2P1p2/2r5/1b3P2/P4N2/4R1PP/3R3K b - - 2 29", + "3r2k1/1p2Rppp/p2P1p2/b1r5/5P2/P4N2/6PP/3R3K b - - 4 30", + "3r2k1/1R3ppp/p1rP1p2/b7/5P2/P4N2/6PP/3R3K b - - 0 31", + "3r2k1/1R3ppp/p2R1p2/b7/5P2/P4N2/6PP/7K b - - 0 32", + "6k1/1R3ppp/p2r1p2/b7/5P2/P4NP1/7P/7K b - - 0 33", + "6k1/1R3p1p/p2r1pp1/b7/5P1P/P4NP1/8/7K b - - 0 34", + "6k1/3R1p1p/pr3pp1/b7/5P1P/P4NP1/8/7K b - - 2 35", + "6k1/5p2/pr3pp1/b2R3p/5P1P/P4NP1/8/7K b - - 1 36", + "6k1/5p2/pr3pp1/7p/5P1P/P1bR1NP1/8/7K b - - 3 37", + "6k1/5p2/p1r2pp1/7p/5P1P/P1bR1NP1/6K1/8 b - - 5 38", + "6k1/5p2/p1r2pp1/b2R3p/5P1P/P4NP1/6K1/8 b - - 7 39", + "6k1/5p2/p4pp1/b2R3p/5P1P/P4NPK/2r5/8 b - - 9 40", + "6k1/2b2p2/p4pp1/7p/5P1P/P2R1NPK/2r5/8 b - - 11 41", + "6k1/2b2p2/5pp1/p6p/3N1P1P/P2R2PK/2r5/8 b - - 1 42", + "6k1/2b2p2/5pp1/p6p/3N1P1P/P1R3PK/r7/8 b - - 3 43", + "6k1/5p2/1b3pp1/p6p/5P1P/P1R3PK/r1N5/8 b - - 5 44", + "8/5pk1/1bR2pp1/p6p/5P1P/P5PK/r1N5/8 b - - 7 45", + "3b4/5pk1/2R2pp1/p4P1p/7P/P5PK/r1N5/8 b - - 0 46", + "8/4bpk1/2R2pp1/p4P1p/6PP/P6K/r1N5/8 b - - 0 47", + "8/5pk1/2R2pP1/p6p/6PP/b6K/r1N5/8 b - - 0 48", + "8/6k1/2R2pp1/p6P/7P/b6K/r1N5/8 b - - 0 49", + "8/6k1/2R2p2/p6p/7P/b5K1/r1N5/8 b - - 1 50", + "8/8/2R2pk1/p6p/7P/b4K2/r1N5/8 b - - 3 51", + "8/8/2R2pk1/p6p/7P/4NK2/rb6/8 b - - 5 52", + "2R5/8/5pk1/7p/p6P/4NK2/rb6/8 b - - 1 53", + "6R1/8/5pk1/7p/p6P/4NK2/1b6/r7 b - - 3 54", + "R7/5k2/5p2/7p/p6P/4NK2/1b6/r7 b - - 5 55", + "R7/5k2/5p2/7p/7P/p3N3/1b2K3/r7 b - - 1 56", + "8/R4k2/5p2/7p/7P/p3N3/1b2K3/7r b - - 3 57", + "8/8/5pk1/7p/R6P/p3N3/1b2K3/7r b - - 5 58", + "8/8/5pk1/7p/R6P/p7/4K3/2bN3r b - - 7 59", + "8/8/5pk1/7p/R6P/p7/4KN1r/2b5 b - - 9 60", + "8/8/5pk1/7p/R6P/p3K3/1b3N1r/8 b - - 11 61", + "8/8/R4pk1/7p/7P/p1b1K3/5N1r/8 b - - 13 62", + "8/8/5pk1/7p/7P/2b1K3/R4N1r/8 b - - 0 63", + "8/8/5pk1/7p/3K3P/8/R4N1r/4b3 b - - 2 64", + } +}; +// clang-format on + } // namespace namespace Stockfish::Benchmark { @@ -160,4 +437,76 @@ std::vector setup_bench(const std::string& currentFen, std::istream return list; } +BenchmarkSetup setup_benchmark(std::istream& is) { + // TT_SIZE_PER_THREAD is chosen such that roughly half of the hash is used all positions + // for the current sequence have been searched. + static constexpr int TT_SIZE_PER_THREAD = 128; + + static constexpr int DEFAULT_DURATION_S = 150; + + BenchmarkSetup setup{}; + + // Assign default values to missing arguments + int desiredTimeS; + + if (!(is >> setup.threads)) + setup.threads = get_hardware_concurrency(); + else + setup.originalInvocation += std::to_string(setup.threads); + + if (!(is >> setup.ttSize)) + setup.ttSize = TT_SIZE_PER_THREAD * setup.threads; + else + setup.originalInvocation += " " + std::to_string(setup.ttSize); + + if (!(is >> desiredTimeS)) + desiredTimeS = DEFAULT_DURATION_S; + else + setup.originalInvocation += " " + std::to_string(desiredTimeS); + + setup.filledInvocation += std::to_string(setup.threads) + " " + std::to_string(setup.ttSize) + + " " + std::to_string(desiredTimeS); + + auto getCorrectedTime = [&](int ply) { + // time per move is fit roughly based on LTC games + // seconds = 50/{ply+15} + // ms = 50000/{ply+15} + // with this fit 10th move gets 2000ms + // adjust for desired 10th move time + return 50000.0 / (static_cast(ply) + 15.0); + }; + + float totalTime = 0; + for (const auto& game : BenchmarkPositions) + { + setup.commands.emplace_back("ucinewgame"); + int ply = 1; + for (int i = 0; i < static_cast(game.size()); ++i) + { + const float correctedTime = getCorrectedTime(ply); + totalTime += correctedTime; + ply += 1; + } + } + + float timeScaleFactor = static_cast(desiredTimeS * 1000) / totalTime; + + for (const auto& game : BenchmarkPositions) + { + setup.commands.emplace_back("ucinewgame"); + int ply = 1; + for (const std::string& fen : game) + { + setup.commands.emplace_back("position fen " + fen); + + const int correctedTime = static_cast(getCorrectedTime(ply) * timeScaleFactor); + setup.commands.emplace_back("go movetime " + std::to_string(correctedTime)); + + ply += 1; + } + } + + return setup; +} + } // namespace Stockfish \ No newline at end of file diff --git a/src/benchmark.h b/src/benchmark.h index b1eba40f38b..eb3a52d894d 100644 --- a/src/benchmark.h +++ b/src/benchmark.h @@ -27,6 +27,16 @@ namespace Stockfish::Benchmark { std::vector setup_bench(const std::string&, std::istream&); +struct BenchmarkSetup { + int ttSize; + int threads; + std::vector commands; + std::string originalInvocation; + std::string filledInvocation; +}; + +BenchmarkSetup setup_benchmark(std::istream&); + } // namespace Stockfish #endif // #ifndef BENCHMARK_H_INCLUDED diff --git a/src/engine.cpp b/src/engine.cpp index b5cc3f832f5..85c84099352 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -67,12 +67,13 @@ Engine::Engine(std::optional path) : options["NumaPolicy"] << Option("auto", [this](const Option& o) { set_numa_config_from_option(o); - return numa_config_information_as_string() + "\n" + thread_binding_information_as_string(); + return numa_config_information_as_string() + "\n" + + thread_allocation_information_as_string(); }); options["Threads"] << Option(1, 1, 1024, [this](const Option&) { resize_threads(); - return thread_binding_information_as_string(); + return thread_allocation_information_as_string(); }); options["Hash"] << Option(16, 1, MaxHashMB, [this](const Option& o) { @@ -156,6 +157,10 @@ void Engine::set_on_bestmove(std::function&& f) { + onVerifyNetworks = std::move(f); +} + void Engine::wait_for_search_finished() { threads.main_thread()->wait_for_search_finished(); } void Engine::set_position(const std::string& fen, const std::vector& moves) { @@ -226,8 +231,8 @@ void Engine::set_ponderhit(bool b) { threads.main_manager()->ponder = b; } // network related void Engine::verify_networks() const { - networks->big.verify(options["EvalFile"]); - networks->small.verify(options["EvalFileSmall"]); + networks->big.verify(options["EvalFile"], onVerifyNetworks); + networks->small.verify(options["EvalFileSmall"], onVerifyNetworks); } void Engine::load_networks() { @@ -285,6 +290,8 @@ std::string Engine::visualize() const { return ss.str(); } +int Engine::get_hashfull(int maxAge) const { return tt.hashfull(maxAge); } + std::vector> Engine::get_bound_thread_count_by_numa_node() const { auto counts = threads.get_bound_thread_count_by_numa_node(); const NumaConfig& cfg = numaContext.get_numa_config(); @@ -310,15 +317,9 @@ std::string Engine::numa_config_information_as_string() const { std::string Engine::thread_binding_information_as_string() const { auto boundThreadsByNode = get_bound_thread_count_by_numa_node(); std::stringstream ss; - - size_t threadsSize = threads.size(); - ss << "Using " << threadsSize << (threadsSize > 1 ? " threads" : " thread"); - if (boundThreadsByNode.empty()) return ss.str(); - ss << " with NUMA node thread binding: "; - bool isFirst = true; for (auto&& [current, total] : boundThreadsByNode) @@ -332,4 +333,20 @@ std::string Engine::thread_binding_information_as_string() const { return ss.str(); } +std::string Engine::thread_allocation_information_as_string() const { + std::stringstream ss; + + size_t threadsSize = threads.size(); + ss << "Using " << threadsSize << (threadsSize > 1 ? " threads" : " thread"); + + auto boundThreadsByNodeStr = thread_binding_information_as_string(); + if (boundThreadsByNodeStr.empty()) + return ss.str(); + + ss << " with NUMA node thread binding: "; + ss << boundThreadsByNodeStr; + + return ss.str(); +} + } diff --git a/src/engine.h b/src/engine.h index efab1c6af83..257826935d9 100644 --- a/src/engine.h +++ b/src/engine.h @@ -81,6 +81,7 @@ class Engine { void set_on_update_full(std::function&&); void set_on_iter(std::function&&); void set_on_bestmove(std::function&&); + void set_on_verify_networks(std::function&&); // network related @@ -97,12 +98,15 @@ class Engine { const OptionsMap& get_options() const; OptionsMap& get_options(); + int get_hashfull(int maxAge = 0) const; + std::string fen() const; void flip(); std::string visualize() const; std::vector> get_bound_thread_count_by_numa_node() const; std::string get_numa_config_as_string() const; std::string numa_config_information_as_string() const; + std::string thread_allocation_information_as_string() const; std::string thread_binding_information_as_string() const; private: @@ -119,7 +123,8 @@ class Engine { TranspositionTable tt; LazyNumaReplicated networks; - Search::SearchManager::UpdateContext updateContext; + Search::SearchManager::UpdateContext updateContext; + std::function onVerifyNetworks; }; } // namespace Stockfish diff --git a/src/memory.cpp b/src/memory.cpp index ae303c5377a..47c901b4e33 100644 --- a/src/memory.cpp +++ b/src/memory.cpp @@ -212,6 +212,37 @@ void* aligned_large_pages_alloc(size_t allocSize) { #endif +bool has_large_pages() { + +#if defined(_WIN32) + + constexpr size_t page_size = 2 * 1024 * 1024; // 2MB page size assumed + void* mem = aligned_large_pages_alloc_windows(page_size); + if (mem == nullptr) + { + return false; + } + else + { + aligned_large_pages_free(mem); + return true; + } + +#elif defined(__linux__) + + #if defined(MADV_HUGEPAGE) + return true; + #else + return false; + #endif + +#else + + return false; + +#endif +} + // aligned_large_pages_free() will free the previously memory allocated // by aligned_large_pages_alloc(). The effect is a nop if mem == nullptr. diff --git a/src/memory.h b/src/memory.h index 3155a5aab12..eaf0261aa2f 100644 --- a/src/memory.h +++ b/src/memory.h @@ -38,6 +38,8 @@ void std_aligned_free(void* ptr); void* aligned_large_pages_alloc(size_t size); void aligned_large_pages_free(void* mem); +bool has_large_pages(); + // Frees memory which was placed there with placement new. // Works for both single objects and arrays of unknown bound. template diff --git a/src/misc.cpp b/src/misc.cpp index 664ab4b89ff..10c86b7a6e7 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -122,7 +122,7 @@ class Logger { // // For releases (non-dev builds) we only include the version number: // Stockfish version -std::string engine_info(bool to_uci) { +std::string engine_version_info() { std::stringstream ss; ss << "Stockfish " << version << std::setfill('0'); @@ -151,11 +151,14 @@ std::string engine_info(bool to_uci) { #endif } - ss << (to_uci ? "\nid author " : " by ") << "the Stockfish developers (see AUTHORS file)"; - return ss.str(); } +std::string engine_info(bool to_uci) { + return engine_version_info() + (to_uci ? "\nid author " : " by ") + + "the Stockfish developers (see AUTHORS file)"; +} + // Returns a string trying to describe the compiler we use std::string compiler_info() { @@ -451,7 +454,7 @@ void remove_whitespace(std::string& s) { s.erase(std::remove_if(s.begin(), s.end(), [](char c) { return std::isspace(c); }), s.end()); } -bool is_whitespace(const std::string& s) { +bool is_whitespace(std::string_view s) { return std::all_of(s.begin(), s.end(), [](char c) { return std::isspace(c); }); } diff --git a/src/misc.h b/src/misc.h index ce49a1f6553..21093769b76 100644 --- a/src/misc.h +++ b/src/misc.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #define stringify2(x) #x @@ -35,6 +36,7 @@ namespace Stockfish { +std::string engine_version_info(); std::string engine_info(bool to_uci = false); std::string compiler_info(); @@ -79,8 +81,8 @@ inline TimePoint now() { .count(); } -inline std::vector split(const std::string& s, const std::string& delimiter) { - std::vector res; +inline std::vector split(std::string_view s, std::string_view delimiter) { + std::vector res; if (s.empty()) return res; @@ -102,7 +104,7 @@ inline std::vector split(const std::string& s, const std::string& d } void remove_whitespace(std::string& s); -bool is_whitespace(const std::string& s); +bool is_whitespace(std::string_view s); enum SyncCout { IO_LOCK, diff --git a/src/nnue/network.cpp b/src/nnue/network.cpp index f7d2cc6ada0..a8e901a0d32 100644 --- a/src/nnue/network.cpp +++ b/src/nnue/network.cpp @@ -234,35 +234,44 @@ Network::evaluate(const Position& pos template -void Network::verify(std::string evalfilePath) const { +void Network::verify(std::string evalfilePath, + const std::function& f) const { if (evalfilePath.empty()) evalfilePath = evalFile.defaultName; if (evalFile.current != evalfilePath) { - std::string msg1 = - "Network evaluation parameters compatible with the engine must be available."; - std::string msg2 = "The network file " + evalfilePath + " was not loaded successfully."; - std::string msg3 = "The UCI option EvalFile might need to specify the full path, " - "including the directory name, to the network file."; - std::string msg4 = "The default net can be downloaded from: " - "https://tests.stockfishchess.org/api/nn/" - + evalFile.defaultName; - std::string msg5 = "The engine will be terminated now."; - - sync_cout << "info string ERROR: " << msg1 << sync_endl; - sync_cout << "info string ERROR: " << msg2 << sync_endl; - sync_cout << "info string ERROR: " << msg3 << sync_endl; - sync_cout << "info string ERROR: " << msg4 << sync_endl; - sync_cout << "info string ERROR: " << msg5 << sync_endl; + if (f) + { + std::string msg1 = + "Network evaluation parameters compatible with the engine must be available."; + std::string msg2 = "The network file " + evalfilePath + " was not loaded successfully."; + std::string msg3 = "The UCI option EvalFile might need to specify the full path, " + "including the directory name, to the network file."; + std::string msg4 = "The default net can be downloaded from: " + "https://tests.stockfishchess.org/api/nn/" + + evalFile.defaultName; + std::string msg5 = "The engine will be terminated now."; + + std::string msg = "ERROR: " + msg1 + '\n' + "ERROR: " + msg2 + '\n' + "ERROR: " + msg3 + + '\n' + "ERROR: " + msg4 + '\n' + "ERROR: " + msg5 + '\n'; + + f(msg); + } + exit(EXIT_FAILURE); } - size_t size = sizeof(*featureTransformer) + sizeof(Arch) * LayerStacks; - sync_cout << "info string NNUE evaluation using " << evalfilePath << " (" - << size / (1024 * 1024) << "MiB, (" << featureTransformer->InputDimensions << ", " - << network[0].TransformedFeatureDimensions << ", " << network[0].FC_0_OUTPUTS << ", " - << network[0].FC_1_OUTPUTS << ", 1))" << sync_endl; + if (f) + { + size_t size = sizeof(*featureTransformer) + sizeof(Arch) * LayerStacks; + f("info string NNUE evaluation using " + evalfilePath + " (" + + std::to_string(size / (1024 * 1024)) + "MiB, (" + + std::to_string(featureTransformer->InputDimensions) + ", " + + std::to_string(network[0].TransformedFeatureDimensions) + ", " + + std::to_string(network[0].FC_0_OUTPUTS) + ", " + std::to_string(network[0].FC_1_OUTPUTS) + + ", 1))"); + } } diff --git a/src/nnue/network.h b/src/nnue/network.h index 152082552c9..95253595a2c 100644 --- a/src/nnue/network.h +++ b/src/nnue/network.h @@ -20,9 +20,11 @@ #define NETWORK_H_INCLUDED #include +#include #include #include #include +#include #include #include @@ -68,7 +70,7 @@ class Network { void hint_common_access(const Position& pos, AccumulatorCaches::Cache* cache) const; - void verify(std::string evalfilePath) const; + void verify(std::string evalfilePath, const std::function&) const; NnueEvalTrace trace_evaluate(const Position& pos, AccumulatorCaches::Cache* cache) const; diff --git a/src/numa.h b/src/numa.h index db8359222cd..1063721e3fc 100644 --- a/src/numa.h +++ b/src/numa.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -653,7 +654,7 @@ class NumaConfig { NumaIndex n = 0; for (auto&& nodeStr : split(s, ":")) { - auto indices = indices_from_shortened_string(nodeStr); + auto indices = indices_from_shortened_string(std::string(nodeStr)); if (!indices.empty()) { for (auto idx : indices) @@ -1015,7 +1016,7 @@ class NumaConfig { if (s.empty()) return indices; - for (const std::string& ss : split(s, ",")) + for (const auto& ss : split(s, ",")) { if (ss.empty()) continue; @@ -1023,13 +1024,13 @@ class NumaConfig { auto parts = split(ss, "-"); if (parts.size() == 1) { - const CpuIndex c = CpuIndex{str_to_size_t(parts[0])}; + const CpuIndex c = CpuIndex{str_to_size_t(std::string(parts[0]))}; indices.emplace_back(c); } else if (parts.size() == 2) { - const CpuIndex cfirst = CpuIndex{str_to_size_t(parts[0])}; - const CpuIndex clast = CpuIndex{str_to_size_t(parts[1])}; + const CpuIndex cfirst = CpuIndex{str_to_size_t(std::string(parts[0]))}; + const CpuIndex clast = CpuIndex{str_to_size_t(std::string(parts[1]))}; for (size_t c = cfirst; c <= clast; ++c) { indices.emplace_back(c); diff --git a/src/tt.cpp b/src/tt.cpp index 4b55e53fdfc..507507539e5 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -193,13 +193,20 @@ void TranspositionTable::clear(ThreadPool& threads) { // Returns an approximation of the hashtable // occupation during a search. The hash is x permill full, as per UCI protocol. // Only counts entries which match the current generation. -int TranspositionTable::hashfull() const { - +int TranspositionTable::hashfull(int maxAge) const { int cnt = 0; for (int i = 0; i < 1000; ++i) for (int j = 0; j < ClusterSize; ++j) - cnt += table[i].entry[j].is_occupied() - && (table[i].entry[j].genBound8 & GENERATION_MASK) == generation8; + { + if (table[i].entry[j].is_occupied()) + { + int age = (generation8 >> GENERATION_BITS) + - ((table[i].entry[j].genBound8 & GENERATION_MASK) >> GENERATION_BITS); + if (age < 0) + age += 1 << (8 - GENERATION_BITS); + cnt += age <= maxAge; + } + } return cnt / ClusterSize; } diff --git a/src/tt.h b/src/tt.h index 1bece002c7b..e7bb5c452b4 100644 --- a/src/tt.h +++ b/src/tt.h @@ -73,7 +73,7 @@ class TranspositionTable { void resize(size_t mbSize, ThreadPool& threads); // Set TT size void clear(ThreadPool& threads); // Re-initialize memory, multithreaded - int hashfull() + int hashfull(int maxAge = 0) const; // Approximate what fraction of entries (permille) have been written to during this root search void diff --git a/src/uci.cpp b/src/uci.cpp index c94f8b914cd..cfb34db791f 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,7 @@ #include "benchmark.h" #include "engine.h" +#include "memory.h" #include "movegen.h" #include "position.h" #include "score.h" @@ -39,6 +41,8 @@ namespace Stockfish { +constexpr auto BenchmarkCommand = "speedtest"; + constexpr auto StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; template struct overload: Ts... { @@ -48,7 +52,7 @@ struct overload: Ts... { template overload(Ts...) -> overload; -void UCIEngine::print_info_string(const std::string& str) { +void UCIEngine::print_info_string(std::string_view str) { sync_cout_start(); for (auto& line : split(str, "\n")) { @@ -69,11 +73,16 @@ UCIEngine::UCIEngine(int argc, char** argv) : print_info_string(*str); }); + init_search_update_listeners(); +} + +void UCIEngine::init_search_update_listeners() { engine.set_on_iter([](const auto& i) { on_iter(i); }); engine.set_on_update_no_moves([](const auto& i) { on_update_no_moves(i); }); engine.set_on_update_full( [this](const auto& i) { on_update_full(i, engine.get_options()["UCI_ShowWDL"]); }); engine.set_on_bestmove([](const auto& bm, const auto& p) { on_bestmove(bm, p); }); + engine.set_on_verify_networks([](const auto& s) { print_info_string(s); }); } void UCIEngine::loop() { @@ -117,7 +126,7 @@ void UCIEngine::loop() { { // send info strings after the go command is sent for old GUIs and python-chess print_info_string(engine.numa_config_information_as_string()); - print_info_string(engine.thread_binding_information_as_string()); + print_info_string(engine.thread_allocation_information_as_string()); go(is); } else if (token == "position") @@ -133,6 +142,8 @@ void UCIEngine::loop() { engine.flip(); else if (token == "bench") bench(is); + else if (token == BenchmarkCommand) + benchmark(is); else if (token == "d") sync_cout << engine.visualize() << sync_endl; else if (token == "eval") @@ -285,6 +296,165 @@ void UCIEngine::bench(std::istream& args) { engine.set_on_update_full([&](const auto& i) { on_update_full(i, options["UCI_ShowWDL"]); }); } +void UCIEngine::benchmark(std::istream& args) { + // Probably not very important for a test this long, but include for completeness and sanity. + static constexpr int NUM_WARMUP_POSITIONS = 3; + + std::string token; + uint64_t nodes = 0, cnt = 1; + uint64_t nodesSearched = 0; + + engine.set_on_update_full([&](const Engine::InfoFull& i) { nodesSearched = i.nodes; }); + + engine.set_on_iter([](const auto&) {}); + engine.set_on_update_no_moves([](const auto&) {}); + engine.set_on_bestmove([](const auto&, const auto&) {}); + engine.set_on_verify_networks([](const auto&) {}); + + Benchmark::BenchmarkSetup setup = Benchmark::setup_benchmark(args); + + const int numGoCommands = count_if(setup.commands.begin(), setup.commands.end(), + [](const std::string& s) { return s.find("go ") == 0; }); + + TimePoint totalTime = 0; + + // Set options once at the start. + auto ss = std::istringstream("name Threads value " + std::to_string(setup.threads)); + setoption(ss); + ss = std::istringstream("name Hash value " + std::to_string(setup.ttSize)); + setoption(ss); + ss = std::istringstream("name UCI_Chess960 value false"); + setoption(ss); + + // Warmup + for (const auto& cmd : setup.commands) + { + std::istringstream is(cmd); + is >> std::skipws >> token; + + if (token == "go") + { + // One new line is produced by the search, so omit it here + std::cerr << "\rWarmup position " << cnt++ << '/' << NUM_WARMUP_POSITIONS; + + Search::LimitsType limits = parse_limits(is); + + TimePoint elapsed = now(); + + // Run with silenced network verification + engine.go(limits); + engine.wait_for_search_finished(); + + totalTime += now() - elapsed; + + nodes += nodesSearched; + nodesSearched = 0; + } + else if (token == "position") + position(is); + else if (token == "ucinewgame") + { + engine.search_clear(); // search_clear may take a while + } + + if (cnt > NUM_WARMUP_POSITIONS) + break; + } + + std::cerr << "\n"; + + cnt = 1; + nodes = 0; + + int numHashfullReadings = 0; + constexpr int hashfullAges[] = {0, 999}; // Only normal hashfull and touched hash. + int totalHashfull[std::size(hashfullAges)] = {0}; + int maxHashfull[std::size(hashfullAges)] = {0}; + + auto updateHashfullReadings = [&]() { + numHashfullReadings += 1; + + for (int i = 0; i < static_cast(std::size(hashfullAges)); ++i) + { + const int hashfull = engine.get_hashfull(hashfullAges[i]); + maxHashfull[i] = std::max(maxHashfull[i], hashfull); + totalHashfull[i] += hashfull; + } + }; + + engine.search_clear(); // search_clear may take a while + + for (const auto& cmd : setup.commands) + { + std::istringstream is(cmd); + is >> std::skipws >> token; + + if (token == "go") + { + // One new line is produced by the search, so omit it here + std::cerr << "\rPosition " << cnt++ << '/' << numGoCommands; + + Search::LimitsType limits = parse_limits(is); + + TimePoint elapsed = now(); + + // Run with silenced network verification + engine.go(limits); + engine.wait_for_search_finished(); + + totalTime += now() - elapsed; + + updateHashfullReadings(); + + nodes += nodesSearched; + nodesSearched = 0; + } + else if (token == "position") + position(is); + else if (token == "ucinewgame") + { + engine.search_clear(); // search_clear may take a while + } + } + + totalTime = std::max(totalTime, 1); // Ensure positivity to avoid a 'divide by zero' + + dbg_print(); + + std::cerr << "\n"; + + static_assert( + std::size(hashfullAges) == 2 && hashfullAges[0] == 0 && hashfullAges[1] == 999, + "Hardcoded for display. Would complicate the code needlessly in the current state."); + + std::string threadBinding = engine.thread_binding_information_as_string(); + if (threadBinding.empty()) + threadBinding = "none"; + + std::cerr << "===========================" + << "\nVersion : " + << engine_version_info() + // "\nCompiled by : " + << compiler_info() + << "Large pages : " << (has_large_pages() ? "yes" : "no") + << "\nUser invocation : " << BenchmarkCommand << " " + << setup.originalInvocation << "\nFilled invocation : " << BenchmarkCommand + << " " << setup.filledInvocation + << "\nAvailable processors : " << engine.get_numa_config_as_string() + << "\nThread count : " << setup.threads + << "\nThread binding : " << threadBinding + << "\nTT size [MiB] : " << setup.ttSize + << "\nHash max, avg [per mille] : " + << "\n single search : " << maxHashfull[0] << ", " + << totalHashfull[0] / numHashfullReadings + << "\n single game : " << maxHashfull[1] << ", " + << totalHashfull[1] / numHashfullReadings + << "\nTotal nodes searched : " << nodes + << "\nTotal search time [s] : " << totalTime / 1000.0 + << "\nNodes/second : " << 1000 * nodes / totalTime << std::endl; + + init_search_update_listeners(); +} void UCIEngine::setoption(std::istringstream& is) { engine.wait_for_search_finished(); diff --git a/src/uci.h b/src/uci.h index 23745f96a96..6adf74cb85a 100644 --- a/src/uci.h +++ b/src/uci.h @@ -58,10 +58,11 @@ class UCIEngine { Engine engine; CommandLine cli; - static void print_info_string(const std::string& str); + static void print_info_string(std::string_view str); void go(std::istringstream& is); void bench(std::istream& args); + void benchmark(std::istream& args); void position(std::istringstream& is); void setoption(std::istringstream& is); std::uint64_t perft(const Search::LimitsType&); @@ -70,6 +71,8 @@ class UCIEngine { static void on_update_full(const Engine::InfoFull& info, bool showWDL); static void on_iter(const Engine::InfoIter& info); static void on_bestmove(std::string_view bestmove, std::string_view ponder); + + void init_search_update_listeners(); }; } // namespace Stockfish From 56444ce1f7e2204d69c35f5826f74130adc77b2c Mon Sep 17 00:00:00 2001 From: peregrineshahin <41402573+peregrineshahin@users.noreply.github.com> Date: Fri, 20 Sep 2024 14:09:03 +0300 Subject: [PATCH 1727/1766] Push expected cutting late moves up in the move ordering. since the passing of the LMR verification is coming from a relatively late move this means we have wasted some time trying/picking other moves, and it would make sense to push it up in the move ordering for future positions not to be as late. Passed STC: https://tests.stockfishchess.org/tests/view/66f0f69186d5ee47d953b2aa LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 34144 W: 9024 L: 8709 D: 16411 Ptnml(0-2): 137, 3875, 8732, 4192, 136 Passed LTC: https://tests.stockfishchess.org/tests/view/66f1d84a86d5ee47d953b325 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 62808 W: 16054 L: 15684 D: 31070 Ptnml(0-2): 24, 6725, 17555, 7057, 43 closes https://github.com/official-stockfish/Stockfish/pull/5608 bench: 1452807 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index d87a6b9a041..7d84bd388c0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1201,8 +1201,8 @@ Value Search::Worker::search( value = -search(pos, ss + 1, -(alpha + 1), -alpha, newDepth, !cutNode); // Post LMR continuation history updates (~1 Elo) - int bonus = value >= beta ? stat_bonus(newDepth) : -stat_malus(newDepth); - + int bonus = value >= beta ? (1 + 2 * (moveCount > depth)) * stat_bonus(newDepth) + : -stat_malus(newDepth); update_continuation_histories(ss, movedPiece, move.to_sq(), bonus); } } From d6043970bd156b1d2ab6cb51e8d5cb0c6a40797c Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Tue, 17 Sep 2024 14:29:55 -0700 Subject: [PATCH 1728/1766] Make Correction History Size Uniform Passed Non-regression STC: LLR: 2.93 (-2.94,2.94) <-1.75,0.25> Total: 207232 W: 53834 L: 53802 D: 99596 Ptnml(0-2): 695, 24486, 53200, 24562, 673 https://tests.stockfishchess.org/tests/view/66e9f5a886d5ee47d953ada1 Passed Non-regression LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 99120 W: 25264 L: 25123 D: 48733 Ptnml(0-2): 66, 10803, 27675, 10956, 60 https://tests.stockfishchess.org/tests/view/66ed7ebc86d5ee47d953b056 Passed Non-regression LTC vs #5606: LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 208950 W: 53049 L: 53019 D: 102882 Ptnml(0-2): 111, 23232, 57760, 23260, 112 https://tests.stockfishchess.org/tests/view/66f1843886d5ee47d953b2f2 closes https://github.com/official-stockfish/Stockfish/pull/5609 bench 1575189 --- src/movepick.h | 32 ++++++++++++++------------------ src/search.cpp | 3 +-- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/src/movepick.h b/src/movepick.h index 13b9635bb2a..c5e565fe448 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -34,18 +34,14 @@ namespace Stockfish { -constexpr int PAWN_HISTORY_SIZE = 512; // has to be a power of 2 -constexpr int PAWN_CORRECTION_HISTORY_SIZE = 16384; // has to be a power of 2 -constexpr int MATERIAL_CORRECTION_HISTORY_SIZE = 32768; // has to be a power of 2 -constexpr int MAJOR_PIECE_CORRECTION_HISTORY_SIZE = 32768; // has to be a power of 2 -constexpr int MINOR_PIECE_CORRECTION_HISTORY_SIZE = 32768; // has to be a power of 2 -constexpr int NON_PAWN_CORRECTION_HISTORY_SIZE = 32768; // has to be a power of 2 -constexpr int CORRECTION_HISTORY_LIMIT = 1024; +constexpr int PAWN_HISTORY_SIZE = 512; // has to be a power of 2 +constexpr int CORRECTION_HISTORY_SIZE = 32768; // has to be a power of 2 +constexpr int CORRECTION_HISTORY_LIMIT = 1024; static_assert((PAWN_HISTORY_SIZE & (PAWN_HISTORY_SIZE - 1)) == 0, "PAWN_HISTORY_SIZE has to be a power of 2"); -static_assert((PAWN_CORRECTION_HISTORY_SIZE & (PAWN_CORRECTION_HISTORY_SIZE - 1)) == 0, +static_assert((CORRECTION_HISTORY_SIZE & (CORRECTION_HISTORY_SIZE - 1)) == 0, "CORRECTION_HISTORY_SIZE has to be a power of 2"); enum PawnHistoryType { @@ -55,24 +51,24 @@ enum PawnHistoryType { template inline int pawn_structure_index(const Position& pos) { - return pos.pawn_key() & ((T == Normal ? PAWN_HISTORY_SIZE : PAWN_CORRECTION_HISTORY_SIZE) - 1); + return pos.pawn_key() & ((T == Normal ? PAWN_HISTORY_SIZE : CORRECTION_HISTORY_SIZE) - 1); } inline int material_index(const Position& pos) { - return pos.material_key() & (MATERIAL_CORRECTION_HISTORY_SIZE - 1); + return pos.material_key() & (CORRECTION_HISTORY_SIZE - 1); } inline int major_piece_index(const Position& pos) { - return pos.major_piece_key() & (MAJOR_PIECE_CORRECTION_HISTORY_SIZE - 1); + return pos.major_piece_key() & (CORRECTION_HISTORY_SIZE - 1); } inline int minor_piece_index(const Position& pos) { - return pos.minor_piece_key() & (MINOR_PIECE_CORRECTION_HISTORY_SIZE - 1); + return pos.minor_piece_key() & (CORRECTION_HISTORY_SIZE - 1); } template inline int non_pawn_index(const Position& pos) { - return pos.non_pawn_key(c) & (NON_PAWN_CORRECTION_HISTORY_SIZE - 1); + return pos.non_pawn_key(c) & (CORRECTION_HISTORY_SIZE - 1); } // StatsEntry stores the stat table value. It is usually a number but could @@ -161,23 +157,23 @@ using PawnHistory = Stats // PawnCorrectionHistory is addressed by color and pawn structure using PawnCorrectionHistory = - Stats; + Stats; // MaterialCorrectionHistory is addressed by color and material configuration using MaterialCorrectionHistory = - Stats; + Stats; // MajorPieceCorrectionHistory is addressed by color and king/major piece (Queen, Rook) positions using MajorPieceCorrectionHistory = - Stats; + Stats; // MinorPieceCorrectionHistory is addressed by color and king/minor piece (Knight, Bishop) positions using MinorPieceCorrectionHistory = - Stats; + Stats; // NonPawnCorrectionHistory is addressed by color and non-pawn material positions using NonPawnCorrectionHistory = - Stats; + Stats; // The MovePicker class is used to pick one pseudo-legal move at a time from the // current position. The most important method is next_move(), which emits one diff --git a/src/search.cpp b/src/search.cpp index 7d84bd388c0..4d581a85093 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -88,8 +88,7 @@ Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { const auto wnpcv = w.nonPawnCorrectionHistory[WHITE][us][non_pawn_index(pos)]; const auto bnpcv = w.nonPawnCorrectionHistory[BLACK][us][non_pawn_index(pos)]; const auto cv = - (99916 * pcv + 55067 * mcv + 55530 * macv + 95324 * micv + 105056 * (wnpcv + bnpcv)) - / 2097152; + (6245 * pcv + 3442 * mcv + 3471 * macv + 5958 * micv + 6566 * (wnpcv + bnpcv)) / 131072; v += cv; return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } From c85f802185dd223bae1197269d17b9b1d5e935a0 Mon Sep 17 00:00:00 2001 From: Taras Vuk <117687515+TarasVuk@users.noreply.github.com> Date: Mon, 30 Sep 2024 18:58:48 +0200 Subject: [PATCH 1729/1766] Tweak ttCapture reduction More reduction at shallow depth for quiet moves when ttMove is a capture. Passed STC: LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 365728 W: 95896 L: 95090 D: 174742 Ptnml(0-2): 1283, 43133, 93262, 43867, 1319 https://tests.stockfishchess.org/tests/view/66edd35986d5ee47d953b0d5 Passed LTC: LLR: 2.96 (-2.94,2.94) <0.50,2.50> Total: 200526 W: 51197 L: 50540 D: 98789 Ptnml(0-2): 119, 21952, 55462, 22613, 117 https://tests.stockfishchess.org/tests/view/66f405dc86d5ee47d953b460 closes https://github.com/official-stockfish/Stockfish/pull/5610 bench: 1269487 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 4d581a85093..a206cddab3d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1157,7 +1157,7 @@ Value Search::Worker::search( // Increase reduction if ttMove is a capture but the current move is not a capture (~3 Elo) if (ttCapture && !capture) - r++; + r += 1 + (depth < 8); // Increase reduction if next ply has a lot of fail high (~5 Elo) if ((ss + 1)->cutoffCnt > 3) From 2b9154882a0e924c28d4de7b98309d889334428c Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Mon, 16 Sep 2024 01:49:04 -0400 Subject: [PATCH 1730/1766] Tweak 7 eval params Values found from 120k / 120k spsa games at 30+0.3 Passed STC: https://tests.stockfishchess.org/tests/view/66ecd7ce86d5ee47d953b003 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 241312 W: 62994 L: 62373 D: 115945 Ptnml(0-2): 754, 28684, 61280, 29063, 875 Passed LTC: https://tests.stockfishchess.org/tests/view/66f1f3a286d5ee47d953b331 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 304896 W: 77580 L: 76709 D: 150607 Ptnml(0-2): 198, 33413, 84360, 34274, 203 closes https://github.com/official-stockfish/Stockfish/pull/5611 bench 1173651 --- src/evaluate.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 221ccde8b8b..087765e36cb 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -77,11 +77,11 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, // Blend optimism and eval with nnue complexity int nnueComplexity = std::abs(psqt - positional); - optimism += optimism * nnueComplexity / (smallNet ? 433 : 453); - nnue -= nnue * nnueComplexity / (smallNet ? 18815 : 17864); + optimism += optimism * nnueComplexity / (smallNet ? 430 : 474); + nnue -= nnue * nnueComplexity / (smallNet ? 20233 : 17879); int material = (smallNet ? 553 : 532) * pos.count() + pos.non_pawn_material(); - v = (nnue * (73921 + material) + optimism * (8112 + material)) / (smallNet ? 68104 : 74715); + v = (nnue * (76898 + material) + optimism * (8112 + material)) / (smallNet ? 74411 : 76256); // Evaluation grain (to get more alpha-beta cuts) with randomization (for robustness) v = (v / 16) * 16 - 1 + (pos.key() & 0x2); From 0186904f53e6b9c90935cd8fe822da795ca9d333 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Sun, 29 Sep 2024 04:22:37 -0400 Subject: [PATCH 1731/1766] Remove evaluation grain Passed non-regression STC: https://tests.stockfishchess.org/tests/view/66fa345a86d5ee47d953b86e LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 39776 W: 10528 L: 10306 D: 18942 Ptnml(0-2): 134, 4674, 10063, 4870, 147 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/66facfb886d5ee47d953b8a8 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 64230 W: 16484 L: 16305 D: 31441 Ptnml(0-2): 38, 7195, 17483, 7348, 51 closes https://github.com/official-stockfish/Stockfish/pull/5613 bench 1013135 --- src/evaluate.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 087765e36cb..b1c7283e3e2 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -83,9 +83,6 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, int material = (smallNet ? 553 : 532) * pos.count() + pos.non_pawn_material(); v = (nnue * (76898 + material) + optimism * (8112 + material)) / (smallNet ? 74411 : 76256); - // Evaluation grain (to get more alpha-beta cuts) with randomization (for robustness) - v = (v / 16) * 16 - 1 + (pos.key() & 0x2); - // Damp down the evaluation linearly when shuffling v -= v * pos.rule50_count() / 212; From 7ac745a736a37f69632d6612d422aa3127f85509 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Wed, 2 Oct 2024 10:49:23 +0300 Subject: [PATCH 1732/1766] Refactor root history into low ply history This patch changes root history to low ply history - butterfly history for plies < 4. Doubles weight of this history for root, latter plies have lesser effect. Passed STC: https://tests.stockfishchess.org/tests/view/66f77d2386d5ee47d953b65d LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 180992 W: 47362 L: 46830 D: 86800 Ptnml(0-2): 554, 21499, 45928, 21891, 624 Passed LTC: https://tests.stockfishchess.org/tests/view/66fb557986d5ee47d953b8e5 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 42462 W: 11013 L: 10682 D: 20767 Ptnml(0-2): 33, 4518, 11795, 4855, 30 closes https://github.com/official-stockfish/Stockfish/pull/5614 Bench 1264335 --- src/movepick.cpp | 12 +++++----- src/movepick.h | 12 ++++++---- src/search.cpp | 62 +++++++++++++++++++----------------------------- src/search.h | 2 +- 4 files changed, 40 insertions(+), 48 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index f4ef0e5499b..1d1aef0f313 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -82,20 +82,20 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const ButterflyHistory* mh, - const ButterflyHistory* rh, + const LowPlyHistory* lph, const CapturePieceToHistory* cph, const PieceToHistory** ch, const PawnHistory* ph, - bool rn) : + int pl) : pos(p), mainHistory(mh), - rootHistory(rh), + lowPlyHistory(lph), captureHistory(cph), continuationHistory(ch), pawnHistory(ph), ttMove(ttm), depth(d), - rootNode(rn) { + ply(pl) { if (pos.checkers()) stage = EVASION_TT + !(ttm && pos.pseudo_legal(ttm)); @@ -179,8 +179,8 @@ void MovePicker::score() { : pt == ROOK && bool(to & threatenedByMinor) ? 24335 : 0); - if (rootNode) - m.value += 4 * (*rootHistory)[pos.side_to_move()][m.from_to()]; + if (ply < 4) + m.value += 8 * (*lowPlyHistory)[ply][m.from_to()] / (1 + 2 * ply); } else // Type == EVASIONS diff --git a/src/movepick.h b/src/movepick.h index c5e565fe448..8deefd140ff 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -135,6 +135,10 @@ enum StatsType { // see https://www.chessprogramming.org/Butterfly_Boards (~11 elo) using ButterflyHistory = Stats; +// LowPlyHistory is adressed by play and move's from and to squares, used +// to improve move ordering near the root +using LowPlyHistory = Stats; + // CapturePieceToHistory is addressed by a move's [piece][to][captured piece type] using CapturePieceToHistory = Stats; @@ -195,11 +199,11 @@ class MovePicker { Move, Depth, const ButterflyHistory*, - const ButterflyHistory*, + const LowPlyHistory*, const CapturePieceToHistory*, const PieceToHistory**, const PawnHistory*, - bool); + int); MovePicker(const Position&, Move, int, const CapturePieceToHistory*); Move next_move(bool skipQuiets = false); @@ -213,7 +217,7 @@ class MovePicker { const Position& pos; const ButterflyHistory* mainHistory; - const ButterflyHistory* rootHistory; + const LowPlyHistory* lowPlyHistory; const CapturePieceToHistory* captureHistory; const PieceToHistory** continuationHistory; const PawnHistory* pawnHistory; @@ -222,7 +226,7 @@ class MovePicker { int stage; int threshold; Depth depth; - bool rootNode; + int ply; ExtMove moves[MAX_MOVES]; }; diff --git a/src/search.cpp b/src/search.cpp index a206cddab3d..0ed7b6a76ef 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -105,21 +105,16 @@ Value value_to_tt(Value v, int ply); Value value_from_tt(Value v, int ply, int r50c); void update_pv(Move* pv, Move move, const Move* childPv); void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus); -void update_quiet_histories(const Position& pos, - Stack* ss, - Search::Worker& workerThread, - Move move, - int bonus, - bool rootNode); -void update_all_stats(const Position& pos, - Stack* ss, - Search::Worker& workerThread, - Move bestMove, - Square prevSq, - ValueList& quietsSearched, - ValueList& capturesSearched, - Depth depth, - bool rootNode); +void update_quiet_histories( + const Position& pos, Stack* ss, Search::Worker& workerThread, Move move, int bonus); +void update_all_stats(const Position& pos, + Stack* ss, + Search::Worker& workerThread, + Move bestMove, + Square prevSq, + ValueList& quietsSearched, + ValueList& capturesSearched, + Depth depth); } // namespace @@ -273,7 +268,7 @@ void Search::Worker::iterative_deepening() { int searchAgainCounter = 0; - rootHistory.fill(0); + lowPlyHistory.fill(0); // Iterative deepening loop until requested to stop or the target depth is reached while (++rootDepth < MAX_PLY && !threads.stop @@ -499,7 +494,7 @@ void Search::Worker::iterative_deepening() { // Reset histories, usually before a new game void Search::Worker::clear() { mainHistory.fill(0); - rootHistory.fill(0); + lowPlyHistory.fill(0); captureHistory.fill(-753); pawnHistory.fill(-1152); pawnCorrectionHistory.fill(0); @@ -638,7 +633,7 @@ Value Search::Worker::search( { // Bonus for a quiet ttMove that fails high (~2 Elo) if (!ttCapture) - update_quiet_histories(pos, ss, *this, ttData.move, stat_bonus(depth), rootNode); + update_quiet_histories(pos, ss, *this, ttData.move, stat_bonus(depth)); // Extra penalty for early quiet moves of // the previous ply (~1 Elo on STC, ~2 Elo on LTC) @@ -928,8 +923,8 @@ Value Search::Worker::search( (ss - 6)->continuationHistory}; - MovePicker mp(pos, ttData.move, depth, &thisThread->mainHistory, &thisThread->rootHistory, - &thisThread->captureHistory, contHist, &thisThread->pawnHistory, rootNode); + MovePicker mp(pos, ttData.move, depth, &thisThread->mainHistory, &thisThread->lowPlyHistory, + &thisThread->captureHistory, contHist, &thisThread->pawnHistory, ss->ply); value = bestValue; @@ -1355,8 +1350,7 @@ Value Search::Worker::search( // If there is a move that produces search value greater than alpha, // we update the stats of searched moves. else if (bestMove) - update_all_stats(pos, ss, *this, bestMove, prevSq, quietsSearched, capturesSearched, depth, - rootNode); + update_all_stats(pos, ss, *this, bestMove, prevSq, quietsSearched, capturesSearched, depth); // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) @@ -1557,9 +1551,8 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) // Initialize a MovePicker object for the current position, and prepare to search // the moves. We presently use two stages of move generator in quiescence search: // captures, or evasions only when in check. - MovePicker mp(pos, ttData.move, DEPTH_QS, &thisThread->mainHistory, &thisThread->rootHistory, - &thisThread->captureHistory, contHist, &thisThread->pawnHistory, - nodeType == Root); + MovePicker mp(pos, ttData.move, DEPTH_QS, &thisThread->mainHistory, &thisThread->lowPlyHistory, + &thisThread->captureHistory, contHist, &thisThread->pawnHistory, ss->ply); // Step 5. Loop through all pseudo-legal moves until no moves remain or a beta // cutoff occurs. @@ -1768,8 +1761,7 @@ void update_all_stats(const Position& pos, Square prevSq, ValueList& quietsSearched, ValueList& capturesSearched, - Depth depth, - bool rootNode) { + Depth depth) { CapturePieceToHistory& captureHistory = workerThread.captureHistory; Piece moved_piece = pos.moved_piece(bestMove); @@ -1780,11 +1772,11 @@ void update_all_stats(const Position& pos, if (!pos.capture_stage(bestMove)) { - update_quiet_histories(pos, ss, workerThread, bestMove, quietMoveBonus, rootNode); + update_quiet_histories(pos, ss, workerThread, bestMove, quietMoveBonus); // Decrease stats for all non-best quiet moves for (Move move : quietsSearched) - update_quiet_histories(pos, ss, workerThread, move, -quietMoveMalus, rootNode); + update_quiet_histories(pos, ss, workerThread, move, -quietMoveMalus); } else { @@ -1826,17 +1818,13 @@ void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { // Updates move sorting heuristics -void update_quiet_histories(const Position& pos, - Stack* ss, - Search::Worker& workerThread, - Move move, - int bonus, - bool rootNode) { +void update_quiet_histories( + const Position& pos, Stack* ss, Search::Worker& workerThread, Move move, int bonus) { Color us = pos.side_to_move(); workerThread.mainHistory[us][move.from_to()] << bonus; - if (rootNode) - workerThread.rootHistory[us][move.from_to()] << bonus; + if (ss->ply < 4) + workerThread.lowPlyHistory[ss->ply][move.from_to()] << bonus; update_continuation_histories(ss, pos.moved_piece(move), move.to_sq(), bonus); diff --git a/src/search.h b/src/search.h index d7a909a82a8..0761328a758 100644 --- a/src/search.h +++ b/src/search.h @@ -278,7 +278,7 @@ class Worker { // Public because they need to be updatable by the stats ButterflyHistory mainHistory; - ButterflyHistory rootHistory; + LowPlyHistory lowPlyHistory; CapturePieceToHistory captureHistory; ContinuationHistory continuationHistory[2][2]; From 81c1d310844e7b41caeabda0d5351ae275d799db Mon Sep 17 00:00:00 2001 From: Taras Vuk <117687515+TarasVuk@users.noreply.github.com> Date: Wed, 2 Oct 2024 15:55:59 +0200 Subject: [PATCH 1733/1766] Decrease probCutBeta based on opponentWorsening Passed STC: LLR: 2.97 (-2.94,2.94) <0.00,2.00> Total: 62112 W: 16305 L: 15947 D: 29860 Ptnml(0-2): 203, 7226, 15856, 7552, 219 https://tests.stockfishchess.org/tests/view/66f85fc986d5ee47d953b71e Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 129552 W: 33223 L: 32710 D: 63619 Ptnml(0-2): 94, 14250, 35573, 14767, 92 https://tests.stockfishchess.org/tests/view/66f93fef86d5ee47d953b7d2 closes https://github.com/official-stockfish/Stockfish/pull/5615 bench: 1511354 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 0ed7b6a76ef..d79b452d9b4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -840,7 +840,7 @@ Value Search::Worker::search( // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search // returns a value much above beta, we can (almost) safely prune the previous move. - probCutBeta = beta + 189 - 53 * improving; + probCutBeta = beta + 189 - 53 * improving - 30 * opponentWorsening; if (!PvNode && depth > 3 && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY // If value from transposition table is lower than probCutBeta, don't attempt From 6592b13d56e43c247ac8b0d6f62564b2a4ca72a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Faruk=20Tutkun?= Date: Fri, 4 Oct 2024 17:46:47 +0300 Subject: [PATCH 1734/1766] Introduce Continuation Correction History Continuation correction history uses last 2 move to correct static eval. ContCorrHist first introduced by @martinnovaak in Motor(https://github.com/martinnovaak/motor/pull/162). Earlier ideas using last move to correct eval is introduced by @MinusKelvin in Ice4(https://github.com/MinusKelvin/ice4/commit/45daf7d9ea64ea4efaf0d2b4e99f53e12e08c838) Passed STC: LLR: 2.96 (-2.94,2.94) <0.00,2.00> Total: 310144 W: 81267 L: 80538 D: 148339 Ptnml(0-2): 1160, 36607, 78834, 37286, 1185 https://tests.stockfishchess.org/tests/view/66f96cbc86d5ee47d953b7f7 Passed LTC: LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 97470 W: 24892 L: 24447 D: 48131 Ptnml(0-2): 63, 10631, 26915, 11050, 76 https://tests.stockfishchess.org/tests/view/66fd59bc86d5ee47d953b9ea closes https://github.com/official-stockfish/Stockfish/pull/5617 Bench: 1143382 --- AUTHORS | 1 + src/movepick.h | 7 +++++++ src/search.cpp | 47 ++++++++++++++++++++++++++++++++++++----------- src/search.h | 36 +++++++++++++++++++----------------- 4 files changed, 63 insertions(+), 28 deletions(-) diff --git a/AUTHORS b/AUTHORS index c0a8beebc45..725b356909d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -176,6 +176,7 @@ Ofek Shochat (OfekShochat, ghostway) Ondrej Mosnáček (WOnder93) Ondřej Mišina (AndrovT) Oskar Werkelin Ahlin +Ömer Faruk Tutkun (OmerFarukTutkun) Pablo Vazquez Panthee Pascal Romaret diff --git a/src/movepick.h b/src/movepick.h index 8deefd140ff..9a68aaa1d7a 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -145,6 +145,9 @@ using CapturePieceToHistory = Stats; +// PieceToCorrectionHistory is addressed by a move's [piece][to] +using PieceToCorrectionHistory = Stats; + // ContinuationHistory is the combined history of a given pair of moves, usually // the current one given a previous one. The nested history table is based on // PieceToHistory instead of ButterflyBoards. @@ -179,6 +182,10 @@ using MinorPieceCorrectionHistory = using NonPawnCorrectionHistory = Stats; +// ContinuationCorrectionHistory is the combined correction history of a given pair of moves +using ContinuationCorrectionHistory = + Stats; + // The MovePicker class is used to pick one pseudo-legal move at a time from the // current position. The most important method is next_move(), which emits one // new pseudo-legal move on every call, until there are no moves left, when diff --git a/src/search.cpp b/src/search.cpp index d79b452d9b4..c55118ece75 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -79,16 +79,23 @@ constexpr int futility_move_count(bool improving, Depth depth) { // Add correctionHistory value to raw staticEval and guarantee evaluation // does not hit the tablebase range. -Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos) { +Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos, Stack* ss) { const Color us = pos.side_to_move(); + const auto m = (ss - 1)->currentMove; const auto pcv = w.pawnCorrectionHistory[us][pawn_structure_index(pos)]; const auto mcv = w.materialCorrectionHistory[us][material_index(pos)]; const auto macv = w.majorPieceCorrectionHistory[us][major_piece_index(pos)]; const auto micv = w.minorPieceCorrectionHistory[us][minor_piece_index(pos)]; const auto wnpcv = w.nonPawnCorrectionHistory[WHITE][us][non_pawn_index(pos)]; const auto bnpcv = w.nonPawnCorrectionHistory[BLACK][us][non_pawn_index(pos)]; - const auto cv = - (6245 * pcv + 3442 * mcv + 3471 * macv + 5958 * micv + 6566 * (wnpcv + bnpcv)) / 131072; + int cntcv = 1; + + if (m.is_ok()) + cntcv = int((*(ss - 2)->continuationCorrectionHistory)[pos.piece_on(m.to_sq())][m.to_sq()]); + + const auto cv = + (5932 * pcv + 2994 * mcv + 3269 * macv + 5660 * micv + 6237 * (wnpcv + bnpcv) + cntcv * 5555) + / 131072; v += cv; return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } @@ -240,7 +247,8 @@ void Search::Worker::iterative_deepening() { { (ss - i)->continuationHistory = &this->continuationHistory[0][0][NO_PIECE][0]; // Use as a sentinel - (ss - i)->staticEval = VALUE_NONE; + (ss - i)->continuationCorrectionHistory = &this->continuationCorrectionHistory[NO_PIECE][0]; + (ss - i)->staticEval = VALUE_NONE; } for (int i = 0; i <= MAX_PLY + 2; ++i) @@ -504,6 +512,10 @@ void Search::Worker::clear() { nonPawnCorrectionHistory[WHITE].fill(0); nonPawnCorrectionHistory[BLACK].fill(0); + for (auto& to : continuationCorrectionHistory) + for (auto& h : to) + h->fill(0); + for (bool inCheck : {false, true}) for (StatsType c : {NoCaptures, Captures}) for (auto& to : continuationHistory[inCheck][c]) @@ -727,7 +739,8 @@ Value Search::Worker::search( else if (PvNode) Eval::NNUE::hint_common_parent_position(pos, networks[numaAccessToken], refreshTable); - ss->staticEval = eval = to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); + ss->staticEval = eval = + to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos, ss); // ttValue can be used as a better position evaluation (~7 Elo) if (ttData.value != VALUE_NONE @@ -738,7 +751,8 @@ Value Search::Worker::search( { unadjustedStaticEval = evaluate(networks[numaAccessToken], pos, refreshTable, thisThread->optimism[us]); - ss->staticEval = eval = to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); + ss->staticEval = eval = + to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos, ss); // Static evaluation is saved as it was before adjustment by correction history ttWriter.write(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_UNSEARCHED, Move::none(), @@ -793,8 +807,9 @@ Value Search::Worker::search( // Null move dynamic reduction based on depth and eval Depth R = std::min(int(eval - beta) / 209, 6) + depth / 3 + 5; - ss->currentMove = Move::null(); - ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; + ss->currentMove = Move::null(); + ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; + ss->continuationCorrectionHistory = &thisThread->continuationCorrectionHistory[NO_PIECE][0]; pos.do_null_move(st, tt); @@ -876,6 +891,8 @@ Value Search::Worker::search( ss->currentMove = move; ss->continuationHistory = &this->continuationHistory[ss->inCheck][true][pos.moved_piece(move)][move.to_sq()]; + ss->continuationCorrectionHistory = + &this->continuationCorrectionHistory[pos.moved_piece(move)][move.to_sq()]; thisThread->nodes.fetch_add(1, std::memory_order_relaxed); pos.do_move(move, st); @@ -1124,7 +1141,8 @@ Value Search::Worker::search( ss->currentMove = move; ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck][capture][movedPiece][move.to_sq()]; - + ss->continuationCorrectionHistory = + &thisThread->continuationCorrectionHistory[movedPiece][move.to_sq()]; uint64_t nodeCount = rootNode ? uint64_t(nodes) : 0; // Step 16. Make the move @@ -1401,6 +1419,8 @@ Value Search::Worker::search( && !(bestValue >= beta && bestValue <= ss->staticEval) && !(!bestMove && bestValue >= ss->staticEval)) { + const auto m = (ss - 1)->currentMove; + auto bonus = std::clamp(int(bestValue - ss->staticEval) * depth / 8, -CORRECTION_HISTORY_LIMIT / 4, CORRECTION_HISTORY_LIMIT / 4); thisThread->pawnCorrectionHistory[us][pawn_structure_index(pos)] @@ -1412,6 +1432,9 @@ Value Search::Worker::search( << bonus * 123 / 128; thisThread->nonPawnCorrectionHistory[BLACK][us][non_pawn_index(pos)] << bonus * 165 / 128; + + if (m.is_ok()) + (*(ss - 2)->continuationCorrectionHistory)[pos.piece_on(m.to_sq())][m.to_sq()] << bonus; } assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE); @@ -1507,7 +1530,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) unadjustedStaticEval = evaluate(networks[numaAccessToken], pos, refreshTable, thisThread->optimism[us]); ss->staticEval = bestValue = - to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); + to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos, ss); // ttValue can be used as a better position evaluation (~13 Elo) if (std::abs(ttData.value) < VALUE_TB_WIN_IN_MAX_PLY @@ -1522,7 +1545,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) ? evaluate(networks[numaAccessToken], pos, refreshTable, thisThread->optimism[us]) : -(ss - 1)->staticEval; ss->staticEval = bestValue = - to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos); + to_corrected_static_eval(unadjustedStaticEval, *thisThread, pos, ss); } // Stand pat. Return immediately if static value is at least beta @@ -1619,6 +1642,8 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) ss->continuationHistory = &thisThread ->continuationHistory[ss->inCheck][capture][pos.moved_piece(move)][move.to_sq()]; + ss->continuationCorrectionHistory = + &thisThread->continuationCorrectionHistory[pos.moved_piece(move)][move.to_sq()]; // Step 7. Make and search the move thisThread->nodes.fetch_add(1, std::memory_order_relaxed); diff --git a/src/search.h b/src/search.h index 0761328a758..a407e10589e 100644 --- a/src/search.h +++ b/src/search.h @@ -61,18 +61,19 @@ namespace Search { // shallower and deeper in the tree during the search. Each search thread has // its own array of Stack objects, indexed by the current ply. struct Stack { - Move* pv; - PieceToHistory* continuationHistory; - int ply; - Move currentMove; - Move excludedMove; - Value staticEval; - int statScore; - int moveCount; - bool inCheck; - bool ttPv; - bool ttHit; - int cutoffCnt; + Move* pv; + PieceToHistory* continuationHistory; + PieceToCorrectionHistory* continuationCorrectionHistory; + int ply; + Move currentMove; + Move excludedMove; + Value staticEval; + int statScore; + int moveCount; + bool inCheck; + bool ttPv; + bool ttHit; + int cutoffCnt; }; @@ -284,11 +285,12 @@ class Worker { ContinuationHistory continuationHistory[2][2]; PawnHistory pawnHistory; - PawnCorrectionHistory pawnCorrectionHistory; - MaterialCorrectionHistory materialCorrectionHistory; - MajorPieceCorrectionHistory majorPieceCorrectionHistory; - MinorPieceCorrectionHistory minorPieceCorrectionHistory; - NonPawnCorrectionHistory nonPawnCorrectionHistory[COLOR_NB]; + PawnCorrectionHistory pawnCorrectionHistory; + MaterialCorrectionHistory materialCorrectionHistory; + MajorPieceCorrectionHistory majorPieceCorrectionHistory; + MinorPieceCorrectionHistory minorPieceCorrectionHistory; + NonPawnCorrectionHistory nonPawnCorrectionHistory[COLOR_NB]; + ContinuationCorrectionHistory continuationCorrectionHistory; private: void iterative_deepening(); From e046c4ef0d743ce57c97c0d40f17610dc2ec3c56 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Mon, 30 Sep 2024 23:53:57 -0400 Subject: [PATCH 1735/1766] Simplify evaluation scaling Set digits in adjusted eval params all to 7. Passed non-regression STC: https://tests.stockfishchess.org/tests/view/66fc493d86d5ee47d953b94c LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 57696 W: 15098 L: 14898 D: 27700 Ptnml(0-2): 205, 6784, 14678, 6968, 213 Passed non-regression LTC: https://tests.stockfishchess.org/tests/view/66fd4b9386d5ee47d953b9d5 LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 93786 W: 23868 L: 23721 D: 46197 Ptnml(0-2): 55, 10322, 25993, 10467, 56 closes https://github.com/official-stockfish/Stockfish/pull/5618 Bench: 1277182 --- src/evaluate.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index b1c7283e3e2..802913a041e 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -59,9 +59,7 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, assert(!pos.checkers()); - bool smallNet = use_smallnet(pos); - int v; - + bool smallNet = use_smallnet(pos); auto [psqt, positional] = smallNet ? networks.small.evaluate(pos, &caches.small) : networks.big.evaluate(pos, &caches.big); @@ -81,7 +79,7 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, nnue -= nnue * nnueComplexity / (smallNet ? 20233 : 17879); int material = (smallNet ? 553 : 532) * pos.count() + pos.non_pawn_material(); - v = (nnue * (76898 + material) + optimism * (8112 + material)) / (smallNet ? 74411 : 76256); + int v = (nnue * (77777 + material) + optimism * (7777 + material)) / 77777; // Damp down the evaluation linearly when shuffling v -= v * pos.rule50_count() / 212; From dce72913feec523f077db8e86cc5797286c6548d Mon Sep 17 00:00:00 2001 From: Disservin Date: Fri, 4 Oct 2024 19:36:02 +0200 Subject: [PATCH 1736/1766] Temporarily fix clang-format mismatch closes https://github.com/official-stockfish/Stockfish/pull/5620 No functional change --- src/uci.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/uci.cpp b/src/uci.cpp index cfb34db791f..8388cad8cee 100644 --- a/src/uci.cpp +++ b/src/uci.cpp @@ -431,6 +431,8 @@ void UCIEngine::benchmark(std::istream& args) { if (threadBinding.empty()) threadBinding = "none"; + // clang-format off + std::cerr << "===========================" << "\nVersion : " << engine_version_info() @@ -453,6 +455,8 @@ void UCIEngine::benchmark(std::istream& args) { << "\nTotal search time [s] : " << totalTime / 1000.0 << "\nNodes/second : " << 1000 * nodes / totalTime << std::endl; + // clang-format on + init_search_update_listeners(); } From 3348603770926e9865fc3f43baaaef8de99d3014 Mon Sep 17 00:00:00 2001 From: mstembera Date: Fri, 4 Oct 2024 10:39:51 -0700 Subject: [PATCH 1737/1766] Simplify previous #5608 https://github.com/official-stockfish/Stockfish/pull/5608 STC: https://tests.stockfishchess.org/tests/view/66fb1bab86d5ee47d953b8cc LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 25536 W: 6797 L: 6560 D: 12179 Ptnml(0-2): 93, 2953, 6460, 3148, 114 LTC https://tests.stockfishchess.org/tests/view/66fb690e86d5ee47d953b8eb LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 225114 W: 57200 L: 57188 D: 110726 Ptnml(0-2): 197, 25076, 61995, 25096, 193 closes https://github.com/official-stockfish/Stockfish/pull/5621 Bench: 1570076 --- src/search.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c55118ece75..34fb5a80579 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1213,8 +1213,7 @@ Value Search::Worker::search( value = -search(pos, ss + 1, -(alpha + 1), -alpha, newDepth, !cutNode); // Post LMR continuation history updates (~1 Elo) - int bonus = value >= beta ? (1 + 2 * (moveCount > depth)) * stat_bonus(newDepth) - : -stat_malus(newDepth); + int bonus = value >= beta ? 3 * stat_bonus(newDepth) : -stat_malus(newDepth); update_continuation_histories(ss, movedPiece, move.to_sq(), bonus); } } From 9a21e3e9968ebdd36c24d9b2762646a76a4e448b Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sat, 5 Oct 2024 13:52:24 +0300 Subject: [PATCH 1738/1766] Simplify bestvalue formula Passed STC: LLR: 2.97 (-2.94,2.94) <-1.75,0.25> Total: 163680 W: 42689 L: 42605 D: 78386 Ptnml(0-2): 619, 19555, 41386, 19683, 597 https://tests.stockfishchess.org/tests/view/66f9451386d5ee47d953b7d9 Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 96498 W: 24582 L: 24438 D: 47478 Ptnml(0-2): 62, 10642, 26718, 10744, 83 https://tests.stockfishchess.org/tests/view/66fd765786d5ee47d953ba1c closes https://github.com/official-stockfish/Stockfish/pull/5622 Bench: 1309815 --- src/search.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 34fb5a80579..5598b5ffb22 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1610,11 +1610,11 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) continue; } - // if static exchange evaluation is low enough + // If static exchange evaluation is low enough // we can prune this move. (~2 Elo) if (!pos.see_ge(move, alpha - futilityBase)) { - bestValue = (futilityBase > alpha) ? alpha : std::max(bestValue, futilityBase); + bestValue = std::min(alpha, futilityBase); continue; } } From 76923bb6fef2982dbce201227f6a33788390ce35 Mon Sep 17 00:00:00 2001 From: mstembera Date: Sat, 5 Oct 2024 16:18:21 -0700 Subject: [PATCH 1739/1766] Optimize magics Reduce the size of the Magics table by half on modern cpu's and lay it out to match our access pattern. Namely we typically access the magics for the same square for both bishop and rook back to back so we want those to be in the same cache line. https://tests.stockfishchess.org/tests/view/6701c9b386d5ee47d953bcf4 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 121664 W: 31931 L: 31497 D: 58236 Ptnml(0-2): 395, 13658, 32322, 14032, 425 A similar patch minus the size reduction finished yellow https://tests.stockfishchess.org/tests/view/6695f03f4ff211be9d4ec16c LLR: -2.94 (-2.94,2.94) <0.00,2.00> Total: 310688 W: 80940 L: 80746 D: 149002 Ptnml(0-2): 1119, 35032, 82846, 35230, 1117 closes https://github.com/official-stockfish/Stockfish/pull/5623 No functional change --- src/bitboard.cpp | 40 +++++++++++++++++++++++----------------- src/bitboard.h | 21 ++++++++++++--------- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/src/bitboard.cpp b/src/bitboard.cpp index a8b4e5f4464..deda6da2a5e 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -34,15 +34,14 @@ Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; -Magic RookMagics[SQUARE_NB]; -Magic BishopMagics[SQUARE_NB]; +alignas(64) Magic Magics[SQUARE_NB][2]; namespace { Bitboard RookTable[0x19000]; // To store rook attacks Bitboard BishopTable[0x1480]; // To store bishop attacks -void init_magics(PieceType pt, Bitboard table[], Magic magics[]); +void init_magics(PieceType pt, Bitboard table[], Magic magics[][2]); // Returns the bitboard of target square for the given step // from the given square. If the step is off the board, returns empty bitboard. @@ -82,8 +81,8 @@ void Bitboards::init() { for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) SquareDistance[s1][s2] = std::max(distance(s1, s2), distance(s1, s2)); - init_magics(ROOK, RookTable, RookMagics); - init_magics(BISHOP, BishopTable, BishopMagics); + init_magics(ROOK, RookTable, Magics); + init_magics(BISHOP, BishopTable, Magics); for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) { @@ -142,39 +141,47 @@ Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) { // bitboards are used to look up attacks of sliding pieces. As a reference see // https://www.chessprogramming.org/Magic_Bitboards. In particular, here we use // the so called "fancy" approach. -void init_magics(PieceType pt, Bitboard table[], Magic magics[]) { +void init_magics(PieceType pt, Bitboard table[], Magic magics[][2]) { +#ifndef USE_PEXT // Optimal PRNG seeds to pick the correct magics in the shortest time int seeds[][RANK_NB] = {{8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020}, {728, 10316, 55013, 32803, 12281, 15100, 16645, 255}}; - Bitboard occupancy[4096], reference[4096], edges, b; - int epoch[4096] = {}, cnt = 0, size = 0; + Bitboard occupancy[4096]; + int epoch[4096] = {}, cnt = 0; +#endif + Bitboard reference[4096]; + int size = 0; for (Square s = SQ_A1; s <= SQ_H8; ++s) { // Board edges are not considered in the relevant occupancies - edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s)); + Bitboard edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s)); // Given a square 's', the mask is the bitboard of sliding attacks from // 's' computed on an empty board. The index must be big enough to contain // all the attacks for each possible subset of the mask and so is 2 power // the number of 1s of the mask. Hence we deduce the size of the shift to // apply to the 64 or 32 bits word to get the index. - Magic& m = magics[s]; + Magic& m = magics[s][pt - BISHOP]; m.mask = sliding_attack(pt, s, 0) & ~edges; - m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask); - +#ifndef USE_PEXT + m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask); +#endif // Set the offset for the attacks table of the square. We have individual // table sizes for each square with "Fancy Magic Bitboards". - m.attacks = s == SQ_A1 ? table : magics[s - 1].attacks + size; + m.attacks = s == SQ_A1 ? table : magics[s - 1][pt - BISHOP].attacks + size; + size = 0; // Use Carry-Rippler trick to enumerate all subsets of masks[s] and // store the corresponding sliding attack bitboard in reference[]. - b = size = 0; + Bitboard b = 0; do { +#ifndef USE_PEXT occupancy[size] = b; +#endif reference[size] = sliding_attack(pt, s, b); if (HasPext) @@ -184,9 +191,7 @@ void init_magics(PieceType pt, Bitboard table[], Magic magics[]) { b = (b - m.mask) & m.mask; } while (b); - if (HasPext) - continue; - +#ifndef USE_PEXT PRNG rng(seeds[Is64Bit][rank_of(s)]); // Find a magic for square 's' picking up an (almost) random number @@ -215,6 +220,7 @@ void init_magics(PieceType pt, Bitboard table[], Magic magics[]) { break; } } +#endif } } } diff --git a/src/bitboard.h b/src/bitboard.h index cdff4c759bc..c4bf18b531b 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -67,27 +67,31 @@ extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; // Magic holds all magic bitboards relevant data for a single square struct Magic { Bitboard mask; - Bitboard magic; Bitboard* attacks; - unsigned shift; +#ifndef USE_PEXT + Bitboard magic; + unsigned shift; +#endif // Compute the attack's index using the 'magic bitboards' approach unsigned index(Bitboard occupied) const { - if (HasPext) - return unsigned(pext(occupied, mask)); - +#ifdef USE_PEXT + return unsigned(pext(occupied, mask)); +#else if (Is64Bit) return unsigned(((occupied & mask) * magic) >> shift); unsigned lo = unsigned(occupied) & unsigned(mask); unsigned hi = unsigned(occupied >> 32) & unsigned(mask >> 32); return (lo * unsigned(magic) ^ hi * unsigned(magic >> 32)) >> shift; +#endif } + + Bitboard attacks_bb(Bitboard occupied) const { return attacks[index(occupied)]; } }; -extern Magic RookMagics[SQUARE_NB]; -extern Magic BishopMagics[SQUARE_NB]; +extern Magic Magics[SQUARE_NB][2]; constexpr Bitboard square_bb(Square s) { assert(is_ok(s)); @@ -229,9 +233,8 @@ inline Bitboard attacks_bb(Square s, Bitboard occupied) { switch (Pt) { case BISHOP : - return BishopMagics[s].attacks[BishopMagics[s].index(occupied)]; case ROOK : - return RookMagics[s].attacks[RookMagics[s].index(occupied)]; + return Magics[s][Pt - BISHOP].attacks_bb(occupied); case QUEEN : return attacks_bb(s, occupied) | attacks_bb(s, occupied); default : From d4358ddba7184aa7403d12397f2f49f5ea6364fd Mon Sep 17 00:00:00 2001 From: Mathias Parnaudeau Date: Sat, 5 Oct 2024 15:28:39 +0200 Subject: [PATCH 1740/1766] Add autodetection of ppc64 architectures That allows 'make -j profile-build' work on ppc64 architectures, setting the use of the appropriate SIMD extension, Altivec or VSX. For VSX, gcc allows to map SSE2 intrinsics and get benefit of the existing SIMD code. On PowerMac G5, using altivec provides a performance improvement of 30%. On Talos 2, using vsx provides a performance improvement of 120%. closes https://github.com/official-stockfish/Stockfish/pull/5624 No functional change --- AUTHORS | 1 + scripts/get_native_properties.sh | 18 ++++++++++++++ src/Makefile | 42 ++++++++++++++++++++++++++++++-- 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index 725b356909d..31a64c17e3a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -143,6 +143,7 @@ Maciej Żenczykowski (zenczykowski) Malcolm Campbell (xoto10) Mark Tenzer (31m059) marotear +Mathias Parnaudeau (mparnaudeau) Matt Ginsberg (mattginsberg) Matthew Lai (matthewlai) Matthew Sullivan (Matt14916) diff --git a/scripts/get_native_properties.sh b/scripts/get_native_properties.sh index dfbfac0eab6..ed5fc9af093 100755 --- a/scripts/get_native_properties.sh +++ b/scripts/get_native_properties.sh @@ -54,6 +54,20 @@ set_arch_x86_64() { fi } +set_arch_ppc_64() { + if $(grep -q -w "altivec" /proc/cpuinfo); then + power=$(grep -oP -m 1 'cpu\t+: POWER\K\d+' /proc/cpuinfo) + if [ "0$power" -gt 7 ]; then + # VSX started with POWER8 + true_arch='ppc-64-vsx' + else + true_arch='ppc-64-altivec' + fi + else + true_arch='ppc-64' + fi +} + # Check the system type uname_s=$(uname -s) uname_m=$(uname -m) @@ -87,6 +101,10 @@ case $uname_s in file_os='ubuntu' true_arch='x86-32' ;; + 'ppc64'*) + file_os='ubuntu' + set_arch_ppc_64 + ;; 'aarch64') file_os='android' true_arch='armv8' diff --git a/src/Makefile b/src/Makefile index 6cb778a6822..15066781b39 100644 --- a/src/Makefile +++ b/src/Makefile @@ -98,6 +98,8 @@ VPATH = syzygy:nnue:nnue/features # avx512 = yes/no --- -mavx512bw --- Use Intel Advanced Vector Extensions 512 # vnni256 = yes/no --- -mavx256vnni --- Use Intel Vector Neural Network Instructions 512 with 256bit operands # vnni512 = yes/no --- -mavx512vnni --- Use Intel Vector Neural Network Instructions 512 +# altivec = yes/no --- -maltivec --- Use PowerPC Altivec SIMD extension +# vsx = yes/no --- -mvsx --- Use POWER VSX SIMD extension # neon = yes/no --- -DUSE_NEON --- Use ARM SIMD architecture # dotprod = yes/no --- -DUSE_NEON_DOTPROD --- Use ARM advanced SIMD Int8 dot product instructions # lsx = yes/no --- -mlsx --- Use Loongson SIMD eXtension @@ -126,7 +128,7 @@ endif ifeq ($(ARCH), $(filter $(ARCH), \ x86-64-vnni512 x86-64-vnni256 x86-64-avx512 x86-64-avxvnni x86-64-bmi2 \ x86-64-avx2 x86-64-sse41-popcnt x86-64-modern x86-64-ssse3 x86-64-sse3-popcnt \ - x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-32 e2k \ + x86-64 x86-32-sse41-popcnt x86-32-sse2 x86-32 ppc-64 ppc-64-altivec ppc-64-vsx ppc-32 e2k \ armv7 armv7-neon armv8 armv8-dotprod apple-silicon general-64 general-32 riscv64 \ loongarch64 loongarch64-lsx loongarch64-lasx)) SUPPORTED_ARCH=true @@ -151,6 +153,8 @@ avxvnni = no avx512 = no vnni256 = no vnni512 = no +altivec = no +vsx = no neon = no dotprod = no arm_version = 0 @@ -360,6 +364,20 @@ ifeq ($(ARCH),ppc-64) prefetch = yes endif +ifeq ($(ARCH),ppc-64-altivec) + arch = ppc64 + popcnt = yes + prefetch = yes + altivec = yes +endif + +ifeq ($(ARCH),ppc-64-vsx) + arch = ppc64 + popcnt = yes + prefetch = yes + vsx = yes +endif + ifeq ($(findstring e2k,$(ARCH)),e2k) arch = e2k mmx = yes @@ -650,7 +668,7 @@ else endif ifeq ($(popcnt),yes) - ifeq ($(arch),$(filter $(arch),ppc64 armv7 armv8 arm64)) + ifeq ($(arch),$(filter $(arch),ppc64 ppc64-altivec ppc64-vsx armv7 armv8 arm64)) CXXFLAGS += -DUSE_POPCNT else CXXFLAGS += -msse3 -mpopcnt -DUSE_POPCNT @@ -720,6 +738,20 @@ ifeq ($(mmx),yes) endif endif +ifeq ($(altivec),yes) + CXXFLAGS += -maltivec + ifeq ($(COMP),gcc) + CXXFLAGS += -mabi=altivec + endif +endif + +ifeq ($(vsx),yes) + CXXFLAGS += -mvsx + ifeq ($(COMP),gcc) + CXXFLAGS += -DNO_WARN_X86_INTRINSICS -DUSE_SSE2 + endif +endif + ifeq ($(neon),yes) CXXFLAGS += -DUSE_NEON=$(arm_version) ifeq ($(KERNEL),Linux) @@ -852,6 +884,8 @@ help: @echo "x86-32-sse2 > x86 32-bit with sse2 support" @echo "x86-32 > x86 32-bit generic (with mmx compile support)" @echo "ppc-64 > PPC 64-bit" + @echo "ppc-64-altivec > PPC 64-bit with altivec support" + @echo "ppc-64-vsx > PPC 64-bit with vsx support" @echo "ppc-32 > PPC 32-bit" @echo "armv7 > ARMv7 32-bit" @echo "armv7-neon > ARMv7 32-bit with popcnt and neon" @@ -987,6 +1021,8 @@ config-sanity: net @echo "avx512: '$(avx512)'" @echo "vnni256: '$(vnni256)'" @echo "vnni512: '$(vnni512)'" + @echo "altivec: '$(altivec)'" + @echo "vsx: '$(vsx)'" @echo "neon: '$(neon)'" @echo "dotprod: '$(dotprod)'" @echo "arm_version: '$(arm_version)'" @@ -1020,6 +1056,8 @@ config-sanity: net @test "$(avx512)" = "yes" || test "$(avx512)" = "no" @test "$(vnni256)" = "yes" || test "$(vnni256)" = "no" @test "$(vnni512)" = "yes" || test "$(vnni512)" = "no" + @test "$(altivec)" = "yes" || test "$(altivec)" = "no" + @test "$(vsx)" = "yes" || test "$(vsx)" = "no" @test "$(neon)" = "yes" || test "$(neon)" = "no" @test "$(lsx)" = "yes" || test "$(lsx)" = "no" @test "$(lasx)" = "yes" || test "$(lasx)" = "no" From aaadbe0572e793cea9ebdf37e32c79235e4d573b Mon Sep 17 00:00:00 2001 From: Nonlinear2 <131959792+Nonlinear2@users.noreply.github.com> Date: Wed, 9 Oct 2024 20:00:19 +0200 Subject: [PATCH 1741/1766] Introduce mean squared score for delta adjustments This patch introduces the value `meanSquaredScore`, which makes the initial delta sensitive to unstable iterative deepening scores. Passed STC: https://tests.stockfishchess.org/tests/view/66fed74286d5ee47d953bb42 LLR: 2.98 (-2.94,2.94) <0.00,2.00> Total: 71104 W: 18635 L: 18262 D: 34207 Ptnml(0-2): 234, 8365, 17993, 8714, 246 Passed LTC: https://tests.stockfishchess.org/tests/view/6700088e86d5ee47d953bbe9 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 212544 W: 54238 L: 53560 D: 104746 Ptnml(0-2): 120, 23093, 59172, 23763, 124 closes https://github.com/official-stockfish/Stockfish/pull/5627 Bench: 1395505 --- src/search.cpp | 8 ++++++-- src/search.h | 19 ++++++++++--------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5598b5ffb22..647bae764f3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -312,8 +312,8 @@ void Search::Worker::iterative_deepening() { selDepth = 0; // Reset aspiration window starting size + delta = 5 + std::abs(rootMoves[pvIdx].meanSquaredScore) / 13797; Value avg = rootMoves[pvIdx].averageScore; - delta = 5 + avg * avg / 11797; alpha = std::max(avg - delta, -VALUE_INFINITE); beta = std::min(avg + delta, VALUE_INFINITE); @@ -1065,7 +1065,7 @@ Value Search::Worker::search( // (alpha, beta), then that move is singular and should be extended. To // verify this we do a reduced search on the position excluding the ttMove // and if the result is lower than ttValue minus a margin, then we will - // extend the ttMove. Recursive singular search is avoided. + // extend the ttMove. Recursive singular search is avoided. // Note: the depth margin and singularBeta margin are known for having // non-linear scaling. Their values are optimized to time controls of @@ -1265,6 +1265,10 @@ Value Search::Worker::search( rm.averageScore = rm.averageScore != -VALUE_INFINITE ? (value + rm.averageScore) / 2 : value; + rm.meanSquaredScore = rm.meanSquaredScore != -VALUE_INFINITE * VALUE_INFINITE + ? (value * std::abs(value) + rm.meanSquaredScore) / 2 + : value * std::abs(value); + // PV move or new best move? if (moveCount == 1 || value > alpha) { diff --git a/src/search.h b/src/search.h index a407e10589e..2342d9e93c4 100644 --- a/src/search.h +++ b/src/search.h @@ -91,15 +91,16 @@ struct RootMove { return m.score != score ? m.score < score : m.previousScore < previousScore; } - uint64_t effort = 0; - Value score = -VALUE_INFINITE; - Value previousScore = -VALUE_INFINITE; - Value averageScore = -VALUE_INFINITE; - Value uciScore = -VALUE_INFINITE; - bool scoreLowerbound = false; - bool scoreUpperbound = false; - int selDepth = 0; - int tbRank = 0; + uint64_t effort = 0; + Value score = -VALUE_INFINITE; + Value previousScore = -VALUE_INFINITE; + Value averageScore = -VALUE_INFINITE; + Value meanSquaredScore = -VALUE_INFINITE * VALUE_INFINITE; + Value uciScore = -VALUE_INFINITE; + bool scoreLowerbound = false; + bool scoreUpperbound = false; + int selDepth = 0; + int tbRank = 0; Value tbScore; std::vector pv; }; From b261df970d5207069a06e89b48983aece1c60925 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Wed, 9 Oct 2024 20:16:14 -0700 Subject: [PATCH 1742/1766] Fix majorPieceKey Updates Passed STC: LLR: 2.98 (-2.94,2.94) <0.00,2.00> Total: 476160 W: 124285 L: 123311 D: 228564 Ptnml(0-2): 1662, 56266, 121219, 57302, 1631 https://tests.stockfishchess.org/tests/view/66ea3dc186d5ee47d953ae07 Failed Yellow LTC: LLR: -2.94 (-2.94,2.94) <0.50,2.50> Total: 230634 W: 58525 L: 58295 D: 113814 Ptnml(0-2): 113, 25301, 64299, 25451, 153 https://tests.stockfishchess.org/tests/view/66f1825e86d5ee47d953b2ec Passed Non-regression LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 112344 W: 28590 L: 28462 D: 55292 Ptnml(0-2): 71, 12439, 31039, 12537, 86 https://tests.stockfishchess.org/tests/view/6707474486d5ee47d953bfe3 closes https://github.com/official-stockfish/Stockfish/pull/5629 Bench: 1283457 --- src/position.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/position.cpp b/src/position.cpp index f596b015355..bab7a1fcac5 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -759,7 +759,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) { st->nonPawnMaterial[them] -= PieceValue[captured]; st->nonPawnKey[them] ^= Zobrist::psq[captured][capsq]; - if (type_of(pc) == QUEEN || type_of(pc) == ROOK) + if (type_of(captured) == QUEEN || type_of(captured) == ROOK) st->majorPieceKey ^= Zobrist::psq[captured][capsq]; else From 9766db8139ce8815110c15bdde8381d0564a63fa Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Sat, 12 Oct 2024 08:32:15 +0300 Subject: [PATCH 1743/1766] Make low ply history size fixed Size of low ply history should always be the same, so ensure it. closes https://github.com/official-stockfish/Stockfish/pull/5630 No functional change --- src/movepick.cpp | 2 +- src/movepick.h | 3 ++- src/search.cpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 1d1aef0f313..0649518938a 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -179,7 +179,7 @@ void MovePicker::score() { : pt == ROOK && bool(to & threatenedByMinor) ? 24335 : 0); - if (ply < 4) + if (ply < LOW_PLY_HISTORY_SIZE) m.value += 8 * (*lowPlyHistory)[ply][m.from_to()] / (1 + 2 * ply); } diff --git a/src/movepick.h b/src/movepick.h index 9a68aaa1d7a..5c312531ef1 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -37,6 +37,7 @@ namespace Stockfish { constexpr int PAWN_HISTORY_SIZE = 512; // has to be a power of 2 constexpr int CORRECTION_HISTORY_SIZE = 32768; // has to be a power of 2 constexpr int CORRECTION_HISTORY_LIMIT = 1024; +constexpr int LOW_PLY_HISTORY_SIZE = 4; static_assert((PAWN_HISTORY_SIZE & (PAWN_HISTORY_SIZE - 1)) == 0, "PAWN_HISTORY_SIZE has to be a power of 2"); @@ -137,7 +138,7 @@ using ButterflyHistory = Stats; +using LowPlyHistory = Stats; // CapturePieceToHistory is addressed by a move's [piece][to][captured piece type] using CapturePieceToHistory = Stats; diff --git a/src/search.cpp b/src/search.cpp index 647bae764f3..6e513b458ff 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1851,7 +1851,7 @@ void update_quiet_histories( Color us = pos.side_to_move(); workerThread.mainHistory[us][move.from_to()] << bonus; - if (ss->ply < 4) + if (ss->ply < LOW_PLY_HISTORY_SIZE) workerThread.lowPlyHistory[ss->ply][move.from_to()] << bonus; update_continuation_histories(ss, pos.moved_piece(move), move.to_sq(), bonus); From 656b2cb6459cf3c91f8d8ed6aa770026f77ee7b9 Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Sun, 6 Oct 2024 20:11:19 -0400 Subject: [PATCH 1744/1766] Update default main net to nn-1cedc0ffeeee.nnue Created by setting output weights (256) and biases (8) of the previous main net nn-1111cefa1111.nnue to values found with spsa after 38k / 120k games at 120+1.2 using the same method as: https://github.com/official-stockfish/Stockfish/pull/5459 nn-1111cefa1111.nnue -> nn-1cedc0ffeeee.nnue # weights changed: 185 mean: 0.0703 +/- 2.53 min: -6 max: 6 Passed STC: https://tests.stockfishchess.org/tests/view/6703589b86d5ee47d953bda1 LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 101984 W: 26690 L: 26275 D: 49019 Ptnml(0-2): 375, 11944, 25926, 12385, 362 Passed LTC: https://tests.stockfishchess.org/tests/view/670542d286d5ee47d953befa LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 106224 W: 27079 L: 26618 D: 52527 Ptnml(0-2): 71, 11508, 29487, 11981, 65 closes https://github.com/official-stockfish/Stockfish/pull/5632 Bench: 1351413 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index c9041efbf84..9bd436b58c6 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -33,7 +33,7 @@ namespace Eval { // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro or the location where this macro is defined, as it is used // in the Makefile/Fishtest. -#define EvalFileDefaultNameBig "nn-1111cefa1111.nnue" +#define EvalFileDefaultNameBig "nn-1cedc0ffeeee.nnue" #define EvalFileDefaultNameSmall "nn-37f18f62d772.nnue" namespace NNUE { From 2f3e6198e878818f9f90de8cb31e287de34bed0e Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sun, 13 Oct 2024 13:59:20 +0300 Subject: [PATCH 1745/1766] Simplify optimism divisor. Passed STC: LLR: 2.97 (-2.94,2.94) <-1.75,0.25> Total: 139360 W: 36143 L: 36033 D: 67184 Ptnml(0-2): 436, 16456, 35778, 16582, 428 https://tests.stockfishchess.org/tests/view/66fc49c786d5ee47d953b94e Passed LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 257748 W: 65163 L: 65184 D: 127401 Ptnml(0-2): 173, 28471, 71611, 28442, 177 https://tests.stockfishchess.org/tests/view/66ff01ae86d5ee47d953bb54 Passed LTC against rebased version: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 53610 W: 13691 L: 13501 D: 26418 Ptnml(0-2): 52, 5942, 14605, 6176, 30 https://tests.stockfishchess.org/tests/view/670a9c5c86d5ee47d953c231 closes https://github.com/official-stockfish/Stockfish/pull/5633 Bench: 1282078 --- src/evaluate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 802913a041e..7c7b54a4ff8 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -75,7 +75,7 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, // Blend optimism and eval with nnue complexity int nnueComplexity = std::abs(psqt - positional); - optimism += optimism * nnueComplexity / (smallNet ? 430 : 474); + optimism += optimism * nnueComplexity / 468; nnue -= nnue * nnueComplexity / (smallNet ? 20233 : 17879); int material = (smallNet ? 553 : 532) * pos.count() + pos.non_pawn_material(); From bf2a0d53925da1a0d58a91ef78d577a448eb4b5a Mon Sep 17 00:00:00 2001 From: Taras Vuk <117687515+TarasVuk@users.noreply.github.com> Date: Mon, 14 Oct 2024 21:30:18 +0200 Subject: [PATCH 1746/1766] Simplify internal iterative reductions Passed STC: LLR: 2.92 (-2.94,2.94) <-1.75,0.25> Total: 138656 W: 36182 L: 36074 D: 66400 Ptnml(0-2): 523, 16422, 35310, 16570, 503 https://tests.stockfishchess.org/tests/view/6702beb386d5ee47d953bd41 Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 680844 W: 172021 L: 172480 D: 336343 Ptnml(0-2): 492, 76259, 187419, 75720, 532 https://tests.stockfishchess.org/tests/view/67042b1f86d5ee47d953be7c closes https://github.com/official-stockfish/Stockfish/pull/5634 Bench: 1169252 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 6e513b458ff..75a8c963344 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -850,7 +850,7 @@ Value Search::Worker::search( // For cutNodes, if depth is high enough, decrease depth by 2 if there is no ttMove, // or by 1 if there is a ttMove with an upper bound. if (cutNode && depth >= 7 && (!ttData.move || ttData.bound == BOUND_UPPER)) - depth -= 1 + !ttData.move; + depth -= 2; // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search From 7f386d109e1b38d530d98f81e7213a2f1b2090af Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Wed, 16 Oct 2024 03:06:58 +0300 Subject: [PATCH 1747/1766] Remove material corrHist Passed STC: LLR: 2.96 (-2.94,2.94) <-1.75,0.25> Total: 80832 W: 21150 L: 20975 D: 38707 Ptnml(0-2): 283, 9531, 20598, 9736, 268 https://tests.stockfishchess.org/tests/view/670302fe86d5ee47d953bd68 Passed LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 46008 W: 11621 L: 11423 D: 22964 Ptnml(0-2): 30, 5072, 12606, 5262, 34 https://tests.stockfishchess.org/tests/view/6704074686d5ee47d953be53 Passed LTC Rebased: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 95814 W: 24340 L: 24195 D: 47279 Ptnml(0-2): 71, 10497, 26602, 10690, 47 https://tests.stockfishchess.org/tests/view/670ae1ac86d5ee47d953c262 closes https://github.com/official-stockfish/Stockfish/pull/5636 Bench: 1119774 --- src/movepick.h | 4 ---- src/search.cpp | 6 +----- src/search.h | 1 - 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/movepick.h b/src/movepick.h index 5c312531ef1..6ad13397ab1 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -167,10 +167,6 @@ using PawnHistory = Stats using PawnCorrectionHistory = Stats; -// MaterialCorrectionHistory is addressed by color and material configuration -using MaterialCorrectionHistory = - Stats; - // MajorPieceCorrectionHistory is addressed by color and king/major piece (Queen, Rook) positions using MajorPieceCorrectionHistory = Stats; diff --git a/src/search.cpp b/src/search.cpp index 75a8c963344..568e147c8bf 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -83,7 +83,6 @@ Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos, St const Color us = pos.side_to_move(); const auto m = (ss - 1)->currentMove; const auto pcv = w.pawnCorrectionHistory[us][pawn_structure_index(pos)]; - const auto mcv = w.materialCorrectionHistory[us][material_index(pos)]; const auto macv = w.majorPieceCorrectionHistory[us][major_piece_index(pos)]; const auto micv = w.minorPieceCorrectionHistory[us][minor_piece_index(pos)]; const auto wnpcv = w.nonPawnCorrectionHistory[WHITE][us][non_pawn_index(pos)]; @@ -94,8 +93,7 @@ Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos, St cntcv = int((*(ss - 2)->continuationCorrectionHistory)[pos.piece_on(m.to_sq())][m.to_sq()]); const auto cv = - (5932 * pcv + 2994 * mcv + 3269 * macv + 5660 * micv + 6237 * (wnpcv + bnpcv) + cntcv * 5555) - / 131072; + (5932 * pcv + 3269 * macv + 5660 * micv + 6666 * (wnpcv + bnpcv) + 5555 * cntcv) / 131072; v += cv; return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } @@ -506,7 +504,6 @@ void Search::Worker::clear() { captureHistory.fill(-753); pawnHistory.fill(-1152); pawnCorrectionHistory.fill(0); - materialCorrectionHistory.fill(0); majorPieceCorrectionHistory.fill(0); minorPieceCorrectionHistory.fill(0); nonPawnCorrectionHistory[WHITE].fill(0); @@ -1428,7 +1425,6 @@ Value Search::Worker::search( -CORRECTION_HISTORY_LIMIT / 4, CORRECTION_HISTORY_LIMIT / 4); thisThread->pawnCorrectionHistory[us][pawn_structure_index(pos)] << bonus * 101 / 128; - thisThread->materialCorrectionHistory[us][material_index(pos)] << bonus * 99 / 128; thisThread->majorPieceCorrectionHistory[us][major_piece_index(pos)] << bonus * 157 / 128; thisThread->minorPieceCorrectionHistory[us][minor_piece_index(pos)] << bonus * 153 / 128; thisThread->nonPawnCorrectionHistory[WHITE][us][non_pawn_index(pos)] diff --git a/src/search.h b/src/search.h index 2342d9e93c4..b599da110a2 100644 --- a/src/search.h +++ b/src/search.h @@ -287,7 +287,6 @@ class Worker { PawnHistory pawnHistory; PawnCorrectionHistory pawnCorrectionHistory; - MaterialCorrectionHistory materialCorrectionHistory; MajorPieceCorrectionHistory majorPieceCorrectionHistory; MinorPieceCorrectionHistory minorPieceCorrectionHistory; NonPawnCorrectionHistory nonPawnCorrectionHistory[COLOR_NB]; From b325b2c348df02e415b6c78121e0502622d57f34 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Wed, 16 Oct 2024 13:14:13 +0300 Subject: [PATCH 1748/1766] Simplify bestValue formula Passed STC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 45888 W: 12051 L: 11841 D: 21996 Ptnml(0-2): 123, 5356, 11807, 5504, 154 https://tests.stockfishchess.org/tests/view/670bb89086d5ee47d953c2d8 Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 51336 W: 13021 L: 12830 D: 25485 Ptnml(0-2): 34, 5594, 14227, 5773, 40 https://tests.stockfishchess.org/tests/view/670c587f86d5ee47d953c31b closes https://github.com/official-stockfish/Stockfish/pull/5637 Bench: 1192999 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 568e147c8bf..c398b7d2568 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1551,7 +1551,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) if (bestValue >= beta) { if (std::abs(bestValue) < VALUE_TB_WIN_IN_MAX_PLY) - bestValue = (3 * bestValue + beta) / 4; + bestValue = (bestValue + beta) / 2; if (!ss->ttHit) ttWriter.write(posKey, value_to_tt(bestValue, ss->ply), false, BOUND_LOWER, DEPTH_UNSEARCHED, Move::none(), unadjustedStaticEval, From 2ce47573b4d3664dca4cbc4354c8c600540d16ad Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Wed, 16 Oct 2024 19:40:49 +0300 Subject: [PATCH 1749/1766] Remove -stat_malus(newDepth) Passed STC: LLR: 2.97 (-2.94,2.94) <-1.75,0.25> Total: 92544 W: 23940 L: 23778 D: 44826 Ptnml(0-2): 286, 10936, 23638, 11154, 258 https://tests.stockfishchess.org/tests/view/670c3d6986d5ee47d953c30b Passed LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 43164 W: 10986 L: 10786 D: 21392 Ptnml(0-2): 27, 4713, 11905, 4907, 30 https://tests.stockfishchess.org/tests/view/670eda3d86d5ee47d953c51d closes https://github.com/official-stockfish/Stockfish/pull/5639 Bench: 1281912 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index c398b7d2568..c78acb6c40c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1210,7 +1210,7 @@ Value Search::Worker::search( value = -search(pos, ss + 1, -(alpha + 1), -alpha, newDepth, !cutNode); // Post LMR continuation history updates (~1 Elo) - int bonus = value >= beta ? 3 * stat_bonus(newDepth) : -stat_malus(newDepth); + int bonus = 2 * (value >= beta) * stat_bonus(newDepth); update_continuation_histories(ss, movedPiece, move.to_sq(), bonus); } } From c15113554f53890d7944c00a70d0f2d8a78916fb Mon Sep 17 00:00:00 2001 From: Disservin Date: Sat, 19 Oct 2024 16:56:02 +0200 Subject: [PATCH 1750/1766] Speedup Makefile on Windows The Makefile is notoriously slow on windows, because of new processes being spawned I believe. This pr improves it a little bit for the help and config-sanity targets, with the latter also improving `make -j build` because it depends on that. On the same machine ubuntu (wsl) is more than 3 times faster, if there are other improvements we can make I'd be happy to hear about them. Ultimately https://github.com/official-stockfish/Stockfish/pull/5543 also aims to improve this I believe, but it will take some additional time before that lands. ``` make config-sanity: patch: 6.199s master: 12.738s make help: patch: 3.1s master: 11.49s make -j build: patch: 36s master: 43.25s make -j build: master ubuntu: 10s ``` closes https://github.com/official-stockfish/Stockfish/pull/5642 No functional change --- src/Makefile | 264 ++++++++++++++++++++++++++------------------------- 1 file changed, 133 insertions(+), 131 deletions(-) diff --git a/src/Makefile b/src/Makefile index 15066781b39..4307b7c7473 100644 --- a/src/Makefile +++ b/src/Makefile @@ -851,75 +851,75 @@ endif ### ========================================================================== help: - @echo "" - @echo "To compile stockfish, type: " - @echo "" - @echo "make -j target [ARCH=arch] [COMP=compiler] [COMPCXX=cxx]" - @echo "" - @echo "Supported targets:" - @echo "" - @echo "help > Display architecture details" - @echo "profile-build > standard build with profile-guided optimization" - @echo "build > skip profile-guided optimization" - @echo "net > Download the default nnue nets" - @echo "strip > Strip executable" - @echo "install > Install executable" - @echo "clean > Clean up" - @echo "" - @echo "Supported archs:" - @echo "" - @echo "native > select the best architecture for the host processor (default)" - @echo "x86-64-vnni512 > x86 64-bit with vnni 512bit support" - @echo "x86-64-vnni256 > x86 64-bit with vnni 512bit support, limit operands to 256bit wide" - @echo "x86-64-avx512 > x86 64-bit with avx512 support" - @echo "x86-64-avxvnni > x86 64-bit with vnni 256bit support" - @echo "x86-64-bmi2 > x86 64-bit with bmi2 support" - @echo "x86-64-avx2 > x86 64-bit with avx2 support" - @echo "x86-64-sse41-popcnt > x86 64-bit with sse41 and popcnt support" - @echo "x86-64-modern > deprecated, currently x86-64-sse41-popcnt" - @echo "x86-64-ssse3 > x86 64-bit with ssse3 support" - @echo "x86-64-sse3-popcnt > x86 64-bit with sse3 compile and popcnt support" - @echo "x86-64 > x86 64-bit generic (with sse2 support)" - @echo "x86-32-sse41-popcnt > x86 32-bit with sse41 and popcnt support" - @echo "x86-32-sse2 > x86 32-bit with sse2 support" - @echo "x86-32 > x86 32-bit generic (with mmx compile support)" - @echo "ppc-64 > PPC 64-bit" - @echo "ppc-64-altivec > PPC 64-bit with altivec support" - @echo "ppc-64-vsx > PPC 64-bit with vsx support" - @echo "ppc-32 > PPC 32-bit" - @echo "armv7 > ARMv7 32-bit" - @echo "armv7-neon > ARMv7 32-bit with popcnt and neon" - @echo "armv8 > ARMv8 64-bit with popcnt and neon" - @echo "armv8-dotprod > ARMv8 64-bit with popcnt, neon and dot product support" - @echo "e2k > Elbrus 2000" - @echo "apple-silicon > Apple silicon ARM64" - @echo "general-64 > unspecified 64-bit" - @echo "general-32 > unspecified 32-bit" - @echo "riscv64 > RISC-V 64-bit" - @echo "loongarch64 > LoongArch 64-bit" - @echo "loongarch64-lsx > LoongArch 64-bit with SIMD eXtension" - @echo "loongarch64-lasx > LoongArch 64-bit with Advanced SIMD eXtension" - @echo "" - @echo "Supported compilers:" - @echo "" - @echo "gcc > GNU compiler (default)" - @echo "mingw > GNU compiler with MinGW under Windows" - @echo "clang > LLVM Clang compiler" - @echo "icx > Intel oneAPI DPC++/C++ Compiler" - @echo "ndk > Google NDK to cross-compile for Android" - @echo "" - @echo "Simple examples. If you don't know what to do, you likely want to run one of: " - @echo "" - @echo "make -j profile-build ARCH=x86-64-avx2 # typically a fast compile for common systems " - @echo "make -j profile-build ARCH=x86-64-sse41-popcnt # A more portable compile for 64-bit systems " - @echo "make -j profile-build ARCH=x86-64 # A portable compile for 64-bit systems " - @echo "" - @echo "Advanced examples, for experienced users: " - @echo "" - @echo "make -j profile-build ARCH=x86-64-avxvnni" - @echo "make -j profile-build ARCH=x86-64-avxvnni COMP=gcc COMPCXX=g++-12.0" - @echo "make -j build ARCH=x86-64-ssse3 COMP=clang" - @echo "" + @echo "" && \ + echo "To compile stockfish, type: " && \ + echo "" && \ + echo "make -j target [ARCH=arch] [COMP=compiler] [COMPCXX=cxx]" && \ + echo "" && \ + echo "Supported targets:" && \ + echo "" && \ + echo "help > Display architecture details" && \ + echo "profile-build > standard build with profile-guided optimization" && \ + echo "build > skip profile-guided optimization" && \ + echo "net > Download the default nnue nets" && \ + echo "strip > Strip executable" && \ + echo "install > Install executable" && \ + echo "clean > Clean up" && \ + echo "" && \ + echo "Supported archs:" && \ + echo "" && \ + echo "native > select the best architecture for the host processor (default)" && \ + echo "x86-64-vnni512 > x86 64-bit with vnni 512bit support" && \ + echo "x86-64-vnni256 > x86 64-bit with vnni 512bit support, limit operands to 256bit wide" && \ + echo "x86-64-avx512 > x86 64-bit with avx512 support" && \ + echo "x86-64-avxvnni > x86 64-bit with vnni 256bit support" && \ + echo "x86-64-bmi2 > x86 64-bit with bmi2 support" && \ + echo "x86-64-avx2 > x86 64-bit with avx2 support" && \ + echo "x86-64-sse41-popcnt > x86 64-bit with sse41 and popcnt support" && \ + echo "x86-64-modern > deprecated, currently x86-64-sse41-popcnt" && \ + echo "x86-64-ssse3 > x86 64-bit with ssse3 support" && \ + echo "x86-64-sse3-popcnt > x86 64-bit with sse3 compile and popcnt support" && \ + echo "x86-64 > x86 64-bit generic (with sse2 support)" && \ + echo "x86-32-sse41-popcnt > x86 32-bit with sse41 and popcnt support" && \ + echo "x86-32-sse2 > x86 32-bit with sse2 support" && \ + echo "x86-32 > x86 32-bit generic (with mmx compile support)" && \ + echo "ppc-64 > PPC 64-bit" && \ + echo "ppc-64-altivec > PPC 64-bit with altivec support" && \ + echo "ppc-64-vsx > PPC 64-bit with vsx support" && \ + echo "ppc-32 > PPC 32-bit" && \ + echo "armv7 > ARMv7 32-bit" && \ + echo "armv7-neon > ARMv7 32-bit with popcnt and neon" && \ + echo "armv8 > ARMv8 64-bit with popcnt and neon" && \ + echo "armv8-dotprod > ARMv8 64-bit with popcnt, neon and dot product support" && \ + echo "e2k > Elbrus 2000" && \ + echo "apple-silicon > Apple silicon ARM64" && \ + echo "general-64 > unspecified 64-bit" && \ + echo "general-32 > unspecified 32-bit" && \ + echo "riscv64 > RISC-V 64-bit" && \ + echo "loongarch64 > LoongArch 64-bit" && \ + echo "loongarch64-lsx > LoongArch 64-bit with SIMD eXtension" && \ + echo "loongarch64-lasx > LoongArch 64-bit with Advanced SIMD eXtension" && \ + echo "" && \ + echo "Supported compilers:" && \ + echo "" && \ + echo "gcc > GNU compiler (default)" && \ + echo "mingw > GNU compiler with MinGW under Windows" && \ + echo "clang > LLVM Clang compiler" && \ + echo "icx > Intel oneAPI DPC++/C++ Compiler" && \ + echo "ndk > Google NDK to cross-compile for Android" && \ + echo "" && \ + echo "Simple examples. If you don't know what to do, you likely want to run one of: " && \ + echo "" && \ + echo "make -j profile-build ARCH=x86-64-avx2 # typically a fast compile for common systems " && \ + echo "make -j profile-build ARCH=x86-64-sse41-popcnt # A more portable compile for 64-bit systems " && \ + echo "make -j profile-build ARCH=x86-64 # A portable compile for 64-bit systems " && \ + echo "" && \ + echo "Advanced examples, for experienced users: " && \ + echo "" && \ + echo "make -j profile-build ARCH=x86-64-avxvnni" && \ + echo "make -j profile-build ARCH=x86-64-avxvnni COMP=gcc COMPCXX=g++-12.0" && \ + echo "make -j build ARCH=x86-64-ssse3 COMP=clang" && \ + echo "" ifneq ($(SUPPORTED_ARCH), true) @echo "Specify a supported architecture with the ARCH option for more details" @echo "" @@ -1000,69 +1000,71 @@ all: $(EXE) .depend config-sanity: net @echo "" - @echo "Config:" - @echo "debug: '$(debug)'" - @echo "sanitize: '$(sanitize)'" - @echo "optimize: '$(optimize)'" - @echo "arch: '$(arch)'" - @echo "bits: '$(bits)'" - @echo "kernel: '$(KERNEL)'" - @echo "os: '$(OS)'" - @echo "prefetch: '$(prefetch)'" - @echo "popcnt: '$(popcnt)'" - @echo "pext: '$(pext)'" - @echo "sse: '$(sse)'" - @echo "mmx: '$(mmx)'" - @echo "sse2: '$(sse2)'" - @echo "ssse3: '$(ssse3)'" - @echo "sse41: '$(sse41)'" - @echo "avx2: '$(avx2)'" - @echo "avxvnni: '$(avxvnni)'" - @echo "avx512: '$(avx512)'" - @echo "vnni256: '$(vnni256)'" - @echo "vnni512: '$(vnni512)'" - @echo "altivec: '$(altivec)'" - @echo "vsx: '$(vsx)'" - @echo "neon: '$(neon)'" - @echo "dotprod: '$(dotprod)'" - @echo "arm_version: '$(arm_version)'" - @echo "lsx: '$(lsx)'" - @echo "lasx: '$(lasx)'" - @echo "target_windows: '$(target_windows)'" - @echo "" - @echo "Flags:" - @echo "CXX: $(CXX)" - @echo "CXXFLAGS: $(CXXFLAGS)" - @echo "LDFLAGS: $(LDFLAGS)" - @echo "" - @echo "Testing config sanity. If this fails, try 'make help' ..." - @echo "" - @test "$(debug)" = "yes" || test "$(debug)" = "no" - @test "$(optimize)" = "yes" || test "$(optimize)" = "no" - @test "$(SUPPORTED_ARCH)" = "true" - @test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \ + @echo "Config:" && \ + echo "debug: '$(debug)'" && \ + echo "sanitize: '$(sanitize)'" && \ + echo "optimize: '$(optimize)'" && \ + echo "arch: '$(arch)'" && \ + echo "bits: '$(bits)'" && \ + echo "kernel: '$(KERNEL)'" && \ + echo "os: '$(OS)'" && \ + echo "prefetch: '$(prefetch)'" && \ + echo "popcnt: '$(popcnt)'" && \ + echo "pext: '$(pext)'" && \ + echo "sse: '$(sse)'" && \ + echo "mmx: '$(mmx)'" && \ + echo "sse2: '$(sse2)'" && \ + echo "ssse3: '$(ssse3)'" && \ + echo "sse41: '$(sse41)'" && \ + echo "avx2: '$(avx2)'" && \ + echo "avxvnni: '$(avxvnni)'" && \ + echo "avx512: '$(avx512)'" && \ + echo "vnni256: '$(vnni256)'" && \ + echo "vnni512: '$(vnni512)'" && \ + echo "altivec: '$(altivec)'" && \ + echo "vsx: '$(vsx)'" && \ + echo "neon: '$(neon)'" && \ + echo "dotprod: '$(dotprod)'" && \ + echo "arm_version: '$(arm_version)'" && \ + echo "lsx: '$(lsx)'" && \ + echo "lasx: '$(lasx)'" && \ + echo "target_windows: '$(target_windows)'" && \ + echo "" && \ + echo "Flags:" && \ + echo "CXX: $(CXX)" && \ + echo "CXXFLAGS: $(CXXFLAGS)" && \ + echo "LDFLAGS: $(LDFLAGS)" && \ + echo "" && \ + echo "Testing config sanity. If this fails, try 'make help' ..." && \ + echo "" && \ + (test "$(debug)" = "yes" || test "$(debug)" = "no") && \ + (test "$(optimize)" = "yes" || test "$(optimize)" = "no") && \ + (test "$(SUPPORTED_ARCH)" = "true") && \ + (test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \ test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || test "$(arch)" = "e2k" || \ - test "$(arch)" = "armv7" || test "$(arch)" = "armv8" || test "$(arch)" = "arm64" || test "$(arch)" = "riscv64" || test "$(arch)" = "loongarch64" - @test "$(bits)" = "32" || test "$(bits)" = "64" - @test "$(prefetch)" = "yes" || test "$(prefetch)" = "no" - @test "$(popcnt)" = "yes" || test "$(popcnt)" = "no" - @test "$(pext)" = "yes" || test "$(pext)" = "no" - @test "$(sse)" = "yes" || test "$(sse)" = "no" - @test "$(mmx)" = "yes" || test "$(mmx)" = "no" - @test "$(sse2)" = "yes" || test "$(sse2)" = "no" - @test "$(ssse3)" = "yes" || test "$(ssse3)" = "no" - @test "$(sse41)" = "yes" || test "$(sse41)" = "no" - @test "$(avx2)" = "yes" || test "$(avx2)" = "no" - @test "$(avx512)" = "yes" || test "$(avx512)" = "no" - @test "$(vnni256)" = "yes" || test "$(vnni256)" = "no" - @test "$(vnni512)" = "yes" || test "$(vnni512)" = "no" - @test "$(altivec)" = "yes" || test "$(altivec)" = "no" - @test "$(vsx)" = "yes" || test "$(vsx)" = "no" - @test "$(neon)" = "yes" || test "$(neon)" = "no" - @test "$(lsx)" = "yes" || test "$(lsx)" = "no" - @test "$(lasx)" = "yes" || test "$(lasx)" = "no" - @test "$(comp)" = "gcc" || test "$(comp)" = "icx" || test "$(comp)" = "mingw" || test "$(comp)" = "clang" \ - || test "$(comp)" = "armv7a-linux-androideabi16-clang" || test "$(comp)" = "aarch64-linux-android21-clang" + test "$(arch)" = "armv7" || test "$(arch)" = "armv8" || test "$(arch)" = "arm64" || \ + test "$(arch)" = "riscv64" || test "$(arch)" = "loongarch64") && \ + (test "$(bits)" = "32" || test "$(bits)" = "64") && \ + (test "$(prefetch)" = "yes" || test "$(prefetch)" = "no") && \ + (test "$(popcnt)" = "yes" || test "$(popcnt)" = "no") && \ + (test "$(pext)" = "yes" || test "$(pext)" = "no") && \ + (test "$(sse)" = "yes" || test "$(sse)" = "no") && \ + (test "$(mmx)" = "yes" || test "$(mmx)" = "no") && \ + (test "$(sse2)" = "yes" || test "$(sse2)" = "no") && \ + (test "$(ssse3)" = "yes" || test "$(ssse3)" = "no") && \ + (test "$(sse41)" = "yes" || test "$(sse41)" = "no") && \ + (test "$(avx2)" = "yes" || test "$(avx2)" = "no") && \ + (test "$(avx512)" = "yes" || test "$(avx512)" = "no") && \ + (test "$(vnni256)" = "yes" || test "$(vnni256)" = "no") && \ + (test "$(vnni512)" = "yes" || test "$(vnni512)" = "no") && \ + (test "$(altivec)" = "yes" || test "$(altivec)" = "no") && \ + (test "$(vsx)" = "yes" || test "$(vsx)" = "no") && \ + (test "$(neon)" = "yes" || test "$(neon)" = "no") && \ + (test "$(lsx)" = "yes" || test "$(lsx)" = "no") && \ + (test "$(lasx)" = "yes" || test "$(lasx)" = "no") && \ + (test "$(comp)" = "gcc" || test "$(comp)" = "icx" || test "$(comp)" = "mingw" || \ + test "$(comp)" = "clang" || test "$(comp)" = "armv7a-linux-androideabi16-clang" || \ + test "$(comp)" = "aarch64-linux-android21-clang") $(EXE): $(OBJS) +$(CXX) -o $@ $(OBJS) $(LDFLAGS) From 8ef403c7869b2d3b7e480cedae97e97d3b271f56 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Mon, 21 Oct 2024 10:42:31 +0300 Subject: [PATCH 1751/1766] Small cleanup for stats adjustments After some simplifications bonuses and maluses are the same for quiet and non-quiet moves so it makes no sense to use quietMoveBonus/Malus, instead use just bonus/malus. closes https://github.com/official-stockfish/Stockfish/pull/5649 No functional change --- src/search.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c78acb6c40c..8a7bd8109f3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1791,35 +1791,35 @@ void update_all_stats(const Position& pos, Piece moved_piece = pos.moved_piece(bestMove); PieceType captured; - int quietMoveBonus = stat_bonus(depth); - int quietMoveMalus = stat_malus(depth); + int bonus = stat_bonus(depth); + int malus = stat_malus(depth); if (!pos.capture_stage(bestMove)) { - update_quiet_histories(pos, ss, workerThread, bestMove, quietMoveBonus); + update_quiet_histories(pos, ss, workerThread, bestMove, bonus); // Decrease stats for all non-best quiet moves for (Move move : quietsSearched) - update_quiet_histories(pos, ss, workerThread, move, -quietMoveMalus); + update_quiet_histories(pos, ss, workerThread, move, -malus); } else { // Increase stats for the best move in case it was a capture move captured = type_of(pos.piece_on(bestMove.to_sq())); - captureHistory[moved_piece][bestMove.to_sq()][captured] << quietMoveBonus; + captureHistory[moved_piece][bestMove.to_sq()][captured] << bonus; } // Extra penalty for a quiet early move that was not a TT move in // previous ply when it gets refuted. if (prevSq != SQ_NONE && ((ss - 1)->moveCount == 1 + (ss - 1)->ttHit) && !pos.captured_piece()) - update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, -quietMoveMalus); + update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, -malus); // Decrease stats for all non-best capture moves for (Move move : capturesSearched) { moved_piece = pos.moved_piece(move); captured = type_of(pos.piece_on(move.to_sq())); - captureHistory[moved_piece][move.to_sq()][captured] << -quietMoveMalus; + captureHistory[moved_piece][move.to_sq()][captured] << -malus; } } From 4a9c980f3bb666648054a9710ec0346561229312 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Tue, 22 Oct 2024 14:57:07 -0700 Subject: [PATCH 1752/1766] Template Corrhist Avoids duplication of `using ... = Stats;` closes https://github.com/official-stockfish/Stockfish/pull/5650 No functional change Co-authored-by: Disservin --- src/movepick.h | 41 ++++++++++++++++++++++------------------- src/search.h | 36 ++++++++++++++++++------------------ 2 files changed, 40 insertions(+), 37 deletions(-) diff --git a/src/movepick.h b/src/movepick.h index 6ad13397ab1..dff09f79c3e 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -146,9 +146,6 @@ using CapturePieceToHistory = Stats; -// PieceToCorrectionHistory is addressed by a move's [piece][to] -using PieceToCorrectionHistory = Stats; - // ContinuationHistory is the combined history of a given pair of moves, usually // the current one given a previous one. The nested history table is based on // PieceToHistory instead of ButterflyBoards. @@ -162,26 +159,32 @@ using PawnHistory = Stats // positions and their search score. It is used to improve the static evaluation // used by some search heuristics. // see https://www.chessprogramming.org/Static_Evaluation_Correction_History +enum CorrHistType { + Pawn, // By color and pawn structure + Major, // By color and positions of major pieces (Queen, Rook) and King + Minor, // By color and positions of minor pieces (Knight, Bishop) and King + NonPawn, // By color and non-pawn material positions + PieceTo, // By [piece][to] move + Continuation, // Combined history of move pairs +}; -// PawnCorrectionHistory is addressed by color and pawn structure -using PawnCorrectionHistory = - Stats; - -// MajorPieceCorrectionHistory is addressed by color and king/major piece (Queen, Rook) positions -using MajorPieceCorrectionHistory = - Stats; +template +struct CorrHistTypedef { + using type = Stats; +}; -// MinorPieceCorrectionHistory is addressed by color and king/minor piece (Knight, Bishop) positions -using MinorPieceCorrectionHistory = - Stats; +template<> +struct CorrHistTypedef { + using type = Stats; +}; -// NonPawnCorrectionHistory is addressed by color and non-pawn material positions -using NonPawnCorrectionHistory = - Stats; +template<> +struct CorrHistTypedef { + using type = Stats::type, NOT_USED, PIECE_NB, SQUARE_NB>; +}; -// ContinuationCorrectionHistory is the combined correction history of a given pair of moves -using ContinuationCorrectionHistory = - Stats; +template +using CorrectionHistory = typename CorrHistTypedef::type; // The MovePicker class is used to pick one pseudo-legal move at a time from the // current position. The most important method is next_move(), which emits one diff --git a/src/search.h b/src/search.h index b599da110a2..751a398483f 100644 --- a/src/search.h +++ b/src/search.h @@ -61,19 +61,19 @@ namespace Search { // shallower and deeper in the tree during the search. Each search thread has // its own array of Stack objects, indexed by the current ply. struct Stack { - Move* pv; - PieceToHistory* continuationHistory; - PieceToCorrectionHistory* continuationCorrectionHistory; - int ply; - Move currentMove; - Move excludedMove; - Value staticEval; - int statScore; - int moveCount; - bool inCheck; - bool ttPv; - bool ttHit; - int cutoffCnt; + Move* pv; + PieceToHistory* continuationHistory; + CorrectionHistory* continuationCorrectionHistory; + int ply; + Move currentMove; + Move excludedMove; + Value staticEval; + int statScore; + int moveCount; + bool inCheck; + bool ttPv; + bool ttHit; + int cutoffCnt; }; @@ -286,11 +286,11 @@ class Worker { ContinuationHistory continuationHistory[2][2]; PawnHistory pawnHistory; - PawnCorrectionHistory pawnCorrectionHistory; - MajorPieceCorrectionHistory majorPieceCorrectionHistory; - MinorPieceCorrectionHistory minorPieceCorrectionHistory; - NonPawnCorrectionHistory nonPawnCorrectionHistory[COLOR_NB]; - ContinuationCorrectionHistory continuationCorrectionHistory; + CorrectionHistory pawnCorrectionHistory; + CorrectionHistory majorPieceCorrectionHistory; + CorrectionHistory minorPieceCorrectionHistory; + CorrectionHistory nonPawnCorrectionHistory[COLOR_NB]; + CorrectionHistory continuationCorrectionHistory; private: void iterative_deepening(); From 8681d3c2b38096120829c2fb47acaeb32b2fbf8b Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Fri, 25 Oct 2024 15:28:10 +0300 Subject: [PATCH 1753/1766] Simplify Time Management Formula Decreasing the number of operations Passed STC: LLR: 2.97 (-2.94,2.94) <-1.75,0.25> Total: 38880 W: 10038 L: 9823 D: 19019 Ptnml(0-2): 92, 4334, 10395, 4505, 114 https://tests.stockfishchess.org/tests/view/67112bf586d5ee47d953c6be Passed LTC: LLR: 2.94 (-2.94,2.94) <-1.75,0.25> Total: 242844 W: 61425 L: 61431 D: 119988 Ptnml(0-2): 145, 25175, 70797, 25151, 154 https://tests.stockfishchess.org/tests/view/6712387486d5ee47d953c737 closes https://github.com/official-stockfish/Stockfish/pull/5655 Bench: 1281912 --- src/search.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 8a7bd8109f3..c7a8c28b474 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -443,9 +443,9 @@ void Search::Worker::iterative_deepening() { { int nodesEffort = rootMoves[0].effort * 100 / std::max(size_t(1), size_t(nodes)); - double fallingEval = (1067 + 223 * (mainThread->bestPreviousAverageScore - bestValue) - + 97 * (mainThread->iterValue[iterIdx] - bestValue)) - / 10000.0; + double fallingEval = (11 + 2 * (mainThread->bestPreviousAverageScore - bestValue) + + (mainThread->iterValue[iterIdx] - bestValue)) + / 100.0; fallingEval = std::clamp(fallingEval, 0.580, 1.667); // If the bestMove is stable over several iterations, reduce time accordingly From 24c57793e1917b2110d1e3ce8edc634f43eadc67 Mon Sep 17 00:00:00 2001 From: MinetaS Date: Wed, 30 Oct 2024 21:30:21 +0900 Subject: [PATCH 1754/1766] Remove moveCountPruning in search.cpp The definition of moveCountPruning may cause confusion by implying that the variable is unconstrained. However, once it is set to true, it should not be reset to false, otherwise it would break the internal logic of MovePicker. Several patches have overlooked this constraint. For example: https://tests.stockfishchess.org/tests/view/671e7c0486d5ee47d953d226 https://tests.stockfishchess.org/tests/view/66a1de7b4ff211be9d4eccea The implementation approach was suggested by Disservin. Passed non-regression STC: LLR: 3.02 (-2.94,2.94) <-1.75,0.25> Total: 180672 W: 47072 L: 47006 D: 86594 Ptnml(0-2): 536, 19482, 50247, 19522, 549 https://tests.stockfishchess.org/tests/view/6720df6f86d5ee47d953d542 closes https://github.com/official-stockfish/Stockfish/pull/5661 No functional change --- src/movepick.cpp | 4 +++- src/movepick.h | 4 +++- src/search.cpp | 8 ++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/movepick.cpp b/src/movepick.cpp index 0649518938a..2a1fb837348 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -216,7 +216,7 @@ Move MovePicker::select(Pred filter) { // This is the most important method of the MovePicker class. We emit one // new pseudo-legal move on every call until there are no more moves left, // picking the move with the highest score from a list of generated moves. -Move MovePicker::next_move(bool skipQuiets) { +Move MovePicker::next_move() { auto quiet_threshold = [](Depth d) { return -3560 * d; }; @@ -322,4 +322,6 @@ Move MovePicker::next_move(bool skipQuiets) { return Move::none(); // Silence warning } +void MovePicker::skip_quiet_moves() { skipQuiets = true; } + } // namespace Stockfish diff --git a/src/movepick.h b/src/movepick.h index dff09f79c3e..f8f84d02474 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -212,7 +212,8 @@ class MovePicker { const PawnHistory*, int); MovePicker(const Position&, Move, int, const CapturePieceToHistory*); - Move next_move(bool skipQuiets = false); + Move next_move(); + void skip_quiet_moves(); private: template @@ -234,6 +235,7 @@ class MovePicker { int threshold; Depth depth; int ply; + bool skipQuiets = false; ExtMove moves[MAX_MOVES]; }; diff --git a/src/search.cpp b/src/search.cpp index c7a8c28b474..4864057c1b1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -942,12 +942,11 @@ Value Search::Worker::search( value = bestValue; - int moveCount = 0; - bool moveCountPruning = false; + int moveCount = 0; // Step 13. Loop through all pseudo-legal moves until no moves remain // or a beta cutoff occurs. - while ((move = mp.next_move(moveCountPruning)) != Move::none()) + while ((move = mp.next_move()) != Move::none()) { assert(move.is_ok()); @@ -993,7 +992,8 @@ Value Search::Worker::search( if (!rootNode && pos.non_pawn_material(us) && bestValue > VALUE_TB_LOSS_IN_MAX_PLY) { // Skip quiet moves if movecount exceeds our FutilityMoveCount threshold (~8 Elo) - moveCountPruning = moveCount >= futility_move_count(improving, depth); + if (moveCount >= futility_move_count(improving, depth)) + mp.skip_quiet_moves(); // Reduced depth of the next LMR search int lmrDepth = newDepth - r; From 16fee2a7da25c6d0267930eb9677862cb1f009c7 Mon Sep 17 00:00:00 2001 From: mstembera Date: Wed, 23 Oct 2024 03:37:32 -0700 Subject: [PATCH 1755/1766] Cleanup TT::hashfull() closes https://github.com/official-stockfish/Stockfish/pull/5651 No functional change --- src/tt.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/tt.cpp b/src/tt.cpp index 507507539e5..75689562d61 100644 --- a/src/tt.cpp +++ b/src/tt.cpp @@ -194,19 +194,12 @@ void TranspositionTable::clear(ThreadPool& threads) { // occupation during a search. The hash is x permill full, as per UCI protocol. // Only counts entries which match the current generation. int TranspositionTable::hashfull(int maxAge) const { - int cnt = 0; + int maxAgeInternal = maxAge << GENERATION_BITS; + int cnt = 0; for (int i = 0; i < 1000; ++i) for (int j = 0; j < ClusterSize; ++j) - { - if (table[i].entry[j].is_occupied()) - { - int age = (generation8 >> GENERATION_BITS) - - ((table[i].entry[j].genBound8 & GENERATION_MASK) >> GENERATION_BITS); - if (age < 0) - age += 1 << (8 - GENERATION_BITS); - cnt += age <= maxAge; - } - } + cnt += table[i].entry[j].is_occupied() + && table[i].entry[j].relative_age(generation8) <= maxAgeInternal; return cnt / ClusterSize; } From c2611efe5c317969b583a5ff81352439f905e722 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Thu, 24 Oct 2024 13:16:49 -0700 Subject: [PATCH 1756/1766] Move history code to a separate header Since no correction histories are ever used inside Movepick, and many existing histories are closely integrated into search, it might be more logical to separate them into their own header. PR based on #5650 closes https://github.com/official-stockfish/Stockfish/pull/5652 No functional change --- src/Makefile | 2 +- src/history.h | 185 +++++++++++++++++++++++++++++++++++++++++++++++ src/movepick.cpp | 2 + src/movepick.h | 163 +---------------------------------------- src/search.cpp | 1 + src/search.h | 2 +- 6 files changed, 192 insertions(+), 163 deletions(-) create mode 100644 src/history.h diff --git a/src/Makefile b/src/Makefile index 4307b7c7473..e7f8ce556bb 100644 --- a/src/Makefile +++ b/src/Makefile @@ -57,7 +57,7 @@ SRCS = benchmark.cpp bitboard.cpp evaluate.cpp main.cpp \ search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \ nnue/nnue_misc.cpp nnue/features/half_ka_v2_hm.cpp nnue/network.cpp engine.cpp score.cpp memory.cpp -HEADERS = benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h \ +HEADERS = benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h history.h \ nnue/nnue_misc.h nnue/features/half_ka_v2_hm.h nnue/layers/affine_transform.h \ nnue/layers/affine_transform_sparse_input.h nnue/layers/clipped_relu.h nnue/layers/simd.h \ nnue/layers/sqr_clipped_relu.h nnue/nnue_accumulator.h nnue/nnue_architecture.h \ diff --git a/src/history.h b/src/history.h new file mode 100644 index 00000000000..8d14a7a7cb1 --- /dev/null +++ b/src/history.h @@ -0,0 +1,185 @@ +/* + Stockfish, a UCI chess playing engine derived from Glaurung 2.1 + Copyright (C) 2004-2024 The Stockfish developers (see AUTHORS file) + + Stockfish is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Stockfish is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef HISTORY_H_INCLUDED +#define HISTORY_H_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: keep + +#include "position.h" + +namespace Stockfish { + +constexpr int PAWN_HISTORY_SIZE = 512; // has to be a power of 2 +constexpr int CORRECTION_HISTORY_SIZE = 32768; // has to be a power of 2 +constexpr int CORRECTION_HISTORY_LIMIT = 1024; +constexpr int LOW_PLY_HISTORY_SIZE = 4; + +static_assert((PAWN_HISTORY_SIZE & (PAWN_HISTORY_SIZE - 1)) == 0, + "PAWN_HISTORY_SIZE has to be a power of 2"); + +static_assert((CORRECTION_HISTORY_SIZE & (CORRECTION_HISTORY_SIZE - 1)) == 0, + "CORRECTION_HISTORY_SIZE has to be a power of 2"); + +enum PawnHistoryType { + Normal, + Correction +}; + +template +inline int pawn_structure_index(const Position& pos) { + return pos.pawn_key() & ((T == Normal ? PAWN_HISTORY_SIZE : CORRECTION_HISTORY_SIZE) - 1); +} + +inline int major_piece_index(const Position& pos) { + return pos.major_piece_key() & (CORRECTION_HISTORY_SIZE - 1); +} + +inline int minor_piece_index(const Position& pos) { + return pos.minor_piece_key() & (CORRECTION_HISTORY_SIZE - 1); +} + +template +inline int non_pawn_index(const Position& pos) { + return pos.non_pawn_key(c) & (CORRECTION_HISTORY_SIZE - 1); +} + +// StatsEntry stores the stat table value. It is usually a number but could +// be a move or even a nested history. We use a class instead of a naked value +// to directly call history update operator<<() on the entry so to use stats +// tables at caller sites as simple multi-dim arrays. +template +class StatsEntry { + + T entry; + + public: + void operator=(const T& v) { entry = v; } + T* operator&() { return &entry; } + T* operator->() { return &entry; } + operator const T&() const { return entry; } + + void operator<<(int bonus) { + static_assert(D <= std::numeric_limits::max(), "D overflows T"); + + // Make sure that bonus is in range [-D, D] + int clampedBonus = std::clamp(bonus, -D, D); + entry += clampedBonus - entry * std::abs(clampedBonus) / D; + + assert(std::abs(entry) <= D); + } +}; + +// Stats is a generic N-dimensional array used to store various statistics. +// The first template parameter T is the base type of the array, and the second +// template parameter D limits the range of updates in [-D, D] when we update +// values with the << operator, while the last parameters (Size and Sizes) +// encode the dimensions of the array. +template +struct Stats: public std::array, Size> { + using stats = Stats; + + void fill(const T& v) { + + // For standard-layout 'this' points to the first struct member + assert(std::is_standard_layout_v); + + using entry = StatsEntry; + entry* p = reinterpret_cast(this); + std::fill(p, p + sizeof(*this) / sizeof(entry), v); + } +}; + +template +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 ordering decisions. +// It uses 2 tables (one for each color) indexed by the move's from and to squares, +// see https://www.chessprogramming.org/Butterfly_Boards (~11 elo) +using ButterflyHistory = Stats; + +// LowPlyHistory is adressed by play and move's from and to squares, used +// to improve move ordering near the root +using LowPlyHistory = Stats; + +// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type] +using CapturePieceToHistory = Stats; + +// PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to] +using PieceToHistory = Stats; + +// ContinuationHistory is the combined history of a given pair of moves, usually +// the current one given a previous one. The nested history table is based on +// PieceToHistory instead of ButterflyBoards. +// (~63 elo) +using ContinuationHistory = Stats; + +// PawnHistory is addressed by the pawn structure and a move's [piece][to] +using PawnHistory = Stats; + +// Correction histories record differences between the static evaluation of +// positions and their search score. It is used to improve the static evaluation +// used by some search heuristics. +// see https://www.chessprogramming.org/Static_Evaluation_Correction_History +enum CorrHistType { + Pawn, // By color and pawn structure + Major, // By color and positions of major pieces (Queen, Rook) and King + Minor, // By color and positions of minor pieces (Knight, Bishop) and King + NonPawn, // By color and non-pawn material positions + PieceTo, // By [piece][to] move + Continuation, // Combined history of move pairs +}; + +template +struct CorrHistTypedef { + using type = Stats; +}; + +template<> +struct CorrHistTypedef { + using type = Stats; +}; + +template<> +struct CorrHistTypedef { + using type = Stats::type, NOT_USED, PIECE_NB, SQUARE_NB>; +}; + +template +using CorrectionHistory = typename CorrHistTypedef::type; + +} // namespace Stockfish + +#endif // #ifndef HISTORY_H_INCLUDED diff --git a/src/movepick.cpp b/src/movepick.cpp index 2a1fb837348..720f2e031ea 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -19,7 +19,9 @@ #include "movepick.h" #include +#include #include +#include #include #include "bitboard.h" diff --git a/src/movepick.h b/src/movepick.h index f8f84d02474..0278b70ec82 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -19,172 +19,13 @@ #ifndef MOVEPICK_H_INCLUDED #define MOVEPICK_H_INCLUDED -#include -#include -#include -#include -#include -#include -#include -#include // IWYU pragma: keep - +#include "history.h" #include "movegen.h" -#include "position.h" #include "types.h" namespace Stockfish { -constexpr int PAWN_HISTORY_SIZE = 512; // has to be a power of 2 -constexpr int CORRECTION_HISTORY_SIZE = 32768; // has to be a power of 2 -constexpr int CORRECTION_HISTORY_LIMIT = 1024; -constexpr int LOW_PLY_HISTORY_SIZE = 4; - -static_assert((PAWN_HISTORY_SIZE & (PAWN_HISTORY_SIZE - 1)) == 0, - "PAWN_HISTORY_SIZE has to be a power of 2"); - -static_assert((CORRECTION_HISTORY_SIZE & (CORRECTION_HISTORY_SIZE - 1)) == 0, - "CORRECTION_HISTORY_SIZE has to be a power of 2"); - -enum PawnHistoryType { - Normal, - Correction -}; - -template -inline int pawn_structure_index(const Position& pos) { - return pos.pawn_key() & ((T == Normal ? PAWN_HISTORY_SIZE : CORRECTION_HISTORY_SIZE) - 1); -} - -inline int material_index(const Position& pos) { - return pos.material_key() & (CORRECTION_HISTORY_SIZE - 1); -} - -inline int major_piece_index(const Position& pos) { - return pos.major_piece_key() & (CORRECTION_HISTORY_SIZE - 1); -} - -inline int minor_piece_index(const Position& pos) { - return pos.minor_piece_key() & (CORRECTION_HISTORY_SIZE - 1); -} - -template -inline int non_pawn_index(const Position& pos) { - return pos.non_pawn_key(c) & (CORRECTION_HISTORY_SIZE - 1); -} - -// StatsEntry stores the stat table value. It is usually a number but could -// be a move or even a nested history. We use a class instead of a naked value -// to directly call history update operator<<() on the entry so to use stats -// tables at caller sites as simple multi-dim arrays. -template -class StatsEntry { - - T entry; - - public: - void operator=(const T& v) { entry = v; } - T* operator&() { return &entry; } - T* operator->() { return &entry; } - operator const T&() const { return entry; } - - void operator<<(int bonus) { - static_assert(D <= std::numeric_limits::max(), "D overflows T"); - - // Make sure that bonus is in range [-D, D] - int clampedBonus = std::clamp(bonus, -D, D); - entry += clampedBonus - entry * std::abs(clampedBonus) / D; - - assert(std::abs(entry) <= D); - } -}; - -// Stats is a generic N-dimensional array used to store various statistics. -// The first template parameter T is the base type of the array, and the second -// template parameter D limits the range of updates in [-D, D] when we update -// values with the << operator, while the last parameters (Size and Sizes) -// encode the dimensions of the array. -template -struct Stats: public std::array, Size> { - using stats = Stats; - - void fill(const T& v) { - - // For standard-layout 'this' points to the first struct member - assert(std::is_standard_layout_v); - - using entry = StatsEntry; - entry* p = reinterpret_cast(this); - std::fill(p, p + sizeof(*this) / sizeof(entry), v); - } -}; - -template -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 ordering decisions. -// It uses 2 tables (one for each color) indexed by the move's from and to squares, -// see https://www.chessprogramming.org/Butterfly_Boards (~11 elo) -using ButterflyHistory = Stats; - -// LowPlyHistory is adressed by play and move's from and to squares, used -// to improve move ordering near the root -using LowPlyHistory = Stats; - -// CapturePieceToHistory is addressed by a move's [piece][to][captured piece type] -using CapturePieceToHistory = Stats; - -// PieceToHistory is like ButterflyHistory but is addressed by a move's [piece][to] -using PieceToHistory = Stats; - -// ContinuationHistory is the combined history of a given pair of moves, usually -// the current one given a previous one. The nested history table is based on -// PieceToHistory instead of ButterflyBoards. -// (~63 elo) -using ContinuationHistory = Stats; - -// PawnHistory is addressed by the pawn structure and a move's [piece][to] -using PawnHistory = Stats; - -// Correction histories record differences between the static evaluation of -// positions and their search score. It is used to improve the static evaluation -// used by some search heuristics. -// see https://www.chessprogramming.org/Static_Evaluation_Correction_History -enum CorrHistType { - Pawn, // By color and pawn structure - Major, // By color and positions of major pieces (Queen, Rook) and King - Minor, // By color and positions of minor pieces (Knight, Bishop) and King - NonPawn, // By color and non-pawn material positions - PieceTo, // By [piece][to] move - Continuation, // Combined history of move pairs -}; - -template -struct CorrHistTypedef { - using type = Stats; -}; - -template<> -struct CorrHistTypedef { - using type = Stats; -}; - -template<> -struct CorrHistTypedef { - using type = Stats::type, NOT_USED, PIECE_NB, SQUARE_NB>; -}; - -template -using CorrectionHistory = typename CorrHistTypedef::type; +class Position; // The MovePicker class is used to pick one pseudo-legal move at a time from the // current position. The most important method is next_move(), which emits one diff --git a/src/search.cpp b/src/search.cpp index 4864057c1b1..d6914da0b67 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -34,6 +34,7 @@ #include #include "evaluate.h" +#include "history.h" #include "misc.h" #include "movegen.h" #include "movepick.h" diff --git a/src/search.h b/src/search.h index 751a398483f..b618855b9fc 100644 --- a/src/search.h +++ b/src/search.h @@ -31,8 +31,8 @@ #include #include +#include "history.h" #include "misc.h" -#include "movepick.h" #include "nnue/network.h" #include "nnue/nnue_accumulator.h" #include "numa.h" From ecf5646f6e8446a3498aca04723dcee2b74f2d77 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Fri, 1 Nov 2024 02:04:35 +0300 Subject: [PATCH 1757/1766] Refine definition of improving This patch also allows improving flag to be true if static evaluation of the position is good enough. Passed STC: https://tests.stockfishchess.org/tests/view/6720906086d5ee47d953d4d0 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 34816 W: 9172 L: 8858 D: 16786 Ptnml(0-2): 113, 3988, 8887, 4312, 108 Passed LTC: https://tests.stockfishchess.org/tests/view/6721162686d5ee47d953d597 LLR: 2.96 (-2.94,2.94) <0.50,2.50> Total: 145374 W: 37118 L: 36574 D: 71682 Ptnml(0-2): 91, 15875, 40212, 16417, 92 closes https://github.com/official-stockfish/Stockfish/pull/5662 Bench: 1518856 --- src/search.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/search.cpp b/src/search.cpp index d6914da0b67..1b9b745cace 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -795,6 +795,8 @@ Value Search::Worker::search( && eval < VALUE_TB_WIN_IN_MAX_PLY) return beta + (eval - beta) / 3; + improving |= ss->staticEval >= beta + 100; + // Step 9. Null move search with verification search (~35 Elo) if (cutNode && (ss - 1)->currentMove != Move::null() && eval >= beta && ss->staticEval >= beta - 23 * depth + 400 && !excludedMove && pos.non_pawn_material(us) From 54cf226604cfc9d17f432fa0b5bca56277e5561c Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Fri, 1 Nov 2024 13:54:50 +0300 Subject: [PATCH 1758/1766] Revert VLTC regression from #5634 https://tests.stockfishchess.org/tests/view/671bf61b86d5ee47d953cf23 And thanks to @xu-shawn for suggesting running a VLTC regress test since depth modifications affect scaling. Also, the LTC was showing a slight regress after 680+k games ~= -0.34 , for reference: https://tests.stockfishchess.org/tests/view/67042b1f86d5ee47d953be7c closes https://github.com/official-stockfish/Stockfish/pull/5663 Bench: 1307308 --- src/search.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 1b9b745cace..5c6a62c8b47 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -850,7 +850,7 @@ Value Search::Worker::search( // For cutNodes, if depth is high enough, decrease depth by 2 if there is no ttMove, // or by 1 if there is a ttMove with an upper bound. if (cutNode && depth >= 7 && (!ttData.move || ttData.bound == BOUND_UPPER)) - depth -= 2; + depth -= 1 + !ttData.move; // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search From f77bac3dcab84a31238289ade55f9d85b650ac1a Mon Sep 17 00:00:00 2001 From: mstembera Date: Sun, 3 Nov 2024 16:50:47 -0800 Subject: [PATCH 1759/1766] Remove stale Cache::clear() method closes https://github.com/official-stockfish/Stockfish/pull/5666 No functional change --- src/nnue/nnue_accumulator.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/nnue/nnue_accumulator.h b/src/nnue/nnue_accumulator.h index b8dcf1e480f..b92901e4a29 100644 --- a/src/nnue/nnue_accumulator.h +++ b/src/nnue/nnue_accumulator.h @@ -80,11 +80,6 @@ struct AccumulatorCaches { entry.clear(network.featureTransformer->biases); } - void clear(const BiasType* biases) { - for (auto& entry : entries) - entry.clear(biases); - } - std::array& operator[](Square sq) { return entries[sq]; } std::array, SQUARE_NB> entries; From cc5c67c564f52a0611ba38d04af02636291280b6 Mon Sep 17 00:00:00 2001 From: Shawn Xu Date: Sun, 27 Oct 2024 14:07:03 -0700 Subject: [PATCH 1760/1766] Introduce Fractional LMR Tuning Run (90k Games): https://tests.stockfishchess.org/tests/view/67202b1c86d5ee47d953d442 Passed STC: LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 241024 W: 62616 L: 62001 D: 116407 Ptnml(0-2): 716, 28231, 62015, 28822, 728 https://tests.stockfishchess.org/tests/view/6725196786d5ee47d953d9f2 Passed LTC: LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 92532 W: 23678 L: 23246 D: 45608 Ptnml(0-2): 45, 9981, 25797, 10383, 60 https://tests.stockfishchess.org/tests/view/6727d3cb86d5ee47d953db9d closes https://github.com/official-stockfish/Stockfish/pull/5667 Bench: 1066071 --- src/search.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5c6a62c8b47..c807f1bdf88 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -999,7 +999,7 @@ Value Search::Worker::search( mp.skip_quiet_moves(); // Reduced depth of the next LMR search - int lmrDepth = newDepth - r; + int lmrDepth = newDepth - r / 1024; if (capture || givesCheck) { @@ -1156,36 +1156,36 @@ Value Search::Worker::search( // Decrease reduction if position is or has been on the PV (~7 Elo) if (ss->ttPv) - r -= 1 + (ttData.value > alpha) + (ttData.depth >= depth); + r -= 1024 + (ttData.value > alpha) * 1024 + (ttData.depth >= depth) * 1024; // Decrease reduction for PvNodes (~0 Elo on STC, ~2 Elo on LTC) if (PvNode) - r--; + r -= 1024; // These reduction adjustments have no proven non-linear scaling // Increase reduction for cut nodes (~4 Elo) if (cutNode) - r += 2 - (ttData.depth >= depth && ss->ttPv); + r += 2518 - (ttData.depth >= depth && ss->ttPv) * 991; // Increase reduction if ttMove is a capture but the current move is not a capture (~3 Elo) if (ttCapture && !capture) - r += 1 + (depth < 8); + r += 1043 + (depth < 8) * 999; // Increase reduction if next ply has a lot of fail high (~5 Elo) if ((ss + 1)->cutoffCnt > 3) - r += 1 + allNode; + r += 938 + allNode * 960; // For first picked move (ttMove) reduce reduction (~3 Elo) else if (move == ttData.move) - r -= 2; + r -= 1879; ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + (*contHist[0])[movedPiece][move.to_sq()] + (*contHist[1])[movedPiece][move.to_sq()] - 4410; // Decrease/increase reduction for moves with a good/bad history (~8 Elo) - r -= ss->statScore / 11016; + r -= ss->statScore * 1287 / 16384; // Step 17. Late moves reduction / extension (LMR, ~117 Elo) if (depth >= 2 && moveCount > 1) @@ -1195,7 +1195,7 @@ Value Search::Worker::search( // beyond the first move depth. // To prevent problems when the max value is less than the min value, // std::clamp has been replaced by a more robust implementation. - Depth d = std::max(1, std::min(newDepth - r, newDepth + !allNode)); + Depth d = std::max(1, std::min(newDepth - r / 1024, newDepth + !allNode)); value = -search(pos, ss + 1, -(alpha + 1), -alpha, d, true); @@ -1223,10 +1223,11 @@ Value Search::Worker::search( { // Increase reduction if ttMove is not present (~6 Elo) if (!ttData.move) - r += 2; + r += 2037; // Note that if expected reduction is high, we reduce search depth by 1 here (~9 Elo) - value = -search(pos, ss + 1, -(alpha + 1), -alpha, newDepth - (r > 3), !cutNode); + value = + -search(pos, ss + 1, -(alpha + 1), -alpha, newDepth - (r > 2983), !cutNode); } // For PV nodes only, do a full PV search on the first move or after a fail high, @@ -1700,7 +1701,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) Depth Search::Worker::reduction(bool i, Depth d, int mn, int delta) const { int reductionScale = reductions[d] * reductions[mn]; - return (reductionScale + 1239 - delta * 795 / rootDelta) / 1024 + (!i && reductionScale > 1341); + return (reductionScale + 1239 - delta * 795 / rootDelta) + (!i && reductionScale > 1341) * 1135; } // elapsed() returns the time elapsed since the search started. If the From 3d084e9164a96bff265b3afb32f2da0aa4e97c47 Mon Sep 17 00:00:00 2001 From: Muzhen Gaming <61100393+XInTheDark@users.noreply.github.com> Date: Wed, 13 Nov 2024 20:18:36 +0100 Subject: [PATCH 1761/1766] VVLTC Search Tune A single tuning run of 190k games was conducted: https://tests.stockfishchess.org/tests/view/670f3e3786d5ee47d953c554. Passed VVLTC 1st sprt: https://tests.stockfishchess.org/tests/view/672344dc86d5ee47d953d8c3 LLR: 2.94 (-2.94,2.94) <0.00,2.00> Total: 56768 W: 14615 L: 14323 D: 27830 Ptnml(0-2): 3, 5152, 17789, 5430, 10 Passed VVLTC 2nd sprt (rebased): https://tests.stockfishchess.org/tests/view/6726d83786d5ee47d953db03 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 57884 W: 14885 L: 14554 D: 28445 Ptnml(0-2): 5, 5300, 17999, 5635, 3 closes https://github.com/official-stockfish/Stockfish/pull/5669 Bench: 920336 --- src/search.cpp | 102 ++++++++++++++++++++++++------------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index c807f1bdf88..2a2331cb40e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -67,7 +67,7 @@ namespace { // Futility margin Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) { - Value futilityMult = 118 - 33 * noTtCutNode; + Value futilityMult = 109 - 27 * noTtCutNode; Value improvingDeduction = improving * futilityMult * 2; Value worseningDeduction = oppWorsening * futilityMult / 3; @@ -94,16 +94,16 @@ Value to_corrected_static_eval(Value v, const Worker& w, const Position& pos, St cntcv = int((*(ss - 2)->continuationCorrectionHistory)[pos.piece_on(m.to_sq())][m.to_sq()]); const auto cv = - (5932 * pcv + 3269 * macv + 5660 * micv + 6666 * (wnpcv + bnpcv) + 5555 * cntcv) / 131072; + (6384 * pcv + 3583 * macv + 6492 * micv + 6725 * (wnpcv + bnpcv) + cntcv * 5880) / 131072; v += cv; return std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1); } // History and stats update bonus, based on depth -int stat_bonus(Depth d) { return std::min(179 * d - 108, 1598); } +int stat_bonus(Depth d) { return std::min(168 * d - 100, 1718); } // History and stats update malus, based on depth -int stat_malus(Depth d) { return std::min(820 * d - 261, 2246); } +int stat_malus(Depth d) { return std::min(768 * d - 257, 2351); } // Add a small random component to draw evaluations to avoid 3-fold blindness Value value_draw(size_t nodes) { return VALUE_DRAW - 1 + Value(nodes & 0x2); } @@ -311,13 +311,13 @@ void Search::Worker::iterative_deepening() { selDepth = 0; // Reset aspiration window starting size - delta = 5 + std::abs(rootMoves[pvIdx].meanSquaredScore) / 13797; + delta = 5 + std::abs(rootMoves[pvIdx].meanSquaredScore) / 13461; Value avg = rootMoves[pvIdx].averageScore; alpha = std::max(avg - delta, -VALUE_INFINITE); beta = std::min(avg + delta, VALUE_INFINITE); // Adjust optimism based on root move's averageScore (~4 Elo) - optimism[us] = 132 * avg / (std::abs(avg) + 89); + optimism[us] = 150 * avg / (std::abs(avg) + 85); optimism[~us] = -optimism[us]; // Start with a small aspiration window and, in the case of a fail @@ -502,8 +502,8 @@ void Search::Worker::iterative_deepening() { void Search::Worker::clear() { mainHistory.fill(0); lowPlyHistory.fill(0); - captureHistory.fill(-753); - pawnHistory.fill(-1152); + captureHistory.fill(-758); + pawnHistory.fill(-1158); pawnCorrectionHistory.fill(0); majorPieceCorrectionHistory.fill(0); minorPieceCorrectionHistory.fill(0); @@ -518,10 +518,10 @@ void Search::Worker::clear() { for (StatsType c : {NoCaptures, Captures}) for (auto& to : continuationHistory[inCheck][c]) for (auto& h : to) - h->fill(-678); + h->fill(-645); for (size_t i = 1; i < reductions.size(); ++i) - reductions[i] = int((18.43 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); + reductions[i] = int((19.43 + std::log(size_t(options["Threads"])) / 2) * std::log(i)); refreshTable.clear(networks[numaAccessToken]); } @@ -760,7 +760,7 @@ Value Search::Worker::search( // Use static evaluation difference to improve quiet move ordering (~9 Elo) if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture) { - int bonus = std::clamp(-10 * int((ss - 1)->staticEval + ss->staticEval), -1641, 1423) + 760; + int bonus = std::clamp(-10 * int((ss - 1)->staticEval + ss->staticEval), -1831, 1428) + 623; thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) thisThread->pawnHistory[pawn_structure_index(pos)][pos.piece_on(prevSq)][prevSq] @@ -778,7 +778,7 @@ Value Search::Worker::search( // Step 7. Razoring (~1 Elo) // If eval is really low, check with qsearch if we can exceed alpha. If the // search suggests we cannot exceed alpha, return a speculative fail low. - if (eval < alpha - 501 - 272 * depth * depth) + if (eval < alpha - 469 - 307 * depth * depth) { value = qsearch(pos, ss, alpha - 1, alpha); if (value < alpha && std::abs(value) < VALUE_TB_WIN_IN_MAX_PLY) @@ -787,9 +787,9 @@ Value Search::Worker::search( // Step 8. Futility pruning: child node (~40 Elo) // The depth condition is important for mate finding. - if (!ss->ttPv && depth < 13 + if (!ss->ttPv && depth < 14 && eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening) - - (ss - 1)->statScore / 272 + - (ss - 1)->statScore / 290 >= beta && eval >= beta && (!ttData.move || ttCapture) && beta > VALUE_TB_LOSS_IN_MAX_PLY && eval < VALUE_TB_WIN_IN_MAX_PLY) @@ -799,13 +799,13 @@ Value Search::Worker::search( // Step 9. Null move search with verification search (~35 Elo) if (cutNode && (ss - 1)->currentMove != Move::null() && eval >= beta - && ss->staticEval >= beta - 23 * depth + 400 && !excludedMove && pos.non_pawn_material(us) + && ss->staticEval >= beta - 21 * depth + 421 && !excludedMove && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly && beta > VALUE_TB_LOSS_IN_MAX_PLY) { assert(eval - beta >= 0); // Null move dynamic reduction based on depth and eval - Depth R = std::min(int(eval - beta) / 209, 6) + depth / 3 + 5; + Depth R = std::min(int(eval - beta) / 235, 7) + depth / 3 + 5; ss->currentMove = Move::null(); ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0]; @@ -855,7 +855,7 @@ Value Search::Worker::search( // Step 11. ProbCut (~10 Elo) // If we have a good enough capture (or queen promotion) and a reduced search // returns a value much above beta, we can (almost) safely prune the previous move. - probCutBeta = beta + 189 - 53 * improving - 30 * opponentWorsening; + probCutBeta = beta + 187 - 53 * improving - 27 * opponentWorsening; if (!PvNode && depth > 3 && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY // If value from transposition table is lower than probCutBeta, don't attempt @@ -926,7 +926,7 @@ Value Search::Worker::search( moves_loop: // When in check, search starts here // Step 12. A small Probcut idea (~4 Elo) - probCutBeta = beta + 379; + probCutBeta = beta + 417; if ((ttData.bound & BOUND_LOWER) && ttData.depth >= depth - 4 && ttData.value >= probCutBeta && std::abs(beta) < VALUE_TB_WIN_IN_MAX_PLY && std::abs(ttData.value) < VALUE_TB_WIN_IN_MAX_PLY) @@ -1010,15 +1010,15 @@ Value Search::Worker::search( // Futility pruning for captures (~2 Elo) if (!givesCheck && lmrDepth < 7 && !ss->inCheck) { - Value futilityValue = ss->staticEval + 300 + 238 * lmrDepth + Value futilityValue = ss->staticEval + 287 + 253 * lmrDepth + PieceValue[capturedPiece] + captHist / 7; if (futilityValue <= alpha) continue; } // SEE based pruning for captures and checks (~11 Elo) - int seeHist = std::clamp(captHist / 32, -159 * depth, 160 * depth); - if (!pos.see_ge(move, -167 * depth - seeHist)) + int seeHist = std::clamp(captHist / 33, -161 * depth, 156 * depth); + if (!pos.see_ge(move, -162 * depth - seeHist)) continue; } else @@ -1029,15 +1029,15 @@ Value Search::Worker::search( + thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()]; // Continuation history based pruning (~2 Elo) - if (history < -4071 * depth) + if (history < -3884 * depth) continue; history += 2 * thisThread->mainHistory[us][move.from_to()]; - lmrDepth += history / 3653; + lmrDepth += history / 3609; Value futilityValue = - ss->staticEval + (bestValue < ss->staticEval - 51 ? 145 : 49) + 144 * lmrDepth; + ss->staticEval + (bestValue < ss->staticEval - 45 ? 140 : 43) + 141 * lmrDepth; // Futility pruning: parent node (~13 Elo) if (!ss->inCheck && lmrDepth < 12 && futilityValue <= alpha) @@ -1051,7 +1051,7 @@ Value Search::Worker::search( lmrDepth = std::max(lmrDepth, 0); // Prune moves with negative SEE (~4 Elo) - if (!pos.see_ge(move, -24 * lmrDepth * lmrDepth)) + if (!pos.see_ge(move, -25 * lmrDepth * lmrDepth)) continue; } } @@ -1074,11 +1074,11 @@ Value Search::Worker::search( // and lower extension margins scale well. if (!rootNode && move == ttData.move && !excludedMove - && depth >= 4 - (thisThread->completedDepth > 36) + ss->ttPv + && depth >= 4 - (thisThread->completedDepth > 33) + ss->ttPv && std::abs(ttData.value) < VALUE_TB_WIN_IN_MAX_PLY && (ttData.bound & BOUND_LOWER) && ttData.depth >= depth - 3) { - Value singularBeta = ttData.value - (54 + 77 * (ss->ttPv && !PvNode)) * depth / 64; + Value singularBeta = ttData.value - (56 + 79 * (ss->ttPv && !PvNode)) * depth / 64; Depth singularDepth = newDepth / 2; ss->excludedMove = move; @@ -1088,8 +1088,8 @@ Value Search::Worker::search( if (value < singularBeta) { - int doubleMargin = 262 * PvNode - 204 * !ttCapture; - int tripleMargin = 97 + 266 * PvNode - 255 * !ttCapture + 94 * ss->ttPv; + int doubleMargin = 249 * PvNode - 194 * !ttCapture; + int tripleMargin = 94 + 287 * PvNode - 249 * !ttCapture + 99 * ss->ttPv; extension = 1 + (value < singularBeta - doubleMargin) + (value < singularBeta - tripleMargin); @@ -1127,7 +1127,7 @@ Value Search::Worker::search( else if (PvNode && move.to_sq() == prevSq && thisThread->captureHistory[movedPiece][move.to_sq()] [type_of(pos.piece_on(move.to_sq()))] - > 4299) + > 4321) extension = 1; } @@ -1182,7 +1182,7 @@ Value Search::Worker::search( ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + (*contHist[0])[movedPiece][move.to_sq()] - + (*contHist[1])[movedPiece][move.to_sq()] - 4410; + + (*contHist[1])[movedPiece][move.to_sq()] - 3996; // Decrease/increase reduction for moves with a good/bad history (~8 Elo) r -= ss->statScore * 1287 / 16384; @@ -1204,8 +1204,8 @@ Value Search::Worker::search( { // Adjust full-depth search based on LMR results - if the result was // good enough search deeper, if it was bad enough search shallower. - const bool doDeeperSearch = value > (bestValue + 38 + 2 * newDepth); // (~1 Elo) - const bool doShallowerSearch = value < bestValue + 8; // (~2 Elo) + const bool doDeeperSearch = value > (bestValue + 42 + 2 * newDepth); // (~1 Elo) + const bool doShallowerSearch = value < bestValue + 10; // (~2 Elo) newDepth += doDeeperSearch - doShallowerSearch; @@ -1377,29 +1377,29 @@ Value Search::Worker::search( // Bonus for prior countermove that caused the fail low else if (!priorCapture && prevSq != SQ_NONE) { - int bonus = (118 * (depth > 5) + 38 * !allNode + 169 * ((ss - 1)->moveCount > 8) - + 116 * (!ss->inCheck && bestValue <= ss->staticEval - 101) - + 133 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 92)); + int bonus = (117 * (depth > 5) + 39 * !allNode + 168 * ((ss - 1)->moveCount > 8) + + 115 * (!ss->inCheck && bestValue <= ss->staticEval - 108) + + 119 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 83)); // Proportional to "how much damage we have to undo" - bonus += std::min(-(ss - 1)->statScore / 102, 305); + bonus += std::min(-(ss - 1)->statScore / 113, 300); bonus = std::max(bonus, 0); update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, - stat_bonus(depth) * bonus / 107); + stat_bonus(depth) * bonus / 93); thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] - << stat_bonus(depth) * bonus / 174; + << stat_bonus(depth) * bonus / 179; if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION) thisThread->pawnHistory[pawn_structure_index(pos)][pos.piece_on(prevSq)][prevSq] - << stat_bonus(depth) * bonus / 25; + << stat_bonus(depth) * bonus / 24; } // Bonus when search fails low and there is a TT move else if (ttData.move && !allNode) - thisThread->mainHistory[us][ttData.move.from_to()] << stat_bonus(depth) / 4; + thisThread->mainHistory[us][ttData.move.from_to()] << stat_bonus(depth) * 23 / 100; if (PvNode) bestValue = std::min(bestValue, maxValue); @@ -1428,13 +1428,13 @@ Value Search::Worker::search( auto bonus = std::clamp(int(bestValue - ss->staticEval) * depth / 8, -CORRECTION_HISTORY_LIMIT / 4, CORRECTION_HISTORY_LIMIT / 4); thisThread->pawnCorrectionHistory[us][pawn_structure_index(pos)] - << bonus * 101 / 128; - thisThread->majorPieceCorrectionHistory[us][major_piece_index(pos)] << bonus * 157 / 128; - thisThread->minorPieceCorrectionHistory[us][minor_piece_index(pos)] << bonus * 153 / 128; + << bonus * 107 / 128; + thisThread->majorPieceCorrectionHistory[us][major_piece_index(pos)] << bonus * 162 / 128; + thisThread->minorPieceCorrectionHistory[us][minor_piece_index(pos)] << bonus * 148 / 128; thisThread->nonPawnCorrectionHistory[WHITE][us][non_pawn_index(pos)] - << bonus * 123 / 128; + << bonus * 122 / 128; thisThread->nonPawnCorrectionHistory[BLACK][us][non_pawn_index(pos)] - << bonus * 165 / 128; + << bonus * 185 / 128; if (m.is_ok()) (*(ss - 2)->continuationCorrectionHistory)[pos.piece_on(m.to_sq())][m.to_sq()] << bonus; @@ -1566,7 +1566,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) if (bestValue > alpha) alpha = bestValue; - futilityBase = ss->staticEval + 280; + futilityBase = ss->staticEval + 306; } const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory, @@ -1629,11 +1629,11 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) + (*contHist[1])[pos.moved_piece(move)][move.to_sq()] + thisThread->pawnHistory[pawn_structure_index(pos)][pos.moved_piece(move)] [move.to_sq()] - <= 5036) + <= 5095) continue; // Do not search moves with bad enough SEE values (~5 Elo) - if (!pos.see_ge(move, -82)) + if (!pos.see_ge(move, -83)) continue; } @@ -1701,7 +1701,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta) Depth Search::Worker::reduction(bool i, Depth d, int mn, int delta) const { int reductionScale = reductions[d] * reductions[mn]; - return (reductionScale + 1239 - delta * 795 / rootDelta) + (!i && reductionScale > 1341) * 1135; + return (reductionScale + 1304 - delta * 814 / rootDelta) + (!i && reductionScale > 1423) * 1135; } // elapsed() returns the time elapsed since the search started. If the @@ -1832,7 +1832,7 @@ void update_all_stats(const Position& pos, // at ply -1, -2, -3, -4, and -6 with current move. void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { - bonus = bonus * 53 / 64; + bonus = bonus * 50 / 64; for (int i : {1, 2, 3, 4, 6}) { From 43e100ae06376d63461005422f26e5517db07c6d Mon Sep 17 00:00:00 2001 From: Nonlinear2 <131959792+Nonlinear2@users.noreply.github.com> Date: Wed, 6 Nov 2024 21:35:59 +0100 Subject: [PATCH 1762/1766] Use cutnode as TT Cutoff Condition At low enough depths, fail high with TT only when expected cutnode. Passed STC: https://tests.stockfishchess.org/tests/view/6726357b86d5ee47d953da8c LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 41184 W: 10873 L: 10551 D: 19760 Ptnml(0-2): 131, 4728, 10554, 5046, 133 Passed LTC: https://tests.stockfishchess.org/tests/view/6727326a86d5ee47d953db30 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 270888 W: 69040 L: 68243 D: 133605 Ptnml(0-2): 180, 29385, 75485, 30246, 148 closes https://github.com/official-stockfish/Stockfish/pull/5670 Bench: 805776 --- src/search.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/search.cpp b/src/search.cpp index 2a2331cb40e..5fdfdeb2d71 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -636,7 +636,8 @@ Value Search::Worker::search( // At non-PV nodes we check for an early TT cutoff if (!PvNode && !excludedMove && ttData.depth > depth - (ttData.value <= beta) && ttData.value != VALUE_NONE // Can happen when !ttHit or when access race in probe() - && (ttData.bound & (ttData.value >= beta ? BOUND_LOWER : BOUND_UPPER))) + && (ttData.bound & (ttData.value >= beta ? BOUND_LOWER : BOUND_UPPER)) + && (cutNode == (ttData.value >= beta) || depth > 8)) { // If ttMove is quiet, update move sorting heuristics on TT hit (~2 Elo) if (ttData.move && ttData.value >= beta) From 070db8b3a1ecfb4753753a3e285578b35acd63cd Mon Sep 17 00:00:00 2001 From: Linmiao Xu Date: Sun, 3 Nov 2024 22:48:42 -0500 Subject: [PATCH 1763/1766] Update default main net to nn-1c0000000000.nnue Found by updating 489 L2 weights with values found from around 31k / 60k spsa games. Spsa was configured to use 60k games, down from 120k games in: https://github.com/official-stockfish/Stockfish/pull/5459 623 spsa params: L2 weights from `nn-1cedc0ffeeee.nnue` where 24 <= |value| <= 30 A: 3000, alpha: 0.602, gamma: 0.101 weights: [-127, 127], c_end = 6 Passed STC: https://tests.stockfishchess.org/tests/view/6728d61e86d5ee47d953dcaf LLR: 2.93 (-2.94,2.94) <0.00,2.00> Total: 187168 W: 48642 L: 48107 D: 90419 Ptnml(0-2): 558, 21888, 48213, 22311, 614 Passed LTC: https://tests.stockfishchess.org/tests/view/672b018f86d5ee47d953de98 LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 235074 W: 59924 L: 59202 D: 115948 Ptnml(0-2): 131, 25467, 65610, 26207, 122 closes https://github.com/official-stockfish/Stockfish/pull/5673 Bench: 898850 --- src/evaluate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.h b/src/evaluate.h index 9bd436b58c6..4604321d378 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -33,7 +33,7 @@ namespace Eval { // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro or the location where this macro is defined, as it is used // in the Makefile/Fishtest. -#define EvalFileDefaultNameBig "nn-1cedc0ffeeee.nnue" +#define EvalFileDefaultNameBig "nn-1c0000000000.nnue" #define EvalFileDefaultNameSmall "nn-37f18f62d772.nnue" namespace NNUE { From ce2d9e27ea8b10abbd69ebd5dd73e7dcf0aa0655 Mon Sep 17 00:00:00 2001 From: FauziAkram Date: Sun, 10 Nov 2024 18:52:29 +0300 Subject: [PATCH 1764/1766] Simplify big-net reevaluation Passed STC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 37408 W: 9699 L: 9477 D: 18232 Ptnml(0-2): 130, 4326, 9577, 4534, 137 https://tests.stockfishchess.org/tests/view/672ffd8086d5ee47d953e633 Passed LTC: LLR: 2.95 (-2.94,2.94) <-1.75,0.25> Total: 151062 W: 38087 L: 37999 D: 74976 Ptnml(0-2): 63, 16686, 41958, 16748, 76 https://tests.stockfishchess.org/tests/view/673087aa86d5ee47d953e66b closes https://github.com/official-stockfish/Stockfish/pull/5674 Bench: 848812 --- src/evaluate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 7c7b54a4ff8..bc86a7420b8 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -66,7 +66,7 @@ Value Eval::evaluate(const Eval::NNUE::Networks& networks, Value nnue = (125 * psqt + 131 * positional) / 128; // Re-evaluate the position when higher eval accuracy is worth the time spent - if (smallNet && (nnue * psqt < 0 || std::abs(nnue) < 227)) + if (smallNet && (std::abs(nnue) < 236)) { std::tie(psqt, positional) = networks.big.evaluate(pos, &caches.big); nnue = (125 * psqt + 131 * positional) / 128; From 49138b8c33ca7bacff710efba4a90630a3490c08 Mon Sep 17 00:00:00 2001 From: Disservin Date: Wed, 13 Nov 2024 14:56:19 +0100 Subject: [PATCH 1765/1766] Fix CI Docker Buildx closes https://github.com/official-stockfish/Stockfish/pull/5678 No functional change --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a826e6f063e..b97aaa29c5d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -139,7 +139,7 @@ jobs: - name: Build Docker container if: matrix.config.base_image run: | - docker buildx build --load -t sf_builder - << EOF + docker buildx build --platform ${{ matrix.config.platform }} --load -t sf_builder - << EOF FROM ${{ matrix.config.base_image }} WORKDIR /app RUN apk update && apk add make g++ From 82b092ca48c2efeadf2108a8351bb1309c4b7780 Mon Sep 17 00:00:00 2001 From: Michael Chaly Date: Wed, 13 Nov 2024 20:43:04 +0300 Subject: [PATCH 1766/1766] Adjust statscore for captures Instead of using quiet histories use capture history with a different offset. Passed STC: https://tests.stockfishchess.org/tests/view/6731d5cc86d5ee47d953e719 LLR: 2.96 (-2.94,2.94) <0.00,2.00> Total: 428896 W: 111160 L: 110269 D: 207467 Ptnml(0-2): 1220, 50296, 110534, 51169, 1229 Passed LTC: https://tests.stockfishchess.org/tests/view/6733d9fd86d5ee47d953e962 LLR: 2.95 (-2.94,2.94) <0.50,2.50> Total: 105882 W: 26918 L: 26458 D: 52506 Ptnml(0-2): 66, 11430, 29482, 11904, 59 closes https://github.com/official-stockfish/Stockfish/pull/5679 Bench: 840721 --- src/search.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index 5fdfdeb2d71..94b20c85be2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1181,9 +1181,14 @@ Value Search::Worker::search( else if (move == ttData.move) r -= 1879; - ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] - + (*contHist[0])[movedPiece][move.to_sq()] - + (*contHist[1])[movedPiece][move.to_sq()] - 3996; + if (capture) + ss->statScore = + thisThread->captureHistory[movedPiece][move.to_sq()][type_of(pos.captured_piece())] + - 13000; + else + ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()] + + (*contHist[0])[movedPiece][move.to_sq()] + + (*contHist[1])[movedPiece][move.to_sq()] - 3996; // Decrease/increase reduction for moves with a good/bad history (~8 Elo) r -= ss->statScore * 1287 / 16384;